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

2949 lines
93 KiB
C++
Raw Permalink Blame History

/////////////////////////////////////////////////////////////////////////////////
//
// FILE : script.cpp
// PURPOSE : Scripting and stuff.
// AUTHOR : Graeme & Obbe.
// anyway)
// CREATED : 12-11-99
//
/////////////////////////////////////////////////////////////////////////////////
#include "script/script.h"
// Rage headers
#include "audioengine/engine.h"
#include "system/param.h"
#include "vector/quaternion.h"
// Framework headers
#include "fwnet/nettypes.h"
#include "fwscene/stores/mapdatastore.h"
#include "fwscript/scriptInterface.h"
// Security headers
#include "security/ragesecwinapi.h"
// Game headers
#include "animation/AnimScene/AnimSceneManager.h"
#include "audio/scriptaudioentity.h"
#include "camera/CamInterface.h"
#include "control/replay/replay.h"
#include "cutscene/CutSceneManagerNew.h"
#include "debug/BugstarIntegration.h"
#include "event/EventErrors.h"
#include "event/EventGroup.h"
#include "event/EventNetwork.h"
#include "frontend/landing_page/LandingPageArbiter.h"
#include "frontend/landing_page/LandingPageConfig.h"
#include "Network/Events/NetworkEventTypes.h"
#include "Network/Network.h"
#include "Network/NetworkInterface.h"
#include "Network/Live/NetworkTelemetry.h"
#include "Objects/DummyObject.h"
#include "Objects/object.h"
#include "peds/ped.h"
#include "peds/Ped.h"
#include "scene/MapChange.h"
#include "scene/ExtraContentDefs.h"
#include "scene/worldpoints.h"
#include "scene/world/GameWorld.h"
#include "script/commands_apps.h"
#include "script/commands_audio.h"
#include "script/commands_brains.h"
#include "script/commands_camera.h"
#include "script/commands_cutscene.h"
#include "script/commands_datafile.h"
#include "script/commands_debug.h"
#include "script/commands_decorator.h"
#include "script/commands_extrametadata.h"
#include "script/commands_entity.h"
#include "script/commands_event.h"
#include "script/commands_gamestream.h"
#include "script/commands_gang.h"
#include "script/commands_hud.h"
#include "script/commands_interiors.h"
#include "script/commands_itemsets.h"
#include "script/commands_landingpage.h"
#include "script/commands_law.h"
#include "script/commands_localization.h"
#include "script/commands_misc.h"
#include "script/commands_money.h"
#include "script/commands_object.h"
#include "script/commands_pad.h"
#include "script/commands_path.h"
#include "script/commands_ped.h"
#include "script/commands_player.h"
#include "script/commands_physics.h"
#include "script/commands_script.h"
#include "script/commands_security.h"
#include "script/commands_shapetest.h"
#include "script/commands_socialclub.h"
#include "script/commands_stats.h"
#include "script/commands_streaming.h"
#include "script/commands_task.h"
#include "script/commands_vehicle.h"
#include "script/commands_volume.h"
#include "script/commands_weapon.h"
#include "script/commands_fire.h"
#include "script/commands_zone.h"
#include "script/commands_graphics.h"
#include "script/commands_clock.h"
#include "script/commands_network.h"
#include "script/commands_lobby.h"
#include "script/commands_inventory.h"
#include "script/commands_water.h"
#include "script/commands_xml.h"
#include "script/commands_ny.h"
#include "script/commands_dlc.h"
#include "script/commands_recording.h"
#include "script/commands_replay.h"
#include "script/commands_netshopping.h"
#include "script/commands_savemigration.h"
#include "script/script_areas.h"
#include "script/script_brains.h"
#include "script/script_cars_and_peds.h"
#include "script/script_debug.h"
#include "script/script_hud.h"
#include "script/handlers/GameScriptEntity.h"
#include "script/Handlers/GameScriptResources.h"
#include "script/script_helper.h"
#include "script/streamedscripts.h"
#include "script/script_channel.h"
#include "script/ScriptMetadataManager.h"
#include "security/plugins/apicheckplugin.h"
#include "streaming/streaming.h"
#include "system/controlMgr.h"
#include "system/exception.h"
#include "system/FileMgr.h"
#include "system/hangdetect.h"
#include "task/Scenario/info/ScenarioInfoManager.h"
#include "vfx/misc/MovieMeshManager.h"
#include "control/replay/replay.h"
SCRIPT_OPTIMISATIONS ()
NETWORK_OPTIMISATIONS ()
PARAM(startupscript, "[script] load and run this script in place of startup.sc");
PARAM(scriptInstructionLimit, "[script] set the maximum number of instructions per frame to something other than the default of 1,000,000");
PARAM(scriptProtectGlobals, "[script] protects scripts global memory outide of the script update process");
//Define a script assert channel
RAGE_DEFINE_CHANNEL(script)
RAGE_DEFINE_SUBCHANNEL(script,hud)
RAGE_DEFINE_SUBCHANNEL(script,camera)
CScriptsForBrains CTheScripts::ms_ScriptsForBrains;
CScriptBrainDispatcher CTheScripts::ms_ScriptBrainDispatcher;
bool CTheScripts::ms_bPlayerIsOnAMission = false;
bool CTheScripts::ms_bPlayerIsOnARandomEvent = false;
u32 CTheScripts::ms_Frack = 0xEBEFC45E;
parTree* CTheScripts::ms_pXmlTree = NULL;
parTreeNode* CTheScripts::ms_pCurrentXmlTreeNode = NULL;
bool CTheScripts::ms_bUpdatingScriptThreads = false;
bool CTheScripts::ms_bPlayerIsRepeatingAMission = false;
bool CTheScripts::ms_bPlayerIsInAnimalForm = false;
bool CTheScripts::ms_bIsInDirectorMode = false;
bool CTheScripts::ms_directorModeAvailable = true;
s32 CTheScripts::ms_customMPHudColor = HUD_COLOUR_INVALID;
CScriptPatrol CTheScripts::ms_ScriptPatrol;
CMissionReplayStats CTheScripts::ms_MissionReplayStats;
CHiddenObjects CTheScripts::ms_HiddenObjects;
atBinaryMap<CDLCScript*,u32> CDLCScript::ms_scripts;
#if __DEV
static bool bDisplayUsedScriptResources = false;
#endif
int CTheScripts::ms_NumberOfMiniGamesInProgress = 0;
#if !__PPU
bool CTheScripts::ms_bSinglePlayerRestoreHasJustOccurred = false;
#endif
#if __BANK
f32 CTheScripts::sm_fTimeFadedOut = 0.0f;
#endif // __BANK
#if !__FINAL
bool CTheScripts::ms_bScriptLaunched = false;
#endif //!__FINAL
sysTimer CTheScripts::sm_ScriptTimer;
char CTheScripts::ms_ContentIdOfCurrentUGCMission[RLUGC_MAX_CONTENTID_CHARS];
bool CTheScripts::ms_bAllowGameToPauseForStreaming = true;
bool CTheScripts::ms_bScenarioPedsCanBeGrabbedByScript = false;
bool CTheScripts::ms_bCodeRequestedAutosave = false;
bool CTheScripts::ms_bProcessingTheScriptsDuringGameInit = false;
CGameScriptHandlerMgr CTheScripts::ms_ScriptHandlerMgr(&CNetwork::GetBandwidthManager());
bool CTheScripts::ms_bKillingAllScriptThreads = false;
CScriptOpList CTheScripts::ms_scriptOpList;
static int s_InstructionLimit = 0;
GEN9_STANDALONE_ONLY(CTheScripts::InitState CTheScripts::ms_initState = CTheScripts::InitState::Uninitialized);
bool CTheScripts::GetClosestObjectCB(CEntity* pEntity, void* data)
{
Assert(pEntity);
ClosestObjectDataStruct* pClosestObjectData = reinterpret_cast<ClosestObjectDataStruct*>(data);
if (pClosestObjectData->bCheckModelIndex && (pEntity->GetModelIndex() != pClosestObjectData->ModelIndexToCheck) )
{
return true;
}
if (pClosestObjectData->bCheckStealableFlag)
{
scriptAssertf(pEntity->GetIsTypeObject(), "%s:GetClosestObjectCB - Object should be a real object (not a dummy) if you're checking if it's stealable. This is a code problem (probably not a script problem)", CTheScripts::GetCurrentScriptNameAndProgramCounter());
if (pEntity->GetIsTypeObject())
{
CObject *pObj = (CObject *) pEntity;
if (pObj->m_nObjectFlags.bIsStealable == false)
{
return true;
}
}
}
if(!((1 << pEntity->GetOwnedBy()) & pClosestObjectData->nOwnedByMask))
{
return true;
}
Vector3 DiffVector = VEC3V_TO_VECTOR3(pEntity->GetTransform().GetPosition()) - pClosestObjectData->CoordToCalculateDistanceFrom;
float ObjDistanceSquared = DiffVector.Mag2();
if (ObjDistanceSquared < pClosestObjectData->fClosestDistanceSquared)
{
pClosestObjectData->pClosestObject = pEntity;
pClosestObjectData->fClosestDistanceSquared = ObjDistanceSquared;
}
return true;
}
bool CTheScripts::CountMissionEntitiesInAreaCB(CEntity* pEntity, void* data)
{
int* pNumberOfMissionEntities = reinterpret_cast<int*>(data);
if (IsMissionEntity(pEntity))
(*pNumberOfMissionEntities)++;
return(true);
}
bool CTheScripts::IsMissionEntity(CEntity * pEntity)
{
Assert(pEntity);
switch (pEntity->GetType())
{
case ENTITY_TYPE_VEHICLE :
if (((CVehicle *)pEntity)->PopTypeIsMission())
{
return true;
}
break;
case ENTITY_TYPE_PED :
if (((CPed *)pEntity)->PopTypeIsMission())
{
return true;
}
break;
case ENTITY_TYPE_OBJECT :
if (((CObject *)pEntity)->GetOwnedBy() == ENTITY_OWNEDBY_SCRIPT) // || (((CObject *)pEntity)->GetOwnedBy() == MISSION_BRAIN_OBJECT) )
{
return true;
}
break;
default :
break;
}
return false;
}
extern scrCommandHash<scrCmd> s_CommandHash;
typedef void (*regFunction) ();
void CTheScripts::ScrambleAndRegister()
{
atArray<regFunction> sourceArray;
sourceArray.Reserve(100);
sourceArray.Push(netshopping_commands::SetupScriptCommands);
sourceArray.Push(script_commands::SetupScriptCommands);
sourceArray.Push(object_commands::SetupScriptCommands);
sourceArray.Push(xml_commands::SetupScriptCommands);
sourceArray.Push(volume_commands::SetupScriptCommands);
sourceArray.Push(brain_commands::SetupScriptCommands);
sourceArray.Push(replay_commands::SetupScriptCommands);
sourceArray.Push(weapon_commands::SetupScriptCommands);
sourceArray.Push(apps_commands::SetupScriptCommands);
sourceArray.Push(ped_commands::SetupScriptCommands);
sourceArray.Push(fire_commands::SetupScriptCommands);
sourceArray.Push(clock_commands::SetupScriptCommands);
sourceArray.Push(interior_commands::SetupScriptCommands);
sourceArray.Push(pad_commands::SetupScriptCommands);
sourceArray.Push(graphics_commands::SetupScriptCommands);
sourceArray.Push(inventory_commands::SetupScriptCommands);
sourceArray.Push(localization_commands::SetupScriptCommands);
sourceArray.Push(player_commands::SetupScriptCommands);
sourceArray.Push(physics_commands::SetupScriptCommands);
sourceArray.Push(entity_commands::SetupScriptCommands);
sourceArray.Push(task_commands::SetupScriptCommands);
sourceArray.Push(zone_commands::SetupScriptCommands);
sourceArray.Push(misc_commands::SetupScriptCommands);
sourceArray.Push(streaming_commands::SetupScriptCommands);
sourceArray.Push(socialclub_commands::SetupScriptCommands);
sourceArray.Push(law_commands::SetupScriptCommands);
sourceArray.Push(audio_commands::SetupScriptCommands);
sourceArray.Push(gta_commands::SetupScriptCommands);
sourceArray.Push(path_commands::SetupScriptCommands);
sourceArray.Push(itemsets_commands::SetupScriptCommands);
sourceArray.Push(datafile_commands::SetupScriptCommands);
sourceArray.Push(dlc_commands::SetupScriptCommands);
sourceArray.Push(cutscene_commands::SetupScriptCommands);
sourceArray.Push(stats_commands::SetupScriptCommands);
sourceArray.Push(shapetest_commands::SetupScriptCommands);
sourceArray.Push(recording_commands::SetupScriptCommands);
sourceArray.Push(extrametadata_commands::SetupScriptCommands);
sourceArray.Push(camera_commands::SetupScriptCommands);
sourceArray.Push(vehicle_commands::SetupScriptCommands);
sourceArray.Push(lobby_commands::SetupScriptCommands);
sourceArray.Push(event_commands::SetupScriptCommands);
sourceArray.Push(water_commands::SetupScriptCommands);
sourceArray.Push(money_commands::SetupScriptCommands);
sourceArray.Push(network_commands::SetupScriptCommands);
sourceArray.Push(debug_commands::SetupScriptCommands);
sourceArray.Push(gang_commands::SetupScriptCommands);
sourceArray.Push(decorator_commands::SetupScriptCommands);
sourceArray.Push(hud_commands::SetupScriptCommands);
sourceArray.Push(savemigration_commands::SetupScriptCommands);
sourceArray.Push(landingpage_commands::SetupScriptCommands);
sourceArray.Push(security_commands::SetupScriptCommands);
// scramble the function array
u32 size = sourceArray.GetCount();
for(u32 scrambleCount = 0; scrambleCount < 1000; scrambleCount++)
{
u32 randomIndex = fwRandom::GetRandomNumberInRange(0, size);
regFunction temp = sourceArray[randomIndex];
sourceArray.DeleteFast(randomIndex);
sourceArray.Push(temp);
}
// execute all the functions in the scambled array
for(u32 i = 0; i < sourceArray.GetCount(); i++)
{
sourceArray[i]();
}
ApiCheckPlugin_StopNativeHash();
s_CommandHash.RegistrationComplete(true);
}
void CTheScripts::RegisterScriptCommands()
{
#if 1
ScrambleAndRegister();
#else //
//@@: location CTHESCRIPTS_REGISTER_SCRIPTCOMMANDS
netshopping_commands::SetupScriptCommands();
script_commands::SetupScriptCommands();
object_commands::SetupScriptCommands();
xml_commands::SetupScriptCommands();
volume_commands::SetupScriptCommands();
brain_commands::SetupScriptCommands();
replay_commands::SetupScriptCommands();
weapon_commands::SetupScriptCommands();
apps_commands::SetupScriptCommands();
ped_commands::SetupScriptCommands();
fire_commands::SetupScriptCommands();
clock_commands::SetupScriptCommands();
interior_commands::SetupScriptCommands();
pad_commands::SetupScriptCommands();
graphics_commands::SetupScriptCommands();
inventory_commands::SetupScriptCommands();
localization_commands::SetupScriptCommands();
player_commands::SetupScriptCommands();
physics_commands::SetupScriptCommands();
entity_commands::SetupScriptCommands();
task_commands::SetupScriptCommands();
zone_commands::SetupScriptCommands();
misc_commands::SetupScriptCommands();
streaming_commands::SetupScriptCommands();
socialclub_commands::SetupScriptCommands();
law_commands::SetupScriptCommands();
audio_commands::SetupScriptCommands();
gta_commands::SetupScriptCommands();
path_commands::SetupScriptCommands();
itemsets_commands::SetupScriptCommands();
datafile_commands::SetupScriptCommands();
dlc_commands::SetupScriptCommands();
cutscene_commands::SetupScriptCommands();
stats_commands::SetupScriptCommands();
shapetest_commands::SetupScriptCommands();
recording_commands::SetupScriptCommands();
extrametadata_commands::SetupScriptCommands();
camera_commands::SetupScriptCommands();
vehicle_commands::SetupScriptCommands();
lobby_commands::SetupScriptCommands();
event_commands::SetupScriptCommands();
water_commands::SetupScriptCommands();
money_commands::SetupScriptCommands();
network_commands::SetupScriptCommands();
debug_commands::SetupScriptCommands();
gang_commands::SetupScriptCommands();
decorator_commands::SetupScriptCommands();
hud_commands::SetupScriptCommands();
savemigration_commands::SetupScriptCommands();
landingpage_commands::SetupScriptCommands();
security_commands::SetupScriptCommands();
#endif // 1
}
void CTheScripts::GtaFaultHandler(const char *ASSERT_ONLY(CauseOfFault), scrThread*, scrThreadFaultID faultType)
{
if (GetCurrentGtaScriptThread())
{
sErrorScriptID scriptID;
scriptID.m_scriptNameHash = GetCurrentGtaScriptThread()->m_HashOfScriptName;
switch(faultType)
{
case THREAD_ARRAY_OVERFLOW:
GetEventScriptErrorsGroup()->Add(CEventErrorArrayOverflow(scriptID));
break;
case THREAD_STACK_OVERFLOW:
GetEventScriptErrorsGroup()->Add(CEventErrorStackOverflow(scriptID));
break;
case THREAD_INSTRUCTION_COUNT_OVERFLOW:
GetEventScriptErrorsGroup()->Add(CEventErrorInstructionLimit(scriptID));
break;
case THREAD_UNKNOWN_ERROR_TYPE:
default:
GetEventScriptErrorsGroup()->Add(CEventErrorUnknownError(scriptID));
break;
}
#if __ASSERT
// display the program counter of the current script here
char ErrorString[1024];
sprintf(ErrorString, "%s : Program Counter = %d - Check the .scd file\n", GetCurrentScriptName(), GetCurrentGtaScriptThread()->GetThreadPC());
scriptAssertf(0, "%s:%s", ErrorString, CauseOfFault);
#endif
}
else
{
scriptAssertf(0, "%s:%s", "Unknown script name", CauseOfFault);
}
}
// KS - WR 156 - added stacksize argument
scrThreadId CTheScripts::GtaStartNewThreadOverride(const char *progname,const void *argStruct,int argStructSize, int stackSize)
{
if (!progname || (strlen(progname) == 0) )
{
if (GetCurrentGtaScriptThread())
{
scriptAssertf(0, "%s:CTheScripts::GtaStartNewThreadOverride - trying to start a script without passing in a script name", GetCurrentScriptNameAndProgramCounter());
}
else
{
scriptAssertf(0, "CTheScripts::GtaStartNewThreadOverride - trying to start a script without passing in a script name");
}
}
scrProgramId pid = scrProgram::IsCompiledAndResident(progname);
if (!pid)
{
strLocalIndex id = g_StreamedScripts.FindSlot(progname);
scriptAssertf(id != -1, "%s:Script doesn't exist", progname);
if (id == -1)
return THREAD_INVALID;
scriptAssertf(g_StreamedScripts.Get(id), "%s:Script is not in memory", progname);
scriptDisplayf("About to start script thread %s", progname);
pid = g_StreamedScripts.GetProgramId(id.Get());
}
return GtaStartNewThreadWithProgramId(pid, argStruct,argStructSize, stackSize, progname);
}
scrThreadId CTheScripts::GtaStartNewThreadWithNameHashOverride(atHashValue hashOfProgName,const void *argStruct,int argStructSize, int stackSize)
{
if (hashOfProgName.GetHash() == 0)
{
if (GetCurrentGtaScriptThread())
{
scriptAssertf(0, "%s:CTheScripts::GtaStartNewThreadWithNameHashOverride - the hash of the script to be started is 0", GetCurrentScriptNameAndProgramCounter());
}
else
{
scriptAssertf(0, "CTheScripts::GtaStartNewThreadWithNameHashOverride - the hash of the script to be started is 0");
}
}
// I'm not sure what to do with IsCompiledAndResident. It seems to be using a different hash function - scrComputeHash
scrProgramId pid = srcpidNONE; // = scrProgram::IsCompiledAndResident(progname);
// if (!pid)
{
strLocalIndex id = g_StreamedScripts.FindSlotFromHashKey(hashOfProgName.GetHash());
scriptAssertf(id != -1, "Script with hash %u doesn't exist", hashOfProgName.GetHash());
if (id == -1)
return THREAD_INVALID;
scriptAssertf(g_StreamedScripts.Get(id), "Script with hash %u is not in memory", hashOfProgName.GetHash());
scriptDisplayf("About to start script thread with hash %u", hashOfProgName.GetHash());
pid = g_StreamedScripts.GetProgramId(id.Get());
}
char debugStringContainingHash[32];
formatf(debugStringContainingHash, "0x%x", hashOfProgName.GetHash());
return GtaStartNewThreadWithProgramId(pid, argStruct,argStructSize, stackSize, debugStringContainingHash);
}
scrThreadId CTheScripts::GtaStartNewThreadWithProgramId(scrProgramId progId,const void *argStruct,int argStructSize,int stackSize, const char* OUTPUT_ONLY(progname) )
{
scrThreadId threadId = scrThread::CreateThread(progId, argStruct, argStructSize, stackSize);
#if !__NO_OUTPUT
if (threadId == THREAD_INVALID)
{
#if __BANK
GtaThread::DisplayAllRunningScripts(false);
#endif // __BANK
#if __ASSERT
scriptAssertf(0, "%s:Failed to create a new thread (stack %d)", progname, stackSize);
#else
scriptErrorf("%s:Failed to create a new thread (stack %d)", progname, stackSize);
#endif // __ASSERT
}
#endif // !__NO_OUTPUT
GtaThread *pNewOne = static_cast<GtaThread *> (scrThread::GetThread(threadId));
if (pNewOne == NULL)
{
#if __ASSERT
scriptAssertf(0, "CTheScripts::GtaStartNewThreadWithProgramId - Could not get a pointer to the new thread %s", progname);
#else
scriptErrorf("CTheScripts::GtaStartNewThreadWithProgramId - Could not get a pointer to the new thread %s", progname);
#endif // __ASSERT
return THREAD_INVALID;
}
pNewOne->Initialise();
ms_ScriptHandlerMgr.RegisterScript(*pNewOne);
#if __BANK
if (CScriptDebug::ShouldThisScriptBeDebugged(progname))
{
GtaThread::AllocateDebuggerForThread(threadId);
}
CScriptDebug::SetComboBoxOfThreadNamesNeedsUpdated(true);
#endif
#if BACKTRACE_ENABLED
sysException::BacktraceNotifyScriptLaunched((u32)progId, fwTimer::GetSystemTimeInMilliseconds());
#endif // BACKTRACE_ENABLED
scriptDisplayf("Started new script thread %s(%d) with stack size %d", progname, threadId, stackSize);
return threadId;
}
#if !__PPU
void CTheScripts::SetSinglePlayerRestoreHasJustOccurred(bool bRestoreJustOccurred)
{
ms_bSinglePlayerRestoreHasJustOccurred = bRestoreJustOccurred;
}
bool CTheScripts::GetSinglePlayerRestoreHasJustOccurred()
{
return ms_bSinglePlayerRestoreHasJustOccurred;
}
#endif // !__PPU
void CScriptPatrol::Init()
{
bPatrolRouteOpen = false;
ScriptPatrolNodeIndex = 0;
ScriptPatrolLinkIndex = 0;
ScriptPatrolRouteHash = 0;
// fwIplPatrolNode ScriptPatrolNodes[MAX_NUMBER_OF_PATROL_NODES];
// fwIplPatrolLink ScriptPatrolLinks[MAX_NUMBER_OF_PATROL_LINKS];
// int ScriptPatrolNodeIDs[MAX_NUMBER_OF_PATROL_NODES];
}
void CScriptPatrol::OpenPatrolRoute(const char* RouteName)
{
if (SCRIPT_VERIFY((strnicmp ("miss_", RouteName,5) == 0), "CScriptPatrol::OpenPatrolRoute - Script patrol routes name must start with miss_"))
{
if (SCRIPT_VERIFY(bPatrolRouteOpen == false, "CScriptPatrol::OpenPatrolRoute - Patrol route must be closed before being opened"))
{
ScriptPatrolRouteHash = atStringHash(RouteName);
bPatrolRouteOpen = true;
ScriptPatrolNodeIndex = 0;
ScriptPatrolLinkIndex = 0;
}
}
}
void CScriptPatrol::ClosePatrolRoute()
{
bPatrolRouteOpen = false;
}
void CScriptPatrol::AddPatrolNode(int NodeId, const char* NodeType, Vector3 &vNodePos, Vector3 &vNodeHeading, int Duration)
{
if (!vNodeHeading.IsZero())
{
vNodeHeading = vNodeHeading - vNodePos;
}
int iDuration = (int) Duration / 1000;
// Check if the node type references a valid scenario right away, rather than waiting for
// a more cryptic assert from the tasks.
u32 nodeTypeHash = atStringHash(NodeType);
scriptAssertf(!nodeTypeHash || SCENARIOINFOMGR.GetScenarioIndex(nodeTypeHash, false) >= 0,
"%s:ADD_PATROL_NODE - failed for find scenario '%s'.",
CTheScripts::GetCurrentScriptNameAndProgramCounter(), NodeType);
if (SCRIPT_VERIFY(bPatrolRouteOpen == true, "CScriptPatrol::AddPatrolNode- Patrol route must be open before adding nodes"))
{
if (SCRIPT_VERIFY((ScriptPatrolNodeIndex <= MAX_NUMBER_OF_PATROL_NODES - 1) , "CScriptPatrol::AddPatrolNode - Patrol route can have no more than 16 nodes"))
{
ScriptPatrolNodes[ScriptPatrolNodeIndex].x = vNodePos.x;
ScriptPatrolNodes[ScriptPatrolNodeIndex].y = vNodePos.y;
ScriptPatrolNodes[ScriptPatrolNodeIndex].z = vNodePos.z;
ScriptPatrolNodes[ScriptPatrolNodeIndex].headingX = vNodeHeading.x;
ScriptPatrolNodes[ScriptPatrolNodeIndex].headingY = vNodeHeading.y;
ScriptPatrolNodes[ScriptPatrolNodeIndex].headingZ = vNodeHeading.z;
ScriptPatrolNodes[ScriptPatrolNodeIndex].nodeTypeHash = nodeTypeHash;
ScriptPatrolNodes[ScriptPatrolNodeIndex].routeNameHash = ScriptPatrolRouteHash;
ScriptPatrolNodes[ScriptPatrolNodeIndex].duration = iDuration;
ScriptPatrolNodeIDs[ScriptPatrolNodeIndex] = NodeId;
ScriptPatrolNodeIndex++;
}
}
}
void CScriptPatrol::AddPatrolLink(int NodeID1, int NodeID2)
{
if (SCRIPT_VERIFY(bPatrolRouteOpen == true, "CScriptPatrol::AddPatrolLink - Patrol route must be open before adding nodes"))
{
if (SCRIPT_VERIFY(ScriptPatrolLinkIndex <= MAX_NUMBER_OF_PATROL_LINKS -1, "CScriptPatrol::AddPatrolLink - Patrol links must be less than 64"))
{
ScriptPatrolLinks[ScriptPatrolLinkIndex].node1 = NodeID1;
ScriptPatrolLinks[ScriptPatrolLinkIndex].node2 = NodeID2;
ScriptPatrolLinkIndex++;
}
}
}
void CScriptPatrol::CreatePatrolRoute()
{
if (SCRIPT_VERIFY(bPatrolRouteOpen == false, "CScriptPatrol::CreatePatrolRoute - Must close the nodes before adding the route"))
{
CScriptResource_PatrolRoute patrolRoute(ScriptPatrolRouteHash, ScriptPatrolNodes, ScriptPatrolNodeIDs, ScriptPatrolNodeIndex, ScriptPatrolLinks, ScriptPatrolLinkIndex, 0);
CTheScripts::GetCurrentGtaScriptHandler()->RegisterScriptResource(patrolRoute);
}
}
void CTheScripts::Init(unsigned initMode)
{
USE_MEMBUCKET(MEMBUCKET_SCRIPT);
//@@: location CTHESCRIPTS_INIT_INIT_ENTRY_POINT
GtaThread::StaticInit(initMode);
if(initMode == INIT_CORE)
{
GEN9_STANDALONE_ONLY(Assertf(ms_initState == InitState::Uninitialized, "Unexpected state %d, should be %d", ms_initState, InitState::Uninitialized));
GEN9_STANDALONE_ONLY(ms_initState = InitState::Initialized);
RAGE_TRACK(CTheScripts);
#if ENABLE_SCRIPT_GLOBALS_HEAP
const int globalsHeapSize = fwConfigManager::GetInstance().GetSizeOfPool(atHashString("scrGlobals", 0xC780FC5), CONFIGURED_FROM_FILE);
sysMemStartPool();
u8* heapMem = rage_new u8[globalsHeapSize];
sysMemEndPool();
scrProgram::InitGlobalsHeap(heapMem, globalsHeapSize);
#endif // ENABLE_SCRIPT_GLOBALS_HEAP
scrProgram::InitClass();
scrThread::InitClass();
GtaThread::AllocateThreads();
ms_ScriptHandlerMgr.Init();
scriptInterface::Init(ms_ScriptHandlerMgr);
CGameScriptResource::Init(initMode);
//@@: location CTHESCRIPTS_INIT_INIT_SCRIPTSMETADATAMANAGER
CScriptMetadataManager::Init();
scrThread::AppFaultHandler = GtaFaultHandler;
scrThread::StartNewThreadOverride = GtaStartNewThreadOverride;
scrThread::StartNewThreadWithNameHashOverride = GtaStartNewThreadWithNameHashOverride;
#if !__PPU
ms_bSinglePlayerRestoreHasJustOccurred = false;
#endif
ms_bProcessingTheScriptsDuringGameInit = false;
// register commands
//@@: location CTHESCRIPTS_INIT_REGISTER_BUILTIN_COMMANDS
scrThread::RegisterBuiltinCommands();
RegisterScriptCommands();
#if SCRIPT_DEBUGGING
// Process the -nativetrace command line argument to update actions for specified natives
scrThread::ParseNativeTraceCommandLine();
#endif
ms_pXmlTree = NULL;
ms_pCurrentXmlTreeNode = NULL;
ms_bUpdatingScriptThreads = false;
s_InstructionLimit = 0;
#if !__FINAL
if (PARAM_scriptInstructionLimit.Get())
{
PARAM_scriptInstructionLimit.Get(s_InstructionLimit);
}
#endif // !__FINAL
#if RSG_PC && ENABLE_SCRIPT_TAMPER_CHECKER
// Add the listener
CScriptGlobalTamperChecker::Initialize();
#endif
}
#if GEN9_STANDALONE_ENABLED
// We want to run this exactly once during INIT_CORE for first launch. If the game session is
// subsequently shut down, we want to run this again when it is rebuilt during INIT_SESSION.
if(ms_initState == InitState::Initialized)
#else
if (initMode == INIT_SESSION)
#endif // GEN9_STANDALONE_ENABLED
{
GEN9_STANDALONE_ONLY(ms_initState = InitState::Running);
#if __DEV
g_StreamedScripts.ValidateAllScripts();
#endif
scriptAssertf (ms_pXmlTree== NULL, "CTheScripts::Init - XmlNodeTree is not Null");
ms_pXmlTree = NULL;
ms_pCurrentXmlTreeNode = NULL;
ms_bUpdatingScriptThreads = false;
//patrol routes
ms_ScriptPatrol.Init();
ms_ScriptsForBrains.Init();
ms_ScriptBrainDispatcher.Init();
#if __DEV
bDisplayUsedScriptResources = false;
#endif
ms_bPlayerIsOnAMission = false;
ms_bPlayerIsOnARandomEvent = false;
ms_bPlayerIsRepeatingAMission = false;
ms_NumberOfMiniGamesInProgress = 0;
ms_HiddenObjects.Init();
ms_bAllowGameToPauseForStreaming = true;
ms_bScenarioPedsCanBeGrabbedByScript = false;
ms_bCodeRequestedAutosave = false;
ms_bKillingAllScriptThreads = false;
ms_bProcessingTheScriptsDuringGameInit = false;
#if __BANK
sm_fTimeFadedOut = 0.0f;
#endif // __BANK
#if !__FINAL
ms_bScriptLaunched = false;
#endif //!__FINAL
sm_ScriptTimer.Reset();
SetContentIdOfCurrentUGCMission("");
static const int kDefaultItemSetBufferSize = 128; // May want to override this to be much larger through the configuration file, RDR2 needed 2048. Memory per unit should be 4 byets.
const int itemSetBufferSize = fwConfigManager::GetInstance().GetSizeOfPool(ATSTRINGHASH("ItemSetBuffer", 0x04e2adf9b), kDefaultItemSetBufferSize);
CItemSetManager::InitClass(itemSetBufferSize);
LaunchStartupScript();
}
//@@: location CTHESCRIPTS_INIT_INITIALIZE_SCRIPT_SHAPE_TEST_MANAGER
CScriptShapeTestManager::Init(initMode);
//@@: location CTHESCRIPTS_INIT_INITIALIZE_MISSION_REPLAY_STATS_INIT
ms_MissionReplayStats.Init(initMode);
}
void CTheScripts::LaunchStartupScript()
{
// if (!bLoadingAPreviouslySavedGame)
{
strLocalIndex mainScriptId = strLocalIndex(-1);
// Gen9 Landing Page: There is now a pre-startup script that runs before the game session exists.
// The -startupscript commandline param is delayed until the game session is created,
// then it is loaded in script by landing_pre_startup.
#if GEN9_LANDING_PAGE_ENABLED
const char *pScriptName = "landing_pre_startup";
#else // GEN9_LANDING_PAGE_ENABLED
const char *pScriptName = CFileMgr::IsGameInstalled() ? "startup" : "startup_install";
NOTFINAL_ONLY(PARAM_startupscript.Get(pScriptName));
#endif // GEN9_LANDING_PAGE_ENABLED
#if !__FINAL
ms_bScriptLaunched = true;
#endif //!__FINAL
mainScriptId = g_StreamedScripts.FindSlot(pScriptName);
if(mainScriptId != -1)
{
g_StreamedScripts.StreamingRequest(mainScriptId, STRFLAG_DONTDELETE);
CStreaming::LoadAllRequestedObjects();
if (g_StreamedScripts.HasObjectLoaded(mainScriptId))
{
#if EXECUTABLE_SCRIPTS
// Load all of the precompiled controller scripts now. (multiple scripts per PRX/DLL now)
// We need to do this after we've registered all script commands, and also after all globals
// have been registered as well.
// See x:\gta5\script\dev\singleplayer\make_controllers.bat and build_controllers.bat.
scrProgram::RegisterBinary("controllers");
#endif // EXECUTABLE_SCRIPTS
scrThreadId NewScrThreadId = scrThread::CreateThread(g_StreamedScripts.GetProgramId(mainScriptId.Get()), NULL, 0, GtaThread::GetDefaultStackSize());
scrThread *pNewScriptThread = scrThread::GetThread(NewScrThreadId);
if (pNewScriptThread)
{
GtaThread *pNewGtaThread = (GtaThread*) pNewScriptThread;
pNewGtaThread->Initialise();
ms_ScriptHandlerMgr.RegisterScript(*pNewScriptThread);
#if __BANK
if (CScriptDebug::ShouldThisScriptBeDebugged(pNewScriptThread->GetScriptName()))
{
GtaThread::AllocateDebuggerForThread(NewScrThreadId);
}
#endif
}
g_StreamedScripts.ClearRequiredFlag(mainScriptId.Get(), STRFLAG_DONTDELETE);
}
else
{
Quitf(ERR_SCR_LAUNCH_1,"CTheScripts::LaunchStartupScript - the %s script has not loaded despite LoadAllRequestedObjects() being called", pScriptName);
}
}
else
{
Quitf(ERR_SCR_LAUNCH_2,"CTheScripts::LaunchStartupScript - failed to find a script with the name %s in the script.rpf", pScriptName);
}
}
}
//
// name: CTheScripts::Shutdown
// description:
void CTheScripts::Shutdown(unsigned shutdownMode)
{
if(shutdownMode == SHUTDOWN_CORE)
{
GEN9_STANDALONE_ONLY(ms_initState = InitState::Uninitialized);
// shutdown script event queues
GetEventScriptErrorsGroup()->Shutdown();
GetEventScriptAIGroup()->Shutdown();
GetEventScriptNetworkGroup()->Shutdown();
g_ScriptAudioEntity.StopStream();
ms_ScriptHandlerMgr.Shutdown();
g_StreamedScripts.Shutdown();
CScriptMetadataManager::Shutdown();
GtaThread::DeallocateThreads();
scrProgram::ShutdownClass();
scriptInterface::Shutdown();
}
else if(shutdownMode == SHUTDOWN_SESSION)
{
// Inform the script engine that it will need to relaunch scripts if the game session is initialised
GEN9_STANDALONE_ONLY(ms_initState = InitState::Initialized);
ms_bKillingAllScriptThreads = true;
scrThread::KillAllThreads();
ms_bKillingAllScriptThreads = false;
CItemSetManager::ShutdownClass();
g_ScriptAudioEntity.StopStream();
g_StreamedScripts.ShutdownLevel();
ms_HiddenObjects.UndoHiddenObjects();
CScriptHud::Shutdown(shutdownMode);
ms_scriptOpList.Flush();
CGameScriptResource::Shutdown(shutdownMode);
#if ENABLE_SCRIPT_GLOBALS_HEAP
ASSERT_ONLY(scrProgram::AssertGlobalsHeapEmpty();)
#endif // ENABLE_SCRIPT_GLOBALS_HEAP
}
CScriptShapeTestManager::Shutdown(shutdownMode);
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : Update
// PURPOSE : Called by the main update loop.
/////////////////////////////////////////////////////////////////////////////////
void
CTheScripts::Update()
{
}
bool CTheScripts::ShouldBeProcessed()
{
if ( (fwTimer::IsGamePaused()) && (fwTimer::ShouldScriptsBePaused()) )
{
return false;
}
// Graeme - 14.06.10 - I had to comment this out to allow the scripts to run for one frame during initialisation
// if(CGame::IsChangingSessionState())
// { // If the network connection is lost then a message is displayed telling the player this and waiting
// for A to be pressed. It's probably a bad idea to continue processing the scripts while a session restart is pending.
// This return will stop any scripts running until the session has ended and a new one has started.
// return false;
// }
// scripts are only processed when the game is performing a full update/render or HTML is being displayed
// or the player is on the player settings screen in a network game
// or the scripts are processing for one frame during the initialisation phase
// or the Gen9 landing page is displayed before the game session is initialized
if(!CGame::ShouldDoFullUpdateRender()
&& !NetworkInterface::IsGameInProgress()
&& !GetProcessingTheScriptsDuringGameInit()
GEN9_LANDING_PAGE_ONLY(&& (!CLandingPageArbiter::IsLandingPageActive() || CGame::IsSessionInitialized()))
)
{
return false;
}
ReFrack();
// Don't process the scripts with a replay going on.
#if GTA_REPLAY
if (CReplayMgr::IsEditModeActive() && !CReplayMgr::ShouldUpdateScripts())
{
return false;
}
#endif
return true;
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : Process
// PURPOSE : Wrapper for InternalProcess so we can protect script globals from
// Tampering on PC MP
/////////////////////////////////////////////////////////////////////////////////
void CTheScripts::Process(void)
{
#if RSG_PC && ENABLE_SCRIPT_TAMPER_CHECKER
//////////////////////////////////////////////////////////////////////////
// PC only - check for memory-tampering in script globals
static bool s_bReportedPlayer = false;
#if !__FINAL
static bool s_bProtectMemory = true;
if(PARAM_scriptProtectGlobals.Get())
{
s_bProtectMemory = true;
}
#endif
int page = -1;
bool isMultiplayer = NetworkInterface::IsAnySessionActive() && NetworkInterface::IsGameInProgress();
if (
#if !__FINAL
s_bProtectMemory &&
#endif
!CPauseMenu::IsActive() && CScriptGlobalTamperChecker::CurrentGlobalsDifferFromPreviousFrame(page))
{
if ( !s_bReportedPlayer && isMultiplayer )
{
s_bReportedPlayer = true;
RageSecPluginGameReactionObject obj(REACT_GAMESERVER, GENERIC_GAME_ID, 0x20C8C138, 0x611E42B9, 0x23346609);
rageSecGamePluginManager::GetRageSecGameReactionObjectQueue()->Push(obj);
}
}
{ //unchecked scope on PC only
CScriptGlobalTamperChecker::UncheckedScope uncheckedScope;
#endif //RSG_PC
InternalProcess();
#if RSG_PC && ENABLE_SCRIPT_TAMPER_CHECKER
} // end unchecked scope on PC
CScriptGlobalTamperChecker::Update();
#if !__FINAL
if(s_bProtectMemory)
#endif
{
#if __BANK
if(CScriptDebug::GetFakeTampering())
{
CScriptGlobalTamperChecker::FakeTamper(0);
}
#endif //__BANK
}
#endif
}
/////////////////////////////////////////////////////////////////////////////////
// FUNCTION : Process
// PURPOSE : Processes the scripts.
/////////////////////////////////////////////////////////////////////////////////
void CTheScripts::InternalProcess(void)
{
AUTO_HANG_DETECT_CRASH_ZONE;
ms_ScriptHandlerMgr.Update();
#if __BANK
if (camInterface::IsFadedOut())
{
sm_fTimeFadedOut += fwTimer::GetTimeStep();
}
else
{
sm_fTimeFadedOut = 0.0f;
}
if (DisplayScriptProcessMessagesWhileScreenIsFaded())
{
scriptDisplayf("CTheScripts::Process called while screen is faded");
}
#endif // __BANK
CControlMgr::UpdateVirtualKeyboard();
if (!ShouldBeProcessed())
{
return;
}
#if GTA_REPLAY
if(!CReplayMgr::IsReplayDisabled() && !CReplayMgr::IsRecordingEnabled() && !CReplayMgr::ShouldUpdateScripts())
{
// update movie mesh manager
g_movieMeshMgr.Update();
return;
}
#endif //GTA_REPLAY
PF_START_TIMEBAR_DETAIL("Script update");
NetworkInterface::UpdateBeforeScripts();
#if RSG_PC
NtDll::VerifyProtection();
Kernel32::VerifyProtection();
#endif
sm_ScriptTimer.Reset();
CScriptAreas::Process();
CScriptCars::Process();
CScriptPeds::Process();
CScriptDebug::Process();
CScriptHud::Process();
#if __DEV
if (bDisplayUsedScriptResources)
{
ms_ScriptHandlerMgr.DisplayObjectAndResourceInfo();
}
#endif
#if __BANK
ms_ScriptHandlerMgr.DisplayScriptHandlerInfo();
#endif
if (CGameWorld::GetMainPlayerInfo() REPLAY_ONLY(&& CReplayMgr::IsReplayInControlOfWorld() == false))
{
CWorldPoints::ScanForScriptTriggerWorldPoints(CGameWorld::FindLocalPlayerCentreOfWorld());
}
ms_ScriptBrainDispatcher.Process();
#if __BANK
if (CScriptDebug::GetOutputScriptDisplayTextCommands())
{
scripthudDisplayf("CTheScripts::Process - Start of processing script threads");
}
#endif // __BANK
scrThread::SetTimeStep(fwTimer::GetTimeStep());
ms_bUpdatingScriptThreads = true;
scrThread::UpdateAll(s_InstructionLimit);
ms_bUpdatingScriptThreads = false;
#if __BANK
if (CScriptDebug::GetOutputScriptDisplayTextCommands())
{
scripthudDisplayf("CTheScripts::Process - End of processing script threads");
}
#endif // __BANK
//Doing this immediately after threads update to minimise time spent with shapetest requests enqueued
CScriptShapeTestManager::Update();
// To free .sco files faster so that they can be used within the Preview folder
GtaThread::KillAllAbortedScriptThreads();
// flush script event queues
GetEventScriptErrorsGroup()->FlushAllAndMovePending();
GetEventScriptAIGroup()->FlushAllAndMovePending();
GetEventScriptNetworkGroup()->FlushAllAndMovePending();
// update movie mesh manager
g_movieMeshMgr.Update();
CutSceneManager::GetInstance()->RenderBinkMovieAndUpdateRenderTargets();
#if __BANK
if (DisplayScriptProcessMessagesWhileScreenIsFaded())
{
sm_fTimeFadedOut -= 4.0f;
}
#endif // __BANK
NetworkInterface::UpdateAfterScripts();
CAnimSceneManager::Update(kAnimSceneUpdatePostScript);
}
void CTheScripts::RegisterEntity(CPhysical* pEntity, bool bScriptHostObject, bool bNetworked, bool bNewEntity)
{
if (AssertVerify(pEntity) &&
scriptVerifyf(!pEntity->GetExtension<CScriptEntityExtension>(), "CTheScripts::RegisterEntity - The entity with model %s and PopType %s is already registered as a mission entity", pEntity->GetModelName(), CTheScripts::GetPopTypeName(pEntity->PopTypeGet()) ))
{
CScriptEntityExtension* pNewExtension = rage_new CScriptEntityExtension(*pEntity);
Assert(pEntity->GetExtension<CScriptEntityExtension>());
scriptHandler* pScriptHandler = GetCurrentGtaScriptThread()->m_Handler;
if (bScriptHostObject && NetworkInterface::IsGameInProgress())
{
if (!bNetworked)
{
sysStack::PrintStackTrace();
Displayf("CTheScripts::RegisterEntity - trying to register a non-networked script host entity. Forcing networking.");
Assertf(0, "CTheScripts::RegisterEntity - trying to register a non-networked script host entity. Forcing networking.");
bNetworked = true;
}
if (bNetworked && !pEntity->GetNetworkObject())
{
sysStack::PrintStackTrace();
Displayf("CTheScripts::RegisterEntity - trying to register a script host entity with no network object. Forcing client object.");
Assertf(0, "CTheScripts::RegisterEntity - trying to register a script host entity with no network object. Forcing client object.");
bScriptHostObject = false;
bNetworked = false;
}
}
// cache the networked state so that we know whether to register the object with the network code when a network game starts
pNewExtension->SetIsNetworked(bNetworked);
// all non-networked entities must be client ones as no other machine will know about them
if (!bNetworked)
{
bScriptHostObject = false;
}
if (AssertVerify(pScriptHandler))
{
pScriptHandler->RegisterNewScriptObject(static_cast<scriptHandlerObject&>(*pNewExtension), bScriptHostObject, bNetworked);
if (pEntity->GetNetworkObject() && pScriptHandler->GetNetworkComponent())
{
if (bScriptHostObject)
{
static_cast<CGameScriptHandlerNetwork*>(pScriptHandler)->ValidateRegistrationRequest(pEntity, bNewEntity);
// host objects should always be cloned on all machines running the script
pEntity->GetNetworkObject()->SetGlobalFlag(CNetObjGame::GLOBALFLAG_CLONEALWAYS_SCRIPT, true);
if (pEntity->GetNetworkObject())
{
// update the scope state immediately so that the clone creates will go out a.s.a.p
NetworkInterface::GetObjectManager().UpdateAllInScopeStateImmediately(pEntity->GetNetworkObject());
}
}
}
}
pEntity->ProtectStreamedArchetype();
}
}
void CTheScripts::UnregisterEntity(CPhysical* pEntity, bool bCleanupScriptState)
{
// unregister the ped from its associated script handler.
CScriptEntityExtension* pExtension = pEntity->GetExtension<CScriptEntityExtension>();
scriptHandler* pScriptHandler = pExtension ? pExtension->GetScriptHandler() : NULL;
if (pExtension && pScriptHandler)
{
// don't cleanup the script state of the target object if the object is about to be destroyed
pExtension->SetCleanupTargetEntity(bCleanupScriptState);
// set the no longer needed flag here so that the script handler knows that it needs to clean up the script info for the entity
pExtension->SetNoLongerNeeded(true);
pScriptHandler->UnregisterScriptObject(*pExtension);
pEntity->UnprotectStreamedArchetype();
}
// Graeme - the correct thing to do would be to delete the script guid for this entity here so that the entity can no longer be referenced by script
// A few scripts are relying on being able to make a temp copy of an entityId, mark the entity as no longer needed and then call a few extra commands through the copied entityId
// This wouldn't be possible if the line below was uncommented
// fwScriptGuid::DeleteGuid(*pEntity);
}
CEntity** CTheScripts::GetAllScriptEntities(u32& numEntities)
{
const u32 maxNumberOfScriptEntities = 700;
static CEntity* entities[maxNumberOfScriptEntities];
DEV_ONLY(memset(entities, 0, sizeof(entities)));
scriptAssertf(maxNumberOfScriptEntities == (u32) CScriptEntityExtension::GetPool()->GetSize(), "CTheScripts::GetAllScriptEntities - maxNumberOfScriptEntities is %u. Change it to match the size of the CScriptEntityExtension pool which is defined in gameconfig.xml to be %d",
maxNumberOfScriptEntities, CScriptEntityExtension::GetPool()->GetSize());
numEntities = ms_ScriptHandlerMgr.GetAllScriptHandlerEntities(entities, maxNumberOfScriptEntities);
return entities;
}
bool CTheScripts::GetPlayerIsOnAMission(void)
{
if (ms_bPlayerIsOnAMission)
{
return true;
}
return false;
}
bool CTheScripts::IsPlayerPlaying(CPed *pPlayerPed)
{
if (scriptVerifyf(pPlayerPed, "CTheScripts::IsPlayerPlaying - ped pointer is NULL"))
{
CPlayerInfo *pPlayerInfo = pPlayerPed->GetPlayerInfo();
if (scriptVerifyf(pPlayerInfo, "CTheScripts::IsPlayerPlaying - ped doesn't have PlayerInfo"))
{
if (pPlayerInfo->IsRestartingAfterDeath() || pPlayerPed->IsInjured()
|| pPlayerInfo->IsRestartingAfterArrest())
{
return false;
}
else
{
return true;
}
}
}
return false;
}
#if __DEV
void CTheScripts::ClearAllEntityCheckedForDeadFlags(void)
{
CPed::Pool *pedpool = CPed::GetPool();
CPed *pPed;
CVehicle::Pool *vehpool = CVehicle::GetPool();
CVehicle *pVehicle;
s32 i;
i = (s32) pedpool->GetSize();
while (i--)
{
pPed = pedpool->GetSlot(i);
if (pPed) // && !pPed->IsPlayer())
{
pPed->m_nDEflags.bCheckedForDead = FALSE;
}
}
i = (s32) vehpool->GetSize();
while (i--)
{
pVehicle = vehpool->GetSlot(i);
if (pVehicle)
{
pVehicle->m_nDEflags.bCheckedForDead = FALSE;
}
}
}
#endif // __DEV
GtaThread *CTheScripts::GetCurrentGtaScriptThread(void)
{
return static_cast<GtaThread *>(scrThread::GetActiveThread());
}
CGameScriptHandler *CTheScripts::GetCurrentGtaScriptHandler(void)
{
return (scrThread::GetActiveThread() ? static_cast<GtaThread *>(scrThread::GetActiveThread())->m_Handler : NULL);
}
CGameScriptHandlerNetwork *CTheScripts::GetCurrentGtaScriptHandlerNetwork(void)
{
CGameScriptHandler *pHandler = GetCurrentGtaScriptHandler();
if (pHandler && pHandler->RequiresANetworkComponent())
{
return static_cast<CGameScriptHandlerNetwork *>(pHandler);
}
return NULL;
}
void CTheScripts::PrioritizeRequestsFromMissionScript(s32 &streamingFlags)
{
if (!GetCurrentGtaScriptThread() || !GetCurrentGtaScriptThread()->m_bIsHighPrio)
return;
if (GetCurrentGtaScriptThread()->IsThisAMissionScript || GetCurrentGtaScriptThread()->GetIsARandomEventScript() || GetCurrentGtaScriptThread()->GetIsATriggerScript())
{
streamingFlags |= STRFLAG_PRIORITY_LOAD;
}
}
u32 CTheScripts::GetCurrentScriptHash(void)
{
const u32 noScript = 0;
if (ms_bUpdatingScriptThreads)
{
return scrThread::GetActiveThread()->GetScriptNameHash();
}
else
{
return noScript;
}
}
const char *CTheScripts::GetCurrentScriptName(void)
{
const char* noScript = "";
if (ms_bUpdatingScriptThreads)
{
return scrThread::GetActiveThread()->GetScriptName();
}
else
{
return noScript;
}
}
#if !__NO_OUTPUT
const char *CTheScripts::GetCurrentScriptNameAndProgramCounter(void)
{
static char text[256];
if (ms_bUpdatingScriptThreads)
{
sprintf(text, "SCRIPT: Script Name = %s : Program Counter = %d", GetCurrentScriptName(), GetCurrentGtaScriptThread()->GetThreadPC() );
}
else
{
sprintf(text, " ");
}
return text;
}
#endif // !__NO_OUTPUT
void CTheScripts::SetContentIdOfCurrentUGCMission(const char *pContentIdString)
{
if (pContentIdString == NULL)
{
scriptAssertf(0, "CTheScripts::SetContentIdOfCurrentUGCMission called with a NULL string so setting the string to empty");
pContentIdString = "";
}
safecpy(ms_ContentIdOfCurrentUGCMission, pContentIdString, NELEM(ms_ContentIdOfCurrentUGCMission));
}
// ********************* CMissionReplayStats **************************
void CMissionReplayStats::Init(unsigned initMode)
{
// Don't clear the structure in INIT_SESSION
if(initMode == INIT_CORE)
{
ClearReplayStatsStructure();
}
}
// When a Replay Mission is passed
void CMissionReplayStats::BeginReplayStatsStructure(s32 missionId, s32 missionType)
{
if (scriptVerifyf(m_bStatsHaveBeenStored == false, "CMissionReplayStats::BeginReplayStatsStructure - stats have already been stored"))
{
if (scriptVerifyf(m_bConstructingStructureOfStats == false, "CMissionReplayStats::BeginReplayStatsStructure - structure of stats is already being constructed"))
{
m_bConstructingStructureOfStats = true;
m_nMissionId = missionId;
m_nMissionType = missionType;
m_ArrayOfMissionStats.Reset();
}
}
}
void CMissionReplayStats::AddStatValueToReplayStatsStructure(s32 valueOfStat)
{
if (scriptVerifyf(m_bConstructingStructureOfStats, "CMissionReplayStats::AddStatValueToReplayStatsStructure - structure of stats is not being constructed. Check that BEGIN_REPLAY_STATS has been called first"))
{
if (scriptVerifyf(m_bStatsHaveBeenStored == false, "CMissionReplayStats::AddStatValueToReplayStatsStructure - stats have already been stored"))
{
m_ArrayOfMissionStats.PushAndGrow(valueOfStat);
}
}
}
void CMissionReplayStats::EndReplayStatsStructure()
{
if (scriptVerifyf(m_bConstructingStructureOfStats, "CMissionReplayStats::EndReplayStatsStructure - structure of stats is not being constructed. Check that BEGIN_REPLAY_STATS has been called first"))
{
if (scriptVerifyf(m_bStatsHaveBeenStored == false, "CMissionReplayStats::EndReplayStatsStructure - stats have already been stored"))
{
m_bStatsHaveBeenStored = true;
m_bConstructingStructureOfStats = false;
}
}
}
// End of "When a Replay Mission is passed"
// When a Load happens and the script detects that it<69>s a Replay save file
s32 CMissionReplayStats::GetNumberOfMissionStats()
{
if (scriptVerifyf(m_bStatsHaveBeenStored, "CMissionReplayStats::GetNumberOfMissionStats - stats haven't been stored"))
{
return m_ArrayOfMissionStats.GetCount();
}
return 0;
}
s32 CMissionReplayStats::GetStatValueAtIndex(s32 arrayIndex)
{
if (scriptVerifyf(m_bStatsHaveBeenStored, "CMissionReplayStats::GetStatValueAtIndex - stats haven't been stored"))
{
if (scriptVerifyf( (arrayIndex >= 0) && (arrayIndex < m_ArrayOfMissionStats.GetCount()), "CMissionReplayStats::GetStatValueAtIndex - arrayIndex %d is out of range 0 to %d", arrayIndex, m_ArrayOfMissionStats.GetCount()))
{
return m_ArrayOfMissionStats[arrayIndex];
}
}
return 0;
}
void CMissionReplayStats::ClearReplayStatsStructure()
{
m_bStatsHaveBeenStored = false;
m_bConstructingStructureOfStats = false;
m_nMissionId = 0;
m_nMissionType = 0;
m_ArrayOfMissionStats.Reset();
}
// End of "When a Load happens and the script detects that it<69>s a Replay save file"
// ********************* CHiddenObjects **************************
void CHiddenObjects::Init()
{
m_MapOfHiddenObjects.Reset();
}
#if !__NO_OUTPUT
void CHiddenObjects::DisplayInteriorDetails(fwInteriorLocation &interiorLoc)
{
if (interiorLoc.IsValid())
{
scriptDisplayf("CHiddenObjects::DisplayInteriorDetails - interiorLoc is valid");
scriptDisplayf("CHiddenObjects::DisplayInteriorDetails - interior proxy index is %d", interiorLoc.GetInteriorProxyIndex());
if (interiorLoc.IsAttachedToRoom())
{
scriptDisplayf("CHiddenObjects::DisplayInteriorDetails - interiorLoc is attached to room. Room index is %d", interiorLoc.GetRoomIndex());
}
if (interiorLoc.IsAttachedToPortal())
{
scriptDisplayf("CHiddenObjects::DisplayInteriorDetails - interiorLoc is attached to portal. Portal index is %d. PortalToExterior=%s", interiorLoc.GetPortalIndex(), interiorLoc.IsPortalToExterior()?"true":"false");
}
}
else
{
scriptDisplayf("CHiddenObjects::DisplayInteriorDetails - interiorLoc is not valid");
}
}
#endif // !__NO_OUTPUT
void CHiddenObjects::AddToHiddenObjectMap(s32 ScriptGUIDOfEntity, spdSphere &SearchVolume, s32 ModelHash,
CDummyObject *pDummyObj, fwInteriorLocation DummyInteriorLocation, s32 IplIndex)
{
if (m_MapOfHiddenObjects.Access(ScriptGUIDOfEntity) == NULL)
{
hidden_object_struct HiddenObjStruct;
HiddenObjStruct.m_SearchVolume = SearchVolume;
HiddenObjStruct.m_ModelHash = ModelHash;
#if !__NO_OUTPUT
fwModelId modelId;
CBaseModelInfo* pModelInfo = CModelInfo::GetBaseModelInfoFromHashKey(static_cast< u32 >( ModelHash ), &modelId);
if (scriptVerifyf(modelId.IsValid(), "CHiddenObjects::AddToHiddenObjectMap - %d (signed) %u (unsigned) is not a valid model hash", ModelHash, (u32) ModelHash))
{
Vec3V SearchPos = SearchVolume.GetCenter();
scriptDisplayf("CHiddenObjects::AddToHiddenObjectMap - called for %s at %f %f %f", pModelInfo->GetModelName(), SearchPos.GetXf(), SearchPos.GetYf(), SearchPos.GetZf());
}
#endif // !__NO_OUTPUT
HiddenObjStruct.m_pRelatedDummyForObjectGrabbedFromTheWorld = pDummyObj;
HiddenObjStruct.m_InteriorLocationOfRelatedDummyForObjectGrabbedFromTheWorld = DummyInteriorLocation;
HiddenObjStruct.m_IplIndexOfObjectGrabbedFromTheWorld = IplIndex;
#if !__NO_OUTPUT
scriptDisplayf("CHiddenObjects::AddToHiddenObjectMap - interior location details are");
DisplayInteriorDetails(DummyInteriorLocation);
#endif // !__NO_OUTPUT
m_MapOfHiddenObjects.Insert(ScriptGUIDOfEntity, HiddenObjStruct);
}
g_MapChangeMgr.Add((u32) ModelHash, (u32) ModelHash, SearchVolume, true, CMapChange::TYPE_HIDE, true, true);
}
void CHiddenObjects::ReregisterInHiddenObjectMap(s32 oldScriptGUIDOfEntity, s32 newScriptGUIDOfEntity)
{
if (oldScriptGUIDOfEntity != newScriptGUIDOfEntity)
{
// Get entry for old GUID
hidden_object_struct *pHideDataForThisEntity = m_MapOfHiddenObjects.Access(oldScriptGUIDOfEntity);
if (pHideDataForThisEntity)
{
hidden_object_struct HiddenObjStruct = *pHideDataForThisEntity;
// Remove entry for old GUID
m_MapOfHiddenObjects.Delete(oldScriptGUIDOfEntity);
if (scriptVerifyf(m_MapOfHiddenObjects.Access(newScriptGUIDOfEntity) == NULL,
"CHiddenObjects::ReregisterInHiddenObjectMap - MapOfHiddenObjects contains data for old GUID %d, but we didn't expect it to also contain data for the new GUID %d", oldScriptGUIDOfEntity, newScriptGUIDOfEntity))
{
#if !__NO_OUTPUT
fwModelId modelId;
CBaseModelInfo* pModelInfo = CModelInfo::GetBaseModelInfoFromHashKey(static_cast< u32 >( HiddenObjStruct.m_ModelHash ), &modelId);
if (scriptVerifyf(modelId.IsValid(), "CHiddenObjects::ReregisterInHiddenObjectMap - %d (signed) %u (unsigned) is not a valid model hash", HiddenObjStruct.m_ModelHash, (u32) HiddenObjStruct.m_ModelHash))
{
Vec3V SearchPos = HiddenObjStruct.m_SearchVolume.GetCenter();
scriptDisplayf("CHiddenObjects::ReregisterInHiddenObjectMap - called for %s at %f %f %f (Old GUID=%d, New GUID=%d)",
pModelInfo->GetModelName(), SearchPos.GetXf(), SearchPos.GetYf(), SearchPos.GetZf(),
oldScriptGUIDOfEntity, newScriptGUIDOfEntity);
}
scriptDisplayf("CHiddenObjects::ReregisterInHiddenObjectMap - interior location details are");
DisplayInteriorDetails(HiddenObjStruct.m_InteriorLocationOfRelatedDummyForObjectGrabbedFromTheWorld);
#endif // !__NO_OUTPUT
// Add entry for new GUID
m_MapOfHiddenObjects.Insert(newScriptGUIDOfEntity, HiddenObjStruct);
}
}
}
}
bool CHiddenObjects::RemoveFromHiddenObjectMap(s32 ScriptGUIDOfEntity, bool bHandleRelatedDummy)
{
bool bReturnValue = false;
hidden_object_struct *pHideDataForThisEntity = m_MapOfHiddenObjects.Access(ScriptGUIDOfEntity);
if (pHideDataForThisEntity)
{
g_MapChangeMgr.Remove(pHideDataForThisEntity->m_ModelHash, pHideDataForThisEntity->m_ModelHash,
pHideDataForThisEntity->m_SearchVolume, CMapChange::TYPE_HIDE, false);
CObject *pObject = CTheScripts::GetEntityToModifyFromGUID<CObject>(ScriptGUIDOfEntity, 0);
if (bHandleRelatedDummy && pObject)
{
#if !__NO_OUTPUT
Vec3V SearchPos = pHideDataForThisEntity->m_SearchVolume.GetCenter();
scriptDisplayf("CHiddenObjects::RemoveFromHiddenObjectMap has been called for %s at %f %f %f", pObject->GetModelName(), SearchPos.GetXf(), SearchPos.GetYf(), SearchPos.GetZf());
#endif // !__NO_OUTPUT
if (!pObject->GetIsRetainedByInteriorProxy())
{ // If the object is on a retained list then that means the interior is shut down so skip the following code
// and just make the object ENTITY_OWNEDBY_TEMP
CDummyObject *pRelatedDummy = pHideDataForThisEntity->m_pRelatedDummyForObjectGrabbedFromTheWorld;
fwInteriorLocation RelatedDummyLocation = pHideDataForThisEntity->m_InteriorLocationOfRelatedDummyForObjectGrabbedFromTheWorld;
s32 IplIndex = pHideDataForThisEntity->m_IplIndexOfObjectGrabbedFromTheWorld;
if (!pRelatedDummy)
{
scriptDisplayf("CHiddenObjects::RemoveFromHiddenObjectMap - pointer to related dummy for object %s has been lost so start a world search", pObject->GetModelName());
pRelatedDummy = FindClosestMatchingDummy(pHideDataForThisEntity, pObject);
}
if (pRelatedDummy)
{
pObject->SetIplIndex(IplIndex);
pObject->SetRelatedDummy(pRelatedDummy, RelatedDummyLocation);
// Can I just call CDummyObject::Disable() and let CObjectPopulation::ManageObject decide whether the object
// should be swapped to the dummy?
pRelatedDummy->Disable();
scriptDisplayf("CHiddenObjects::RemoveFromHiddenObjectMap - related dummy for object %s has been set up", pObject->GetModelName());
bReturnValue = true;
}
}
else
{
scriptDisplayf("CHiddenObjects::RemoveFromHiddenObjectMap - object %s is on a retained list so skipping the code to re-establish a connection with the related dummy", pObject->GetModelName());
}
}
m_MapOfHiddenObjects.Delete(ScriptGUIDOfEntity);
}
return bReturnValue;
}
void CHiddenObjects::UndoHiddenObjects()
{
scriptAssertf(m_MapOfHiddenObjects.GetNumUsed() == 0, "CHiddenObjects::UndoHiddenObjects - expected all hidden objects to have been dealt with already during Shutdown Session");
// If any objects haven't already been unhidden then do that now
atMap<s32, hidden_object_struct>::Iterator mapIterator = m_MapOfHiddenObjects.CreateIterator();
while (!mapIterator.AtEnd())
{
s32 ScriptGUID = mapIterator.GetKey();
// Can I assume that all CObjects will be deleted in Shutdown Session
// and that UndoHiddenObjects() will only be called during Shutdown Session
RemoveFromHiddenObjectMap(ScriptGUID, false);
mapIterator.Start();
}
}
struct ClosestDummyObjectStruct
{
CEntity *m_pClosestDummyObject;
u32 m_ModelHash;
fwInteriorLocation m_InteriorLocation;
s32 m_IplIndex;
float m_fClosestDistanceSquared;
Vec3V m_vCoordToCalculateDistanceFrom;
};
bool GetClosestDummyObjectCB(CEntity* pEntity, void* data)
{
if (scriptVerifyf(pEntity, "GetClosestDummyObjectCB - entity pointer is NULL"))
{
if (scriptVerifyf(data, "GetClosestDummyObjectCB - data pointer is NULL"))
{
ClosestDummyObjectStruct* pClosestDummyObjectData = reinterpret_cast<ClosestDummyObjectStruct*>(data);
if(pEntity->GetIsTypeDummyObject())
{
if (pEntity->GetBaseModelInfo()->GetModelNameHash() == pClosestDummyObjectData->m_ModelHash)
{
if (pEntity->GetIplIndex() == pClosestDummyObjectData->m_IplIndex)
{
if (pEntity->GetInteriorLocation().IsSameLocation(pClosestDummyObjectData->m_InteriorLocation))
{
//Get the distance between the centre of the locate and the entities position
Vec3V DiffVector = pEntity->GetTransform().GetPosition() - pClosestDummyObjectData->m_vCoordToCalculateDistanceFrom;
const float ObjDistanceSquared = MagSquared(DiffVector).Getf();
if (ObjDistanceSquared < pClosestDummyObjectData->m_fClosestDistanceSquared)
{
pClosestDummyObjectData->m_pClosestDummyObject = pEntity;
pClosestDummyObjectData->m_fClosestDistanceSquared = ObjDistanceSquared;
}
}
}
}
}
}
}
return true;
}
// Debug function to print reason for failure of FindClosestMatchingDummy
void CHiddenObjects::DisplayFailReason(const char* ASSERT_ONLY(pFailReason), const hidden_object_struct* ASSERT_ONLY(pHideData), const CObject* ASSERT_ONLY(pObject))
{
#if __ASSERT
Vector3 vObjectCoors(0.0f, 0.0f, 0.0f);
const char *pObjectName = "Unknown Object";
if (pObject)
{
vObjectCoors = VEC3V_TO_VECTOR3(pObject->GetTransform().GetPosition());
pObjectName = pObject->GetModelName();
}
Vec3V SearchPos = pHideData->m_SearchVolume.GetCenter();
scriptAssertf(0,
"%s Object Name = %s, Object Pos = %f %f %f, Original Search Pos = %f %f %f",
pFailReason, pObjectName, vObjectCoors.GetX(), vObjectCoors.GetY(), vObjectCoors.GetZ(),
SearchPos.GetXf(), SearchPos.GetYf(), SearchPos.GetZf());
#endif // __ASSERT
}
CDummyObject *CHiddenObjects::FindClosestMatchingDummy(hidden_object_struct *pHideData, const CObject* pObject)
{
CDummyObject *pDummyObject = NULL;
fwInteriorLocation InteriorLoc = pHideData->m_InteriorLocationOfRelatedDummyForObjectGrabbedFromTheWorld;
bool bInteriorIsLoaded = false;
bool bExteriorIsLoaded = false;
if (InteriorLoc.IsValid())
{
#if !__NO_OUTPUT
scriptDisplayf("CHiddenObjects::FindClosestMatchingDummy - interior location details are");
DisplayInteriorDetails(InteriorLoc);
#endif // !__NO_OUTPUT
CInteriorProxy *pInteriorProxy = CInteriorProxy::GetFromLocation(InteriorLoc);
if (pInteriorProxy)
{
if (pInteriorProxy->GetCurrentState() >= CInteriorProxy::PS_FULL)
{
bInteriorIsLoaded = true;
scriptDisplayf("CHiddenObjects::FindClosestMatchingDummy - interior %d is instanced", InteriorLoc.GetInteriorProxyIndex());
}
else
{
scriptDisplayf("CHiddenObjects::FindClosestMatchingDummy - current state of interior proxy is %u", pInteriorProxy->GetCurrentState());
scriptDisplayf("---");
scriptDisplayf("interior & object dump...\n");
scriptDisplayf("interior : %s, room : %d\n",pInteriorProxy->GetModelName(),InteriorLoc.GetRoomIndex());
CDebug::DumpEntity(pObject);
// this assert isn't a real problem - object may have been pushed into exterior
//DisplayFailReason("CHiddenObjects::FindClosestMatchingDummy - expected interior to be instanced at this stage. If this was not the case, we should have earlied-out due to the CObject being retained.", pHideData, pObject);
}
}
else
{
DisplayFailReason("CHiddenObjects::FindClosestMatchingDummy - pInteriorProxy is NULL.", pHideData, pObject);
}
}
else
{
// fwMapDataDef* pDef = fwMapDataStore::GetStore().GetSlot(pHideData->m_IplIndexOfObjectGrabbedFromTheWorld);
// if (pDef && pDef->IsLoaded())
if ( fwMapDataStore::GetStore().HasObjectLoaded(strLocalIndex(pHideData->m_IplIndexOfObjectGrabbedFromTheWorld)) )
{
bExteriorIsLoaded = true;
scriptDisplayf("CHiddenObjects::FindClosestMatchingDummy - ipl %d is loaded", pHideData->m_IplIndexOfObjectGrabbedFromTheWorld);
}
}
if (bInteriorIsLoaded || bExteriorIsLoaded)
{
ClosestDummyObjectStruct searchData;
searchData.m_pClosestDummyObject = NULL;
searchData.m_ModelHash = (u32) pHideData->m_ModelHash;
searchData.m_InteriorLocation = InteriorLoc;
searchData.m_IplIndex = pHideData->m_IplIndexOfObjectGrabbedFromTheWorld;
searchData.m_fClosestDistanceSquared = pHideData->m_SearchVolume.GetRadiusSquaredf() * 4.0f; // set this to larger than the scan range (remember it's squared distance)
searchData.m_vCoordToCalculateDistanceFrom = pHideData->m_SearchVolume.GetCenter();
SEARCH_LOCATION_FLAGS SearchFlags = SEARCH_LOCATION_EXTERIORS; // Search exteriors only
if (bInteriorIsLoaded)
{
SearchFlags = SEARCH_LOCATION_INTERIORS; // Search interiors only
}
fwIsSphereIntersecting searchSphere(pHideData->m_SearchVolume);
CGameWorld::ForAllEntitiesIntersecting(&searchSphere, GetClosestDummyObjectCB, (void*) &searchData,
ENTITY_TYPE_MASK_DUMMY_OBJECT, SearchFlags,
SEARCH_LODTYPE_HIGHDETAIL, SEARCH_OPTION_NONE, WORLDREP_SEARCHMODULE_SCRIPT);
if (searchData.m_pClosestDummyObject)
{
if (scriptVerifyf(searchData.m_pClosestDummyObject->GetIsTypeDummyObject(), "CHiddenObjects::FindClosestMatchingDummy - expected entity to be a dummy object"))
{
pDummyObject = static_cast<CDummyObject*>(searchData.m_pClosestDummyObject);
}
}
}
return pDummyObject;
}
// ********************* End of CHiddenObjects **************************
#if __ASSERT
bool CTheScripts::IsValidGlobalVariable(int *AddressToTest)
{
for (int i=0; i<scrProgram::MAX_GLOBAL_BLOCKS; i++)
{
scrValue *pLow_addr = scrProgram::GetGlobals(i);
scrValue *pHigh_addr = scrProgram::GetGlobals(i) + (scrProgram::GetGlobalSize(i)); // 4 *
if ( (pLow_addr <= (scrValue *) AddressToTest) && ((scrValue *) AddressToTest < pHigh_addr) )
{
return true;
}
}
return false;
}
void CTheScripts::DoEntityModifyChecks(const fwEntity* pEntity, unsigned assertFlags)
{
if (scrThread::GetCurrentCmdName())
{
if (assertFlags & GUID_ASSERT_FLAG_ENTITY_EXISTS)
{
scriptAssertf(pEntity, "%s: %s - Entity doesn't exist", GetCurrentScriptNameAndProgramCounter(), scrThread::GetCurrentCmdName());
}
if (pEntity && (pEntity->GetType() == ENTITY_TYPE_VEHICLE || pEntity->GetType() == ENTITY_TYPE_PED || pEntity->GetType() == ENTITY_TYPE_OBJECT))
{
const CPhysical* pPhysical = static_cast<const CPhysical*>(pEntity);
if (assertFlags & GUID_ASSERT_FLAG_DEAD_CHECK && pEntity->GetType() != ENTITY_TYPE_OBJECT)
{
scriptAssertf (pPhysical->m_nDEflags.bCheckedForDead == TRUE, "%s: %s - Check entity is alive this frame", GetCurrentScriptNameAndProgramCounter(), scrThread::GetCurrentCmdName());
}
if (assertFlags & GUID_ASSERT_FLAG_NOT_CLONE)
{
if (pPhysical->IsNetworkClone() || (pPhysical->GetNetworkObject() && pPhysical->GetNetworkObject()->IsPendingOwnerChange()))
{
scriptAssertf (0, "%s: %s - Cannot call this command on an entity (%s) owned by another machine! (Clone: %s, migrating: %s)", GetCurrentScriptNameAndProgramCounter(), scrThread::GetCurrentCmdName(), pPhysical->GetNetworkObject()->GetLogName(), pPhysical->IsNetworkClone() ? "true" : "false", pPhysical->GetNetworkObject()->IsPendingOwnerChange() ? "true" : "false");
}
}
}
}
}
#endif // __ASSERT
#if __BANK
const char *CTheScripts::GetPopTypeName(ePopType PopType)
{
switch (PopType)
{
case POPTYPE_UNKNOWN :
return "POPTYPE_UNKNOWN";
case POPTYPE_RANDOM_PERMANENT :
return "POPTYPE_RANDOM_PERMANENT";
case POPTYPE_RANDOM_PARKED :
return "POPTYPE_RANDOM_PARKED";
case POPTYPE_RANDOM_PATROL :
return "POPTYPE_RANDOM_PATROL";
case POPTYPE_RANDOM_SCENARIO :
return "POPTYPE_RANDOM_SCENARIO";
case POPTYPE_RANDOM_AMBIENT :
return "POPTYPE_RANDOM_AMBIENT";
case POPTYPE_PERMANENT :
return "POPTYPE_PERMANENT";
case POPTYPE_MISSION :
return "POPTYPE_MISSION";
case POPTYPE_REPLAY :
return "POPTYPE_REPLAY";
case POPTYPE_TOOL :
return "POPTYPE_TOOL";
case POPTYPE_CACHE :
return "POPTYPE_CACHE";
case NUM_POPTYPES :
return "NUM_POPTYPES";
}
return "Unknown PopType";
}
#endif // __BANK
fwExtensibleBase* CTheScripts::GetExtensibleBaseToModifyFromGUID(int guid, const char* ASSERT_ONLY(commandName), unsigned ASSERT_ONLY(assertFlags))
{
fwExtensibleBase* pBase = fwScriptGuid::FromGuid<fwExtensibleBase>(guid);
#if __ASSERT
if (commandName)
{
if (assertFlags & GUID_ASSERT_FLAG_ENTITY_EXISTS)
{
scriptAssertf(pBase, "%s: %s - Object doesn't exist", GetCurrentScriptNameAndProgramCounter(), commandName);
}
}
#endif // __ASSERT
return pBase;
}
CItemSet* CTheScripts::GetItemSetToModifyFromGUID(int guid, const char* ASSERT_ONLY(commandName), unsigned ASSERT_ONLY(assertFlags))
{
CItemSet* pItemSet = fwScriptGuid::FromGuid<CItemSet>(guid);
#if __ASSERT
if (commandName)
{
if (assertFlags & GUID_ASSERT_FLAG_ENTITY_EXISTS)
{
scriptAssertf(pItemSet, "%s: %s - Item set doesn't exist", GetCurrentScriptNameAndProgramCounter(), commandName);
}
}
#endif // __ASSERT
return pItemSet;
}
CScriptedCoverPoint* CTheScripts::GetScriptedCoverPointToModifyFromGUID(int guid, const char* ASSERT_ONLY(commandName), unsigned ASSERT_ONLY(assertFlags))
{
CScriptedCoverPoint* pScriptedCoverPoint = fwScriptGuid::FromGuid<CScriptedCoverPoint>(guid);
#if __ASSERT
if (commandName)
{
if (assertFlags & GUID_ASSERT_FLAG_ENTITY_EXISTS)
{
scriptAssertf(pScriptedCoverPoint, "%s: %s - Scripted coverpoint doesn't exist", GetCurrentScriptNameAndProgramCounter(), commandName);
}
}
#endif // __ASSERT
return pScriptedCoverPoint;
}
const fwExtensibleBase* CTheScripts::GetExtensibleBaseToQueryFromGUID(int guid, const char* commandName, unsigned assertFlags)
{
assertFlags &= (~GUID_ASSERT_FLAG_NOT_CLONE);
return GetExtensibleBaseToModifyFromGUID(guid, commandName, assertFlags);
}
const CItemSet* CTheScripts::GetItemSetToQueryFromGUID(int guid, const char* commandName, unsigned assertFlags)
{
assertFlags &= (~GUID_ASSERT_FLAG_NOT_CLONE);
return GetItemSetToModifyFromGUID(guid, commandName, assertFlags);
}
const CScriptedCoverPoint* CTheScripts::GetScriptedCoverPointToQueryFromGUID(int guid, const char* commandName, unsigned assertFlags)
{
return GetScriptedCoverPointToModifyFromGUID(guid, commandName, assertFlags);
}
int CTheScripts::GetGUIDFromEntity(fwEntity& entity)
{
return fwScriptGuid::CreateGuid(entity);
}
int CTheScripts::GetGUIDFromExtensibleBase(fwExtensibleBase& base)
{
return fwScriptGuid::GetGuidFromBase(base);
}
int CTheScripts::GetGUIDFromPreferredCoverPoint(CScriptedCoverPoint& preferredCoverPoint)
{
return fwScriptGuid::CreateGuid(preferredCoverPoint);
}
#if !__FINAL
EXTERN_PARSER_ENUM(ScriptTaskTypes);
const char* CTheScripts::GetScriptTaskName( s32 iCommandIndex )
{
return PARSER_ENUM(ScriptTaskTypes).NameFromValue(iCommandIndex);
}
const char* CTheScripts::GetScriptStatusName( u8 iScriptStatus )
{
switch( iScriptStatus )
{
case CPedScriptedTaskRecordData::EVENT_STAGE: return "WAITING_TO_START_TASK";
case CPedScriptedTaskRecordData::ACTIVE_TASK_STAGE: return "PERFORMING_TASK";
case CPedScriptedTaskRecordData::DORMANT_TASK_STAGE: return "DORMANT_TASK";
case CPedScriptedTaskRecordData::VACANT_STAGE: return "VACANT_STAGE";
case CPedScriptedTaskRecordData::GROUP_TASK_STAGE: return "GROUP_TASK_STAGE";
case CPedScriptedTaskRecordData::UNUSED_ATTRACTOR_SCRIPT_TASK_STAGE: return "ATTRACTOR_SCRIPT_TASK_STAGE";
case CPedScriptedTaskRecordData::SECONDARY_TASK_STAGE: return "SECONDARY_TASK_STAGE";
default: return "FINISHED_TASK";
}
}
#endif //!__FINAL
CPed* CTheScripts::FindLocalPlayerPed( int playerIndex, bool ASSERT_ONLY(bAssert) )
{
CPed* pPlayerPed = NULL;
if (NetworkInterface::IsGameInProgress())
{
CNetGamePlayer* pNetPlayer = NetworkInterface::GetPhysicalPlayerFromIndex(static_cast<PhysicalPlayerIndex>(playerIndex));
if (scriptVerifyf(pNetPlayer && pNetPlayer->IsMyPlayer(), "%s : %s - Can only be called on the local player", CTheScripts::GetCurrentScriptNameAndProgramCounter(), scrThread::GetCurrentCmdName()))
{
pPlayerPed = pNetPlayer->GetPlayerPed();
}
}
else if (scriptVerifyf(playerIndex==0, "%s : %s - Can only be called on the local player", CTheScripts::GetCurrentScriptNameAndProgramCounter(), scrThread::GetCurrentCmdName()))
{
pPlayerPed = CGameWorld::FindLocalPlayer();
}
#if __ASSERT
if (bAssert)
{
scriptAssertf(pPlayerPed, "%s : %s - Player ped doesn't exist", CTheScripts::GetCurrentScriptNameAndProgramCounter(), scrThread::GetCurrentCmdName());
}
#endif
return pPlayerPed;
}
CPed* CTheScripts::FindNetworkPlayerPed( int playerIndex, bool ASSERT_ONLY(bAssert) )
{
CPed* pPlayerPed = NULL;
CNetGamePlayer* pNetPlayer = NULL;
if (NetworkInterface::IsGameInProgress())
{
if (SCRIPT_VERIFY_PLAYER_INDEX(scrThread::GetCurrentCmdName(), playerIndex))
{
pNetPlayer = NetworkInterface::GetPhysicalPlayerFromIndex(static_cast<PhysicalPlayerIndex>(playerIndex));
if (pNetPlayer)
{
pPlayerPed = pNetPlayer->GetPlayerPed();
}
}
}
else if (scriptVerifyf(playerIndex==0, "%s : %s - Can only be called on the local player", CTheScripts::GetCurrentScriptNameAndProgramCounter(), scrThread::GetCurrentCmdName()))
{
pPlayerPed = CGameWorld::FindLocalPlayer();
}
#if __ASSERT
if (bAssert)
{
scriptAssertf(!pPlayerPed || pPlayerPed->GetPlayerInfo(), "%s : %s - Player %d (%s) has no player info", CTheScripts::GetCurrentScriptNameAndProgramCounter(), scrThread::GetCurrentCmdName(), playerIndex, pNetPlayer ? pNetPlayer->GetLogName() : "-none-");
}
#endif
return pPlayerPed;
}
CNetGamePlayer *CTheScripts::FindNetworkPlayer( int playerIndex, bool ASSERT_ONLY(bAssert))
{
CNetGamePlayer* pNetPlayer = NULL;
if (scriptVerifyf(NetworkInterface::IsGameInProgress(), "%s : %s - A network game is not running", CTheScripts::GetCurrentScriptNameAndProgramCounter(), scrThread::GetCurrentCmdName()))
{
if (SCRIPT_VERIFY_PLAYER_INDEX(scrThread::GetCurrentCmdName(), playerIndex))
{
pNetPlayer = NetworkInterface::GetPhysicalPlayerFromIndex(static_cast<PhysicalPlayerIndex>(playerIndex));
#if __ASSERT
if (bAssert)
{
scriptAssertf(pNetPlayer, "%s : %s - Network player doesn't exist", CTheScripts::GetCurrentScriptNameAndProgramCounter(), scrThread::GetCurrentCmdName());
}
#endif
}
}
return pNetPlayer;
}
bool CTheScripts::ExportGamerHandle(const rlGamerHandle* phGamer, int& handleData, int sizeOfData ASSERT_ONLY(, const char* szCommand))
{
#if __NO_OUTPUT
if(!scriptVerifyf(phGamer->IsValid(), "%s : %s - Invalid handle!", CTheScripts::GetCurrentScriptNameAndProgramCounter(), szCommand))
#else
#if __ASSERT
char szToString[RL_MAX_GAMER_HANDLE_CHARS];
#endif // __ASSERT
if(!scriptVerifyf(phGamer->IsValid(), "%s : %s - Invalid handle! As String: %s", CTheScripts::GetCurrentScriptNameAndProgramCounter(), szCommand, phGamer->ToString(szToString)))
#endif
return false;
// script returns size in script words
if(!scriptVerifyf(sizeOfData == SCRIPT_GAMER_HANDLE_SIZE, "%s : %s - Invalid data size!", CTheScripts::GetCurrentScriptNameAndProgramCounter(), szCommand))
return false;
sizeOfData *= sizeof(scrValue);
// buffer to write our handle into
u8* pHandleBuffer = reinterpret_cast<u8*>(&handleData);
// retrieve gamer handle
unsigned nExported = 0;
#if __NO_OUTPUT
if(!scriptVerifyf(phGamer->Export(pHandleBuffer, sizeOfData, &nExported), "%s : %s - Export failed!", CTheScripts::GetCurrentScriptNameAndProgramCounter(), szCommand))
#else
if(!scriptVerifyf(phGamer->Export(pHandleBuffer, sizeOfData, &nExported), "%s : %s - Export failed! As String: %s", CTheScripts::GetCurrentScriptNameAndProgramCounter(), szCommand, phGamer->ToString(szToString)))
#endif
return false;
if(!scriptVerifyf(nExported < GAMER_HANDLE_SIZE, "%s : %s - Increase GAMER_HANDLE_SIZE!", CTheScripts::GetCurrentScriptNameAndProgramCounter(), szCommand))
return false;
return true;
}
bool CTheScripts::ImportGamerHandle(rlGamerHandle* phGamer, int& handleData, int sizeOfData ASSERT_ONLY(, const char* szCommand))
{
// script returns size in script words
if(!scriptVerifyf(sizeOfData == SCRIPT_GAMER_HANDLE_SIZE, "%s : %s - Invalid data size!", CTheScripts::GetCurrentScriptNameAndProgramCounter(), szCommand))
return false;
sizeOfData *= sizeof(scrValue);
// buffer to write our handle into
u8* pHandleBuffer = reinterpret_cast<u8*>(&handleData);
// retrieve gamer handle
unsigned nImported = 0;
if(!scriptVerifyf(phGamer->Import(pHandleBuffer, sizeOfData, &nImported), "%s : %s - Import failed!", CTheScripts::GetCurrentScriptNameAndProgramCounter(), szCommand))
{
#if !__NO_OUTPUT
if (sizeOfData == SCRIPT_GAMER_HANDLE_SIZE)
{
scriptErrorf("CTheScripts::ImportGamerHandle() - FAILED - left to right - Service 1st 8bits from left:");
int* handleDataOut = &handleData;
for (int i=0; i<SCRIPT_GAMER_HANDLE_SIZE; i++)
scriptErrorf(".............. handleData[%d] = '%d'", i, handleDataOut[i]);
}
#endif //!__NO_OUTPUT
return false;
}
if(!scriptVerifyf(nImported < GAMER_HANDLE_SIZE, "%s : %s - Increase GAMER_HANDLE_SIZE!", CTheScripts::GetCurrentScriptNameAndProgramCounter(), szCommand))
return false;
return scriptVerifyf(phGamer->IsValid(), "%s : %s - Invalid handle!", CTheScripts::GetCurrentScriptNameAndProgramCounter(), szCommand);
}
bool CTheScripts::ImportGamerHandle(rlGamerHandle* phGamer, u8* pData ASSERT_ONLY(, const char* szCommand))
{
// retrieve gamer handle
unsigned nImported = 0;
if(!scriptVerifyf(phGamer->Import(pData, GAMER_HANDLE_SIZE, &nImported), "%s : %s - Import failed! Invalid gamerhandle: %s", CTheScripts::GetCurrentScriptNameAndProgramCounter(), szCommand, pData))
return false;
if(!scriptVerifyf(nImported < GAMER_HANDLE_SIZE, "%s : %s - Increase GAMER_HANDLE_SIZE!", CTheScripts::GetCurrentScriptNameAndProgramCounter(), szCommand))
return false;
return scriptVerifyf(phGamer->IsValid(), "%s : %s - Invalid handle!", CTheScripts::GetCurrentScriptNameAndProgramCounter(), szCommand);
}
bool CTheScripts::HasEntityBeenRegisteredWithCurrentThread(const CPhysical* pPhysical)
{
const CScriptEntityExtension* pExtension = pPhysical->GetExtension<CScriptEntityExtension>();
return (pExtension && pExtension->GetScriptInfo() && pExtension->GetScriptInfo()->GetScriptId() == CGameScriptId(*GetCurrentGtaScriptThread()));
}
int CTheScripts::GetIdForEntityRegisteredWithCurrentThread(const CPhysical* pPhysical)
{
if (AssertVerify(HasEntityBeenRegisteredWithCurrentThread(pPhysical)))
{
const CScriptEntityExtension* pExtension = pPhysical->GetExtension<CScriptEntityExtension>();
return static_cast<int>(pExtension->GetScriptInfo()->GetObjectId());
}
return NULL_IN_SCRIPTING_LANGUAGE;
}
void CScriptEulers::MatrixFromEulers(Matrix34& matrix, const Vector3& eulers, EulerAngleOrder rotationOrder)
{
switch(rotationOrder)
{
case EULER_XYZ:
matrix.FromEulersXYZ(eulers);
break;
case EULER_XZY:
matrix.FromEulersXZY(eulers);
break;
case EULER_YXZ:
matrix.FromEulersYXZ(eulers);
break;
case EULER_YZX:
matrix.FromEulersYZX(eulers);
break;
case EULER_ZXY:
matrix.FromEulersZXY(eulers);
break;
case EULER_ZYX:
matrix.FromEulersZYX(eulers);
break;
default:
scriptAssertf(false, "Invalid Euler rotation order");
matrix.Identity();
break;
}
}
Vector3 CScriptEulers::MatrixToEulers(const Matrix34& matrix, EulerAngleOrder rotationOrder)
{
Vector3 eulers;
switch(rotationOrder)
{
case EULER_XYZ:
matrix.ToEulersXYZ(eulers);
break;
case EULER_XZY:
matrix.ToEulersXZY(eulers);
break;
case EULER_YXZ:
matrix.ToEulersYXZ(eulers);
break;
case EULER_YZX:
matrix.ToEulersYZX(eulers);
break;
case EULER_ZXY:
matrix.ToEulersZXY(eulers);
break;
case EULER_ZYX:
matrix.ToEulersZYX(eulers);
break;
default:
scriptAssertf(false, "Invalid Euler rotation order");
eulers.Zero();
break;
}
return eulers;
}
void CScriptEulers::QuaternionFromEulers(Quaternion& quat, const Vector3& eulers, EulerAngleOrder rotationOrder)
{
switch(rotationOrder)
{
case EULER_XYZ:
quat.FromEulers(eulers, eEulerOrderXYZ);
break;
case EULER_XZY:
quat.FromEulers(eulers, eEulerOrderXZY);
break;
case EULER_YXZ:
quat.FromEulers(eulers, eEulerOrderYXZ);
break;
case EULER_YZX:
quat.FromEulers(eulers, eEulerOrderYZX);
break;
case EULER_ZXY:
quat.FromEulers(eulers, eEulerOrderZXY);
break;
case EULER_ZYX:
quat.FromEulers(eulers, eEulerOrderZYX);
break;
default:
scriptAssertf(false, "CScriptEulers::QuaternionFromEulers - Invalid Euler rotation order");
quat.Identity();
break;
}
}
Vector3 CScriptEulers::QuaternionToEulers(Quaternion& quat, EulerAngleOrder rotationOrder)
{
Vector3 eulers;
switch(rotationOrder)
{
case EULER_XYZ:
quat.ToEulers(eulers, eEulerOrderXYZ);
break;
case EULER_XZY:
quat.ToEulers(eulers, eEulerOrderXZY);
break;
case EULER_YXZ:
quat.ToEulers(eulers, eEulerOrderYXZ);
break;
case EULER_YZX:
quat.ToEulers(eulers, eEulerOrderYZX);
break;
case EULER_ZXY:
quat.ToEulers(eulers, eEulerOrderZXY);
break;
case EULER_ZYX:
quat.ToEulers(eulers, eEulerOrderZYX);
break;
default:
scriptAssertf(false, "CScriptEulers::QuaternionToEulers - Invalid Euler rotation order");
eulers.Zero();
break;
}
return eulers;
}
// *************************************************************************************************
// CPersistentScriptGlobals CTheScripts::PersistentScriptGlobals;
/*
void CPersistentScriptGlobals::Init()
{
m_nSizeOfPersistentGlobalsBuffer = 0;
m_pBackupOfPersistentGlobalsBuffer = NULL;
m_nOffsetToStartOfPersistentGlobalsBuffer = -1;
}
void CPersistentScriptGlobals::Clear()
{
m_nSizeOfPersistentGlobalsBuffer = 0;
if (m_pBackupOfPersistentGlobalsBuffer)
{
delete[] m_pBackupOfPersistentGlobalsBuffer;
}
m_pBackupOfPersistentGlobalsBuffer = NULL;
m_nOffsetToStartOfPersistentGlobalsBuffer = -1;
}
void CPersistentScriptGlobals::RegisterPersistentGlobalVariables(int *pStartOfPersistentGlobals, int nSizeOfBuffer)
{
u8 *pStartOfGlobalsMemory = reinterpret_cast<u8*>(scrProgram::GetGlobals());
u8 *pStartOfPersistents = reinterpret_cast<u8*>(pStartOfPersistentGlobals);
int nOffsetToGlobalsInBytes = pStartOfPersistents - pStartOfGlobalsMemory;
scriptAssertf( (nOffsetToGlobalsInBytes >= 0) && (nOffsetToGlobalsInBytes < static_cast<int>(sizeof(scrValue) * scrProgram::GetGlobalSize())), "CPersistentScriptGlobals::RegisterPersistentGlobalVariables - offset to persistent globals is out of range");
scriptAssertf( (nOffsetToGlobalsInBytes + nSizeOfBuffer) <= static_cast<int>(sizeof(scrValue) * scrProgram::GetGlobalSize()), "CPersistentScriptGlobals::RegisterPersistentGlobalVariables - persistent globals structure extends past the end of the global script memory");
scriptAssertf((scrProgram::GetGlobals() + (nOffsetToGlobalsInBytes/sizeof(scrValue)) ) == reinterpret_cast<scrValue*>(pStartOfPersistentGlobals), "CPersistentScriptGlobals::RegisterPersistentGlobalVariables - I've made a mistake in my calculations - Graeme");
scriptAssertf( (m_nSizeOfPersistentGlobalsBuffer == 0) || (m_nSizeOfPersistentGlobalsBuffer == nSizeOfBuffer), "CPersistentScriptGlobals::RegisterPersistentGlobalVariables - attempting to change the size of the persistent globals structure");
scriptAssertf( (m_nOffsetToStartOfPersistentGlobalsBuffer == -1) || (m_nOffsetToStartOfPersistentGlobalsBuffer == nOffsetToGlobalsInBytes), "CPersistentScriptGlobals::RegisterPersistentGlobalVariables - attempting to change the offset of the persistent globals structure");
scriptAssertf(!m_pBackupOfPersistentGlobalsBuffer, "CPersistentScriptGlobals::RegisterPersistentGlobalVariables - didn't expect the backup buffer to be allocated at this point");
Clear();
m_nSizeOfPersistentGlobalsBuffer = nSizeOfBuffer;
m_nOffsetToStartOfPersistentGlobalsBuffer = nOffsetToGlobalsInBytes;
}
bool CPersistentScriptGlobals::StorePersistentGlobalsInBackupBuffer()
{
if (m_nSizeOfPersistentGlobalsBuffer == 0)
{
scriptAssertf(m_nOffsetToStartOfPersistentGlobalsBuffer == -1, "CPersistentScriptGlobals::StorePersistentGlobalsInBackupBuffer - size of buffer is 0 but offset is not -1");
return true; // returning false would probably suggest that the function had failed rather than that the buffer didn't need to be allocated
}
if (m_pBackupOfPersistentGlobalsBuffer)
{
scriptAssertf(0, "CPersistentScriptGlobals::StorePersistentGlobalsInBackupBuffer - didn't expect backup buffer to already be allocated at this stage");
delete[] m_pBackupOfPersistentGlobalsBuffer;
m_pBackupOfPersistentGlobalsBuffer = NULL;
}
scriptAssertf( (m_nOffsetToStartOfPersistentGlobalsBuffer >= 0) && (m_nOffsetToStartOfPersistentGlobalsBuffer < static_cast<int>(sizeof(scrValue) * scrProgram::GetGlobalSize())), "CPersistentScriptGlobals::StorePersistentGlobalsInBackupBuffer - offset to persistent globals is out of range");
scriptAssertf( (m_nOffsetToStartOfPersistentGlobalsBuffer + m_nSizeOfPersistentGlobalsBuffer) <= static_cast<int>(sizeof(scrValue) * scrProgram::GetGlobalSize()), "CPersistentScriptGlobals::StorePersistentGlobalsInBackupBuffer - persistent globals structure extends past the end of the global script memory");
// Allocate buffer of the correct size
m_pBackupOfPersistentGlobalsBuffer = rage_new u8[m_nSizeOfPersistentGlobalsBuffer];
if (m_pBackupOfPersistentGlobalsBuffer)
{ // Copy the global data into this buffer
const u8 *pStartOfPersistentStructureInGlobalsMemory = reinterpret_cast<const u8*>(scrProgram::GetGlobals()) + m_nOffsetToStartOfPersistentGlobalsBuffer;
sysMemCpy(m_pBackupOfPersistentGlobalsBuffer, pStartOfPersistentStructureInGlobalsMemory, m_nSizeOfPersistentGlobalsBuffer);
}
else
{
scriptAssertf(0, "CPersistentScriptGlobals::StorePersistentGlobalsInBackupBuffer - failed to allocate memory for the backup buffer");
return false;
}
return true;
}
bool CPersistentScriptGlobals::RestorePersistentGlobalsFromBackupBuffer()
{
bool bSucceeded = true;
if (scrProgram::GetGlobalSize() > 0)
{
if (m_pBackupOfPersistentGlobalsBuffer)
{
scriptAssertf(m_nSizeOfPersistentGlobalsBuffer > 0, "CPersistentScriptGlobals::RestorePersistentGlobalsFromBackupBuffer - size of buffer to restore must be greater than 0");
scriptAssertf( (m_nOffsetToStartOfPersistentGlobalsBuffer >= 0) && (m_nOffsetToStartOfPersistentGlobalsBuffer < static_cast<int>(sizeof(scrValue) * scrProgram::GetGlobalSize())), "CPersistentScriptGlobals::RestorePersistentGlobalsFromBackupBuffer - offset to persistent globals is out of range");
scriptAssertf( (m_nOffsetToStartOfPersistentGlobalsBuffer + m_nSizeOfPersistentGlobalsBuffer) <= static_cast<int>(sizeof(scrValue) * scrProgram::GetGlobalSize()), "CPersistentScriptGlobals::RestorePersistentGlobalsFromBackupBuffer - persistent globals structure extends past the end of the global script memory");
// Copy the contents of the backup buffer back into the global data
u8 *pStartOfPersistentStructureInGlobalsMemory = reinterpret_cast<u8*>(scrProgram::GetGlobals()) + m_nOffsetToStartOfPersistentGlobalsBuffer;
sysMemCpy(pStartOfPersistentStructureInGlobalsMemory, m_pBackupOfPersistentGlobalsBuffer, m_nSizeOfPersistentGlobalsBuffer);
}
else
{
if ( (m_nSizeOfPersistentGlobalsBuffer != 0) || (m_nOffsetToStartOfPersistentGlobalsBuffer != -1) )
{
scriptAssertf(0, "CPersistentScriptGlobals::RestorePersistentGlobalsFromBackupBuffer - no backup buffer to restore from");
bSucceeded = false;
}
}
}
else
{
scriptAssertf(0, "CPersistentScriptGlobals::RestorePersistentGlobalsFromBackupBuffer - no global variables have been allocated at this stage");
bSucceeded = false;
}
// free the backup buffer
if (m_pBackupOfPersistentGlobalsBuffer)
{
delete[] m_pBackupOfPersistentGlobalsBuffer;
}
m_pBackupOfPersistentGlobalsBuffer = NULL;
return bSucceeded;
}
*/
// *************************************************************************************************
class CDLCScriptDataMounter : public CDataFileMountInterface
{
virtual bool LoadDataFile(const CDataFileMgr::DataFile& file)
{
switch(file.m_fileType)
{
case CDataFileMgr::DLC_SCRIPT_METAFILE:
{
CDLCScript::AddScriptData(file.m_filename);
return true;
}
break;
default:
return false;
}
}
virtual void UnloadDataFile(const CDataFileMgr::DataFile & file)
{
CDLCScript::RemoveScriptData(file.m_filename);
}
}g_dlcScriptDataMounter;
void CDLCScript::AddScriptData(const char* fileName)
{
CDLCScript* scriptData = rage_new CDLCScript;
if(PARSER.LoadObject(fileName,NULL,*scriptData))
{
if(!CDLCScript::ms_scripts.Access(scriptData->GetScriptLiteralName().GetHash()))
{
scriptData->InitScript();
CDLCScript::ms_scripts.SafeInsert(scriptData->GetScriptLiteralName().GetHash(),scriptData);
CDLCScript::ms_scripts.FinishInsertion();
}
return;
}
delete scriptData;
}
void CDLCScript::RemoveScriptData(const char* fileName)
{
CDLCScript* scriptData = rage_new CDLCScript;
if(PARSER.LoadObject(fileName, NULL, *scriptData))
{
RemoveScript(scriptData);
}
delete scriptData;
}
void CDLCScript::Init(unsigned initMode)
{
switch(initMode)
{
case INIT_CORE:
CDataFileMount::RegisterMountInterface(CDataFileMgr::DLC_SCRIPT_METAFILE, &g_dlcScriptDataMounter);
break;
case INIT_SESSION:
for(dlcScriptIterator it=ms_scripts.Begin(); it!= ms_scripts.End();++it)
{
(*it)->InitScript();
}
break;
}
}
void CDLCScript::Update()
{
for(dlcScriptIterator it=ms_scripts.Begin(); it!= ms_scripts.End();++it)
{
(*it)->UpdateScript();
}
}
void CDLCScript::Shutdown(unsigned shutdownMode)
{
switch(shutdownMode)
{
case SHUTDOWN_SESSION:
{
for(dlcScriptIterator it=ms_scripts.Begin(); it!= ms_scripts.End();++it)
{
(*it)->ShutdownScript();
}
}
break;
case SHUTDOWN_WITH_MAP_LOADED:
{
for(dlcScriptIterator it=ms_scripts.Begin(); it!= ms_scripts.End();++it)
{
RemoveScript((*it));
}
ms_scripts.Reset();
}
break;
//TODO map reset on tom's changes
}
}
void CDLCScript::InitScript()
{
const strLocalIndex scrIndex = g_StreamedScripts.FindSlotFromHashKey(m_startupScript.GetHash());
if (Verifyf(scrIndex.IsValid(), "DLC Script %s can't be found", m_startupScript.GetCStr()))
{
m_request.Request(scrIndex, g_StreamedScripts.GetStreamingModuleId(), STRFLAG_PRIORITY_LOAD|STRFLAG_MISSION_REQUIRED);
m_state = CDLCScript::DLC_SCR_REQUESTED;
}
}
void CDLCScript::UpdateScript()
{
switch (m_state)
{
case CDLCScript::DLC_SCR_REQUESTED:
{
if ( m_request.HasLoaded() )
{
const int objIdx = m_request.GetRequestId().Get();
const char* progName = "";
m_request.ClearRequiredFlags(STRFLAG_MISSION_REQUIRED);
#if !__NO_OUTPUT
if( m_startupScript.GetCStr() != NULL )
progName = m_startupScript.GetCStr();
#endif
m_thread = CTheScripts::GtaStartNewThreadWithProgramId(g_StreamedScripts.GetProgramId(objIdx), NULL, 0, m_scriptCallstackSize, progName);
m_request.Release();
if (Verifyf(m_thread!= THREAD_INVALID, "Error launching program %s", progName))
{
m_state = CDLCScript::DLC_SCR_LOADED;
}
}
}
break;
default:
break;
}
}
void CDLCScript::ShutdownScript()
{
m_request.Release();
if (m_thread != THREAD_INVALID)
{
scrThread::KillThread(m_thread);
m_thread = THREAD_INVALID;
strLocalIndex slot = g_StreamedScripts.FindSlotFromHashKey(GetScriptLiteralName().GetHash());
strIndex idx =g_StreamedScripts.GetStreamingIndex(slot);
strStreamingEngine::GetInfo().RemoveObject(idx);
}
m_state = CDLCScript::DLC_SCR_NONE;
}
void CDLCScript::RemoveScript(CDLCScript* dlcScript)
{
if(CDLCScript** curScriptPtr = CDLCScript::ms_scripts.SafeGet(dlcScript->GetScriptLiteralName().GetHash()))
{
CDLCScript* curScript = (*curScriptPtr);
if(curScript)
{
curScript->ShutdownScript();
curScript->GetRequest().StreamingRemove();
ms_scripts.Remove(ms_scripts.GetIndexFromDataPtr(curScriptPtr));
}
}
}
bool CDLCScript::ContainsScript(atLiteralHashValue nameHash)
{
if(CDLCScript::ms_scripts.Access(nameHash.GetHash()))
return true;
return false;
}
#if RSG_PC && ENABLE_SCRIPT_TAMPER_CHECKER
atRangeArray<bool, scrProgram::MAX_GLOBAL_BLOCKS> CScriptGlobalTamperChecker::ms_abValidPage;
bool CScriptGlobalTamperChecker::ms_bTamperingDetected = false;
s32 CScriptGlobalTamperChecker::ms_cUncheckedSection = 0;
CScriptGlobalTamperCheckerTunablesListener CScriptGlobalTamperChecker::ms_pScriptGlobalTamperTunablesListener;
void CScriptGlobalTamperChecker::Initialize()
{
// Add the listener
Tunables::GetInstance().AddListener(&ms_pScriptGlobalTamperTunablesListener);
}
void CScriptGlobalTamperChecker::Reset()
{
for (s32 i=0; i<scrProgram::MAX_GLOBAL_BLOCKS; i++)
{
ms_abValidPage[i] = false;
}
ms_bTamperingDetected = false;
}
bool CScriptGlobalTamperChecker::CurrentGlobalsDifferFromPreviousFrame(int &page)
{
for (s32 i=0; i<scrProgram::MAX_GLOBAL_BLOCKS; i++)
{
if (ms_abValidPage[i] && (ms_cUncheckedSection == 0))
{
if (HaveGlobalsBeenWrittenToSinceReset(i))
{
page = i;
return true;
}
}
}
return ms_bTamperingDetected;
}
void CScriptGlobalTamperChecker::Update()
{
Assertf(ms_cUncheckedSection == 0, "Resetting Global Write Tracking in unchecked Section! Called Begin without End?");
for (s32 i=0; i<scrProgram::MAX_GLOBAL_BLOCKS; i++)
{
if (scrProgram::GetGlobals(i) && ResetGlobalWriteTracking(i))
{
ms_abValidPage[i]= true;
}
else
{
ms_abValidPage[i] = false;
}
}
}
//sets the memory for the globals as write only, letting us catch any
//exceptional cases left over in code
void CScriptGlobalTamperChecker::LockGlobalsMemory()
{
#if !RSG_FINAL
for (s32 i=0; i<scrProgram::MAX_GLOBAL_BLOCKS; i++)
{
if (scrProgram::GetGlobals(i))
{
LockGlobalsMemory(i);
}
}
#endif
}
//unlock the globals memory for the script update
void CScriptGlobalTamperChecker::UnlockGlobalsMemory()
{
#if !RSG_FINAL
for (s32 i=0; i<scrProgram::MAX_GLOBAL_BLOCKS; i++)
{
if (scrProgram::GetGlobals(i))
{
UnlockGlobalsMemory(i);
}
}
#endif
}
// Changes between a call to this and a call to EndUncheckedSection
// will not register as tampering
void CScriptGlobalTamperChecker::BeginUncheckedSection()
{
// Do tamper check now as any results will be invalidated
// once we end the unchecked section
int page = -1;
ms_bTamperingDetected = NetworkInterface::IsGameInProgress() && (ms_cUncheckedSection == 0) && CurrentGlobalsDifferFromPreviousFrame(page);
if(ms_cUncheckedSection == 0 && PARAM_scriptProtectGlobals.Get())
{
CScriptGlobalTamperChecker::UnlockGlobalsMemory();
}
ms_cUncheckedSection++;
}
void CScriptGlobalTamperChecker::EndUncheckedSection()
{
ms_cUncheckedSection--;
if(ms_cUncheckedSection == 0)
{
for (s32 i=0; i<scrProgram::MAX_GLOBAL_BLOCKS; i++)
{
if(ms_abValidPage[i])
{
ResetGlobalWriteTracking(i);
}
}
if(PARAM_scriptProtectGlobals.Get())
{
CScriptGlobalTamperChecker::LockGlobalsMemory();
}
}
}
void CScriptGlobalTamperChecker::FakeTamper(const s32 offsetIndex)
{
#if !RSG_FINAL
for (s32 i=0; i<scrProgram::MAX_GLOBAL_BLOCKS; i++)
{
if (scrProgram::GetGlobals(i) && ResetGlobalWriteTracking(i))
{
FakeTamper(i,offsetIndex);
}
}
#else
(void)offsetIndex;
#endif
}
bool CScriptGlobalTamperChecker::ResetGlobalWriteTracking(s32 pageIndex)
{
scrValue* globals = scrProgram::GetGlobals(pageIndex);
if (globals)
{
const s32 globalSize = scrProgram::GetGlobalSize(pageIndex);
return (ResetWriteWatch(globals, globalSize*sizeof(scrValue)) == 0);
}
return false;
}
void CScriptGlobalTamperCheckerTunablesListener::OnTunablesRead()
{
m_IsDisabled = Tunables::GetInstance().TryAccess(MP_GLOBAL_HASH, ATSTRINGHASH("DISABLE_SCRIPT_STATISTICS", 0xD4329E00), false);
}
bool CScriptGlobalTamperChecker::ShouldCheckGlobalBlock(s32 pageIndex)
{
// Should we ignore everything because of tuneables?
if(ms_pScriptGlobalTamperTunablesListener.IsDisabled())
return false;
//TODO: Extend to flexibly include other buckets via a tunable arrayn
u32 blockNameHash = scrProgram::GetGlobalNameHash(pageIndex);
const int numIgnorableGlobalBlocks = 3;
const u32 ignoreableGlobalBlockNameHashes[numIgnorableGlobalBlocks] = {
ATSTRINGHASH("startup", 0x41D6F794),
ATSTRINGHASH("ugc_global_registration", 0x19F4EE3D),
ATSTRINGHASH("global_fmmc_registration", 0x562E9707)};
for(int i = 0; i < numIgnorableGlobalBlocks; i++)
{
if(blockNameHash == ignoreableGlobalBlockNameHashes[i])
return false;
}
return true;
}
bool CScriptGlobalTamperChecker::HaveGlobalsBeenWrittenToSinceReset(s32 pageIndex)
{
if(!ShouldCheckGlobalBlock(pageIndex))
return false;
scrValue* globals = scrProgram::GetGlobals(pageIndex);
if (globals)
{
const s32 globalSize = scrProgram::GetGlobalSize(pageIndex);
void* addressArray[128];
ULONG_PTR count = 128;
ULONG pageSize;
if ( GetWriteWatch(0, globals, globalSize*sizeof(scrValue), addressArray, &count, &pageSize) == 0 )
{
if (count)
{
#if !RSG_FINAL
scriptWarningf("CScriptGlobalTamperChecker - Global %p / Page %d / Size %d / Valid Page? %x", globals, pageIndex, globalSize, ms_abValidPage[pageIndex]);
for(int i = 0; i < count && i < 128; i++)
{
scriptWarningf("\tAddress %d/%d - %p", i , count-1, addressArray[i]);
}
if(PARAM_scriptProtectGlobals.Get())
{
__debugbreak();
}
#endif
return true;
}
}
}
return false;
}
void CScriptGlobalTamperChecker::LockGlobalsMemory(s32 pageIndex)
{
scrValue* globals = scrProgram::GetGlobals(pageIndex);
if (globals)
{
const s32 globalSize = scrProgram::GetGlobalSize(pageIndex);
sysMemLockMemory(globals, globalSize*sizeof(scrValue));
}
}
void CScriptGlobalTamperChecker::UnlockGlobalsMemory(s32 pageIndex)
{
scrValue* globals = scrProgram::GetGlobals(pageIndex);
if (globals)
{
const s32 globalSize = scrProgram::GetGlobalSize(pageIndex);
sysMemUnlockMemory(globals, globalSize*sizeof(scrValue));
}
}
void CScriptGlobalTamperChecker::FakeTamper(s32 pageIndex,const s32 offsetIndex)
{
scrValue* globals = scrProgram::GetGlobals(pageIndex);
if (globals)
{
const s32 globalSize = scrProgram::GetGlobalSize(pageIndex);
if(offsetIndex < globalSize)
{
globals[offsetIndex].Int = 123;
}
}
}
CScriptGlobalTamperChecker::UncheckedScope::UncheckedScope()
{
CScriptGlobalTamperChecker::BeginUncheckedSection();
}
CScriptGlobalTamperChecker::UncheckedScope::~UncheckedScope()
{
CScriptGlobalTamperChecker::EndUncheckedSection();
}
#endif //RSG_PC