////////////////////////////////////////////////////////////////////////////////////////// // // // SCRIPT NAME : autosave_controller.sc // // AUTHOR : Kenneth Ross // // DESCRIPTION : Allows for the game to be automatically saved whenever an // // autosave request has been made (usually at the end of a // // mission or ambient script). // // // ////////////////////////////////////////////////////////////////////////////////////////// //Compile out Title Update changes to header functions. //Must be before includes. //CONST_INT USE_TU_CHANGES 0 // Removed by Kenneth R. USING "rage_builtins.sch" USING "globals.sch" USING "script_player.sch" USING "savegame_public.sch" USING "savegame_private.sch" USING "cellphone_public.sch" USING "flow_help_public.sch" USING "respawn_location_private.sch" CONST_INT AUTOSAVE_DELAY_TIME 1000 ENUM SCRIPT_STAGE_ENUM SCRIPT_STAGE_INITIALISE = 0, SCRIPT_STAGE_WAIT_FOR_REQUEST, SCRIPT_STAGE_PERFORM_AUTOSAVE, SCRIPT_STAGE_CLEANUP ENDENUM SCRIPT_STAGE_ENUM eScriptStage = SCRIPT_STAGE_INITIALISE #IF IS_DEBUG_BUILD BOOL g_d_bAutoSavePhoneOnScreenSpam = FALSE #ENDIF #IF IS_DEBUG_BUILD BOOL bMakeAutoSaveRequest PROC SETUP_AUTOSAVE_WIDGETS() START_WIDGET_GROUP("Autosave Controller") ADD_WIDGET_BOOL("bMakeAutoSaveRequest", bMakeAutoSaveRequest) ADD_WIDGET_STRING("Autosave Flags - READ ONLY") ADD_WIDGET_INT_READ_ONLY("iQueuedRequests", g_sAutosaveData.iQueuedRequests) ADD_WIDGET_BOOL("bRequest", g_sAutosaveData.bRequest) ADD_WIDGET_BOOL("bPerforming", g_sAutosaveData.bPerforming) ADD_WIDGET_BOOL("bInProgress", g_sAutosaveData.bInProgress) ADD_WIDGET_BOOL("bComplete", g_sAutosaveData.bComplete) ADD_WIDGET_BOOL("bBeenOffMission", g_sAutosaveData.bBeenOffMission) ADD_WIDGET_BOOL("bFirstAutosaveComplete", g_sAutosaveData.bFirstAutosaveComplete) ADD_WIDGET_BOOL("bIgnoreMissionFlag", g_sAutosaveData.bIgnoreOnMissionFlag) ADD_WIDGET_BOOL("Flush Autosaves", g_sAutosaveData.bFlushAutosaves) ADD_WIDGET_BOOL("Block Autosaves", g_bDebugBlockAutosaves) STOP_WIDGET_GROUP() ENDPROC PROC MAINTAIN_AUTOSAVE_WIDGETS() IF bMakeAutoSaveRequest MAKE_AUTOSAVE_REQUEST() bMakeAutoSaveRequest = FALSE ENDIF ENDPROC #ENDIF /// PURPOSE: Cleans up any assets that were created PROC CLEANUP_SCRIPT() #IF IS_DEBUG_BUILD CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] AUTOSAVE CONTROLLER SCRIPT TERMINATING") #ENDIF TERMINATE_THIS_THREAD() ENDPROC /// PURPOSE: Loads the required shop assets and sets up items to browse PROC INITIALISE_CONTROLLER() eScriptStage = SCRIPT_STAGE_WAIT_FOR_REQUEST ENDPROC /// PURPOSE: Checks for an autosave request being made PROC CHECK_AUTOSAVE_REQUEST() // Scripts call MAKE_AUTOSAVE_REQUEST() in autosave_public.sch which will set up // The appropriate flags in the autosave data struct. Therefore all we need to do in // this controller is check that the bRequest flag has been set. IF g_sAutosaveData.iQueuedRequests > 0 // Reset the appropriate autosave flags g_sAutosaveData.bRequest = TRUE g_sAutosaveData.bPerforming = FALSE g_sAutosaveData.bInProgress = FALSE g_sAutosaveData.bComplete = FALSE g_sAutosaveData.bBeenOffMission = FALSE g_sAutosaveData.iTriggerTime = GET_GAME_TIMER() eScriptStage = SCRIPT_STAGE_PERFORM_AUTOSAVE ELSE #IF IS_DEBUG_BUILD IF NOT g_bDebug_KeepAutoSaveControllerRunning g_sAutosaveData.bFlushAutosaves = FALSE eScriptStage = SCRIPT_STAGE_CLEANUP ENDIF #ENDIF ENDIF ENDPROC /// PURPOSE: Performs an autosave if safe to do so PROC PERFORM_AUTOSAVE() // Reset the flags and exit stage if the process is complete IF (g_sAutosaveData.bComplete) g_sAutosaveData.bRequest = FALSE g_sAutosaveData.bPerforming = FALSE g_sAutosaveData.bInProgress = FALSE g_sAutosaveData.bBeenOffMission = FALSE g_sAutosaveData.bComplete = FALSE g_sAutosaveData.iQueuedRequests-- // Clearing the block flags for safety. SET_AUTOSAVE_IGNORES_ON_MISSION_FLAG(FALSE, FALSE) IF g_sAutosaveData.iQueuedRequests > 0 #IF IS_DEBUG_BUILD OR g_bDebug_KeepAutoSaveControllerRunning #ENDIF eScriptStage = SCRIPT_STAGE_WAIT_FOR_REQUEST ELSE g_sAutosaveData.bFlushAutosaves = FALSE eScriptStage = SCRIPT_STAGE_CLEANUP ENDIF EXIT ENDIF // Attempt to perform an autosave if we haven't done so already IF NOT (g_sAutosaveData.bPerforming) //Check for debug skip. #IF IS_DEBUG_BUILD IF g_bDebugBlockAutosaves CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Skipped: Autosaves blocked by debug global.") g_sAutosaveData.bComplete = TRUE g_sAutosaveData.bFirstAutosaveComplete = TRUE EXIT ENDIF #ENDIF // Before we perform an autosave we need to check that the game is in a safe state. // If we are not in a safe state and just want to delay the process, then bail out // of the proc. // If we are not in a safe state and want to cancel the request, set the 'complete' // flag to TRUE and bail out of the proc. // IF (GET_GAME_TIMER() - g_sAutosaveData.iTriggerTime) < AUTOSAVE_DELAY_TIME // CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Delay: Waiting for autosave time delay to complete (") // PRINTINT(ROUND((TO_FLOAT(GET_GAME_TIMER() - g_sAutosaveData.iTriggerTime)/TO_FLOAT(AUTOSAVE_DELAY_TIME))*100)) // PRINTSTRING("%).") // EXIT // ENDIF IF (g_bInMultiplayer) CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Failed: Autosave not available in multiplayer.") g_sAutosaveData.bComplete = TRUE EXIT ENDIF IF NOT g_savedGlobals.sFlow.isGameflowActive OR NOT IS_BIT_SET(g_savedGlobals.sFlow.strandSavedVars[STRAND_PROLOGUE].savedBitflags,SAVED_BITS_STRAND_ACTIVATED) CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Failed: Currently running in debug mode.") g_sAutosaveData.bComplete = TRUE EXIT ENDIF enumCharacterList eCurrentPed = GET_CURRENT_PLAYER_PED_ENUM() IF NOT IS_PLAYER_PED_PLAYABLE(eCurrentPed) IF IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_ANIMAL) CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Failed: Player is playing as an animal.") ELIF IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_DIRECTOR) CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Failed: Player is playing in director mode.") ELSE MODEL_NAMES playerModel = GET_ENTITY_MODEL(PLAYER_PED_ID()) CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Failed: Player is not a main story character.") CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] ENUM_TO_INT(eCurrentPed) = ",ENUM_TO_INT(eCurrentPed)) CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] playerModel = ",playerModel) //B* - 2308260 IF IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_SWITCH) CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Delay: Player has not switched back yet.") EXIT ENDIF SCRIPT_ASSERT("Autosave failed: Player is not a main story character. Tell Kenneth R.") ENDIF g_sAutosaveData.bComplete = TRUE EXIT ENDIF IF NOT IS_PED_INJURED(PLAYER_PED_ID()) IF GET_CLOSEST_SAVEHOUSE(GET_ENTITY_COORDS(PLAYER_PED_ID()), eCurrentPed) = NUMBER_OF_SAVEHOUSE_LOCATIONS #IF IS_DEBUG_BUILD SCRIPT_ASSERT("Autosave failed: No savehouse available for the current player character. Tell Kenneth R.") #ENDIF CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Failed: No savehouse available for the current player character.") g_sAutosaveData.bComplete = TRUE EXIT ENDIF ENDIF IF g_sAutosaveData.bFlushAutosaves CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Failed: Flushing out autosaves.") g_sAutosaveData.bComplete = TRUE EXIT ENDIF IF NOT g_sAutosaveData.bIgnoreOnMissionFlag // IF (IS_CURRENTLY_ON_MISSION_TO_TYPE()) // IF (g_sAutosaveData.bBeenOffMission) // CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Failed: New mission started before autosave could kick in.") // g_sAutosaveData.bComplete = TRUE // EXIT // ENDIF // // //CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Delay: Script mission flag still set.") // //EXIT // CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Failed: Script mission flag still set.") // g_sAutosaveData.bComplete = TRUE // EXIT // ENDIF // IF (GET_MISSION_FLAG()) // CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Delay: Code mission flag still set.") // EXIT // ENDIF g_sAutosaveData.bBeenOffMission = TRUE ENDIF IF IS_PED_INJURED(PLAYER_PED_ID()) CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Delay: Player is dead.") EXIT ENDIF IF IS_AUTO_SAVE_IN_PROGRESS() CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Delay: The game is current saving.") EXIT ENDIF IF IS_MEMORY_CARD_IN_USE() CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Delay: memory card is in use.") EXIT ENDIF IF g_flowUnsaved.bFlowControllerBusy CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Failed: Flow is changing state. Waiting until it is stable.") //g_sAutosaveData.bComplete = TRUE //commented out this line as it was preventing saves after missions EXIT ENDIF IF NOT (IS_PLAYER_PLAYING(PLAYER_ID())) CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Delay: Player is not playing.") EXIT ENDIF IF IS_PLAYER_BROWSING_ITEMS_IN_ANY_SHOP() //CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Delay: Player is using shop.") EXIT ENDIF IF IS_PLAYER_SWITCH_IN_PROGRESS() OR IS_PLAYER_PED_SWITCH_IN_PROGRESS() CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Delay: Waiting for player switch to end") EXIT ENDIF IF (IS_PHONE_ONSCREEN() AND GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH(HASH("appTrackify")) = 0) #IF IS_DEBUG_BUILD IF NOT g_d_bAutoSavePhoneOnScreenSpam CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Delay: Cellphone is still on screen, holding until it isn't.") g_d_bAutoSavePhoneOnScreenSpam = TRUE ENDIF #ENDIF EXIT ENDIF IF IS_MISSION_STAT_UPLOAD_PENDING() CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Delay: waiting for IS_MISSION_STAT_UPLOAD_PENDING to return false") EXIT ENDIF #IF IS_DEBUG_BUILD g_d_bAutoSavePhoneOnScreenSpam = FALSE #ENDIF IF CHECK_PLAYER_MISSION_BLIPS_on_autosave() CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] CHECK_PLAYER_MISSION_BLIPS_on_autosave returned TRUE") g_sAutosaveData.bIgnoreScreenFade = TRUE IF ARE_VECTORS_EQUAL(g_savedGlobals.sRepeatPlayData.mPlayerStruct.vPos, <<0.0,0.0,0.0>>) g_savedGlobals.sFlowCustom.wasFadedOut_switch = TRUE ELSE PRINTLN("[AUTOSAVE] - keep wasFadedOut_switch false for quicksave, inform Alwyn (mission blip)") ENDIF ELSE CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] CHECK_PLAYER_MISSION_BLIPS_on_autosave returned false") ENDIF IF NOT g_sAutosaveData.bIgnoreScreenFade IF NOT (IS_SCREEN_FADED_IN()) CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Delay: Screen is not faded in.") EXIT ENDIF IF (IS_FRONTEND_FADING()) CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Delay: Front end is fading.") EXIT ENDIF SET_FADE_IN_AFTER_LOAD(TRUE)//set this back to default g_savedGlobals.sFlowCustom.wasFadedOut = FALSE g_savedGlobals.sFlowCustom.wasFadedOut_switch = FALSE ELSE SET_FADE_IN_AFTER_LOAD(FALSE) g_savedGlobals.sFlowCustom.wasFadedOut = TRUE IF ARE_VECTORS_EQUAL(g_savedGlobals.sRepeatPlayData.mPlayerStruct.vPos, <<0.0,0.0,0.0>>) g_savedGlobals.sFlowCustom.wasFadedOut_switch = TRUE ELSE PRINTLN("[AUTOSAVE] - keep wasFadedOut_switch false for quicksave, inform Alwyn (bIgnoreScreenFade)") ENDIF ENDIF //Check to see if any strands are sitting on DO_MISSION_NOW flow commands. //If they are we need to set the wasFadedOut flag to enure the screen doesn't fade in //on loading until after the mission is running. INT iStrandIndex BOOL bMissionAutostartFound = FALSE REPEAT MAX_STRANDS iStrandIndex INT iCommandPos = g_savedGlobals.sFlow.strandSavedVars[iStrandIndex].thisCommandPos IF iCommandPos != ILLEGAL_ARRAY_POSITION IF g_flowUnsaved.flowCommands[iCommandPos].command = FLOW_DO_MISSION_NOW PRINTLN("[AUTOSAVE] Considering setting SET_FADE_IN_AFTER_LOAD as strand ", GET_STRAND_DISPLAY_STRING_FROM_STRAND_ID(INT_TO_ENUM(STRANDS, iStrandIndex)), " is on a DO_MISSION_NOW_COMMAND.") //Only set this flag if this strand doesn't have a save command //point override set. BOOL bOverrideSetOnStrand = FALSE INT iOverrideIndex REPEAT g_savedGlobals.sFlowCustom.numberStoredOverrides iOverrideIndex IF ENUM_TO_INT(g_savedGlobals.sFlowCustom.strandToOverride[iOverrideIndex]) = iStrandIndex IF NOT g_savedGlobals.sFlowCustom.applyOnMPSwitchOnly[iOverrideIndex] PRINTLN("[AUTOSAVE] Strand ", GET_STRAND_DISPLAY_STRING_FROM_STRAND_ID(INT_TO_ENUM(STRANDS, iStrandIndex)), " had a save override set. Backing out of setting SET_FADE_IN_AFTER_LOAD.") bOverrideSetOnStrand = TRUE ENDIF ENDIF ENDREPEAT IF NOT bOverrideSetOnStrand PRINTLN("[AUTOSAVE] SET_FADE_IN_AFTER_LOAD set for strand ", GET_STRAND_DISPLAY_STRING_FROM_STRAND_ID(INT_TO_ENUM(STRANDS, iStrandIndex)), ".") SET_FADE_IN_AFTER_LOAD(FALSE) g_savedGlobals.sFlowCustom.wasFadedOut = TRUE g_savedGlobals.sFlowCustom.wasFadedOut_switch = FALSE bMissionAutostartFound = TRUE ENDIF ENDIF ENDIF ENDREPEAT // Do the save PERFORM_PRE_SAVEGAME_ROUTINE(TRUE, FALSE, bMissionAutostartFound) CLEAR_REPLAY_STATS()//these will have been processed at this point DO_AUTO_SAVE() g_sAutosaveData.bPerforming = TRUE IF NOT (g_sAutosaveData.bFirstAutosaveComplete) CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] This is the first attempt to autosave.") ENDIF // If autosave is switched off then the 'autosave in progress' flag should be false immediately IF NOT (IS_AUTO_SAVE_IN_PROGRESS()) CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Failed: Autosave is switched off.") IF NOT (g_sAutosaveData.bFirstAutosaveComplete) ADD_HELP_TO_FLOW_QUEUE("SAVE_OFF", FHP_HIGH) CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Letting player know how to turn on autosave.") ENDIF EXIT ENDIF g_sAutosaveData.bInProgress = TRUE CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Performing autosave.") ENDIF // Wait for the save to finish IF (IS_AUTO_SAVE_IN_PROGRESS()) IF IS_WARNING_MESSAGE_ACTIVE() DISABLE_CELLPHONE_THIS_FRAME_ONLY() DISABLE_SELECTOR_THIS_FRAME() ENDIF EXIT ENDIF // We can tell a save was successful by checking the 'autosave off' flag IF (g_sAutosaveData.bInProgress) IF (GET_IS_AUTO_SAVE_OFF()) CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Failed: Autosave has just been turned off.") ELSE CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Success: Autosave process complete.") ENDIF ENDIF // Clear the ignore screen flag as we're ending this autosave. IF g_sAutosaveData.bIgnoreScreenFade IF g_sAutosaveData.iQueuedRequests = 0 CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] Success:Clearing the ignore screen fade flag.") g_sAutosaveData.bIgnoreScreenFade = FALSE ENDIF ENDIF // Setting the 'complete' flag allows other flags to be reset when this proc is called again. g_sAutosaveData.bComplete = TRUE g_sAutosaveData.bFirstAutosaveComplete = TRUE ENDPROC SCRIPT CPRINTLN(DEBUG_SYSTEM,"[AUTOSAVE] \nStarting autosave 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)) PRINTSTRING("...autosave_controller.sc has been forced to cleanup (SP to MP)") CLEANUP_SCRIPT() ENDIF #IF IS_DEBUG_BUILD SETUP_AUTOSAVE_WIDGETS() #ENDIF // Main loop WHILE TRUE WAIT(0) #IF IS_DEBUG_BUILD MAINTAIN_AUTOSAVE_WIDGETS() #ENDIF // Perform the main processing SWITCH eScriptStage CASE SCRIPT_STAGE_INITIALISE INITIALISE_CONTROLLER() BREAK CASE SCRIPT_STAGE_WAIT_FOR_REQUEST CHECK_AUTOSAVE_REQUEST() BREAK CASE SCRIPT_STAGE_PERFORM_AUTOSAVE PERFORM_AUTOSAVE() BREAK CASE SCRIPT_STAGE_CLEANUP CLEANUP_SCRIPT() BREAK ENDSWITCH ENDWHILE ENDSCRIPT