//╒═════════════════════════════════════════════════════════════════════════════╕ //│ Mission Repeat Controller │ //╞═════════════════════════════════════════════════════════════════════════════╡ //│ │ //│ AUTHOR: Ben Rollinson / Andrew Minghella │ //│ DATE: 13/09/11 │ //│ DESCRIPTION: A system used to trigger mission scripts │ //│ that the player has chosen to replay after │ //│ having already completed them. │ //│ │ //╘═════════════════════════════════════════════════════════════════════════════╛ USING "rage_builtins.sch" USING "globals.sch" USING "snapshot_private.sch" USING "flow_processing_game.sch" USING "mission_repeat_private.sch" USING "savegame_private.sch" USING "RC_launcher_public.sch" USING "flow_processing_missions_game.sch" USING "script_heist.sch" USING "mission_repeat_public.sch" ENUM RP_STATE RP_SETUP, RP_HEIST_PLANNING_BOARD, RP_LAUNCH, RP_RUNNING, RP_PASSED, RP_FAILED ENDENUM // repeat play variables RP_STATE eRPState RP_HEIST_STATE eRPHeistState TEXT_LABEL_31 tScriptName INT iTriggerCharBitset INT iCandidateID = NO_CANDIDATE_ID INT iHeistCrewUnlockBackup // Used for remembering which crew memebers had been unlocked before resetting the flow state. // --------------------------functions ---------------------------------------------------------- /// PURPOSE: /// Moves the player into position and loads the world in /// PARAMS: /// vPos - where we are moving the player /// fHeading - heading the player should use /// bRepopulate - do we want to repropulate the world here? /// RETURNS: /// TRUE if everything loaded ok FUNC BOOL RepeatPlay_Warp_Player(VECTOR vPos, FLOAT fHeading, BOOL bRepopulate) IF IS_PLAYER_PLAYING(PLAYER_ID()) // start the teleport CPRINTLN(DEBUG_REPEAT, "RepeatPlay_Warp_Player started") SET_GAME_PAUSED(FALSE) // turn off emergency streaming (pausing game will ensure player doesnt fall through floor) //SET_GAME_PAUSES_FOR_STREAMING(FALSE) // move player now CLEAR_AREA(vPos, 5.0, TRUE) IF DOES_ENTITY_EXIST(PLAYER_PED_ID()) IF NOT IS_PED_INJURED(PLAYER_PED_ID()) SET_ENTITY_COORDS(PLAYER_PED_ID(), vPos) SET_ENTITY_HEADING(PLAYER_PED_ID(), fHeading) SET_GAMEPLAY_CAM_RELATIVE_PITCH(0.0) SET_GAMEPLAY_CAM_RELATIVE_HEADING(0.0) ENDIF ENDIF NEW_LOAD_SCENE_START_SPHERE(vPos, 600) WAIT(0) // wait a frame for the camera to catch up before pausing // clear wanted level again (in case repeat play triggered in restricted area) IF IS_PLAYER_PLAYING(PLAYER_ID()) CPRINTLN(DEBUG_REPEAT,"Repeat play clearing wanted level now (after warp).") CLEAR_PLAYER_WANTED_LEVEL(PLAYER_ID()) ENDIF // clear the area CLEAR_AREA(vPos, 5000, TRUE, FALSE) DELETE_VEHICLE_GEN_VEHICLES_IN_AREA(vPos, 5000) CLEAR_AREA_OF_OBJECTS(vPos, 5000) REMOVE_PARTICLE_FX_IN_RANGE(vPos, 5000) REMOVE_DECALS_IN_RANGE(vPos, 5000) CLEAR_ALL_MUST_LEAVE_AREA_VEHICLE_GEN_FLAGS() RESET_VEHICLE_GEN_LOADED_CHECKS() // start timer + pause game SETTIMERA(0) SET_GAME_PAUSED(TRUE) // try to create the new load scene IF NOT IS_NEW_LOAD_SCENE_ACTIVE() WHILE NOT NEW_LOAD_SCENE_START_SPHERE(vPos, REPLAY_LOAD_SCENE_SIZE) WAIT(0) IF TIMERA() > 2000 CPRINTLN(DEBUG_REPEAT, "RepeatPlay_Warp_Player failed to create load scene- exit.") //SET_GAME_PAUSES_FOR_STREAMING(TRUE) // allow emergency streaming again SET_GAME_PAUSED(FALSE) RETURN FALSE ENDIF ENDWHILE ENDIF // wait for scene to load CPRINTLN(DEBUG_REPEAT, "REPLAY_WARP_PLAYER new load scene succesfully started.") SETTIMERA(0) WHILE NOT IS_NEW_LOAD_SCENE_LOADED() WAIT(0) CPRINTLN(DEBUG_REPEAT, "RepeatPlay_Warp_Player new load scene still loading: ", TIMERA()) // check for early out IF TIMERA() > REPLAY_LOAD_SCENE_TIMEOUT CPRINTLN(DEBUG_REPEAT, "RepeatPlay_Warp_Player took too long: early out") SET_GAME_PAUSED(FALSE) // get rid of the stream vol IF IS_NEW_LOAD_SCENE_ACTIVE() NEW_LOAD_SCENE_STOP() ENDIF //SET_GAME_PAUSES_FOR_STREAMING(TRUE) // allow emergency streaming again RETURN FALSE ENDIF // check for debug skip #IF IS_DEBUG_BUILD IF (IS_KEYBOARD_KEY_JUST_PRESSED(KEY_V)) CPRINTLN(DEBUG_REPLAY, "RepeatPlay_Warp_Player debug skipping streaming.") SET_GAME_PAUSED(FALSE) // get rid of the stream vol IF IS_NEW_LOAD_SCENE_ACTIVE() NEW_LOAD_SCENE_STOP() ENDIF //SET_GAME_PAUSES_FOR_STREAMING(TRUE) // allow emergency streaming again RETURN FALSE ENDIF #ENDIF ENDWHILE CPRINTLN(DEBUG_REPEAT, "RepeatPlay_Warp_Player warp finished") // post warp cleanup- then start repopulation // clear and repopulate the area IF DOES_ENTITY_EXIST(PLAYER_PED_ID()) IF NOT IS_PED_INJURED(PLAYER_PED_ID()) // get rid of the stream vol IF IS_NEW_LOAD_SCENE_ACTIVE() NEW_LOAD_SCENE_STOP() ENDIF //SET_GAME_PAUSES_FOR_STREAMING(TRUE) // allow emergency streaming again // need to unpause for repopulate stuff to work SET_GAME_PAUSED(FALSE) SETTIMERA(0) // refill population INSTANTLY_FILL_PED_POPULATION() IF bRepopulate = TRUE INSTANTLY_FILL_VEHICLE_POPULATION() ELSE // if not doing vehicle repop, set the early out SETTIMERA(REPOP_EARLY_OUT - REPOP_MIN_WAIT) ENDIF POPULATE_NOW() ENDIF ENDIF // wait for repopulation to finish WHILE (NOT HAS_INSTANT_FILL_VEHICLE_POPULATION_FINISHED() OR NOT HAVE_ALL_VEHICLE_GENS_LOADED_NEAR_PLAYER() OR TIMERA() < REPOP_MIN_WAIT) AND TIMERA() < REPOP_EARLY_OUT WAIT(0) CPRINTLN(DEBUG_REPEAT, "RepeatPlay_Warp_Player waiting for vehicle repopulation.") ENDWHILE IF TIMERA() > REPOP_EARLY_OUT IF bRepopulate = TRUE CPRINTLN(DEBUG_REPEAT, "RepeatPlay_Warp_Player repopulation took too long, early out.") ELSE CPRINTLN(DEBUG_REPEAT, "RepeatPlay_Warp_Player skipping vehicle repopulation.") ENDIF ENDIF // reset camera behind player SET_GAMEPLAY_CAM_RELATIVE_PITCH(0.0) SET_GAMEPLAY_CAM_RELATIVE_HEADING(0.0) // finished CPRINTLN(DEBUG_REPLAY, "RepeatPlay_Warp_Player: repopulation finished") SET_GAME_PAUSED(FALSE) RETURN TRUE ENDIF CPRINTLN(DEBUG_REPLAY, "RepeatPlay_Warp_Player: player not playing") SET_GAME_PAUSED(FALSE) RETURN FALSE ENDFUNC /// PURPOSE: /// (Un)Freezes the player /// PARAMS: /// bFreeze- are we freezing or unfreezing? PROC RepeatPlay_Freeze_Player(BOOL bFreeze) IF NOT IS_PED_INJURED(PLAYER_PED_ID()) IF bFreeze = TRUE // freeze SET_BIT(g_iRepeatPlayBits, ENUM_TO_INT(RPB_FROZEN_PLAYER)) CPRINTLN(DEBUG_REPEAT, "Repeat Play is freezing the player.") // disable player control IF IS_PLAYER_PLAYING(PLAYER_ID()) SET_PLAYER_CONTROL(PLAYER_ID(), FALSE) ENDIF CLEAR_PED_TASKS(PLAYER_PED_ID()) IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()) FREEZE_ENTITY_POSITION(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()), TRUE) ELSE IF NOT IS_PED_GETTING_INTO_A_VEHICLE(PLAYER_PED_ID()) IF IS_ENTITY_ATTACHED(PLAYER_PED_ID()) CPRINTLN(DEBUG_REPEAT, "Repeat Play is detaching entity.") DETACH_ENTITY(PLAYER_PED_ID()) ENDIF ENDIF IF NOT IS_ENTITY_ATTACHED(PLAYER_PED_ID()) FREEZE_ENTITY_POSITION(PLAYER_PED_ID(), TRUE) ENDIF ENDIF SET_ENTITY_INVINCIBLE(PLAYER_PED_ID(), TRUE) SET_ENTITY_PROOFS(PLAYER_PED_ID(), TRUE, TRUE, TRUE, TRUE, TRUE) ELSE // unfreeze IF IS_BIT_SET(g_iRepeatPlayBits, ENUM_TO_INT(RPB_FROZEN_PLAYER)) CPRINTLN(DEBUG_REPEAT, "Repeat Play is unfreezing the player.") // disable player control IF IS_PLAYER_PLAYING(PLAYER_ID()) SET_PLAYER_CONTROL(PLAYER_ID(), TRUE) ENDIF IF IS_ENTITY_ATTACHED(PLAYER_PED_ID()) // unfreeze player FREEZE_ENTITY_POSITION(GET_ENTITY_ATTACHED_TO(PLAYER_PED_ID()), FALSE) ELSE FREEZE_ENTITY_POSITION(PLAYER_PED_ID(), FALSE) ENDIF IF IS_PED_IN_ANY_VEHICLE(PLAYER_PED_ID()) FREEZE_ENTITY_POSITION(GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID()), FALSE) ENDIF SET_ENTITY_INVINCIBLE(PLAYER_PED_ID(), FALSE) SET_ENTITY_PROOFS(PLAYER_PED_ID(), FALSE, FALSE, FALSE, FALSE, FALSE) CLEAR_BIT(g_iRepeatPlayBits, ENUM_TO_INT(RPB_FROZEN_PLAYER)) ENDIF ENDIF ENDIF ENDPROC /// PURPOSE: /// Reactivates game flow and comm queues PROC Exit_Repeat_Play(BOOL bFadeIn) g_savedGlobals.sFlow.isGameflowActive = TRUE g_bPauseCommsQueues = FALSE CPRINTLN(DEBUG_REPEAT, "Unpaused Flow + communication queue.") SET_GAME_PAUSED(FALSE) IF bFadeIn = TRUE DO_SCREEN_FADE_IN(DEFAULT_FADE_TIME) ENDIF // stop the player being invinvible // this was set on by the repeat play menu IF NOT IS_PED_INJURED(PLAYER_PED_ID()) SET_ENTITY_INVINCIBLE(PLAYER_PED_ID(), FALSE) ENDIF // reset the bitset - we don't clear the bits after as they are used for putting the player back after things like the rollercoaster CLEAR_BIT(g_iRepeatPlayBits, ENUM_TO_INT(RPB_ACTIVE)) CLEAR_BIT(g_iRepeatPlayBits, ENUM_TO_INT(RPB_PASSED)) CLEAR_BIT(g_iRepeatPlayBits, ENUM_TO_INT(RPB_FROZEN_PLAYER)) CLEAR_BIT(g_iRepeatPlayBits, ENUM_TO_INT(RPB_PAUSED_GAME)) SET_PLAYER_IS_REPEATING_A_MISSION(FALSE) TERMINATE_THIS_THREAD() ENDPROC /// PURPOSE: /// This is called after repeat play is passed, or repeat play replay is rejected (or if player quits via pause menu) /// Calls mission termination commands, loads the autosave and calls exit repeat play PROC Repeat_Play_Completed() CPRINTLN(DEBUG_REPEAT,"Mission repeat ended for mission script ", tScriptName) // End of mission cleanup IF g_RepeatPlayData.eMissionType = CP_GROUP_MISSIONS MISSION_PROCESSING_MISSION_OVER(INT_TO_ENUM(SP_MISSIONS, g_RepeatPlayData.iMissionIndex)) //Inform the mission candidate system that the mission is over Mission_Over(iCandidateID) ELSE // nothing needs to be done here as the RCs run as normal via world points and launchers ENDIF // Load the auto-save here QUEUE_MISSION_REPEAT_LOAD() Exit_Repeat_Play(FALSE) ENDPROC /// PURPOSE: /// Returns the position we want to warp the player to for this story mission. /// Uses static blip position for normal missions. /// Uses custom vectors for moving prep missions. /// RETURNS: /// VECTOR position to warp the player to FUNC VECTOR GET_STORY_MISSION_WARP_POS() VECTOR vPos = <<0.0,0.0,0.0>> SP_MISSIONS eMissionID = INT_TO_ENUM(SP_MISSIONS, g_RepeatPlayData.iMissionIndex) SWITCH eMissionID // moving prep missions CASE SP_HEIST_AGENCY_PREP_1 vPos = <<354.3055, -1722.2062, 28.2590>> BREAK CASE SP_MISSION_FBI_4_PREP_1 vPos = <<1062.2830, -357.3208, 66.1474>> BREAK CASE SP_HEIST_JEWELRY_PREP_1B g_iForcePrepMissionRoute = GET_RANDOM_INT_IN_RANGE(1,4) SWITCH g_iForcePrepMissionRoute CASE 1 vPos = <<-745.47900, -1118.11047, 9.67980>> BREAK CASE 2 vPos = <<895.8368, -788.0649, 41.9022>> BREAK CASE 3 vPos = <<-964.6421, -336.7097, 36.8155>> BREAK ENDSWITCH BREAK CASE SP_HEIST_JEWELRY_PREP_2A g_iForcePrepMissionRoute = 1 vPos = <<1261.4415, 589.6306, 80.5991>> BREAK CASE SP_HEIST_RURAL_PREP_1 vPos = <<45.7300, 3064.2739, 39.9430>> BREAK CASE SP_HEIST_DOCKS_PREP_1 vPos = <<1219.1857, -2977.8999, 4.8653>> BREAK // other special cases CASE SP_MISSION_CHINESE_1 vPos = <<1991.0757, 3054.2473, 46.2147>> BREAK // normal missions DEFAULT IF g_sMissionStaticData[g_RepeatPlayData.iMissionIndex].blip != STATIC_BLIP_NAME_DUMMY_FINAL vPos = GET_STATIC_BLIP_POSITION(g_sMissionStaticData[g_RepeatPlayData.iMissionIndex].blip) ENDIF BREAK ENDSWITCH RETURN vPos ENDFUNC /// PURPOSE: /// Sets up the flow ready for the chosen repeat play mission PROC Setup_Repeat_Play() CPRINTLN(DEBUG_REPEAT,"Setup_Repeat_Play called") // pause game CPRINTLN(DEBUG_REPEAT, "Pausing game in mission repeat controller.") SET_GAME_PAUSED(TRUE) //Check the mission repeat index to trigger is valid. IF g_RepeatPlayData.iMissionIndex < 0 OR g_RepeatPlayData.iMissionIndex >= ENUM_TO_INT(SP_MISSION_MAX) SCRIPT_ASSERT("mission_repeat_controller.sc: The controller was asked to trigger a repeatable mission with an invalid index.") Exit_Repeat_Play(TRUE) ENDIF // ------------Set initial mission details-------------------------- g_structRCMissionsStatic sRCMissionDetails IF g_RepeatPlayData.eMissionType = CP_GROUP_RANDOMCHARS g_bSceneAutoTrigger = TRUE // set scene auto trigger on for RCs Retrieve_Random_Character_Static_Mission_Details(INT_TO_ENUM(g_eRC_MissionIDs ,g_RepeatPlayData.iMissionIndex), sRCMissionDetails) ENDIF // Some are different for story and RC missions SWITCH g_RepeatPlayData.eMissionType CASE CP_GROUP_MISSIONS tScriptName = g_sMissionStaticData[g_RepeatPlayData.iMissionIndex].scriptName iTriggerCharBitset = g_sMissionStaticData[g_RepeatPlayData.iMissionIndex].triggerCharBitset BREAK CASE CP_GROUP_RANDOMCHARS tScriptName = sRCMissionDetails.rcScriptName iTriggerCharBitset = sRCMissionDetails.rcPlayableChars BREAK DEFAULT CPRINTLN(DEBUG_REPEAT,"Run_Mission_Repeat passed a mission type that isn't repeat playable: ", g_RepeatPlayData.eMissionType) BREAK ENDSWITCH SET_PLAYER_IS_REPEATING_A_MISSION(TRUE) // ------------------set up the player ready for the mission------------------------- WHILE NOT IS_SCREEN_FADED_OUT() IF NOT IS_SCREEN_FADING_OUT() DO_SCREEN_FADE_OUT(500) ENDIF WAIT(0) ENDWHILE // Perform the repeat play autosave Store_RepeatPlay_Snapshot() // overwrite the vehicle's velocity- as we've already stored it // and it is now frozen so velocity is 0 g_savedGlobals.sRepeatPlayData.mVehicleStruct.vVehicleVelocity = g_vRPVelocity CPRINTLN(DEBUG_REPEAT, "Overwriting velocity as g_vRPVelocity x= ", g_vRPVelocity.x, " y= ", g_vRPVelocity.y, " z= ", g_vRPVelocity.z) PERFORM_PRE_SAVEGAME_ROUTINE(FALSE, TRUE) QUEUE_MISSION_REPEAT_SAVE() WHILE GET_STATUS_OF_MISSION_REPEAT_SAVE() = SAVEGAME_OPERATION_IN_PROGRESS WAIT(0) CPRINTLN(DEBUG_REPEAT,"Waiting for repeat play to save the game.") ENDWHILE // Exit if we failed to save the game IF GET_STATUS_OF_MISSION_REPEAT_SAVE() = SAVEGAME_OPERATION_FAILED CPRINTLN(DEBUG_REPEAT,"Repeat play save failed. exit repeat play.") Exit_Repeat_Play(TRUE) EXIT ELSE CPRINTLN(DEBUG_REPEAT,"Repeat play save was successful.") ENDIF // stop player being on fire, ground him etc VECTOR vPos IF DOES_ENTITY_EXIST(PLAYER_PED_ID()) IF NOT IS_PED_INJURED(PLAYER_PED_ID()) CPRINTLN(DEBUG_REPEAT,"Doing new safety checks: not injured.") // get player out of car and ground him CLEAR_PED_TASKS_IMMEDIATELY(PLAYER_PED_ID()) vPos = GET_ENTITY_COORDS(PLAYER_PED_ID()) GET_GROUND_Z_FOR_3D_COORD(vPos, vPos.z) SET_ENTITY_COORDS(PLAYER_PED_ID(), vPos) IF IS_ENTITY_ON_FIRE(PLAYER_PED_ID()) STOP_ENTITY_FIRE(PLAYER_PED_ID()) ENDIF IF NOT IS_PED_INJURED(PLAYER_PED_ID()) STOP_FIRE_IN_RANGE(vPos, 50.0) ENDIF CLEAR_AREA_OF_PROJECTILES(vPos, 50.0) SET_ENTITY_HEALTH(PLAYER_PED_ID(),GET_PED_MAX_HEALTH(PLAYER_PED_ID())) //Always set the player to full health. SET_CURRENT_PED_WEAPON(PLAYER_PED_ID(), WEAPONTYPE_UNARMED, TRUE) ELSE CPRINTLN(DEBUG_REPEAT,"Doing new safety checks: Player already injured.") ENDIF ENDIF // Repeat play is initialising, force clean-up any scripts we need to terminate RepeatPlay_Freeze_Player(TRUE) RUN_GENERAL_SCRIPT_CLEANUP() // #1542560: Backup old crew unlock bitset before reset so we can use them on // any heist planning board that supports them later. iHeistCrewUnlockBackup = g_savedGlobals.sHeistData.iCrewUnlockedBitset // set up building swaps, available characters etc REPEAT_PLAY_UNLOCKING(g_RepeatPlayData.iMissionIndex, g_RepeatPlayData.eMissionType) #if use_CLF_dlc //always remove the spy vehicle for a repeat play spy_vehicle_system_struct temp_spy_veh_system spy_vehicle_remove_all_system_assets(temp_spy_veh_system) #endif IF g_RepeatPlayData.eMissionType = CP_GROUP_MISSIONS // (heists are in mission group at the moment for repeat play) AND IS_BIT_SET(g_sMissionStaticData[g_RepeatPlayData.iMissionIndex].settingsBitset, MF_INDEX_IS_HEIST) eRPState = RP_HEIST_PLANNING_BOARD // need to do planning board ELSE eRPState = RP_LAUNCH // no planning board needed, go to the mission ENDIF ENDPROC /// PURPOSE: /// Lets the player choose which crew / approach etc to use for this repeat play heist PROC Do_Heist_Planning_Board() CPRINTLN(DEBUG_REPEAT, "Do_Heist_Planning_Board called.") // heist only variables INT iHeist = GET_HEIST_FROM_MISSION_ID(INT_TO_ENUM(SP_MISSIONS, g_RepeatPlayData.iMissionIndex)) TEXT_LABEL_31 tHeistController = GET_HEIST_CONTROLLER_SCRIPT(INT_TO_ENUM(SP_MISSIONS, g_RepeatPlayData.iMissionIndex)) VECTOR vHeistPos = GET_HEIST_PLANNING_BOARD_LOCATION(iHeist) //#1542560: Unlock previously unlocked heist crew members who are valid for this heist. SWITCH iHeist CASE HEIST_FINALE CASE HEIST_AGENCY IF IS_BIT_SET(iHeistCrewUnlockBackup, ENUM_TO_INT(CM_DRIVER_G_TALINA_UNLOCK)) SET_HEIST_CREW_MEMBER_UNLOCKED(CM_DRIVER_G_TALINA_UNLOCK) ENDIF FALLTHRU CASE HEIST_RURAL_BANK IF IS_BIT_SET(iHeistCrewUnlockBackup, ENUM_TO_INT(CM_GUNMAN_G_CHEF_UNLOCK)) SET_HEIST_CREW_MEMBER_UNLOCKED(CM_GUNMAN_G_CHEF_UNLOCK) ENDIF FALLTHRU CASE HEIST_JEWEL IF IS_BIT_SET(iHeistCrewUnlockBackup, ENUM_TO_INT(CM_GUNMAN_G_PACKIE_UNLOCK)) SET_HEIST_CREW_MEMBER_UNLOCKED(CM_GUNMAN_G_PACKIE_UNLOCK) ENDIF IF IS_BIT_SET(iHeistCrewUnlockBackup, ENUM_TO_INT(CM_HACKER_B_RICKIE_UNLOCK)) SET_HEIST_CREW_MEMBER_UNLOCKED(CM_HACKER_B_RICKIE_UNLOCK) ENDIF BREAK ENDSWITCH // load the heist controller REQUEST_SCRIPT(tHeistController) WHILE NOT (HAS_SCRIPT_LOADED(tHeistController)) CPRINTLN(DEBUG_REPEAT, "RepeatPlay: Waiting for script to load: ", tHeistController) WAIT(0) ENDWHILE // Script has loaded, so launch it START_NEW_SCRIPT(tHeistController, FRIEND_STACK_SIZE) //Fix for 1219287: This stack size must match the ones defined in Define_Restore_Launched_ScriptNames() SET_SCRIPT_AS_NO_LONGER_NEEDED(tHeistController) CPRINTLN(DEBUG_REPEAT, "RepeatPlay: script launched: ", tHeistController) // Warp the player to the planning board CPRINTLN(DEBUG_REPEAT, "iHeist = ", iHeist) IF NOT IS_PED_INJURED(PLAYER_PED_ID()) CPRINTLN(DEBUG_REPEAT, "RepeatPlay: warping player to heist board pos: ", vHeistPos) RepeatPlay_Warp_Player(vHeistPos, GET_ENTITY_HEADING(PLAYER_PED_ID()), FALSE) ENDIF // set up any board display groups that should be on SET_HEIST_BOARD_DISPLAY_GROUPS_ON(iHeist) // any other special case setup IF iHeist = HEIST_DOCKS SET_BUILDING_STATE(BUILDINGNAME_ES_FLOYDS_APPARTMENT_SHIT, BUILDINGSTATE_DESTROYED) SET_BUILDING_STATE(BUILDINGNAME_ES_FLOYDS_APPARTMENT_SHIT_SOFA, BUILDINGSTATE_DESTROYED) ELIF iHeist = HEIST_AGENCY SET_BUILDING_STATE(BUILDINGNAME_IPL_SWEATSHOP_WITH_INTERIOR, BUILDINGSTATE_DESTROYED) ENDIF // set flow flag to activate the planning board REPEAT_PLAY_SET_HEIST_FLOW_FLAG(iHeist, RPH_START_BOARD_INTERACTIONS, TRUE) // fade in when the board is ready to be displayed WHILE g_bHeistBoardViewActive = FALSE CPRINTLN(DEBUG_REPEAT, "Waiting for heist board to set up..") WAIT(0) ENDWHILE RepeatPlay_Freeze_Player(FALSE) // un freeze + no longer invincible // slight wait so we don't see stuff fade in WAIT(500) CPRINTLN(DEBUG_REPEAT, "Heist board has been setup, fading in.") DO_SCREEN_FADE_IN(500) // ---------------------------HEIST PLANNING BOARD FLOW------------------------------------------------- eRPHeistState = RPHS_POI_OVERVIEW WHILE eRPHeistState <> RPHS_FINISHED WAIT(0) SWITCH eRPHeistState CASE RPHS_POI_OVERVIEW eRPHeistState = REPEAT_PLAY_DO_BOARD_INTERACTION(iHeist, INTERACT_POI_OVERVIEW, REPEAT_PLAY_GET_HEIST_CHOICE_DISPLAY_GROUP(iHeist, TRUE), eRPHeistState) BREAK CASE RPHS_POI_OVERVIEW_DONE CPRINTLN(DEBUG_REPEAT, "Point of interest overview done.") WAIT(REPEAT_PLAY_GET_HEIST_CHOICE_DONE_WAIT(iHeist, eRPHeistState)) IF iHeist = HEIST_RURAL_BANK // bank heist doesn't have a gameplay choice CPRINTLN(DEBUG_REPEAT, "Bank heist skipping gameplay choice.") Set_Mission_Flow_Int_Value(FLOWINT_HEIST_CHOICE_RURAL, HEIST_CHOICE_RURAL_NO_TANK) REPEAT_PLAY_SET_HEIST_FLOW_FLAG(iHeist, RPH_LOAD_EXIT_CUTSCENE, TRUE) eRPHeistState = RPHS_CREW_CHOICE ELSE eRPHeistState = RPHS_GAMEPLAY_CHOICE ENDIF BREAK CASE RPHS_GAMEPLAY_CHOICE eRPHeistState = REPEAT_PLAY_DO_BOARD_INTERACTION(iHeist, INTERACT_GAMEPLAY_CHOICE, REPEAT_PLAY_GET_HEIST_CHOICE_DISPLAY_GROUP(iHeist, TRUE), eRPHeistState) IF eRPHeistState <> RPHS_GAMEPLAY_CHOICE REPEAT_PLAY_SET_HEIST_FLOW_FLAG(iHeist, RPH_LOAD_EXIT_CUTSCENE, TRUE) ENDIF BREAK CASE RPHS_GAMEPLAY_CHOICE_DONE CPRINTLN(DEBUG_REPEAT, "Gameplay choice done.") WAIT(REPEAT_PLAY_GET_HEIST_CHOICE_DONE_WAIT(iHeist, eRPHeistState)) eRPHeistState = RPHS_CREW_CHOICE BREAK CASE RPHS_CREW_CHOICE eRPHeistState = REPEAT_PLAY_DO_BOARD_INTERACTION(iHeist, INTERACT_CREW, REPEAT_PLAY_GET_HEIST_CHOICE_DISPLAY_GROUP(iHeist, FALSE), eRPHeistState) BREAK CASE RPHS_CREW_CHOICE_DONE CPRINTLN(DEBUG_REPEAT, "Crew choice done.") WAIT(REPEAT_PLAY_GET_HEIST_CHOICE_DONE_WAIT(iHeist, eRPHeistState)) REPEAT_PLAY_TOGGLE_BOARD_VIEWING(iHeist, FALSE) eRPHeistState = RPHS_EXIT_CUTSCENE BREAK CASE RPHS_EXIT_CUTSCENE CPRINTLN(DEBUG_REPEAT, "Waiting for heist board exit cut-scene to finish...") WHILE NOT REPEAT_PLAY_HAS_HEIST_CUTSCENE_FINISHED(iHeist) WAIT(0) ENDWHILE CPRINTLN(DEBUG_REPEAT, "Finished.") DO_SCREEN_FADE_OUT(0) CPRINTLN(DEBUG_REPEAT, "Waiting for heist board cut-scene to unload...") WHILE IS_CUTSCENE_ACTIVE() WAIT(0) ENDWHILE CPRINTLN(DEBUG_REPEAT, "Unloaded.") eRPHeistState = RPHS_FINISHED BREAK DEFAULT CPRINTLN(DEBUG_REPEAT, "Heist planning board in unknown state.") BREAK ENDSWITCH ENDWHILE CPRINTLN(DEBUG_REPEAT, "Heist planning board finished.") //-------------------------------------------------------------------------------------------- // wait for the heist board to cleanup CPRINTLN(DEBUG_REPEAT, "Waiting for heist board to cleanup.") WHILE IS_CURRENTLY_ON_MISSION_TO_TYPE() WAIT(0) ENDWHILE CPRINTLN(DEBUG_REPEAT, "Heist board has finished clean up.") // any special case clean-up IF iHeist = HEIST_AGENCY // Switch to empty sweatshop interior SET_BUILDING_STATE(BUILDINGNAME_IPL_SWEATSHOP_NO_INTERIOR, BUILDINGSTATE_DESTROYED) SET_BUILDING_STATE(BUILDINGNAME_IPL_SWEATSHOP_WITH_INTERIOR, BUILDINGSTATE_CLEANUP) SET_BUILDING_STATE(BUILDINGNAME_IPL_SWEATSHOP_LOD_DOOR, BUILDINGSTATE_DESTROYED) SET_BUILDING_STATE(BUILDINGNAME_IPL_SWEATSHOP_BURNT, BUILDINGSTATE_NORMAL) SET_BUILDING_STATE(BUILDINGNAME_IPL_SWEATSHOP_WINDOW_LIGHTS, BUILDINGSTATE_NORMAL) ELIF iHeist = HEIST_DOCKS SET_BUILDING_STATE(BUILDINGNAME_ES_FLOYDS_APPARTMENT_SHIT, BUILDINGSTATE_NORMAL) SET_BUILDING_STATE(BUILDINGNAME_ES_FLOYDS_APPARTMENT_SHIT_SOFA, BUILDINGSTATE_NORMAL) ENDIF // do the actual heist mission g_RepeatPlayData.iMissionIndex = REPEAT_PLAY_GET_SELECTED_HEIST_MISSION_ID(iHeist) tScriptName = g_sMissionStaticData[g_RepeatPlayData.iMissionIndex].scriptName iTriggerCharBitset = g_sMissionStaticData[g_RepeatPlayData.iMissionIndex].triggerCharBitset RepeatPlay_Freeze_Player(TRUE) // re freeze + invincible eRPState = RP_LAUNCH ENDPROC /// PURPOSE: /// Launches the repeat play mission script PROC Launch_Repeat_Play() CPRINTLN(DEBUG_REPEAT, "Launch_Repeat_Play called.") INT iHour, iMinute // used to set time of day to something suitable // RC variables g_structRCMissionsStatic sRCMissionDetails // ----------------------load and launch the mission ------------------------------------- //Reset mission passed flag. CLEAR_BIT(g_iRepeatPlayBits, ENUM_TO_INT(RPB_PASSED)) //Wait for the player character to be switched to the correct character for this mission. REPEAT_PLAY_SWITCH_TO_SUITABLE_CHARACTER(iTriggerCharBitset) IF NOT IS_PED_INJURED(PLAYER_PED_ID()) //---------- warp the player into position + set time of day------------- SWITCH g_RepeatPlayData.eMissionType CASE CP_GROUP_MISSIONS // warp VECTOR vPos vPos = GET_STORY_MISSION_WARP_POS() IF NOT ARE_VECTORS_EQUAL(vPos, <<0.0,0.0,0.0>>) CPRINTLN(DEBUG_REPEAT,"Moving player to blip pos") RepeatPlay_Warp_Player(vPos, GET_ENTITY_HEADING(PLAYER_PED_ID()), TRUE) CPRINTLN(DEBUG_REPEAT,"Moved player to blip pos") ELSE CPRINTLN(DEBUG_REPEAT,"Move to vector was 0,0,0. Leave player where he was.") ENDIF // set time to be suitable SP_MISSIONS eMissionID eMissionID = INT_TO_ENUM(SP_MISSIONS, g_RepeatPlayData.iMissionIndex) IF (g_sMissionStaticData[eMissionID].startHour != NULL_HOUR) AND (g_sMissionStaticData[eMissionID].endHour != NULL_HOUR) AND NOT IS_BIT_SET(g_sMissionStaticData[eMissionID].settingsBitset, MF_INDEX_MID_MISSION_TOD) // ignore if the TOD skip is done mid mission INT iCurrentHour INT iStartHour INT iEndHour iCurrentHour = GET_TIMEOFDAY_HOUR(GET_CURRENT_TIMEOFDAY()) iStartHour = g_sMissionStaticData[eMissionID].startHour iEndHour = g_sMissionStaticData[eMissionID].endHour IF NOT IS_HOUR_BETWEEN_THESE_HOURS(iCurrentHour, iStartHour, iEndHour) CPRINTLN(DEBUG_REPEAT, "Current hour is ", iCurrentHour, " which is not in the valid mission triggering hours of ", iStartHour, "->", iEndHour, ".") CPRINTLN(DEBUG_REPEAT, "Overriding time hour to be ", iStartHour, ".") SET_CLOCK_TIME(iStartHour, 00, 00) ENDIF ENDIF BREAK CASE CP_GROUP_RANDOMCHARS g_eRC_MissionIDs eRCMissionID // Warp Retrieve_Random_Character_Static_Mission_Details(INT_TO_ENUM(g_eRC_MissionIDs ,g_RepeatPlayData.iMissionIndex), sRCMissionDetails) CPRINTLN(DEBUG_REPEAT,"Moving player to blip pos") // Get mission ID eRCMissionID = Get_RC_MissionID_For_Script(tScriptName) IF ARE_VECTORS_EQUAL(sRCMissionDetails.rcCoords, <<0,0,0>>) IF eRCMissionID = RC_TONYA_3 OR eRCMissionID = RC_TONYA_4 sRCMissionDetails.rcCoords = <<-3.2349, -1469.9525, 29.5503>> // Heading should be... 265.8950 ELSE CERRORLN(DEBUG_REPEAT,"Launch_Repeat_Play: rcCoords are <<0,0,0 with no associated mission! Bug for Andy Minghella") SCRIPT_ASSERT("Launch_Repeat_Play: rcCoords are <<0,0,0>> with no associated mission! Bug for Andy Minghella") ENDIF ENDIF // other special cases where we need to spawn player away from blip pos IF eRCMissionID = RC_BARRY_1 OR eRCMissionID = RC_BARRY_2 sRCMissionDetails.rcCoords = <<188.51234, -954.81543, 29.09192>> CPRINTLN(DEBUG_REPEAT,"Using special case pos for Barry 1/2 RC.") ELIF eRCMissionID = RC_BARRY_3A sRCMissionDetails.rcCoords = <<1205.7874, -1289.2284, 34.2264>> ELIF eRCMissionID = RC_BARRY_3C sRCMissionDetails.rcCoords = <<-447.0243, -1704.6481, 17.9004>> ENDIF RepeatPlay_Warp_Player(sRCMissionDetails.rcCoords, GET_ENTITY_HEADING(PLAYER_PED_ID()), TRUE) CPRINTLN(DEBUG_REPEAT,"Moved player to blip pos") // Time IF sRCMissionDetails.rcStartTime != 0000 OR sRCMissionDetails.rcEndTime != 2359 iHour = (sRCMissionDetails.rcStartTime/100) iMinute = (sRCMissionDetails.rcStartTime%100) SET_CLOCK_TIME(iHour, iMinute, 0) CPRINTLN(DEBUG_REPEAT,"REPEAT_PLAY_SET_SUITABLE_TIME_OF_DAY set time to : ", iHour, iMinute) ENDIF // Activate the RC so we can launch it Activate_RC_Mission(eRCMissionID, FALSE) // Special case for Tonya phone call missions IF eRCMissionID = RC_TONYA_3 OR eRCMissionID = RC_TONYA_4 // Load script REQUEST_SCRIPT("ambient_Tonya") WHILE NOT HAS_SCRIPT_LOADED("ambient_Tonya") REQUEST_SCRIPT("ambient_Tonya") WAIT(0) ENDWHILE // Launch script START_NEW_SCRIPT("ambient_Tonya", DEFAULT_STACK_SIZE) SET_SCRIPT_AS_NO_LONGER_NEEDED("ambient_Tonya") CPRINTLN(DEBUG_REPEAT, "Launched ambient script for Tonya 3 or 4.") ELSE CPRINTLN(DEBUG_REPEAT, "Reactivating all world brains.") REACTIVATE_ALL_WORLD_BRAINS_THAT_ARE_WAITING_TILL_OUT_OF_RANGE() ENDIF g_RandomChars[eRCMissionID].rcLeaveAreaCheck = FALSE g_bSceneAutoTrigger = TRUE //Flag launchers to setup their RC scene to automatically trigger. //Delay until rc is flagged as running. CPRINTLN(DEBUG_REPEAT, "Waiting for RC mission to trigger.") WHILE NOT g_RandomChars[eRCMissionID].rcIsRunning g_RandomChars[eRCMissionID].rcLeaveAreaCheck = FALSE WAIT(0) ENDWHILE g_bSceneAutoTrigger = FALSE BREAK DEFAULT CPRINTLN(DEBUG_REPEAT,"Launch_Repeat_Play passed a mission type that isn't repeat playable: ", g_RepeatPlayData.eMissionType) BREAK ENDSWITCH ELSE CERRORLN(DEBUG_REPEAT,"Launch_Repeat_Play: player is injured. Abort!. bug for Andy Minghella.") SCRIPT_ASSERT("Launch_Repeat_Play: player is injured. Abort!. bug for Andy Minghella") Exit_Repeat_Play(TRUE) EXIT ENDIF RepeatPlay_Freeze_Player(FALSE) // un freeze + no longer invincible eRPState = RP_RUNNING ENDPROC /// PURPOSE: /// Waits for the repeat play thread to terminate PROC Run_Repeat_Play() CPRINTLN(DEBUG_REPEAT, "Run_Repeat_Play called.") SWITCH g_RepeatPlayData.eMissionType CASE CP_GROUP_MISSIONS INT iCoreVarsArrayPos iCoreVarsArrayPos = g_sMissionStaticData[g_RepeatPlayData.iMissionIndex].coreVariablesIndex CPRINTLN(DEBUG_MISSION, "CoreVarsIndex in static data set to ", iCoreVarsArrayPos) WHILE PERFORM_DO_MISSION_NOW(iCoreVarsArrayPos) = STAY_ON_THIS_COMMAND WAIT(0) // Story mission repeat play still running. ENDWHILE CPRINTLN(DEBUG_REPEAT, "Run_Repeat_Play story mission passed or failed +rejected.") // handle 2 part mission SP_MISSIONS eMissionID eMissionID= INT_TO_ENUM(SP_MISSIONS, g_RepeatPlayData.iMissionIndex) //Handle Finale A and B IF eMissionID = SP_MISSION_FINALE_A OR eMissionID = SP_MISSION_FINALE_B IF IS_BIT_SET(g_iRepeatPlayBits, ENUM_TO_INT(RPB_PASSED)) //Wait until the Finale Credits is passed as well CPRINTLN(DEBUG_REPEAT, "Run_Repeat_Play launching finale credits.") // passed 2nd part, do credits iCoreVarsArrayPos = g_sMissionStaticData[SP_MISSION_FINALE_CREDITS].coreVariablesIndex WHILE PERFORM_DO_MISSION_NOW(iCoreVarsArrayPos) = STAY_ON_THIS_COMMAND WAIT(0) // Story mission repeat play still running ENDWHILE ENDIF //Handle Finale C1 ELIF eMissionID = SP_MISSION_FINALE_C1 IF IS_BIT_SET(g_iRepeatPlayBits, ENUM_TO_INT(RPB_PASSED)) CPRINTLN(DEBUG_REPEAT, "Run_Repeat_Play launching finale c2.") // passed 1st part, do 2nd part of mission iCoreVarsArrayPos = g_sMissionStaticData[SP_MISSION_FINALE_C2].coreVariablesIndex WHILE PERFORM_DO_MISSION_NOW(iCoreVarsArrayPos) = STAY_ON_THIS_COMMAND WAIT(0) // Story mission repeat play still running ENDWHILE //Wait until the Finale Credits is passed as well CPRINTLN(DEBUG_REPEAT, "Run_Repeat_Play launching finale credits.") // passed 2nd part, do credits iCoreVarsArrayPos = g_sMissionStaticData[SP_MISSION_FINALE_CREDITS].coreVariablesIndex WHILE PERFORM_DO_MISSION_NOW(iCoreVarsArrayPos) = STAY_ON_THIS_COMMAND WAIT(0) // Story mission repeat play still running ENDWHILE ENDIF ENDIF // if we're not staying on this command, repeat play was either passed or failed + rejected IF IS_BIT_SET(g_iRepeatPlayBits, ENUM_TO_INT(RPB_PASSED)) eRPState = RP_PASSED ELSE eRPState = RP_FAILED ENDIF BREAK CASE CP_GROUP_RANDOMCHARS // wait for the mission thread to actually start WHILE GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH(GET_HASH_KEY(tScriptName)) =0 WAIT(0) CPRINTLN(DEBUG_REPEAT, "Run_Repeat_Play waiting for mission thread to start.") ENDWHILE CPRINTLN(DEBUG_REPEAT, "Run_Repeat_Play mission thread started.") //--------------------Wait for the mission script to terminate---------------------------- WHILE GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH(GET_HASH_KEY(tScriptName)) >0 WAIT(0) ENDWHILE CPRINTLN(DEBUG_REPEAT, "Run_Repeat_Play mission thread terminated.") /// ----------Pass / Fail------------------------------- IF IS_BIT_SET(g_iRepeatPlayBits, ENUM_TO_INT(RPB_PASSED)) eRPState = RP_PASSED ELSE eRPState = RP_FAILED ENDIF BREAK DEFAULT CPRINTLN(DEBUG_REPEAT,"Run_Repeat_Play passed a mission type that isn't repeat playable: ", g_RepeatPlayData.eMissionType) BREAK ENDSWITCH ENDPROC /// PURPOSE: /// Waits for pass stats to display then calls repeat play completed PROC Repeat_Play_Passed() INT iEarlyExitTimer = 0 INT iEarlyOutLimit = 20000 CPRINTLN(DEBUG_REPEAT, "Repeat_Play_Passed called.") BOOL bWaitForPassedScreen = TRUE IF g_RepeatPlayData.eMissionType = CP_GROUP_MISSIONS SP_MISSIONS eMissionID = INT_TO_ENUM(SP_MISSIONS, g_RepeatPlayData.iMissionIndex) // Some missions do their passed screens during the mission. IF eMissionID = SP_MISSION_FAMILY_3 OR eMissionID = SP_MISSION_FAMILY_5 OR eMissionID = SP_MISSION_FAMILY_6 OR eMissionID = SP_HEIST_FINALE_2A OR eMissionID = SP_HEIST_FINALE_2B //OR eMissionID = SP_MISSION_FINALE_C1 //C1 IS c2, c1 seamless passes into C2, fix for last comment on 1593783 OR eMissionID = SP_HEIST_JEWELRY_2 bWaitForPassedScreen = FALSE CPRINTLN(DEBUG_REPEAT, "Repeat_Play_Passed: This mission does passed screen on mission: Don't wait for passed screen.") ENDIF ENDIF IF bWaitForPassedScreen = TRUE //Wait for the mission pass stats to start displaying iEarlyExitTimer = GET_GAME_TIMER() + iEarlyOutLimit WHILE g_bResultScreenDisplaying = FALSE AND GET_GAME_TIMER() < iEarlyExitTimer AND (NOT g_MissionStatSystemSuppressVisual) WAIT(0) CPRINTLN(DEBUG_REPEAT, "Repeat_Play_Passed: Waiting for stats to start displaying.") ENDWHILE //Wait for the mission pass stats to finish displaying iEarlyExitTimer = GET_GAME_TIMER() + 30000 CPRINTLN(DEBUG_REPEAT, "Repeat_Play_Passed: Starting to wait for stats to finish displaying...") WHILE (g_bResultScreenDisplaying OR g_bMissionStatSystemUploadPending) AND GET_GAME_TIMER() < iEarlyExitTimer WAIT(0) ENDWHILE CPRINTLN(DEBUG_REPEAT, "Repeat_Play_Passed: Stats finished displaying.") ENDIF // fade out and exit WHILE NOT IS_SCREEN_FADED_OUT() IF NOT IS_SCREEN_FADING_OUT() DO_SCREEN_FADE_OUT(DEFAULT_FADE_TIME) ENDIF WAIT(0) ENDWHILE Repeat_Play_Completed() ENDPROC /// PURPOSE: /// Handles setting up, accepting and rejecting replays PROC Repeat_Play_Failed() CPRINTLN(DEBUG_REPEAT, "Repeat_Play_Failed called.") // End of mission cleanup SWITCH g_RepeatPlayData.eMissionType CASE CP_GROUP_MISSIONS // Story missions only enter the failed state after replay rejection // so can go stright to the exit CPRINTLN(DEBUG_REPEAT,"Story Mission repeat rejected.") Repeat_Play_Completed() BREAK CASE CP_GROUP_RANDOMCHARS // RC replays should work ok since they don't rely on flow commands WHILE IS_REPLAY_BEING_PROCESSED() WAIT(0) CPRINTLN(DEBUG_REPEAT,"Mission repeat RC waiting to be set up for a replay.") ENDWHILE // Handle accepting / rejecting of replay IF IS_REPLAY_IN_PROGRESS() CPRINTLN(DEBUG_REPEAT,"Mission repeat RC accepted.") // replay accepted, wait for script to be running and go back into running state WHILE GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH(GET_HASH_KEY(tScriptName)) =0 WAIT(0) CPRINTLN(DEBUG_REPEAT,"Waiting for RC replay to launch.") ENDWHILE eRPState = RP_RUNNING ELSE CPRINTLN(DEBUG_REPEAT,"Mission repeat RC rejected.") Repeat_Play_Completed() // replay rejected, this repeat play is complete ENDIF BREAK DEFAULT CPRINTLN(DEBUG_REPEAT,"Repeat_Play_Failed passed a mission type that isn't repeat playable: ", g_RepeatPlayData.eMissionType) Repeat_Play_Completed() BREAK ENDSWITCH ENDPROC // ----------------Main Loop------------------------------------------------------------- SCRIPT CPRINTLN(DEBUG_REPEAT,"Starting mission repeat controller.") // This script needs to cleanup only when the game moves from SP to MP IF (HAS_FORCE_CLEANUP_OCCURRED(FORCE_CLEANUP_FLAG_SP_TO_MP|FORCE_CLEANUP_FLAG_MAGDEMO)) CPRINTLN(DEBUG_REPEAT,"Mission_repeat_controller.sc has been forced to cleanup.") IF GET_CAUSE_OF_MOST_RECENT_FORCE_CLEANUP() = FORCE_CLEANUP_FLAG_SP_TO_MP CPRINTLN(DEBUG_REPEAT,"Mission_repeat_controller.sc has been forced to cleanup (SP to MP)") SET_FRONTEND_ACTIVE(FALSE) LOBBY_SET_AUTO_MULTIPLAYER(TRUE) SHUTDOWN_SESSION_CLEARS_AUTO_MULTIPLAYER(FALSE) QUEUE_MISSION_REPEAT_LOAD() ENDIF SET_PLAYER_IS_REPEATING_A_MISSION(FALSE) TERMINATE_THIS_THREAD() ENDIF WHILE TRUE IF IS_REPEAT_PLAY_ACTIVE() SWITCH eRPState CASE RP_SETUP Setup_Repeat_Play() BREAK CASE RP_HEIST_PLANNING_BOARD Do_Heist_Planning_Board() BREAK CASE RP_LAUNCH Launch_Repeat_Play() BREAK CASE RP_RUNNING Run_Repeat_Play() BREAK CASE RP_PASSED Repeat_Play_Passed() BREAK CASE RP_FAILED Repeat_Play_Failed() BREAK ENDSWITCH ENDIF WAIT(0) ENDWHILE ENDSCRIPT