5910 lines
194 KiB
C++
5910 lines
194 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// FILE : scene/world/GameWorld.cpp
|
|
// PURPOSE : Store data about the world, or specific things in the world. But not
|
|
// any form of representation of the world!
|
|
// AUTHOR : John W.
|
|
// CREATED : 1/10/08
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "scene/world/gameWorld.h"
|
|
#include "scene/world/GameWorldHeightMap.h"
|
|
|
|
// Rage headers
|
|
#include "system/nelem.h"
|
|
#include "system/threadtype.h"
|
|
#include "spatialdata/sphere.h"
|
|
#include "phbound/BoundBox.h"
|
|
#include "profile/timebars.h"
|
|
|
|
// Framework headers
|
|
#include "fwanimation/animdirector.h"
|
|
#include "fwanimation/directorcomponentmotiontree.h"
|
|
#include "fwanimation/directorcomponentsyncedscene.h"
|
|
#include "fwanimation/facialclipsetgroups.h"
|
|
#include "fwnet/netblender.h"
|
|
#include "fwpheffects/ropemanager.h"
|
|
#include "fwscene/world/WorldRepMulti.h"
|
|
#include "fwscene/world/WorldLimits.h"
|
|
#include "fwscene/world/worldMgr.h"
|
|
#include "fwscene/scan/ScanDebug.h"
|
|
#include "fwscene/search/SearchVolumes.h"
|
|
#include "fwscene/scan/ScanDebug.h"
|
|
#include "streaming/streamingvisualize.h"
|
|
|
|
// Game headers
|
|
#include "ai/aichannel.h"
|
|
#include "animation/AnimScene/AnimSceneManager.h"
|
|
#include "animation/debug/AnimViewer.h"
|
|
#include "animation/FacialData.h"
|
|
#include "audio/northaudioengine.h"
|
|
#include "audio/conductorsutil.h"
|
|
#include "audio/superconductor.h"
|
|
#include "camera/viewports/ViewportManager.h"
|
|
#include "camera/CamInterface.h"
|
|
#include "camera/helpers/ControlHelper.h"
|
|
#include "camera/system/CameraManager.h"
|
|
#include "control/gamelogic.h"
|
|
#include "control/garages.h"
|
|
#include "control/WaypointRecording.h"
|
|
#include "control/record.h"
|
|
#include "control/replay/replay.h"
|
|
#include "control/replay/Effects/ParticlePedFxPacket.h"
|
|
#include "core/game.h"
|
|
#include "debug/Debug.h"
|
|
#include "debug/DebugScene.h"
|
|
#include "event/eventdamage.h"
|
|
#include "event/events.h"
|
|
#include "debug/GtaPicker.h"
|
|
#include "event/ShockingEvents.h"
|
|
#include "frontend/NewHud.h"
|
|
#include "game/Dispatch/IncidentManager.h"
|
|
#include "modelinfo/mlomodelinfo.h"
|
|
#include "ModelInfo/ModelInfo.h"
|
|
#include "ModelInfo/ModelInfo_Factories.h"
|
|
#include "modelinfo/pedmodelinfo.h"
|
|
#include "modelinfo/vehiclemodelinfo.h"
|
|
#include "network/general/NetworkUtil.h"
|
|
#include "network/Live/livemanager.h"
|
|
#include "network/NetworkInterface.h"
|
|
#include "network/objects/entities/NetObjPlayer.h"
|
|
#include "network/players/NetworkPlayerMgr.h"
|
|
#include "objects/Door.h"
|
|
#include "objects/DummyObject.h"
|
|
#include "objects/ObjectIntelligence.h"
|
|
#include "pathserver/ExportCollision.h"
|
|
#include "pedgroup/PedGroup.h"
|
|
#include "peds/PedEventScanner.h"
|
|
#include "peds/pedfactory.h"
|
|
#include "peds/PedGeometryAnalyser.h"
|
|
#include "peds/PedIntelligence.h"
|
|
#include "peds/pedpopulation.h"
|
|
#include "peds/pedtaskrecord.h"
|
|
#include "peds/Ped.h"
|
|
#include "peds/PlayerInfo.h"
|
|
#include "Peds/HealthConfig.h"
|
|
#include "physics/breakable.h"
|
|
#include "physics/Collider.h"
|
|
#include "physics/gtaArchetype.h"
|
|
#include "physics/GtaInst.h"
|
|
#include "physics/Physics.h"
|
|
#include "physics/WorldProbe/worldprobe.h"
|
|
#include "pickups/PickupManager.h"
|
|
#include "pickups/Pickup.h"
|
|
#include "renderer/lights/lights.h"
|
|
#include "Renderer/Lights/LightEntity.h"
|
|
#include "renderer/PostProcessFX.h"
|
|
#include "renderer/Mirrors.h"
|
|
#include "renderer/PostProcessFX.h"
|
|
#include "renderer/RenderPhases/RenderPhaseMirrorReflection.h"
|
|
#include "scene/scene_channel.h"
|
|
#include "scene/Building.h"
|
|
#include "scene/Entity.h"
|
|
#include "scene/InstancePriority.h"
|
|
#include "scene/lod/LodMgr.h"
|
|
#include "scene/RegdRefTypes.h"
|
|
#include "scene/entities/compEntity.h"
|
|
#include "scene/world/WorldDebugHelper.h"
|
|
#include "script/script.h"
|
|
#include "script/script_areas.h"
|
|
#include "script/script_cars_and_peds.h"
|
|
#include "script/Handlers/GameScriptEntity.h"
|
|
#include "streaming/streaming.h" // For CStreaming::HasObjectLoaded(), etc.
|
|
#include "system/Timer.h"
|
|
#include "system/TamperActions.h"
|
|
#include "Task/Combat/CombatManager.h"
|
|
#include "Task/Combat/Cover/TaskCover.h"
|
|
#include "Task/Combat/TaskNewCombat.h"
|
|
#include "Task/Default/TaskPlayer.h"
|
|
#include "Task/Default/TaskWander.h"
|
|
#include "Task/Response/TaskFlee.h"
|
|
#include "Task/Motion/Locomotion/TaskMotionPed.h"
|
|
#include "Task/Movement/Climbing/TaskGoToAndClimbLadder.h"
|
|
#include "Task/Movement/Climbing/TaskLadderClimb.h"
|
|
#include "Task/Scenario/ScenarioManager.h"
|
|
#include "Task/Scenario/ScenarioPointManager.h"
|
|
#include "Task/Scenario/ScenarioVehicleManager.h"
|
|
#include "vehicleAi/task/DeferredTasks.h"
|
|
#include "vehicles/train.h"
|
|
#include "vehicles/Vehicle.h"
|
|
#include "vehicles/VehicleFactory.h"
|
|
#include "vehicles/vehiclepopulation.h"
|
|
#include "Vfx/Decals/DecalManager.h"
|
|
#include "Vfx/Misc/Fire.h"
|
|
#include "Vfx/Misc/WaterCannon.h"
|
|
#include "Vfx/Misc/LensFlare.h"
|
|
#include "vfx/particles/PtFxManager.h"
|
|
#include "weapons/Explosion.h"
|
|
#include "weapons/WeaponManager.h"
|
|
#include "weapons/projectiles/projectile.h"
|
|
#include "weapons/projectiles/ProjectileManager.h"
|
|
#include "stats/StatsInterface.h"
|
|
#include "game/ModelIndices.h"
|
|
|
|
#if __BANK
|
|
#include "fwDebug/Picker.h"
|
|
#endif
|
|
|
|
AI_COVER_OPTIMISATIONS()
|
|
|
|
// All this is a workaround because we had trouble getting the marker for UpdateThroughAnimQueue_Stall
|
|
// to appear in Tuner captures, for some reason.
|
|
#if !__STATS
|
|
#define PF_START_EXTRA_MARKER(name)
|
|
#define PF_STOP_EXTRA_MARKER(name)
|
|
#else
|
|
#if __PPU
|
|
extern "C" {
|
|
int snPushMarker(const char *pText);
|
|
int snPopMarker();
|
|
}
|
|
extern bool g_EnableTunerAnnotation;
|
|
#define PF_START_EXTRA_MARKER(x) do { PF_START(x); if(g_EnableTunerAnnotation) { snPushMarker(#x "M"); } } while(0)
|
|
#define PF_STOP_EXTRA_MARKER(x) do { if(g_EnableTunerAnnotation) { snPopMarker(); } PF_STOP(x); } while(0)
|
|
#else // __PPU
|
|
#define PF_START_EXTRA_MARKER(x) PF_START(x)
|
|
#define PF_STOP_EXTRA_MARKER(x) PF_STOP(x)
|
|
#endif // __PPU
|
|
#endif
|
|
|
|
namespace gtaWorldStats
|
|
{
|
|
PF_PAGE(GTA_World_Proc, "Gta World Process");
|
|
PF_GROUP(World_Proc);
|
|
PF_LINK(GTA_World_Proc, World_Proc);
|
|
PF_TIMER(Proc_Global_Events, World_Proc);
|
|
PF_TIMER(Proc_Animation, World_Proc);
|
|
|
|
PF_GROUP(Proc_Control);
|
|
PF_LINK(GTA_World_Proc, Proc_Control);
|
|
PF_TIMER(Ped_ProcCont, Proc_Control);
|
|
PF_TIMER(Veh_ProcCont, Proc_Control);
|
|
PF_TIMER(DummyVeh_ProcCont, Proc_Control);
|
|
PF_TIMER(Obj_ProcCont, Proc_Control);
|
|
|
|
PF_TIMER(Proc_Control2, World_Proc);
|
|
PF_TIMER(Proc_PedGroups, World_Proc);
|
|
PF_TIMER(Proc_CombatManager, World_Proc);
|
|
|
|
PF_GROUP(Proc_Physics);
|
|
PF_LINK(GTA_World_Proc,Proc_Physics);
|
|
|
|
PF_TIMER(Phys_Total, Proc_Physics);
|
|
PF_TIMER(Phys_Requests, Proc_Physics);
|
|
PF_TIMER(Phys_Pre, Proc_Physics);
|
|
PF_TIMER(Phys_Pre_Sim, Proc_Physics);
|
|
PF_TIMER(Phys_Sim, Proc_Physics);
|
|
PF_TIMER(Phys_Post_Sim, Proc_Physics);
|
|
PF_TIMER(Phys_Iterate, Proc_Physics);
|
|
PF_TIMER(Phys_Post, Proc_Physics);
|
|
|
|
PF_TIMER(Phys_RageSimUpdate,Proc_Physics);
|
|
PF_TIMER(Phys_RageFragUpdate,Proc_Physics);
|
|
PF_TIMER(Phys_RageClothUpdate,Proc_Physics);
|
|
PF_TIMER(Phys_RageRopeUpdate,Proc_Physics);
|
|
PF_TIMER(Phys_RageVerletWaitForTasks,Proc_Physics);
|
|
PF_TIMER(Phys_RagInstBehvrs,Proc_Physics);
|
|
PF_TIMER(Phys_RageClothAttachmentFrameUpdate,Proc_Physics);
|
|
|
|
|
|
PF_TIMER(Proc_VehRecording, World_Proc);
|
|
PF_TIMER(Proc_PedRecordingAlways, World_Proc);
|
|
PF_TIMER(Proc_Players, World_Proc);
|
|
PF_TIMER(Proc_ScriptRecTasks, World_Proc);
|
|
PF_TIMER(Proc_RemoveFallen, World_Proc);
|
|
PF_TIMER(Proc_Replay, World_Proc);
|
|
|
|
|
|
PF_GROUP(Scene_Update);
|
|
PF_LINK(GTA_World_Proc, Scene_Update);
|
|
PF_TIMER_OFF(GameWorldPedsAndObjects, Scene_Update);
|
|
PF_TIMER(SU_UPDATE, Scene_Update);
|
|
PF_TIMER(SU_UPDATE_VEHICLE, Scene_Update);
|
|
PF_TIMER(UpdateThroughAnimQueue, Scene_Update);
|
|
PF_TIMER(UpdateThroughAnimQueue_Stall, Scene_Update);
|
|
PF_TIMER(ProcessVehicleEvents, Scene_Update);
|
|
|
|
PF_GROUP_OFF(Scene_Update_Counters);
|
|
PF_LINK(GTA_World_Proc, Scene_Update_Counters);
|
|
PF_VALUE_INT(UpdateThroughAnimQueue_StallCount, Scene_Update_Counters);
|
|
};
|
|
|
|
SCENE_OPTIMISATIONS()
|
|
AI_OPTIMISATIONS()
|
|
NETWORK_OPTIMISATIONS()
|
|
|
|
PARAM(noPlayer, "[PlayerPed] Game never creates the player.");
|
|
PARAM(dynamicAddRemoveSpew, "Debug spew for game world Add/Remove for dynamic entities");
|
|
|
|
PARAM(logClearCarFromArea, "[GameWorld] print the reason for a clear_area failing to remove a vehicle");
|
|
PARAM(logClearCarFromAreaOutOfRange, "[GameWorld] enable the print that gets output if a clear_area fails to remove a vehicle due to the vehicle being out of range");
|
|
|
|
using namespace gtaWorldStats;
|
|
|
|
bool CGameWorld::ms_bAllowDelete = true;
|
|
#if __BANK
|
|
bool CGameWorld::ms_detailedOutOrMapSpew = false;
|
|
bool ms_UpdateEntityDesc_StopOnSelectedEntity = false;
|
|
#endif
|
|
|
|
fwInteriorLocation CGameWorld::OUTSIDE = fwInteriorLocation();
|
|
|
|
u8 CGameWorld::ms_DynEntVisCountdownToRejuvenate = 0;
|
|
bool CGameWorld::ms_freezeExteriorVehicles = false;
|
|
bool CGameWorld::ms_freezeExteriorPeds = false;
|
|
bool CGameWorld::ms_overrideFreezeFlags = false;
|
|
#if !__FINAL
|
|
bool CGameWorld::ms_busyWaitIfAnimQueueExhausted = false;
|
|
#endif // !__FINAL
|
|
bool CGameWorld::ms_updateObjectsThroughAnimQueue = false;
|
|
bool CGameWorld::ms_updatePedsThroughAnimQueue = false;
|
|
bool CGameWorld::ms_updateVehiclesThroughAnimQueue = false;
|
|
bank_bool CGameWorld::ms_makeFrozenEntitiesInvisible = true;
|
|
atArray< CPhysical* > CGameWorld::ms_frozenEntities;
|
|
atArray< CPhysical* > CGameWorld::ms_unfrozenEntities;
|
|
atArray< CPed* > CGameWorld::ms_pedsRunningInstantAnimUpdate;
|
|
bool CGameWorld::ms_playerFallbackPosSet = false;
|
|
Vector3 CGameWorld::ms_playerFallbackPos(0.0f, 0.0f, 0.0f);
|
|
u16 CGameWorld::ms_nNumDelayedRootAttachments = 0;
|
|
RegdPhy CGameWorld::ms_delayedRootAttachments[MAX_NUM_POST_PRE_RENDER_ATTACHMENTS];
|
|
const CEntity* CGameWorld::ms_pEntityBeingRemovedAndAdded = NULL;
|
|
const CEntity* CGameWorld::ms_pEntityBeingRemoved = NULL;
|
|
const CPed* CGameWorld::ms_pPedBeingDeleted = NULL;
|
|
u32 CGameWorld::m_LastLensFlareModelHash = 0;
|
|
const char* CGameWorld::sm_FollowPlayerCarName=NULL;
|
|
|
|
s32 CGameWorld::sm_localPlayerWanted=0;
|
|
eArrestState CGameWorld::sm_localPlayerArrestState=eArrestState::ArrestState_None;
|
|
eDeathState CGameWorld::sm_localPlayerDeathState=eDeathState::DeathState_Alive;
|
|
float CGameWorld::sm_localPlayerHealth=0.0f;
|
|
float CGameWorld::sm_localPlayerMaxHealth=0.0f;
|
|
|
|
// --- CFindData : helper class to hold list of found objects during a search
|
|
//
|
|
// name: CFindData
|
|
// description: Structure filled out with entity data for find functions
|
|
class CFindData
|
|
{
|
|
public:
|
|
enum {
|
|
MAX_FIND_ENTITIES = 32
|
|
};
|
|
CFindData(CEntity** pArray, s32 size) : m_size(size), m_number(0), m_pEntities(pArray) {}
|
|
CFindData(CEntity** pArray, s32 size, u32 data) : m_size(size), m_number(0), m_data(data), m_pEntities(pArray) {}
|
|
|
|
// name: AddEntity
|
|
// description: Add entity to list
|
|
bool AddEntity(CEntity* pEntity)
|
|
{
|
|
if(m_number < m_size)
|
|
{
|
|
if(m_pEntities)
|
|
m_pEntities[m_number] = pEntity;
|
|
m_number++;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
CEntity* GetEntity(u32 i) {FastAssert(i<m_number); return m_pEntities[i];}
|
|
u32 GetNumEntities() {return m_number;}
|
|
u32 GetData() {return m_data;}
|
|
void Reset() {m_number = 0;}
|
|
|
|
private:
|
|
u32 m_size;
|
|
u32 m_number;
|
|
u32 m_data;
|
|
CEntity** m_pEntities;
|
|
};
|
|
|
|
struct SceneUpdateParameters
|
|
{
|
|
RegdEnt *apEntitiesToDelete;
|
|
s32 iNumEntitiesToDelete;
|
|
|
|
// some object may need to be removed from to the process control list instead of processing them
|
|
RegdDyn *apEntitiesToRemoveFromProcContList;
|
|
s32 iNumEntitiesToRemoveFromProcCont;
|
|
|
|
#if __DEV
|
|
bool bPrintProcessList;
|
|
#endif // __DEV
|
|
};
|
|
|
|
|
|
//
|
|
// CGameWorld methods
|
|
//
|
|
|
|
void CGameWorld::ResetLineTestOptions()
|
|
{
|
|
}
|
|
|
|
// Name : ShutdownLevelWithMapUnloaded
|
|
// Purpose : clean out lists and delete everything
|
|
// Parameters : none
|
|
// Returns : none
|
|
|
|
void CGameWorld::Shutdown2(unsigned shutdownMode)
|
|
{
|
|
if(shutdownMode == SHUTDOWN_WITH_MAP_LOADED)
|
|
{
|
|
const u32 maxEntityCount = 10000;
|
|
CEntity** entityArray = rage_new CEntity*[ maxEntityCount ];
|
|
u32 entityCount;
|
|
|
|
gWorldMgr.GetAll( reinterpret_cast< fwEntity** >( entityArray ), &entityCount );
|
|
|
|
// WARNING: The array is traversed from end to start because
|
|
// it happens to respect entity dependencies. Thin Ice!
|
|
|
|
for (s32 i = entityCount - 1; i >= 0; i--)
|
|
if ( entityArray[i] )
|
|
{
|
|
CGameWorld::Remove( entityArray[i] );
|
|
delete entityArray[i];
|
|
}
|
|
|
|
delete entityArray;
|
|
}
|
|
}
|
|
|
|
//
|
|
// name: Shutdown3
|
|
// description: Used so we can shutdown the pools post everything else having been shutdown.
|
|
// mapdata was crashing because of double shutdown for some CBuilding objects.
|
|
void CGameWorld::Shutdown3(unsigned shutdownMode)
|
|
{
|
|
if(shutdownMode == SHUTDOWN_WITH_MAP_LOADED)
|
|
{
|
|
// Remove remaining Buildings's that weren't in the world.
|
|
CBuilding::GetPool()->DeleteAll();
|
|
CDummyObject::GetPool()->DeleteAll();
|
|
}
|
|
}
|
|
|
|
//
|
|
// name: InitLevelWithMapLoaded
|
|
// description: Initialise world for a new level
|
|
void CGameWorld::Init(unsigned initMode)
|
|
{
|
|
if(initMode == INIT_CORE)
|
|
{
|
|
fwSceneUpdate::Init();
|
|
fwSceneUpdate::RegisterSceneUpdate(SU_ADD_SHOCKING_EVENTS, SceneUpdateAddShockingEventsCb, "ShockEv");
|
|
fwSceneUpdate::RegisterSceneUpdate(SU_START_ANIM_UPDATE_PRE_PHYSICS, SceneUpdateStartAnimUpdatePrePhysicsCb, "StrtAnm");
|
|
fwSceneUpdate::RegisterSceneUpdate(SU_END_ANIM_UPDATE_PRE_PHYSICS, SceneUpdateEndAnimUpdatePrePhysicsCb, "EndAnm");
|
|
fwSceneUpdate::RegisterSceneUpdate(SU_UPDATE_PAUSED, SceneUpdateUpdatePausedCb, "UpdPaus");
|
|
fwSceneUpdate::RegisterSceneUpdate(SU_START_ANIM_UPDATE_POST_CAMERA, SceneUpdateStartAnimUpdatePostCameraCb, "AnAfCam");
|
|
fwSceneUpdate::RegisterSceneUpdate(SU_UPDATE, SceneUpdateUpdateCb, "Update");
|
|
fwSceneUpdate::RegisterSceneUpdate(SU_AFTER_ALL_MOVEMENT, SceneUpdateAfterAllMovementCb, "AftMove");
|
|
fwSceneUpdate::RegisterSceneUpdate(SU_RESET_VISIBILITY, SceneUpdateResetVisibilityCb, "RstVis");
|
|
fwSceneUpdate::RegisterSceneUpdate(SU_TRAIN, NULL, "Train");
|
|
fwSceneUpdate::RegisterSceneUpdate(SU_UPDATE_PERCEPTION, SceneUpdateUpdatePerceptionCb, "UpdPrcp");
|
|
fwSceneUpdate::RegisterSceneUpdate(SU_PRESIM_PHYSICS_UPDATE, SceneUpdatePreSimPhysicsUpdateCb, "PreSim");
|
|
fwSceneUpdate::RegisterSceneUpdate(SU_PHYSICS_PRE_UPDATE, SceneUpdatePhysicsPreUpdateCb, "PreUpd");
|
|
fwSceneUpdate::RegisterSceneUpdate(SU_PHYSICS_POST_UPDATE, SceneUpdatePhysicsPostUpdateCb, "PostUpd");
|
|
fwSceneUpdate::RegisterSceneUpdate(SU_UPDATE_VEHICLE, SceneUpdateUpdateCb, "UpdVeh");
|
|
fwSceneUpdate::RegisterSceneUpdate(SU_START_ANIM_UPDATE_PRE_PHYSICS_PASS2, SceneUpdateStartAnimUpdatePrePhysicsCb, "StrtAnmPass2");
|
|
|
|
// Set the Entity -> EntityDesc function
|
|
fwEntityContainer::SetUpdateEntityDescFunc( CGameWorld::UpdateEntityDesc );
|
|
|
|
// create our default world rep
|
|
fwWorldRepMulti* pDefaultRep = rage_new fwWorldRepMulti;
|
|
|
|
// set the types of objects handled by this representation
|
|
pDefaultRep->SetHandlingTypes
|
|
(
|
|
ENTITY_TYPE_MASK_BUILDING |
|
|
ENTITY_TYPE_MASK_ANIMATED_BUILDING |
|
|
ENTITY_TYPE_MASK_VEHICLE |
|
|
ENTITY_TYPE_MASK_PED |
|
|
ENTITY_TYPE_MASK_OBJECT |
|
|
ENTITY_TYPE_MASK_DUMMY_OBJECT |
|
|
ENTITY_TYPE_MASK_PORTAL |
|
|
ENTITY_TYPE_MASK_MLO |
|
|
ENTITY_TYPE_MASK_NOTINPOOLS |
|
|
ENTITY_TYPE_MASK_PARTICLESYSTEM |
|
|
ENTITY_TYPE_MASK_LIGHT |
|
|
ENTITY_TYPE_MASK_COMPOSITE |
|
|
|
|
ENTITY_TYPE_MASK_NOTHING
|
|
);
|
|
|
|
// set the operations which are routed through this representation
|
|
pDefaultRep->SetOperations
|
|
(
|
|
USAGE_MASK_ADD_REMOVE |
|
|
USAGE_MASK_PRESCAN |
|
|
USAGE_MASK_SCAN |
|
|
USAGE_MASK_POSTSCAN |
|
|
USAGE_MASK_SEARCH |
|
|
USAGE_MASK_UPDATE |
|
|
USAGE_MASK_MISC |
|
|
USAGE_MASK_DEBUG
|
|
);
|
|
|
|
gWorldMgr.Initialise(pDefaultRep);
|
|
|
|
#if !__NO_OUTPUT
|
|
if (PARAM_logClearCarFromArea.Get())
|
|
{
|
|
ms_bLogClearCarFromArea = true;
|
|
}
|
|
|
|
if (PARAM_logClearCarFromAreaOutOfRange.Get())
|
|
{
|
|
ms_bLogClearCarFromArea_OutOfRange = true;
|
|
}
|
|
#endif // !__NO_OUTPUT
|
|
}
|
|
else if(initMode == INIT_BEFORE_MAP_LOADED)
|
|
{
|
|
CInstancePriority::Init();
|
|
//CGameWorldHeightMap::Clear(); -- don't clear the heightmap, i've initialised it with static data
|
|
}
|
|
else if(initMode == INIT_SESSION)
|
|
{
|
|
ms_freezeExteriorPeds = false;
|
|
ms_freezeExteriorVehicles = false;
|
|
ms_overrideFreezeFlags = false;
|
|
|
|
ms_updateObjectsThroughAnimQueue = true;
|
|
ms_updatePedsThroughAnimQueue = true;
|
|
ms_updateVehiclesThroughAnimQueue = true;
|
|
}
|
|
}
|
|
|
|
|
|
// Name : ShutdownLevelWithMapLoaded
|
|
// Purpose : only cleans out the lists and removes the stuff from the world
|
|
// : Used for restart/load game and When you have been arrested
|
|
// Parameters : none
|
|
// Returns : none
|
|
|
|
void CGameWorld::Shutdown1(unsigned shutdownMode)
|
|
{
|
|
if(shutdownMode == SHUTDOWN_CORE)
|
|
{
|
|
gWorldMgr.Shutdown();
|
|
}
|
|
else if(shutdownMode == SHUTDOWN_SESSION)
|
|
{
|
|
// Use the pools to remove all vehicles and peds. Safer as drivers weren't removed with the code above.
|
|
CVehicle::Pool *VehiclePool = CVehicle::GetPool();
|
|
CVehicle* pVehicle;
|
|
s32 i = (s32) VehiclePool->GetSize();
|
|
while(i--)
|
|
{
|
|
pVehicle = VehiclePool->GetSlot(i);
|
|
if(pVehicle)
|
|
{
|
|
if(pVehicle->IsNetworkClone())
|
|
{
|
|
NetworkInterface::UnregisterObject(pVehicle, true);
|
|
}
|
|
else
|
|
{
|
|
CGameWorld::Remove(pVehicle);
|
|
CVehicleFactory::GetFactory()->Destroy(pVehicle);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ensure all dummy peds are removed
|
|
CPedPopulation::RemoveAllRandomPeds();
|
|
|
|
CPed::Pool *PedPool = CPed::GetPool();
|
|
CPed* pPed;
|
|
i=PedPool->GetSize();
|
|
while(i--)
|
|
{
|
|
pPed = PedPool->GetSlot(i);
|
|
if(pPed)
|
|
{
|
|
if(pPed->IsNetworkClone())
|
|
{
|
|
NetworkInterface::UnregisterObject(pPed, true);
|
|
}
|
|
else
|
|
{
|
|
if(pPed->IsPlayer())
|
|
{
|
|
CPedFactory::GetFactory()->DestroyPlayerPed(pPed);
|
|
}
|
|
else
|
|
{
|
|
CPedFactory::GetFactory()->DestroyPed(pPed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// don't want to remove dummy objects (those that are part of the map)
|
|
// Any that have been converted to real objects have to be converted back to dummies before all
|
|
// the real objects are destroyed
|
|
CObjectPopulation::ConvertAllObjectsToDummyObjects(true);
|
|
|
|
CObject::Pool *ObjPool = CObject::GetPool();
|
|
CObject* pObj;
|
|
i = (s32) ObjPool->GetSize();
|
|
while (i--)
|
|
{
|
|
pObj = ObjPool->GetSlot(i);
|
|
if (pObj)
|
|
{
|
|
if(pObj->IsNetworkClone())
|
|
{
|
|
NetworkInterface::UnregisterObject(pObj, true);
|
|
}
|
|
else
|
|
{
|
|
CObjectPopulation::DestroyObject(pObj);
|
|
}
|
|
}
|
|
}
|
|
|
|
//gWorldMgr.ShutdownLevelWithMapUnloaded();
|
|
|
|
CPhysics::GetRopeManager()->Reset();
|
|
|
|
#if __ASSERT
|
|
const u32 maxEntityCount = 10000;
|
|
CEntity** entityArray = rage_new CEntity*[ maxEntityCount ];
|
|
u32 entityCount;
|
|
|
|
gWorldMgr.GetAll( reinterpret_cast< fwEntity** >( entityArray ), &entityCount );
|
|
|
|
for (s32 i = 0; i < entityCount; ++i)
|
|
if ( entityArray[i] )
|
|
{
|
|
Assert( static_cast< CEntity* >( entityArray[i] )->GetIsTypePed() == false );
|
|
Assert( static_cast< CEntity* >( entityArray[i] )->GetIsTypeVehicle() == false );
|
|
}
|
|
|
|
delete entityArray;
|
|
#endif
|
|
|
|
#if !__FINAL
|
|
ValidateShutdown();
|
|
#endif // !__FINAL
|
|
}
|
|
}
|
|
|
|
#if !__FINAL
|
|
void CGameWorld::PrintProcessControlList()
|
|
{
|
|
fwSceneUpdate::PrintSceneUpdateList();
|
|
}
|
|
|
|
void CGameWorld::ValidateShutdown()
|
|
{
|
|
CPed::Pool *PedPool = CPed::GetPool();
|
|
if (PedPool->GetNoOfUsedSpaces() != 0)
|
|
{
|
|
int i=PedPool->GetSize();
|
|
while(i--)
|
|
{
|
|
CPed *pPed = PedPool->GetSlot(i);
|
|
if(pPed)
|
|
{
|
|
Errorf("Left-over ped: %p (%s)", pPed, pPed->GetModelName());
|
|
}
|
|
}
|
|
|
|
Assertf(false, "Failed to free up all peds - see TTY for list");
|
|
}
|
|
|
|
CObject::Pool *ObjPool = CObject::GetPool();
|
|
if (ObjPool->GetNoOfUsedSpaces() != 0)
|
|
{
|
|
int i = (s32) ObjPool->GetSize();
|
|
while (i--)
|
|
{
|
|
CObject *pObj = ObjPool->GetSlot(i);
|
|
if (pObj)
|
|
{
|
|
Errorf("Left-over object: %p (%s)", pObj, pObj->GetModelName());
|
|
}
|
|
}
|
|
|
|
Assertf(false, "Failed to free up all objects - see TTY for list");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// some debug tests we want stuff to pass before entering the world
|
|
#if __DEV
|
|
void CGameWorld::DebugTestsForAdd(CEntity* pEntity){
|
|
|
|
// Try to catch entities with duff matrix
|
|
const Matrix34 mat = MAT34V_TO_MATRIX34(pEntity->GetMatrix());
|
|
Assert(rage::FPIsFinite(mat.a.x) && rage::FPIsFinite(mat.a.y) && rage::FPIsFinite(mat.a.z));
|
|
Assert(rage::FPIsFinite(mat.b.x) && rage::FPIsFinite(mat.b.y) && rage::FPIsFinite(mat.b.z));
|
|
Assert(rage::FPIsFinite(mat.c.x) && rage::FPIsFinite(mat.c.y) && rage::FPIsFinite(mat.c.z));
|
|
|
|
// try to catch peds with unitialised shaders getting into the world
|
|
if (pEntity->GetIsTypePed()){
|
|
CPedModelInfo* pPMI = static_cast<CPedModelInfo*>(pEntity->GetBaseModelInfo());
|
|
Assert(pPMI);
|
|
if (!pPMI->GetIsStreamedGfx()){
|
|
CPed* pPed = static_cast<CPed*>(pEntity);
|
|
sceneAssertf(pPed->GetPedDrawHandler().GetVarData().IsShaderSetup(), "Adding an instance of ped <%s> to world, but shader is not setup!", pPMI->GetModelName());
|
|
}
|
|
}
|
|
|
|
if (pEntity->GetOwnedBy() == ENTITY_OWNEDBY_INTERIOR)
|
|
{
|
|
if (pEntity->GetIsTypePed()){
|
|
Assertf(false, "Info : Peds or dummy peds shouldn't have interior info idx set\n");
|
|
}
|
|
|
|
if (pEntity->GetIsTypeLight())
|
|
{
|
|
pEntity->m_nFlags.bLightObject = true;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
#else //__DEV
|
|
void CGameWorld::DebugTestsForAdd(CEntity*) {}
|
|
#endif //__DEV
|
|
|
|
|
|
// Name : Add
|
|
// Purpose : adds entity to world
|
|
// Parameters : entity
|
|
// Returns : none
|
|
|
|
void CGameWorld::Add(CEntity* pEntity, fwInteriorLocation interiorLocation, bool bIgnorePhysicsWorld)
|
|
{
|
|
Assert(pEntity);
|
|
Assertf(pEntity != ms_pEntityBeingRemoved, "%s : don't add this entity back into world whilst it is being removed!", pEntity->GetModelName());
|
|
|
|
Assertf(!pEntity->GetIsTypePed() || sysThreadType::IsUpdateThread(), "%s: CGameWorld::Add() called from some other thread, this is probably not safe.", pEntity->GetModelName());
|
|
Assertf(pEntity != ms_pPedBeingDeleted, "%s: a ped is being added to the game world when it's about to be deleted.", pEntity->GetModelName());
|
|
|
|
#if __BANK
|
|
if(pEntity->GetIsTypePed() && NetworkInterface::IsNetworkOpen())
|
|
{
|
|
CPed* pPlayerPed = static_cast<CPed*>(pEntity);
|
|
|
|
CBaseModelInfo* pBaseModelInfo = pPlayerPed->GetBaseModelInfo();
|
|
CBuoyancyInfo* pBuoyancyInfo = pBaseModelInfo->GetBuoyancyInfo();
|
|
if(!Verifyf(pBuoyancyInfo && pBuoyancyInfo->m_WaterSamples, "Adding ped to world without water samples!"))
|
|
{
|
|
Displayf("[CGameWorld::Add() - start] %s", pBaseModelInfo->GetModelName());
|
|
Displayf("Player num water samples: %d, water sample ptr: 0x%p", pBuoyancyInfo->m_nNumWaterSamples, pBuoyancyInfo->m_WaterSamples);
|
|
sysStack::PrintStackTrace();
|
|
}
|
|
}
|
|
#endif // __BANK
|
|
|
|
#if __BANK
|
|
if ( PARAM_dynamicAddRemoveSpew.Get() && pEntity->GetIsDynamic() )
|
|
{
|
|
const char* debugName = ASSERT_ONLY( pEntity->GetIsPhysical() ? static_cast< CPhysical* >(pEntity)->GetDebugName() : ) "none";
|
|
worldDebugf1( "CGameWorld::Add called on entity %p (model %s, debugname %s)", pEntity, pEntity->GetModelName(), debugName );
|
|
worldDebugf1( "Start stack trace" );
|
|
worldDebugf1StackTrace();
|
|
worldDebugf1( "End stack trace" );
|
|
}
|
|
#endif
|
|
|
|
#if __ASSERT
|
|
if (pEntity->GetIsTypeVehicle())
|
|
{
|
|
CVehicle* veh = (CVehicle*)pEntity;
|
|
Assert(!veh->GetIsInPopulationCache());
|
|
}
|
|
#endif
|
|
|
|
Assert(pEntity->GetBaseModelInfo() != NULL);
|
|
|
|
const bool alreadyInWorld = pEntity->GetIsRetainedByInteriorProxy() || pEntity->GetOwnerEntityContainer() != NULL;
|
|
Assertf( !alreadyInWorld, "Trying to add entity %s to the world twice", pEntity->GetModelName() );
|
|
if ( alreadyInWorld )
|
|
return;
|
|
|
|
DEV_BREAK_IF_FOCUS( CDebugScene::ShouldDebugBreakOnWorldAddOfFocusEntity(), pEntity );
|
|
#if __DEV
|
|
Vector3 vEntPosition = VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition());
|
|
#endif // __DEV
|
|
DEV_BREAK_ON_PROXIMITY( CDebugScene::ShouldDebugBreakOnProximityOfWorldAddCallingEntity(), vEntPosition );
|
|
DEV_BREAK_ON_PROXIMITY( pEntity->GetIsTypePed() && CDebugScene::ShouldDebugBreakOnProximityOfWorldAddCallingPed(), vEntPosition );
|
|
|
|
DebugTestsForAdd(pEntity);
|
|
|
|
bool bAddToInterior;
|
|
|
|
if ( pEntity->GetIsTypeDummyObject() && pEntity->m_iplIndex > 0 )
|
|
bAddToInterior = false;
|
|
else
|
|
bAddToInterior = interiorLocation.IsValid();
|
|
|
|
// objects in interiors aren't added to world sector lists - they are added to the CInteriorInst entity instead.
|
|
// the CInteriorInst entity does appear in the world sector list.
|
|
if (!bAddToInterior){
|
|
pEntity->Add();
|
|
Assert(!pEntity->m_nFlags.bInMloRoom);
|
|
} else {
|
|
pEntity->AddToInterior( interiorLocation );
|
|
Assert(pEntity->m_nFlags.bInMloRoom); // check that it went in!
|
|
}
|
|
|
|
// Need to cache fwInteriorLocation because it's unsafe to get it from the CEntity on the northaudioupdate thread
|
|
pEntity->SetAudioInteriorLocation(interiorLocation);
|
|
|
|
CGameWorld::PostAdd( pEntity, bIgnorePhysicsWorld );
|
|
|
|
#if __BANK
|
|
if(pEntity->GetIsTypePed() && NetworkInterface::IsNetworkOpen())
|
|
{
|
|
CPed* pPlayerPed = static_cast<CPed*>(pEntity);
|
|
|
|
CBaseModelInfo* pBaseModelInfo = pPlayerPed->GetBaseModelInfo();
|
|
CBuoyancyInfo* pBuoyancyInfo = pBaseModelInfo->GetBuoyancyInfo();
|
|
if(!Verifyf(pBuoyancyInfo && pBuoyancyInfo->m_WaterSamples, "Adding ped to world without water samples!"))
|
|
{
|
|
Displayf("[CGameWorld::Add() - end] %s", pBaseModelInfo->GetModelName());
|
|
Displayf("Local player num water samples: %d, water sample ptr: 0x%p", pBuoyancyInfo->m_nNumWaterSamples, pBuoyancyInfo->m_WaterSamples);
|
|
sysStack::PrintStackTrace();
|
|
}
|
|
}
|
|
#endif // __BANK
|
|
}
|
|
|
|
void CGameWorld::PostAdd(CEntity* pEntity, bool bIgnorePhysicsWorld)
|
|
{
|
|
ASSERT_ONLY( spdAABB worldAabb( Vec3V(WORLDLIMITS_XMIN, WORLDLIMITS_YMIN, WORLDLIMITS_ZMIN), Vec3V(WORLDLIMITS_XMAX, WORLDLIMITS_YMAX, WORLDLIMITS_ZMAX) ); )
|
|
Assertf( worldAabb.ContainsPoint( pEntity->GetTransform().GetPosition() ), "Adding an entity %s outside world limits (at %f,%f,%f)", pEntity->GetModelName(),
|
|
pEntity->GetTransform().GetPosition().GetXf(), pEntity->GetTransform().GetPosition().GetYf(), pEntity->GetTransform().GetPosition().GetZf() );
|
|
|
|
if(pEntity->RequiresProcessControl() && pEntity->GetIsDynamic())
|
|
{
|
|
//if(!((CPhysical *)pEntity)->GetIsOnSceneUpdate()){
|
|
{
|
|
((CDynamicEntity*) pEntity)->AddToSceneUpdate();
|
|
}
|
|
}
|
|
|
|
// GTA_RAGE_PHYSICS
|
|
if(!bIgnorePhysicsWorld)
|
|
pEntity->AddPhysics();
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// scenario spawn points
|
|
AddScenarioPointsForEntity(*pEntity, true);
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//ladders
|
|
CBaseModelInfo* pModelInfo = pEntity->GetBaseModelInfo();
|
|
if (pModelInfo->GetIsLadder() && (pEntity->GetIsTypeBuilding() || pEntity->GetIsTypeAnimatedBuilding()))
|
|
{
|
|
CTaskGoToAndClimbLadder::AddLadder(pEntity);
|
|
}
|
|
|
|
if(pEntity->GetIsTypePed())
|
|
{
|
|
CPed* pPed = static_cast<CPed*>(pEntity);
|
|
|
|
CSpatialArrayNode& node = pPed->GetSpatialArrayNode();
|
|
u32 typeFlags = pPed->FindDesiredSpatialArrayTypeFlags();
|
|
CPed::GetSpatialArray().Insert(node, typeFlags);
|
|
pPed->UpdateInSpatialArray();
|
|
|
|
pPed->UpdateCachedSceneUpdateFlags();
|
|
}
|
|
|
|
netObject *networkObject = NetworkUtils::GetNetworkObjectFromEntity(pEntity);
|
|
|
|
if(networkObject)
|
|
{
|
|
NetworkInterface::OnAddedToGameWorld(networkObject);
|
|
}
|
|
}
|
|
|
|
|
|
void CGameWorld::AddScenarioPointsForEntity(CEntity& entity, bool useOverrides)
|
|
{
|
|
if(entity.GetIsTypeDummyObject())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(IsEntityBeingAddedAndRemoved(&entity))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(useOverrides)
|
|
{
|
|
// Currently, only CObject and CBuilding objects can have overrides
|
|
// (see CScenarioEditor::IsEntityValidForAttachment()), so don't
|
|
// bother calling ApplyOverridesForEntity() for anything else.
|
|
if(entity.GetIsTypeObject() || entity.GetIsTypeBuilding())
|
|
{
|
|
if(SCENARIOPOINTMGR.ApplyOverridesForEntity(entity))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(entity.m_nFlags.bHasSpawnPoints)
|
|
{
|
|
if (entity.GetOwnerEntityContainer() && entity.GetOwnerEntityContainer()->GetOwnerSceneNode())
|
|
{
|
|
entity.GetOwnerEntityContainer()->GetOwnerSceneNode()->SetContainsSpawnPoints(true);
|
|
}
|
|
|
|
CBaseModelInfo* pModelInfo = entity.GetBaseModelInfo();
|
|
if(pModelInfo)
|
|
{
|
|
GET_2D_EFFECTS_NOLIGHTS(pModelInfo);
|
|
for (s32 j=0; j<iNumEffects; ++j)
|
|
{
|
|
C2dEffect* pEffect = (*pa2dEffects)[j];
|
|
if(pEffect->GetType() == ET_SPAWN_POINT)
|
|
{
|
|
CPedPopulation::AddScenarioPointToEntityFromEffect(pEffect, &entity);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Name : Remove
|
|
// Purpose : removes entity from world
|
|
// Parameters : entity
|
|
// Returns : none
|
|
|
|
void CGameWorld::Remove(CEntity* pEntity, bool bIgnorePhysicsWorld, bool bSkipPostRemove)
|
|
{
|
|
DEV_BREAK_IF_FOCUS( CDebugScene::ShouldDebugBreakOnWorldRemoveOfFocusEntity(), pEntity );
|
|
DEV_BREAK_ON_PROXIMITY( CDebugScene::ShouldDebugBreakOnProximityOfWorldRemoveCallingEntity(), VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition()) );
|
|
|
|
|
|
|
|
if (pEntity != ms_pEntityBeingRemovedAndAdded)
|
|
{
|
|
Assert(ms_pEntityBeingRemoved == NULL);
|
|
}
|
|
|
|
ms_pEntityBeingRemoved = pEntity;
|
|
|
|
#if __BANK
|
|
if ( PARAM_dynamicAddRemoveSpew.Get() && pEntity->GetIsDynamic() )
|
|
{
|
|
const char* debugName = ASSERT_ONLY( pEntity->GetIsPhysical() ? static_cast< CPhysical* >(pEntity)->GetDebugName() : ) "none";
|
|
worldDebugf1( "CGameWorld::Remove called on entity %p (model %s, debugname %s)", pEntity, pEntity->GetModelName(), debugName );
|
|
worldDebugf1( "Start stack trace" );
|
|
worldDebugf1StackTrace();
|
|
worldDebugf1( "End stack trace" );
|
|
}
|
|
|
|
// the MP carrier has a peculiar problem where the 2 hangars meet up
|
|
//if ( pLocalPlayer && (pLocalPlayer != m_pOwner) && pLocalPlayer->GetIsInInterior() && NetworkInterface::IsGameInProgress())
|
|
if (pEntity->GetIsTypePed())
|
|
{
|
|
CPed* pPed = static_cast<CPed*>(pEntity);
|
|
|
|
if (pPed->GetOwnedBy() == ENTITY_OWNEDBY_SCRIPT || pPed == CGameWorld::FindLocalPlayer())
|
|
{
|
|
static spdAABB carrierHangarVolumeDebugBox(Vec3V(3062.0f, -4686.0f, 7.0f), Vec3V(3073.0f, -4673.0f, 12.0f));
|
|
Vec3V pos = pPed->GetTransform().GetPosition();
|
|
if (carrierHangarVolumeDebugBox.ContainsPoint(pos))
|
|
{
|
|
const char* debugName = ASSERT_ONLY( pEntity->GetIsPhysical() ? static_cast< CPhysical* >(pEntity)->GetDebugName() : ) "none";
|
|
worldDebugf1( "<PT DEBUG> CGameWorld::Remove called on script ped in debug volume (model %s, debugname %s)", pEntity->GetModelName(), debugName );
|
|
worldDebugf1( "Start stack trace" );
|
|
worldDebugf1StackTrace();
|
|
worldDebugf1( "End stack trace" );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (pEntity->GetIsRetainedByInteriorProxy()){
|
|
CInteriorProxy::CleanupRetainedEntity(pEntity);
|
|
} else if ( pEntity->GetOwnerEntityContainer() )
|
|
{
|
|
if (pEntity->m_nFlags.bInMloRoom){
|
|
pEntity->RemoveFromInterior();
|
|
} else {
|
|
pEntity->Remove();
|
|
}
|
|
}
|
|
|
|
if ( !bSkipPostRemove )
|
|
CGameWorld::PostRemove( pEntity, bIgnorePhysicsWorld );
|
|
|
|
if( !bIgnorePhysicsWorld && pEntity->IsInPathServer() )
|
|
{
|
|
CPathServerGta::MaybeRemoveDynamicObject(pEntity);
|
|
}
|
|
|
|
Assertf(pEntity->m_nFlags.bInMloRoom == false, "Object %s : still in room", pEntity->GetModelName());
|
|
Assertf(pEntity->GetOwnerEntityContainer() == NULL, "Object %s : still in container", pEntity->GetModelName());
|
|
|
|
ms_pEntityBeingRemoved = NULL;
|
|
}
|
|
|
|
void CGameWorld::PostRemove(CEntity* pEntity, bool bIgnorePhysicsWorld, bool bTestScenarioPoints)
|
|
{
|
|
if(pEntity->GetIsTypePed())
|
|
{
|
|
CPed* pPed = static_cast<CPed*>(pEntity);
|
|
CSpatialArrayNode& node = pPed->GetSpatialArrayNode();
|
|
if(node.IsInserted())
|
|
{
|
|
CPed::GetSpatialArray().Remove(node);
|
|
}
|
|
}
|
|
|
|
if (pEntity->GetIsDynamic())
|
|
{
|
|
((CPhysical*) pEntity)->RemoveFromSceneUpdate();
|
|
}
|
|
|
|
if(!bIgnorePhysicsWorld)
|
|
{
|
|
if(pEntity->GetIsTypeBuilding()
|
|
|| pEntity->GetIsTypeAnimatedBuilding()
|
|
|| pEntity->GetIsTypeDummyObject())
|
|
{
|
|
pEntity->CEntity::RemovePhysics();
|
|
}
|
|
else
|
|
{
|
|
pEntity->RemovePhysics();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// scenario spawn points
|
|
// same logic should be here as well: CMapDataContents::Entities_PostRemove
|
|
if (bTestScenarioPoints && pEntity->m_nFlags.bHasSpawnPoints && !IsEntityBeingAddedAndRemoved(pEntity))
|
|
{
|
|
CPedPopulation::RemoveScenarioPointsFromEntity(pEntity);
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//ladders let remove any
|
|
CBaseModelInfo* pModelInfo = pEntity->GetBaseModelInfo();
|
|
if (pModelInfo->GetIsLadder() && (pEntity->GetIsTypeBuilding() || pEntity->GetIsTypeAnimatedBuilding()))
|
|
{
|
|
CTaskGoToAndClimbLadder::RemoveLadder(pEntity);
|
|
|
|
#if __BANK
|
|
CTaskClimbLadder::VerifyNoPedsClimbingDeletedLadder(pEntity);
|
|
#endif
|
|
}
|
|
|
|
#if __ASSERT
|
|
else
|
|
{
|
|
for(int i=0; i < CTaskGoToAndClimbLadder::ms_Ladders.GetCount(); i++)
|
|
{
|
|
if(CTaskGoToAndClimbLadder::ms_Ladders[i] == pEntity)
|
|
{
|
|
Assertf(0, "B* 527265, Call Robert Traffe please, model: %s", pEntity->GetModelName());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CGameWorld::RemoveAndAdd(CEntity* pEntity, fwInteriorLocation interiorLocation, bool bIgnorePhysicsWorld)
|
|
{
|
|
//Set the entity we are removing and adding.
|
|
ms_pEntityBeingRemovedAndAdded = pEntity;
|
|
|
|
//Remove the entity.
|
|
Remove(pEntity, bIgnorePhysicsWorld);
|
|
|
|
//Add the entity.
|
|
Add(pEntity, interiorLocation, bIgnorePhysicsWorld);
|
|
|
|
//Clear the entity being removed and added.
|
|
ms_pEntityBeingRemovedAndAdded = NULL;
|
|
}
|
|
|
|
bool CGameWorld::HasEntityBeenAdded(CEntity *pEntity)
|
|
{
|
|
bool bAddedToWorld = false;
|
|
|
|
if(pEntity && (pEntity->GetIsRetainedByInteriorProxy() || pEntity->GetOwnerEntityContainer() != NULL))
|
|
{
|
|
bAddedToWorld = true;
|
|
}
|
|
|
|
return bAddedToWorld;
|
|
}
|
|
|
|
#if __BANK
|
|
//
|
|
// name: CGameWorld::InitWidgets
|
|
// description:
|
|
void CGameWorld::AddWidgets(bkBank* pBank)
|
|
{
|
|
if (pBank){
|
|
// put this option into the optimization bank so the artists can see how much exterior peds/cars cose
|
|
pBank->PushGroup("Optimisation");
|
|
CEntity::AddPriorityWidget(pBank);
|
|
pBank->AddToggle("Freeze exterior objects", &ms_overrideFreezeFlags);
|
|
pBank->AddToggle("Make frozen entities invisible", &ms_makeFrozenEntitiesInvisible);
|
|
pBank->AddToggle("Update objects through animation update queue", &ms_updateObjectsThroughAnimQueue);
|
|
pBank->AddToggle("Update peds through animation update queue", &ms_updatePedsThroughAnimQueue);
|
|
pBank->AddToggle("Update vehicles through animation update queue", &ms_updateVehiclesThroughAnimQueue);
|
|
pBank->AddToggle("Busy-wait if anim queue exhausted", &ms_busyWaitIfAnimQueueExhausted);
|
|
pBank->PopGroup();
|
|
|
|
pBank->PushGroup("Debug");
|
|
pBank->AddToggle("Detailed out-of-map spew", &ms_detailedOutOrMapSpew );
|
|
pBank->AddToggle("Break on UpdateEntityDesc for selected entity", &ms_UpdateEntityDesc_StopOnSelectedEntity);
|
|
|
|
pBank->AddToggle("Log Clear Car From Area", &ms_bLogClearCarFromArea );
|
|
pBank->AddToggle("Log Clear Car From Area - Out Of Range", &ms_bLogClearCarFromArea_OutOfRange );
|
|
pBank->PopGroup();
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
void CGameWorld::ProcessPaused()
|
|
{
|
|
// Process
|
|
fwSceneUpdate::Update(SU_UPDATE_PAUSED);
|
|
|
|
#if GTA_REPLAY
|
|
if(CReplayMgr::IsEditModeActive())
|
|
{
|
|
//we still need to update this for the lod to work correctly during replay playback
|
|
if (const grcViewport *pViewport = gVpMan.CalcViewportForFragManager())
|
|
{
|
|
FRAGMGR->SetInterestFrustum(*pViewport, *gVpMan.GetFragManagerMatrix());
|
|
bgGlassManager::SetViewport(pViewport);
|
|
}
|
|
// Update any Replay controlled cloth regardless of paused state.
|
|
CPhysics::VerletSimPreUpdate(0.0f); // Sets camera etc for cloth LODing.
|
|
CReplayMgr::PreClothSimUpdate(CGameWorld::FindLocalPlayer());
|
|
CPhysics::VerletSimUpdate(clothManager::ReplayControlled);
|
|
}
|
|
#endif //GTA_REPLAY
|
|
}
|
|
|
|
PF_PAGE(GTAPedEvents, "Gta Ped Events");
|
|
PF_GROUP(Events);
|
|
PF_LINK(GTAPedEvents, Events);
|
|
PF_VALUE_INT(Total, Events);
|
|
PF_VALUE_INT(AddedToGlobal, Events);
|
|
PF_VALUE_INT(FocusPedTotal, Events);
|
|
|
|
|
|
void CGameWorld::ProcessGlobalEvents()
|
|
{
|
|
// update SHOCKING events first, as some SHOCKING events need to be checked to see if they
|
|
// need reacting to this frame or not, and its cheaper to check that once than for every ped!
|
|
//CShockingEventsManager::Update();
|
|
|
|
if(GetEventGlobalGroup()->GetNumEvents())
|
|
{
|
|
//Add global events to each ped's local event list before we flush them
|
|
fwSceneUpdate::Update(SU_ADD_SHOCKING_EVENTS);
|
|
}
|
|
|
|
//Add global events to the script AI queue if they are of interest to the scripts
|
|
GetEventGlobalGroup()->AddEventsToScriptGroup();
|
|
|
|
ProcessVehicleEvents();
|
|
|
|
CInformFriendsEventQueue::Process();
|
|
CInformGroupEventQueue::Process();
|
|
|
|
PF_SET(AddedToGlobal, GetEventGlobalGroup()->GetNumEvents() );
|
|
PF_SET(Total, CEvent::GetPool()->GetNoOfUsedSpaces() );
|
|
|
|
// need to call this even if we're skipping or events will hang around
|
|
GetEventGlobalGroup()->ProcessAndFlush();
|
|
}
|
|
|
|
void CGameWorld::ProcessVehicleEvents()
|
|
{
|
|
PF_FUNC(ProcessVehicleEvents);
|
|
CVehicle::Pool* pool = CVehicle::GetPool();
|
|
s32 i = (s32) pool->GetSize();
|
|
while(i--)
|
|
{
|
|
CVehicle* pVehicle = pool->GetSlot(i);
|
|
if(pVehicle)
|
|
{
|
|
if (1)//(CVehicleAILodManager::ShouldDoFullUpdate(*pVehicle))
|
|
{
|
|
GetEventGlobalGroup()->AddEventsToVehicle(pVehicle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGameWorld::StartAnimationUpdate()
|
|
{
|
|
// TODO: Check if we can get away without this, hopefully we would have gone
|
|
// through the queue on the last frame, and shouldn't have inserted anything else
|
|
// yet.
|
|
fwAnimDirector::GetPrePhysicsQueue().Clear();
|
|
|
|
// Set the animdirector as being locked (prior to the multi threaded update)
|
|
// You cannot do any of the following during this process :
|
|
// Get or set the local matrices of any skeleton (with an animation director)
|
|
// Remove any dynamic entity (with an animation director)
|
|
fwAnimDirectorComponentMotionTree::SetLockedGlobal(true, fwAnimDirectorComponent::kPhasePrePhysics);
|
|
|
|
// Update the synchronized scene phases before the anim buffer is flipped as part of the scene update
|
|
fwAnimDirectorComponentSyncedScene::UpdateSyncedScenes(fwTimer::GetTimeStep());
|
|
|
|
// Start anim pre physics update
|
|
fwSceneUpdate::Update(SU_START_ANIM_UPDATE_PRE_PHYSICS);
|
|
|
|
fwSceneUpdate::Update(SU_START_ANIM_UPDATE_PRE_PHYSICS_PASS2);
|
|
}
|
|
|
|
void CGameWorld::EndAnimationUpdate()
|
|
{
|
|
// Block the main thread until the animation threads have completed
|
|
fwAnimDirector::WaitForAllUpdatesToComplete();
|
|
|
|
// End the anim pre physics update
|
|
fwSceneUpdate::Update(SU_END_ANIM_UPDATE_PRE_PHYSICS);
|
|
|
|
// Set the animdirector as being unlocked (post the multi threaded update)
|
|
fwAnimDirectorComponentMotionTree::SetLockedGlobal(false, fwAnimDirectorComponent::kPhaseNone);
|
|
|
|
// Update the motion blur processing for event tags
|
|
CClipEventTags::CMotionBlurEventTag::Update();
|
|
}
|
|
|
|
void CGameWorld::UpdateEntitiesThroughAnimQueue(SceneUpdateParameters& params)
|
|
{
|
|
PF_FUNC(UpdateThroughAnimQueue);
|
|
|
|
#if __STATS
|
|
int numStalls = 0;
|
|
#endif // __STATS
|
|
|
|
fwAnimDirector::Queue& queue = fwAnimDirector::GetPrePhysicsQueue();
|
|
while(1)
|
|
{
|
|
CEntity* pEntity;
|
|
#if !__FINAL
|
|
if(!ms_busyWaitIfAnimQueueExhausted)
|
|
{
|
|
#endif // !__FINAL
|
|
PF_PUSH_TIMEBAR_BUDGETED("UpdateThroughAnimQueue_Stall - Pop", 1.0f);
|
|
PF_START_EXTRA_MARKER(UpdateThroughAnimQueue_Stall);
|
|
pEntity = static_cast<CEntity*>(queue.Pop());
|
|
PF_STOP_EXTRA_MARKER(UpdateThroughAnimQueue_Stall);
|
|
PF_POP_TIMEBAR(); // "UpdateThroughAnimQueue_Stall"
|
|
|
|
// Would be nice to do this if we stalled:
|
|
// numStalls++;
|
|
// but unfortunately the call to Pop() doesn't tell us.
|
|
#if !__FINAL
|
|
}
|
|
else
|
|
{
|
|
#if __STATS
|
|
bool stalled = false;
|
|
#endif // __STATS
|
|
PF_PUSH_TIMEBAR("UpdateThroughAnimQueue_Stall");
|
|
PF_START_EXTRA_MARKER(UpdateThroughAnimQueue_Stall);
|
|
while(1)
|
|
{
|
|
bool queueIsEmpty = false;
|
|
pEntity = static_cast<CEntity*>(queue.PopIfAvailable(queueIsEmpty));
|
|
if(pEntity)
|
|
{
|
|
Assert(!queueIsEmpty); // Not expected if we got an entity back.
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if(queueIsEmpty)
|
|
{
|
|
break;
|
|
}
|
|
|
|
#if __STATS
|
|
stalled = true;
|
|
#endif // __STATS
|
|
|
|
// May be best to at least try to do some nops here so we don't slow
|
|
// down the other threads too much by overloading the bus, eating CPU
|
|
// cycles is bad enough.
|
|
#if __PS3
|
|
for(int i = 0; i < 64; i++)
|
|
{
|
|
__nop();
|
|
}
|
|
#endif // !__PS3
|
|
// Could try to do some useful processing here, if we had something to do...
|
|
}
|
|
}
|
|
PF_STOP_EXTRA_MARKER(UpdateThroughAnimQueue_Stall);
|
|
PF_POP_TIMEBAR(); // "UpdateThroughAnimQueue_Stall"
|
|
|
|
#if __STATS
|
|
if(stalled)
|
|
{
|
|
numStalls++;
|
|
}
|
|
#endif // __STATS
|
|
}
|
|
#endif // !__FINAL
|
|
|
|
if(!pEntity)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// If this entity shouldn't currently be updated through the queue, skip it.
|
|
// fwAnimDirector doesn't know which entities to push into the queue, so this
|
|
// check is needed unless we want all animated entities to use the queue
|
|
// (probably true in the final game, but not if we want to support toggling
|
|
// the updates during development).
|
|
if(!pEntity->GetUpdatingThroughAnimQueue())
|
|
{
|
|
continue;
|
|
}
|
|
|
|
{ PF_AUTO_PUSH_TIMEBAR("SceneUpdateUpdateCb");
|
|
SceneUpdateUpdateCb(*pEntity, (void*)¶ms);
|
|
}
|
|
|
|
if(!ShouldUpdateThroughAnimQueue(*pEntity))
|
|
{
|
|
PF_PUSH_TIMEBAR("StopUpdatingThroughAnimQueue");
|
|
Assert(pEntity->GetIsDynamic());
|
|
static_cast<CDynamicEntity*>(pEntity)->StopUpdatingThroughAnimQueue();
|
|
PF_POP_TIMEBAR();
|
|
}
|
|
}
|
|
|
|
PF_SET(UpdateThroughAnimQueue_StallCount, numStalls);
|
|
}
|
|
|
|
void CGameWorld::StartPostCameraAnimationUpdate()
|
|
{
|
|
CAnimSceneManager::Update(kAnimSceneUpdatePostCamera);
|
|
// Determine if we can trust CDynamicEntity::GetIsVisibleInSomeViewportThisFrame() during this update.
|
|
// This is lagging one frame behind for the upcoming fraction of the frame, but this is mostly a problem
|
|
// when a camera cut is done, so that's what we are basing it on.
|
|
const bool cameraCut = camInterface::GetFrame().GetFlags().IsFlagSet(camFrame::Flag_HasCutPosition | camFrame::Flag_HasCutOrientation);
|
|
CDynamicEntity::SetCanUseCachedVisibilityThisFrame(!cameraCut);
|
|
|
|
fwSceneUpdate::Update(SU_START_ANIM_UPDATE_POST_CAMERA);
|
|
}
|
|
|
|
void CGameWorld::EndPostCameraAnimationUpdate()
|
|
{
|
|
// Block the main thread until the animation threads have completed
|
|
fwAnimDirector::WaitForAllUpdatesToComplete();
|
|
|
|
// Set the animblender as being unlocked (post the multi threaded update)
|
|
fwAnimDirectorComponentMotionTree::SetLockedGlobal(false, fwAnimDirectorComponent::kPhaseNone);
|
|
}
|
|
|
|
bool CGameWorld::WaitingForCollision( CEntity* pEntity )
|
|
{
|
|
if(pEntity->GetIsTypePed() && pEntity->IsProtectedBaseFlagSet(fwEntity::IS_FIXED_UNTIL_COLLISION) )
|
|
{
|
|
CPed* pPed = static_cast<CPed*>(pEntity);
|
|
if( pPed->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ) && pPed->GetMyVehicle() )
|
|
{
|
|
if( pPed->GetMyVehicle()->IsProtectedBaseFlagSet(fwEntity::IS_FIXED_UNTIL_COLLISION) && !pPed->GetMyVehicle()->IsRunningCarRecording() )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CGameWorld::OverrideFreezeFlags(bool bOverride)
|
|
{
|
|
ms_overrideFreezeFlags = bOverride;
|
|
}
|
|
|
|
|
|
// this is a moderately Dodgy way to decouple fwWorldMgr from CEntity (which is used by IntersectingCB)
|
|
void CGameWorld::ForAllEntitiesIntersecting(fwSearchVolume* pColVol, IntersectingCB cb, void* data, s32 typeFlags, s32 locationFlags, s32 lodFlags, s32 optionFlags, u32 module) {
|
|
CGameWorld::ForAllEntitiesIntersecting(pColVol, (fwIntersectingCB)cb, data, typeFlags, locationFlags, lodFlags, optionFlags, module);
|
|
}
|
|
|
|
void CGameWorld::ForAllEntitiesIntersecting(fwSearchVolume* pColVol, fwIntersectingCB cb, void* data, s32 typeFlags, s32 locationFlags, s32 lodFlags, s32 optionFlags, u32 module)
|
|
{
|
|
const int dynamicEntityTypes =
|
|
ENTITY_TYPE_MASK_ANIMATED_BUILDING |
|
|
ENTITY_TYPE_MASK_VEHICLE |
|
|
ENTITY_TYPE_MASK_PED |
|
|
ENTITY_TYPE_MASK_OBJECT;
|
|
|
|
if ( !( typeFlags & ~dynamicEntityTypes ) )
|
|
optionFlags |= SEARCH_OPTION_DYNAMICS_ONLY;
|
|
|
|
gWorldMgr.ForAllEntitiesIntersecting(pColVol, cb, data, typeFlags, locationFlags, lodFlags, optionFlags, module);
|
|
}
|
|
|
|
// name: FindEntitiesCB
|
|
// description: Find entity callback used by intersecting functions
|
|
bool CGameWorld::FindEntitiesCB(fwEntity* pEntity, void* data)
|
|
{
|
|
CFindData* pFindData = (CFindData*)data;
|
|
return pFindData->AddEntity(static_cast<CEntity*>(pEntity));
|
|
}
|
|
|
|
//
|
|
// name: FindEntitiesCB
|
|
// description: Find entity callback used by intersecting functions
|
|
bool CGameWorld::FindEntitiesOfTypeCB(fwEntity* pEntity, void* data)
|
|
{
|
|
CFindData* pFindData = (CFindData*)data;
|
|
if(pEntity->GetModelIndex() == static_cast<s32>(pFindData->GetData()))
|
|
{
|
|
return pFindData->AddEntity(static_cast<CEntity*>(pEntity));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// name: FindEntitiesCB
|
|
// description: Find entity callback used by intersecting functions
|
|
bool CGameWorld::FindMissionEntitiesCB(fwEntity* pFWEntity, void* data)
|
|
{
|
|
CEntity* pEntity = static_cast<CEntity*>(pFWEntity);
|
|
|
|
if(pEntity->GetIsTypeVehicle() ||
|
|
pEntity->GetIsTypePed() ||
|
|
pEntity->GetIsTypeObject())
|
|
{
|
|
CFindData* pFindData = (CFindData*)data;
|
|
return pFindData->AddEntity(pEntity);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// name: CGameWorld::SetFreezeFlags
|
|
// description: Set the peds and vehicle freeze flags
|
|
void CGameWorld::SetFreezeFlags()
|
|
{
|
|
CInteriorInst* pInterior = CPortalVisTracker::GetPrimaryInteriorInst();
|
|
s32 roomIdx = CPortalVisTracker::GetPrimaryRoomIdx();
|
|
if(pInterior && (roomIdx > 0) && !NetworkInterface::IsGameInProgress() && ms_overrideFreezeFlags == false)
|
|
{
|
|
CMloModelInfo* pMloModelInfo = pInterior->GetMloModelInfo();
|
|
u32 roomFlags = 0;
|
|
|
|
worldAssert(pMloModelInfo->GetIsStreamType());
|
|
|
|
roomFlags = pMloModelInfo->GetRoomFlags(roomIdx);
|
|
|
|
bool bFreeze = (roomFlags & (ROOM_FREEZEPEDS | ROOM_FREEZEVEHICLES)) != 0;
|
|
|
|
if(bFreeze)
|
|
{
|
|
s32 num = pInterior->GetNumPortalsInRoom(0);
|
|
s32 i;
|
|
|
|
// only do this if portals ardebe instanced
|
|
if(pInterior->m_bIsPopulated)
|
|
{
|
|
// go through all the portals in room 0 (all the exterior portals).
|
|
for(i=0; i<num; i++)
|
|
{
|
|
if(pInterior->IsLinkPortal(0, i))
|
|
continue;
|
|
// If portal has no closed door objects attached or any of the doors attached
|
|
// are open then set it can see outside
|
|
s32 numDoors=0;
|
|
s32 numClosedDoors=0;
|
|
s32 globalPortalIdx = pInterior->GetPortalIdxInRoom(0, i);
|
|
for(u32 j=0; j<pInterior->GetNumObjInPortal(globalPortalIdx); j++)
|
|
{
|
|
CEntity* pEntity = pInterior->GetPortalObjInstancePtr(0, i, j);
|
|
if(pEntity && pEntity->GetIsTypeObject())
|
|
{
|
|
CObject* pObject = (CObject*)pEntity;
|
|
if(pObject->IsADoor())
|
|
{
|
|
CDoor * pDoor = static_cast<CDoor*>(pObject);
|
|
numDoors++;
|
|
if(pDoor->IsDoorShut())
|
|
numClosedDoors++;
|
|
}
|
|
}
|
|
}
|
|
// if some of the doors aren't closed then don't freeze
|
|
if(numDoors != numClosedDoors)
|
|
break;
|
|
}
|
|
if(i == num)
|
|
{
|
|
ms_freezeExteriorPeds = (roomFlags & ROOM_FREEZEPEDS) != 0;
|
|
ms_freezeExteriorVehicles = (roomFlags & ROOM_FREEZEVEHICLES) != 0;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ms_freezeExteriorPeds = ms_overrideFreezeFlags;
|
|
ms_freezeExteriorVehicles = ms_overrideFreezeFlags;
|
|
|
|
#if __ASSERT
|
|
if(ms_overrideFreezeFlags)
|
|
Assertf(GetMainPlayerInfo()->AreControlsDisabled() || FindLocalPlayer()->m_nFlags.bInMloRoom, "ms_overrideFreezeFlags has not been reset by script");
|
|
#endif
|
|
}
|
|
|
|
bool CGameWorld::IsEntityFrozen(CEntity* pEntity) { return ((CDynamicEntity*)pEntity)->m_nDEflags.bFrozen; }
|
|
|
|
//
|
|
// name: CGameWorld::IsEntityFrozen
|
|
// description: Return if an entity is frozen
|
|
bool CGameWorld::CheckEntityFrozenByInterior(CEntity* pEntity)
|
|
{
|
|
bool bFreeze = false;
|
|
|
|
if(pEntity->GetIsTypePed())
|
|
{
|
|
CPed* pPed = (CPed*)pEntity;
|
|
if(!pEntity->GetIsInInterior())
|
|
{
|
|
// if ped isn't in an interior, count down timer that stops them being frozen
|
|
if(pPed->GetInteriorFreezeWasInsideCounter() > 0)
|
|
{
|
|
if(pPed->GetInteriorFreezeWasInsideCounter() > fwTimer::GetTimeStepInMilliseconds())
|
|
pPed->SetInteriorFreezeWasInsideCounter( pPed->GetInteriorFreezeWasInsideCounter() - (u16)fwTimer::GetTimeStepInMilliseconds() );
|
|
else
|
|
pPed->SetInteriorFreezeWasInsideCounter(0);
|
|
}
|
|
|
|
if(ms_freezeExteriorPeds)
|
|
{
|
|
// Block freezing of mission peds and cops
|
|
const bool bBlockPedFreezing = pPed->PopTypeIsMission() || pPed->GetPedType() == PEDTYPE_COP || pPed->GetInteriorFreezeWasInsideCounter() REPLAY_ONLY(|| CReplayMgr::IsReplayInControlOfWorld());
|
|
if(!bBlockPedFreezing )
|
|
{
|
|
bFreeze = true;
|
|
}
|
|
}
|
|
}
|
|
// set a timer so when peds run out from inside interiors they don't get frozen in the doorway
|
|
else
|
|
pPed->SetInteriorFreezeWasInsideCounter(10000);
|
|
}
|
|
else if(pEntity->GetIsTypeVehicle())
|
|
{
|
|
CVehicle* pVehicle = (CVehicle*)pEntity;
|
|
if(ms_freezeExteriorVehicles && !pEntity->GetIsInInterior())
|
|
{
|
|
|
|
// Block freezing of mission vehicles and cop cars or cars driver by player
|
|
bool bBlockVehicleFreezing = pVehicle->PopTypeIsMission() || pVehicle->IsLawEnforcementVehicle() || (pVehicle->GetVehicleType() == VEHICLE_TYPE_HELI) || (pVehicle->GetVehicleType() == VEHICLE_TYPE_PLANE);
|
|
|
|
if (!bBlockVehicleFreezing)
|
|
{
|
|
for(s32 i = 0; i < MAX_VEHICLE_SEATS; i++)
|
|
{
|
|
//Ensure the ped is valid.
|
|
CPed* pPed = pVehicle->GetSeatManager()->GetPedInSeat(i);
|
|
if(!pPed)
|
|
{
|
|
continue;
|
|
}
|
|
if (pPed->PopTypeIsMission() || pPed->IsPlayer())
|
|
{
|
|
bBlockVehicleFreezing = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if(!bBlockVehicleFreezing )
|
|
{
|
|
bFreeze = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bFreeze)
|
|
{
|
|
Assert(!NetworkInterface::IsGameInProgress());
|
|
}
|
|
|
|
return bFreeze;
|
|
}
|
|
|
|
|
|
void CGameWorld::ClearFrozenEntitiesLists()
|
|
{
|
|
ms_frozenEntities.Reset();
|
|
ms_unfrozenEntities.Reset();
|
|
}
|
|
|
|
void CGameWorld::FlushFrozenEntitiesLists()
|
|
{
|
|
for (int i = 0; i < ms_frozenEntities.GetCount(); ++i)
|
|
{
|
|
CPhysical* pPhysical = ms_frozenEntities[i];
|
|
|
|
pPhysical->SetFixedPhysics(true);
|
|
pPhysical->m_nDEflags.bFrozenByInterior = true;
|
|
pPhysical->RemoveSceneUpdateFlags(CGameWorld::SU_PRESIM_PHYSICS_UPDATE);
|
|
|
|
// Update the cached scene update flags if we are a ped, since we messed with SU_PRESIM_PHYSICS_UPDATE.
|
|
if(pPhysical->GetIsTypePed())
|
|
{
|
|
static_cast<CPed*>(pPhysical)->UpdateCachedSceneUpdateFlags();
|
|
}
|
|
|
|
if ( ms_makeFrozenEntitiesInvisible )
|
|
pPhysical->SetIsVisibleForModule( SETISVISIBLE_MODULE_WORLD, false, true );
|
|
}
|
|
|
|
for (int i = 0; i < ms_unfrozenEntities.GetCount(); ++i)
|
|
{
|
|
CPhysical* pPhysical = ms_unfrozenEntities[i];
|
|
|
|
pPhysical->SetFixedPhysics(false);
|
|
pPhysical->m_nDEflags.bFrozenByInterior = false;
|
|
pPhysical->AddSceneUpdateFlags(CGameWorld::SU_PRESIM_PHYSICS_UPDATE);
|
|
|
|
// Update the cached scene update flags if we are a ped, since we messed with SU_PRESIM_PHYSICS_UPDATE.
|
|
if(pPhysical->GetIsTypePed())
|
|
{
|
|
static_cast<CPed*>(pPhysical)->UpdateCachedSceneUpdateFlags();
|
|
}
|
|
|
|
if ( ms_makeFrozenEntitiesInvisible )
|
|
pPhysical->SetIsVisibleForModule( SETISVISIBLE_MODULE_WORLD, true, true );
|
|
}
|
|
}
|
|
|
|
void CGameWorld::UpdateLensFlareAfterPlayerSwitch(const CPed* newPlayerPed)
|
|
{
|
|
//update the character specific lens flare
|
|
u32 newPedModelHash = newPlayerPed->GetPedModelInfo()->GetModelNameHash();
|
|
|
|
//if the model is the same has hash we don't need to update the lens flare
|
|
if(newPedModelHash != m_LastLensFlareModelHash)
|
|
{
|
|
if(newPedModelHash == MI_PLAYERPED_PLAYER_ZERO.GetName())
|
|
{
|
|
g_LensFlare.ChangeSettings(0); // Micheal
|
|
}
|
|
else if(newPedModelHash == MI_PLAYERPED_PLAYER_ONE.GetName())
|
|
{
|
|
g_LensFlare.ChangeSettings(1); // Franklin
|
|
}
|
|
else if(newPedModelHash == MI_PLAYERPED_PLAYER_TWO.GetName())
|
|
{
|
|
g_LensFlare.ChangeSettings(2); // Trevor
|
|
}
|
|
else
|
|
{
|
|
g_LensFlare.TransitionSettingsRandom(); // Multiplayer
|
|
}
|
|
|
|
m_LastLensFlareModelHash = newPedModelHash;
|
|
}
|
|
}
|
|
|
|
bool CGameWorld::ShouldUpdateThroughAnimQueue(const CEntity& entity)
|
|
{
|
|
// TODO: probably inline in the header file if it doesn't require new dependencies.
|
|
return (ms_updateObjectsThroughAnimQueue && entity.GetIsTypeObject())
|
|
|| (ms_updatePedsThroughAnimQueue && entity.GetIsTypePed())
|
|
|| (ms_updateVehiclesThroughAnimQueue && entity.GetIsTypeVehicle());
|
|
}
|
|
|
|
//
|
|
// name: CGameWorld::UpdateEntityFrozenByInterior
|
|
// description: Set an entity physics to fixed if it is frozen by the camera being in an interior and set an entity to be active
|
|
// if it is fixed and not frozen by an interior
|
|
void CGameWorld::UpdateEntityFrozenByInterior(CEntity* pEntity)
|
|
{
|
|
//Don't add trains to the frozen entities list as we never want to unfreeze them.
|
|
if(pEntity->GetIsPhysical() && !(pEntity->GetIsTypeVehicle() && static_cast<CVehicle*>(pEntity)->GetVehicleType() == VEHICLE_TYPE_TRAIN))
|
|
{
|
|
CPhysical* pPhysical = (CPhysical*)pEntity;
|
|
|
|
// If entity is frozen and physics hasn't been fixed already then fix physics and set frozen by interior flag
|
|
if(CheckEntityFrozenByInterior(pPhysical))
|
|
{
|
|
|
|
if(!pPhysical->GetIsAnyFixedFlagSet() || pPhysical->IsVisible())
|
|
ms_frozenEntities.Grow() = pPhysical;
|
|
}
|
|
// otherwise if frozen by an interior activate again and clear frozen flag
|
|
else
|
|
{
|
|
if(pPhysical->m_nDEflags.bFrozenByInterior)
|
|
ms_unfrozenEntities.Grow() = pPhysical;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Name : Process
|
|
// Purpose : processes moving entities
|
|
// Parameters : none
|
|
// Returns : none
|
|
//
|
|
float WORLD_PLAYER_SHIFT_DAMPING = (0.707f);
|
|
#define MAX_ENTITIES_TO_DELETE_PER_FRAME (512)
|
|
#define MAX_ENTITIES_TO_REMOVE_FROM_PROCCONT_LIST (16)
|
|
//
|
|
#if !__FINAL
|
|
CEntity* CGameWorld::ms_pProcessingEntity = NULL;
|
|
#endif
|
|
|
|
#if !__NO_OUTPUT
|
|
bool CGameWorld::ms_bLogClearCarFromArea = false;
|
|
bool CGameWorld::ms_bLogClearCarFromArea_OutOfRange = false;
|
|
#endif // !__NO_OUTPUT
|
|
|
|
#if RAGE_TIMEBARS
|
|
static void Entity_PF_Start(CEntity *pEntity)
|
|
{
|
|
if(pEntity->GetIsTypePed())
|
|
PF_START(Ped_ProcCont);
|
|
else if(pEntity->GetIsTypeVehicle())
|
|
{
|
|
CVehicle* pVeh = static_cast<CVehicle*>(pEntity);
|
|
if(!pVeh->IsDummy())
|
|
{
|
|
PF_START(Veh_ProcCont);
|
|
}
|
|
else
|
|
{
|
|
PF_START(DummyVeh_ProcCont);
|
|
}
|
|
}
|
|
else
|
|
PF_START(Obj_ProcCont);
|
|
}
|
|
|
|
static void Entity_PF_Stop(CEntity *pEntity)
|
|
{
|
|
if(pEntity->GetIsTypePed())
|
|
{
|
|
PF_STOP(Ped_ProcCont);
|
|
}
|
|
else if(pEntity->GetIsTypeVehicle())
|
|
{
|
|
CVehicle* pVeh = static_cast<CVehicle*>(pEntity);
|
|
if(!pVeh->IsDummy())
|
|
{
|
|
PF_STOP(Veh_ProcCont);
|
|
}
|
|
else
|
|
{
|
|
PF_STOP(DummyVeh_ProcCont);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PF_STOP(Obj_ProcCont);
|
|
}
|
|
}
|
|
|
|
#else
|
|
#define Entity_PF_Start(pEntity) /* No op*/
|
|
#define Entity_PF_Stop(pEntity) /* No op*/
|
|
#endif // RAGE_TIMEBARS
|
|
|
|
#if __BANK
|
|
|
|
extern bool s_bPreventPlayerProcessForTest; // Debug.cpp
|
|
|
|
static bool debugBlockProcessControlOfEntity(CEntity *pEntity)
|
|
{
|
|
bool debugBlockProcessControlOfEntity = false;
|
|
if(pEntity != FindPlayerPed())
|
|
{
|
|
const CEntity* pFocusEntity0 = CDebugScene::FocusEntities_Get(0);
|
|
const bool isFocus0 = pFocusEntity0 && (pFocusEntity0 == pEntity);
|
|
const bool sameTypeAsFocus0 = pFocusEntity0 && (pFocusEntity0->GetType() == pEntity->GetType());
|
|
|
|
if(CDebugScene::ShouldStopProcessCtrlAllEntities())
|
|
{
|
|
debugBlockProcessControlOfEntity = true;
|
|
}
|
|
else if(CDebugScene::ShouldStopProcessCtrlAllExceptFocusEntity0())
|
|
{
|
|
if(!isFocus0)
|
|
{
|
|
debugBlockProcessControlOfEntity = true;
|
|
}
|
|
}
|
|
else if(CDebugScene::ShouldStopProcessCtrlAllEntitiesOfFocus0Type())
|
|
{
|
|
if(sameTypeAsFocus0)
|
|
{
|
|
debugBlockProcessControlOfEntity = true;
|
|
}
|
|
}
|
|
else if(CDebugScene::ShouldStopProcessCtrlAllEntitiesOfFocus0TypeExceptFocus0())
|
|
{
|
|
if(sameTypeAsFocus0 && !isFocus0)
|
|
{
|
|
debugBlockProcessControlOfEntity = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(s_bPreventPlayerProcessForTest)
|
|
{
|
|
debugBlockProcessControlOfEntity = true;
|
|
}
|
|
|
|
}
|
|
|
|
return debugBlockProcessControlOfEntity;
|
|
}
|
|
#else
|
|
#define debugBlockProcessControlOfEntity(entity) (false)
|
|
#endif // __BANK
|
|
|
|
#if __BANK
|
|
namespace rage
|
|
{
|
|
extern bool g_DisplaySceneUpdateListByType;
|
|
}
|
|
#endif // __BANK
|
|
|
|
#if __STATS
|
|
EXT_PF_TIMER(DeferredAi_PedsVehiclesObjects);
|
|
#endif // __STATS
|
|
|
|
void CGameWorld::Process()
|
|
{
|
|
#if __BANK
|
|
// Let the scene update system know about the selected entity.
|
|
// This information is not available to RAGE.
|
|
fwSceneUpdate::SetSelectedEntity(g_PickerManager.GetSelectedEntity());
|
|
#endif // __BANK
|
|
|
|
// CObject* pObj;
|
|
|
|
PF_START_TIMEBAR("World Process");
|
|
|
|
#if GTA_REPLAY
|
|
// Read before pause test, so we reset regardless
|
|
bool bForceAnimUpdateWhenPaused = CCompEntity::ShouldForceAnimUpdateWhenPaused();
|
|
CCompEntity::ResetForceAnimUpdateWhenPaused();
|
|
#endif
|
|
|
|
if(fwTimer::IsGamePaused())
|
|
{
|
|
ProcessPaused();
|
|
|
|
#if GTA_REPLAY
|
|
if(bForceAnimUpdateWhenPaused)
|
|
{
|
|
StartAnimationUpdate();
|
|
EndAnimationUpdate();
|
|
}
|
|
#endif //GTA_REPLAY
|
|
|
|
return;
|
|
}
|
|
#if __BANK
|
|
camManager::ResetDebugVariables();
|
|
#endif // __BANK
|
|
|
|
SetFreezeFlags();
|
|
|
|
CExpensiveProcessDistributer::Update();
|
|
|
|
CShockingEventScanner::UpdateClass();
|
|
|
|
PF_START(Proc_Global_Events);
|
|
ProcessGlobalEvents();
|
|
PF_STOP(Proc_Global_Events);
|
|
|
|
ClearFrozenEntitiesLists();
|
|
|
|
//******************************************************************
|
|
// Anim update (prior to the camera update)
|
|
// Updates the animation blenders on dynamic entities
|
|
// All dynamic entities will have their mover updated
|
|
// Dynamic entities that are visible in any of the active viewports
|
|
// will have their skeleton updated
|
|
//******************************************************************
|
|
|
|
PF_START_TIMEBAR_BUDGETED ("Animation",2.0f);
|
|
PF_START(Proc_Animation);
|
|
|
|
StartAnimationUpdate();
|
|
|
|
PF_STOP(Proc_Animation);
|
|
|
|
TUNE_BOOL(bBlockAsyncAnimUpdate, false)
|
|
if (bBlockAsyncAnimUpdate)
|
|
{
|
|
EndAnimationUpdate();
|
|
}
|
|
|
|
FlushFrozenEntitiesLists();
|
|
|
|
// Collects Los results from CGameWorldProbeAsync, used for async ped<->ped line tests
|
|
CPedGeometryAnalyser::Process();
|
|
|
|
//! Update special ability manager before all entities start processing input.
|
|
CPlayerSpecialAbilityManager::UpdateSpecialAbilityTrigger(FindLocalPlayer());
|
|
|
|
PF_PUSH_TIMEBAR_BUDGETED("AI Update",NetworkInterface::IsGameInProgress()?6.0f:7.5f);
|
|
|
|
// Add the entity to a removal list rather than deleting it directly.
|
|
// Deleting directly during this process can cause crashes.
|
|
RegdEnt apEntitiesToDelete[MAX_ENTITIES_TO_DELETE_PER_FRAME];
|
|
s32 iNumEntitiesToDelete = 0;
|
|
|
|
// some object may need to be removed from to the process control list instead of processing them
|
|
RegdDyn apEntitiesToRemoveFromProcContList[MAX_ENTITIES_TO_REMOVE_FROM_PROCCONT_LIST];
|
|
s32 iNumEntitiesToRemoveFromProcCont = 0;
|
|
|
|
#if __DEV
|
|
static bool bPrintProcessList = false;
|
|
#endif
|
|
bool prevAllowDelete = ms_bAllowDelete;
|
|
AllowDelete(false);
|
|
|
|
SceneUpdateParameters params;
|
|
params.apEntitiesToDelete = apEntitiesToDelete;
|
|
params.iNumEntitiesToDelete = iNumEntitiesToDelete;
|
|
params.apEntitiesToRemoveFromProcContList = apEntitiesToRemoveFromProcContList;
|
|
params.iNumEntitiesToRemoveFromProcCont = iNumEntitiesToRemoveFromProcCont;
|
|
|
|
#if __DEV
|
|
params.bPrintProcessList = bPrintProcessList;
|
|
#endif // __DEV
|
|
|
|
// 8/7/12 - cthomas - We know only the main thread would possibly be updating physics at this
|
|
// point in time, so set the flag in the physics system to indicate that. This is an optimization
|
|
// that will allow us to skip some locking overhead in the physics system as we update the locations
|
|
// of objects during the AI update - low lod vehicles, for instance, update their location during
|
|
// their AI update.
|
|
PHLEVEL->SetOnlyMainThreadIsUpdatingPhysics(true);
|
|
PF_START(GameWorldPedsAndObjects);
|
|
|
|
CAnimSceneManager::Update(kAnimSceneUpdatePreAi);
|
|
|
|
#if RSG_ORBIS
|
|
Water::UpdateCachedWorldBaseCoords();
|
|
#endif // RSG_ORBIS
|
|
|
|
#if __BANK
|
|
if (g_DisplaySceneUpdateListByType)
|
|
{
|
|
PF_FUNC(SU_UPDATE);
|
|
|
|
PF_START_TIMEBAR_DETAIL("Peds");
|
|
CPed::StartAiUpdate();
|
|
CVehicle::PreGameWorldAiUpdate();
|
|
//CVehicle::StartAiUpdate() is called at the top of the CVehiclePopulation update now
|
|
//so newly-created cars can run a full AI update in-place there
|
|
fwSceneUpdate::UpdateEntityType(ENTITY_TYPE_PED, SU_UPDATE, ¶ms);
|
|
PF_START_TIMEBAR_DETAIL("Vehicles");
|
|
CScenarioManager::GetVehicleScenarioManager().Update();
|
|
fwSceneUpdate::UpdateEntityType(ENTITY_TYPE_VEHICLE, SU_UPDATE, ¶ms);
|
|
PF_START_TIMEBAR_DETAIL("Objects");
|
|
fwSceneUpdate::UpdateEntityType(ENTITY_TYPE_OBJECT, SU_UPDATE, ¶ms);
|
|
PF_START_TIMEBAR_DETAIL("Animated objects");
|
|
fwSceneUpdate::UpdateEntityType(ENTITY_TYPE_ANIMATED_BUILDING, SU_UPDATE, ¶ms);
|
|
|
|
PF_START_TIMEBAR("GameWorld Update Through Anim Queue");
|
|
UpdateEntitiesThroughAnimQueue(params);
|
|
|
|
CVehicle::FinishAiUpdate();
|
|
}
|
|
else
|
|
#endif // __BANK
|
|
{
|
|
PF_START_TIMEBAR("GameWorld Peds & Objects");
|
|
PF_START(DeferredAi_PedsVehiclesObjects);
|
|
CPed::StartAiUpdate();
|
|
CVehicle::PreGameWorldAiUpdate();
|
|
//CVehicle::StartAiUpdate() is called at the top of the CVehiclePopulation update now
|
|
//so newly-created cars can run a full AI update in-place there
|
|
PF_START(SU_UPDATE);
|
|
fwSceneUpdate::Update(SU_UPDATE, ¶ms);
|
|
PF_STOP(SU_UPDATE);
|
|
PF_START(SU_UPDATE_VEHICLE);
|
|
PF_START_TIMEBAR("GameWorld Vehicles");
|
|
CScenarioManager::GetVehicleScenarioManager().Update();
|
|
fwSceneUpdate::Update(SU_UPDATE_VEHICLE, ¶ms);
|
|
PF_STOP(SU_UPDATE_VEHICLE);
|
|
|
|
PF_START_TIMEBAR("GameWorld Update Through Anim Queue");
|
|
UpdateEntitiesThroughAnimQueue(params);
|
|
|
|
PF_START_TIMEBAR("aiDeferredTasks::RunAll");
|
|
aiDeferredTasks::RunAll();
|
|
|
|
CVehicle::FinishAiUpdate();
|
|
PF_STOP(DeferredAi_PedsVehiclesObjects);
|
|
}
|
|
PF_STOP(GameWorldPedsAndObjects);
|
|
PHLEVEL->SetOnlyMainThreadIsUpdatingPhysics(false);
|
|
|
|
PF_START_TIMEBAR_DETAIL("GameWorld Other");
|
|
|
|
|
|
#if !__FINAL
|
|
CGameWorld::ms_pProcessingEntity = NULL;
|
|
#endif
|
|
|
|
// Update melee combat AI.
|
|
PF_START(Proc_CombatManager);
|
|
CCombatManager::GetInstance().Update();
|
|
PF_STOP(Proc_CombatManager);
|
|
|
|
// Update combat
|
|
CTaskCombat::UpdateClass();
|
|
|
|
// Update Wander
|
|
CTaskWander::UpdateClass();
|
|
|
|
// Update Smart Flee
|
|
CTaskSmartFlee::UpdateClass();
|
|
|
|
// Update our relationship groups
|
|
CRelationshipManager::UpdateRelationshipGroups();
|
|
|
|
PF_POP_TIMEBAR();
|
|
|
|
PF_START_TIMEBAR_IDLE( "End Animation");
|
|
EndAnimationUpdate();
|
|
|
|
CAnimSceneManager::Update(kAnimSceneUpdatePostAi);
|
|
|
|
PF_PUSH_TIMEBAR_DETAIL("GameWorld entity removal");
|
|
|
|
iNumEntitiesToDelete = params.iNumEntitiesToDelete;
|
|
iNumEntitiesToRemoveFromProcCont = params.iNumEntitiesToRemoveFromProcCont;
|
|
|
|
AllowDelete(true);
|
|
|
|
// Remove any entities added to the removal list
|
|
for( s32 i = 0; i < iNumEntitiesToDelete; i++ )
|
|
{
|
|
if( apEntitiesToDelete[i] )
|
|
{
|
|
RemoveEntity(apEntitiesToDelete[i]);
|
|
}
|
|
}
|
|
|
|
AllowDelete(false);
|
|
|
|
// Remove any entities from the process control list that don't need processed any more
|
|
for(int i=0; i<iNumEntitiesToRemoveFromProcCont; i++)
|
|
{
|
|
if(apEntitiesToRemoveFromProcContList[i])
|
|
{
|
|
apEntitiesToRemoveFromProcContList[i]->RemoveFromSceneUpdate();
|
|
}
|
|
}
|
|
|
|
AllowDelete(prevAllowDelete);
|
|
|
|
PF_START(Proc_PedGroups);
|
|
CPedGroups::Process(); // assigns new leaders to the group if necessary
|
|
PF_STOP(Proc_PedGroups);
|
|
|
|
PF_POP_TIMEBAR_DETAIL();
|
|
|
|
|
|
//******************************************************************
|
|
// Physics::Update()
|
|
// This runs the physics simulation
|
|
//******************************************************************
|
|
|
|
|
|
|
|
// This is a bit of a hack to get the broken glass to use vertex shader lighting when directional light/shadows are off
|
|
bgBreakable::SetSafeToUseVSLight(Lights::GetUpdateDirectionalLight().GetIntensity() == 0 || !Lights::GetUpdateDirectionalLight().IsCastShadows());
|
|
|
|
|
|
PF_PUSH_TIMEBAR( "Physics streaming" );
|
|
CPhysics::UpdateRequests();
|
|
PF_POP_TIMEBAR();
|
|
|
|
#if SCRATCH_ALLOCATOR
|
|
// Reset the scratch allocator
|
|
sysMemManager::GetInstance().GetScratchAllocator()->Reset();
|
|
#endif
|
|
PF_PUSH_TIMEBAR_BUDGETED( "Physics", NetworkInterface::IsGameInProgress()?6.0f:7.5f);
|
|
CPhysics::Update();
|
|
PF_POP_TIMEBAR();
|
|
|
|
#if GTA_REPLAY
|
|
if(CReplayMgr::IsEditModeActive())
|
|
{
|
|
//we still need to update this for the lod to work correctly
|
|
if (const grcViewport *pViewport = gVpMan.CalcViewportForFragManager())
|
|
{
|
|
FRAGMGR->SetInterestFrustum(*pViewport, *gVpMan.GetFragManagerMatrix());
|
|
bgGlassManager::SetViewport(pViewport);
|
|
}
|
|
}
|
|
#endif //GTA_REPLAY
|
|
|
|
CVehicleDamage::ResetVehicleExplosionBreakOffPartsFlag();
|
|
#if GTA_REPLAY
|
|
// We shouldn't need to do this while a replay is playing back...
|
|
// Having this on while a replay is active causes a leak in tasks somewhere
|
|
if(!CReplayMgr::IsEditModeActive())
|
|
#endif // GTA_REPLAY
|
|
CVehiclePopulation::UpdateLinksAfterGather();
|
|
|
|
#if __TRACK_PEDS_IN_NAVMESH
|
|
PF_START_TIMEBAR_DETAIL ( "Track peds");
|
|
// Keep a track of all the peds positions in the navmesh system
|
|
{
|
|
CPathServer::UpdatePedTracking();
|
|
}
|
|
#endif
|
|
|
|
PF_START_TIMEBAR ( "Ped ai");
|
|
|
|
//NOTE: We always capture vehicle recording data in the game update, but we can optionally playback this data in either the individual physics
|
|
//time-slices or the game update.
|
|
#if !__FINAL
|
|
CVehicleRecordingMgr::SaveDataForThisFrame();
|
|
#endif // !__FINAL
|
|
|
|
if(!CVehicleRecordingMgr::sm_bUpdateWithPhysics)
|
|
{
|
|
PF_START(Proc_VehRecording);
|
|
CVehicleRecordingMgr::RetrieveDataForThisFrame();
|
|
PF_STOP(Proc_VehRecording);
|
|
}
|
|
|
|
// Waypoint-based ped movement recording
|
|
#if __BANK
|
|
CWaypointRecording::UpdateRecording();
|
|
#endif
|
|
|
|
//**********************************************************************
|
|
// ProcessAfterPhysicsUpdate()
|
|
// This does stuff which is needed to be done after objects have been
|
|
// moved. This includes a pass on peds' AI, updating portal trackers,
|
|
// updating obstacle positions for pathfinding and positioning entities
|
|
// which are attached to other entities.
|
|
//
|
|
// NB: Do this before peds CPhysics::Update() so that peds in vehicles
|
|
// that are attached to other moving things get positioned correctly
|
|
//**********************************************************************
|
|
|
|
//ProcessAfterPhysicsUpdate();
|
|
|
|
PF_START(Proc_ScriptRecTasks);
|
|
CPedScriptedTaskRecord::Process();
|
|
PF_STOP(Proc_ScriptRecTasks);
|
|
|
|
// moved before physics update to avoid peds ending up with no tasks
|
|
// if the group leader is destroyed while group members still exist
|
|
//CPedGroups::Process(); // assign's new leaders to the group if necessary
|
|
|
|
// Counting events seems to crash the game during navmesh export, so I've disabled it in that situation
|
|
#if NAVMESH_EXPORT
|
|
bool bCountEvents = (CNavMeshDataExporter::GetExportMode() != CNavMeshDataExporter::eMapExport);
|
|
#else
|
|
static const bool bCountEvents = true;
|
|
#endif
|
|
|
|
if(bCountEvents)
|
|
{
|
|
USE_MEMBUCKET(MEMBUCKET_GAMEPLAY);
|
|
}
|
|
|
|
PF_START_TIMEBAR_DETAIL ( "Remove Fallen");
|
|
PF_START(Proc_RemoveFallen);
|
|
|
|
if ((fwTimer::GetSystemFrameCount() & 7) == 1)
|
|
RemoveFallenPeds();
|
|
else if ((fwTimer::GetSystemFrameCount() & 7) == 3)
|
|
RemoveFallenCars();
|
|
else
|
|
RemoveFallenObjects();
|
|
|
|
PF_STOP(Proc_RemoveFallen);
|
|
|
|
CVehicle *pPlayerVehicle = FindFollowPlayerVehicle();
|
|
if (pPlayerVehicle && pPlayerVehicle->GetVehicleModelInfo())
|
|
{
|
|
sm_FollowPlayerCarName = pPlayerVehicle->GetVehicleModelInfo()->GetGameName();
|
|
}
|
|
else
|
|
{
|
|
sm_FollowPlayerCarName = NULL;
|
|
}
|
|
|
|
// cache data to be accessed from the outside
|
|
CWanted* wanted = FindLocalPlayerWanted_Safe();
|
|
sm_localPlayerWanted = wanted?wanted->GetWantedLevel():0;
|
|
sm_localPlayerArrestState = FindLocalPlayerArrestState_Safe();
|
|
sm_localPlayerDeathState = FindLocalPlayerDeathState_Safe();
|
|
sm_localPlayerHealth = FindLocalPlayerHealth_Safe();
|
|
sm_localPlayerMaxHealth = FindLocalPlayerMaxHealth_Safe();
|
|
}
|
|
|
|
|
|
|
|
// Name : ProcessAfterAllMovement
|
|
// Purpose : processes everything that requires up to date positions
|
|
// Parameters : none
|
|
// Returns : none
|
|
void CGameWorld::ProcessAfterAllMovement()
|
|
{
|
|
if (fwTimer::IsGamePaused())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// We used to wait for this at the end of CPhysics::Update; the wait was moved here so we won't stall
|
|
// the main thread waiting. We need to make sure these tasks are done now because they can update physics
|
|
// outside the main thread
|
|
CPhysics::WaitForVerletTasks();
|
|
|
|
// 8/7/12 - cthomas - We know only the main thread would possibly be updating physics at this
|
|
// point in time, so set the flag in the physics system to indicate that. This is an optimization
|
|
// that will allow us to skip some locking overhead in the physics system as we update the
|
|
// locations of objects in the physics sytem during ProcessAfterAllMovement() - things like
|
|
// attachments update their location at this time.
|
|
PHLEVEL->SetOnlyMainThreadIsUpdatingPhysics(true);
|
|
|
|
// Update the positions of any entities in synchronized scenes.
|
|
// This needs to be done before the attachments are updated because these
|
|
// entities may have objects attached to them. Attaching synchronized scenes
|
|
// To entities that are themselves attached is currently unsupported.
|
|
fwSceneUpdate::Update(SU_AFTER_ALL_MOVEMENT);
|
|
CAnimSceneManager::Update(kAnimSceneUpdatePostMovement);
|
|
|
|
PF_PUSH_TIMEBAR("Vehicles");
|
|
s32 i = (s32) CVehicle::GetPool()->GetSize();
|
|
CVehicle* pVehicle = NULL;
|
|
CVehicle* pNextVehicle = NULL;
|
|
while((pNextVehicle == NULL) && (i > 0))
|
|
{
|
|
pNextVehicle = CVehicle::GetPool()->GetSlot(--i);
|
|
}
|
|
|
|
while(pNextVehicle)
|
|
{
|
|
pVehicle = pNextVehicle;
|
|
|
|
pNextVehicle = NULL;
|
|
while((pNextVehicle == NULL) && (i > 0))
|
|
{
|
|
pNextVehicle = CVehicle::GetPool()->GetSlot(--i);
|
|
}
|
|
PrefetchObject(pNextVehicle);
|
|
|
|
// skip scheduled vehicles, which are vehicles created but waiting to be correctly placed by cargens
|
|
// we don't want to run the portal tracker for them since that might place them in the game world
|
|
// also skip vehicles that are in the reuse pool, we don't want them in the world either
|
|
if(pVehicle && !pVehicle->GetIsScheduled() && !pVehicle->GetIsInReusePool())
|
|
{
|
|
ProcessAttachmentAfterAllMovement(pVehicle);
|
|
if(pVehicle->GetVehicleModelInfo()->GetHasSeatCollision())
|
|
{
|
|
pVehicle->ProcessSeatCollisionBound();
|
|
}
|
|
|
|
if(pVehicle->WasUnableToAddToPathServer())
|
|
{
|
|
CPathServerGta::MaybeAddDynamicObject(pVehicle);
|
|
}
|
|
pVehicle->UpdatePortalTracker();
|
|
|
|
if ( pVehicle->m_nDEflags.bIsOutOfMap )
|
|
{
|
|
pVehicle->m_nDEflags.bIsOutOfMap = false;
|
|
|
|
if( !pVehicle->InheritsFromTrain() || ((CTrain*)pVehicle)->m_nTrainFlags.bCreatedAsScriptVehicle )
|
|
{
|
|
CGameWorld::RemoveOutOfMapVehicle( pVehicle );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
PF_POP_TIMEBAR();
|
|
|
|
PF_PUSH_TIMEBAR("Peds");
|
|
//Process each ped's ai.
|
|
i = CPed::GetPool()->GetSize();
|
|
CPed *pPed = NULL;
|
|
CPed *pNextPed = NULL;
|
|
while((pNextPed == NULL) && (i > 0))
|
|
{
|
|
pNextPed = CPed::GetPool()->GetSlot(--i);
|
|
}
|
|
|
|
while(pNextPed)
|
|
{
|
|
pPed = pNextPed;
|
|
|
|
pNextPed = NULL;
|
|
while((pNextPed == NULL) && (i > 0))
|
|
{
|
|
pNextPed = CPed::GetPool()->GetSlot(--i);
|
|
}
|
|
PrefetchObject(pNextPed);
|
|
|
|
if(pPed && !pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_PedIsInReusePool))
|
|
{
|
|
pPed->m_PedResetFlags.ResetPostMovement(pPed);
|
|
if(pPed->GetPedResetFlag(CPED_RESET_FLAG_ProcessPostMovement) || pPed->GetPedResetFlag(CPED_RESET_FLAG_ProcessPostMovementTimeSliced))
|
|
{
|
|
pPed->GetPedIntelligence()->ProcessPostMovement();
|
|
}
|
|
|
|
pPed->GetMotionData()->ProcessPostMovement();
|
|
|
|
ProcessAttachmentAfterAllMovement(pPed);
|
|
|
|
pPed->UpdatePortalTracker();
|
|
|
|
pPed->ProcessPreCamera();
|
|
|
|
if ( pPed->m_nDEflags.bIsOutOfMap )
|
|
{
|
|
// The dynamic entity flag 'bIsOutOfMap' is only ever set from the portal tracker, when a ped
|
|
// has traversed a portal with no destination. This is handy because we can use this fact to
|
|
// help narrow down why mission may have fallen through map, potentially breaking game progress..
|
|
bool bIsOutOfMap = pPed->m_nDEflags.bIsOutOfMap;
|
|
pPed->m_nDEflags.bIsOutOfMap = false;
|
|
CGameWorld::RemoveOutOfMapPed( pPed, bIsOutOfMap );
|
|
}
|
|
}
|
|
}
|
|
PF_POP_TIMEBAR();
|
|
|
|
PF_PUSH_TIMEBAR("Objects");
|
|
bool bTrackerUpdate = false;
|
|
i = (s32) CObject::GetPool()->GetSize();
|
|
CObject* pObject = NULL;
|
|
CObject* pNextObject = NULL;
|
|
while((pNextObject == NULL) && (i > 0))
|
|
{
|
|
pNextObject = CObject::GetPool()->GetSlot(--i);
|
|
}
|
|
|
|
while(pNextObject)
|
|
{
|
|
bTrackerUpdate = false;
|
|
pObject = pNextObject;
|
|
|
|
pNextObject = NULL;
|
|
while((pNextObject == NULL) && (i > 0))
|
|
{
|
|
pNextObject = CObject::GetPool()->GetSlot(--i);
|
|
}
|
|
PrefetchObject(pNextObject);
|
|
|
|
if (Verifyf(pObject && CObject::GetPool()->IsValidPtr(pObject), "Trying to access a dead object!"))
|
|
{
|
|
ProcessAttachmentAfterAllMovement(pObject);
|
|
CObjectIntelligence* pObjectIntelligence = pObject->GetObjectIntelligence();
|
|
if(pObjectIntelligence)
|
|
pObjectIntelligence->ProcessPostMovement();
|
|
|
|
if(pObject->WasUnableToAddToPathServer())
|
|
{
|
|
CPathServerGta::MaybeAddDynamicObject(pObject);
|
|
}
|
|
|
|
if (pObject->m_nFlags.bInMloRoom) {
|
|
bTrackerUpdate = true; // already in an interior
|
|
} else if (pObject->GetOwnedBy() == ENTITY_OWNEDBY_SCRIPT){ // any mission objects
|
|
bTrackerUpdate = true;
|
|
} else if (pObject->GetOwnedBy() == ENTITY_OWNEDBY_CUTSCENE){
|
|
bTrackerUpdate = true;
|
|
} else if (pObject->GetOwnedBy() == ENTITY_OWNEDBY_REPLAY){
|
|
bTrackerUpdate = true;
|
|
} else if (!pObject->GetIsStatic()){
|
|
bTrackerUpdate = true; // anything moving
|
|
}
|
|
|
|
if (bTrackerUpdate)
|
|
{
|
|
pObject->UpdatePortalTracker();
|
|
}
|
|
|
|
if ( pObject->m_nDEflags.bIsOutOfMap )
|
|
{
|
|
pObject->m_nDEflags.bIsOutOfMap = false;
|
|
CGameWorld::RemoveOutOfMapObject( pObject );
|
|
}
|
|
}
|
|
}
|
|
PF_POP_TIMEBAR();
|
|
|
|
PF_PUSH_TIMEBAR("Pickups");
|
|
i = CPickup::GetPool()->GetSize();
|
|
CPickup *pPickup = NULL;
|
|
CPickup *pNextPickup = NULL;
|
|
while((pNextPickup == NULL) && (i > 0))
|
|
{
|
|
pNextPickup = CPickup::GetPool()->GetSlot(--i);
|
|
}
|
|
|
|
while(pNextPickup)
|
|
{
|
|
pPickup = pNextPickup;
|
|
|
|
pNextPickup = NULL;
|
|
while((pNextPickup == NULL) && (i > 0))
|
|
{
|
|
pNextPickup = CPickup::GetPool()->GetSlot(--i);
|
|
}
|
|
PrefetchObject(pNextPickup);
|
|
|
|
if(pPickup)
|
|
{
|
|
ProcessAttachmentAfterAllMovement(pPickup);
|
|
CObjectIntelligence* pObjectIntelligence = pPickup->GetObjectIntelligence();
|
|
if(pObjectIntelligence)
|
|
pObjectIntelligence->ProcessPostMovement();
|
|
|
|
pPickup->UpdatePortalTracker();
|
|
|
|
if ( pPickup->m_nDEflags.bIsOutOfMap )
|
|
{
|
|
pPickup->m_nDEflags.bIsOutOfMap = false;
|
|
CGameWorld::RemoveOutOfMapObject( pPickup );
|
|
}
|
|
}
|
|
}
|
|
PF_POP_TIMEBAR();
|
|
|
|
if (NetworkInterface::IsGameInProgress())
|
|
{
|
|
NetworkInterface::GetObjectManager().UpdateAfterAllMovement();
|
|
}
|
|
|
|
// Update clipset streaming
|
|
CPrioritizedClipSetRequestManager::GetInstance().Update(fwTimer::GetTimeStep());
|
|
|
|
// Update physics octtree
|
|
CPhysics::CommitDeferredOctreeUpdates();
|
|
PHLEVEL->SetOnlyMainThreadIsUpdatingPhysics(false);
|
|
}
|
|
|
|
// Name : ProcessPedsEarlyAfterCameraUpdate
|
|
// Purpose : processes just peds early based on up to date camera orientation and position. See ProcessAfterCameraUpdate() for the rest.
|
|
// Parameters : none
|
|
// Returns : none
|
|
void CGameWorld::ProcessPedsEarlyAfterCameraUpdate()
|
|
{
|
|
PF_AUTO_PUSH_TIMEBAR("ProcessPedsEarlyAfterCameraUpdate");
|
|
|
|
if (!fwTimer::IsGamePaused())
|
|
{
|
|
// Set the animation director as being locked (prior to the multi threaded update)
|
|
// You cannot do any of the following during this process :
|
|
// Get or set the local matrices of any skeleton (with an animation director)
|
|
// Remove any dynamic entity (with an animation director)
|
|
fwAnimDirectorComponentMotionTree::SetLockedGlobal(true, fwAnimDirectorComponent::kPhasePrePhysics);
|
|
|
|
//Process each ped's ai.
|
|
PF_PUSH_TIMEBAR("Peds");
|
|
Assert(ms_pedsRunningInstantAnimUpdate.GetCount() == 0);
|
|
CPed *pPed = NULL;
|
|
s32 i = CPed::GetPool()->GetSize();
|
|
while(i--)
|
|
{
|
|
pPed = CPed::GetPool()->GetSlot(i);
|
|
if(pPed)
|
|
{
|
|
const bool bStartedInstantAnimUpdate = pPed->ProcessPostCamera();
|
|
if(Unlikely(bStartedInstantAnimUpdate))
|
|
{
|
|
// We simply push the raw ped pointer onto this array for now. We're assuming
|
|
// that nothing can delete this ped between now and when we loop over this
|
|
// array and wait for the animations to finish in ProcessAfterCameraUpdate().
|
|
ms_pedsRunningInstantAnimUpdate.PushAndGrow(pPed);
|
|
}
|
|
}
|
|
}
|
|
PF_POP_TIMEBAR();
|
|
}
|
|
}
|
|
|
|
|
|
// Name : ProcessAfterCameraUpdate
|
|
// Purpose : processes everything that requires up to date camera orientation and position
|
|
// Parameters : none
|
|
// Returns : none
|
|
void CGameWorld::ProcessAfterCameraUpdate()
|
|
{
|
|
PF_AUTO_PUSH_TIMEBAR("World ProcessAfterCameraUpdate");
|
|
|
|
if (!fwTimer::IsGamePaused())
|
|
{
|
|
CVehicle::ProcessAfterCameraUpdate();
|
|
|
|
// Process each objects ai.
|
|
PF_PUSH_TIMEBAR("Objects");
|
|
// Loop over in the object post camera array starting from the end, as calls to
|
|
// ProcessPostCamera() may remove the object from the array, and removing an objects
|
|
// from the array will fill the slot with the last item in the array (DeleteFast()).
|
|
int numPostCameraObjects = CObject::ms_ObjectsThatNeedProcessPostCamera.GetCount();
|
|
for(int i = numPostCameraObjects - 1; i >= 0; i--)
|
|
{
|
|
CObject* pObject = CObject::ms_ObjectsThatNeedProcessPostCamera[i];
|
|
pObject->ProcessPostCamera();
|
|
}
|
|
PF_POP_TIMEBAR();
|
|
|
|
// Finish the post-camera update for any peds which needed to do an instant anim update.
|
|
PF_PUSH_TIMEBAR("Peds with Instant Anim Updates");
|
|
int numPedsWithInstantAnimUpdate = ms_pedsRunningInstantAnimUpdate.GetCount();
|
|
for(int i = 0; i < numPedsWithInstantAnimUpdate; i++)
|
|
{
|
|
CPed* pPed = ms_pedsRunningInstantAnimUpdate[i];
|
|
pPed->InstantAnimUpdateEnd();
|
|
}
|
|
ms_pedsRunningInstantAnimUpdate.ResetCount();
|
|
PF_POP_TIMEBAR();
|
|
}
|
|
|
|
// This doesn't do much now except refreshing the frame count we use for visibility
|
|
// on entities. It probably needs to happen even if we are not paused, since the
|
|
// prerender code will run and do its thing either way.
|
|
ProcessDynamicObjectVisibility();
|
|
|
|
if (!fwTimer::IsGamePaused())
|
|
{
|
|
//******************************************************************
|
|
// Anim update (post the camera update)
|
|
// Updates the animation blenders on dynamic entities
|
|
// All dynamic entities will have already had their mover updated
|
|
// Dynamic entities that were not visible in any active viewport
|
|
// prior to the camera update but are visible post the camera update
|
|
// will have their skeleton updated
|
|
//******************************************************************
|
|
PF_AUTO_PUSH_TIMEBAR("PostCameraAnimationUpdate");
|
|
StartPostCameraAnimationUpdate();
|
|
EndPostCameraAnimationUpdate();
|
|
}
|
|
|
|
if (!fwTimer::IsGamePaused() REPLAY_ONLY(|| CReplayMgr::IsReplayInControlOfWorld()))
|
|
{
|
|
PF_AUTO_PUSH_TIMEBAR("VfxHelper");
|
|
const grcViewport *vp = gVpMan.GetCurrentGameGrcViewport();
|
|
CVfxHelper::SetGameCamPos(vp->GetCameraPosition());
|
|
CVfxHelper::SetGameCamForward(-vp->GetCameraMtx().GetCol2());
|
|
CVfxHelper::SetGameCamFOV(vp->GetFOVY());
|
|
CVfxHelper::UpdateCamInteriorLocation();
|
|
CVfxHelper::UpdateCurrFollowCamViewMode();
|
|
CVfxHelper::SetLastTimeStep(fwTimer::GetTimeStep());
|
|
}
|
|
|
|
if (!fwTimer::IsGamePaused())
|
|
{
|
|
PF_AUTO_PUSH_TIMEBAR("Process IK Solvers");
|
|
CBaseIkManager::ProcessSolvers();
|
|
}
|
|
|
|
BANK_ONLY(fwSceneUpdate::DebugDraw3D());
|
|
}
|
|
|
|
|
|
|
|
void CGameWorld::ProcessAfterPreRender()
|
|
{
|
|
// finish up ped damage render target allocations, now that we have all the priorities
|
|
CPedDamageManager::GetInstance().PostPreRender();
|
|
|
|
PF_PUSH_TIMEBAR("fwAnimDirector::WaitForAllUpdatesToComplete");
|
|
// Block the main thread until the animation threads have completed
|
|
fwAnimDirector::WaitForAllUpdatesToComplete();
|
|
PF_POP_TIMEBAR();
|
|
|
|
// Set the animblender as being unlocked (post the multi threaded update)
|
|
fwAnimDirectorComponentMotionTree::SetLockedGlobal(false, fwAnimDirectorComponent::kPhaseNone);
|
|
|
|
CAnimSceneManager::Update(kAnimSceneUpdatePreRender);
|
|
|
|
// turn on option that forces new objects onto render list if frag object breaks apart in this section of code
|
|
// WARNING need to reset this flag before exiting this function
|
|
CBreakable::SetInsertDirectToRenderList(true);
|
|
|
|
//if(fwTimer::bSkipProcessThisFrame)
|
|
// return;
|
|
|
|
// 8/7/12 - cthomas - We know only the main thread would possibly be updating physics at this
|
|
// point in time, so set the flag in the physics system to indicate that. This is an optimization
|
|
// that will allow us to skip some locking overhead in the physics system as we update locations
|
|
// of objects during this function.
|
|
PHLEVEL->SetOnlyMainThreadIsUpdatingPhysics(true);
|
|
|
|
// Update the UI menu ped last
|
|
if (CPauseMenu::IsActive() && CPauseMenu::DynamicMenuExists())
|
|
{
|
|
CPauseMenu::GetDisplayPed()->UpdatePedPose();
|
|
CPauseMenu::GetLocalPlayerDisplayPed()->UpdatePedPose();
|
|
}
|
|
|
|
//Process each ped's ai.
|
|
CPed *pPed = NULL;
|
|
s32 i = CPed::GetPool()->GetSize();
|
|
while(i--)
|
|
{
|
|
pPed = CPed::GetPool()->GetSlot(i);
|
|
if(pPed)
|
|
{
|
|
pPed->PostPreRender();
|
|
}
|
|
}
|
|
|
|
#if __BANK
|
|
CAnimViewer::OnDynamicEntityPreRender();
|
|
#endif // __BANK
|
|
|
|
// Process all delayed attachments
|
|
// After peds so any Ik etc has been done
|
|
for(int iAttachIndex = 0; iAttachIndex < ms_nNumDelayedRootAttachments; iAttachIndex++)
|
|
{
|
|
if(ms_delayedRootAttachments[iAttachIndex])
|
|
{
|
|
ms_delayedRootAttachments[iAttachIndex]->ProcessAllAttachments();
|
|
ms_delayedRootAttachments[iAttachIndex] = NULL;
|
|
}
|
|
}
|
|
ms_nNumDelayedRootAttachments = 0;
|
|
|
|
// Weapons done after attachments so matrices up to date
|
|
i = CWeapon::GetPool()->GetSize();
|
|
while(i--)
|
|
{
|
|
CWeapon* pWeapon = CWeapon::GetPool()->GetSlot(i);
|
|
if(pWeapon)
|
|
{
|
|
pWeapon->PostPreRender();
|
|
}
|
|
}
|
|
|
|
// Process each ped's after-attachments ai
|
|
i = CPed::GetPool()->GetSize();
|
|
while (i--)
|
|
{
|
|
pPed = CPed::GetPool()->GetSlot(i);
|
|
if(pPed)
|
|
{
|
|
pPed->PostPreRenderAfterAttachments();
|
|
}
|
|
}
|
|
|
|
// Projectiles done after attachments so matrices up to date
|
|
CProjectileManager::PostPreRender();
|
|
|
|
PHLEVEL->SetOnlyMainThreadIsUpdatingPhysics(false);
|
|
|
|
PF_START_TIMEBAR_DETAIL("Weapon");
|
|
CWeaponManager::Process();
|
|
|
|
CPhysics::UpdateBreakableGlass();
|
|
|
|
// WARNING - don't exit this function with out reseting this to false
|
|
CBreakable::SetInsertDirectToRenderList(false);
|
|
|
|
CClipEventTags::CMotionBlurEventTag::PostPreRender();
|
|
|
|
PostFX::GetFPVPos();
|
|
}
|
|
|
|
void CGameWorld::SceneUpdateAddShockingEventsCb(fwEntity &entity, void *)
|
|
{
|
|
Assert(entity.GetType() == ENTITY_TYPE_PED);
|
|
|
|
GetEventGlobalGroup()->AddEventsToPed(static_cast<CPed *>(&entity));
|
|
}
|
|
|
|
void CGameWorld::SceneUpdateStartAnimUpdatePrePhysicsCb(fwEntity &entity, void *)
|
|
{
|
|
// Calculate whether the dynamic entity should be processed this frame and set its frozen flag accordingly.
|
|
// Network clones should not be frozen or their clone tasks will not be processed
|
|
CDynamicEntity* pDynamicEntity = (CDynamicEntity*)&entity;
|
|
|
|
// B*1892279: Prevent damaged objects from doing animation updates. The anim update restores the undamaged skeleton that was previously zeroed out by the frag system.
|
|
// This causes both the undamaged and the damaged model to be rendered for the object, which is obviously wrong!
|
|
// Objects normally remove themselves from animation updates during PreRender, but this doesn't happen when an object is destroyed while it is not in any PreRender lists.
|
|
// If an object doesn't prevent animation updates in the same frame that it is destroyed, animation updates will occur in subsequent frames until the object is back in a PreRender list.
|
|
if(pDynamicEntity->GetIsTypeObject() && pDynamicEntity->m_nFlags.bRenderDamaged)
|
|
{
|
|
pDynamicEntity->RemoveFromSceneUpdate();
|
|
return;
|
|
}
|
|
|
|
if ( pDynamicEntity->GetAnimDirector() )
|
|
{
|
|
pDynamicEntity->GetAnimDirector()->PreUpdate(fwTimer::GetTimeStep());
|
|
}
|
|
|
|
UpdateEntityFrozenByInterior(pDynamicEntity);
|
|
bool bWaitingForCollision = WaitingForCollision(pDynamicEntity);
|
|
if( (pDynamicEntity->m_nDEflags.bFrozenByInterior || bWaitingForCollision) && !pDynamicEntity->IsNetworkClone())
|
|
{
|
|
bool bFreeze = true;
|
|
|
|
if(pDynamicEntity->GetIsTypePed())
|
|
{
|
|
CPed* pPed = static_cast<CPed*>(pDynamicEntity);
|
|
bFreeze = pPed->CanBeFrozen();
|
|
}
|
|
|
|
if(bFreeze)
|
|
{
|
|
pDynamicEntity->m_nDEflags.bFrozen = true;
|
|
|
|
pDynamicEntity->RemoveSceneUpdateFlags(CGameWorld::SU_AFTER_ALL_MOVEMENT | CGameWorld::SU_END_ANIM_UPDATE_PRE_PHYSICS);
|
|
|
|
// In this case we won't be pushed onto the update queue, so we need to switch to regular updates.
|
|
pDynamicEntity->StopUpdatingThroughAnimQueue();
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
pDynamicEntity->m_nDEflags.bFrozen = false;
|
|
|
|
u32 sceneUpdateFlags = CGameWorld::SU_END_ANIM_UPDATE_PRE_PHYSICS;
|
|
if (pDynamicEntity->GetAnimDirector() && pDynamicEntity->GetAnimDirector()->GetComponent<fwAnimDirectorComponentSyncedScene>())
|
|
sceneUpdateFlags |= CGameWorld::SU_AFTER_ALL_MOVEMENT;
|
|
pDynamicEntity->AddSceneUpdateFlags(sceneUpdateFlags);
|
|
|
|
if (entity.IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD))
|
|
{
|
|
// In this case we won't be pushed onto the update queue, so we need to switch to regular updates.
|
|
pDynamicEntity->StopUpdatingThroughAnimQueue();
|
|
|
|
return;
|
|
}
|
|
|
|
if (pDynamicEntity->GetIsDynamic())
|
|
{
|
|
bool bDoAnimUpdate = true;
|
|
if (pDynamicEntity->GetIsTypeVehicle())
|
|
{
|
|
CVehicle* pVehicle = static_cast<CVehicle*>(pDynamicEntity);
|
|
|
|
//Update the reserve components
|
|
if (!pVehicle->IsDummy())
|
|
{
|
|
pVehicle->GetComponentReservationMgr()->Update();
|
|
}
|
|
|
|
if (pVehicle->m_nVehicleFlags.bForcePostCameraAnimUpdate)
|
|
{
|
|
pVehicle->m_nVehicleFlags.bForcePostCameraAnimUpdate = 0;
|
|
bDoAnimUpdate = false;
|
|
}
|
|
}
|
|
|
|
if (bDoAnimUpdate)
|
|
{
|
|
pDynamicEntity->StartAnimUpdate(fwTimer::GetTimeStep());
|
|
}
|
|
else
|
|
{
|
|
// In this case we won't be pushed onto the update queue, so we need to switch to regular updates.
|
|
pDynamicEntity->StopUpdatingThroughAnimQueue();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGameWorld::SceneUpdateAfterAllMovementCb(fwEntity &entity, void *)
|
|
{
|
|
if(entity.IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD))
|
|
return;
|
|
|
|
Assert(((CEntity *) &entity)->GetIsDynamic());
|
|
|
|
CDynamicEntity* pDynamicEntity = (CDynamicEntity*)&entity;
|
|
if (pDynamicEntity->GetAnimDirector() && pDynamicEntity->GetAnimDirector()->GetComponent<fwAnimDirectorComponentSyncedScene>())
|
|
{
|
|
pDynamicEntity->GetAnimDirector()->GetComponent<fwAnimDirectorComponentSyncedScene>()->UpdateSyncedSceneMover();
|
|
}
|
|
}
|
|
|
|
void CGameWorld::SceneUpdateResetVisibilityCb(fwEntity &entity, void *)
|
|
{
|
|
Assert(((CEntity *) &entity)->GetIsDynamic());
|
|
|
|
CDynamicEntity* pDynamicEntity = (CDynamicEntity*)&entity;
|
|
|
|
// If we are not currently visible, reset the time stamp as if we had been seen
|
|
// on the previous frame: this will maximize the time until we would get in trouble
|
|
// for wrapping around (and before then, this code should have run again).
|
|
if(!pDynamicEntity->GetIsVisibleInSomeViewportThisFrame())
|
|
{
|
|
pDynamicEntity->SetIsVisibleInSomeViewportThisFrame(false);
|
|
}
|
|
}
|
|
|
|
void CGameWorld::SceneUpdateEndAnimUpdatePrePhysicsCb(fwEntity &entity, void *)
|
|
{
|
|
if(entity.IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD))
|
|
{
|
|
return;
|
|
}
|
|
|
|
Assert(((CEntity *) &entity)->GetIsDynamic());
|
|
CDynamicEntity* pDynamicEntity = (CDynamicEntity*)&entity;
|
|
|
|
pDynamicEntity->EndAnimUpdate(fwTimer::GetTimeStep());
|
|
}
|
|
|
|
void CGameWorld::SceneUpdateUpdatePausedCb(fwEntity &entity, void *)
|
|
{
|
|
if(IsEntityFrozen((CDynamicEntity*)&entity) || entity.IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD))
|
|
return;
|
|
|
|
Assert(((CEntity *) &entity)->GetIsDynamic());
|
|
|
|
CDynamicEntity* pDynamicEntity = (CDynamicEntity*)&entity;
|
|
pDynamicEntity->UpdatePaused();
|
|
}
|
|
|
|
void CGameWorld::SceneUpdateStartAnimUpdatePostCameraCb(fwEntity &entity, void *)
|
|
{
|
|
if(Unlikely(IsEntityFrozen((CDynamicEntity*)&entity) || entity.IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
Assert(((CEntity *) &entity)->GetIsDynamic());
|
|
|
|
CDynamicEntity* pDynamicEntity = (CDynamicEntity*)&entity;
|
|
pDynamicEntity->StartAnimUpdateAfterCamera(fwTimer::GetTimeStep());
|
|
}
|
|
|
|
void CGameWorld::SceneUpdateUpdateCb(fwEntity &entity, void *userData)
|
|
{
|
|
Assert(userData);
|
|
SceneUpdateParameters *params = (SceneUpdateParameters *) userData;
|
|
CEntity *pEntity = (CEntity *) &entity;
|
|
|
|
#if !__FINAL
|
|
CGameWorld::ms_pProcessingEntity = pEntity;
|
|
#endif
|
|
|
|
if (pEntity->IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD))
|
|
{
|
|
if( params->iNumEntitiesToDelete < MAX_ENTITIES_TO_DELETE_PER_FRAME )
|
|
{
|
|
// Add the entity to a removal list rather than deleting it directly.
|
|
// Deleting directly during this process can cause crashes.
|
|
params->apEntitiesToDelete[params->iNumEntitiesToDelete] = pEntity;
|
|
++(params->iNumEntitiesToDelete);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// If the entity has been frozen
|
|
bool bFrozen = IsEntityFrozen(pEntity);
|
|
if(bFrozen)
|
|
{
|
|
Assert(!CheckEntityFrozenByInterior(pEntity) || !NetworkUtils::IsNetworkClone(pEntity));
|
|
pEntity->ProcessFrozen();
|
|
return;
|
|
}
|
|
|
|
#if __DEV
|
|
if(params->bPrintProcessList)
|
|
grcDebugDraw::AddDebugOutput("Processing %s", CModelInfo::GetBaseModelInfoName(entity.GetModelId()));
|
|
#endif
|
|
|
|
if (Verifyf(!pEntity->IsBaseFlagSet(fwEntity::REMOVE_FROM_WORLD), "CGameWorld::SceneUpdateUpdateCb - REMOVE_FROM_WORLD flag should already have been checked earlier in this function"))
|
|
{
|
|
Entity_PF_Start(pEntity);
|
|
|
|
if( !debugBlockProcessControlOfEntity(pEntity) ) // always false in non __DEV build
|
|
{
|
|
// check for peds with NULL default tasks
|
|
#if __ASSERT
|
|
if(pEntity->GetIsTypePed() && ((CPed *)pEntity)->GetPedIntelligence()->GetTaskDefault() == 0)
|
|
{
|
|
CNetObjPed *netobjPed = static_cast<CNetObjPed *>(NetworkUtils::GetNetworkObjectFromEntity(pEntity));
|
|
Assertf(0, "%s has no default task!", netobjPed ? (const char *)netobjPed->GetLogName() : "Non-networked ped");
|
|
}
|
|
#endif // __ASSERT
|
|
|
|
netObject *pNetworkObject = NetworkUtils::GetNetworkObjectFromEntity(pEntity);
|
|
|
|
bool bRequiresProcessControl = false;
|
|
|
|
if (pNetworkObject)
|
|
bRequiresProcessControl = static_cast<CNetObjEntity *>(pNetworkObject)->ProcessControl();
|
|
|
|
if (!NetworkUtils::IsNetworkClone(pEntity) REPLAY_ONLY(&& CReplayMgr::ShouldProcessControl(pEntity)))
|
|
{
|
|
bRequiresProcessControl = pEntity->ProcessControl();
|
|
}
|
|
|
|
#if __BANK
|
|
if (pEntity->GetIsTypePed())
|
|
{
|
|
CPed* pPed = (CPed*)pEntity;
|
|
pPed->ProcessHeadBlendDebug();
|
|
pPed->ProcessStreamingDebug();
|
|
}
|
|
#endif // __BANK
|
|
|
|
// ProcessControl should return false if it doesn't need to be on the process control list any more
|
|
// most objects don't need to when they come to rest
|
|
if(!bRequiresProcessControl)
|
|
{
|
|
if(params->iNumEntitiesToRemoveFromProcCont < MAX_ENTITIES_TO_REMOVE_FROM_PROCCONT_LIST)
|
|
{
|
|
Assert(pEntity->GetIsDynamic());
|
|
params->apEntitiesToRemoveFromProcContList[params->iNumEntitiesToRemoveFromProcCont] = (CDynamicEntity*)pEntity;
|
|
params->iNumEntitiesToRemoveFromProcCont++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Entity_PF_Stop(pEntity);
|
|
}
|
|
}
|
|
|
|
void CGameWorld::SceneUpdateUpdatePerceptionCb(fwEntity &entity, void *userData)
|
|
{
|
|
CPed *pPed = (CPed*)&entity;
|
|
if(pPed->PopTypeIsMission())
|
|
{
|
|
FastAssert(userData);
|
|
float SenseRange = *((float *) userData);
|
|
|
|
pPed->GetPedIntelligence()->GetPedPerception().SetSeeingRange(SenseRange);
|
|
pPed->GetPedIntelligence()->GetPedPerception().SetHearingRange(SenseRange);
|
|
}
|
|
}
|
|
|
|
void CGameWorld::SceneUpdatePreSimPhysicsUpdateCb(fwEntity &entity, void *userData)
|
|
{
|
|
CPhysics::PreSimControl *preSimControl = (CPhysics::PreSimControl *) userData;
|
|
CPhysical *pEntity = (CPhysical*) &entity;
|
|
Assert(pEntity->GetIsPhysical());
|
|
|
|
FastAssert(preSimControl);
|
|
|
|
if(!pEntity->m_nDEflags.bFrozenByInterior)
|
|
{
|
|
// if we run out of space in the postponed list, then stop entities from postponing. Note: Add in 2nd postpone entities to preserve old behaviour (max is always less than MAX_POSTPONED_ENTITIES).
|
|
bool bCanPostpone = (bool)( (preSimControl->nNumPostponedEntities + preSimControl->nNumPostponedEntities2) < CPhysics::PreSimControl::MAX_POSTPONED_ENTITIES);
|
|
|
|
ePhysicsResult nReturn = pEntity->ProcessPhysics(preSimControl->fTimeStep, bCanPostpone, preSimControl->nSlice);
|
|
|
|
// if entity was postponed then add it to the list
|
|
if(nReturn == PHYSICS_POSTPONE)
|
|
{
|
|
//Assert(nNumPostponedEntities < MAX_POSTPONED_ENTITIES);
|
|
if(preSimControl->nNumPostponedEntities < CPhysics::PreSimControl::MAX_POSTPONED_ENTITIES)
|
|
{
|
|
preSimControl->aPostponedEntities[preSimControl->nNumPostponedEntities] = pEntity;
|
|
(preSimControl->nNumPostponedEntities)++;
|
|
}
|
|
}
|
|
else if (nReturn == PHYSICS_NEED_SECOND_PASS)
|
|
{
|
|
Assert(preSimControl->nNumSecondPassEntities < CPhysics::PreSimControl::GetMaxSecondPassEntities());
|
|
preSimControl->aSecondPassEntities[(preSimControl->nNumSecondPassEntities)++] = pEntity;
|
|
}
|
|
else if (nReturn == PHYSICS_POSTPONE2)
|
|
{
|
|
//Assert(nNumPostponedEntities2 < MAX_POSTPONED_ENTITIES);
|
|
if(preSimControl->nNumPostponedEntities2 < CPhysics::PreSimControl::MAX_POSTPONED_ENTITIES)
|
|
{
|
|
preSimControl->aPostponedEntities2[preSimControl->nNumPostponedEntities2] = pEntity;
|
|
(preSimControl->nNumPostponedEntities2)++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(CPhysics::GetIsFirstTimeSlice(preSimControl->nSlice))
|
|
{
|
|
netObject *networkObject = NetworkUtils::GetNetworkObjectFromEntity(pEntity);
|
|
|
|
if (networkObject && networkObject->IsClone() && networkObject->GetNetBlender())
|
|
{
|
|
// update the network blender that overrides the physics and blends the entity from
|
|
// one position to another
|
|
if (networkObject->CanBlend())
|
|
networkObject->GetNetBlender()->Update();
|
|
}
|
|
}
|
|
}
|
|
|
|
// need to prepare the no collision needs reset, and reset here if it wasn't reset from the last frame
|
|
if(pEntity->GetIsPhysical() && ((CPhysical*)pEntity)->GetNoCollisionEntity())
|
|
{
|
|
((CPhysical*)pEntity)->PrepareNoCollisionReset();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGameWorld::SceneUpdatePhysicsPreUpdateCb(fwEntity &entity, void *)
|
|
{
|
|
CPhysical *pEntity = (CPhysical*) &entity;
|
|
Assert(pEntity->GetIsPhysical());
|
|
|
|
pEntity->ProcessPrePhysics();
|
|
}
|
|
|
|
void CGameWorld::SceneUpdatePhysicsPostUpdateCb(fwEntity &entity, void *)
|
|
{
|
|
CPhysical *pEntity = (CPhysical*) &entity;
|
|
Assert(pEntity->GetIsPhysical());
|
|
|
|
pEntity->ProcessPostPhysics();
|
|
}
|
|
|
|
// Flag whether dynamic objects are visible in any active view ports this
|
|
// frame, and record the distance to the closest one if so.
|
|
void CGameWorld::ProcessDynamicObjectVisibility()
|
|
{
|
|
PF_AUTO_PUSH_TIMEBAR("ProcessDynamicObjectVisibility");
|
|
|
|
// We use an eight bit frame counter in CDynamicEntity to avoid having
|
|
// to reset anything every frame. It will wrap around though, so if we are
|
|
// not careful, entities that are not visible could appear to be visible for
|
|
// one frame. To avoid that, we go through the entities and rejuvenate the
|
|
// frame counts. This may take 0.1 ms or so, but since it only happens once
|
|
// per several seconds, it shouldn't be bad compared to what we were doing before.
|
|
if(!ms_DynEntVisCountdownToRejuvenate)
|
|
{
|
|
fwSceneUpdate::Update(SU_RESET_VISIBILITY);
|
|
|
|
ms_DynEntVisCountdownToRejuvenate = 253;
|
|
}
|
|
else
|
|
{
|
|
ms_DynEntVisCountdownToRejuvenate--;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#if __ASSERT
|
|
// Check Linkage
|
|
void CheckLinkage(CPhysical* pPhysical)
|
|
{
|
|
fwAttachmentEntityExtension *extension = pPhysical->GetAttachmentExtension();
|
|
|
|
if(extension && extension->GetIsRootParentAttachment())
|
|
{
|
|
fwEntity *pThisChild = extension->GetChildAttachment();
|
|
if(pThisChild)
|
|
{
|
|
// Get the childs parent
|
|
fwAttachmentEntityExtension *pChildExtension = pThisChild->GetAttachmentExtension();
|
|
fwEntity *pChildParent = pChildExtension->GetAttachParentForced();
|
|
|
|
const char *pParentName = pPhysical->GetModelName();
|
|
const char *pChildName = pThisChild->GetModelName();
|
|
const char *pChildParentName = pChildParent->GetModelName();
|
|
|
|
if( pChildParent != pPhysical )
|
|
{
|
|
Printf("Linkage is broken, Child doesn't have correct Parent!!\n");
|
|
Printf("Root %s, child %s, parent %s\n", pParentName, pChildName, pChildParentName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif //__ASSERT
|
|
|
|
|
|
|
|
void CGameWorld::ProcessDelayedAttachmentsForPhysical(const CPhysical *const pPhysical)
|
|
{
|
|
for(int iAttachIndex = 0; iAttachIndex < ms_nNumDelayedRootAttachments; iAttachIndex++)
|
|
{
|
|
if(ms_delayedRootAttachments[iAttachIndex] && ms_delayedRootAttachments[iAttachIndex] == pPhysical)
|
|
{
|
|
ms_delayedRootAttachments[iAttachIndex]->ProcessAllAttachments();
|
|
|
|
// NULL out this entry so that it won't get processed again in ProcessAfterPreRender.
|
|
ms_delayedRootAttachments[iAttachIndex] = NULL;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Name : ProcessAttachmentAfterAllMovement
|
|
// Purpose : Process physical attachment if possible or add to list for processing later
|
|
// Paramaters : pPhysical: The physical to process
|
|
// Returns : none
|
|
|
|
void CGameWorld::ProcessAttachmentAfterAllMovement(CPhysical* pPhysical)
|
|
{
|
|
fwAttachmentEntityExtension *extension = pPhysical->GetAttachmentExtension();
|
|
|
|
if(pPhysical->m_nPhysicalFlags.bIsRootOfOtherAttachmentTree)
|
|
{
|
|
pPhysical->ProcessOtherAttachments();
|
|
}
|
|
|
|
if(extension && extension->GetIsRootParentAttachment())
|
|
{
|
|
if(extension->GetRequiresPostPreRender())
|
|
{
|
|
// This is a complex attachment and needs up to date skeletons before processing
|
|
// so delay it for now
|
|
#if __ASSERT
|
|
if ( ms_nNumDelayedRootAttachments >= MAX_NUM_POST_PRE_RENDER_ATTACHMENTS )
|
|
{
|
|
char buffer[512];
|
|
diagLoggedPrintf( "MAX_NUM_POST_PRE_RENDER_ATTACHMENTS\n"
|
|
"-----------------------------------\n");
|
|
for ( int i = 0; i < MAX_NUM_POST_PRE_RENDER_ATTACHMENTS; ++i )
|
|
{
|
|
CPhysical *pStorePhysical = ms_delayedRootAttachments[i];
|
|
if ( pStorePhysical )
|
|
{
|
|
fwAttachmentEntityExtension *pPhysExtension = pStorePhysical->GetAttachmentExtension();
|
|
|
|
fwEntity *pfwParentResult = pPhysExtension->GetAttachParentForced();
|
|
fwEntity *pfwChildResult = pPhysExtension->GetChildAttachment();
|
|
fwEntity *pfwSiblingResult = pPhysExtension->GetSiblingAttachment();
|
|
|
|
const char *pParentString = "NULL";
|
|
if(pfwParentResult)
|
|
pParentString = pfwParentResult->GetModelName();
|
|
const char *pChildString = "NULL";
|
|
if(pfwChildResult)
|
|
pChildString = pfwChildResult->GetModelName();
|
|
const char *pSiblingString = "NULL";
|
|
if(pfwSiblingResult)
|
|
pSiblingString = pfwSiblingResult->GetModelName();
|
|
|
|
sprintf( buffer, "[%2.2d] 0x%p : \"%s\" on (Parent)0x%p : \"%s\" (Child) 0x%p : \"%s\" (Sibling) 0x%p : \"%s\"\n", i, pStorePhysical, pStorePhysical->GetDebugName(),
|
|
pfwParentResult, pParentString,
|
|
pfwChildResult, pChildString,
|
|
pfwSiblingResult, pSiblingString);
|
|
|
|
|
|
diagLoggedPrintf( "%s", buffer );
|
|
|
|
}
|
|
}
|
|
|
|
for ( int i = 0; i < MAX_NUM_POST_PRE_RENDER_ATTACHMENTS; ++i )
|
|
{
|
|
CPhysical *pStorePhysical = ms_delayedRootAttachments[i];
|
|
if ( pStorePhysical )
|
|
{
|
|
CheckLinkage( pStorePhysical );
|
|
}
|
|
}
|
|
|
|
}
|
|
#endif
|
|
if(Verifyf((ms_nNumDelayedRootAttachments < MAX_NUM_POST_PRE_RENDER_ATTACHMENTS),"Run out of space to store delayed attachments - see TTY for list"))
|
|
{
|
|
ms_delayedRootAttachments[ms_nNumDelayedRootAttachments++] = pPhysical;
|
|
// don't return as attachments still need to be updated for camera
|
|
//return;
|
|
}
|
|
}
|
|
|
|
pPhysical->ProcessAllAttachments();
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
// Name : FindObjectsInRange
|
|
// Purpose : Generates a list of entities within a certain range of a certain coordinate.
|
|
// Parameters :
|
|
// Returns : none
|
|
|
|
void CGameWorld::FindObjectsInRange(const Vector3 &Coors, float Range, bool UNUSED_PARAM(Dist2D), s32 *pNum, s32 MaxNum, CEntity **ppResults, bool bCheckBuildings, bool bCheckVehicles, bool bCheckPeds, bool bCheckObjects, bool bCheckDummies)
|
|
{
|
|
CFindData findData(ppResults, MaxNum);
|
|
|
|
s32 iTypeFlags = 0;
|
|
if( bCheckBuildings ) {iTypeFlags |= ENTITY_TYPE_MASK_BUILDING;}
|
|
if( bCheckVehicles ) {iTypeFlags |= ENTITY_TYPE_MASK_VEHICLE;}
|
|
if( bCheckPeds ) {iTypeFlags |= ENTITY_TYPE_MASK_PED;}
|
|
if( bCheckObjects ) {iTypeFlags |= ENTITY_TYPE_MASK_OBJECT;}
|
|
if( bCheckDummies ) {iTypeFlags |= ENTITY_TYPE_MASK_DUMMY_OBJECT;}
|
|
|
|
s32 iControlFlags = SEARCH_LOCATION_INTERIORS|SEARCH_LOCATION_EXTERIORS;
|
|
fwIsSphereInside intersection(spdSphere(RCC_VEC3V(Coors), ScalarV(Range)));
|
|
// Scan through all intersecting entities finding all entities before
|
|
CGameWorld::ForAllEntitiesIntersecting( &intersection,
|
|
CGameWorld::FindEntitiesCB,
|
|
&findData,
|
|
iTypeFlags,
|
|
iControlFlags );
|
|
|
|
if( pNum )
|
|
{
|
|
*pNum = findData.GetNumEntities();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Name : FindObjectsKindaColliding
|
|
// Purpose : Generates a list of entities that we collide
|
|
// with (only taking into account the bounding
|
|
// sphere of that object).
|
|
// Parameters :
|
|
// Returns : none
|
|
|
|
void CGameWorld::FindObjectsKindaColliding(const Vector3 &Coors, float Range, bool useBoundBoxes, bool UNUSED_PARAM(Dist2D), s32 *pNum, s32 MaxNum, CEntity **ppResults, bool bCheckBuildings, bool bCheckVehicles, bool bCheckPeds, bool bCheckObjects, bool bCheckDummies)
|
|
{
|
|
spdSphere testSphere(RCC_VEC3V(Coors), ScalarV(Range));
|
|
CFindData findData(ppResults, MaxNum);
|
|
|
|
s32 iTypeFlags = 0;
|
|
if( bCheckBuildings ) {iTypeFlags |= ENTITY_TYPE_MASK_BUILDING;}
|
|
if( bCheckVehicles ) {iTypeFlags |= ENTITY_TYPE_MASK_VEHICLE;}
|
|
if( bCheckPeds ) {iTypeFlags |= ENTITY_TYPE_MASK_PED;}
|
|
if( bCheckObjects ) {iTypeFlags |= ENTITY_TYPE_MASK_OBJECT;}
|
|
if( bCheckDummies ) {iTypeFlags |= ENTITY_TYPE_MASK_DUMMY_OBJECT;}
|
|
|
|
s32 iControlFlags = SEARCH_LOCATION_INTERIORS|SEARCH_LOCATION_EXTERIORS;
|
|
s32 iOptionFlags = 0;
|
|
if (!bCheckBuildings && !bCheckDummies)
|
|
{
|
|
iOptionFlags |= SEARCH_OPTION_DYNAMICS_ONLY;
|
|
}
|
|
|
|
if( useBoundBoxes )
|
|
{
|
|
fwIsSphereIntersectingBBApprox intersection(testSphere);
|
|
// Scan through all intersecting entities finding all entities before
|
|
ForAllEntitiesIntersecting( &intersection,
|
|
CGameWorld::FindEntitiesCB,
|
|
&findData,
|
|
iTypeFlags,
|
|
iControlFlags,
|
|
SEARCH_LODTYPE_HIGHDETAIL,
|
|
iOptionFlags);
|
|
}
|
|
else
|
|
{
|
|
fwIsSphereIntersecting intersection(testSphere);
|
|
|
|
// Scan through all intersecting entities finding all entities before
|
|
ForAllEntitiesIntersecting( &intersection,
|
|
CGameWorld::FindEntitiesCB,
|
|
&findData,
|
|
iTypeFlags,
|
|
iControlFlags,
|
|
SEARCH_LODTYPE_HIGHDETAIL,
|
|
iOptionFlags);
|
|
}
|
|
|
|
if( pNum )
|
|
{
|
|
*pNum = findData.GetNumEntities();
|
|
}
|
|
}
|
|
|
|
// Name : FindObjectsIntersectingCube
|
|
// Purpose : Generates a list of entities that at least
|
|
// intersect with a cube
|
|
// Parameters :
|
|
// Returns : none
|
|
|
|
void CGameWorld::FindObjectsIntersectingCube(Vector3::Param CoorsMin, Vector3::Param CoorsMax, s32 *pNum, s32 MaxNum, CEntity **ppResults, bool bCheckBuildings, bool bCheckVehicles, bool bCheckPeds, bool bCheckObjects, bool bCheckDummies)
|
|
{
|
|
CFindData findData(ppResults, MaxNum);
|
|
|
|
s32 iTypeFlags = 0;
|
|
if( bCheckBuildings ) {iTypeFlags |= ENTITY_TYPE_MASK_BUILDING;}
|
|
if( bCheckVehicles ) {iTypeFlags |= ENTITY_TYPE_MASK_VEHICLE;}
|
|
if( bCheckPeds ) {iTypeFlags |= ENTITY_TYPE_MASK_PED;}
|
|
if( bCheckObjects ) {iTypeFlags |= ENTITY_TYPE_MASK_OBJECT;}
|
|
if( bCheckDummies ) {iTypeFlags |= ENTITY_TYPE_MASK_DUMMY_OBJECT;}
|
|
|
|
if (pNum) *pNum = 0;
|
|
|
|
s32 iOptionFlags = 0;
|
|
if (!bCheckBuildings && !bCheckDummies)
|
|
{
|
|
iOptionFlags |= SEARCH_OPTION_DYNAMICS_ONLY;
|
|
}
|
|
|
|
s32 iControlFlags = SEARCH_LOCATION_INTERIORS|SEARCH_LOCATION_EXTERIORS;
|
|
fwIsBoxIntersectingApprox intersection(spdAABB(RCC_VEC3V(CoorsMin), RCC_VEC3V(CoorsMax)));
|
|
// Scan through all intersecting entities finding all entities before
|
|
ForAllEntitiesIntersecting( &intersection,
|
|
CGameWorld::FindEntitiesCB,
|
|
&findData,
|
|
iTypeFlags,
|
|
iControlFlags,
|
|
SEARCH_LODTYPE_HIGHDETAIL,
|
|
iOptionFlags);
|
|
|
|
if( pNum )
|
|
{
|
|
*pNum = findData.GetNumEntities();
|
|
}
|
|
|
|
}
|
|
|
|
#if __BANK
|
|
#define OutOfMapDebug(fmt,...) { if (ms_detailedOutOrMapSpew) Displayf( "[out of map] " fmt, ##__VA_ARGS__ ); }
|
|
#else
|
|
#define OutOfMapDebug(fmt,...)
|
|
#endif
|
|
|
|
// Name : RemoveFallenPeds
|
|
// Purpose : Removes peds that have fallen through the map and are about to crash the game.
|
|
// Parameters :
|
|
// Returns :
|
|
#define LOWESTPEDZ (-200.0f)
|
|
#define PEDS_TO_RETURN (192)
|
|
|
|
void CGameWorld::RemoveFallenPeds()
|
|
{
|
|
CSpatialArrayNode* results[PEDS_TO_RETURN];
|
|
int numFound = CPed::ms_spatialArray->FindBelowZ(ScalarV(LOWESTPEDZ), results, PEDS_TO_RETURN);
|
|
CPed::Pool* pPedPool = CPed::GetPool();
|
|
for(int i = 0; i < numFound; i++)
|
|
{
|
|
CPed* pPed = CPed::GetPedFromSpatialArrayNode(results[i]);
|
|
if(pPed && pPedPool->IsValidPtr(pPed) && !pPed->GetPedConfigFlag(CPED_CONFIG_FLAG_InVehicle))
|
|
{
|
|
CGameWorld::RemoveOutOfMapPed(pPed, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// NAME : RemoveOutOfMapPed
|
|
// PURPOSE : Deal with rescue or removal of peds who have fallen through the world
|
|
// We can identify ped who fell from interiors, so can use this to help locate script errors.
|
|
void CGameWorld::RemoveOutOfMapPed(CPed* pPed, bool ASSERT_ONLY(bOutOfInterior))
|
|
{
|
|
const Vector3 vOriginalPedPosition = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
|
|
if(pPed->IsNetworkClone() || (pPed->GetNetworkObject() && !pPed->GetNetworkObject()->CanDelete()))
|
|
{
|
|
// if this is a network clone we don't let it fall below the lowest position while
|
|
// we wait for a network update once the vehicle has been repositioned on the owning machine
|
|
Vector3 newPedPos = vOriginalPedPosition;
|
|
newPedPos.z = LOWESTPEDZ;
|
|
|
|
// need to set ragdolls back to animated before you can warp their position
|
|
if(pPed->GetUsingRagdoll())
|
|
pPed->SwitchToAnimated();
|
|
|
|
pPed->SetPosition(newPedPos, true, true, true);
|
|
netObject *networkObject = pPed->GetNetworkObject();
|
|
netBlender *blender = networkObject ? networkObject->GetNetBlender() : 0;
|
|
|
|
if(blender)
|
|
{
|
|
blender->Reset();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if !__FINAL
|
|
const char *name = pPed->GetModelName();
|
|
if(pPed->GetNetworkObject())
|
|
{
|
|
name = pPed->GetNetworkObject()->GetLogName();
|
|
}
|
|
Displayf("&&&&&&Another ped %s has fallen through the map&&&&&&&&&& %f %f %f\n",
|
|
name,
|
|
vOriginalPedPosition.x, vOriginalPedPosition.y, vOriginalPedPosition.z);
|
|
#endif
|
|
|
|
StatsInterface::FallenThroughTheMap(pPed);
|
|
|
|
// This ped is to be placed back on the map.
|
|
if (pPed->PopTypeIsRandomNonPermanent() && (!pPed->IsPlayer()))
|
|
{
|
|
// Just remove this guy
|
|
CPedFactory::GetFactory()->DestroyPed(pPed);
|
|
}
|
|
else
|
|
{
|
|
// Road nodes are probably our safest positions to use, since they've been
|
|
// placed by hand and will always be in sensible locations
|
|
CNodeAddress NearestNode = ThePaths.FindNodeClosestToCoors(vOriginalPedPosition);
|
|
if (NearestNode.IsEmpty())
|
|
{
|
|
// Failing that check the navmesh for a position. If this is the player, then it
|
|
// will probably work since navmeshes stream in around his position.
|
|
Vector3 vNear(vOriginalPedPosition.x, vOriginalPedPosition.y, 0.0f);
|
|
Vector3 vSafePos;
|
|
if(CPathServer::GetClosestPositionForPed(vNear, vSafePos, 10000.0f) != ENoPositionFound)
|
|
{
|
|
pPed->Teleport(vSafePos, pPed->GetCurrentHeading());
|
|
pPed->SetVelocity(ORIGIN);
|
|
}
|
|
else
|
|
{
|
|
#if __ASSERT
|
|
if(pPed->PopTypeIsMission())
|
|
{
|
|
const char * networkName = pPed->GetDebugName(); //debug name resolves to network name if we have one.
|
|
const char * modelName = pPed->GetModelName();
|
|
const char * debugName = pPed->m_debugPedName ? pPed->m_debugPedName : "(none)";
|
|
|
|
const char* scriptName = "unknown script";
|
|
|
|
const CGameScriptId* scriptId = pPed->GetScriptThatCreatedMe();
|
|
if (scriptId)
|
|
{
|
|
scriptName = scriptId->GetScriptName();
|
|
}
|
|
|
|
if(bOutOfInterior)
|
|
{
|
|
Assertf(false, "Mission ped (name:%s, model:%s, network:%s) has fallen out of an interior. Ped owned by %s Coords (%.1f,%.1f,%.1f)", debugName, modelName, networkName, scriptName, vOriginalPedPosition.x, vOriginalPedPosition.y, vOriginalPedPosition.z);
|
|
}
|
|
else
|
|
{
|
|
Assertf(false, "Mission ped (name:%s, model:%s, network:%s) has fallen through the bottom of the map. Ped owned by %s Coords (%.1f,%.1f,%.1f)", debugName, modelName, networkName, scriptName, vOriginalPedPosition.x, vOriginalPedPosition.y, vOriginalPedPosition.z);
|
|
}
|
|
}
|
|
#endif
|
|
// Final chance to place the ped some where sensible.
|
|
// This code just teleports the ped a few meters above the low cutoff position, at which point he
|
|
// will fall below this cutoff again and thus come back into this code.
|
|
|
|
pPed->Teleport(Vector3(vOriginalPedPosition.x, vOriginalPedPosition.y, LOWESTPEDZ + 5.0f), pPed->GetCurrentHeading());
|
|
pPed->SetVelocity(VEC3_ZERO);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Road node found
|
|
Vector3 nodePos(0.0f, 0.0f, 0.0f);
|
|
ThePaths.FindNodePointer(NearestNode)->GetCoors(nodePos);
|
|
nodePos.z += 1.0f;
|
|
pPed->Teleport(nodePos, pPed->GetCurrentHeading());
|
|
pPed->SetVelocity(ORIGIN);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Name : RemoveFallenCars
|
|
// Purpose : Removes cars that have fallen through the map and are about to crash the game.
|
|
// Parameters :
|
|
// Returns :
|
|
#define LOWESTCARZ (-200.0f)
|
|
#define CARS_TO_RETURN (192)
|
|
|
|
void CGameWorld::RemoveFallenCars()
|
|
{
|
|
CSpatialArrayNode* results[CARS_TO_RETURN];
|
|
int numFound = CVehicle::ms_spatialArray->FindBelowZ(ScalarV(LOWESTCARZ), results, CARS_TO_RETURN);
|
|
CVehicle::Pool* pVehiclePool = CVehicle::GetPool();
|
|
for(int i = 0;i < numFound; i++)
|
|
{
|
|
CVehicle* pVehicle = CVehicle::GetVehicleFromSpatialArrayNode(results[i]);
|
|
if(pVehicle && pVehiclePool->IsValidPtr(pVehicle) && !(pVehicle->GetIsInWater() && pVehicle->ContainsPlayer()))
|
|
CGameWorld::RemoveOutOfMapVehicle( pVehicle );
|
|
}
|
|
}
|
|
|
|
void CGameWorld::RemoveOutOfMapVehicle(CVehicle* pVehicle)
|
|
{
|
|
const Vector3 vOriginalVehiclePosition = VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition());
|
|
|
|
#if !__NO_OUTPUT
|
|
const char *name = "vehicle";
|
|
if(pVehicle->GetNetworkObject())
|
|
{
|
|
name = pVehicle->GetNetworkObject()->GetLogName();
|
|
}
|
|
Displayf("&&&&&&Another %s has fallen through the map&&&&&&&&&& %f %f %f\n", name, vOriginalVehiclePosition.x, vOriginalVehiclePosition.y, vOriginalVehiclePosition.z);
|
|
#endif
|
|
|
|
OutOfMapDebug( "Vehicle %s (debug name) %s has fallen off the map", pVehicle->GetModelName(), pVehicle->GetDebugName() );
|
|
|
|
if(pVehicle->IsNetworkClone() || (pVehicle->GetNetworkObject() && !pVehicle->GetNetworkObject()->CanDelete()))
|
|
{
|
|
// if this is a network clone we don't let it fall below the lowest position while
|
|
// we wait for a network update once the vehicle has been repositioned on the owning machine
|
|
Vector3 newCarPos = vOriginalVehiclePosition;
|
|
newCarPos.z = LOWESTCARZ;
|
|
OutOfMapDebug( "Network vehicle, teleporting back to (%f,%f,%f)", newCarPos.x, newCarPos.y, newCarPos.z );
|
|
pVehicle->SetPosition(newCarPos, true, true, true);
|
|
netObject *networkObject = pVehicle->GetNetworkObject();
|
|
netBlender *blender = networkObject ? networkObject->GetNetBlender() : 0;
|
|
|
|
if(blender)
|
|
{
|
|
blender->Reset();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OutOfMapDebug( "Teleport / destroy conditions:" );
|
|
OutOfMapDebug( "PopTypeIsMission: %s", pVehicle->PopTypeIsMission() ? "true" : "false" );
|
|
OutOfMapDebug( "RenderScorched: %s", pVehicle->m_nPhysicalFlags.bRenderScorched ? "true" : "false" );
|
|
OutOfMapDebug( "PlayerVehicle: %s", (pVehicle == FindPlayerVehicle()) ? "true" : "false" );
|
|
OutOfMapDebug( "HasMissionChars: %s", pVehicle->HasMissionCharsInIt() ? "true" : "false" );
|
|
OutOfMapDebug( "DriverIsPlayer: %s", (pVehicle->GetDriver() && pVehicle->GetDriver()->IsPlayer()) ? "true" : "false" );
|
|
OutOfMapDebug( "PopTypeIsRandom: %s", pVehicle->PopTypeIsRandom() ? "true" : "false" );
|
|
|
|
#if __ASSERT
|
|
const char* scriptName = "unknown script";
|
|
|
|
const CGameScriptId* scriptId = pVehicle->GetScriptThatCreatedMe();
|
|
if (scriptId)
|
|
{
|
|
scriptName = scriptId->GetScriptName();
|
|
}
|
|
#endif // __ASSERT
|
|
|
|
if( (pVehicle->PopTypeIsMission() && !pVehicle->m_nPhysicalFlags.bRenderScorched) || pVehicle == FindPlayerVehicle() || pVehicle->HasMissionCharsInIt() ||
|
|
(pVehicle->GetDriver() && pVehicle->GetDriver()->IsLocalPlayer()) )
|
|
{
|
|
CNodeAddress NearestNode = ThePaths.FindNodeClosestToCoors(vOriginalVehiclePosition);
|
|
|
|
#if __DEV
|
|
static int s_NumOutOfWorldDumps = 0;
|
|
if(s_NumOutOfWorldDumps++ < 10)
|
|
{
|
|
pVehicle->DumpOutOfWorldInfo();
|
|
}
|
|
#endif // __DEV
|
|
|
|
|
|
if (NearestNode.IsEmpty())
|
|
{
|
|
Assertf(false,"Vehicle (%p) (%s)(%s) has fallen through the world. Failed to find a nearby path node so warping to <<%f, %f, %f>> : pop(%s) : scorched(%d) : driveable(%d) : playerVehicle(%d) : hasMissionChar(%d), playerDriver(%d), created by %s, AllowFreezeIfNoCollision(%d), ShouldFixIfNoCollision(%d), Collider(%d), ShouldFixIfNoCollisionLoadedAroundPosition(%d)",
|
|
pVehicle, pVehicle->GetModelName(), pVehicle->GetDebugName(),
|
|
vOriginalVehiclePosition.x, vOriginalVehiclePosition.y, 0.0f,
|
|
CTheScripts::GetPopTypeName(pVehicle->PopTypeGet()), pVehicle->m_nPhysicalFlags.bRenderScorched, pVehicle->m_nVehicleFlags.bIsThisADriveableCar, pVehicle == FindPlayerVehicle(), pVehicle->HasMissionCharsInIt(),
|
|
(pVehicle->GetDriver() && pVehicle->GetDriver()->IsPlayer()), scriptName,
|
|
pVehicle->m_nPhysicalFlags.bAllowFreezeIfNoCollision, pVehicle->m_nVehicleFlags.bShouldFixIfNoCollision,
|
|
pVehicle->GetCollider() != NULL, pVehicle->ShouldFixIfNoCollisionLoadedAroundPosition());
|
|
|
|
OutOfMapDebug( "Nearest node empty, teleporting vehicle to (%f %f %f)", vOriginalVehiclePosition.x, vOriginalVehiclePosition.y, 0.0f );
|
|
pVehicle->Teleport(Vector3(vOriginalVehiclePosition.x, vOriginalVehiclePosition.y, 0.0f), 0.0f);
|
|
}
|
|
else
|
|
{
|
|
Vector3 nodePos(0.0f, 0.0f, 0.0f);
|
|
ThePaths.FindNodePointer(NearestNode)->GetCoors(nodePos);
|
|
nodePos.z += 3.0f;
|
|
|
|
Assertf(false,"Vehicle (%p) (%s)(%s)(%s) has fallen through the world. Warping to nearby path node <<%f, %f, %f>> : pop(%s) : scorched(%d) : driveable(%d) : playerVehicle(%d) : hasMissionChar(%d), playerDriver(%d), created by %s, AllowFreezeIfNoCollision(%d), ShouldFixIfNoCollision(%d), Collider(%d), ShouldFixIfNoCollisionLoadedAroundPosition(%d)",
|
|
pVehicle, pVehicle->GetModelName(), pVehicle->GetDebugName(),
|
|
pVehicle->GetNetworkObject() ? pVehicle->GetNetworkObject()->GetLogName() : "",
|
|
nodePos.x, nodePos.y, nodePos.z,
|
|
CTheScripts::GetPopTypeName(pVehicle->PopTypeGet()), pVehicle->m_nPhysicalFlags.bRenderScorched, pVehicle->m_nVehicleFlags.bIsThisADriveableCar, pVehicle == FindPlayerVehicle(), pVehicle->HasMissionCharsInIt(),
|
|
(pVehicle->GetDriver() && pVehicle->GetDriver()->IsPlayer()), scriptName,
|
|
pVehicle->m_nPhysicalFlags.bAllowFreezeIfNoCollision, pVehicle->m_nVehicleFlags.bShouldFixIfNoCollision,
|
|
pVehicle->GetCollider() != NULL, pVehicle->ShouldFixIfNoCollisionLoadedAroundPosition());
|
|
|
|
OutOfMapDebug( "Nearest node found, teleporting vehicle to (%f %f %f)", nodePos.x, nodePos.y, nodePos.z );
|
|
pVehicle->Teleport(nodePos, 0.0f, false);
|
|
}
|
|
|
|
pVehicle->SetVelocity(ORIGIN);
|
|
}
|
|
else if (pVehicle->PopTypeIsRandom())
|
|
{
|
|
OutOfMapDebug( "Destroying vehicle" );
|
|
CVehicleFactory::GetFactory()->Destroy(pVehicle);
|
|
}
|
|
else if ( pVehicle->PopTypeGet() == POPTYPE_REPLAY )
|
|
{
|
|
OutOfMapDebug( "Replay following out of world vehicle - will follow path of original recorded vehicle");
|
|
} else
|
|
{
|
|
OutOfMapDebug( "Vehicle not teleported nor destroyed" );
|
|
|
|
// script shouldn't maintain refs to burnt out vehicles, otherwise this can happen:
|
|
Assertf(false,"Vehicle (%p) removal not handled (%s)(%s) : pop(%s) : scorched(%d) : driveable(%d) : playerVehicle(%d) : hasMissionChar(%d), driver(%s), playerDriver(%d), created by %s, AllowFreezeIfNoCollision(%d), ShouldFixIfNoCollision(%d), status(%d), cloned(%d)",
|
|
pVehicle, pVehicle->GetModelName(), pVehicle->GetDebugName(),
|
|
CTheScripts::GetPopTypeName(pVehicle->PopTypeGet()), pVehicle->m_nPhysicalFlags.bRenderScorched, pVehicle->m_nVehicleFlags.bIsThisADriveableCar, pVehicle == FindPlayerVehicle(), pVehicle->HasMissionCharsInIt(),
|
|
(pVehicle->GetDriver() ? pVehicle->GetDriver()->GetDebugName() : "0"), (pVehicle->GetDriver() && pVehicle->GetDriver()->IsPlayer()), scriptName,
|
|
pVehicle->m_nPhysicalFlags.bAllowFreezeIfNoCollision, pVehicle->m_nVehicleFlags.bShouldFixIfNoCollision,
|
|
pVehicle->GetStatus(), pVehicle->IsNetworkClone());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Name : RemoveFallenObjects
|
|
// Purpose : Removes objects that have fallen through the map and are about to crash the game.
|
|
// Parameters :
|
|
// Returns :
|
|
#define LOWESTOBJECTZ (-200.0f)
|
|
#define FRAMES_TO_LOOP_THROUGH_OBJECTS (25)
|
|
void CGameWorld::RemoveFallenObjects()
|
|
{
|
|
CObject::Pool *pObjectPool = CObject::GetPool();
|
|
|
|
static s32 currentObject = (s32) pObjectPool->GetSize() - 1;
|
|
|
|
s32 numObjects = (s32) pObjectPool->GetNoOfUsedSpaces();
|
|
|
|
if(numObjects <= 0)
|
|
return; // no objects to check
|
|
|
|
s32 objsToProcess = MAX(numObjects / FRAMES_TO_LOOP_THROUGH_OBJECTS, 1); // timeslice the full slots, always at least one - since we know there are more than 0 objects in the pool
|
|
|
|
while(objsToProcess)
|
|
{
|
|
CObject *pObject = pObjectPool->GetSlot(currentObject);
|
|
if(pObject)
|
|
{
|
|
objsToProcess--;// this is a real object
|
|
const Vector3 vOriginalObjectPosition = VEC3V_TO_VECTOR3(pObject->GetTransform().GetPosition());
|
|
if (vOriginalObjectPosition.z < LOWESTOBJECTZ)
|
|
CGameWorld::RemoveOutOfMapObject( pObject );
|
|
}
|
|
|
|
currentObject--;
|
|
if(currentObject<0)
|
|
{
|
|
// wrap pool
|
|
currentObject = (s32) pObjectPool->GetSize() - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGameWorld::RemoveOutOfMapObject(CObject* pObject)
|
|
{
|
|
const Vector3 vOriginalObjectPosition = VEC3V_TO_VECTOR3(pObject->GetTransform().GetPosition());
|
|
|
|
if(pObject->IsNetworkClone())
|
|
{
|
|
// if this is a network clone we don't let it fall below the lowest position while
|
|
// we wait for a network update once the object has been repositioned on the owning machine
|
|
Vector3 newObjectPos = vOriginalObjectPosition;
|
|
newObjectPos.z = LOWESTOBJECTZ;
|
|
|
|
// B*1854957: This assert is suppressed for object teleports below so it makes sense to do it here too.
|
|
fwAttachmentEntityExtension* pExtension = pObject->GetAttachmentExtension();
|
|
if (pExtension) pExtension->SetAttachFlag(ATTACH_FLAG_DONT_ASSERT_ON_MATRIX_CHANGE, true); // Necessary to stop spurious assert in fwEntity::SetMatrix().
|
|
|
|
pObject->SetPosition(newObjectPos, true, true, true);
|
|
netObject *networkObject = pObject->GetNetworkObject();
|
|
netBlender *blender = networkObject ? networkObject->GetNetBlender() : 0;
|
|
|
|
if(blender)
|
|
{
|
|
blender->Reset();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if !__FINAL
|
|
const char *name = pObject->GetModelName();
|
|
if(pObject->GetNetworkObject())
|
|
{
|
|
name = pObject->GetNetworkObject()->GetLogName();
|
|
}
|
|
Displayf("&&&&&&Another Object %s has fallen through the map&&&&&&&&&& %f %f %f\n",
|
|
name,
|
|
vOriginalObjectPosition.x, vOriginalObjectPosition.y, vOriginalObjectPosition.z);
|
|
#endif
|
|
// This Object is to be placed back on the map.
|
|
if (pObject->GetOwnedBy() == ENTITY_OWNEDBY_RANDOM)
|
|
{
|
|
// If there is a related dummy not attached to a streamed IPL then delete it
|
|
CDummyObject* pDummy = pObject->GetRelatedDummy();
|
|
if(pDummy && pDummy->GetIplIndex() == 0)
|
|
delete pDummy;
|
|
pObject->ClearRelatedDummy();
|
|
CObjectPopulation::DestroyObject(pObject);
|
|
}
|
|
else if (pObject->GetAsProjectile())
|
|
{
|
|
static_cast<CProjectile*>(pObject)->Destroy();
|
|
}
|
|
else if (pObject->GetOwnedBy() == ENTITY_OWNEDBY_TEMP && pObject->CanBeDeleted())
|
|
{
|
|
CObjectPopulation::DestroyObject(pObject);
|
|
}
|
|
else
|
|
{
|
|
// if this is a weapon that has an owner just ignore it
|
|
// when the owner is moved they'll take the weapon with them
|
|
if( pObject->GetWeapon() &&
|
|
pObject->GetWeapon()->GetOwner() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
CNodeAddress NearestNode = ThePaths.FindNodeClosestToCoors(vOriginalObjectPosition);
|
|
if (NearestNode.IsEmpty())
|
|
{
|
|
// This can happen when nodes aren't streamed in or there are no Object nodes nearby
|
|
fwAttachmentEntityExtension* pExtension = pObject->GetAttachmentExtension();
|
|
if (pExtension) pExtension->SetAttachFlag(ATTACH_FLAG_DONT_ASSERT_ON_MATRIX_CHANGE, true); // Necessary to stop spurious assert in fwEntity::SetMatrix().
|
|
pObject->Teleport(Vector3(vOriginalObjectPosition.x, vOriginalObjectPosition.y, LOWESTOBJECTZ + 5.0f));
|
|
if (pExtension) pExtension->SetAttachFlag(ATTACH_FLAG_DONT_ASSERT_ON_MATRIX_CHANGE, false);
|
|
pObject->SetVelocity(ORIGIN);
|
|
}
|
|
else
|
|
{
|
|
Vector3 nodePos(0.0f, 0.0f, 0.0f);
|
|
ThePaths.FindNodePointer(NearestNode)->GetCoors(nodePos);
|
|
nodePos.z = 2.0f;
|
|
fwAttachmentEntityExtension* pExtension = pObject->GetAttachmentExtension();
|
|
if (pExtension) pExtension->SetAttachFlag(ATTACH_FLAG_DONT_ASSERT_ON_MATRIX_CHANGE, true); // Necessary to stop spurious assert in fwEntity::SetMatrix().
|
|
pObject->Teleport(nodePos);
|
|
if (pExtension) pExtension->SetAttachFlag(ATTACH_FLAG_DONT_ASSERT_ON_MATRIX_CHANGE, false);
|
|
pObject->SetVelocity(ORIGIN);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGameWorld::RemoveEntity(CEntity *pEntityToRemove)
|
|
{
|
|
Assertf(pEntityToRemove, "RemoveEntity - Entity doesn't exist");
|
|
|
|
switch (pEntityToRemove->GetType())
|
|
{
|
|
case ENTITY_TYPE_PED:
|
|
{
|
|
CPed *pPed = (CPed *) pEntityToRemove;
|
|
if(pPed->IsPlayer())
|
|
{
|
|
CPedFactory::GetFactory()->DestroyPlayerPed(pPed);
|
|
}
|
|
else
|
|
{
|
|
CPedFactory::GetFactory()->DestroyPed(pPed, true);
|
|
}
|
|
}
|
|
break;
|
|
case ENTITY_TYPE_OBJECT:
|
|
{
|
|
CObject* pObject = (CObject*)pEntityToRemove;
|
|
pObject->ClearRelatedDummy();
|
|
CObjectPopulation::DestroyObject(pObject);
|
|
}
|
|
break;
|
|
case ENTITY_TYPE_VEHICLE:
|
|
{
|
|
CVehicle *pVehicle = (CVehicle *)pEntityToRemove;
|
|
CVehicleFactory::GetFactory()->Destroy(pVehicle);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
CGameWorld::Remove(pEntityToRemove);
|
|
delete pEntityToRemove;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// name: ClearExcitingStuffFromArea
|
|
// description: Clear peds/cars/fires/projectiles etc from area
|
|
void CGameWorld::ClearExcitingStuffFromArea(const Vector3 &Centre, float Radius, bool bRemoveProjectiles, bool bConvertObjectsWithBrainsToDummyObjects, bool bCheckInterestingVehicles, bool bLeaveCarGenCars, CObject *pExceptionObject, bool bClearLowPriorityPickupsOnly, bool bClearPedsAndVehs, bool bClampVehicleHealth )
|
|
{
|
|
// Clear all peds
|
|
if (bClearPedsAndVehs)
|
|
ClearPedsFromArea(Centre, Radius);
|
|
|
|
// Clear all vehicles
|
|
if (bClearPedsAndVehs)
|
|
ClearCarsFromArea(Centre, Radius, bCheckInterestingVehicles, bLeaveCarGenCars);
|
|
|
|
if (bClearPedsAndVehs)
|
|
{
|
|
CTrain::RemoveTrainsFromArea(Centre, Radius);
|
|
}
|
|
|
|
// Delete all objects
|
|
ClearObjectsFromArea(Centre, Radius, bConvertObjectsWithBrainsToDummyObjects, pExceptionObject);
|
|
|
|
// Extinguish all fires in area
|
|
g_fireMan.ExtinguishArea(RCC_VEC3V(Centre), Radius, true);
|
|
|
|
// Clear all decals in area
|
|
g_decalMan.RemoveInRange(RCC_VEC3V(Centre), ScalarV(Radius), NULL);
|
|
|
|
ExtinguishAllCarFiresInArea(Centre, Radius, true, bClampVehicleHealth);
|
|
|
|
// Remove all explosions in area
|
|
CExplosionManager::RemoveExplosionsInArea(EXP_TAG_DONTCARE, Centre, Radius);
|
|
|
|
spdAABB spdBounds = spdAABB(spdSphere(RCC_VEC3V(Centre), ScalarV(Radius)));
|
|
|
|
// Remove all projectiles in the world
|
|
// Also removes all shadows in the world (to get rid of pools of blood and scorch marks)
|
|
if (bRemoveProjectiles)
|
|
{
|
|
ClearProjectilesFromArea(Centre, Radius);
|
|
//RAGE CShadows::TidyUpShadows();
|
|
}
|
|
|
|
// Low priority pickups can also be removed
|
|
// [SPHERE-OPTIMISE]
|
|
CPickupManager::RemoveAllPickupsInBounds(spdBounds, bClearLowPriorityPickupsOnly);
|
|
|
|
// What about objects?
|
|
|
|
// Clear all shocking events in the area, except script-created ones.
|
|
CShockingEventsManager::ClearEventsInSphere(RCC_VEC3V(Centre), Radius, true, false);
|
|
|
|
// After we've cleared fire, give the incident manager a chance to clean up.
|
|
// See B* 212897: "Fire Dept. call carried into replay." for a report of a bug
|
|
// that this should help.
|
|
CIncidentManager::GetInstance().ClearExcitingStuffFromArea(RCC_VEC3V(Centre), Radius);
|
|
}
|
|
|
|
void CGameWorld::ClearProjectilesFromArea(const Vector3 &Centre, float Radius)
|
|
{
|
|
spdAABB spdBounds = spdAABB(spdSphere(RCC_VEC3V(Centre), ScalarV(Radius)));
|
|
|
|
// Remove all projectiles in the world
|
|
CProjectileManager::RemoveAllProjectilesInBounds(spdBounds);
|
|
g_waterCannonMan.ClearAllWatercannonSprayInWorld();
|
|
}
|
|
|
|
|
|
#if !__NO_OUTPUT
|
|
void CGameWorld::LogClearCarFromArea(const char *pFunctionName, const CVehicle *pVehicle, const char *pReasonString)
|
|
{
|
|
if(ms_bLogClearCarFromArea)
|
|
{
|
|
Displayf("%s - cannot delete vehicle with model %s at %.2f,%.2f,%.2f (Entity = 0x%p). Reason: %s", pFunctionName, pVehicle->GetModelName(),
|
|
pVehicle->GetTransform().GetPosition().GetXf(), pVehicle->GetTransform().GetPosition().GetYf(), pVehicle->GetTransform().GetPosition().GetZf(),
|
|
pVehicle, pReasonString);
|
|
}
|
|
}
|
|
#endif // !__NO_OUTPUT
|
|
|
|
|
|
// Return true if the vehicle was deleted, false if it wasn't
|
|
// For DISTANCE_CHECK_CENTRE_RADIUS, vecPoint1 is the Centre and vecPoint2 is ignored, fDistance is the radius
|
|
// For DISTANCE_CHECK_ANGLED_AREA, vecPoint1 is the first point defining a face of the volume, vecPoint1 is the second point, fDistance is the distance to the opposite face
|
|
bool CGameWorld::DeleteThisVehicleIfPossible(CVehicle *pVehicle, bool bOnlyDeleteCopCars, bool bTakeInterestingVehiclesIntoAccount, bool bAllowCopRemovalWithWantedLevel, eTypeOfDistanceCheck DistanceCheckType, Vector3 &vecPoint1, float fDistance, Vector3 &vecPoint2)
|
|
{
|
|
if (bOnlyDeleteCopCars && !pVehicle->IsLawEnforcementVehicle())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#if __BANK
|
|
if(pVehicle->InheritsFromTrain() && CVehicleFactory::ms_bLogDestroyedVehicles)
|
|
{
|
|
Displayf("**************************************************************************************************");
|
|
Displayf("CGameWorld::DeleteThisVehicleIfPossible() is about to delete a CTrain..");
|
|
Displayf(" this could leave a train without an engine, or otherwise mess up train/carriage connections.");
|
|
Displayf("**************************************************************************************************");
|
|
}
|
|
#endif
|
|
|
|
//Test if anyone in the vehicle is in the player's group.
|
|
bool bIsInPlayerGroup=false;
|
|
CPedGroup* pPlayerGroup=CGameWorld::FindLocalPlayerGroup();
|
|
for(int i=0;i<pVehicle->GetSeatManager()->GetMaxSeats();i++)
|
|
{
|
|
CPed* pPassenger=pVehicle->GetSeatManager()->GetPedInSeat(i);
|
|
if(pPassenger && !pVehicle->IsDriver(pPassenger) && pPlayerGroup && pPlayerGroup->GetGroupMembership()->IsFollower(pPassenger))
|
|
{
|
|
bIsInPlayerGroup=true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!bIsInPlayerGroup)
|
|
{
|
|
if ( !pVehicle->InheritsFromBoat()
|
|
|| pVehicle != CGameWorld::FindLocalPlayer()->GetGroundPhysical()
|
|
|| (CGameWorld::FindLocalPlayerVehicle() && CGameWorld::FindLocalPlayerVehicle()!=CGameWorld::FindLocalPlayer()->GetGroundPhysical()) // This line is here because GetEntityStandingOn() lingers even when player jumps off boat and enters another car. Caused the car wash script to get stuck.
|
|
)
|
|
{ // Don't clear the vehicle if the player is standing on it
|
|
|
|
bool bCanDeleteThisVehicle = true;
|
|
|
|
if (pVehicle->m_nVehicleFlags.bCannotRemoveFromWorld)
|
|
{
|
|
OUTPUT_ONLY(LogClearCarFromArea("CGameWorld::DeleteThisVehicleIfPossible", pVehicle, "m_nVehicleFlags.bCannotRemoveFromWorld is TRUE for this vehicle"));
|
|
bCanDeleteThisVehicle = false;
|
|
}
|
|
else
|
|
{
|
|
if (!(pVehicle->CanBeDeletedSpecial(true, false, true, true, bTakeInterestingVehiclesIntoAccount, bAllowCopRemovalWithWantedLevel, false, false, true)))
|
|
{
|
|
OUTPUT_ONLY(LogClearCarFromArea("CGameWorld::DeleteThisVehicleIfPossible", pVehicle, "CanBeDeletedSpecial() returned FALSE for this vehicle"));
|
|
bCanDeleteThisVehicle = false;
|
|
}
|
|
else
|
|
{
|
|
if (CGarages::IsVehicleWithinHideOutGarageThatCanSaveIt(pVehicle))
|
|
{
|
|
OUTPUT_ONLY(LogClearCarFromArea("CGameWorld::DeleteThisVehicleIfPossible", pVehicle, "IsVehicleWithinHideOutGarageThatCanSaveIt() returned TRUE for this vehicle"));
|
|
bCanDeleteThisVehicle = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bCanDeleteThisVehicle)
|
|
{
|
|
// Don't remove vehicles from garages. This results in vehicles being lost from garages.
|
|
// RAGE if (!CGarages::IsPointWithinHideOutGarage(pVehicle->GetPosition()))
|
|
{
|
|
bool bWithinRange = false;
|
|
|
|
switch (DistanceCheckType)
|
|
{
|
|
case DISTANCE_CHECK_NONE :
|
|
bWithinRange = true;
|
|
break;
|
|
|
|
case DISTANCE_CHECK_CENTRE_RADIUS :
|
|
{
|
|
const Vector3 vecPos = VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition());
|
|
|
|
// [SPHERE-OPTIMISE]
|
|
float DistSqr = ((vecPos.x - vecPoint1.x) * (vecPos.x - vecPoint1.x)) + ((vecPos.y - vecPoint1.y) * (vecPos.y - vecPoint1.y));
|
|
|
|
if (DistSqr < (fDistance * fDistance))
|
|
{
|
|
bWithinRange = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DISTANCE_CHECK_ANGLED_AREA :
|
|
{
|
|
const Vector3 vecPos = VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition());
|
|
|
|
if (CScriptAreas::IsVecIn3dAngledArea(vecPos, vecPoint1, vecPoint2, fDistance, false))
|
|
{
|
|
bWithinRange = true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (bWithinRange)
|
|
{
|
|
// flag the vehicle for deletion so that if it becomes locally owned in the near future it will be deleted
|
|
if (pVehicle->GetNetworkObject())
|
|
{
|
|
bool bCanDeleteThisNetworkedVehicle = true;
|
|
if (pVehicle->IsNetworkClone())
|
|
{
|
|
OUTPUT_ONLY(LogClearCarFromArea("CGameWorld::DeleteThisVehicleIfPossible", pVehicle, "It's a network clone"));
|
|
bCanDeleteThisNetworkedVehicle = false;
|
|
}
|
|
else if (!pVehicle->GetNetworkObject()->CanDelete())
|
|
{
|
|
OUTPUT_ONLY(LogClearCarFromArea("CGameWorld::DeleteThisVehicleIfPossible", pVehicle, "GetNetworkObject()->CanDelete() returned false"));
|
|
bCanDeleteThisNetworkedVehicle = false;
|
|
}
|
|
|
|
if (!bCanDeleteThisNetworkedVehicle)
|
|
{
|
|
static_cast<CNetObjEntity*>(pVehicle->GetNetworkObject())->FlagForDeletion();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// remove car occupants (if any) from the world
|
|
// (different from RemoveDriver & RemovePassenger which are called
|
|
// when the ped gets out but still remains in the world)
|
|
|
|
if(pVehicle->GetDriver())
|
|
{
|
|
pVehicle->GetDriver()->SetPedConfigFlag( CPED_CONFIG_FLAG_DontStoreAsPersistent, true );
|
|
CPedFactory::GetFactory()->DestroyPed(pVehicle->GetDriver(), true);
|
|
// If the driver hasn't been removed (just marked to be, then flush their tasks to set them out as the vehicle is about to be deleted)
|
|
if (pVehicle->GetDriver())
|
|
{
|
|
pVehicle->GetDriver()->GetPedIntelligence()->FlushImmediately();
|
|
}
|
|
Assertf(!pVehicle->GetDriver(), "CGameWorld::DeleteThisVehicleIfPossible - expected the vehicle's driver to have been removed by CPedPopulation::RemovePed aborting his car task");
|
|
}
|
|
|
|
for(int i=0; i < pVehicle->GetSeatManager()->GetMaxSeats(); i++)
|
|
{
|
|
CPed *pTempPed = pVehicle->GetSeatManager()->GetPedInSeat(i);
|
|
if(pTempPed && i != pVehicle->GetDriverSeat())
|
|
{
|
|
pTempPed->SetPedConfigFlag( CPED_CONFIG_FLAG_DontStoreAsPersistent, true );
|
|
if(!CPedFactory::GetFactory()->DestroyPed(pTempPed, true)) // the ped is deleted in here so pPassengers[i] will no longer be a valid ped pointer - this might cause problems in RemovePassenger below
|
|
{
|
|
pTempPed->GetPedIntelligence()->FlushImmediately();
|
|
}
|
|
Assertf(!pVehicle->GetSeatManager()->GetPedInSeat(i), "CGameWorld::DeleteThisVehicleIfPossible - expected the vehicle's passenger to have been removed by CPedPopulation::RemovePed aborting his car task");
|
|
}
|
|
}
|
|
|
|
pVehicle->m_nVehicleFlags.bDontStoreAsPersistent = true;
|
|
g_ptFxManager.RemovePtFxFromEntity(pVehicle);
|
|
CVehicleFactory::GetFactory()->Destroy(pVehicle);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
#if !__NO_OUTPUT
|
|
if (ms_bLogClearCarFromArea_OutOfRange)
|
|
{
|
|
LogClearCarFromArea("CGameWorld::DeleteThisVehicleIfPossible", pVehicle, "It's out of the range specified for this clear_area");
|
|
}
|
|
#endif // !__NO_OUTPUT
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OUTPUT_ONLY(LogClearCarFromArea("CGameWorld::DeleteThisVehicleIfPossible", pVehicle, "It's a boat and the player is standing on it"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OUTPUT_ONLY(LogClearCarFromArea("CGameWorld::DeleteThisVehicleIfPossible", pVehicle, "It contains members of the player's group"));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// [SPHERE-OPTIMISE] ClearCentre, ClearRadius could be a spdSphere
|
|
static float ClearRadius = 0.0f; // used in ClearCarFromAreaCB and RemovePedFromAreaCB
|
|
static Vector3 ClearCentre(0.0f, 0.0f, 0.0f);
|
|
static bool s_bCheckInterestingVehicles = false;
|
|
static bool s_bAllowCopRemovalWithWantedLevel = false;
|
|
static bool s_bOnlyCopCars = false;
|
|
static bool s_bLeaveCarGenCars = false;
|
|
static bool s_bCheckViewFrustum = false;
|
|
static bool s_bIfWrecked = false;
|
|
static bool s_bIfAbandoned = false;
|
|
static bool s_bIfEngineDestroyed = false;
|
|
static bool s_bKeepScriptTrain = false;
|
|
|
|
bool IsVehicleScriptTrain(CVehicle* pVehicle)
|
|
{
|
|
if(pVehicle->InheritsFromTrain())
|
|
{
|
|
CTrain* pTrain = static_cast<CTrain*>(pVehicle);
|
|
GtaThread* scriptThread = CTheScripts::GetCurrentGtaScriptThread();
|
|
if(pTrain && scriptThread)
|
|
{
|
|
bool bScriptCreated = (pTrain->GetScriptThatCreatedMe() ? *pTrain->GetScriptThatCreatedMe() == scriptThread->m_Handler->GetScriptId() : false);
|
|
if(pTrain->GetMissionScriptId() == scriptThread->GetThreadId() || bScriptCreated)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// name: ClearCarFromAreaCB
|
|
// description: Clear car from area callback
|
|
bool ClearCarFromAreaCB(void* pItem, void* UNUSED_PARAM(data))
|
|
{
|
|
CVehicle* pVehicle = static_cast<CVehicle*>(pItem);
|
|
|
|
if (!(
|
|
s_bLeaveCarGenCars &&
|
|
(
|
|
pVehicle->PopTypeGet() == POPTYPE_RANDOM_PARKED ||
|
|
pVehicle->m_CarGenThatCreatedUs != -1
|
|
)
|
|
)
|
|
)
|
|
{
|
|
if(s_bIfEngineDestroyed && pVehicle->GetVehicleDamage()->GetEngineHealth() > ENGINE_DAMAGE_ONFIRE)
|
|
return true;
|
|
if(s_bIfWrecked && pVehicle->GetStatus() != STATUS_WRECKED)
|
|
return true;
|
|
if(s_bIfAbandoned && pVehicle->GetStatus() != STATUS_ABANDONED)
|
|
return true;
|
|
if(s_bKeepScriptTrain && IsVehicleScriptTrain(pVehicle))
|
|
return true;
|
|
|
|
if(s_bCheckViewFrustum)
|
|
{
|
|
static const float fOnScreenDist = 100.0f;
|
|
static const float fOffScreenDist = 100.0f;
|
|
Vector3 vOrigin;
|
|
float fRadius = pVehicle->GetBoundCentreAndRadius(vOrigin);
|
|
if(camInterface::IsSphereVisibleInGameViewport(vOrigin, fRadius))
|
|
return true;
|
|
if (NetworkInterface::IsGameInProgress())
|
|
if(NetworkInterface::IsVisibleOrCloseToAnyRemotePlayer(vOrigin, fRadius, fOnScreenDist, fOffScreenDist))
|
|
return true;
|
|
}
|
|
|
|
Vector3 vecDummyCentre(0.0f, 0.0f, 0.0);
|
|
CGameWorld::DeleteThisVehicleIfPossible(pVehicle, s_bOnlyCopCars, s_bCheckInterestingVehicles, s_bAllowCopRemovalWithWantedLevel, DISTANCE_CHECK_CENTRE_RADIUS, ClearCentre, ClearRadius, vecDummyCentre);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// name: ClearCarsFromArea
|
|
// description: Clear all the cars from a 3d box
|
|
// void CGameWorld::ClearCarsFromArea(float MinX, float MinY, float MinZ, float MaxX, float MaxY, float MaxZ)
|
|
void CGameWorld::ClearCarsFromArea(const Vector3 &Centre, float Radius, bool bCheckInterestingVehicles, bool bLeaveCarGenCars, bool bCheckViewFrustum, bool bIfWrecked, bool bIfAbandoned, bool bAllowCopRemovalWithWantedLevel, bool bIfEngineOnFire, bool bKeepScriptTrains)
|
|
{
|
|
ClearRadius = Radius;
|
|
ClearCentre = Centre;
|
|
s_bCheckInterestingVehicles = bCheckInterestingVehicles;
|
|
s_bAllowCopRemovalWithWantedLevel = bAllowCopRemovalWithWantedLevel;
|
|
s_bOnlyCopCars = false;
|
|
s_bLeaveCarGenCars = bLeaveCarGenCars;
|
|
s_bIfWrecked = bIfWrecked;
|
|
s_bIfAbandoned = bIfAbandoned;
|
|
s_bCheckViewFrustum = bCheckViewFrustum;
|
|
s_bIfEngineDestroyed = bIfEngineOnFire;
|
|
s_bKeepScriptTrain = bKeepScriptTrains;
|
|
|
|
#if !__NO_OUTPUT
|
|
if(ms_bLogClearCarFromArea)
|
|
{
|
|
Displayf("******************************************************************************************************************************");
|
|
Displayf("CGameWorld::ClearCarsFromArea Position (%.1f,%.1f,%.1f) Radius:%.1f",
|
|
ClearCentre.GetX(), ClearCentre.GetY(), ClearCentre.GetZ(),
|
|
ClearRadius);
|
|
Displayf("bCheckInterestingVehicles:%s, bAllowCopRemovalWithWantedLevel:%s, bOnlyCopCars:%s, bLeaveCarGenCars:%s",
|
|
s_bCheckInterestingVehicles?"true":"false",
|
|
s_bAllowCopRemovalWithWantedLevel?"true":"false",
|
|
s_bOnlyCopCars?"true":"false",
|
|
s_bLeaveCarGenCars?"true":"false");
|
|
Displayf("bIfWrecked:%s, bIfAbandoned:%s, bCheckViewFrustum:%s, bIfEngineOnFire:%s, bKeepScriptTrains:%s",
|
|
s_bIfWrecked?"true":"false",
|
|
s_bIfAbandoned?"true":"false",
|
|
s_bCheckViewFrustum?"true":"false",
|
|
s_bIfEngineDestroyed?"true":"false",
|
|
s_bKeepScriptTrain?"true":"false");
|
|
Displayf("******************************************************************************************************************************");
|
|
}
|
|
#endif // !__NO_OUTPUT
|
|
|
|
CVehicle::GetPool()->ForAll(ClearCarFromAreaCB, NULL);
|
|
}
|
|
|
|
|
|
static Vector3 vecPoint1OfAngledArea(0.0f, 0.0f, 0.0f);
|
|
static Vector3 vecPoint2OfAngledArea(0.0f, 0.0f, 0.0f);
|
|
static float DistanceToOppositeFaceOfAngledArea = 0.0f;
|
|
|
|
//
|
|
// name: ClearCarFromAngledAreaCB
|
|
// description: Clear car from angled area callback
|
|
bool ClearCarFromAngledAreaCB(void* pItem, void* UNUSED_PARAM(data))
|
|
{
|
|
CVehicle* pVehicle = static_cast<CVehicle*>(pItem);
|
|
|
|
if (!(
|
|
s_bLeaveCarGenCars &&
|
|
(
|
|
pVehicle->PopTypeGet() == POPTYPE_RANDOM_PARKED ||
|
|
pVehicle->m_CarGenThatCreatedUs != -1
|
|
)
|
|
)
|
|
)
|
|
{
|
|
if(s_bIfEngineDestroyed && pVehicle->GetVehicleDamage()->GetEngineHealth() > ENGINE_DAMAGE_ONFIRE)
|
|
return true;
|
|
if(s_bIfWrecked && pVehicle->GetStatus() != STATUS_WRECKED)
|
|
return true;
|
|
if(s_bIfAbandoned && pVehicle->GetStatus() != STATUS_ABANDONED)
|
|
return true;
|
|
if(s_bKeepScriptTrain && IsVehicleScriptTrain(pVehicle))
|
|
return true;
|
|
|
|
|
|
if(s_bCheckViewFrustum)
|
|
{
|
|
static const float fOnScreenDist = 100.0f;
|
|
static const float fOffScreenDist = 100.0f;
|
|
Vector3 vOrigin;
|
|
float fRadius = pVehicle->GetBoundCentreAndRadius(vOrigin);
|
|
if(camInterface::IsSphereVisibleInGameViewport(vOrigin, fRadius))
|
|
return true;
|
|
if (NetworkInterface::IsGameInProgress())
|
|
if(NetworkInterface::IsVisibleOrCloseToAnyRemotePlayer(vOrigin, fRadius, fOnScreenDist, fOffScreenDist))
|
|
return true;
|
|
}
|
|
|
|
CGameWorld::DeleteThisVehicleIfPossible(pVehicle, s_bOnlyCopCars, s_bCheckInterestingVehicles, s_bAllowCopRemovalWithWantedLevel, DISTANCE_CHECK_ANGLED_AREA, vecPoint1OfAngledArea, DistanceToOppositeFaceOfAngledArea, vecPoint2OfAngledArea);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void CGameWorld::ClearCarsFromAngledArea(const Vector3 &vecPoint1, const Vector3 &vecPoint2, float DistanceToOppositeFace, bool bCheckInterestingVehicles, bool bLeaveCarGenCars, bool bCheckViewFrustum, bool bIfWrecked, bool bIfAbandoned, bool bAllowCopRemovalWithWantedLevel, bool bIfEngineOnFire, bool bKeepScriptTrains)
|
|
{
|
|
vecPoint1OfAngledArea = vecPoint1;
|
|
vecPoint2OfAngledArea = vecPoint2;
|
|
DistanceToOppositeFaceOfAngledArea = DistanceToOppositeFace;
|
|
s_bCheckInterestingVehicles = bCheckInterestingVehicles;
|
|
s_bAllowCopRemovalWithWantedLevel = bAllowCopRemovalWithWantedLevel;
|
|
s_bOnlyCopCars = false;
|
|
s_bLeaveCarGenCars = bLeaveCarGenCars;
|
|
s_bIfWrecked = bIfWrecked;
|
|
s_bIfAbandoned = bIfAbandoned;
|
|
s_bCheckViewFrustum = bCheckViewFrustum;
|
|
s_bIfEngineDestroyed = bIfEngineOnFire;
|
|
s_bKeepScriptTrain = bKeepScriptTrains;
|
|
|
|
#if !__NO_OUTPUT
|
|
if(ms_bLogClearCarFromArea)
|
|
{
|
|
Displayf("******************************************************************************************************************************");
|
|
Displayf("CGameWorld::ClearCarsFromAngledArea Pt1 (%.1f,%.1f,%.1f) Pt2 (%.1f,%.1f,%.1f) Dist:%.1f",
|
|
vecPoint1OfAngledArea.GetX(), vecPoint1OfAngledArea.GetY(), vecPoint1OfAngledArea.GetZ(),
|
|
vecPoint2OfAngledArea.GetX(), vecPoint2OfAngledArea.GetY(), vecPoint2OfAngledArea.GetZ(),
|
|
DistanceToOppositeFaceOfAngledArea);
|
|
Displayf("bCheckInterestingVehicles:%s, bAllowCopRemovalWithWantedLevel:%s, bOnlyCopCars:%s, bLeaveCarGenCars:%s",
|
|
s_bCheckInterestingVehicles?"true":"false",
|
|
s_bAllowCopRemovalWithWantedLevel?"true":"false",
|
|
s_bOnlyCopCars?"true":"false",
|
|
s_bLeaveCarGenCars?"true":"false");
|
|
Displayf("bIfWrecked:%s, bIfAbandoned:%s, bCheckViewFrustum:%s, bIfEngineOnFire:%s, bKeepScriptTrains:%s",
|
|
s_bIfWrecked?"true":"false",
|
|
s_bIfAbandoned?"true":"false",
|
|
s_bCheckViewFrustum?"true":"false",
|
|
s_bIfEngineDestroyed?"true":"false",
|
|
s_bKeepScriptTrain?"true":"false");
|
|
Displayf("******************************************************************************************************************************");
|
|
}
|
|
#endif // !__NO_OUTPUT
|
|
|
|
CVehicle::GetPool()->ForAll(ClearCarFromAngledAreaCB, NULL);
|
|
}
|
|
|
|
//
|
|
// name: RemovePedFromAreaCB
|
|
// description: Remove ped from area
|
|
static bool RemovePedFromAreaCB(void* pItem, void* UNUSED_PARAM(data))
|
|
{
|
|
// spdAABB* pBox = (spdAABB*)data;
|
|
// Vector3 *pClearCentre = (Vector3*)data;
|
|
CPed* pPed = static_cast<CPed*>(pItem);
|
|
|
|
if (pPed->IsPlayer() == FALSE && pPed->CanBeDeleted(false, false))
|
|
{
|
|
//Don't remove peds in player's group.
|
|
CPedGroup* pPlayerGroup=CGameWorld::FindLocalPlayerGroup();
|
|
if(!pPlayerGroup || !pPlayerGroup->GetGroupMembership()->IsFollower(pPed))
|
|
{
|
|
Vector3 vecPos = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
|
|
|
|
// [SPHERE-OPTIMISE]
|
|
float DistSqr = ((vecPos.x - ClearCentre.x) * (vecPos.x - ClearCentre.x)) + ((vecPos.y - ClearCentre.y) * (vecPos.y - ClearCentre.y));
|
|
|
|
if (DistSqr < (ClearRadius * ClearRadius))
|
|
{
|
|
// flag the ped for deletion so that if it becomes locally owned in the near future it will be deleted
|
|
if (pPed->GetNetworkObject())
|
|
{
|
|
if (pPed->IsNetworkClone() || !pPed->GetNetworkObject()->CanDelete())
|
|
{
|
|
static_cast<CNetObjEntity*>(pPed->GetNetworkObject())->FlagForDeletion();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
pPed->SetPedConfigFlag( CPED_CONFIG_FLAG_DontStoreAsPersistent, true );
|
|
g_ptFxManager.RemovePtFxFromEntity(pPed);
|
|
CPedFactory::GetFactory()->DestroyPed(pPed);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// name: RemovePedFromAngledAreaCB
|
|
// description: Remove ped from an angled area
|
|
static bool RemovePedFromAngledAreaCB(void* pItem, void* UNUSED_PARAM(data))
|
|
{
|
|
CPed* pPed = static_cast<CPed*>(pItem);
|
|
|
|
if (pPed->IsPlayer() == FALSE && pPed->CanBeDeleted(false, false))
|
|
{
|
|
//Don't remove peds in player's group.
|
|
CPedGroup* pPlayerGroup=CGameWorld::FindLocalPlayerGroup();
|
|
if(!pPlayerGroup || !pPlayerGroup->GetGroupMembership()->IsFollower(pPed))
|
|
{
|
|
Vector3 vecPos = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
|
|
|
|
if (CScriptAreas::IsVecIn3dAngledArea(vecPos, vecPoint1OfAngledArea, vecPoint2OfAngledArea, DistanceToOppositeFaceOfAngledArea, false))
|
|
{
|
|
// flag the ped for deletion so that if it becomes locally owned in the near future it will be deleted
|
|
if (pPed->GetNetworkObject())
|
|
{
|
|
if (pPed->IsNetworkClone() || !pPed->GetNetworkObject()->CanDelete())
|
|
{
|
|
static_cast<CNetObjEntity*>(pPed->GetNetworkObject())->FlagForDeletion();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
pPed->SetPedConfigFlag( CPED_CONFIG_FLAG_DontStoreAsPersistent, true );
|
|
CPedFactory::GetFactory()->DestroyPed(pPed);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// name: RemoveCopPedFromAreaCB
|
|
// description: Remove ped from area
|
|
static bool RemoveCopPedFromAreaCB(void* pItem, void* UNUSED_PARAM(data))
|
|
{
|
|
CPed* pPed = static_cast<CPed*>(pItem);
|
|
|
|
if (pPed->IsPlayer() == FALSE && pPed->CanBeDeleted() && pPed->GetPedType() == PEDTYPE_COP)
|
|
{
|
|
//Don't remove peds in player's group.
|
|
CPedGroup* pPlayerGroup=CGameWorld::FindLocalPlayerGroup();
|
|
if(!pPlayerGroup || !pPlayerGroup->GetGroupMembership()->IsFollower(pPed))
|
|
{
|
|
Vector3 vecPos = VEC3V_TO_VECTOR3(pPed->GetTransform().GetPosition());
|
|
|
|
// [SPHERE-OPTIMISE]
|
|
float DistSqr = ((vecPos.x - ClearCentre.x) * (vecPos.x - ClearCentre.x)) + ((vecPos.y - ClearCentre.y) * (vecPos.y - ClearCentre.y));
|
|
|
|
if (DistSqr < (ClearRadius * ClearRadius))
|
|
{
|
|
// flag the ped for deletion so that if it becomes locally owned in the near future it will be deleted
|
|
if (pPed->GetNetworkObject())
|
|
{
|
|
if (pPed->IsNetworkClone() || !pPed->GetNetworkObject()->CanDelete())
|
|
{
|
|
static_cast<CNetObjEntity*>(pPed->GetNetworkObject())->FlagForDeletion();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
pPed->SetPedConfigFlag( CPED_CONFIG_FLAG_DontStoreAsPersistent, true );
|
|
CPedFactory::GetFactory()->DestroyPed(pPed);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// name: ClearPedsFromArea
|
|
// description: Clear all the peds from a 3d box
|
|
// void CGameWorld::ClearPedsFromArea(float MinX, float MinY, float MinZ, float MaxX, float MaxY, float MaxZ)
|
|
void CGameWorld::ClearPedsFromAngledArea(const Vector3 &vecPoint1, const Vector3 &vecPoint2, float DistanceToOppositeFace)
|
|
{
|
|
vecPoint1OfAngledArea = vecPoint1;
|
|
vecPoint2OfAngledArea = vecPoint2;
|
|
DistanceToOppositeFaceOfAngledArea = DistanceToOppositeFace;
|
|
|
|
CPed::GetPool()->ForAll(RemovePedFromAngledAreaCB, NULL);
|
|
}
|
|
|
|
|
|
//
|
|
// name: ClearPedsFromArea
|
|
// description: Clear all the peds from a 3d box
|
|
// void CGameWorld::ClearPedsFromArea(float MinX, float MinY, float MinZ, float MaxX, float MaxY, float MaxZ)
|
|
void CGameWorld::ClearPedsFromArea(const Vector3 &Centre, float Radius)
|
|
{
|
|
ClearRadius = Radius;
|
|
ClearCentre = Centre;
|
|
// spdAABB box(Vector3(MinX, MinY, MinZ), Vector3(MaxX, MaxY, MaxZ));
|
|
CPed::GetPool()->ForAll(RemovePedFromAreaCB, NULL);
|
|
}
|
|
|
|
//
|
|
// name: ClearCopsFromArea
|
|
// description: Clear all the cop peds from a 3d box
|
|
|
|
void CGameWorld::ClearCopsFromArea(const Vector3 &Centre, float Radius)
|
|
{
|
|
ClearRadius = Radius;
|
|
ClearCentre = Centre;
|
|
|
|
CPed::GetPool()->ForAll(RemoveCopPedFromAreaCB, NULL);
|
|
|
|
s_bOnlyCopCars = true;
|
|
s_bCheckInterestingVehicles = true;
|
|
s_bAllowCopRemovalWithWantedLevel = true;
|
|
CVehicle::GetPool()->ForAll(ClearCarFromAreaCB, NULL);
|
|
|
|
}
|
|
|
|
//
|
|
// name: ClearObjectsFromArea
|
|
// description: Clear all the cop peds from a 3d box
|
|
|
|
void CGameWorld::ClearObjectsFromArea(const Vector3 &Centre, float Radius, bool bConvertObjectsWithBrainsToDummyObjects/*=false*/,
|
|
CObject *pExceptionObject/*=NULL*/, bool bForce/*=false*/, bool bIncludeDoors/*=false*/, bool bExcludeLadders/*=false*/)
|
|
{
|
|
CObjectPopulation::DeleteAllTempObjectsInArea(Centre, Radius);
|
|
|
|
CObjectPopulation::ConvertAllObjectsInAreaToDummyObjects(Centre, Radius, bConvertObjectsWithBrainsToDummyObjects, pExceptionObject, bIncludeDoors, bForce, bExcludeLadders);
|
|
}
|
|
|
|
//
|
|
// name: SetCarDamagedCB
|
|
// description: Set car to be damaged
|
|
bool SetCarDamagedCB(void* pItem, void* data)
|
|
{
|
|
CVehicle* pVehicle = static_cast<CVehicle*>(pItem);
|
|
pVehicle->SetCanBeDamaged(data != NULL);
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// name: SetAllCarsCanBeDamaged
|
|
// description: Set whether all the cars can be damaged or not
|
|
void CGameWorld::SetAllCarsCanBeDamaged(bool DmgeFlag)
|
|
{
|
|
CVehicle::GetPool()->ForAll(SetCarDamagedCB, (void*)DmgeFlag);
|
|
}
|
|
|
|
// Name : StopAllLawEnforcersInTheirTracks
|
|
// Purpose : Any law enforcer is stopped so that we don't get into trouble with the cut scenes
|
|
// Parameters :
|
|
// Returns :
|
|
|
|
bool SetLawEnforcerVehicleMoveSpeedCB(void* pItem, void* UNUSED_PARAM(data))
|
|
{
|
|
CVehicle* pVehicle = static_cast<CVehicle*>(pItem);
|
|
if (pVehicle->IsLawEnforcementCar() && ((!pVehicle->GetDriver()) || (!pVehicle->GetDriver()->IsPlayer())))
|
|
{
|
|
pVehicle->SetVelocity(ORIGIN);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CGameWorld::StopAllLawEnforcersInTheirTracks()
|
|
{
|
|
if(!NetworkInterface::IsGameInProgress())
|
|
{
|
|
CVehicle::GetPool()->ForAll(SetLawEnforcerVehicleMoveSpeedCB, NULL);
|
|
}
|
|
}
|
|
|
|
//
|
|
// name: ExtinguishAllCarFiresInArea
|
|
// description: Extinguish all car fires in area
|
|
void CGameWorld::ExtinguishAllCarFiresInArea(const Vector3& Centre, float Radius, bool finishPtFx, bool bClampVehicleHealth)
|
|
{
|
|
s32 nPoolSize;
|
|
s32 loop;
|
|
|
|
CVehicle *pVehicle = NULL;
|
|
CVehicle::Pool* pVehPool = CVehicle::GetPool();
|
|
|
|
float DistSqr;
|
|
|
|
nPoolSize = (s32) pVehPool->GetSize();
|
|
|
|
for (loop = 0; loop < nPoolSize; loop++)
|
|
{
|
|
pVehicle = pVehPool->GetSlot(loop);
|
|
|
|
if (pVehicle)
|
|
{
|
|
// [SPHERE-OPTIMISE]
|
|
const Vector3 vVehiclePos = VEC3V_TO_VECTOR3(pVehicle->GetTransform().GetPosition());
|
|
DistSqr = (Centre.x - vVehiclePos.x)*(Centre.x - vVehiclePos.x) +
|
|
(Centre.y - vVehiclePos.y)*(Centre.y - vVehiclePos.y) +
|
|
(Centre.z - vVehiclePos.z)*(Centre.z - vVehiclePos.z);
|
|
|
|
if (DistSqr < Radius*Radius)
|
|
{
|
|
pVehicle->ExtinguishCarFire(finishPtFx, bClampVehicleHealth);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGameWorld::ClearCulpritFromAllCarFires(const CEntity* pCulprit)
|
|
{
|
|
s32 nPoolSize;
|
|
|
|
CVehicle *pVehicle = NULL;
|
|
CVehicle::Pool* pVehPool = CVehicle::GetPool();
|
|
nPoolSize = (s32) pVehPool->GetSize();
|
|
|
|
for (s32 loop = 0; loop < nPoolSize; loop++)
|
|
{
|
|
pVehicle = pVehPool->GetSlot(loop);
|
|
if(pVehicle)
|
|
{
|
|
pVehicle->ClearCulpritFromCarFires(pCulprit);
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Finds an empty slot in the Players array for a new player, returns -1 if none.
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
struct SlotPred
|
|
{
|
|
bool operator()(unsigned* a, unsigned* b) const
|
|
{
|
|
return *a < *b;
|
|
}
|
|
};
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Adds player registered at the given index to the world at the given coords
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool CGameWorld::AddPlayerToWorld(CPed* pPlayerPed, const Vector3& pos)
|
|
{
|
|
Assert(pPlayerPed);
|
|
Assert(pPlayerPed->GetPlayerInfo());
|
|
|
|
pPlayerPed->SetPosition(pos);
|
|
CScriptEntities::ClearSpaceForMissionEntity(pPlayerPed);
|
|
|
|
Add(pPlayerPed, CGameWorld::OUTSIDE );
|
|
|
|
#if __DEV
|
|
pPlayerPed->m_nDEflags.bCheckedForDead = TRUE;
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// PURPOSE
|
|
// Creates the game player, if the player already exists sets the player health.
|
|
// Player Zeta position is corrected for the right height to the floor.
|
|
// mjc-Moved from CPlayerPed
|
|
// RETURN
|
|
// The slot number were the player was created.
|
|
int CGameWorld::CreatePlayer(Vector3& pos, u32 nameHash, float health)
|
|
{
|
|
if (PARAM_noPlayer.Get())
|
|
return 0;
|
|
|
|
CPed* pPlayerPed = CGameWorld::FindLocalPlayer();
|
|
if (!pPlayerPed)
|
|
{
|
|
fwModelId modelId;
|
|
CModelInfo::GetBaseModelInfoFromHashKey(nameHash, &modelId);
|
|
|
|
if (!CModelInfo::HaveAssetsLoaded(modelId))
|
|
{
|
|
CModelInfo::RequestAssets(modelId, STRFLAG_DONTDELETE|STRFLAG_FORCE_LOAD|STRFLAG_PRIORITY_LOAD);
|
|
CStreaming::LoadAllRequestedObjects(true);
|
|
}
|
|
|
|
// need to fix for network stuff:
|
|
Matrix34 tempMat;
|
|
tempMat.Identity();
|
|
const CControlledByInfo localPlayerControl(false, true);
|
|
pPlayerPed = CPedFactory::GetFactory()->CreatePlayerPed(localPlayerControl, modelId.GetModelIndex(), &tempMat);
|
|
if (!Verifyf(pPlayerPed, "Can not find player ped"))
|
|
return -1;
|
|
|
|
StatsInterface::SetStatsModelPrefix( pPlayerPed );
|
|
|
|
pPlayerPed->PopTypeSet(POPTYPE_PERMANENT);
|
|
pPlayerPed->SetDefaultDecisionMaker();
|
|
pPlayerPed->SetCharParamsBasedOnManagerType();
|
|
pPlayerPed->GetPedIntelligence()->SetDefaultRelationshipGroup();
|
|
|
|
// Correct Zeta position to match the ground floor
|
|
if (pos.z <= INVALID_MINIMUM_WORLD_Z)
|
|
{
|
|
pos.z = WorldProbe::FindGroundZForCoord(BOTTOM_SURFACE, pos.x, pos.y);
|
|
}
|
|
pos.z += pPlayerPed->GetDistanceFromCentreOfMassToBaseOfModel();
|
|
|
|
CGameWorld::AddPlayerToWorld(pPlayerPed, pos);
|
|
|
|
CModelInfo::SetAssetsAreDeletable(modelId);
|
|
|
|
pPlayerPed->GetPedIntelligence()->AddTaskDefault(pPlayerPed->GetCurrentMotionTask()->CreatePlayerControlTask());
|
|
|
|
pPlayerPed->InitSpecialAbility();
|
|
}
|
|
|
|
// DEBUG-PH
|
|
// rage: the player spawns with 0 health as default
|
|
// temporarily increase this to 100.0f
|
|
pPlayerPed->SetHealth(health);
|
|
// END DEBUG
|
|
|
|
#if __DEV
|
|
// This flag is used and checked on certain script commands.
|
|
pPlayerPed->m_nDEflags.bCheckedForDead = true;
|
|
#endif
|
|
|
|
if (pPlayerPed->GetPlayerInfo())
|
|
{
|
|
pPlayerPed->GetPlayerInfo()->SetPlayerControl(false, false);
|
|
}
|
|
|
|
// local player always created in slot 0 now
|
|
return (0);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Replaces a ped with a new ped. If the ped is a player ped this is
|
|
// done without affecting the player info structure.
|
|
// This is temporary code until John Whyte implements the
|
|
// ability to change a players model
|
|
// RETURNS : pointer to the new player ped
|
|
//////////////////////////////////////////////////////////////////////
|
|
CPed *CGameWorld::ChangePedModel(CPed& ped, fwModelId newModelId)
|
|
{
|
|
ConductorMessageData messageData;
|
|
messageData.conductorName = SuperConductor;
|
|
messageData.message = SwitchConductorsOff;
|
|
|
|
CONDUCTORS_ONLY(SUPERCONDUCTOR.SendConductorMessage(messageData);)
|
|
// cache the player info
|
|
CPlayerInfo* pPlayerInfo = ped.GetPlayerInfo();
|
|
|
|
// Cache the position of the ped
|
|
Vector3 vecOldPedPosition = VEC3V_TO_VECTOR3(ped.GetTransform().GetPosition());
|
|
float fOldPedHeading = ped.GetTransform().GetHeading();
|
|
|
|
// cache the ped's tasks
|
|
CTaskInfo *oldTaskInfos[MAX_TASK_INFOS_PER_PED];
|
|
u32 numTaskInfos = 0;
|
|
|
|
CTaskInfo *taskInfo = ped.GetPedIntelligence()->GetQueriableInterface()->GetFirstTaskInfo();
|
|
|
|
while(taskInfo)
|
|
{
|
|
Assert(numTaskInfos < MAX_TASK_INFOS_PER_PED);
|
|
|
|
if(numTaskInfos < MAX_TASK_INFOS_PER_PED)
|
|
{
|
|
oldTaskInfos[numTaskInfos++] = taskInfo;
|
|
}
|
|
|
|
ped.GetPedIntelligence()->GetQueriableInterface()->UnlinkTaskInfoFromNetwork(taskInfo);
|
|
|
|
taskInfo = ped.GetPedIntelligence()->GetQueriableInterface()->GetFirstTaskInfo();
|
|
}
|
|
|
|
// cache the ped's task sequences
|
|
TaskSequenceInfo* oldTaskSequenceInfos[CTaskTypes::MAX_NUM_TASK_TYPES];
|
|
u32 numTaskSequenceInfos = 0;
|
|
|
|
TaskSequenceInfo *taskSeqInfo = ped.GetPedIntelligence()->GetQueriableInterface()->GetFirstTaskSequenceInfo();
|
|
|
|
while(taskSeqInfo)
|
|
{
|
|
if (AssertVerify(numTaskSequenceInfos < CTaskTypes::MAX_NUM_TASK_TYPES))
|
|
{
|
|
oldTaskSequenceInfos[numTaskSequenceInfos++] = taskSeqInfo;
|
|
}
|
|
|
|
ped.GetPedIntelligence()->GetQueriableInterface()->UnlinkTaskSequenceInfoFromNetwork(taskSeqInfo);
|
|
|
|
taskSeqInfo = ped.GetPedIntelligence()->GetQueriableInterface()->GetFirstTaskSequenceInfo();
|
|
}
|
|
|
|
// Explicitly flush the ped's tasks, the tasks aren't recreated anyway and not flushing can leave the new ped in a bad state
|
|
ped.GetPedIntelligence()->FlushImmediately();
|
|
|
|
// reset the player info pointer (is not a non-player ped). Needs to be done after the intelligence flush
|
|
ped.SetPlayerInfo(NULL);
|
|
|
|
// cache all of the important flags
|
|
bool isFixed = ped.IsBaseFlagSet(fwEntity::IS_FIXED);
|
|
bool usesCollision = ped.IsCollisionEnabled();
|
|
// cache wanted level multiplier
|
|
float fWantedLevelMultiplier = pPlayerInfo ? pPlayerInfo->GetWanted().m_fMultiplier : 0.0f;
|
|
|
|
// cache the relationship group
|
|
CRelationshipGroup* pRelationshipGrp = ped.GetPedIntelligence()->GetRelationshipGroup();
|
|
|
|
// cache the control info
|
|
const CControlledByInfo controlInfo(ped.GetControlledByInfo());
|
|
|
|
// now create a new player ped with the specified model index
|
|
Matrix34 tempMat = MAT34V_TO_MATRIX34(ped.GetMatrix());
|
|
|
|
ASSERT_ONLY(CPlayerInfo::SetChangingPlayerModel(true));
|
|
CPed* pNewPed = NULL;
|
|
if(pPlayerInfo)
|
|
{
|
|
pNewPed = CPedFactory::GetFactory()->CreatePlayerPed(controlInfo, newModelId.GetModelIndex(), &tempMat, pPlayerInfo);
|
|
}
|
|
else
|
|
{
|
|
pNewPed = CPedFactory::GetFactory()->CreatePed(controlInfo, newModelId, &tempMat, true, true, false);
|
|
}
|
|
Assert(pNewPed);
|
|
|
|
// FA: why do we randomize when we change model? we'd expect to set a required variation afterwards, no?
|
|
// this breaks the MP blended characters. we could potential skip the randomization when previous head has a head blend if really needed
|
|
//pNewPlayerPed->SetVarRandom(PV_FLAG_NONE, PV_FLAG_NONE, NULL, NULL);
|
|
|
|
pNewPed->GetPedIntelligence()->GetCombatBehaviour().SetCombatFloat(kAttribFloatWeaponAccuracy, 1.0f);
|
|
pNewPed->GetPedIntelligence()->GetCombatBehaviour().SetCombatFloat(kAttribFloatWeaponDamageModifier, 1.0f);
|
|
pNewPed->GetPedIntelligence()->GetCombatBehaviour().SetFlag(CCombatData::BF_CanTauntInVehicle);
|
|
|
|
// Reset our firing pattern
|
|
pNewPed->GetPedIntelligence()->GetCombatBehaviour().SetFiringPattern(FIRING_PATTERN_FULL_AUTO);
|
|
|
|
// Restore wanted level multiplier
|
|
if(pNewPed->GetPlayerInfo())
|
|
{
|
|
pNewPed->GetPlayerInfo()->GetWanted().m_fMultiplier = fWantedLevelMultiplier;
|
|
}
|
|
|
|
// Player character should always be a permanent char
|
|
pNewPed->PopTypeSet(POPTYPE_PERMANENT);
|
|
pNewPed->SetDefaultDecisionMaker();
|
|
pNewPed->SetCharParamsBasedOnManagerType();
|
|
pNewPed->GetPedIntelligence()->SetDefaultRelationshipGroup();
|
|
|
|
// Set the position of the new player
|
|
pNewPed->SetPosition(vecOldPedPosition);
|
|
pNewPed->SetHeading(fOldPedHeading);
|
|
pNewPed->SetDesiredHeading(fOldPedHeading);
|
|
|
|
// restore the old player's tasks & sequence infos
|
|
pNewPed->GetPedIntelligence()->GetQueriableInterface()->Reset();
|
|
|
|
for(u32 index = 0; index < numTaskInfos; index++)
|
|
{
|
|
pNewPed->GetPedIntelligence()->GetQueriableInterface()->AddTaskInfoFromNetwork(oldTaskInfos[index]);
|
|
}
|
|
|
|
for(u32 index = 0; index < numTaskSequenceInfos; index++)
|
|
{
|
|
pNewPed->GetPedIntelligence()->GetQueriableInterface()->AddTaskSequenceInfo(oldTaskSequenceInfos[index]);
|
|
}
|
|
|
|
// restore the important flags
|
|
if (isFixed){
|
|
pNewPed->SetBaseFlag(fwEntity::IS_FIXED);
|
|
} else {
|
|
pNewPed->ClearBaseFlag(fwEntity::IS_FIXED);
|
|
}
|
|
|
|
pNewPed->CopyIsVisibleFlagsFrom(&ped, true);
|
|
usesCollision ? pNewPed->EnableCollision() : pNewPed->DisableCollision();
|
|
|
|
// Set the health of the new player
|
|
const CHealthConfigInfo* pHealthInfo = pNewPed->GetPedHealthInfo();
|
|
Assert( pHealthInfo );
|
|
pNewPed->SetHealth( pHealthInfo->GetDefaultHealth() );
|
|
|
|
// Add the new player to the world
|
|
CGameWorld::Add(pNewPed, CGameWorld::OUTSIDE );
|
|
|
|
CPedModelInfo* pModelInfo = (CPedModelInfo*)CModelInfo::GetBaseModelInfo(newModelId);
|
|
Assert(pModelInfo->GetModelType() == MI_TYPE_PED);
|
|
Assertf(pModelInfo->GetDrawable() || pModelInfo->GetFragType(), "%s:Ped model is not loaded", pModelInfo->GetModelName());
|
|
|
|
pNewPed->GetPrimaryMotionTask()->SetDefaultClipSets(*pNewPed);
|
|
|
|
// Set the gesture anim group from the model info
|
|
// Stored in the ped so that it can be overridden at runtime
|
|
pNewPed->SetGestureClipSet(pModelInfo->GetDefaultGestureClipSet());
|
|
|
|
// Set the facial anim group from the model info
|
|
// Stored in the ped so that it can be overridden at runtime
|
|
CFacialDataComponent* pFacialData = pNewPed->GetFacialData();
|
|
if(pFacialData)
|
|
{
|
|
fwFacialClipSetGroup* pFacialClipSetGroup = fwFacialClipSetGroupManager::GetFacialClipSetGroup(pModelInfo->GetFacialClipSetGroupId());
|
|
if (pFacialClipSetGroup)
|
|
{
|
|
fwMvClipSetId facialBaseClipsetId(pFacialClipSetGroup->GetBaseClipSetName());
|
|
Assertf(fwClipSetManager::GetClipSet(facialBaseClipsetId), "%s:Unrecognised facial base clipset", pFacialClipSetGroup->GetBaseClipSetName().GetCStr());
|
|
pFacialData->SetFacialClipSet(pNewPed, facialBaseClipsetId);
|
|
}
|
|
}
|
|
|
|
// restore the player's relationship group
|
|
pNewPed->GetPedIntelligence()->SetRelationshipGroup(pRelationshipGrp);
|
|
|
|
// cache the network object before destroying the old player, also ensure the task trees
|
|
// are restored before deletion
|
|
CNetObjPlayer *pNetObjPlayer = static_cast<CNetObjPlayer *>(ped.GetNetworkObject());
|
|
if(pNetObjPlayer && pNetObjPlayer->IsClone())
|
|
{
|
|
pNetObjPlayer->SetupPedAIAsLocal();
|
|
}
|
|
ped.SetNetworkObject(0);
|
|
|
|
if(!pPlayerInfo)
|
|
{
|
|
CPedPopulation::RemovePedFromPopulationCount(&ped);
|
|
}
|
|
|
|
if(pNetObjPlayer)
|
|
{
|
|
pNetObjPlayer->ChangePed(pNewPed);
|
|
pNewPed->SetNetworkObject(pNetObjPlayer);
|
|
|
|
// restore the player's group
|
|
pNewPed->GetPlayerInfo()->SetupPlayerGroup();
|
|
}
|
|
|
|
CPedFactory::GetFactory()->DestroyPlayerPed(&ped);
|
|
ASSERT_ONLY(CPlayerInfo::SetChangingPlayerModel(false));
|
|
|
|
// pNewPlayerPed->GetPedIntelligence()->AddTaskDefault(pNewPlayerPed->GetMoveBlender()->CreatePlayerControlTask());
|
|
|
|
StatsInterface::SetStatsModelPrefix(pNewPed);
|
|
CNewHud::HandleCharacterChange();
|
|
|
|
pNewPed->InitSpecialAbility();
|
|
|
|
if(pNewPed->IsLocalPlayer())
|
|
{
|
|
UpdateLensFlareAfterPlayerSwitch(pNewPed);
|
|
}
|
|
|
|
return pNewPed;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Changes the player control from one ped to another.
|
|
// RETURNS : pointer to the new player ped
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool CGameWorld::ChangePlayerPed(CPed& playerPed, CPed& newPlayerPed, bool bKeepScriptedTasks, bool bClearPedDamage)
|
|
{
|
|
//@@: range CGAMEWORLD_CHANGEPLAYERPED {
|
|
#if TAMPERACTIONS_ENABLED && TAMPERACTIONS_DISABLEPHONE
|
|
TamperActions::OnPlayerSwitched(playerPed, newPlayerPed);
|
|
#endif
|
|
//@@: } CGAMEWORLD_CHANGEPLAYERPED
|
|
const bool netGameInProgress = NetworkInterface::IsGameInProgress();
|
|
|
|
bool bPlayerVisible = playerPed.GetIsVisibleForModule(SETISVISIBLE_MODULE_SCRIPT);
|
|
|
|
// get the old player ped to drop any pickups he's carrying
|
|
CPickupManager::DetachAllPortablePickupsFromPed(playerPed);
|
|
|
|
//If the network Game is in progress we want to fix the pointers
|
|
// for the game object and the pointers to the network object.
|
|
if (netGameInProgress)
|
|
{
|
|
CNetObjPed* playerPedNetObj = playerPed.GetNetworkObject() ? static_cast<CNetObjPed *> (playerPed.GetNetworkObject()) : NULL;
|
|
CNetObjPed* pedNetObj = newPlayerPed.GetNetworkObject() ? static_cast<CNetObjPed *> (newPlayerPed.GetNetworkObject()) : NULL;
|
|
|
|
newPlayerPed.SetNetworkObject(NULL);
|
|
newPlayerPed.SetNetworkObject(playerPedNetObj);
|
|
if (playerPedNetObj)
|
|
{
|
|
playerPedNetObj->ChangePed(&newPlayerPed, true);
|
|
|
|
#if __BANK
|
|
AI_LOG_WITH_ARGS("[CGameWorld::ChangePlayerPed] Changing the player %s ped %s [%p] to player %s ped %s. Vehicle Entry Config: [%p] changed to [%p]",
|
|
AILogging::GetDynamicEntityIsCloneStringSafe(&playerPed), AILogging::GetDynamicEntityNameSafe(&playerPed), &playerPed, AILogging::GetDynamicEntityIsCloneStringSafe(&newPlayerPed),
|
|
AILogging::GetDynamicEntityNameSafe(&newPlayerPed), playerPed.GetVehicleEntryConfig(), newPlayerPed.GetVehicleEntryConfig());
|
|
#endif
|
|
}
|
|
|
|
playerPed.SetNetworkObject(NULL);
|
|
playerPed.SetNetworkObject(pedNetObj);
|
|
if (pedNetObj)
|
|
{
|
|
//We can not SetupPedAIAsClone on the ped because we can't Flush clone task's.
|
|
pedNetObj->ChangePed(&playerPed, true, false);
|
|
|
|
#if __BANK
|
|
AI_LOG_WITH_ARGS("[CGameWorld::ChangePlayerPed] Changing the player %s ped %s [%p] to player %s ped %s. Vehicle Entry Config: [%p] changed to [%p]",
|
|
AILogging::GetDynamicEntityIsCloneStringSafe(&newPlayerPed), AILogging::GetDynamicEntityNameSafe(&newPlayerPed), &newPlayerPed, AILogging::GetDynamicEntityIsCloneStringSafe(&playerPed),
|
|
AILogging::GetDynamicEntityNameSafe(&playerPed), newPlayerPed.GetVehicleEntryConfig(), playerPed.GetVehicleEntryConfig());
|
|
#endif
|
|
}
|
|
}
|
|
|
|
bool bFaceLeft = false;
|
|
bool bNewAIPedGoIntoCover = false;
|
|
const bool bNewPlayerPedWasReloading = newPlayerPed.GetPedResetFlag(CPED_RESET_FLAG_IsReloading);
|
|
|
|
//Cache the ped type for exchange
|
|
const ePedType playerPedType = playerPed.GetPedType();
|
|
const ePedType pedType = newPlayerPed.GetPedType();
|
|
|
|
// Don't bother doing any of the cover persisting stuff in multiplayer, we wouldn't see it and the change of
|
|
// ped type screws it up.
|
|
// See if we're currently in cover, if so, when the ped turns into an ai one, it should be in cover
|
|
if (!netGameInProgress && playerPed.GetPedModelInfo() && playerPed.GetPedModelInfo()->GetIsPlayerModel())
|
|
{
|
|
bNewAIPedGoIntoCover = StoreCoverTaskInfoForCurrentPlayer(playerPed, bFaceLeft);
|
|
}
|
|
|
|
bool bNewPlayerPedGoIntoCover = false;
|
|
|
|
if (!netGameInProgress && !playerPed.IsNetworkClone())
|
|
{
|
|
// See if the ped we're switching to is in cover, if so, when we turn into this ped, we should be in cover
|
|
if (newPlayerPed.GetPedModelInfo() && newPlayerPed.GetPedModelInfo()->GetIsPlayerModel())
|
|
{
|
|
bNewPlayerPedGoIntoCover = StoreCoverTaskInfoForAIPed(newPlayerPed);
|
|
}
|
|
|
|
// Reset the default tasks
|
|
playerPed.GetPedIntelligence()->AddTaskDefault(playerPed.ComputeDefaultTask(playerPed));
|
|
newPlayerPed.GetPedIntelligence()->AddTaskDefault(newPlayerPed.ComputeDefaultTask(newPlayerPed));
|
|
}
|
|
|
|
// cache the player info
|
|
CPlayerInfo* pPlayerInfo = playerPed.GetPlayerInfo();
|
|
Assert(pPlayerInfo);
|
|
|
|
float fWantedLevelMultiplier = pPlayerInfo->GetWanted().m_fMultiplier;
|
|
|
|
// Switch the control info
|
|
const CControlledByInfo controlInfo(playerPed.GetControlledByInfo());
|
|
|
|
// have to do this before setting the controlled by info to a player, because
|
|
// CPedPopulation::UpdatePedCountIfNotAlready does an early out if
|
|
// it's called on a player ped
|
|
CPedPopulation::RemovePedFromPopulationCount(&newPlayerPed);
|
|
|
|
newPlayerPed.SetControlledByInfo(controlInfo);
|
|
const CControlledByInfo controlInfoOld(false, false);
|
|
playerPed.SetControlledByInfo(controlInfoOld);
|
|
|
|
//Set the proper Ped Type before setting the population type because it is important
|
|
//for the population count in multiplayer.
|
|
if (netGameInProgress)
|
|
{
|
|
playerPed.SetPedType(pedType);
|
|
//Old player ped is left behind as a ambient ped in network games.
|
|
playerPed.PopTypeSet(POPTYPE_RANDOM_AMBIENT);
|
|
}
|
|
else
|
|
{
|
|
// Setup the dimishing ammo rate for this ped
|
|
playerPed.SetPedConfigFlag( CPED_CONFIG_FLAG_UseDiminishingAmmoRate, true );
|
|
playerPed.PopTypeSet(POPTYPE_MISSION);
|
|
}
|
|
playerPed.SetDefaultDecisionMaker();
|
|
playerPed.SetCharParamsBasedOnManagerType();
|
|
playerPed.GetPedIntelligence()->SetDefaultRelationshipGroup();
|
|
|
|
// Set whether player can play in-car idles (should be true). Need to do this before call to CTheScripts::RegisterEntity as this resets the flag in CPed::SetupMissionState.
|
|
newPlayerPed.SetCanPlayInCarIdles(playerPed.CanPlayInCarIdles());
|
|
|
|
//Pass control of the old player ped to the script, it was previously controlled by the game
|
|
if (POPTYPE_MISSION == playerPed.PopTypeGet() && CTheScripts::GetCurrentGtaScriptThread())
|
|
{
|
|
CTheScripts::RegisterEntity(&playerPed, false, true);
|
|
}
|
|
|
|
// reset the player into pointer (is not a non-player ped). Needs to be done after the intelligence flush.
|
|
// CTheScripts::RegisterEntity also replaces the default task, so do this after that
|
|
playerPed.SetPlayerInfo(NULL);
|
|
playerPed.m_nFlags.bAddtoMotionBlurMask = false;
|
|
|
|
ASSERT_ONLY(CPlayerInfo::SetChangingPlayerModel(true));
|
|
pPlayerInfo->SetPlayerPed(&newPlayerPed, bClearPedDamage);
|
|
ASSERT_ONLY(CPlayerInfo::SetChangingPlayerModel(false));
|
|
|
|
//Pass control of the ped from the script to the game
|
|
if (POPTYPE_MISSION == newPlayerPed.PopTypeGet())
|
|
{
|
|
CTheScripts::UnregisterEntity(&newPlayerPed, false);
|
|
}
|
|
|
|
// Unregister the script extension from the new player
|
|
CScriptEntityExtension* pExtension = newPlayerPed.GetExtension<CScriptEntityExtension>();
|
|
if (pExtension)
|
|
{
|
|
newPlayerPed.GetExtensionList().Destroy(pExtension);
|
|
}
|
|
|
|
#if FPS_MODE_SUPPORTED
|
|
const int viewMode = camControlHelper::GetViewModeForContext(camControlHelperMetadataViewMode::ON_FOOT);
|
|
if (viewMode == camControlHelperMetadataViewMode::FIRST_PERSON)
|
|
{
|
|
//B*2038645: Ensure we instantly process the FPS view mode change in CTaskPlayerOnFoot::ProcessViewModeSwitch
|
|
newPlayerPed.SetPedConfigFlag(CPED_CONFIG_FLAG_SwitchingCharactersInFirstPerson, true);
|
|
|
|
// B*2038645: If ped we're switching to is aiming and player is in FPS mode, reset the RNG timer so we don't start in the FPS_IDLE state
|
|
if (newPlayerPed.GetPedResetFlag(CPED_RESET_FLAG_IsAiming))
|
|
{
|
|
pPlayerInfo->ResetFPSRNGTimer();
|
|
pPlayerInfo->ForceFPSRNGState(true);
|
|
newPlayerPed.SetPedConfigFlag(CPED_CONFIG_FLAG_SimulatingAiming, true);
|
|
}
|
|
}
|
|
#endif //FPS_MODE_SUPPORTED
|
|
|
|
newPlayerPed.SetPlayerInfo(pPlayerInfo);
|
|
|
|
newPlayerPed.GetPedIntelligence()->FlushEvents();
|
|
|
|
newPlayerPed.ResetWaterTimers();
|
|
|
|
// Reset the diminishing ammo rate
|
|
newPlayerPed.SetPedConfigFlag( CPED_CONFIG_FLAG_UseDiminishingAmmoRate, false );
|
|
|
|
// wanted peds will update their enclosed region
|
|
playerPed.SetPedConfigFlag(CPED_CONFIG_FLAG_UpdateEnclosedSearchRegion, false);
|
|
newPlayerPed.SetPedConfigFlag(CPED_CONFIG_FLAG_UpdateEnclosedSearchRegion, true);
|
|
|
|
// unlock the ragdoll on the new player ped if it has been left locked by script, etc.
|
|
if (newPlayerPed.GetRagdollState()==RAGDOLL_STATE_ANIM_LOCKED)
|
|
{
|
|
newPlayerPed.SetRagdollState(RAGDOLL_STATE_ANIM, true);
|
|
}
|
|
|
|
newPlayerPed.GetPedIntelligence()->GetCombatBehaviour().SetCombatFloat(kAttribFloatWeaponAccuracy, 1.0f);
|
|
newPlayerPed.GetPedIntelligence()->GetCombatBehaviour().SetCombatFloat(kAttribFloatWeaponDamageModifier, 1.0f);
|
|
newPlayerPed.GetPedIntelligence()->GetCombatBehaviour().SetFlag(CCombatData::BF_CanTauntInVehicle);
|
|
playerPed.GetPedIntelligence()->GetCombatBehaviour().ClearFlag(CCombatData::BF_CanTauntInVehicle);
|
|
|
|
// Reset our firing pattern
|
|
newPlayerPed.GetPedIntelligence()->GetCombatBehaviour().SetFiringPattern(FIRING_PATTERN_FULL_AUTO);
|
|
|
|
// Restore wanted level multiplier
|
|
aiFatalAssertf(newPlayerPed.GetPlayerInfo(), "Player ped must have playerinfo!");
|
|
newPlayerPed.GetPlayerInfo()->GetWanted().m_fMultiplier = fWantedLevelMultiplier;
|
|
//Set the proper Ped Type before setting the population type because it is important
|
|
//for the population count in multiplayer
|
|
if (netGameInProgress)
|
|
{
|
|
newPlayerPed.SetPedType(playerPedType);
|
|
}
|
|
// Player character should always be a permanent char
|
|
newPlayerPed.PopTypeSet(POPTYPE_PERMANENT);
|
|
newPlayerPed.SetDefaultDecisionMaker();
|
|
newPlayerPed.SetCharParamsBasedOnManagerType();
|
|
newPlayerPed.GetPedIntelligence()->SetDefaultRelationshipGroup();
|
|
ASSERT_ONLY(CPlayerInfo::SetChangingPlayerModel(true));
|
|
if(newPlayerPed.GetControlledByInfo().IsControlledByLocalPlayer())
|
|
{
|
|
g_WeaponAudioEntity.RemoveAllPlayerInventoryWeapons();
|
|
CPed* pOldPlayerPed = CPedFactory::GetFactory()->GetLocalPlayer();
|
|
if(pOldPlayerPed && pOldPlayerPed != &newPlayerPed)
|
|
{
|
|
pOldPlayerPed->UpdateFirstPersonFlags(false);
|
|
}
|
|
CPedFactory::GetFactory()->SetLocalPlayer(&newPlayerPed);
|
|
g_WeaponAudioEntity.AddAllInventoryWeaponsOnPed(&newPlayerPed);
|
|
}
|
|
ASSERT_ONLY(CPlayerInfo::SetChangingPlayerModel(false));
|
|
|
|
// Update default tasks on both peds:
|
|
|
|
if (!playerPed.IsNetworkClone())
|
|
{
|
|
newPlayerPed.SetPedConfigFlag( CPED_CONFIG_FLAG_UseKinematicModeWhenStationary, false );
|
|
|
|
// Update the local player
|
|
if (bNewPlayerPedGoIntoCover)
|
|
{
|
|
RestoreCoverTaskInfoForNewPlayer(newPlayerPed);
|
|
|
|
// Force a pre-camera AI Update to ensure we have a valid player cover state for the gameplay camera to use
|
|
newPlayerPed.SetPedResetFlag(CPED_RESET_FLAG_ForcePreCameraAIUpdate, true);
|
|
newPlayerPed.SetPedResetFlag(CPED_RESET_FLAG_ForcePostCameraAnimUpdate, true);
|
|
}
|
|
else
|
|
{
|
|
if (!bKeepScriptedTasks || newPlayerPed.GetUsingRagdoll())
|
|
{
|
|
newPlayerPed.GetPedIntelligence()->ClearPrimaryTask();
|
|
// Don't clear the ragdoll response task
|
|
if (!newPlayerPed.GetUsingRagdoll())
|
|
{
|
|
newPlayerPed.GetPedIntelligence()->ClearTaskEventResponse();
|
|
}
|
|
else if (newPlayerPed.GetPedIntelligence()->FindTaskCombat())
|
|
{
|
|
newPlayerPed.GetPedIntelligence()->ClearTaskTempEventResponse();
|
|
}
|
|
newPlayerPed.GetPedIntelligence()->AddTaskDefault(newPlayerPed.ComputeDefaultTask(newPlayerPed));
|
|
}
|
|
else
|
|
{
|
|
newPlayerPed.SetPedConfigFlag(CPED_CONFIG_FLAG_ForcedToStayInCoverDueToPlayerSwitch, true);
|
|
newPlayerPed.SetPedConfigFlag(CPED_CONFIG_FLAG_WaitingForPlayerControlInterrupt, true);
|
|
CPlayerInfo* pPlayerInfo = newPlayerPed.GetPlayerInfo();
|
|
if (pPlayerInfo)
|
|
{
|
|
pPlayerInfo->TimeSinceSwitch = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
}
|
|
|
|
if (newPlayerPed.GetPlayerResetFlag(CPlayerResetFlags::PRF_FORCE_SKIP_AIM_INTRO))
|
|
{
|
|
newPlayerPed.SetPedResetFlag(CPED_RESET_FLAG_ForcePostCameraAIUpdate, true);
|
|
newPlayerPed.SetPedResetFlag(CPED_RESET_FLAG_ForcePostCameraAnimUpdate, true);
|
|
}
|
|
}
|
|
|
|
// Update the ped that we used to be (now under ai control)
|
|
if (bNewAIPedGoIntoCover)
|
|
{
|
|
RestoreCoverTaskInfoForNewAIPed(playerPed, bFaceLeft);
|
|
// Force a post-camera AI/Anim Update to ensure the pose is updated this frame
|
|
playerPed.SetPedResetFlag(CPED_RESET_FLAG_ForcePostCameraAIUpdate, true);
|
|
playerPed.SetPedResetFlag(CPED_RESET_FLAG_ForcePostCameraAnimUpdate, true);
|
|
}
|
|
|
|
playerPed.GetPedIntelligence()->AddTaskDefault(playerPed.ComputeDefaultTask(playerPed));
|
|
}
|
|
|
|
TUNE_GROUP_BOOL(PLAYER_SWITCH_TUNE, AUTO_SWITCH_WEAPON_IF_OUT_OF_AMMO, true);
|
|
if (AUTO_SWITCH_WEAPON_IF_OUT_OF_AMMO)
|
|
{
|
|
if (newPlayerPed.GetWeaponManager() && newPlayerPed.GetInventory())
|
|
{
|
|
const CWeaponInfo* pWeaponInfo = newPlayerPed.GetWeaponManager()->GetEquippedWeaponInfo();
|
|
if (pWeaponInfo && newPlayerPed.GetInventory()->GetIsWeaponOutOfAmmo(pWeaponInfo))
|
|
{
|
|
newPlayerPed.GetWeaponManager()->EquipBestWeapon();
|
|
}
|
|
|
|
// Hack for B*1572354, insta-refill rpg ammo for the new player ped if they were reloading the rpg
|
|
TUNE_GROUP_BOOL(PLAYER_SWITCH_HACK, USE_HACK_2, true);
|
|
if (USE_HACK_2 && bNewPlayerPedWasReloading)
|
|
{
|
|
CWeapon* pWeapon = newPlayerPed.GetWeaponManager()->GetEquippedWeapon();
|
|
if (pWeapon && pWeapon->GetWeaponInfo() && pWeapon->GetWeaponInfo()->GetIsRpg())
|
|
{
|
|
CWeapon::RefillPedsWeaponAmmoInstant(newPlayerPed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CPlayerSpecialAbility* specialAbility = playerPed.GetSpecialAbility();
|
|
if (specialAbility)
|
|
{
|
|
specialAbility->Deactivate();
|
|
}
|
|
|
|
// If the new player is in a vehicle, then ensure that this vehicle is real and not in any dummy mode
|
|
if(newPlayerPed.GetVehiclePedInside() && newPlayerPed.GetVehiclePedInside()->GetVehicleAiLod().GetDummyMode()!=VDM_REAL)
|
|
{
|
|
const bool bSkipClearanceTest = true;
|
|
newPlayerPed.GetVehiclePedInside()->TryToMakeFromDummy(bSkipClearanceTest);
|
|
newPlayerPed.GetVehiclePedInside()->m_nVehicleFlags.uAllowedDummyConversion = 0;
|
|
CVehicleAILodManager::DisableTimeslicingImmediately(*newPlayerPed.GetVehiclePedInside());
|
|
Assertf(!newPlayerPed.GetVehiclePedInside()->IsDummy(), "Hotswapped player vehicle could not be converted from dummy to real.");
|
|
}
|
|
|
|
//This needs to be reset in the player - If this flag is set cop's dont scan for crooks and their wanted level's
|
|
if (netGameInProgress && newPlayerPed.IsAPlayerPed())
|
|
{
|
|
newPlayerPed.SetBlockingOfNonTemporaryEvents(false);
|
|
}
|
|
|
|
StatsInterface::SetStatsModelPrefix(&newPlayerPed);
|
|
CNewHud::HandleCharacterChange();
|
|
|
|
// clear the entity pointer for any wanted incidents for this player, as the existing wanted incident will now be left pointing to the old player. This screws up the network syncing
|
|
if (NetworkInterface::IsGameInProgress())
|
|
{
|
|
CIncidentManager::GetInstance().RemoveEntityFromIncidents(playerPed);
|
|
|
|
Assertf(newPlayerPed.GetPedModelInfo()->GetSpecialAbilityType() == SAT_NONE, "Ped type %s should not have a special ability set!", newPlayerPed.GetModelName());
|
|
}
|
|
|
|
newPlayerPed.InitSpecialAbility(); // this uses the stats prefix set above
|
|
|
|
if (playerPed.GetPedVfx())
|
|
{
|
|
playerPed.GetPedVfx()->Init(&playerPed);
|
|
}
|
|
|
|
if (newPlayerPed.GetPedVfx())
|
|
{
|
|
newPlayerPed.GetPedVfx()->Init(&newPlayerPed);
|
|
}
|
|
|
|
if (newPlayerPed.IsNetworkClone() && !bPlayerVisible)
|
|
{
|
|
playerPed.SetIsVisibleForModule(SETISVISIBLE_MODULE_SCRIPT, false);
|
|
newPlayerPed.SetIsVisibleForModule(SETISVISIBLE_MODULE_SCRIPT, false);
|
|
}
|
|
|
|
// Update leg IK mode
|
|
newPlayerPed.m_PedConfigFlags.SetPedLegIkMode(CIkManager::GetDefaultLegIkMode(&newPlayerPed));
|
|
playerPed.m_PedConfigFlags.SetPedLegIkMode(CIkManager::GetDefaultLegIkMode(&playerPed));
|
|
|
|
//Update local player scar data.
|
|
if (NetworkInterface::IsGameInProgress() && newPlayerPed.IsLocalPlayer())
|
|
{
|
|
StatsInterface::ApplyPedScarData(&newPlayerPed, StatsInterface::GetCurrentMultiplayerCharaterSlot());
|
|
}
|
|
|
|
UpdateLensFlareAfterPlayerSwitch(&newPlayerPed);
|
|
|
|
#if GTA_REPLAY
|
|
if(CReplayMgr::ShouldRecord() && FindPlayerPed() == &newPlayerPed)
|
|
{
|
|
CReplayMgr::RecordFx(CPacketPlayerSwitch(), &newPlayerPed, &playerPed);
|
|
}
|
|
#endif // GTA_REPLAY
|
|
|
|
CVehicle::Pool* vehiclePool = CVehicle::GetPool();
|
|
const u32 NUM_VEHICLES = (u32) vehiclePool->GetSize();
|
|
for(unsigned vehicleIndex = 0; vehicleIndex < NUM_VEHICLES; vehicleIndex++)
|
|
{
|
|
CVehicle* pVeh = vehiclePool->GetSlot(vehicleIndex);
|
|
if(pVeh)
|
|
{
|
|
for(int i = 0; i < CVehicle::MAX_NUM_EXCLUSIVE_DRIVERS; i++)
|
|
{
|
|
if(pVeh->GetExclusiveDriverPed(i) == &playerPed)
|
|
{
|
|
pVeh->SetExclusiveDriverPed(&newPlayerPed, i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Stores the cover info for the player ped we're switching from
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool CGameWorld::StoreCoverTaskInfoForCurrentPlayer(CPed& playerPed, bool& bFacingLeft)
|
|
{
|
|
// Check if we are currently using cover, if so, store the cover point details in the static
|
|
// as the local player normally stores a dynamic cover point, which won't exist for the new ped unless we store it elsewhere
|
|
CCoverPoint* pOldPlayerCoverPoint = playerPed.GetCoverPoint();
|
|
CTaskInCover* pCoverTask = static_cast<CTaskInCover*>(playerPed.GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_IN_COVER));
|
|
if (pCoverTask && pOldPlayerCoverPoint)
|
|
{
|
|
// Store the facing direction so we can put the ai ped in correctly
|
|
bFacingLeft = pCoverTask->IsFacingLeft();
|
|
// Release our existing cover point
|
|
playerPed.ReleaseCoverPoint();
|
|
// Copy our cover details into the peds switch coverpoint
|
|
CTaskInCover::GetStaticCoverPointForPlayer(&playerPed).Copy(*pOldPlayerCoverPoint);
|
|
// Mark the ped to go into cover when it turns to ai control
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Stores the cover info for the ai ped we're switching to
|
|
//////////////////////////////////////////////////////////////////////
|
|
bool CGameWorld::StoreCoverTaskInfoForAIPed(CPed& aiPed)
|
|
{
|
|
// Check if the ped we're switching to is using cover
|
|
CCoverPoint* pNewPedsCoverPoint = aiPed.GetCoverPoint();
|
|
|
|
if (aiPed.GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_IN_COVER) && pNewPedsCoverPoint)
|
|
{
|
|
// Release the peds existing cover point
|
|
aiPed.ReleaseCoverPoint();
|
|
// Copy our cover details into the peds switch coverpoint
|
|
CTaskInCover::GetStaticCoverPointForPlayer(&aiPed).Copy(*pNewPedsCoverPoint);
|
|
// Mark the ped to go into cover when it turns to player control
|
|
return true;
|
|
}
|
|
else if (aiPed.GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_ENTER_COVER) && pNewPedsCoverPoint)
|
|
{
|
|
aiPed.SetPedResetFlag(CPED_RESET_FLAG_KeepCoverPoint, true);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Puts the new player ped into cover
|
|
//////////////////////////////////////////////////////////////////////
|
|
void CGameWorld::RestoreCoverTaskInfoForNewPlayer(CPed& newPlayerPed)
|
|
{
|
|
// Clear out the cover entry entity, it's no longer valid as we're switching to another ped
|
|
CPlayerInfo::ms_DynamicCoverHelper.SetCoverEntryEntity(NULL);
|
|
newPlayerPed.GetPedIntelligence()->FlushImmediately();
|
|
CTaskInCover::GetStaticCoverPointForPlayer(&newPlayerPed).ReserveCoverPointForPed(&newPlayerPed);
|
|
newPlayerPed.SetCoverPoint(&CTaskInCover::GetStaticCoverPointForPlayer(&newPlayerPed));
|
|
TUNE_GROUP_BOOL(COVER_TUNE, WARP_TO_COVER_POSITION_ON_SWITCH, true);
|
|
if (WARP_TO_COVER_POSITION_ON_SWITCH)
|
|
{
|
|
Vector3 vPedPos;
|
|
if (newPlayerPed.GetCoverPoint()->GetCoverPointPosition(vPedPos))
|
|
{
|
|
vPedPos.z += 1.0f;
|
|
TUNE_GROUP_FLOAT(COVER_TUNE, MAX_DIFF_TO_SET_GROUNDZ, 0.25f, 0.0f, 1.0f, 0.01f);
|
|
float fGroundZ = WorldProbe::FindGroundZFor3DCoord(TOP_SURFACE, vPedPos);
|
|
if (Abs(fGroundZ - vPedPos.z) < MAX_DIFF_TO_SET_GROUNDZ)
|
|
{
|
|
vPedPos.z = fGroundZ;
|
|
}
|
|
newPlayerPed.SetPosition(vPedPos);
|
|
|
|
TUNE_GROUP_FLOAT(COVER_TUNE, MAX_XY_DIST_TO_COVER, 0.25f, 0.0f, 1.0f, 0.01f);
|
|
const float fXYDist2 = (VEC3V_TO_VECTOR3(newPlayerPed.GetTransform().GetPosition()) - vPedPos).XYMag2();
|
|
const bool bConsideredAwayFromCover = fXYDist2 < square(MAX_XY_DIST_TO_COVER);
|
|
|
|
Vector3 vCoverDirection = VEC3V_TO_VECTOR3(newPlayerPed.GetCoverPoint()->GetCoverDirectionVector());
|
|
|
|
// Choose the closest heading to face the ped in cover
|
|
float fPedHeading = rage::Atan2f(-vCoverDirection.x, vCoverDirection.y);
|
|
const float fLeftCoverHeading = fPedHeading + HALF_PI;
|
|
const float fRightCoverHeading = fPedHeading - HALF_PI;
|
|
if (bConsideredAwayFromCover && newPlayerPed.GetCoverPoint()->GetUsage() == CCoverPoint::COVUSE_WALLTOLEFT)
|
|
{
|
|
fPedHeading = fRightCoverHeading;
|
|
}
|
|
else if (bConsideredAwayFromCover && newPlayerPed.GetCoverPoint()->GetUsage() == CCoverPoint::COVUSE_WALLTORIGHT)
|
|
{
|
|
fPedHeading = fLeftCoverHeading;
|
|
}
|
|
else
|
|
{
|
|
const float fLeftHeadingDelta = fwAngle::LimitRadianAngle(newPlayerPed.GetCurrentHeading() - fLeftCoverHeading);
|
|
const float fRightHeadingDelta = fwAngle::LimitRadianAngle(newPlayerPed.GetCurrentHeading() - fRightCoverHeading);
|
|
if (Abs(fLeftHeadingDelta) < Abs(fRightHeadingDelta))
|
|
{
|
|
fPedHeading = fLeftCoverHeading;
|
|
}
|
|
else
|
|
{
|
|
fPedHeading = fRightCoverHeading;
|
|
}
|
|
}
|
|
fPedHeading = fwAngle::LimitRadianAngle(fPedHeading);
|
|
newPlayerPed.SetHeading(fPedHeading);
|
|
}
|
|
}
|
|
CTaskPlayerOnFoot* pPlayerTask = rage_new CTaskPlayerOnFoot();
|
|
pPlayerTask->SetScriptedToGoIntoCover(true);
|
|
newPlayerPed.GetPedIntelligence()->AddTaskDefault(pPlayerTask);
|
|
newPlayerPed.SetPedResetFlag(CPED_RESET_FLAG_KeepCoverPoint, true);
|
|
newPlayerPed.SetPlayerResetFlag(CPlayerResetFlags::PRF_SKIP_COVER_ENTRY_ANIM);
|
|
newPlayerPed.SetIsInCover(2);
|
|
//const bool bShouldFaceLeft = newPlayerPed.GetCoverPoint()->PedClosestToFacingLeft(newPlayerPed);
|
|
//aiDisplayf("Frame %i, %s Ped %s %p Cover Info Restored, Facing Left ? %s", fwTimer::GetFrameCount(), newPlayerPed.IsLocalPlayer() ? "LOCAL PLAYER" : "AI PED", newPlayerPed.GetModelName(), &newPlayerPed, bShouldFaceLeft ? "TRUE" : "FALSE");
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Puts the new ai ped into cover
|
|
//////////////////////////////////////////////////////////////////////
|
|
void CGameWorld::RestoreCoverTaskInfoForNewAIPed(CPed& newAiPed, bool UNUSED_PARAM(bFaceLeft))
|
|
{
|
|
newAiPed.GetPedIntelligence()->FlushImmediately();
|
|
CTaskInCover::GetStaticCoverPointForPlayer(&newAiPed).ReserveCoverPointForPed(&newAiPed);
|
|
newAiPed.SetCoverPoint(&CTaskInCover::GetStaticCoverPointForPlayer(&newAiPed));
|
|
// Create a fake target
|
|
Vector3 vCoverDirection = VEC3V_TO_VECTOR3(newAiPed.GetCoverPoint()->GetCoverDirectionVector()) * 5.0f;
|
|
const bool bShouldFaceLeft = newAiPed.GetCoverPoint()->PedClosestToFacingLeft(newAiPed);
|
|
s32 iCoverFlags = bShouldFaceLeft ? CTaskCover::CF_PutPedDirectlyIntoCover | CTaskCover::CF_SpecifyInitialHeading | CTaskCover::CF_SkipIdleCoverTransition | CTaskCover::CF_FacingLeft : CTaskCover::CF_PutPedDirectlyIntoCover | CTaskCover::CF_SpecifyInitialHeading | CTaskCover::CF_SkipIdleCoverTransition;
|
|
iCoverFlags |= CTaskCover::CF_DisableAimingAndPeeking;
|
|
vCoverDirection.RotateZ(bShouldFaceLeft ? QUARTER_PI : -QUARTER_PI);
|
|
Vector3 vTargetPos = VEC3V_TO_VECTOR3(newAiPed.GetTransform().GetPosition()) + vCoverDirection;
|
|
newAiPed.GetPedIntelligence()->AddTaskAtPriority(rage_new CTaskCover(CWeaponTarget(vTargetPos), iCoverFlags), PED_TASK_PRIORITY_PRIMARY);
|
|
newAiPed.SetPedResetFlag(CPED_RESET_FLAG_KeepCoverPoint, true);
|
|
newAiPed.SetIsInCover(2);
|
|
//aiDisplayf("Frame %i, %s Ped %s %p Cover Info Restored, Facing Left ? %s", fwTimer::GetFrameCount(), newAiPed.IsLocalPlayer() ? "LOCAL PLAYER" : "AI PED", newAiPed.GetModelName(), &newAiPed, bShouldFaceLeft ? "TRUE" : "FALSE");
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Returns the player info of the local player
|
|
//////////////////////////////////////////////////////////////////////
|
|
CPlayerInfo* CGameWorld::GetMainPlayerInfo()
|
|
{
|
|
CPed* pLocalPlayer = FindLocalPlayer();
|
|
return (pLocalPlayer ? pLocalPlayer->GetPlayerInfo() : NULL);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Returns the player info of the local player
|
|
//////////////////////////////////////////////////////////////////////
|
|
CPlayerInfo* CGameWorld::GetMainPlayerInfoSafe()
|
|
{
|
|
CPed* pLocalPlayer = FindLocalPlayerSafe();
|
|
return (pLocalPlayer ? pLocalPlayer->GetPlayerInfo() : NULL);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Returns the local players group
|
|
//////////////////////////////////////////////////////////////////////
|
|
CPedGroup* CGameWorld::FindLocalPlayerGroup()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayer();
|
|
if( !pPlayer )
|
|
{
|
|
return NULL;
|
|
}
|
|
return pPlayer->GetPedsGroup();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Returns the local players vehicle
|
|
//////////////////////////////////////////////////////////////////////
|
|
CVehicle* CGameWorld::FindLocalPlayerVehicle(bool UNUSED_PARAM(bReturnRemoteVehicle))
|
|
{
|
|
CPed* pPlayer = FindLocalPlayer();
|
|
if( !pPlayer )
|
|
return NULL;
|
|
|
|
if (pPlayer->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ))
|
|
{
|
|
// if(bReturnRemoteVehicle && pPlayer->GetPlayerInfo()->GetRemoteVehicle())
|
|
// return pPlayer->GetPlayerInfo()->GetRemoteVehicle();
|
|
// else
|
|
return (pPlayer->GetMyVehicle());
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CPed* CGameWorld::FindLocalPlayerMount()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayer();
|
|
|
|
if (pPlayer && pPlayer->GetPedConfigFlag( CPED_CONFIG_FLAG_OnMount ))
|
|
{
|
|
return pPlayer->GetMyMount();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Returns the local players train if they are in one
|
|
//////////////////////////////////////////////////////////////////////
|
|
CVehicle* CGameWorld::FindLocalPlayerTrain()
|
|
{
|
|
CVehicle* pVehicle = FindLocalPlayerVehicle();
|
|
if( !pVehicle )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (pVehicle->InheritsFromTrain())
|
|
{
|
|
return pVehicle;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Returns the local players wanted level
|
|
//////////////////////////////////////////////////////////////////////
|
|
CWanted* CGameWorld::FindLocalPlayerWanted()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayer();
|
|
if( !pPlayer )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return pPlayer->GetPlayerWanted();
|
|
}
|
|
|
|
CWanted* CGameWorld::FindLocalPlayerWanted_Safe()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayerSafe();
|
|
if( !pPlayer )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return pPlayer->GetPlayerWanted();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Returns the local players arrest state
|
|
//////////////////////////////////////////////////////////////////////
|
|
eArrestState CGameWorld::FindLocalPlayerArrestState()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayer();
|
|
if( !pPlayer )
|
|
{
|
|
return eArrestState::ArrestState_None;
|
|
}
|
|
|
|
return pPlayer->GetArrestState();
|
|
}
|
|
|
|
eArrestState CGameWorld::FindLocalPlayerArrestState_Safe()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayerSafe();
|
|
if( !pPlayer )
|
|
{
|
|
return eArrestState::ArrestState_None;
|
|
}
|
|
|
|
return pPlayer->GetArrestState();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Returns the local players death state
|
|
//////////////////////////////////////////////////////////////////////
|
|
eDeathState CGameWorld::FindLocalPlayerDeathState()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayer();
|
|
if( !pPlayer )
|
|
{
|
|
return eDeathState::DeathState_Alive;
|
|
}
|
|
|
|
return pPlayer->GetDeathState();
|
|
}
|
|
|
|
eDeathState CGameWorld::FindLocalPlayerDeathState_Safe()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayerSafe();
|
|
if( !pPlayer )
|
|
{
|
|
return eDeathState::DeathState_Alive;
|
|
}
|
|
|
|
return pPlayer->GetDeathState();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Returns the local players health level
|
|
//////////////////////////////////////////////////////////////////////
|
|
float CGameWorld::FindLocalPlayerHealth()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayer();
|
|
if( !pPlayer )
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
return pPlayer->GetHealth();
|
|
}
|
|
|
|
float CGameWorld::FindLocalPlayerHealth_Safe()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayerSafe();
|
|
if( !pPlayer )
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
return pPlayer->GetHealth();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Returns the local players max health level
|
|
//////////////////////////////////////////////////////////////////////
|
|
float CGameWorld::FindLocalPlayerMaxHealth()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayer();
|
|
if( !pPlayer )
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
return pPlayer->GetMaxHealth();
|
|
}
|
|
|
|
float CGameWorld::FindLocalPlayerMaxHealth_Safe()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayerSafe();
|
|
if( !pPlayer )
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
return pPlayer->GetMaxHealth();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Returns the local players heading
|
|
//////////////////////////////////////////////////////////////////////
|
|
float CGameWorld::FindLocalPlayerHeading()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayer();
|
|
if( !pPlayer )
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
return pPlayer->GetTransform().GetHeading();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Returns the local players height
|
|
//////////////////////////////////////////////////////////////////////
|
|
float CGameWorld::FindLocalPlayerHeight()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayer();
|
|
if( !pPlayer )
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
return pPlayer->GetTransform().GetPosition().GetZf();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Returns the local players position
|
|
//////////////////////////////////////////////////////////////////////
|
|
Vector3 CGameWorld::FindLocalPlayerCoors()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayer();
|
|
if( !pPlayer )
|
|
{
|
|
Assertf( ms_playerFallbackPosSet, "FindLocalPlayerCoors called when no local player!" );
|
|
return ms_playerFallbackPos;
|
|
}
|
|
|
|
return VEC3V_TO_VECTOR3(pPlayer->GetTransform().GetPosition());
|
|
}
|
|
|
|
Vector3 CGameWorld::FindLocalPlayerCoors_Safe()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayerSafe();
|
|
if( !pPlayer )
|
|
{
|
|
Assertf( ms_playerFallbackPosSet, "FindLocalPlayerCoors called when no local player!" );
|
|
return ms_playerFallbackPos;
|
|
}
|
|
|
|
return VEC3V_TO_VECTOR3(pPlayer->GetTransform().GetPosition());
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Returns the local players speed
|
|
//////////////////////////////////////////////////////////////////////
|
|
Vector3 CGameWorld::FindLocalPlayerSpeed()
|
|
{
|
|
CPed* pPlayer = FindLocalPlayer();
|
|
if( !pPlayer )
|
|
{
|
|
return Vector3(0.0f, 0.0f, 0.0f);
|
|
}
|
|
CVehicle* vehicle = FindLocalPlayerVehicle();
|
|
if (vehicle)
|
|
{
|
|
return (vehicle->GetVelocity());
|
|
}
|
|
else
|
|
{
|
|
return (pPlayer->GetVelocity());
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// FUNCTION : Returns the Centre of the world for loading, population spawning etc...
|
|
//////////////////////////////////////////////////////////////////////
|
|
Vector3 CGameWorld::FindLocalPlayerCentreOfWorld()
|
|
{
|
|
return FindLocalPlayerCoors();
|
|
}
|
|
|
|
CPed* CGameWorld::FindPlayer(PhysicalPlayerIndex playerIndex)
|
|
{
|
|
if (playerIndex == 0)
|
|
{
|
|
return FindLocalPlayer();
|
|
}
|
|
|
|
if (NetworkInterface::IsNetworkOpen())
|
|
{
|
|
CNetGamePlayer *pPlayer = NetworkInterface::GetPhysicalPlayerFromIndex(playerIndex);
|
|
|
|
if (pPlayer)
|
|
{
|
|
return pPlayer->GetPlayerPed();
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CGameWorld::SetPlayerFallbackPos(const Vector3 &pos)
|
|
{
|
|
ms_playerFallbackPos = pos;
|
|
ms_playerFallbackPosSet = true;
|
|
}
|
|
|
|
void CGameWorld::ClearPlayerFallbackPos()
|
|
{
|
|
ms_playerFallbackPos.Zero();
|
|
ms_playerFallbackPosSet = false;
|
|
}
|
|
|
|
CPed*
|
|
CGameWorld::FindFollowPlayer()
|
|
{
|
|
if (NetworkInterface::IsInSpectatorMode() && AssertVerify(NetworkInterface::IsGameInProgress()))
|
|
{
|
|
netObject* netobj = NetworkInterface::GetNetworkObject(NetworkInterface::GetSpectatorObjectId());
|
|
if (netobj)
|
|
{
|
|
NetworkObjectType objtype = netobj->GetObjectType();
|
|
if (AssertVerify(objtype == NET_OBJ_TYPE_PLAYER || objtype == NET_OBJ_TYPE_PED))
|
|
{
|
|
CPed* ped = static_cast<CPed*>(static_cast<CNetObjPed*>(netobj)->GetGameObject());
|
|
if (AssertVerify(ped))
|
|
{
|
|
return ped;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gnetDebug1("Reset local player spectator mode");
|
|
NetworkInterface::SetSpectatorObjectId(NETWORK_INVALID_OBJECT_ID);
|
|
}
|
|
}
|
|
|
|
return FindLocalPlayer();
|
|
}
|
|
|
|
CVehicle*
|
|
CGameWorld::FindFollowPlayerVehicle()
|
|
{
|
|
CPed* pPlayer = FindFollowPlayer();
|
|
if (pPlayer && pPlayer->GetPedConfigFlag( CPED_CONFIG_FLAG_InVehicle ))
|
|
{
|
|
return (pPlayer->GetMyVehicle());
|
|
}
|
|
|
|
return FindLocalPlayerVehicle();
|
|
}
|
|
|
|
float
|
|
CGameWorld::FindFollowPlayerHeading()
|
|
{
|
|
CPed* pPlayer = FindFollowPlayer();
|
|
if(pPlayer)
|
|
{
|
|
return pPlayer->GetTransform().GetHeading();
|
|
}
|
|
|
|
return FindLocalPlayerHeading();
|
|
}
|
|
|
|
float
|
|
CGameWorld::FindFollowPlayerHeight()
|
|
{
|
|
CPed* pPlayer = FindFollowPlayer();
|
|
if(pPlayer)
|
|
{
|
|
return pPlayer->GetTransform().GetPosition().GetZf();
|
|
}
|
|
|
|
return FindLocalPlayerHeight();
|
|
}
|
|
|
|
Vector3
|
|
CGameWorld::FindFollowPlayerCoors()
|
|
{
|
|
CPed* pPlayer = FindFollowPlayer();
|
|
if(pPlayer)
|
|
{
|
|
return VEC3V_TO_VECTOR3(pPlayer->GetTransform().GetPosition());
|
|
}
|
|
|
|
return FindLocalPlayerCoors();
|
|
}
|
|
|
|
Vector3
|
|
CGameWorld::FindFollowPlayerSpeed()
|
|
{
|
|
CPed* pPlayer = FindFollowPlayer();
|
|
|
|
// This part used to be:
|
|
// CVehicle* vehicle = FindFollowPlayerVehicle();
|
|
// but doing it like this eliminates one of the calls to FindFollowPlayer(),
|
|
// which may be somewhat expensive in MP.
|
|
CVehicle* vehicle;
|
|
if(pPlayer && pPlayer->GetPedConfigFlag(CPED_CONFIG_FLAG_InVehicle))
|
|
{
|
|
vehicle = pPlayer->GetMyVehicle();
|
|
}
|
|
else
|
|
{
|
|
vehicle = FindLocalPlayerVehicle();
|
|
}
|
|
|
|
if (vehicle)
|
|
{
|
|
return (vehicle->GetVelocity());
|
|
}
|
|
else if(pPlayer)
|
|
{
|
|
return pPlayer->GetVelocity();
|
|
}
|
|
|
|
return FindLocalPlayerSpeed();
|
|
}
|
|
|
|
Vector3
|
|
CGameWorld::FindFollowPlayerCentreOfWorld()
|
|
{
|
|
return FindFollowPlayerCoors();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// FUNCTION: UpdateEntityDesc
|
|
// PURPOSE: Caches data from entity to entity descriptor
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CGameWorld::UpdateEntityDesc(fwEntity* entity, fwEntityDesc* entityDesc)
|
|
{
|
|
SceneGraphLockedFatalAssert();
|
|
|
|
const CEntity* RESTRICT pEntity = static_cast< const CEntity *RESTRICT >( entity );
|
|
|
|
SceneGraphAssertf( pEntity && entityDesc, "Trying to cache data from an pEntity to a descriptor but either of them is null" );
|
|
if ( !( pEntity && entityDesc ) )
|
|
return;
|
|
|
|
#if __BANK
|
|
if (ms_UpdateEntityDesc_StopOnSelectedEntity && g_PickerManager.GetSelectedEntity() == entityDesc->GetEntity())
|
|
{
|
|
__debugbreak();
|
|
}
|
|
#endif
|
|
// set pEntity ptr
|
|
entityDesc->SetEntity( entity );
|
|
|
|
spdAABB aabb;
|
|
spdSphere sphere;
|
|
Vec3V lodPos;
|
|
|
|
// set bound shapes
|
|
if( pEntity->GetIsDynamic() )
|
|
{
|
|
// Certain entities (mainly glass) uses wider larger difference bounds
|
|
CDynamicEntity *dynamicEntity = (CDynamicEntity*)pEntity;
|
|
dynamicEntity->GetExtendedBoundBox( aabb ); // DO NOT use GetBoundingBoxMin/Max, as they are not transformed to world coordinate space!
|
|
Vector3 center;
|
|
float radius = dynamicEntity->GetExtendedBoundCentreAndRadius( center );
|
|
sphere.Set( VECTOR3_TO_VEC3V(center), ScalarV(radius) );
|
|
}
|
|
else if(pEntity->GetIsTypeLight() || pEntity->GetIsTypePortal())
|
|
{
|
|
// DO NOT use GetBoundingBoxMin/Max, as they are not transformed to world coordinate space!
|
|
aabb = pEntity->GetBoundBox();
|
|
sphere = pEntity->GetBoundSphere();
|
|
}
|
|
else
|
|
{
|
|
// Calling this function is faster than fetching each bound individually
|
|
pEntity->GetBounds(aabb, sphere);
|
|
}
|
|
|
|
// set vec to parent lod position.
|
|
Vector3 vBasisPivot;
|
|
|
|
if ( pEntity->GetIsTypeLight() )
|
|
{
|
|
// At the moment all lights have an identity transform, that is worked around (probably for performance
|
|
// reasons) in the rest of engine code, but that causes their pivot to be always placed at 0,0,0.
|
|
// Use the center of their bounding box until that gets fixed. - AP
|
|
vBasisPivot = VEC3V_TO_VECTOR3( aabb.GetCenter() );
|
|
}
|
|
else
|
|
g_LodMgr.GetBasisPivot(entity, vBasisPivot);
|
|
|
|
entityDesc->SetApproxBoundingBoxAndSphereAndVecToLod( aabb, sphere, RCC_VEC3V(vBasisPivot) );
|
|
|
|
const fwFlags16 visFlags = pEntity->GetRenderPhaseVisibilityMask();
|
|
|
|
// cache useful data from CEntity
|
|
int entityType = pEntity->GetType();
|
|
entityDesc->SetEntityType( entityType );
|
|
entityDesc->SetRenderPhaseVisibilityMask( visFlags );
|
|
entityDesc->SetVisibilityType( pEntity->GetVisibilityType() );
|
|
entityDesc->SetDontCullSmallShadow( pEntity->IsBaseFlagSet(fwEntity::RENDER_SMALL_SHADOW) || pEntity->GetIsDynamic());
|
|
entityDesc->SetIsVisible( pEntity->GetIsVisible() );
|
|
entityDesc->SetIsSearchable( pEntity->GetIsSearchable() );
|
|
entityDesc->SetNeverDummy( pEntity->m_nFlags.bNeverDummy );
|
|
entityDesc->SetIsProcedural( pEntity->GetOwnedBy()==ENTITY_OWNEDBY_PROCEDURAL );
|
|
|
|
if (entityType == ENTITY_TYPE_OBJECT || entityType == ENTITY_TYPE_DUMMY_OBJECT)
|
|
{
|
|
CBaseModelInfo *pModelInfo = (CBaseModelInfo *)pEntity->GetArchetype();
|
|
Assertf(pModelInfo, "Entity has no modelinfo");
|
|
entityDesc->SetIsADoor(pModelInfo->GetUsesDoorPhysics());
|
|
}
|
|
else
|
|
{
|
|
entityDesc->SetIsADoor(false);
|
|
}
|
|
|
|
if (pEntity->GetIsTypeComposite())
|
|
{
|
|
// stop composite entities coming through visibility pipeline - IanK
|
|
entityDesc->SetIsVisible(false);
|
|
entityDesc->SetIsSearchable(false);
|
|
}
|
|
|
|
// take copy of lodtree access details
|
|
entityDesc->SetLodType( pEntity->GetLodData().GetLodType() );
|
|
entityDesc->SetIsRootLod( pEntity->GetLodData().IsContainerLod() && !pEntity->GetLodData().HasLod() );
|
|
|
|
// cache lod dist
|
|
entityDesc->SetLodDistCached( (s16) pEntity->GetLodDistance() );
|
|
|
|
bool pedIsInVehicle = pEntity->GetIsTypePed() && static_cast<const CPed *RESTRICT>(pEntity)->GetPedConfigFlag(CPED_CONFIG_FLAG_InVehicle);
|
|
entityDesc->SetIsPedInVehicle(pedIsInVehicle);
|
|
|
|
entityDesc->SetHDTexCapable(pEntity->GetIsHDTxdCapable());
|
|
|
|
#if STREAMING_VISUALIZE
|
|
bool strVsUpdate = true;
|
|
#endif
|
|
|
|
entityDesc->SetStraddlingPortal(false);
|
|
entityDesc->SetUseMaxDistanceForWaterReflection(pEntity->m_nFlags.bUseMaxDistanceForWaterReflection);
|
|
|
|
if (pEntity->GetIsDynamic())
|
|
{
|
|
CDynamicEntity *RESTRICT pDE = static_cast<CDynamicEntity *RESTRICT>(entity);
|
|
entityDesc->SetStraddlingPortal(pDE->m_nDEflags.bIsStraddlingPortal);
|
|
if (pDE->m_nDEflags.bIsStraddlingPortal)
|
|
{
|
|
pDE->UpdateStraddledPortalEntityDesc();
|
|
#if STREAMING_VISUALIZE
|
|
strVsUpdate = false;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if STREAMING_VISUALIZE
|
|
if (strVsUpdate)
|
|
{
|
|
STRVIS_UPDATE_ENTITY(entity);
|
|
}
|
|
#endif
|
|
}
|
|
|