Files
gtav-src/script/dev_ng/singleplayer/scripts/Replay/replay_controller.sc
T
2025-09-29 00:52:08 +02:00

1395 lines
52 KiB
Python
Executable File

// *****************************************************************************************
//
// SCRIPT NAME : replay_controller.sc
// AUTHOR : Andy Minghella
// DESCRIPTION : Controls the setting up and activation of mission replays.
//
// *****************************************************************************************
//Compile out Title Update changes to header functions.
//Must be before includes.
//CONST_INT USE_TU_CHANGES 0 // Removed by Kenneth R.
USING "commands_brains.sch"
USING "replay_public.sch"
USING "shop_public.sch"
USING "RC_Launcher_public.sch"
USING "script_misc.sch"
#IF IS_DEBUG_BUILD
USING "replay_debug.sch"
USING "flow_debug_GAME.sch"
USING "debug_channels_structs.sch"
USING "commands_debug.sch"
BOOL bWaitingForFlowToStart = FALSE // Used to avoid replays being setup just before flow setup begins after using debug menu
#ENDIF
BOOL bForceSkipReplayScreen = FALSE // Used for callbacks since they don't all terminate the script
BOOL bCallbacksSetup = FALSE
BOOL bFadeInOnSkipReplayScreen = FALSE
INT iButtonNumber = 0 // used to display the button prompts in correct order
BOOL bScreenAlreadyFading = FALSE // used in special cases like start of FBI 1
BOOL bSetSystemTime = FALSE //Used to set the scaleform to use the system timer, see B* 2036584
// variables used for handling replay warps
INT iReplayWarpTimer
ENUM REPLAY_SCREEN_BUTTONS
RSB_NONE,
RSB_OK,
RSB_YES,
RSB_RETRY,
RSB_NO,
RSB_EXIT,
RSB_RESTART,
RSB_FORCE_RESTART,
RSB_SKIP_SECTION,
RSB_SKIP_MISSION
ENDENUM
// ===========================================================================================================
// Cleanup
// ===========================================================================================================
/// PURPOSE:
/// Ensures that the script gets a chance to cleanup under specific circumstances (ie: moving from SP to MP)
PROC Script_Cleanup()
CPRINTLN(DEBUG_REPLAY, "Replay controller cleaning up.")
#IF IS_DEBUG_BUILD Delete_Replay_Debug_Widgets() #ENDIF
Cleanup_Replay_Controller()
CPRINTLN(DEBUG_REPLAY, "Replay controller thread terminating now.")
g_replay.replayStageID = RS_NOT_RUNNING
TERMINATE_THIS_THREAD()
ENDPROC
/// PURPOSE:
/// Used to skip over the replay screen, and reset any changes made.
PROC SkipReplayScreen(BOOL bFadeIn #IF IS_DEBUG_BUILD , BOOL bDebugMenuCleanup #ENDIF)
CPRINTLN(DEBUG_REPLAY, "Replay screen was skipped- J/F skip or debug menu cleanup.")
IF bFadeIn
DO_SCREEN_FADE_IN(0)
ENDIF
SET_SCRIPT_GFX_DRAW_ORDER(GFX_ORDER_AFTER_HUD)
ResetCommonReplayChanges()
// If we were part way through setting up a replay we need to handle it as rejected
IF IS_REPLAY_BEING_PROCESSED()
// set state to one which passes GET_MISSION_FLOW_SAFE_TO_CLEANUP + wait for cleanup
g_replay.replayStageID = RS_REJECTING
Wait_For_Mission_To_Cleanup()
CLEAR_TIMECYCLE_MODIFIER()
// then handle it as rejected
CPRINTLN(DEBUG_REPLAY, "SkipReplayScreen: Mission script terminated, treating as rejected", g_replay.replayScriptName)
RESET_MISSION_STATS_SYSTEM()
TERMINATE_STAT_WATCHER() //B* 2194192
Replay_RejectionCleanup()
ELSE
#IF IS_DEBUG_BUILD
// replay isn't being processed,
// need to enter the waiting for flow state
// to avoid trying to setup a replay just as the gameflow setup begins
IF bDebugMenuCleanup = TRUE
IF g_savedGlobals.sFlow.isGameflowActive = TRUE // don't need this in debug mode
bWaitingForFlowToStart = TRUE
g_replay.replayStageID = RS_WAITING_FOR_FLOW
CPRINTLN(DEBUG_REPLAY, "Entering RS_WAITING_FOR_FLOW.")
ENDIF
ENDIF
#ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Draws the chosen button prompt as the next button in the current list
/// PARAMS:
/// eButton - the replay button propt (ok, yes, no, restart etc)
PROC DrawButtonPrompt(REPLAY_SCREEN_BUTTONS eButton)
STRING eButtonIcon
STRING sButtonString
CONTROL_ACTION eButtonClickInput = MAX_INPUTS
SWITCH eButton
CASE RSB_OK
eButtonIcon = GET_CONTROL_INSTRUCTIONAL_BUTTONS_STRING(FRONTEND_CONTROL, INPUT_FRONTEND_ACCEPT)
sButtonString = "REPLAY_OK"
eButtonClickInput = INPUT_FRONTEND_ACCEPT
BREAK
CASE RSB_YES
eButtonIcon = GET_CONTROL_INSTRUCTIONAL_BUTTONS_STRING(FRONTEND_CONTROL, INPUT_FRONTEND_ACCEPT)
sButtonString = "REPLAY_YES"
eButtonClickInput = INPUT_FRONTEND_ACCEPT
BREAK
CASE RSB_RETRY
eButtonIcon = GET_CONTROL_INSTRUCTIONAL_BUTTONS_STRING(FRONTEND_CONTROL, INPUT_FRONTEND_ACCEPT)
sButtonString = "REPLAY_RETRY"
eButtonClickInput = INPUT_FRONTEND_ACCEPT
BREAK
CASE RSB_NO
eButtonIcon = GET_CONTROL_INSTRUCTIONAL_BUTTONS_STRING(FRONTEND_CONTROL, INPUT_FRONTEND_CANCEL)
sButtonString = "REPLAY_NO"
eButtonClickInput = INPUT_FRONTEND_CANCEL
BREAK
CASE RSB_EXIT
eButtonIcon = GET_CONTROL_INSTRUCTIONAL_BUTTONS_STRING(FRONTEND_CONTROL, INPUT_FRONTEND_CANCEL)
sButtonString = "REPLAY_EXIT"
eButtonClickInput = INPUT_FRONTEND_CANCEL
BREAK
CASE RSB_RESTART
eButtonIcon = GET_CONTROL_INSTRUCTIONAL_BUTTONS_STRING(FRONTEND_CONTROL, INPUT_FRONTEND_Y)
sButtonString = "REPLAY_RESTART"
eButtonClickInput = INPUT_FRONTEND_Y
BREAK
CASE RSB_FORCE_RESTART
eButtonIcon = GET_CONTROL_INSTRUCTIONAL_BUTTONS_STRING(FRONTEND_CONTROL, INPUT_FRONTEND_ACCEPT)
sButtonString = "REPLAY_RESTART"
eButtonClickInput = INPUT_FRONTEND_ACCEPT
BREAK
CASE RSB_SKIP_SECTION
eButtonIcon = GET_CONTROL_INSTRUCTIONAL_BUTTONS_STRING(FRONTEND_CONTROL, INPUT_FRONTEND_X)
sButtonString = "REPLAY_SKIP_S"
eButtonClickInput = INPUT_FRONTEND_X
BREAK
CASE RSB_SKIP_MISSION
eButtonIcon = GET_CONTROL_INSTRUCTIONAL_BUTTONS_STRING(FRONTEND_CONTROL, INPUT_FRONTEND_X)
sButtonString = "REPLAY_SKIP_M"
eButtonClickInput = INPUT_FRONTEND_X
BREAK
DEFAULT // no button, exit
EXIT
BREAK
ENDSWITCH
// set data slot info for this button
BEGIN_SCALEFORM_MOVIE_METHOD(g_replay.mFailButtonsScaleform, "SET_DATA_SLOT")
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(iButtonNumber)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INSTRUCTIONAL_BUTTONS(eButtonIcon)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_STRING(sButtonString)
// Add clickable input [PC platform only]
IF IS_PC_VERSION()
IF eButtonClickInput = MAX_INPUTS
SCALEFORM_MOVIE_METHOD_ADD_PARAM_BOOL(FALSE)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(ENUM_TO_INT(MAX_INPUTS))
ELSE
SCALEFORM_MOVIE_METHOD_ADD_PARAM_BOOL(TRUE)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(ENUM_TO_INT(eButtonClickInput))
ENDIF
ENDIF
END_SCALEFORM_MOVIE_METHOD()
CPRINTLN(DEBUG_REPLAY, "button prompt set up for button: ", iButtonNumber)
iButtonNumber++
ENDPROC
/// PURPOSE:
/// Checks if we are currently handling shitskips
/// (Shitskips not handled during restart confirmation screens)
/// RETURNS:
/// True if we are handling shitskips
FUNC BOOL HandleShitskips()
IF g_bShitskipOffered = TRUE
AND g_replay.replayStageID <> RS_SCREEN_RESTART_CONFIRMATION // don't show shitskip on confirmation screen
RETURN TRUE
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks whether shitskips are allowed for this mission type
/// And whether this mission has been failed enough for a shitskip
/// If so it displays the correct button prompt (skip section / mission)
/// PARAMS:
/// bShitskipsAllowed - is this type of replay screen allowed to show the "skip" option?
PROC DrawShitskipButtonPrompts(BOOL bShitskipsAllowed = TRUE)
IF bShitskipsAllowed = TRUE
IF HandleShitskips()
IF g_bShitskipToEnd = TRUE
DrawButtonPrompt(RSB_SKIP_MISSION)
ELSE
DrawButtonPrompt(RSB_SKIP_SECTION)
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Returns whether the button prompts scaleform movie has loaded
/// This needs to be checked before setting up any button prompts
/// RETURNS:
/// TRUE if scaleform has loaded, FALSE otherwise
FUNC BOOL HasButtonsScaleformLoaded()
RETURN HAS_SCALEFORM_MOVIE_LOADED(g_replay.mFailButtonsScaleform)
ENDFUNC
/// PURPOSE:
/// Requests the fail screen scaleform
PROC RequestScaleform()
// main scaleform
IF g_replay.mFailTextScaleform = NULL
g_replay.mFailTextScaleform = REQUEST_SCALEFORM_MOVIE("MP_BIG_MESSAGE_FREEMODE")
bSetSystemTime = FALSE
ELSE
IF NOT HAS_SCALEFORM_MOVIE_LOADED(g_replay.mFailTextScaleform)
g_replay.mFailTextScaleform = REQUEST_SCALEFORM_MOVIE("MP_BIG_MESSAGE_FREEMODE")
ENDIF
ENDIF
// instructional buttons
IF g_replay.mFailButtonsScaleform = NULL
g_replay.mFailButtonsScaleform = REQUEST_SCALEFORM_MOVIE("INSTRUCTIONAL_BUTTONS")
ELSE
IF NOT HAS_SCALEFORM_MOVIE_LOADED(g_replay.mFailButtonsScaleform)
g_replay.mFailButtonsScaleform = REQUEST_SCALEFORM_MOVIE("INSTRUCTIONAL_BUTTONS")
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Draws button prompts for 2 buttons
/// Then handles the shitskip button prompts
/// (This ensures that the skip options are always the last button prompts)
/// PARAMS:
/// eButton1 - 1st button to display
/// eButton2 - 2nd button to display
PROC DrawButtonPrompts(REPLAY_SCREEN_BUTTONS eButton1, REPLAY_SCREEN_BUTTONS eButton2, BOOL bAllowShitskips = TRUE)
// check if player has switched between mouse/joypad
IF IS_PC_VERSION()
IF HAVE_CONTROLS_CHANGED(FRONTEND_CONTROL)
CLEAR_BIT(g_replay.iReplayBits, ENUM_TO_INT(RB_BUTTONS_SETUP))
ENDIF
IF IS_USING_KEYBOARD_AND_MOUSE(FRONTEND_CONTROL)
SET_MOUSE_CURSOR_THIS_FRAME()
ENDIF
ENDIF
IF NOT IS_BIT_SET(g_replay.iReplayBits, ENUM_TO_INT(RB_BUTTONS_SETUP))
IF HasButtonsScaleformLoaded()
// only do the setup once the scaleform has loaded
// reset the button number (so buttons get displayed in correct order)
iButtonNumber = 0
// clear all
BEGIN_SCALEFORM_MOVIE_METHOD(g_replay.mFailButtonsScaleform, "CLEAR_ALL")
END_SCALEFORM_MOVIE_METHOD()
// clear space
BEGIN_SCALEFORM_MOVIE_METHOD(g_replay.mFailButtonsScaleform, "SET_CLEAR_SPACE")
SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(200)
END_SCALEFORM_MOVIE_METHOD()
// set background colour
BEGIN_SCALEFORM_MOVIE_METHOD(g_replay.mFailButtonsScaleform, "SET_BACKGROUND_COLOUR")
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(0)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(0)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(0)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(80)
END_SCALEFORM_MOVIE_METHOD()
// enable clickable input [PC platform only]
IF IS_PC_VERSION()
BEGIN_SCALEFORM_MOVIE_METHOD(g_replay.mFailButtonsScaleform, "TOGGLE_MOUSE_BUTTONS")
SCALEFORM_MOVIE_METHOD_ADD_PARAM_BOOL(TRUE)
END_SCALEFORM_MOVIE_METHOD()
ENDIF
// Set up each button
DrawButtonPrompt(eButton1)
DrawButtonPrompt(eButton2)
DrawShitskipButtonPrompts(bAllowShitskips)
CPRINTLN(DEBUG_REPLAY, "Replay button prompts have been setup.")
SET_BIT(g_replay.iReplayBits, ENUM_TO_INT(RB_BUTTONS_SETUP))
// draw the buttons
BEGIN_SCALEFORM_MOVIE_METHOD(g_replay.mFailButtonsScaleform, "DRAW_INSTRUCTIONAL_BUTTONS")
END_SCALEFORM_MOVIE_METHOD()
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Displays the question text if we have any, otherwise the fail reason
/// PARAMS:
/// sQuestionText - text label of the question text to use
PROC HANDLE_SECONDARY_FAIL_TEXT(STRING sQuestionText)
IF NOT IS_STRING_NULL_OR_EMPTY(sQuestionText)
// if we have question text, display it
SCALEFORM_MOVIE_METHOD_ADD_PARAM_STRING(sQuestionText)
ELSE
// otherwise show fail reason
#IF IS_DEBUG_BUILD
// show a debug fail reason if we don't have one set
IF IS_STRING_NULL_OR_EMPTY(g_txtMissionFailReason)
g_txtMissionFailReason= "REPLAY_NFR"
CPRINTLN(DEBUG_REPLAY, "Setting fail reason to debug fail.")
ENDIF
#ENDIF
// handle string insertions
IF IS_STRING_NULL_OR_EMPTY(g_txtMissionFailAddText)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_STRING(g_txtMissionFailReason)
ELSE
BEGIN_TEXT_COMMAND_SCALEFORM_STRING(g_txtMissionFailReason)
ADD_TEXT_COMPONENT_SUBSTRING_TEXT_LABEL(g_txtMissionFailAddText)
END_TEXT_COMMAND_SCALEFORM_STRING()
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Draws the replay screen
/// PARAMS:
/// sQuestionText - the question text to be displayed
/// bButtonPrompts - display the button prompts
/// bShowFailReason- If true we show the fail reason, otherwise we show the question text.
/// bDoAudio - only true while we need to play the failed audio (before screen faded out)
PROC DrawReplayScreen(STRING sQuestionText, BOOL bButtonPrompts = TRUE, BOOL bDoAudio = FALSE)
RequestScaleform()
SET_SCRIPT_GFX_DRAW_ORDER(GFX_ORDER_AFTER_FADE)
// fail screen scaleforms
IF NOT IS_BIT_SET(g_replay.iReplayBits, ENUM_TO_INT(RB_TEXT_SCALE_LOADED))
IF HAS_SCALEFORM_MOVIE_LOADED(g_replay.mFailTextScaleform)
// set bit once scaleform has loaded
IF NOT IS_BIT_SET(g_replay.iReplayBits, ENUM_TO_INT(RB_TEXT_SCALE_LOADED))
SET_BIT(g_replay.iReplayBits, ENUM_TO_INT(RB_TEXT_SCALE_LOADED))
// set transition time
g_replay.fTextTransitionTime = GET_GAME_TIMER() + (FAIL_TRANSITION_DELAY * FAIL_OUT_EFFECT_SLO_MO)
ENDIF
//Set scaleform to use system timer
IF NOT bSetSystemTime
CPRINTLN(debug_replay,"Setting scaleform to use system timer")
SET_SCALEFORM_MOVIE_TO_USE_SYSTEM_TIME(g_replay.mFailTextScaleform,TRUE)
bSetSystemTime = true
ENDIF
ELSE
CPRINTLN(DEBUG_REPLAY, "Loading replay scaleform movie, mFailTextScaleform.")
ENDIF
ENDIF
// only do the fail text if scaleform has loaded
IF HAS_SCALEFORM_MOVIE_LOADED(g_replay.mFailTextScaleform)
// draw the scaleform
DRAW_SCALEFORM_MOVIE_FULLSCREEN(g_replay.mFailTextScaleform,255,255,255,0)
//#2053591 - Don't record footage for the Rockstar Editor while drawing a fail screen.
REPLAY_PREVENT_RECORDING_AND_UI_THIS_FRAME()
IF NOT IS_BIT_SET(g_replay.iReplayBits, ENUM_TO_INT(RB_QUESTION_SETUP))
// set info for fail text
CPRINTLN(DEBUG_REPLAY, "Updating info for scaleform, mFailTextScaleform.")
BEGIN_SCALEFORM_MOVIE_METHOD(g_replay.mFailTextScaleform, "SHOW_SHARD_CENTERED_MP_MESSAGE_LARGE")
IF g_replay.replayType = REPLAY_TYPE_MINIGAME
OR IS_REPLAY_MISSION_A_RAMPAGE()
// minigames, bail bonds + rampages just say failed
SCALEFORM_MOVIE_METHOD_ADD_PARAM_STRING("REPLAY_TMG")
ELSE
SCALEFORM_MOVIE_METHOD_ADD_PARAM_STRING("REPLAY_T") // mission failed
ENDIF
HANDLE_SECONDARY_FAIL_TEXT(sQuestionText)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(0)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(100.0)
SCALEFORM_MOVIE_METHOD_ADD_PARAM_BOOL(TRUE)
END_SCALEFORM_MOVIE_METHOD()
SET_BIT(g_replay.iReplayBits, ENUM_TO_INT(RB_QUESTION_SETUP))
SET_BIT(g_replay.iReplayBits, ENUM_TO_INT(RB_2ND_TEXT_SETUP))
CPRINTLN(DEBUG_REPLAY, "Updated Fail text with '",sQuestionText)
ENDIF
// handle the secondary text (fail reason / question)
IF NOT IS_BIT_SET(g_replay.iReplayBits, ENUM_TO_INT(RB_2ND_TEXT_SETUP))
BEGIN_SCALEFORM_MOVIE_METHOD(g_replay.mFailTextScaleform, "UPDATE_MESSAGE")
HANDLE_SECONDARY_FAIL_TEXT(sQuestionText)
END_SCALEFORM_MOVIE_METHOD_RETURN_VALUE()
CPRINTLN(DEBUG_REPLAY, "Updating secondary text now with '",sQuestionText)
SET_BIT(g_replay.iReplayBits, ENUM_TO_INT(RB_2ND_TEXT_SETUP))
ENDIF
// transition the fail message up the screen
IF NOT IS_BIT_SET(g_replay.iReplayBits, ENUM_TO_INT(RB_DONE_TRANSITION))
// if we've paused before transition has finished- unpause
Replay_Pause_Game(FALSE)
IF NOT IS_BIT_SET(g_replay.iReplayBits, ENUM_TO_INT(RB_STARTED_TRANSITION))
CDEBUG1LN(debug_replay,"Started transition bit not set: ",GET_GAME_TIMER(),"/",g_replay.fTextTransitionTime)
// transition hasn't started yet, is it time?
IF GET_GAME_TIMER() > g_replay.fTextTransitionTime
CDEBUG1LN(debug_replay,"Starting transition")
// start the transition now
BEGIN_SCALEFORM_MOVIE_METHOD(g_replay.mFailTextScaleform, "TRANSITION_UP")
SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(0.15 * FAIL_OUT_EFFECT_SLO_MO)
END_SCALEFORM_MOVIE_METHOD_RETURN_VALUE()
SET_BIT(g_replay.iReplayBits, ENUM_TO_INT(RB_STARTED_TRANSITION))
// set the time that the transition will be complete
g_replay.fTextTransitionTime = GET_GAME_TIMER() + (150 * FAIL_OUT_EFFECT_SLO_MO)
CPRINTLN(DEBUG_REPLAY, "Triggering text transition now 1")
ELSE
CPRINTLN(DEBUG_REPLAY, "Waiting to do text transition: ", GET_GAME_TIMER())
ENDIF
ENDIF
//Bug 1926954: If the fade-out is hurried up, make sure the RB_DONE_TRANSITION bit is set in the same frame
IF IS_BIT_SET(g_replay.iReplayBits, ENUM_TO_INT(RB_STARTED_TRANSITION))
CDEBUG1LN(debug_replay,"Checking if transition finished:", GET_GAME_TIMER(),"/",g_replay.fTextTransitionTime)
// transition has been started, has it finished?
IF GET_GAME_TIMER() > g_replay.fTextTransitionTime
CPRINTLN(DEBUG_REPLAY, "Text transition finished")
SET_BIT(g_replay.iReplayBits, ENUM_TO_INT(RB_DONE_TRANSITION))
IF bButtonPrompts = TRUE
// if we're not paused after started showing button prompts, pause now
CPRINTLN(DEBUG_REPLAY, "Pausing as transition done, as we're showing button prompts")
Replay_Pause_Game(TRUE)
ENDIF
ENDIF
ENDIF
ENDIF
IF bDoAudio = TRUE
// play the audio as the text appears
IF IS_BIT_SET(g_replay.iReplayBits, ENUM_TO_INT(RB_AUDIO_LOADED))
// don't try to play audio after we've faded out
IF IS_SCREEN_FADED_OUT()
AND IS_BIT_SET(g_replay.iReplayBits, ENUM_TO_INT(RB_DONE_TRANSITION))
// already faded out, don't do audio
CPRINTLN(DEBUG_REPLAY, "Already faded and done transition. Don't do Texthit audio")
ELSE
IF g_replay.iFailSoundID = -1
g_replay.iFailSoundID = GET_SOUND_ID()
PLAY_SOUND_FRONTEND(g_replay.iFailSoundID, "TextHit", "MissionFailedSounds")
CPRINTLN(DEBUG_REPLAY, "Playing fail audio now. TextHit")
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
IF bButtonPrompts = TRUE
IF iButtonNumber > 0
// button prompts (only do this if we've set a button to display)
IF NOT HasButtonsScaleformLoaded()
CPRINTLN(DEBUG_REPLAY, "Loading replay scaleform movie, mFailButtonsScaleform.")
EXIT
ENDIF
DRAW_SCALEFORM_MOVIE_FULLSCREEN(g_replay.mFailButtonsScaleform, 255,255,255,0)
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Resets scaleform setup variables as we need to change text / button prompts
/// Updates state to go to correct screen
PROC ChangeReplayScreen(g_eReplayStages eStage)
CPRINTLN(DEBUG_REPLAY, "Changing replay screen to ", eStage)
ResetScaleformSetupVariables()
g_replay.replayStageID = eStage
ENDPROC
/// PURPOSE:
/// Sets the replay stage to show correct fail screen based on which replay type we are processing
PROC SetReplayScreenType()
SWITCH g_replay.replayType
CASE REPLAY_TYPE_MISSION_FORCE_RESTART
IF IS_REPEAT_PLAY_ACTIVE()
// in repeat play, just use the default screen for this mission
ChangeReplayScreen(RS_SCREEN_DEFAULT)
ELSE
ChangeReplayScreen(RS_SCREEN_FORCE_RESTART)
ENDIF
BREAK
CASE REPLAY_TYPE_FAIL_SCREEN_ONLY
ChangeReplayScreen(RS_SCREEN_FAIL_ONLY)
BREAK
DEFAULT
ChangeReplayScreen(RS_SCREEN_DEFAULT)
// this currently covers these replay types:
// REPLAY_TYPE_MISSION, REPLAY_TYPE_RANDOM_CHARACTER, REPLAY_TYPE_MINIGAME
// REPLAY_TYPE_MICHAEL_EVENT, REPLAY_TYPE_PHONECALL_TRIGGER
BREAK
ENDSWITCH
ENDPROC
// ===========================================================================================================
// Replay Progression Control Functions
// ===========================================================================================================
/// PURPOSE:
/// Shows the fail reason and triggers fail effects
/// If the player was killed / arrested it waits for code to fade the screen out
/// Moves on to RS_SCREEN_FORCE_RESTART, RS_SCREEN_FAIL_ONLY or RS_SCREEN_DEFAULT
PROC Replay_Do_Blur()
// TimerA is reset when fail effect is triggered
// and is used to time triggering of fail out effect
// TimerB is reset when failOut effect is triggered
// and is used to time when to fade out
// debug stuff
#IF IS_DEBUG_BUILD
IF g_bDebugDisableFailScreen
//Replay screens blocked. Cancel replay.
Reset_All_Replay_Variables()
EXIT
ENDIF
#ENDIF
// Trigger the death fail effect here
IF NOT IS_BIT_SET(g_replay.iReplayBits, ENUM_TO_INT(RB_FAIL_EFFECT_TRIGGERED))
AND NOT IS_BIT_SET(g_replay.iReplayBits, ENUM_TO_INT(RB_FAIL_OUT_EFFECT_TRIGGERED))
IF IS_SCREEN_FADED_OUT()
// if we're already faded out, skip to showing the fail reason
// special case for FBI 1 fail cut-scene
CPRINTLN(DEBUG_REPLAY, "Skipping initial fail effect as screen is faded out")
SET_BIT(g_replay.iReplayBits, ENUM_TO_INT(RB_FAIL_EFFECT_TRIGGERED))
SETTIMERA(CEIL(FAIL_EFFECT_TIME * FAIL_EFFECT_SLO_MO))
Replay_Control_Slow_Motion(TRUE, 1)
ELSE
// not started fail effect, do it now
Replay_Control_Fail_Effect(TRUE)
PLAY_SOUND_FRONTEND(-1, "ScreenFlash", "MissionFailedSounds")
CPRINTLN(DEBUG_REPLAY, "Playing fail audio now. ScreenFlash")
SETTIMERA(0)
ENDIF
// start audio scene as effect appears
Replay_Control_Audio_Scene(TRUE)
// ------------other stuff that only needs to be done once--------------
DISABLE_CELLPHONE(TRUE) //Added for bug 1591786
// block the death jingle, so we don't get 2 fail sounds played
// if the player dies during the fail screen
Replay_Block_Death_Jingle(TRUE)
Replay_Block_Damage_Overlay(TRUE)
// clear stunt jump display
CANCEL_STUNT_JUMP()
Replay_Block_Load_Screen(TRUE)
// check if screen is already fading out (used for special cases like start of FBI1)
IF IS_SCREEN_FADING_OUT()
OR IS_SCREEN_FADED_OUT()
bScreenAlreadyFading = TRUE
// block load screen so failed screen displays correctly (needs to be done as close to fade out as possible)
ELSE
bScreenAlreadyFading = FALSE
ENDIF
// B*1575302 - We need to clear the mission title during a fail
MISSION_FLOW_CLEAR_DISPLAY_MISSION_TITLE()
// kill chase hint cam effects
IF IS_GAMEPLAY_HINT_ACTIVE()
CPRINTLN(DEBUG_REPLAY, "Stopping hint effects")
ANIMPOSTFX_STOP("FocusIn")
ANIMPOSTFX_PLAY("FocusOut",0,false)
STOP_GAMEPLAY_HINT()
ENDIF
//Unblock the respawn controller
CPRINTLN(DEBUG_REPLAY,"Unblocking the respawn controller")
CLEAR_BIT(g_replay.iReplayBits,ENUM_TO_INT(RB_BLOCK_RESPAWN))
ENDIF
// things we need to block every frame
// remove any god text / subtitles
IF NOT IS_CUTSCENE_PLAYING()
CLEAR_PRINTS()
ELSE
// block load screen so failed screen displays correctly
// (needs to be done as close to fade out as possible)
Replay_Block_Load_Screen(TRUE)
ENDIF
// hide the feed
THEFEED_HIDE_THIS_FRAME()
// disable the pause menu during fail screen
DISABLE_CONTROL_ACTION(FRONTEND_CONTROL, INPUT_FRONTEND_PAUSE)
// block aggresive cop clean-up
SUPPRESS_AMBIENT_PED_AGGRESSIVE_CLEANUP_THIS_FRAME()
//#2067723 - Ensure the game is in 3rd person view on all fail screens.
DISABLE_CONTROL_ACTION(FRONTEND_CONTROL, INPUT_NEXT_CAMERA)
DISABLE_ON_FOOT_FIRST_PERSON_VIEW_THIS_UPDATE()
// stop player from being able to right upside down vehicles
IF IS_BIT_SET(g_replay.iReplayBits, ENUM_TO_INT(RB_DISABLED_VEH_CONT))
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_MOVE_LR)
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_VEH_MOVE_UD)
ENDIF
// don't let the player use the sniper scope (since HUD needs to be hidden)
IF DOES_ENTITY_EXIST(PLAYER_PED_ID())
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
WEAPON_TYPE mCurrentWeapon
GET_CURRENT_PED_WEAPON(PLAYER_PED_ID(), mCurrentWeapon)
IF mCurrentWeapon = WEAPONTYPE_SNIPERRIFLE
OR mCurrentWeapon = WEAPONTYPE_HEAVYSNIPER
OR mCurrentWeapon = WEAPONTYPE_REMOTESNIPER
DISABLE_CONTROL_ACTION(PLAYER_CONTROL, INPUT_AIM)
ENDIF
ENDIF
ENDIF
// disable weapon switching
Disable_Weapon_Select(TRUE)
// load the fail screen audio + play sound as soon as loaded
IF Load_Replay_Audio()
IF NOT IS_SCREEN_FADED_OUT()
IF g_replay.iFailBedSoundID = -1
g_replay.iFailBedSoundID = GET_SOUND_ID()
PLAY_SOUND_FRONTEND(g_replay.iFailBedSoundID, "Bed", "MissionFailedSounds")
CPRINTLN(DEBUG_REPLAY, "Playing fail audio now. Bed")
ENDIF
ENDIF
ENDIF
// check if it is time to start showing fail text
CDEBUG1LN(debug_replay,"Show fail text time: ",TIMERA(),"/",FAIL_EFFECT_TIME * FAIL_EFFECT_SLO_MO)
IF TIMERA() > FAIL_EFFECT_TIME * FAIL_EFFECT_SLO_MO
// Display the mission failed text
DrawReplayScreen("", FALSE, TRUE)
// Trigger the Death Fail Out Effect here
IF NOT IS_BIT_SET(g_replay.iReplayBits, ENUM_TO_INT(RB_FAIL_OUT_EFFECT_TRIGGERED))
// not started fail out effect, do it now + pause the game
Replay_Block_Load_Screen(TRUE) //Block the load screen early
Replay_Control_Fail_Out_Effect(TRUE)
SETTIMERB(0)
ELSE
// we are waiting for the fail out effect to finish
// check if effect has finished
CDEBUG1LN(debug_replay,"---Check if effect has finished: ",TIMERB(),"/",FAIL_OUT_EFFECT_TIME * FAIL_OUT_EFFECT_SLO_MO)
IF TIMERB() > FAIL_OUT_EFFECT_TIME * FAIL_OUT_EFFECT_SLO_MO
OR (IS_SCREEN_FADED_OUT() // used for skipping fail cut-scenes
AND bScreenAlreadyFading = FALSE) // if screen was already fading out for a special case, wait as normal
CDEBUG1LN(debug_replay,"Finished fadeout effect")
// --------finished fail out effect ------------------------------
// start fading out
IF NOT IS_SCREEN_FADED_OUT()
IF NOT IS_SCREEN_FADING_OUT()
AND NOT IS_CUTSCENE_PLAYING()
SET_SCRIPT_GFX_DRAW_ORDER(GFX_ORDER_AFTER_FADE)
DO_SCREEN_FADE_OUT(FAIL_FADE_OUT_TIME)
// block load screen so failed screen displays correctly (needs to be done as close to fade out as possible)
Replay_Block_Load_Screen(TRUE)
ENDIF
ELSE
// -------------screen has faded out--------------------------
// Do necessary setup, and then move on to showing the replay screen
CPRINTLN(DEBUG_REPLAY, "\nFail effect for script [", GET_THIS_SCRIPT_NAME(), "] completed.")
// disable player control
IF IS_PLAYER_PLAYING(PLAYER_ID())
SET_PLAYER_CONTROL(PLAYER_ID(), FALSE)
ENDIF
// stuff that needs to be set after fade
SET_SCRIPTS_SAFE_FOR_CUTSCENE(TRUE)
DISABLE_CELLPHONE(TRUE)
SET_FRONTEND_ACTIVE(FALSE)
SET_PAUSE_MENU_ACTIVE(FALSE)
CLEAR_HELP()
CLEAR_PRINTS()
Replay_Control_Audio_Scene(FALSE)
RESET_RETICULE_VALUES()
IF IS_GAMEPLAY_CAM_SHAKING()
STOP_GAMEPLAY_CAM_SHAKING(TRUE)
ENDIF
KILL_ANY_CONVERSATION()
IF IS_PLAYER_SWITCH_IN_PROGRESS()
STOP_PLAYER_SWITCH()
CPRINTLN(DEBUG_REPLAY, "Stopping current player switch")
ENDIF
STOP_ALL_ALARMS(TRUE)
// ------Wait here for a short time before updating the fail text---------
// brief delay before showing the button prompts
//DrawReplayScreen("", FALSE)
FLOAT fDelayTimer = GET_GAME_TIMER() + (FAIL_PROMPT_DELAY *FAIL_OUT_EFFECT_SLO_MO)
WHILE GET_GAME_TIMER() < fDelayTimer
//B*1926954: Keep waiting until the text transition finishes
OR GET_GAME_TIMER() < g_replay.fTextTransitionTime
WAIT(0)
DrawReplayScreen("", FALSE)
ENDWHILE
// store vehicle's velocity before pause
IF DOES_ENTITY_EXIST(PLAYER_PED_ID())
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
VEHICLE_INDEX mPlayerVehicle = GET_PLAYERS_LAST_VEHICLE()
IF DOES_ENTITY_EXIST(mPlayerVehicle)
IF IS_VEHICLE_DRIVEABLE(mPlayerVehicle)
g_replay.vVehicleVelocity = GET_ENTITY_VELOCITY(mPlayerVehicle)
CPRINTLN(DEBUG_REPLAY, "Stored velocity as g_replay.vVehicleVelocity x= ", g_replay.vVehicleVelocity.x, " y= ", g_replay.vVehicleVelocity.y, " z= ", g_replay.vVehicleVelocity.z)
ENDIF
ENDIF
ENDIF
ENDIF
// pause the game (only if we've done the text transition)
IF IS_BIT_SET(g_replay.iReplayBits, ENUM_TO_INT(RB_DONE_TRANSITION))
Replay_Pause_Game(TRUE)
ENDIF
// set which state we're moving on to
// based on which replay type we are configuring
SetReplayScreenType()
ENDIF
ELSE
CDEBUG1LN(DEBUG_REPLAY, "Accepting input, TimerB = .", TIMERB())
// Allow player to skip to the retry options
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_ACCEPT)
AND (TIMERB()<CEIL(FAIL_OUT_EFFECT_TIME * FAIL_OUT_EFFECT_SLO_MO*0.7))
SETTIMERB(CEIL(FAIL_OUT_EFFECT_TIME * FAIL_OUT_EFFECT_SLO_MO*0.7))
CPRINTLN(DEBUG_REPLAY, "Skipping to the retry prompts.")
ENDIF
ENDIF
ENDIF
ELSE
//CPRINTLN(DEBUG_REPLAY, "TimerA = .", TIMERA())
// not drawing fail screen yet, request scaleform (DrawReplayScreen also does this)
RequestScaleform()
ENDIF
ENDPROC
/// PURPOSE:
/// Called every frame while a replay screen is on
/// Hides loading, disables pause menu etc.
PROC DoCommonReplayScreenSetup()
CLEAR_PRINTS() // remove any god text / subtitles
SET_CONTROL_SHAKE(PLAYER_CONTROL, 0, 0) // stop pad vibrating
DISABLE_CONTROL_ACTION(FRONTEND_CONTROL, INPUT_FRONTEND_PAUSE) // disable pause menu
HIDE_LOADING_ON_FADE_THIS_FRAME()
//Has this replay screen been requested to be skipped by debug routines?
#IF IS_DEBUG_BUILD
IF g_bDebugSkipReplayScreen
g_bDebugSkipReplayScreen = FALSE
Replay_Player_Has_Accepted(FALSE)
ENDIF
#ENDIF
ENDPROC
/// PURPOSE:
/// Draws the default mission replay screen and waits for the player to respond
/// REPLAY_PREPARE_ACTIVATION (player has accepted replay)
/// RS_SCREEN_REJECT_CONFIRMATION (player rejected replay on a story / flow mission)
PROC Display_RS_SCREEN_DEFAULT()
STRING sQuestionText
DoCommonReplayScreenSetup()
sQuestionText = ""
DrawButtonPrompts(RSB_RETRY, RSB_EXIT) // fail reason + A:RETRY B:EXIT
DrawReplayScreen(sQuestionText)
//-----------Wait for selection button press--------------------------------------------
IF HandleShitskips()
AND IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_X)
PLAY_SOUND_FRONTEND(-1, "SKIP", "HUD_FRONTEND_DEFAULT_SOUNDSET")
ChangeReplayScreen(RS_SCREEN_SKIP_CONFIRMATION)
ELSE
// not shit skipping- do other options
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_ACCEPT)
PLAY_SOUND_FRONTEND(-1, "RETRY", "HUD_FRONTEND_DEFAULT_SOUNDSET")
Replay_Player_Has_Accepted(FALSE)
ELSE
// handle player rejecting replay
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_CANCEL)
PLAY_SOUND_FRONTEND(-1, "EXIT", "HUD_FRONTEND_DEFAULT_SOUNDSET")
ChangeReplayScreen(RS_SCREEN_REJECT_CONFIRMATION)
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Draws the Force Restart mission reaply screen and waits for the player to respond
/// Moves on to one of these:
/// REPLAY_PREPARE_ACTIVATION (player chose to restart from checkpoint)
/// RS_SCREEN_RESTART_CONFIRMATION (player chose to restart from beginning)
PROC Display_RS_SCREEN_FORCE_RESTART()
STRING sQuestionText
DoCommonReplayScreenSetup()
IF g_replay.replayStageReadOnly > 0
sQuestionText = ""
DrawButtonPrompts(RSB_RETRY, RSB_RESTART) // Fail reason + A: RETRY Y: RESTART
ELSE
sQuestionText = ""
DrawButtonPrompts(RSB_FORCE_RESTART, RSB_NONE) // Fail reason + A: RESTART
ENDIF
DrawReplayScreen(sQuestionText)
//-----------Wait for selection button press--------------------------------------------
IF HandleShitskips()
AND IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_X)
PLAY_SOUND_FRONTEND(-1, "SKIP", "HUD_FRONTEND_DEFAULT_SOUNDSET")
ChangeReplayScreen(RS_SCREEN_SKIP_CONFIRMATION)
ELSE
// not shitskipping- handle other options
IF g_replay.replayStageReadOnly > 0
// retry from checkpoint, or restart
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_ACCEPT)
PLAY_SOUND_FRONTEND(-1, "RETRY", "HUD_FRONTEND_DEFAULT_SOUNDSET")
Replay_Player_Has_Accepted(FALSE)
ELSE
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_Y)
PLAY_SOUND_FRONTEND(-1, "RESTART", "HUD_FRONTEND_DEFAULT_SOUNDSET")
// player is choosing restart over checkpoint- give confirmation screen
ChangeReplayScreen(RS_SCREEN_RESTART_CONFIRMATION)
ENDIF
ENDIF
ELSE
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_ACCEPT)
PLAY_SOUND_FRONTEND(-1, "RESTART", "HUD_FRONTEND_DEFAULT_SOUNDSET")
// player is choosing to restart, when he has made no progress, same as accepting replay
Replay_Player_Has_Accepted(FALSE)
ENDIF
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Draws the restart confirmation screen and waits for the player to respond
/// Moves on to one of these:
/// REPLAY_PREPARE_ACTIVATION (player has accepted restarting from the beginning)
/// RS_SCREEN_FORCE_RESTART (player has cancelled restarting from begininng, back to previous screen)
PROC Display_RS_SCREEN_RESTART_CONFIRMATION()
DoCommonReplayScreenSetup()
DrawButtonPrompts(RSB_YES,RSB_NO, FALSE) // Restarting the mission will result in losing checkpoint progress. Are you sure you want to restart? A: YES B: NO
DrawReplayScreen("REPLAY_AYS")
//-----------Wait for selection button press--------------------------------------------
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_ACCEPT)
PLAY_SOUND_FRONTEND(-1, "YES", "HUD_FRONTEND_DEFAULT_SOUNDSET")
Replay_Player_Has_Accepted(TRUE)
ELSE
// handle player rejecting replay
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_CANCEL)
PLAY_SOUND_FRONTEND(-1, "NO", "HUD_FRONTEND_DEFAULT_SOUNDSET")
// rejecting a restart confirmation screen takes you back to the force restart screen
ChangeReplayScreen(RS_SCREEN_FORCE_RESTART)
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Draws the replay rejection confirmation screen and waits for the player to respond
/// Moves on to one of these:
/// RS_NOT_REQUIRED (player has confirmed rejecting the non story / flow mission)
/// RS_REJECTED (player has confirmed rejecting the story / flow mission)
/// PREVIOUS SCREEN: If player cancels rejection we return to one of the other fail screens
/// Currently if cancelled this always goes back to RS_SCREEN_DEFAULT as it is only one that offers replay rejection
PROC Display_RS_SCREEN_REJECT_CONFIRMATION()
DoCommonReplayScreenSetup()
DrawButtonPrompts(RSB_YES,RSB_NO, FALSE) // Exiting the mission will result in losing mission progress. Are you sure you want to exit? A: YES B: NO
DrawReplayScreen("REPLAY_REJ")
//-----------Wait for selection button press--------------------------------------------
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_ACCEPT)
PLAY_SOUND_FRONTEND(-1, "YES", "HUD_FRONTEND_DEFAULT_SOUNDSET")
CPRINTLN(DEBUG_REPLAY, "Player rejected replay")
Replay_Player_Has_Rejected()
ELSE
// handle player rejecting replay
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_CANCEL)
PLAY_SOUND_FRONTEND(-1, "NO", "HUD_FRONTEND_DEFAULT_SOUNDSET")
// rejecting a restart confirmation screen takes you back to the previous replay screen
CPRINTLN(DEBUG_REPLAY, "Player rejected reject confirmation screen, going back to previous screen")
SetReplayScreenType()
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Draws the shitskip confirmation screen and waits for the player to respond
/// Moves on to one of these:
/// REPLAY_PREPARE_ACTIVATION (player has accepted the shitskip)
/// PREVIOUS SCREEN: If player cancels the shitskip we return to one of the other fail screens
PROC Display_RS_SCREEN_SKIP_CONFIRMATION()
DoCommonReplayScreenSetup()
DrawButtonPrompts(RSB_YES,RSB_NO, FALSE)
IF g_bShitskipToEnd = TRUE
DrawReplayScreen("REPLAY_SKIPM") // Are you sure you want to skip this mission? A: YES B: NO
ELSE
DrawReplayScreen("REPLAY_SKIPS") // Are you sure you want to skip this section? A: YES B: NO
ENDIF
//-----------Wait for selection button press--------------------------------------------
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_ACCEPT)
PLAY_SOUND_FRONTEND(-1, "YES", "HUD_FRONTEND_DEFAULT_SOUNDSET")
CPRINTLN(DEBUG_REPLAY, "Player accepted shitskip")
g_bShitskipAccepted = TRUE
INFORM_MISSION_STATS_SYSTEM_OF_SHITSKIP()
//Update the profile stat tracking how many times this player has skipped a checkpoint.
INT iTimesSkipped
STAT_GET_INT(TIMES_MISSION_SKIPPED, iTimesSkipped)
STAT_SET_INT(TIMES_MISSION_SKIPPED, iTimesSkipped+1)
SET_BIT(g_replay.iReplayBits, ENUM_TO_INT(RB_DID_WE_EVER_SHITSKIP))
Replay_Player_Has_Accepted(FALSE)
ELSE
// handle player rejecting shitskip
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_CANCEL)
PLAY_SOUND_FRONTEND(-1, "NO", "HUD_FRONTEND_DEFAULT_SOUNDSET")
// rejecting a shitskip confirmation screen takes you back to the previous replay screen
CPRINTLN(DEBUG_REPLAY, "Player rejected shitskip confirmation screen, going back to previous screen")
SetReplayScreenType()
ENDIF
ENDIF
ENDPROC
/// PURPOSE:
/// Draws the Fail Only mission reaply screen and waits for the player to respond
/// Moves on to one of these:
/// RS_NOT_REQUIRED (player rejected non story / flow mission)
/// RS_REJECTED (player rejected replay on a story / flow mission)
PROC Display_RS_SCREEN_FAIL_ONLY()
DoCommonReplayScreenSetup()
DrawButtonPrompts(RSB_OK, RSB_NONE, FALSE) // fail reason + A: OK
DrawReplayScreen(" ")
//-----------Wait for selection button press--------------------------------------------
IF IS_CONTROL_JUST_PRESSED(FRONTEND_CONTROL, INPUT_FRONTEND_ACCEPT)
PLAY_SOUND_FRONTEND(-1, "OK", "HUD_FRONTEND_DEFAULT_SOUNDSET")
CPRINTLN(DEBUG_REPLAY, "Player rejected replay- fail only")
Replay_Player_Has_Rejected()
ENDIF
ENDPROC
/// PURPOSE:
/// Sets the bit that lets the mission know it is being replayed
PROC Setup_Mission_Specific_Replay_State()
CPRINTLN(DEBUG_REPLAY, "Setting up Mission replay state.")
// Stop the flow from prestreaming intro mocaps for replayed missions.
g_eMissionIDToLoadCutscene = SP_MISSION_NONE
g_bFlowLoadIntroCutscene = FALSE
g_bFlowLoadRequestStarted = FALSE
g_bFlowCleanupIntroCutscene = TRUE
g_bFlowLoadingIntroCutscene = FALSE
IF IsThisAPrepMission()
CPRINTLN(DEBUG_REPLAY, "Accepted replay for prep mission")
// set replay as rejected for prep missions
// as we don't want them to retrigger (just re-blip)
// also need to clear the candidate ID as the mission needs to be able to retrigger normally
Replay_RejectionCleanup()
ELSE
SET_BIT(g_availableMissions[Get_Available_Mission_Index_For_Stored_Mission(g_replay.replayCoreVarsIndex)].bitflags, BITS_AVAILABLE_MISSION_BEING_REPLAYED)
ENDIF
ENDPROC
/// PURPOSE:
/// Flag a global flag to inform all minigame launchers that a minigame replay is ready to be processed.
PROC Setup_Minigame_Specific_Replay_State()
CPRINTLN(DEBUG_REPLAY, "Setting up Minigame replay state.")
g_bLaunchMinigameReplay = TRUE
ENDPROC
/// PURPOSE:
/// Sets up any requirements for an RC mission to trigger its replay
/// then waits until the replay activates
PROC Setup_Random_Character_Specific_Replay_State()
CPRINTLN(DEBUG_REPLAY, "Setting up Random Character replay state. for ", g_replay.replayScriptName)
//Get RC mission data.
g_eRC_MissionIDs eRCMissionID = Get_RC_MissionID_For_Script(g_replay.replayScriptName)
g_structRCMissionsStatic sRCMissionDetails
Retrieve_Random_Character_Static_Mission_Details(eRCMissionID, sRCMissionDetails)
// Unpause the game here, so we can do the warp / reactivate properly
Replay_Pause_Game(FALSE)
g_structRCScriptArgs sRCLauncherData // Scene information to pass to mission script
RC_Reset_LauncherData(sRCLauncherData)
sRCLauncherData.sScriptName = g_replay.replayScriptName
sRCLauncherData.eMissionID = eRCMissionID
INFORM_MISSION_STATS_SYSTEM_OF_RESTART()
WHILE NOT LAUNCH_RC_MISSION(sRCLauncherData)
WAIT(0)
CPRINTLN(DEBUG_REPLAY, "Waiting for RC mission to relaunch.")
ENDWHILE
CPRINTLN(DEBUG_REPLAY, "RC mission has relaunched.")
SET_RC_AS_RUNNING(eRCMissionID)
ENDPROC
/// PURPOSE:
/// Waits for player to respawn, sets him to have at least a decent amount of health
/// Sets up any specific requirements for this replay type then resets common replay changes (enables phone etc)
/// Moves on to RS_ACTIVE
PROC ReplayActivate()
BOOL bDoFade = TRUE
#IF IS_DEBUG_BUILD
bDoFade = bFadeOut // skip the fade?
#ENDIF
// DO NOT CHECK THIS IN!! DEBUG ONLY!
//#IF IS_DEBUG_BUILD BREAK_ON_NATIVE_COMMAND("NEW_LOAD_SCENE_START_SPHERE", FALSE) #ENDIF
//#IF IS_DEBUG_BUILD BREAK_ON_NATIVE_COMMAND("SET_GAMEPLAY_CAM_RELATIVE_HEADING", FALSE) #ENDIF
//#IF IS_DEBUG_BUILD BREAK_ON_NATIVE_COMMAND("SET_GAMEPLAY_CAM_RELATIVE_PITCH", FALSE) #ENDIF
IF DOES_MISSION_USE_NEW_REPLAY_SYSTEM()
IF IsThisAPrepMission() = FALSE
// for normal replays, don't let the replay controller get paused
// so it can handle the teleport whilst the mission sets up
CPRINTLN(DEBUG_REPLAY, "Using new replay warp system for this mission.")
SET_THIS_SCRIPT_CAN_BE_PAUSED(FALSE)
g_eReplayWarpStage = RWS_WAIT
ENDIF
ENDIF
// Fade out if needed
IF bDoFade = TRUE
AND NOT IS_SCREEN_FADED_OUT()
AND NOT IS_SCREEN_FADING_OUT()
DO_SCREEN_FADE_OUT(0)
ENDIF
// Ensure player health is at a minimum level (ensure this is after the player swap).
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
INT iPlayerHealth = GET_ENTITY_HEALTH(PLAYER_PED_ID())
CPRINTLN(DEBUG_REPLAY, "Current Player Health: ", iPlayerHealth)
IF (iPlayerHealth < MINIMUM_REPLAY_HEALTH)
CPRINTLN(DEBUG_REPLAY, "Health for Replay too low. Being boosted to ", MINIMUM_REPLAY_HEALTH, ".")
SET_ENTITY_HEALTH(PLAYER_PED_ID(), MINIMUM_REPLAY_HEALTH)
ENDIF
ENDIF
// launcher setup
IF DoesThisReplayTypeUseStoryMissionLauncher()
Setup_Mission_Specific_Replay_State()
ELSE
SWITCH g_replay.replayType
CASE REPLAY_TYPE_MINIGAME
Setup_Minigame_Specific_Replay_State()
BREAK
CASE REPLAY_TYPE_RANDOM_CHARACTER
Setup_Random_Character_Specific_Replay_State()
BREAK
DEFAULT
SCRIPT_ASSERT("Replay_Controller: Wrong replay type in Maintain_Wait_Until_Replay_Ready_To_Launch()")
BREAK
ENDSWITCH
ENDIF
IF IsThisAPrepMission() = FALSE
// prep missions handle this stuff via the rejection cleanup
// as they don't actually launch the mission again
//TODO, add check here for invalidation type relating to bug 536824
IF GET_REPLAY_MID_MISSION_STAGE()!= 0
INFORM_MISSION_STATS_SYSTEM_OF_RESTART()
ENDIF
//ELSE
// INFORM_MISSION_STATS_SYSTEM_OF_RESTART(TRUE)
//ENDIF
// reset necessary replay variables
Reset_Fail_Warp_Location()
ResetFailReasons()
ENDIF
ResetCommonReplayChanges()
WAIT(0) // wait needed as we are unpausing
IF IsThisAPrepMission() = FALSE
// prep missions handle this stuff via the rejection cleanup
// as they don't actually launch the mission again
// Replay now active - mission flow should take over now and re-launch the script.
g_replay.replayStageID = RS_ACTIVE
ELSE
// slight delay before fading back in so we don't see repopulated area peds not having anims
WAIT(500)
CPRINTLN(DEBUG_REPLAY, "Fading screen back in for prep mission.")
DO_SCREEN_FADE_IN(DEFAULT_FADE_TIME)
ENDIF
ENDPROC
// ===========================================================================================================
// Script Loop
// ===========================================================================================================
SCRIPT
CPRINTLN(DEBUG_REPLAY, "Replay_controller.sc is now being launched.")
#IF IS_DEBUG_BUILD Initialise_Replay_Debug_Widgets() #ENDIF
WHILE (TRUE)
// ----------------------Handle debug menu callback --------------------------
IF bCallbacksSetup = FALSE
CPRINTLN(DEBUG_REPLAY, "bCallbacksSetup = false.")
bCallbacksSetup = TRUE
bForceSkipReplayScreen = FALSE
bFadeInOnSkipReplayScreen = FALSE
CPRINTLN(DEBUG_REPLAY, "SETTING UP CALLBACKS.")
IF HAS_FORCE_CLEANUP_OCCURRED(FORCE_CLEANUP_FLAG_DEBUG_MENU | FORCE_CLEANUP_FLAG_SP_TO_MP | FORCE_CLEANUP_FLAG_MAGDEMO | FORCE_CLEANUP_FLAG_REPEAT_PLAY)
SWITCH GET_CAUSE_OF_MOST_RECENT_FORCE_CLEANUP()
CASE FORCE_CLEANUP_FLAG_DEBUG_MENU
CPRINTLN(DEBUG_REPLAY, "Replay_controller.sc skipping replay: DebugMenu force cleanup.")
bForceSkipReplayScreen = TRUE
bFadeInOnSkipReplayScreen = TRUE
bCallbacksSetup = FALSE
BREAK
CASE FORCE_CLEANUP_FLAG_REPEAT_PLAY
CPRINTLN(DEBUG_REPLAY, "Replay_controller.sc skipping replay: Repeat Play force cleanup.")
bForceSkipReplayScreen = TRUE
bFadeInOnSkipReplayScreen = FALSE
bCallbacksSetup = FALSE
BREAK
CASE FORCE_CLEANUP_FLAG_SP_TO_MP
CPRINTLN(DEBUG_REPLAY, "Replay_controller.sc has been forced to cleanup SP to MP.")
Script_Cleanup()
BREAK
CASE FORCE_CLEANUP_FLAG_MAGDEMO
CPRINTLN(DEBUG_REPLAY, "Replay_controller.sc has been forced to cleanup Magdemo.")
Script_Cleanup()
BREAK
DEFAULT
// other force cleanups are ignored
BREAK
ENDSWITCH
ENDIF
ENDIF
If bForceSkipReplayScreen = TRUE
CPRINTLN(DEBUG_REPLAY, "Skipping replay screen caused by debug menu force cleanup.")
SkipReplayScreen(bFadeInOnSkipReplayScreen #IF IS_DEBUG_BUILD ,TRUE #ENDIF)
ELSE
// -----------------------Debug stuff--------------------------------
#IF IS_DEBUG_BUILD
Maintain_Replay_Debug_Widgets()
//Allow us to F / J skip replays. (J skip is used by missionTester / autoplaythrough)
IF IS_KEYBOARD_KEY_JUST_PRESSED(KEY_F)
OR IS_KEYBOARD_KEY_JUST_PRESSED(KEY_J)
IF IsReplayWaitingForReplayScreen()
SkipReplayScreen(TRUE #IF IS_DEBUG_BUILD ,FALSE #ENDIF)
ENDIF
ENDIF
#ENDIF
BOOL bCheckState = TRUE
#IF IS_DEBUG_BUILD
// only check replay stage if gameflow isn't being setup
// or we're in the RS_WAITING_FOR_FLOW stage
IF g_flowUnsaved.bUpdatingGameflow = FALSE
OR g_replay.replayStageID = RS_WAITING_FOR_FLOW
bCheckState = TRUE
ELSE
bCheckState = FALSE
// Gameflow is updating, so don't try to set up a new replay
//CPRINTLN(DEBUG_REPLAY, "Gameflow is busy, so pausing replay controller.")
ENDIF
#ENDIF
// ------------Main Replay Flow ------------------------------------------
IF bCheckState = TRUE
SWITCH (g_replay.replayStageID)
// If replay controller loads up in this state move straight into not required
CASE RS_NOT_RUNNING
g_replay.replayStageID = RS_NOT_REQUIRED
BREAK
// show fail reason, wait if necessary then fade out
CASE RS_DO_BLUR
Replay_Do_Blur()
BREAK
// Replay screen for normal missions.
CASE RS_SCREEN_DEFAULT
Display_RS_SCREEN_DEFAULT()
BREAK
// Replay screen for force restart missions.
CASE RS_SCREEN_FORCE_RESTART
Display_RS_SCREEN_FORCE_RESTART()
BREAK
// Replay screen for force restart missions.
CASE RS_SCREEN_RESTART_CONFIRMATION
Display_RS_SCREEN_RESTART_CONFIRMATION()
BREAK
// Replay screen for confirming the player wants to exit the mission.
CASE RS_SCREEN_REJECT_CONFIRMATION
Display_RS_SCREEN_REJECT_CONFIRMATION()
BREAK
// Replay screen for confirming the player wants to skip forwards in the mission.
CASE RS_SCREEN_SKIP_CONFIRMATION
Display_RS_SCREEN_SKIP_CONFIRMATION()
BREAK
// Replay screen for fail screen only missions. No replay options.
CASE RS_SCREEN_FAIL_ONLY
Display_RS_SCREEN_FAIL_ONLY()
BREAK
// Does some setup and terminates the mission thread
CASE RS_ACCEPTING
// This is handled in Replay_Player_Has_Accepted in replay private
// as it needs to run in same frame as player accepting the replay
BREAK
// Does some setup and terminates the mission thread
CASE RS_REJECTING
// This is handled in Replay_Player_Has_Accepted in replay private
// as it needs to run in same frame as player rejecting the replay
BREAK
// Waiting for the replay preparations to finish ready to start the replay
CASE RS_ACTIVATE
ReplayActivate()
BREAK
// The replay is now active - control has been handed over to the replaying activity
CASE RS_ACTIVE
IF DOES_MISSION_USE_NEW_REPLAY_SYSTEM()
// Handle warping the player into position
// Clearing + repopulating area
IF g_eReplayWarpStage <> RWS_DONE
IF REPLAY_WARP_PLAYER(iReplayWarpTimer)
SET_THIS_SCRIPT_CAN_BE_PAUSED(TRUE)
ELSE
/*
#IF IS_DEBUG_BUILD
// do not check in!
IF g_eReplayWarpStage > RWS_START
AND g_eReplayWarpStage < RWS_DONE
// warp ongoing, check for player and camera movement
VECTOR vPos
IF NOT IS_PED_INJURED(PLAYER_PED_ID())
vPos = GET_ENTITY_COORDS(PLAYER_PED_ID())
IF GET_DISTANCE_BETWEEN_COORDS(vPos, g_eReplayWarpPos) > 30
SCRIPT_ASSERT("ReplayWarp: Player moved too far from g_eReplayWarpPos!")
ENDIF
vPos = GET_GAMEPLAY_CAM_COORD()
IF GET_DISTANCE_BETWEEN_COORDS(vPos, g_eReplayWarpPos) > 30
SCRIPT_ASSERT("ReplayWarp: Camera moved too far from g_eReplayWarpPos!")
ENDIF
ENDIF
ENDIF
#ENDIF
*/
ENDIF
ENDIF
ENDIF
BREAK
// The player has rejected the replay, and we have done the teleport etc (story / flow only)
CASE RS_REJECTED
// This is currently only used by story missions to get the mission flow to work correctly
// Flow waits for replay to finish processing,
// Then if it was rejected, cleans up + moves on, otherwise it relaunches mission
BREAK
// Not currently processing a replay and a replay is not being played
CASE RS_NOT_REQUIRED
IF NOT(IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_STORY)
OR IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_STORY_FRIENDS)
OR IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_STORY_PREP)
OR IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_RANDOM_CHAR)
OR IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_MINIGAME)
OR IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_MINIGAME_FRIENDS))
CPRINTLN(DEBUG_REPLAY, "Cleaning up replay controller as there are no missions running to service.")
Script_Cleanup()
ENDIF
BREAK
// debug only states
#IF IS_DEBUG_BUILD
// A debug menu force cleanup was triggred
// Now wait in this state until flow setup is done
// So a replay can't be offered just before flow setup begins
CASE RS_WAITING_FOR_FLOW
IF bWaitingForFlowToStart = TRUE
IF g_flowUnsaved.bUpdatingGameflow = FALSE
//CPRINTLN(DEBUG_REPLAY, "Waiting for flow setup to begin.")
ELSE
bWaitingForFlowToStart = FALSE
//CPRINTLN(DEBUG_REPLAY, "Flow setup has started.")
ENDIF
ELSE
IF g_flowUnsaved.bUpdatingGameflow = FALSE
//CPRINTLN(DEBUG_REPLAY, "Exiting RS_WAITING_FOR_FLOW.")
g_replay.replayStageID = RS_NOT_REQUIRED
// Can also get out of this state by calling CleanupReplayController
ENDIF
ENDIF
BREAK
#ENDIF
DEFAULT
SCRIPT_ASSERT("Replay_Controller: Unknown Replay Stage ID")
BREAK
ENDSWITCH
ENDIF
ENDIF
WAIT (0)
ENDWHILE
// Script should never reach here. Always terminate with cleanup function.
ENDSCRIPT