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

1110 lines
35 KiB
C++

//
// name: NetGameScriptHandler.cpp
// description: Project specific network script handler
// written by: John Gurney
//
#include "script/handlers/GameScriptHandler.h"
// game includes
#include "ai/blockingbounds.h"
#include "audio/scriptaudioentity.h"
#include "camera/CamInterface.h"
#include "camera/cinematic/CinematicDirector.h"
#include "camera/gameplay/GameplayDirector.h"
#include "camera/scripted/ScriptDirector.h"
#include "control/gps.h"
#include "control/record.h"
#include "control/stuntjump.h"
#include "control/WaypointRecording.h"
#include "event/EventWeapon.h"
#include "event/Events.h"
#include "frontend/MiniMap.h"
#include "game/Dispatch/DispatchData.h"
#include "game/Dispatch/DispatchHelpers.h"
#include "game/Dispatch/DispatchManager.h"
#include "game/Dispatch/DispatchServices.h"
#include "game/Dispatch/IncidentManager.h"
#include "game/Dispatch/Incidents.h"
#include "game/user.h"
#include "game/weather.h"
#include "network/Debug/NetworkDebug.h"
#include "pathserver/PathServer.h"
#include "peds/AssistedMovementStore.h"
#include "peds/Ped.h"
#include "peds/pedpopulation.h" // CPedPopulation.
#include "peds/PopCycle.h"
#include "physics/gtaInst.h"
#include "pickups/PickupManager.h"
#include "renderer/PostProcessFX.h"
#include "renderer/RenderPhases/RenderPhaseFX.h"
#include "scene/InstancePriority.h"
#include "scene/LoadScene.h"
#include "scene/MapChange.h"
#include "Scene/Physical.h"
#include "scene/streamer/StreamVolume.h"
#include "Scene/world/GameWorld.h"
#include "Script/handlers/GameScriptEntity.h"
#include "Script/handlers/GameScriptResources.h"
#include "Script/gta_thread.h"
#include "script/script.h"
#include "script/script_cars_and_peds.h"
#include "script/script_channel.h"
#include "script/script_helper.h"
#include "script/script_hud.h"
#include "script/script_itemsets.h"
#include "streaming/streaming.h" // For CStreaming::EnableStreaming().
#include "streaming/populationstreaming.h"
#include "task/Default/Patrol/PatrolRoutes.h"
#include "task/Scenario/info/ScenarioInfo.h"
#include "task/Scenario/ScenarioPointManager.h"
#include "timecycle/TimeCycle.h"
#include "vehicleAi/junctions.h"
#include "vehicleAi/pathfind.h"
#include "vehicleAi/task/TaskVehicleCruise.h"
#include "Vehicles/cargen.h"
#include "Vehicles/train.h"
#include "Vehicles/vehiclepopulation.h"
#include "Vfx/Decals/DecalManager.h"
#include "Vfx/Metadata/VfxRegionInfo.h"
#include "Vfx/Misc/Fire.h"
#include "Vfx/Particles/PtFxManager.h"
#include "Vfx/Systems/VfxBlood.h"
#include "Vfx/Systems/VfxEntity.h"
#include "Vfx/Systems/VfxMaterial.h"
#include "Vfx/Systems/VfxPed.h"
#include "Vfx/Systems/VfxVehicle.h"
#include "Vfx/Systems/VfxWater.h"
#include "Vfx/Systems/VfxWeapon.h"
#include "Vfx/Systems/VfxWheel.h"
#include "vfx/misc/DistantLights.h"
// framework includes
#include "fwnet/netObjectMgrBase.h"
#include "fwnet/netTypes.h"
#include "fwscene/stores/imapgroup.h"
#include "fwscript/scripthandlermgr.h"
#include "fwscript/scriptinterface.h"
#include "vfx/ptfx/ptfxmanager.h"
SCRIPT_OPTIMISATIONS()
NETWORK_OPTIMISATIONS()
// all handlers are network ones at the moment
FW_INSTANTIATE_CLASS_POOL_SPILLOVER(CGameScriptHandler, CGameScriptHandler::MAX_NUM_SCRIPT_HANDLERS, 0.26f, atHashString("CGameScriptHandler",0x1182232c));
CGameScriptHandler::CGameScriptHandler(scrThread& scriptThread)
: scriptHandler(scriptThread)
, m_ScriptId(CGameScriptId(scriptThread))
{
if (scriptVerify(m_Thread))
{
GtaThread* pGtaThread = static_cast<GtaThread*>(m_Thread);
pGtaThread->m_Handler = this;
#if __DEV || __BANK
m_ScriptName = pGtaThread->GetScriptName();
#endif
}
if (NetworkInterface::IsGameInProgress())
{
CGameScriptHandlerMgr::CRemoteScriptInfo* pInfo = CTheScripts::GetScriptHandlerMgr().GetRemoteScriptInfo(m_ScriptId, true);
if (pInfo)
{
pInfo->SetRunningLocally(true);
}
}
}
CGameScriptHandler::~CGameScriptHandler()
{
if (m_Thread)
{
static_cast<GtaThread*>(m_Thread)->m_Handler = NULL;
}
}
CObject* CGameScriptHandler::GetScriptCObject(ScriptObjectId objectId)
{
scriptHandlerObject* pObj = GetScriptObject(objectId);
if (pObj)
{
return static_cast<CObject*>(pObj->GetEntity());
}
return NULL;
}
CPhysical* CGameScriptHandler::GetScriptEntity(ScriptObjectId objectId)
{
scriptHandlerObject* pObj = GetScriptObject(objectId);
if (pObj)
{
return static_cast<CPhysical*>(pObj->GetEntity());
}
return NULL;
}
bool CGameScriptHandler::Update()
{
#if __DEV
// try and assign the script name (this can stream in a little later than the thread being created)
if (!GetScriptName())
{
FindScriptName();
}
#endif
if (m_NetComponent)
{
static_cast<CGameScriptHandlerNetComponent*>(m_NetComponent)->TriggerJoinEvents();
}
return scriptHandler::Update();
}
void CGameScriptHandler::Shutdown()
{
CTheScripts::GetScriptHandlerMgr().GetLog();
if (m_ObjectList.GetHead() == NULL)
{
scriptInterface::GetScriptManager().WriteScriptHeader(scriptInterface::GetScriptManager().GetLog(), GetScriptId(), "No objects in object list to cleanup");
}
scriptHandler::Shutdown();
}
bool CGameScriptHandler::Terminate()
{
// the following code was copied from the old mission_cleanup.cpp file. Not sure if it is all still required. Someone should review it.
GtaThread* pGtaThread = SafeCast(GtaThread, m_Thread);
if (AssertVerify(pGtaThread))
{
// Remove all ped wandering/generations areas which may have been switched on/off by this script
if (pGtaThread->GetHasUsedPedPathCommand())
{
CPathServer::ClearPedSwitchAreasForScript(pGtaThread->GetThreadId());
}
// Remove script-controlled restrictions over what vehicle players may enter
if (pGtaThread->GetHasOverriddenVehicleEntering() && CGameWorld::FindLocalPlayer())
{
CGameWorld::FindLocalPlayer()->GetPlayerInfo()->SetMayOnlyEnterThisVehicle(NULL);
}
// Remove script requested path node loads
if (pGtaThread->GetThreadId() == GtaThread::GetHasLoadedAllPathNodesScriptId())
{
GtaThread::ClearHasLoadedAllPathNodes( ASSERT_ONLY( pGtaThread->GetThreadId()) );
ThePaths.bLoadAllRegions = false;
}
// If the navmesh required slot for script was used by this thread, set it as no longer active
if(CPathServer::GetNavmeshMeshRegionScriptID() == pGtaThread->GetThreadId())
{
CPathServer::RemoveNavMeshRegion( NMR_Script, pGtaThread->GetThreadId());
}
// Remove all blocking objects which may have been set up by this script
CPathServerGta::RemoveAllBlockingObjectsForScript(pGtaThread->GetThreadId());
g_LoadScene.DeregisterForScript(pGtaThread->GetThreadId());
CStreamVolumeMgr::DeregisterForScript(pGtaThread->GetThreadId());
g_MapChangeMgr.RemoveAllOwnedByScript(pGtaThread->GetThreadId());
g_MapDataStore.GetGroupSwapper().AbandonOwnedByScript(pGtaThread->GetThreadId());
// clear active script hints for entity instance priority
CInstancePriority::SetScriptHint(CInstancePriority::SCRIPT_HINT_NONE);
// Remove all the loaded waypoint recordings which were streamed by this script
CWaypointRecording::RemoveAllRecordingsRequestedByScript(pGtaThread->GetThreadId());
ThePaths.TidyUpNodeSwitchesAfterMission(pGtaThread->GetThreadId());
g_ScriptAudioEntity.RemoveScript(pGtaThread->GetThreadId());
g_vfxBlood.RemoveScript(pGtaThread->GetThreadId());
g_vfxEntity.RemoveScript(pGtaThread->GetThreadId());
g_vfxMaterial.RemoveScript(pGtaThread->GetThreadId());
g_vfxPed.RemoveScript(pGtaThread->GetThreadId());
g_vfxVehicle.RemoveScript(pGtaThread->GetThreadId());
g_vfxWater.RemoveScript(pGtaThread->GetThreadId());
g_vfxWeapon.RemoveScript(pGtaThread->GetThreadId());
g_vfxWheel.RemoveScript(pGtaThread->GetThreadId());
g_decalMan.RemoveScript(pGtaThread->GetThreadId());
g_fireMan.RemoveScript(pGtaThread->GetThreadId());
g_ptFxManager.RemoveScript(pGtaThread->GetThreadId());
ptfxManager::RemoveScript(pGtaThread->GetThreadId());
g_vfxRegionInfoMgr.RemoveScript(pGtaThread->GetThreadId());
CPedGroups::ResetAllFormationsModifiedByThisScript(pGtaThread->GetThreadId());
CDispatchManager::SetLawResponseDelayOverride(-1.0f);
// Should I be calling CTheScripts::GetHiddenObjects().UndoHiddenObjects() to clear all hidden objects belonging
// to this script? Probably best to leave it up to each individual CObject to handle its own
if( camInterface::IsInitialized() )
{
camInterface::GetScriptDirector().OnScriptTermination(pGtaThread);
}
if (pGtaThread->IsThisAMissionScript || pGtaThread->GetIsARandomEventScript())
{
CVehiclePopulation::ms_overrideNumberOfParkedCars = -1;
CPedPopulation::PedNonRemovalAreaClear();
CPedPopulation::SetScenarioPedDensityMultipliers(1.0f, 1.0f);
CVehiclePopulation::ms_bAllowRandomBoats = true;
ThePaths.bIgnoreNoGpsFlag = false;
ThePaths.bIgnoreNoGpsFlagUntilFirstNode = false;
//Ensure the cinematic button is not disabled beyond the end of a mission.
camInterface::GetCinematicDirector().SetCinematicButtonDisabledByScriptState(false);
camInterface::GetGameplayDirector().SetPersistentHighAltitudeFovScalingState(true);
g_weather.SetWindOverride(-0.01f);
CTrain::DisableRandomTrains(false);
CTrain::ReleaseMissionTrains(pGtaThread->GetThreadId());
CTrain::RestoreScriptModificationsToTracks(pGtaThread->GetThreadId());
CWanted::GetWantedBlips().EnablePoliceRadarBlips(true);
CWanted::GetWantedBlips().ResetBlipSpriteToUseForCopPeds();
CWanted::SetMaximumWantedLevel(WANTED_LEVEL_LAST - 1);
CTheCarGenerators::SetAllCarGeneratorsBackToActive();
CVehiclePopulation::ms_bAllowGarbageTrucks = true;
CPedPopulation::SetAmbientPedDensityMultiplier(1.0f);
CVehiclePopulation::SetParkedCarDensityMultiplier( 1.0f );
CVehiclePopulation::SetRandomVehDensityMultiplier( 1.0f );
CVehiclePopulation::SetScriptedRangeMultiplier( 1.0f );
CVehiclePopulation::ms_bAllowEmergencyServicesToBeCreated = true;
CPedPopulation::SetForcedNewAmbientPedType(-1);
gPopStreaming.SetReduceAmbientPedModelBudget(false);
gPopStreaming.SetReduceAmbientVehicleModelBudget(false);
gPopStreaming.ResetPedMemoryBudgetLevel();
gPopStreaming.ResetVehMemoryBudgetLevel();
CPedPopulation::SetAllowCreateRandomCops(true);
CPedPopulation::SetAllowCreateRandomCopsOnScenarios(true);
CPedPopulation::SetAllowDummyConversions(true);
CTheCarGenerators::bScriptDisabledCarGenerators = false;
CGps::RenderRaceTrack(false);
CGps::ClearGpsFlagsOnScriptExit(pGtaThread->GetThreadId());
CGps::GetSlot(GPS_SLOT_USED_FOR_WAYPOINTS_GPS).Clear(GPS_SLOT_USED_FOR_WAYPOINTS_GPS, true);
CGps::GetSlot(GPS_SLOT_USED_FOR_CUSTOM_ROUTE).Clear(GPS_SLOT_USED_FOR_CUSTOM_ROUTE, true);
CGps::GetSlot(GPS_SLOT_USED_FOR_DISCRETE_ROUTE).Clear(GPS_SLOT_USED_FOR_DISCRETE_ROUTE, true);
CPed::ms_MoneyCarriedByAllNewPeds = -1;
if(pGtaThread->IsThisAMissionScript && SStuntJumpManager::IsInstantiated())
{
SStuntJumpManager::GetInstance().AbortStuntJumpInProgress(); // Do this as there was a bug with missions failing while a stuntjump was active. The slowmo would never end.
SStuntJumpManager::GetInstance().SetActive(true); // The script may have disabled the stunt jumps.
}
CScriptHud::fFakeMinimapAltimeterHeight = 0.0f;
CScriptHud::ms_bColourMinimapAltimeter = false;
CScriptHud::iFakeWantedLevel = 0;
CScriptHud::vFakeWantedLevelPos = Vector3(0,0,0);
ThePaths.ReleaseRequestedNodes(NODE_REQUEST_SCRIPT);
// Call James' function to remove all navmesh required regions
CPathServer::RemoveNavMeshRegion(NMR_Script, pGtaThread->GetThreadId());
// Remove all assisted movement routes
CAssistedMovementRouteStore::GetRouteToggles().ResetAll(pGtaThread->GetThreadId());
//Removing scripted patrol nodes
CPatrolRoutes::ClearScriptedPatrolRoutes();
g_weather.ForceTypeClear();
g_timeCycle.SetRegionOverride(-1);
g_distantLights.SetDisableVehicleLights(false);
PostFX::SetNoiseOverride(false);
PostFX::SetNoisinessOverride(0.0f);
RenderPhaseSeeThrough::ResetHeatScale();
g_vfxWeapon.ResetIntSmokeOverrides();
// Disable infra red vision
RenderPhaseSeeThrough::SetState(false);
CScriptCars::GetSuppressedCarModels().ClearAllSuppressedModels();
CScriptPeds::GetSuppressedPedModels().ClearAllSuppressedModels();
CScriptPeds::GetRestrictedPedModels().ClearAllRestrictedModels();
CStreaming::EnableStreaming();
CTheScripts::SetAllowGameToPauseForStreaming(true);
CMiniMap::ClearSonarBlips();
CMiniMap::AllowSonarBlips(true);
CScriptHud::ms_fRadarZoomDistanceThisFrame = 0.0f;
CScriptHud::ms_fMiniMapForcedZoomPercentage = 0.0f;
CScriptHud::ms_iRadarZoomValue = 0;
CScriptHud::bDisplayHud = true;
CScriptHud::bDisplayRadar = true;
// CTheScripts::fCameraHeadingWhenPlayerIsAttached = 0.0f;
// CTheScripts::fCameraHeadingStepWhenPlayerIsAttached = 0.0f;
CUserDisplay::OnscnTimer.FreezeTimers = FALSE;
// CScriptHud::bUseMessageFormatting = false;
// CScriptHud::MessageCentre = 0;
// CScriptHud::MessageWidth = 0;
// player might not exist if we are returning to the single player script
// from a network script
if (CGameWorld::FindLocalPlayer())
{
CGameWorld::FindLocalPlayer()->CleanupMissionState();
if (CTheScripts::GetNumberOfMiniGamesInProgress() == 0 && pGtaThread->bSetPlayerControlOnInMissionCleanup)
{
CPlayerInfo* pPlayerInfo = CGameWorld::FindLocalPlayer()->GetPlayerInfo();
if (scriptVerifyf(pPlayerInfo, "CGameScriptHandler::Terminate - pPlayerInfo is NULL for the local player"))
{
pPlayerInfo->GetWanted().m_AllRandomsFlee = FALSE;
pPlayerInfo->GetWanted().m_AllNeutralRandomsFlee = FALSE;
pPlayerInfo->GetWanted().m_AllRandomsOnlyAttackWithGuns = FALSE;
pPlayerInfo->GetWanted().m_PoliceBackOff = FALSE;
pPlayerInfo->GetWanted().m_EverybodyBackOff = FALSE;
pPlayerInfo->GetWanted().m_IgnoreLowPriorityShockingEvents = FALSE;
pPlayerInfo->SetPlayerControl(false, false);
}
}
}
// Clear the array used for checking for upside-down cars
CScriptCars::GetUpsideDownCars().Init();
// Clear the array used for checking for stuck cars
CScriptCars::GetStuckCars().Init();
// Reset the GunShot Range
CEventGunShot::SetGunShotSenseRangeForRiot2(-1.0f);
//CScriptedPriorities::Clear();
if (CTheScripts::GetNumberOfMiniGamesInProgress() == 0 && pGtaThread->bClearHelpInMissionCleanup)
{
CHelpMessage::ClearAll();
}
// Some relationship groups we need to make sure we reset the feelings between said group and the player rel group
if( CRelationshipManager::s_pPlayerGroup )
{
if( CRelationshipManager::s_pCopGroup )
{
CRelationshipManager::s_pPlayerGroup->ClearRelationship(CRelationshipManager::s_pCopGroup);
CRelationshipManager::s_pCopGroup->ClearRelationship(CRelationshipManager::s_pPlayerGroup);
}
if( CRelationshipManager::s_pArmyGroup )
{
CRelationshipManager::s_pPlayerGroup->ClearRelationship(CRelationshipManager::s_pArmyGroup);
CRelationshipManager::s_pArmyGroup->ClearRelationship(CRelationshipManager::s_pPlayerGroup);
}
if( CRelationshipManager::s_pSecurityGuardGroup )
{
CRelationshipManager::s_pPlayerGroup->ClearRelationship(CRelationshipManager::s_pSecurityGuardGroup);
CRelationshipManager::s_pSecurityGuardGroup->ClearRelationship(CRelationshipManager::s_pPlayerGroup);
}
}
// Remove any cover blocking areas added during a mission
CCover::FlushCoverBlockingAreas();
// Remove any ped non-creation or non-removal zones.
CPedPopulation::PedNonCreationAreaClear();
CPedPopulation::PedNonRemovalAreaClear();
CDispatchManager::GetInstance().EnableAllDispatch(true);
CDispatchManager::GetInstance().BlockAllDispatchResourceCreation(false);
CIncidentManager::GetInstance().ClearAllIncidentsOfType(CIncident::IT_Scripted);
//Reset the dispatch spawn properties.
CDispatchSpawnProperties::GetInstance().Reset();
//Reset the wanted response overrides.
CWantedResponseOverrides::GetInstance().Reset();
//Restore points that had their priority toggled.
CScenarioPointPriorityManager::GetInstance().RestoreGroupsToOriginalPriorities();
CJunctions::OnScriptTerminate(pGtaThread->GetThreadId());
CDebug::DumpCollisionStatsToNetwork(pGtaThread->GetScriptName());
CVehicle::SetLightsCutoffDistanceTweak(0.0f);
CVehicle::ForceActualVehicleLightsOff(false);
CPed::SetAllowHurtForAllMissionPeds(true);
} // end of if (pGtaThread->IsThisAMissionScript || pGtaThread->GetIsARandomEventScript())
if (pGtaThread->IsThisAMissionScript)
{
pGtaThread->IsThisAMissionScript = false;
scriptAssertf(CTheScripts::GetPlayerIsOnAMission() == true, "%s:CMissionCleanup::Process - Mission Flag is already FALSE", pGtaThread->GetScriptName());
CTheScripts::SetPlayerIsOnAMission(false);
scriptDisplayf("MISSION_FLAG cleared by mission cleanup of %s\n", pGtaThread->GetScriptName());
#if __BANK
if (fragInstNMGta::ms_bLogUsedRagdolls)
{
// Display max ragdolls used for the level
Printf("\n\n");
Displayf("[RAGDOLL WATERMARK] Mission %s ended. A maximum of %d NM AGENTS and %d RAGE RAGDOLLS were used individually this mission. %d fallback animations were used.", pGtaThread->GetScriptName(),
fragInstNMGta::ms_MaxNMAgentsUsedCurrentLevel, fragInstNMGta::ms_MaxRageRagdollsUsedCurrentLevel, fragInstNMGta::ms_NumFallbackAnimsUsedCurrentLevel);
Displayf("[RAGDOLL WATERMARK] At peak usage this mission, a total of %d ragdolls were used simultaneously. %d of those were NM agents and %d were rage ragdolls.",
fragInstNMGta::ms_MaxTotalRagdollsUsedCurrentLevel, fragInstNMGta::ms_MaxNMAgentsUsedInComboCurrentLevel, fragInstNMGta::ms_MaxRageRagdollsUsedInComboCurrentLevel);
Printf("\n\n");
// Clear counters
fragInstNMGta::ms_MaxNMAgentsUsedCurrentLevel = 0;
fragInstNMGta::ms_MaxRageRagdollsUsedCurrentLevel = 0;
fragInstNMGta::ms_MaxNMAgentsUsedInComboCurrentLevel = 0;
fragInstNMGta::ms_MaxRageRagdollsUsedInComboCurrentLevel = 0;
fragInstNMGta::ms_MaxTotalRagdollsUsedCurrentLevel = 0;
fragInstNMGta::ms_NumFallbackAnimsUsedCurrentLevel = 0;
}
#endif
}
if (pGtaThread->GetIsARandomEventScript())
{
pGtaThread->SetIsARandomEventScript(false);
scriptAssertf(CTheScripts::GetPlayerIsOnARandomEvent() == true, "%s: CMissionCleanup::Process - ms_bPlayerIsOnARandomEvent is already FALSE", pGtaThread->GetScriptName());
CTheScripts::SetPlayerIsOnARandomEvent(false);
scriptDisplayf("Random Event flag cleared by mission cleanup of %s\n", pGtaThread->GetScriptName());
}
if (pGtaThread->bIsThisAMiniGameScript)
{
pGtaThread->TidyUpMiniGameFlag();
}
}
return scriptHandler::Terminate();
}
void CGameScriptHandler::CreateNetworkComponent()
{
Assertf(0 , "Trying to create a network component for a game script handler");
}
scriptResource* CGameScriptHandler::RegisterScriptResource(scriptResource& resource, bool *pbResourceHasJustBeenAddedToList)
{
scriptResource* pNewResource = NULL;
#if __DEV
if (!GetScriptName())
{
FindScriptName();
}
#endif
if (pbResourceHasJustBeenAddedToList)
{
*pbResourceHasJustBeenAddedToList = false;
}
pNewResource = GetScriptResource(resource.GetType(), resource.GetReference());
// Argh! Can't assert here - lots of the scripts using the old mission cleanup stuff were registering resources twice, so this has to fail
// silently at the moment!
// scriptAssertf(!pNewResource, "%s: Script resource %s with reference %d already exists", CTheScripts::GetCurrentScriptNameAndProgramCounter(), resource.GetResourceName(), resource.GetReference()))
if (!pNewResource)
{
if (CGameScriptResource::GetPool()->GetNoOfFreeSpaces() > 0)
{
pNewResource = scriptHandler::RegisterScriptResource(resource, pbResourceHasJustBeenAddedToList);
}
#if !__FINAL
else
{
#if __DEV
scriptDisplayf("CGameScriptResource is full. Here is a list of all entities and resources belonging to all scripts");
CTheScripts::GetScriptHandlerMgr().SpewObjectAndResourceInfo();
#endif // __DEV
const s32 pool_size = CGameScriptResource::GetPool()->GetSize();
scriptDisplayf("CGameScriptResource is full. Size = %d, Number of Used Spaces = %d", pool_size, CGameScriptResource::GetPool()->GetNoOfUsedSpaces());
s32 pool_array_index = 0;
while (pool_array_index < pool_size)
{
CGameScriptResource *pCurrentResource = CGameScriptResource::GetPool()->GetSlot(pool_array_index);
if (pCurrentResource)
{
scriptDisplayf("Pool index %d: Reference %d Type %s", pool_array_index, pCurrentResource->GetReference(), pCurrentResource->GetResourceName());
}
pool_array_index++;
}
#if __BANK
scriptDisplayf("%s : Ran out of script resources, probably due to a bug in the script", GetScriptName());
#endif // __BANK
scriptAssertf(0, "%s : Ran out of script resources, probably due to a bug in the script", GetScriptName());
}
#endif // !__FINAL
#if __DEV
if (pNewResource)
{
scriptDisplayf("%s : Register new resource %s(%d) (id: %d, ref: %d)", GetScriptName(), pNewResource->GetResourceName(), pNewResource->GetType(), pNewResource->GetId(), pNewResource->GetReference());
}
else
{
scriptDisplayf("%s : Register new resource %s(%d) **failed**", GetScriptName(), resource.GetResourceName(), resource.GetType());
}
#endif
}
return pNewResource;
}
bool CGameScriptHandler::RemoveScriptResource(ScriptResourceType type, const ScriptResourceRef ref, bool bDetach, bool UNUSED_PARAM(bAssert))
{
if (!scriptHandler::RemoveScriptResource(type, ref, bDetach, false))
{
// Argh! Can't assert here - lots of the scripts using the old mission cleanup stuff were removing resources twice, so this has to fail
// silently at the moment!
//scriptAssertf(!bAssert, "%s: Resource with type %d and ref %d does not exist", CTheScripts::GetCurrentScriptNameAndProgramCounter(), type, ref);
return false;
}
return true;
}
bool CGameScriptHandler::RemoveScriptResource(ScriptResourceId resourceId, bool bDetach, bool UNUSED_PARAM(bAssert), ScriptResourceType resourceType)
{
if (!scriptHandler::RemoveScriptResource(resourceId, bDetach, false, resourceType))
{
// Argh! Can't assert here - lots of the scripts using the old mission cleanup stuff were removing resources twice, so this has to fail
// silently at the moment!
//scriptVerifyf(!bAssert, "%s: Resource with id %d does not exist", CTheScripts::GetCurrentScriptNameAndProgramCounter(), resourceId);
return false;
}
return true;
}
ScriptResourceRef CGameScriptHandler::RegisterScriptResourceAndGetRef(scriptResource& resource)
{
scriptResource* pNewResource = RegisterScriptResource(resource);
if (pNewResource)
{
return pNewResource->GetReference();
}
else
{
return resource.GetInvalidReference();
}
}
ScriptResourceId CGameScriptHandler::RegisterScriptResourceAndGetId(scriptResource& resource)
{
scriptResource* pNewResource = RegisterScriptResource(resource);
if (pNewResource)
{
return pNewResource->GetId();
}
else
{
return scriptResource::INVALID_RESOURCE_ID;
}
}
u32 CGameScriptHandler::GetNumRegisteredObjects() const
{
u32 numObjects = 0;
const atDNode<scriptHandlerObject*, datBase> *node = m_ObjectList.GetHead();
while (node)
{
numObjects++;
node = node->GetNext();
}
return numObjects;
}
u32 CGameScriptHandler::GetNumRegisteredNetworkedObjects() const
{
u32 numObjects = 0;
const atDNode<scriptHandlerObject*, datBase> *node = m_ObjectList.GetHead();
while (node)
{
if (!node->Data->IsUnnetworkedObject())
{
numObjects++;
}
node = node->GetNext();
}
return numObjects;
}
u32 CGameScriptHandler::GetAllEntities(CEntity* entityArray[], u32 arrayLen)
{
atDNode<scriptHandlerObject*, datBase> *node = m_ObjectList.GetHead();
u32 numEntities = 0;
while (node && AssertVerify(numEntities<arrayLen))
{
if (node->Data->GetEntity())
{
entityArray[numEntities++] = static_cast<CEntity*>(node->Data->GetEntity());
}
node = node->GetNext();
}
return numEntities;
}
void CGameScriptHandler::GetStreamingIndices(atArray<strIndex>& streamingIndices, u32 skipFlag) const
{
const atDNode<scriptHandlerObject*, datBase> *node = m_ObjectList.GetHead();
while (node)
{
if (node->Data->GetEntity())
{
strIndex index = static_cast<CScriptEntityExtension*>(node->Data)->GetStreamingIndex();
if(!(strStreamingEngine::GetInfo().GetObjectFlags(index) & skipFlag))
streamingIndices.PushAndGrow(index);
}
node = node->GetNext();
}
ResourceList::const_iterator curr = m_ResourceList.begin();
ResourceList::const_iterator end = m_ResourceList.end();
for(; curr != end; ++curr)
{
const CGameScriptResource* resource = static_cast<const CGameScriptResource*>(*curr);
strIndex streamingIndex = resource->GetStreamingIndex();
if (streamingIndex != strIndex(strIndex::INVALID_INDEX) && !(strStreamingEngine::GetInfo().GetObjectFlags(streamingIndex) & skipFlag))
{
streamingIndices.PushAndGrow(streamingIndex);
}
}
}
#if __BANK
void CGameScriptHandler::DisplayScriptHandlerInfo() const
{
grcDebugDraw::AddDebugOutput("");
grcDebugDraw::AddDebugOutput("%s", GetScriptName());
grcDebugDraw::AddDebugOutput("");
grcDebugDraw::AddDebugOutput("Num registered objects: %d", GetNumRegisteredObjects());
grcDebugDraw::AddDebugOutput("");
const atDNode<scriptHandlerObject*, datBase> *node = m_ObjectList.GetHead();
while (node)
{
if (node->Data->GetEntity())
{
netObject* pNetObj = static_cast<CPhysical*>(node->Data->GetEntity())->GetNetworkObject();
if (pNetObj)
{
grcDebugDraw::AddDebugOutput("%s", pNetObj->GetLogName());
}
}
node = node->GetNext();
}
grcDebugDraw::AddDebugOutput("");
}
void CGameScriptHandler::DisplayObjectAndResourceInfo() const
{
const atDNode<scriptHandlerObject*, datBase> *node = m_ObjectList.GetHead();
while (node)
{
if (node->Data->GetEntity())
{
static_cast<CScriptEntityExtension*>(node->Data)->DisplayDebugInfo(GetScriptName());
}
node = node->GetNext();
}
ResourceList::const_iterator curr = m_ResourceList.begin();
ResourceList::const_iterator end = m_ResourceList.end();
for(; curr != end; ++curr)
{
const CGameScriptResource* resource = static_cast<const CGameScriptResource*>(*curr);
resource->DisplayDebugInfo(GetScriptName());
}
}
void CGameScriptHandler::SpewObjectAndResourceInfo() const
{
u32 count = 0;
const atDNode<scriptHandlerObject*, datBase> *node = m_ObjectList.GetHead();
while (node)
{
node->Data->SpewDebugInfo(GetScriptName());
node = node->GetNext();
count++;
}
if (count > 0)
{
scriptDisplayf("CGameScriptHandler::SpewObjectAndResourceInfo - %u entities registered with %s", count, GetScriptName());
}
count = 0;
ResourceList::const_iterator curr = m_ResourceList.begin();
ResourceList::const_iterator end = m_ResourceList.end();
for(; curr != end; ++curr)
{
const CGameScriptResource* resource = static_cast<const CGameScriptResource*>(*curr);
resource->SpewDebugInfo(GetScriptName());
count++;
}
if (count > 0)
{
scriptDisplayf("CGameScriptHandler::SpewObjectAndResourceInfo - %u resources registered with %s", count, GetScriptName());
}
}
void CGameScriptHandler::SpewAllResourcesOfType(ScriptResourceType resourceType) const
{
ResourceList::const_iterator curr = m_ResourceList.begin();
ResourceList::const_iterator end = m_ResourceList.end();
u32 count = 0;
for(; curr != end; ++curr)
{
const CGameScriptResource* resource = static_cast<const CGameScriptResource*>(*curr);
if(resource->GetType() == resourceType)
{
resource->SpewDebugInfo(GetScriptName());
++count;
}
}
if (count > 0)
{
scriptDisplayf("CGameScriptHandler::SpewAllResourcesOfType - %u resources registered with %s\n", count, GetScriptName());
}
}
#endif // __BANK
#if __SCRIPT_MEM_CALC
#if __SCRIPT_MEM_DISPLAY
typedef struct
{
strIndex index;
u32 count;
} strIndexAndCount;
void CGameScriptHandler::DisplayUniqueObjectsForScript(const atArray<strIndex>& streamingIndices, const strIndex* ignoreList, s32 numIgnores) const
{
// Now tally the streaming index's
atArray<strIndexAndCount> talliedStreamingIndices;
for(int i=0; i<streamingIndices.size();i++)
{
strIndex thisIndex = streamingIndices[i];
int foundIDX = -1;
for(int j=0;j<talliedStreamingIndices.size();j++)
{
if( talliedStreamingIndices[j].index == thisIndex )
{
foundIDX = j;
break;
}
}
if(foundIDX == -1)
{
// New
strIndexAndCount thisTally;
thisTally.index = thisIndex;
thisTally.count = 1;
talliedStreamingIndices.Grow() = thisTally;
}
else
{
// Tally
talliedStreamingIndices[foundIDX].count++;
}
}
// Now print em out tallied up
// Get modelinfo... strStreamingEngine::GetInfo().GetObjectName(strIndex) ..
// to get model name. get sizes from strIndex, not sure about type...
// look into that
for(int i=0;i<talliedStreamingIndices.size();i++)
{
u32 virtualSize = 0;
u32 physicalSize = 0;
strIndexAndCount entry = talliedStreamingIndices[i];
// Need to get fwModelId from strIndex here
u32 index = strStreamingEngine::GetInfo().GetObjectIndex(entry.index).Get();
CBaseModelInfo* pModelInfo = CModelInfo::GetModelInfoFromLocalIndex(index);
const char *pName = strStreamingEngine::GetInfo().GetObjectName(entry.index);
bool bDDFlagSet = false;
if(strStreamingEngine::GetInfo().GetObjectFlags(entry.index) & STRFLAG_DONTDELETE)
{
bDDFlagSet = true;
}
strStreamingEngine::GetInfo().GetObjectAndDependenciesSizes(entry.index, virtualSize, physicalSize, ignoreList, numIgnores, true);
strStreamingModule* pModule = CModelInfo::GetStreamingModule();
if (Verifyf( pModule == strStreamingEngine::GetInfo().GetModule(entry.index), "not a model info"))
{
char typeBuffer[64];
if(pModelInfo)
{
u8 modelType = pModelInfo->GetModelType();
if( modelType == MI_TYPE_VEHICLE )
{
sprintf(typeBuffer, "Vehicle");
}
else if( modelType == MI_TYPE_PED )
{
sprintf(typeBuffer, "Ped");
}
else
{
sprintf(typeBuffer, "Object");
}
}
grcDebugDraw::AddDebugOutput("%s: %s %s (%d) Model %dK %dK %s(for this entity, including deps)",
GetScriptName(),
typeBuffer,
pName,
entry.count,
virtualSize>>10, physicalSize>>10,
bDDFlagSet ? "(DD)":"");
}
}
}
#endif //__SCRIPT_MEM_DISPLAY
// Is the skipFlag STRFLAG_DONTDELETE still required?
void CGameScriptHandler::CalculateMemoryUsage(atArray<strIndex>& streamingIndices,
const strIndex* ignoreList, s32 numIgnores,
u32 &VirtualForResourcesWithoutStreamingIndex, u32 &PhysicalForResourcesWithoutStreamingIndex,
bool bDisplayScriptDetails, bool bFilterDetails, u32 skipFlag) const
{
const atDNode<scriptHandlerObject*, datBase> *node = m_ObjectList.GetHead();
while (node)
{
fwEntity* pEntity = node->Data->GetEntity();
if (pEntity)
{
strIndex index = static_cast<CScriptEntityExtension*>(node->Data)->GetStreamingIndex();
if(!(strStreamingEngine::GetInfo().GetObjectFlags(index) & skipFlag))
{
#if !__FINAL
// Do not count if this is a script-created personal vehicle not owned by the local player
if (pEntity->GetType() == ENTITY_TYPE_VEHICLE)
{
CVehicle* pVehicle = verify_cast<CVehicle*>(pEntity);
if (pVehicle && pVehicle->IsRemotePersonalVehicle())
{
//Displayf("SCRIPT MEM: Not counting remote personal vehicle: %s", pVehicle->GetDebugName());
node = node->GetNext();
continue;
}
}
#endif
streamingIndices.Append() = index;
#if __SCRIPT_MEM_DISPLAY
if (bDisplayScriptDetails && !bFilterDetails)
{
static_cast<CScriptEntityExtension*>(node->Data)->DisplayMemoryUsage(GetScriptName(), ignoreList, numIgnores, skipFlag);
}
#endif // __SCRIPT_MEM_DISPLAY
}
}
node = node->GetNext();
}
#if __SCRIPT_MEM_DISPLAY
if (bDisplayScriptDetails && bFilterDetails)
{
DisplayUniqueObjectsForScript(streamingIndices, ignoreList, numIgnores);
}
#endif // __SCRIPT_MEM_DISPLAY
ResourceList::const_iterator curr = m_ResourceList.begin();
ResourceList::const_iterator end = m_ResourceList.end();
for(; curr != end; ++curr)
{
const CGameScriptResource* resource = static_cast<const CGameScriptResource*>(*curr);
u32 NonStreamingVirtualForThisResource = 0;
u32 NonStreamingPhysicalForThisResource = 0;
const char *pScriptName = "";
#if __SCRIPT_MEM_DISPLAY
pScriptName = GetScriptName();
#endif // __SCRIPT_MEM_DISPLAY
resource->CalculateMemoryUsage(pScriptName, streamingIndices, ignoreList, numIgnores,
NonStreamingVirtualForThisResource, NonStreamingPhysicalForThisResource,
bDisplayScriptDetails, skipFlag);
VirtualForResourcesWithoutStreamingIndex += NonStreamingVirtualForThisResource;
PhysicalForResourcesWithoutStreamingIndex += NonStreamingPhysicalForThisResource;
}
}
#endif // __SCRIPT_MEM_CALC
ScriptResourceId CGameScriptHandler::GetNextFreeResourceId(scriptResource& resource)
{
return scriptHandler::GetNextFreeResourceId(resource);
}
void CGameScriptHandler::DetachAllUnnetworkedObjects(scriptHandlerObject* objects[MAX_UNNETWORKED_OBJECTS], u32& numObjects)
{
atDNode<scriptHandlerObject*, datBase> *node = m_ObjectList.GetHead();
numObjects = 0;
netLoggingInterface *log = CTheScripts::GetScriptHandlerMgr().GetLog();
static const unsigned LOG_NAME_LEN = 128;
char logName[LOG_NAME_LEN];
while (node && AssertVerify(numObjects < MAX_UNNETWORKED_OBJECTS))
{
atDNode<scriptHandlerObject*, datBase> *nextNode = node->GetNext();
scriptHandlerObject* pObject = node->Data;
if (pObject->IsUnnetworkedObject())
{
objects[numObjects++] = pObject;
m_ObjectList.PopNode(*node);
delete node;
pObject->SetScriptHandler(NULL);
if (log)
{
pObject->GetScriptInfo()->GetLogName(logName, LOG_NAME_LEN);
NetworkLogUtils::WriteLogEvent(*log, "DETACH_UNNETWORKED_OBJECT", "%s %s", pObject->GetScriptInfo()->GetScriptId().GetLogName(), logName);
log->WriteDataValue("Script id", "%d", pObject->GetScriptInfo()->GetObjectId());
}
}
node = nextNode;
}
}
bool CGameScriptHandler::DestroyScriptResource(scriptResource& resource)
{
bool success = scriptHandler::DestroyScriptResource(resource);
#if __DEV
if (success)
{
scriptDisplayf("%s : Destroy script resource %s(%d) (id: %d, ref: %d)", GetScriptName(), resource.GetResourceName(), resource.GetType(), resource.GetId(), resource.GetReference());
}
else
{
scriptDisplayf("%s : Script resource %s(%d) left for other scripts (id: %d, ref: %d)", GetScriptName(), resource.GetResourceName(), resource.GetType(), resource.GetId(), resource.GetReference());
}
#endif
return success;
}
void CGameScriptHandler::CleanupScriptObject(scriptHandlerObject &object)
{
scriptHandler::CleanupScriptObject(object);
fwEntity* pEntity = object.GetEntity();
if (pEntity)
{
static_cast<CDynamicEntity*>(pEntity)->DestroyScriptExtension();
}
}
#if __DEV
void CGameScriptHandler::FindScriptName()
{
GtaThread* pGtaThread = static_cast<GtaThread*>(m_Thread);
// try and assign the script name (this can stream in a little later than the thread being created)
if (!GetScriptName() && pGtaThread && pGtaThread->GetScriptName())
{
m_ScriptName = pGtaThread->GetScriptName();
#if __BANK
CTheScripts::GetScriptHandlerMgr().UpdateScriptHandlerCombo();
#endif
}
}
#endif // __DEV