Files
gtav-src/script/dev_ng/singleplayer/include/public/randomChar_public.sch
T
2025-09-29 00:52:08 +02:00

908 lines
37 KiB
Scheme
Executable File

USING "rage_builtins.sch"
USING "globals.sch"
USING "code_control_public.sch"
USING "commands_brains.sch"
USING "flow_public_core_override.sch"
USING "mission_control_public.sch"
USING "randomChar_private.sch"
USING "replay_public.sch"
USING "savegame_public.sch"
USING "director_mode_public.sch"
// *****************************************************************************************
// *****************************************************************************************
// *****************************************************************************************
//
// MISSION NAME : randomChar_Public.sch
// CREATED : Keith
// MAINTAINED : Andrew Minghella
// DESCRIPTION : Contains all Random Character public functions.
//
// *****************************************************************************************
// *****************************************************************************************
// *****************************************************************************************
/// PURPOSE:
/// Return the state of the 'Completed' flag for this mission ID
/// PARAMS:
/// paramID - RC mission ID
/// RETURNS:
/// TRUE if this RC mission is already completed. FALSE otherwise
FUNC BOOL IS_THIS_RANDOM_CHARACTER_MISSION_COMPLETED(g_eRC_MissionIDs paramID)
IF ((paramID = MAX_RC_MISSIONS) OR (paramID = NO_RC_MISSION))
SCRIPT_ASSERT("ERROR: Is_This_Random_Character_Mission_Completed has been passed an illegal Random Character mission ID")
RETURN FALSE
ENDIF
// Return the Completed state of this RC mission
RETURN IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[paramID].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
ENDFUNC
/// PURPOSE:
/// Is current RC blocked due to an active story mission?
FUNC BOOL Random_Character_Blocked_Due_To_Mission(g_eRC_MissionIDs eRCMission)
IF eRCMission = RC_TONYA_1
OR eRCMission = RC_TONYA_2
OR eRCMission = RC_TONYA_5
IF IS_MISSION_AVAILABLE(SP_MISSION_FRANKLIN_0)
OR IS_MISSION_AVAILABLE(SP_MISSION_FRANKLIN_1)
OR IS_MISSION_AVAILABLE(SP_MISSION_LAMAR)
OR IS_MISSION_AVAILABLE(SP_MISSION_MICHAEL_2)
RETURN TRUE
ENDIF
ENDIF
//B*-2167022
IF eRCMission = RC_HAO_1
IF IS_MISSION_AVAILABLE(SP_MISSION_FRANKLIN_1)
RETURN TRUE
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Returns whether or not this RC mission is allowed to launch if the player has a wanted level.
/// PARAMS:
/// eRCMission - RC mission we are checking
/// RETURNS:
/// TRUE if this RC is allowed to launch with the player wanted. FALSE otherwise
FUNC BOOL Random_Character_Allows_Wanted_Level(g_eRC_MissionIDs eRCMission)
// Missions which can be launched with a wanted level
IF eRCMission = RC_BARRY_3A
OR eRCMission = RC_BARRY_3C
OR eRCMission = RC_NIGEL_1D
OR eRCMission = RC_TONYA_3
OR eRCMission = RC_TONYA_4
RETURN TRUE
ENDIF
// Wanted level not allowed
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Is current RC blocked due to player being wanted?
FUNC BOOL Random_Character_Blocked_Due_To_Wanted_Level(g_eRC_MissionIDs eRCMission)
IF Random_Character_Allows_Wanted_Level(eRCMission)
RETURN FALSE
ELSE
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
IF (GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) != 0)
RETURN TRUE
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Can the RC mission activate with the current player character?
FUNC BOOL Random_Character_Blocked_Due_To_Character(g_eRC_MissionIDs eRCMission = NO_RC_MISSION)
g_structRCMissionsStatic sRCMissionDetails
Retrieve_Random_Character_Static_Mission_Details(eRCMission, sRCMissionDetails)
// Check for valid SP character
enumCharacterList eCurrChar = GET_CURRENT_PLAYER_PED_ENUM()
IF eCurrChar = NO_CHARACTER
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Blocked_Due_To_Character: eCurrChar = NO CHARACTER : ", sRCMissionDetails.rcScriptName)
RETURN TRUE
ELIF eCurrChar <> CHAR_FRANKLIN AND eCurrChar <> CHAR_MICHAEL AND eCurrChar <> CHAR_TREVOR
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Blocked_Due_To_Character: eCurrChar = NOT PLAYABLE CHAR :", sRCMissionDetails.rcScriptName)
RETURN TRUE
ENDIF
// Ensure the player is the correct character for this RCM
IF NOT IS_BIT_SET(sRCMissionDetails.rcPlayableChars,ENUM_TO_INT(eCurrChar))
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Blocked_Due_To_Character: eCurrChar = NOT CORRECT CHAR :", sRCMissionDetails.rcScriptName)
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks if this specific mission is currently allowed to launch.
/// If not allowed, the script should terminate.
/// PARAMS:
/// eRCMission - The Rc mission we are checking
/// (Leave the eRCMission param to NO_RC_MISSION to auto detect mission ID.)
/// RETURNS:
/// TRUE if the misison is allowed to launch, false otherwsie.
FUNC BOOL Random_Character_Is_Allowed_To_Launch(g_eRC_MissionIDs eRCMission = NO_RC_MISSION)
g_structRCMissionsStatic sRCMissionDetails
Retrieve_Random_Character_Static_Mission_Details(eRCMission, sRCMissionDetails)
// Don't allow the mission to launch if another mission or activity is currently running
// Will not cancel on switch scenes...
IF NOT CAN_MISSION_TYPE_START_AGAINST_CURRENT_TYPE(MISSION_TYPE_RANDOM_CHAR)
IF IS_THIS_RC_MISSION_BEING_PROCESSED_FOR_REPLAY(eRCMission)
// only allow to run if a replay is being setup for this mission
CPRINTLN(DEBUG_REPLAY, "Allowing RC to launch- it is being processed for a replay.", sRCMissionDetails.rcScriptName)
ELSE
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Is_Allowed_To_Launch: Failed, player already on mission - terminate ", sRCMissionDetails.rcScriptName)
RETURN FALSE
ENDIF
ENDIF
IF IS_MISSION_LEADIN_WITH_HIGH_MEMORY_ACTIVE()
AND NOT IS_HIGH_MEMORY_PC()
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Is_Allowed_To_Launch: Failed, mission with high memory active. Terminate.", sRCMissionDetails.rcScriptName)
RETURN FALSE
ENDIF
IF Random_Character_Blocked_Due_To_Wanted_Level(eRCMission)
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Is_Allowed_To_Launch: Failed, player is wanted. Terminate.", sRCMissionDetails.rcScriptName)
RETURN FALSE
ENDIF
// Retrieve the Random Character array index for this script
IF eRCMission = NO_RC_MISSION
eRCMission = Get_RC_MissionID_For_This_Script()
ENDIF
IF (eRCMission = NO_RC_MISSION)
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Is_Allowed_To_Launch: Failed. invalid RC mission. Terminate.", sRCMissionDetails.rcScriptName)
RETURN FALSE
ENDIF
IF NOT IsThisRCBeingRepeatPlayed(ENUM_TO_INT(eRCMission))
// don't need these checks in rpeeat play
// Ensure this Random Character mission is Activated
IF NOT (Is_This_Random_Character_Mission_Active(eRCMission))
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Is_Allowed_To_Launch: Failed. Not activated. Terminate ", sRCMissionDetails.rcScriptName)
RETURN FALSE
ENDIF
// Ensure this Random Character mission is Ready To Play
IF NOT (Is_This_Random_Character_Mission_Ready_To_Play(eRCMission))
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Is_Allowed_To_Launch: Failed. Not ready to play ", sRCMissionDetails.rcScriptName)
RETURN FALSE
ENDIF
// Ensure this Random Character mission is not Completed
IF (Is_This_Random_Character_Mission_Completed(eRCMission))
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Is_Allowed_To_Launch: Failed. Already completed ", sRCMissionDetails.rcScriptName)
RETURN FALSE
ENDIF
ENDIF
// Allow Tonya 1 to activate when in Franklin's safehouse in the event of reloading from an autosave (B*1324029)
IF eRCMission = RC_TONYA_1
#if not USE_CLF_DLC
#if not USE_NRM_DLC
IF IS_PLAYER_IN_SAVEHOUSE(SAVEHOUSE_FRANKLIN_SC)
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Is_Allowed_To_Launch: Player is in safehouse, bypassing leave area check ", sRCMissionDetails.rcScriptName)
g_RandomChars[eRCMission].rcLeaveAreaCheck = FALSE
ENDIF
#endif
#endif
ENDIF
// Ensure this RC mission is not waiting for the player to leave the area
IF g_RandomChars[eRCMission].rcLeaveAreaCheck = TRUE
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Is_Allowed_To_Launch: Waiting for player to leave area ", sRCMissionDetails.rcScriptName)
RETURN FALSE
ENDIF
// Allowed to Launch
CPRINTLN(DEBUG_RANDOM_CHAR, sRCMissionDetails.rcScriptName, " RC mission allowed to launch")
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Sets the RC as running, not passed and not failed. Called when it has permission to run
/// PARAMS:
/// eRCMission - ID of the RC mission we are setting as running
PROC SET_RC_AS_RUNNING(g_eRC_MissionIDs eRCMission)
CPRINTLN(DEBUG_RANDOM_CHAR, "SET_RC_AS_RUNNING()")
g_RandomChars[eRCMission].rcIsRunning = TRUE
g_RandomChars[eRCMission].rcPassed = FALSE
g_RandomChars[eRCMission].rcFailed = FALSE
g_RandomChars[eRCMission].rcTerminateLauncher = FALSE // The RC mission will set this to TRUE when up and running
ENDPROC
/// PURPOSE:
/// Uses candidate ID system to try to get permission for specified RC mission to run.
/// PARAMS:
/// eRCMission - RC mission that wants to run
/// ( Leave the eRCMission param to NO_RC_MISSION to auto detect mission ID.)
/// RETURNS:
/// TRUE if the RC mission has permission to run. FALSE otherwise.
FUNC BOOL RC_REQUEST_PERMISSION_TO_RUN(g_eRC_MissionIDs eRCMission = NO_RC_MISSION)
IF eRCMission = NO_RC_MISSION
eRCMission = Get_RC_MissionID_For_This_Script()
ENDIF
IF (eRCMission = NO_RC_MISSION)
SCRIPT_ASSERT("ERROR: RC_REQUEST_PERMISSION_TO_RUN has been passed an illegal Random Character mission ID")
RETURN FALSE
ENDIF
m_enumMissionCandidateReturnValue allowLaunch = Request_Mission_Launch(g_RandomChars[eRCMission].rcMissionCandidateID, MCTID_MEET_CHARACTER, MISSION_TYPE_RANDOM_CHAR)
#IF IS_DEBUG_BUILD
g_structRCMissionsStatic sRCMissionDetails
Retrieve_Random_Character_Static_Mission_Details(eRCMission, sRCMissionDetails)
CPRINTLN(DEBUG_RANDOM_CHAR, "RC Controller - RC_REQUEST_PERMISSION_TO_RUN: ", sRCMissionDetails.rcScriptName)
IF (allowLaunch = MCRET_DENIED)
CPRINTLN(DEBUG_RANDOM_CHAR,"DENIED - so another mission must have launched so this Random Character should terminate")
ENDIF
IF (allowLaunch = MCRET_PROCESSING)
CPRINTLN(DEBUG_RANDOM_CHAR,"PROCESSING - so this Random Character should keep trying")
ENDIF
IF (allowLaunch = MCRET_ACCEPTED)
CPRINTLN(DEBUG_RANDOM_CHAR,"ACCEPTED - Random Character must run because it now has control of the IS_CURRENTLY_ON_MISSION_TO_TYPE() flag")
ENDIF
#ENDIF
IF NOT (allowLaunch = MCRET_ACCEPTED)
// DENIED or still PROCESSING.
RETURN FALSE
ENDIF
// This mission has permission to launch
SET_RC_AS_RUNNING(eRCMission)
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Checks if this RC launcher now needs to terminate.
/// (If player has gone on another mission, got wanted level, is wrong character etc)
/// PARAMS:
/// eRCMission - the RC mission we are checking
/// RETURNS:
/// TRUE if the RC mission should now terminate. FALSE otherwise
FUNC BOOL SHOULD_RC_TERMINATE(g_eRC_MissionIDs eRCMission = NO_RC_MISSION)
IF eRCMission = NO_RC_MISSION
eRCMission = Get_RC_MissionID_For_This_Script()
ENDIF
IF (eRCMission = NO_RC_MISSION)
SCRIPT_ASSERT("ERROR: SHOULD_RC_TERMINATE has been passed an illegal Random Character mission ID")
// Needs to terminate
RETURN TRUE
ENDIF
// If this RC is being repeat played, we should never terminate here
IF IsThisRCBeingRepeatPlayed(ENUM_TO_INT(eRCMission))
CPRINTLN(DEBUG_RANDOM_CHAR, GET_RC_MISSION_DISPLAY_STRING_FROM_ID(eRCMission), " is being repeat played. SHOULD_RC_TERMINATE = FALSE")
RETURN FALSE
ENDIF
// Don't allow the mission to launch if another mission or activity is currently running
// Will not cancel on switch scenes...
IF NOT CAN_MISSION_TYPE_START_AGAINST_CURRENT_TYPE(MISSION_TYPE_RANDOM_CHAR)
IF IS_THIS_RC_MISSION_BEING_PROCESSED_FOR_REPLAY(eRCMission)
// Only allow to run if a replay is being setup for this mission
CPRINTLN(DEBUG_REPLAY, "Allowing RC to launch- it is being processed for a replay.")
ELSE
CPRINTLN(DEBUG_RANDOM_CHAR,"SHOULD_RC_TERMINATE. Player is on another mission. Terminate.", GET_THIS_SCRIPT_NAME())
RETURN TRUE
ENDIF
ENDIF
IF IS_MISSION_LEADIN_WITH_HIGH_MEMORY_ACTIVE()
AND NOT IS_HIGH_MEMORY_PC()
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Is_Allowed_To_Launch: Failed, mission with high memory lead-in has activated. Terminate.")
RETURN TRUE
ENDIF
// Don't allow the mission to launch if the player has a wanted level
IF NOT Random_Character_Allows_Wanted_Level(eRCMission)
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
IF (GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) != 0)
CPRINTLN(DEBUG_RANDOM_CHAR,"SHOULD_RC_TERMINATE. Player has wanted level. Terminate.", GET_THIS_SCRIPT_NAME())
RETURN TRUE
ENDIF
ENDIF
ENDIF
// Don't allow the mission to launch if the 'Ready to play' flag has been cleared
IF NOT (IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[ENUM_TO_INT(eRCMission)].rcFlags, ENUM_TO_INT(RC_FLAG_READY_TO_PLAY)))
CPRINTLN(DEBUG_RANDOM_CHAR,"SHOULD_RC_TERMINATE. Ready to play flag has been cleared. Terminate.", GET_THIS_SCRIPT_NAME())
RETURN TRUE
ENDIF
// Doesn't need to terminate
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks if this RC launcher now needs to terminate when it is active as an invalid character
FUNC BOOL SHOULD_RC_TERMINATE_WHEN_INCORRECT_CHAR(g_eRC_MissionIDs eRCMission)
IF NOT IS_WORLD_POINT_WITHIN_BRAIN_ACTIVATION_RANGE()
CPRINTLN(DEBUG_RANDOM_CHAR,"SHOULD_RC_TERMINATE_WHEN_INCORRECT_CHAR: NOT IS_WORLD_POINT_WITHIN_BRAIN_ACTIVATION_RANGE()")
RETURN TRUE
ENDIF
IF IS_PLAYER_SWITCH_IN_PROGRESS()
CPRINTLN(DEBUG_RANDOM_CHAR,"SHOULD_RC_TERMINATE_WHEN_INCORRECT_CHAR: IS_PLAYER_SWITCH_IN_PROGRESS()")
RETURN TRUE
ENDIF
IF SHOULD_RC_TERMINATE(eRCMission)
CPRINTLN(DEBUG_RANDOM_CHAR,"SHOULD_RC_TERMINATE_WHEN_INCORRECT_CHAR: SHOULD_RC_TERMINATE()")
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Updates playstats, launches ambient scripts, executes code IDs, activates next RC mission
/// Updates repeat play completion order. Sets completed bit
PROC Set_Random_Character_Mission_Complete(g_eRC_MissionIDs eRCMission,bool bdebugJump = false)
// Before we set the complete flag, check to see if we need to perform any more actions
g_structRCMissionsStatic sRCMissionDetails
Retrieve_Random_Character_Static_Mission_Details(eRCMission, sRCMissionDetails)
// Launch ambient script?
IF NOT ARE_STRINGS_EQUAL(sRCMissionDetails.rcAmbientPassScript, "")
// Attempt to launch...
WHILE NOT Random_Character_Launch_Ambient_Script(sRCMissionDetails.rcAmbientPassScript)
WAIT(0)
ENDWHILE
ENDIF
// Execute code ID?
IF sRCMissionDetails.rcPassCodeID != CID_BLANK
Execute_Code_ID(sRCMissionDetails.rcPassCodeID)
ENDIF
// Make Director Mode characters linked to this RC mission available to the player.
#IF FEATURE_SP_DLC_DIRECTOR_MODE
UNLOCK_DIRECTOR_STORY_CHAR_FOR_RC_MISSION(eRCMission, g_flowUnsaved.bUpdatingGameflow)
#ENDIF
if not bdebugJump
// Unlock news story for RCM if it exists...
INT iNewsStoryUnlock = GET_RCM_NEWS_STORY_UNLOCK(eRCMission)
IF iNewsStoryUnlock != ENUM_TO_INT(NEWS_BLANK)
CPRINTLN(DEBUG_FLOW, "Unlocked news story for ", GET_RC_MISSION_DISPLAY_STRING_FROM_ID(eRCMission))
if not IS_MISSION_NEWS_STORY_UNLOCKED(iNewsStoryUnlock)
CPRINTLN(debug_flow,"Unlocking news story #",iNewsStoryUnlock)
UNLOCK_MISSION_NEWS_STORY(iNewsStoryUnlock)
endif
ENDIF
endif
// Activate next RC mission?
IF sRCMissionDetails.rcNextMission <> NO_RC_MISSION
BOOL bActivateMission = TRUE
// If this mission is part of a group, make sure all other missions in this group have
// been marked as 'Completed' before we activate the next RC mission.
IF sRCMissionDetails.rcMissionGroup <> NO_RC_MISSION_GROUP
INT iMission
g_structRCMissionsStatic sRCTempMissionDetails
g_eRC_MissionIDs eRCTempMission
REPEAT MAX_RC_MISSIONS iMission
eRCTempMission = INT_TO_ENUM(g_eRC_MissionIDs, iMission)
IF (eRCTempMission <> eRCMission) // Ignore this mission
Retrieve_Random_Character_Static_Mission_Details(eRCTempMission, sRCTempMissionDetails)
// In the same group?
IF (sRCTempMissionDetails.rcMissionGroup = sRCMissionDetails.rcMissionGroup)
IF NOT IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCTempMission].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
bActivateMission = FALSE
ENDIF
// Ensure that this mission has been Activated.
IF NOT IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[eRCTempMission].rcFlags, ENUM_TO_INT(RC_FLAG_ACTIVATED))
// do we need to check RC_FLAG_ACTIVATED_IN_FLOW ?
Random_Character_Activate_Mission(eRCTempMission)
ENDIF
ENDIF
ENDIF
ENDREPEAT
ENDIF
// Safe to activate next RC mission?
IF bActivateMission
Random_Character_Activate_Mission(sRCMissionDetails.rcNextMission)
ENDIF
// Activate next group of RC missions?
ELIF sRCMissionDetails.rcNextMissionGroup <> NO_RC_MISSION_GROUP
// Activate all missions that are part of this group.
INT iMission
g_structRCMissionsStatic sRCTempMissionDetails
g_eRC_MissionIDs eRCTempMission
// Grab all the individual missions that are part of the next group and activate.
REPEAT MAX_RC_MISSIONS iMission
eRCTempMission = INT_TO_ENUM(g_eRC_MissionIDs, iMission)
IF (eRCTempMission <> eRCMission) // Ignore this mission
Retrieve_Random_Character_Static_Mission_Details(eRCTempMission, sRCTempMissionDetails)
// In the same group?
IF (sRCTempMissionDetails.rcMissionGroup = sRCMissionDetails.rcNextMissionGroup)
Random_Character_Activate_Mission(eRCTempMission)
ENDIF
ENDIF
ENDREPEAT
ENDIF
// Pass procedure complete so set the Completed flag (only if not already flagged- used in debug skipping)
IF NOT IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[ENUM_TO_INT(eRCMission)].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
CPRINTLN(DEBUG_FLOW, "Flagging RC mission as completed + passed:", sRCMissionDetails.rcScriptName, " Variation:", sRCMissionDetails.rcStatVariation)
SET_BIT(g_savedGlobals.sRandomChars.savedRC[ENUM_TO_INT(eRCMission)].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
g_RandomChars[ENUM_TO_INT(eRCMission)].rcPassed = TRUE
// Update repeat play completion order
UpdateRCMissionRepeatCompletionOrder(ENUM_TO_INT(eRCMission))
// Set 100% completion stuff
enumCompletionPercentageEntries eMissionCompletion = GET_RC_MISSION_COMPLETION_ID(eRCMission)
IF eMissionCompletion != UNUSED_DEFAULT
REGISTER_SCRIPT_IN_COMPLETION_PERCENTAGE_TOTAL(eMissionCompletion)
CPRINTLN(DEBUG_FLOW, "Adding RC to 100% completion: ", GET_RC_MISSION_DISPLAY_STRING_FROM_ID(eRCMission))
ELSE
CPRINTLN(DEBUG_FLOW, "Not adding RC to 100% completion as it has no completion percentage: ", GET_RC_MISSION_DISPLAY_STRING_FROM_ID(eRCMission))
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Sets flags needed to activate RC mission
/// PARAMS:
/// eRCTempMission - the RC mission we want to activate
/// bCompleteMission - if True the mission is be set as completed.
PROC Activate_RC_Mission(g_eRC_MissionIDs eRCTempMission, BOOL bCompleteMission)
SET_BIT(g_savedGlobals.sRandomChars.savedRC[eRCTempMission].rcFlags, ENUM_TO_INT(RC_FLAG_ACTIVATED))
SET_BIT(g_savedGlobals.sRandomChars.savedRC[eRCTempMission].rcFlags, ENUM_TO_INT(RC_FLAG_ACTIVATED_IN_FLOW))
SET_BIT(g_savedGlobals.sRandomChars.savedRC[eRCTempMission].rcFlags, ENUM_TO_INT(RC_FLAG_READY_TO_PLAY))
IF bCompleteMission = TRUE
Set_Random_Character_Mission_Complete(eRCTempMission,true)
ENDIF
ENDPROC
/// PURPOSE:
/// Sets flags needed to deactivate RC mission
PROC Deactivate_RC_Mission(g_eRC_MissionIDs eRCTempMission)
CLEAR_BIT(g_savedGlobals.sRandomChars.savedRC[eRCTempMission].rcFlags, ENUM_TO_INT(RC_FLAG_ACTIVATED))
CLEAR_BIT(g_savedGlobals.sRandomChars.savedRC[eRCTempMission].rcFlags, ENUM_TO_INT(RC_FLAG_ACTIVATED_IN_FLOW))
CLEAR_BIT(g_savedGlobals.sRandomChars.savedRC[eRCTempMission].rcFlags, ENUM_TO_INT(RC_FLAG_READY_TO_PLAY))
ENDPROC
/// PURPOSE:
/// Record that the Random Character mission passed, and perform any pass-specific functionality.
/// PARAMS:
/// bClearWantedLevel - Clears the wanted level
PROC Random_Character_Passed(enumCompletionPercentageEntries eCompletionPercentage=UNUSED_DEFAULT, BOOL bClearWantedLevel=TRUE)
// Retrieve the Random Character array index for this script
g_eRC_MissionIDs eRCMission = Get_RC_MissionID_For_This_Script()
IF (eRCMission = NO_RC_MISSION)
SCRIPT_ASSERT("ERROR: Random_Character_Passed has been passed an illegal Random Character mission ID")
EXIT
ENDIF
// Make sure that the Random Character was running
IF NOT (g_RandomChars[eRCMission].rcIsRunning)
CPRINTLN(DEBUG_RANDOM_CHAR, "ERROR: Random_Character_Passed But script wasn't running: ", GET_THIS_SCRIPT_NAME())
SCRIPT_ASSERT("RC Controller - Random_Character_Passed: Random Character Mission wasn't running")
EXIT
ENDIF
// Make sure it hasn't already passed
IF (g_RandomChars[eRCMission].rcPassed)
CPRINTLN(DEBUG_RANDOM_CHAR, "ERROR: Random_Character_Passed But script has already passed: ", GET_THIS_SCRIPT_NAME())
SCRIPT_ASSERT("RC Controller - Random_Character_Passed: Random Character Mission has already passed")
EXIT
ENDIF
// Make sure it hasn't already been classed as failed
IF (g_RandomChars[eRCMission].rcFailed)
CPRINTLN(DEBUG_RANDOM_CHAR, "ERROR: Random_Character_Passed But script already failed: ", GET_THIS_SCRIPT_NAME())
SCRIPT_ASSERT("RC Controller - Random_Character_Passed: Random Character Mission has already been classed as failed")
EXIT
ENDIF
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Passed: ", GET_THIS_SCRIPT_NAME())
// Do all of the RC complete stuff
Set_Random_Character_Mission_Complete(eRCMission)
SET_BIT(g_iRepeatPlayBits, ENUM_TO_INT(RPB_PASSED))
// Mark the mission as over
// Ensure the mission candidate ID is valid
IF (g_RandomChars[eRCMission].rcMissionCandidateID = NO_CANDIDATE_ID)
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Passed: invalid Mission Candidate System ID: ", GET_THIS_SCRIPT_NAME())
SCRIPT_ASSERT("RC Controller - Random_Character_Passed: Random Character Mission has an invalid Mission Candidate System ID")
ELSE
// ...the ID is valid, so inform Mission Candidate System that the mission is over
CPRINTLN(DEBUG_REPLAY, "RC mission passed and calling mission over")
Mission_Over(g_RandomChars[eRCMission].rcMissionCandidateID)
ENDIF
#IF IS_DEBUG_BUILD
g_txtFlowAutoplayRunningMission = "NO MISSION"
g_bFlowAutoplayJustPassed = TRUE
#ENDIF
// Clear the player's wanted level (on by default)
IF bClearWantedLevel
IF IS_PLAYER_PLAYING( PLAYER_ID() )
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Passed: : Clearing wanted level and starting firing amnesty now")
SET_PLAYER_WANTED_LEVEL(PLAYER_ID(), 0)
SET_PLAYER_WANTED_LEVEL_NOW(PLAYER_ID())
START_FIRING_AMNESTY()
ENDIF
ENDIF
// Call the common RC Mission Over procedure.
RC_MISSION_OVER(eRCMission, TRUE, FALSE, FALSE)
// Play mission passed audio and show passed screen.
TRIGGER_MISSION_STATS_UI()
// Set the last completed mission so that it appears on the savegame.
TEXT_LABEL_15 tlRCMissionName = GET_RC_MISSION_NAME_LABEL(eRCMission)
SET_LAST_COMPLETED_MISSION_STAT(tlRCMissionName, GET_CURRENT_PLAYER_PED_BIT())
// Remove headsets from player characters
REMOVE_ALL_HEADSETS()
//Restore phone's sleep mode if it was set before the mission.
IF IS_BIT_SET(g_savedGlobals.sFlowCustom.spInitBitset, SP_INIT_RESTORE_SLEEP_MODE)
SET_CELLPHONE_PROFILE_TO_SLEEP()
ENDIF
// Reset replay variables, so the game knows we are no longer on a replay
Reset_All_Replay_Variables()
// Tonya 3 and 4 registration
IF eCompletionPercentage = CP_OJ_TOW3
OR eCompletionPercentage = CP_OJ_TOW4
REGISTER_SCRIPT_IN_COMPLETION_PERCENTAGE_TOTAL(eCompletionPercentage, 406.38, -1635.86)
// Mrs Philips 2
ELIF eCompletionPercentage = CP_RAND_C_MRS2
REGISTER_SCRIPT_IN_COMPLETION_PERCENTAGE_TOTAL(eCompletionPercentage, 1973.84, 3814.89)
// Register completion percentage
ELIF eCompletionPercentage <> UNUSED_DEFAULT
REGISTER_SCRIPT_IN_COMPLETION_PERCENTAGE_TOTAL(eCompletionPercentage)
ENDIF
// Save the game
MAKE_AUTOSAVE_REQUEST()
ENDPROC
/// PURPOSE:
/// Record that the Random Character mission failed and perform any fail-specific functionality.
/// PARAMS:
/// bSetupReplay - can this mission be replayed?
PROC Random_Character_Failed(BOOL bSetupReplay = TRUE)
// Set whether player gets warped when rejecting replay + update fail reason if killed / arrested
// always need to check this, to handle player being killed during fail screen
SET_FORCE_CLEANUP_FAIL_REASON()
// this check is needed as it is possible to fail a mission and then get killed
// during the fade (after mission already marked as failed, and replay is being processed)
IF NOT IS_REPLAY_BEING_PROCESSED()
// Retrieve the Random Character array index for this script
g_eRC_MissionIDs eRCMission = Get_RC_MissionID_For_This_Script()
IF (eRCMission = NO_RC_MISSION)
SCRIPT_ASSERT("ERROR: Random_Character_Failed has been passed an illegal Random Character mission ID")
EXIT
ENDIF
// Make sure that the Random Character was running
IF NOT (g_RandomChars[eRCMission].rcIsRunning)
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Failed: Script wasn't running: ", GET_THIS_SCRIPT_NAME())
SCRIPT_ASSERT("RC Controller - Random_Character_Failed: Random Character Mission wasn't running")
EXIT
ENDIF
// Make sure it hasn't already passed
IF (g_RandomChars[eRCMission].rcPassed)
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Failed: RC has already been classed as passed: ", GET_THIS_SCRIPT_NAME())
SCRIPT_ASSERT("RC Controller - Random_Character_Failed: Random Character Mission has already been classed as passed")
EXIT
ENDIF
// Make sure it hasn't already been classed as failed
IF (g_RandomChars[eRCMission].rcFailed)
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Failed: RC has already been classed as failed: ", GET_THIS_SCRIPT_NAME())
SCRIPT_ASSERT("RC Controller - Random_Character_Failed: Random Character Mission has already failed")
EXIT
ENDIF
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Failed: ", GET_THIS_SCRIPT_NAME())
#IF IS_DEBUG_BUILD
g_txtFlowAutoplayRunningMission = "NO MISSION"
g_bFlowAutoplayJustPassed = FALSE
#ENDIF
//Increment the shitskip counter but only if the retry value is the same as last time
IF g_iLastObservedRetryStatusOnFail = g_replayMissionStage
g_savedGlobals.sRandomChars.savedRC[eRCMission].iFailsNoProgress++
CPRINTLN(DEBUG_REPLAY, "Mission fail without progress counter increased to ",g_savedGlobals.sRandomChars.savedRC[eRCMission].iFailsNoProgress)
ENDIF
// update the checkpoint we reached
g_iLastObservedRetryStatusOnFail = g_replayMissionStage
IF bSetupReplay
// Tell the playstats system that we've failed an RC mission.
SET_RC_MISSION_OVER_PLAYSTATS(eRCMission, TRUE, FALSE)
// set up the replay
Setup_RC_Replay(GET_THIS_SCRIPT_NAME(), ENUM_TO_INT(eRCMission))
ELSE
// Mark the mission as over
IF (g_RandomChars[eRCMission].rcMissionCandidateID = NO_CANDIDATE_ID) // Ensure the mission candidate ID is valid
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Failed: RC has an invalid Mission Candidate System ID: ", GET_THIS_SCRIPT_NAME())
SCRIPT_ASSERT("Random_Character_Failed: Random Character Mission has an invalid Mission Candidate System ID")
ELSE
// ...the ID is valid, so inform Mission Candidate System that the mission is over
Mission_Over(g_RandomChars[eRCMission].rcMissionCandidateID)
ENDIF
// Call the common RC Mission Over procedure
// (also called if player passes mission, or rejects replay)
RC_MISSION_OVER(eRCMission, TRUE, TRUE, FALSE)
ENDIF
g_RandomChars[eRCMission].rcFailed = TRUE
ENDIF
ENDPROC
/// PURPOSE:
/// Record that the Random Character mission failed, and set the fail message that will appear.
/// PARAMS:
/// paramFailReason - fail reason for the mission failed screen
/// paramSetupReplay - is this RC replayable?
/// bDelayFade - should the replay controller delay the fade out for this fail reason
PROC Random_Character_Failed_With_Reason(STRING paramFailReason, BOOL paramSetupReplay = TRUE)
MISSION_FLOW_SET_FAIL_REASON(paramFailReason)
Random_Character_Failed(paramSetupReplay)
ENDPROC
/// PURPOSE:
/// Cleanup if the random character was running.
/// RETURNS:
/// TRUE if this random character mission had been triggered, FALSE if not triggered
FUNC BOOL Random_Character_Cleanup_If_Triggered()
// Retrieve the Random Character array index for this script
g_eRC_MissionIDs eRCMission = Get_RC_MissionID_For_This_Script()
IF (eRCMission = NO_RC_MISSION)
SCRIPT_ASSERT("ERROR: Random_Character_Cleanup_If_Triggered has been passed an illegal Random Character mission ID")
RETURN FALSE
ENDIF
IF NOT g_RandomChars[eRCMission].rcIsRunning
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Cleanup_If_Triggered: mission wasn't triggered. Basic cleanup.", GET_THIS_SCRIPT_NAME())
RETURN FALSE
ENDIF
// The mission had been triggered
g_RandomChars[eRCMission].rcIsRunning = FALSE
// Ensure mission brief is cleared
CLEAR_ADDITIONAL_TEXT(MISSION_TEXT_SLOT, TRUE)
CLEAR_ADDITIONAL_TEXT(MINIGAME_TEXT_SLOT, TRUE) // Used on Tonya RCM
CLEAR_ADDITIONAL_TEXT(ODDJOB_TEXT_SLOT, TRUE)
IF NOT IS_REPEAT_PLAY_ACTIVE()
// These error checks are not needed in repeat play, as it uses it own passed / failed flag
// Ensure it hasn't been classed as both passed and failed
IF (g_RandomChars[eRCMission].rcPassed)
AND (g_RandomChars[eRCMission].rcFailed)
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Cleanup_If_Triggered: is classed as having both passed and failed - TEMP TREATING AS PASSED.", GET_THIS_SCRIPT_NAME())
SCRIPT_ASSERT("Random_Character_Cleanup_If_Triggered: Random Character Mission is classed as having both passed and failed - TEMP TREATING AS PASSED")
g_RandomChars[eRCMission].rcFailed = FALSE
// Although an assert, allow it to continue to cleanup properly
ENDIF
// Ensure it has been classed as either passed or failed
IF NOT (g_RandomChars[eRCMission].rcFailed)
AND NOT (g_RandomChars[eRCMission].rcPassed)
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Cleanup_If_Triggered: is not classed as having passed or failed - TEMP TREATING AS FAILED.", GET_THIS_SCRIPT_NAME())
SCRIPT_ASSERT("Random_Character_Cleanup_If_Triggered: Random Character Mission is not classed as having passed or failed - TEMP TREATING AS FAILED")
g_RandomChars[eRCMission].rcFailed = TRUE
// Although an assert, allow it to continue to cleanup properly
ENDIF
ENDIF
// Any other cleanup here to allow the random character mission to continue
// Mission was running so probably needs additional cleanup
CPRINTLN(DEBUG_RANDOM_CHAR, "Random_Character_Cleanup_If_Triggered: full clean up - mission was triggered - returning TRUE.", GET_THIS_SCRIPT_NAME())
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Sets a flag telling the RC launcher that it needs to terminate
PROC RC_CLEANUP_LAUNCHER()
// Retrieve the Random Character array index for this script
g_eRC_MissionIDs eRCMission = Get_RC_MissionID_For_This_Script()
IF (eRCMission = NO_RC_MISSION)
SCRIPT_ASSERT("ERROR: RC_CLEANUP_LAUNCHER has been passed an illegal Random Character mission ID")
EXIT
ENDIF
CPRINTLN(DEBUG_RANDOM_CHAR, "RC_CLEANUP_LAUNCHER()")
g_RandomChars[eRCMission].rcTerminateLauncher = TRUE
ENDPROC
/// PURPOSE:
/// Sets a flag so the random character controller what state the mission is in.
/// PARAMS:
/// eRCMission - the RC misison we are using
/// (Leave the eRCMission param to NO_RC_MISSION to auto detect mission ID)
PROC SET_RC_AWAITING_TRIGGER(g_eRC_MissionIDs eRCMission = NO_RC_MISSION)
// Retrieve the Random Character array index for this script
IF eRCMission = NO_RC_MISSION
eRCMission = Get_RC_MissionID_For_This_Script()
ENDIF
IF (eRCMission = NO_RC_MISSION)
SCRIPT_ASSERT("ERROR: SET_RC_AWAITING_TRIGGER has been passed an illegal Random Character mission ID")
EXIT
ENDIF
g_RandomChars[eRCMission].rcIsAwaitingTrigger = TRUE
ENDPROC
/// PURPOSE:
/// Gets the mission ID of a RC mission which is valid, active and ready to play
/// NOTES:
/// This function is duplicated in comms_control_private.sch due to cyclic header error
/// on compilation - please update both functions when making edits
/// RETURNS:
/// The mission ID of the nearest RC valid mission (NO_RC_MISSION is returned if nothing is found or the player isn't playing)
FUNC g_eRC_MissionIDs GET_NEAREST_VALID_RC_MISSION(FLOAT fDistanceToCheck=RC_BRAIN_ACTIVATION_RANGE_EXTRA, BOOL bCheckPlayerChar=TRUE)
// Mission and player info
g_structRCMissionsStatic sRCMissionDetails
g_eRC_MissionIDs eRCMission
g_eRC_MissionIDs eNearestRCM = NO_RC_MISSION
FLOAT fRCMDistance = fDistanceToCheck
FLOAT fCurrentDist
// Valid player check
IF IS_PLAYER_PLAYING(PLAYER_ID())
#IF USE_TU_CHANGES
IF IS_PLAYER_PED_PLAYABLE(GET_CURRENT_PLAYER_PED_ENUM())
#ENDIF
INT iPlayerChar = GET_CURRENT_PLAYER_PED_BIT()
// Iterate through all RCM's
INT iMission = 0
REPEAT MAX_RC_MISSIONS iMission
eRCMission = INT_TO_ENUM(g_eRC_MissionIDs, iMission)
// RC mission is ready to play
IF IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[ENUM_TO_INT(eRCMission)].rcFlags, ENUM_TO_INT(RC_FLAG_READY_TO_PLAY))
AND NOT IS_BIT_SET(g_savedGlobals.sRandomChars.savedRC[ENUM_TO_INT(eRCMission)].rcFlags, ENUM_TO_INT(RC_FLAG_COMPLETED))
// Get the RCM information (only really need the trigger coords)
Retrieve_Random_Character_Static_Mission_Details(eRCMission, sRCMissionDetails)
// Is this RCM within specified distance and nearer to the player than any previously stored RCM?
fCurrentDist = GET_DISTANCE_BETWEEN_COORDS(GET_ENTITY_COORDS(PLAYER_PED_ID(), FALSE),sRCMissionDetails.rcCoords)
IF (fCurrentDist < fRCMDistance)
BOOL bValidMission = TRUE
// Is the player currently the correct character for this RCM?
IF bCheckPlayerChar
IF iPlayerChar <> sRCMissionDetails.rcPlayableChars
bValidMission = FALSE
ENDIF
ENDIF
// Store this as the nearest RCM
IF bValidMission
eNearestRCM = eRCMission
fRCMDistance = fCurrentDist
ENDIF
ENDIF
ENDIF
ENDREPEAT
#IF USE_TU_CHANGES
ENDIF
#ENDIF
ENDIF
// Return nearest valid RCM (NO_RC_MISSION if nothing is found...)
RETURN eNearestRCM
ENDFUNC
/// PURPOSE:
/// Check to see if a random char mission is available
/// PARAMS:
/// paramRCMission - Mission id
/// RETURNS:
/// True if it is, false if it isn't
FUNC BOOL IS_RC_MISSION_AVAILABLE(g_eRC_MissionIDs paramRCMission)
RETURN g_RandomChars[paramRCMission].rcIsAwaitingTrigger
ENDFUNC
/// PURPOSE:
/// Checks if a post RC script should terminate due to character switch
/// Or a mission trigger scene being active etc
/// PARAMS:
/// eChar - the character the play was in this mission
/// RETURNS:
/// TRUE if script should terminate, FALSE otherwise
FUNC BOOL SHOULD_POST_RC_SCRIPT_TERMINATE(enumCharacterList eChar)
IF NOT (GET_CURRENT_PLAYER_PED_ENUM() = eChar)
OR IS_CURRENTLY_ON_MISSION_OF_ANY_TYPE()
OR IS_MISSION_LEADIN_ACTIVE()
OR g_iOffMissionCutsceneRequestActive <> NULL_OFFMISSION_CUTSCENE_REQUEST
IF NOT (GET_CURRENT_PLAYER_PED_ENUM() = CHAR_FRANKLIN)
CPRINTLN(DEBUG_RANDOM_CHAR, "POSTRC Script termination! - swapped character. ", GET_THIS_SCRIPT_NAME())
ENDIF
IF IS_CURRENTLY_ON_MISSION_OF_ANY_TYPE()
CPRINTLN(DEBUG_RANDOM_CHAR, "POSTRC Script termination! - gone on mission. ", GET_THIS_SCRIPT_NAME())
ENDIF
IF IS_MISSION_LEADIN_ACTIVE()
CPRINTLN(DEBUG_RANDOM_CHAR, "POSTRC Script termination! - lead in active. ", GET_THIS_SCRIPT_NAME())
ENDIF
IF g_iOffMissionCutsceneRequestActive <> NULL_OFFMISSION_CUTSCENE_REQUEST
CPRINTLN(DEBUG_RANDOM_CHAR, "POSTRC Script termination! -cut-scene requested. ", GET_THIS_SCRIPT_NAME())
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
FUNC BOOL IS_RC_LEADIN_ACTIVE()
RETURN g_iNoRCLeadinsActive > 0
ENDFUNC