Files
2025-09-29 00:52:08 +02:00

445 lines
15 KiB
Python
Executable File

//
// Author: Ben Rollinson Date: 13/05/11
//
//
// Flow Autoplay System
//
// A light-weight system that runs alongside the flow controller
// and automatically steps through the game flow while logging
// progress. This is done by simulating J-skip and Q-skip key
// presses on the keyboard.
//
//
USING "rage_builtins.sch"
USING "globals.sch"
USING "commands_script.sch"
#IF IS_DEBUG_BUILD
USING "commands_cutscene.sch"
USING "flow_reset_GAME.sch"
CONST_INT SIMULATE_KEYPRESS_DELAY_TIME 6000 //How long does the script wait between simulated skip keypresses (in milliseconds).
CONST_INT SKIP_LIMIT 30 //How many skips without flow progress before the system decides the flow is stuck.
STRING m_strFile = "autoplay.log"
STRING m_strPath = "X:/gta5/build/dev/"
BOOL m_bResetPlaythrough = FALSE
BOOL bShownCutMessage = FALSE
INT m_iSimulateDebugPressTimer
INT m_iSkipWithoutChangeCounter = 0
INT m_iPlaythroughCount = 0
TEXT_LABEL_31 m_txtLastRunningMissionName = "NO MISSION"
TEXT_LABEL_63 m_txtLogString
TEXT_LABEL_63 m_txtAutoplayMessage
enumCharacterList m_eLastChar = CHAR_BLANK_ENTRY
WIDGET_GROUP_ID widgetGroup
BOOL bPauseTimer = FALSE
//Log the start of a new session.
PROC Log_New_Autoplay_Session()
OPEN_NAMED_DEBUG_FILE(m_strPath, m_strFile)
SAVE_NEWLINE_TO_NAMED_DEBUG_FILE(m_strPath, m_strFile)
SAVE_NEWLINE_TO_NAMED_DEBUG_FILE(m_strPath, m_strFile)
SAVE_STRING_TO_NAMED_DEBUG_FILE("---------------------------------------------------",m_strPath, m_strFile)
SAVE_NEWLINE_TO_NAMED_DEBUG_FILE(m_strPath, m_strFile)
m_txtLogString = "[NEW AUTOPLAY SESSION | SKIP_LIMIT="
m_txtLogString += SKIP_LIMIT
m_txtLogString += "]"
SAVE_STRING_TO_NAMED_DEBUG_FILE(m_txtLogString, m_strPath, m_strFile)
CLOSE_DEBUG_FILE()
ENDPROC
//Log the start of a new playthrough attempt.
PROC Log_New_Playthrough()
m_iPlaythroughCount++
OPEN_NAMED_DEBUG_FILE(m_strPath, m_strFile)
SAVE_NEWLINE_TO_NAMED_DEBUG_FILE(m_strPath, m_strFile)
SAVE_STRING_TO_NAMED_DEBUG_FILE("---------------------------------------------------",m_strPath, m_strFile)
SAVE_NEWLINE_TO_NAMED_DEBUG_FILE(m_strPath, m_strFile)
m_txtLogString = "[PLAYTHROUGH #"
m_txtLogString += m_iPlaythroughCount
m_txtLogString += "]"
SAVE_STRING_TO_NAMED_DEBUG_FILE(m_txtLogString, m_strPath, m_strFile)
SAVE_NEWLINE_TO_NAMED_DEBUG_FILE(m_strPath, m_strFile)
CLOSE_DEBUG_FILE()
ENDPROC
//Log a single simulated keypress.
PROC Log_Skip_Keypress()
OPEN_NAMED_DEBUG_FILE(m_strPath, m_strFile)
SAVE_STRING_TO_NAMED_DEBUG_FILE(".",m_strPath, m_strFile)
CLOSE_DEBUG_FILE()
ENDPROC
PROC Draw_Autoplay_Message()
DRAW_RECT(0.5, 0.26, 0.22, 0.09, 0, 0, 0, 180)
SET_TEXT_FONT(FONT_STANDARD)
SET_TEXT_SCALE (0.4, 0.42)
SET_TEXT_WRAP(0.0, 1.0)
SET_TEXT_DROPSHADOW (0,0,0,0,255)
SET_TEXT_COLOUR(255,255,255,85)
SET_TEXT_EDGE (0,0,0,0,255)
SET_TEXT_PROPORTIONAL (FALSE)
SET_TEXT_JUSTIFICATION(FONT_CENTRE)
DISPLAY_TEXT_WITH_LITERAL_STRING(0.5, 0.225, "STRING", "AutoPlay")
SET_TEXT_FONT(FONT_STANDARD)
SET_TEXT_SCALE (0.52, 0.57)
SET_TEXT_WRAP(0.0, 1.0)
SET_TEXT_DROPSHADOW (0,0,0,0,225)
SET_TEXT_COLOUR(0,200,0,180)
SET_TEXT_EDGE (0,0,0,0,255)
SET_TEXT_PROPORTIONAL (FALSE)
SET_TEXT_JUSTIFICATION(FONT_CENTRE)
DISPLAY_TEXT_WITH_LITERAL_STRING(0.5, 0.255, "STRING", m_txtAutoplayMessage)
ENDPROC
PROC SETUP_FOR_RAGE_WIDGETS()
widgetGroup = START_WIDGET_GROUP("Auto-PT Timer")
ADD_WIDGET_BOOL("Pause AutoSkip Timer", bPauseTimer)
STOP_WIDGET_GROUP()
ENDPROC
PROC CLEAN_UP_WIDGET()
IF DOES_WIDGET_GROUP_EXIST(widgetGroup)
DELETE_WIDGET_GROUP(widgetGroup)
ENDIF
ENDPROC
#ENDIF
SCRIPT
#IF IS_DEBUG_BUILD
g_bFlowMetricZonesEnabled = TRUE //Output metrics by default - had to turn this off due to the metrics stuff asserting
g_bDebugBlockAutosaves = TRUE //Disable autosaving by default.
//Check command line to see if we want a smoketest running.
g_bFlowSmokeTestEnabled = ARE_STRINGS_EQUAL(GET_COMMANDLINE_PARAM("smoketest"),"codebuilder_stats")
SET_PROFILING_OF_THIS_SCRIPT(TRUE)
//Make the player invincible.
FAKE_KEY_PRESS(KEY_V)
//Flag a global to inform other systems that the autoplay system is active.
g_bFlowAutoplayInProgress = TRUE
//Do we need to wait for the smoketest to be ready?
IF g_bFlowSmoketestEnabled
CPRINTLN(DEBUG_AUTOPLAY, "Waiting for smoketest to start.")
WHILE NOT SMOKETEST_STARTED()
WAIT(0)
ENDWHILE
CPRINTLN(DEBUG_AUTOPLAY, "New smoketest started.")
ENDIF
//Put the game into gameflow mode and activate the prologue strand.
Activate_Gameflow_At_First_Strand()
Log_New_Autoplay_Session()
Log_New_Playthrough()
SETUP_FOR_RAGE_WIDGETS()
WHILE TRUE
//Automatically clear wanted level if we have one. (Cop car check is so we can autoplay past Michael's ambient Amanda mission
IF IS_PLAYER_PLAYING(PLAYER_ID()) AND NOT IS_PED_IN_ANY_POLICE_VEHICLE(PLAYER_PED_ID())
IF GET_PLAYER_WANTED_LEVEL(PLAYER_ID()) > 0
SET_PLAYER_WANTED_LEVEL(PLAYER_ID(), 0)
SET_PLAYER_WANTED_LEVEL_NOW(PLAYER_ID())
ENDIF
ENDIF
// Hacks to get around various autoplay issues
IF NOT IS_ENTITY_DEAD(PLAYER_PED_ID())
// Stop Amanda getting killed if we're playing Michael's Amanda Event
IF IS_PED_IN_ANY_POLICE_VEHICLE(PLAYER_PED_ID())
VEHICLE_INDEX mVeh = GET_VEHICLE_PED_IS_IN(PLAYER_PED_ID())
IF NOT IS_ENTITY_DEAD(mVeh)
IF NOT IS_VEHICLE_SEAT_FREE(mVeh, VS_BACK_RIGHT)
PED_INDEX mAmanda = GET_PED_IN_VEHICLE_SEAT(mVeh, VS_BACK_RIGHT)
IF NOT IS_ENTITY_DEAD(mAmanda)
SET_ENTITY_PROOFS(mAmanda, TRUE, TRUE, TRUE, TRUE, TRUE)
ENDIF
ENDIF
ENDIF
ENDIF
ENDIF
IF m_iSimulateDebugPressTimer < GET_GAME_TIMER()
//Is a mocap cutscene running?
IF IS_CUTSCENE_PLAYING()
IF NOT bShownCutMessage
CPRINTLN(DEBUG_AUTOPLAY, "Stopping Cutscene")
bShownCutMessage = TRUE
ENDIF
STOP_CUTSCENE()
ELSE
IF bShownCutMessage = TRUE
bShownCutMessage = FALSE
ENDIF
IF NOT IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_RANDOM_EVENT)
//Simulate a J-skip, we're on mission or off-mission and not on an RE.
CPRINTLN(DEBUG_AUTOPLAY, "J-skipping...", m_iSkipWithoutChangeCounter+1 )
FAKE_KEY_PRESS(KEY_J)
ELSE
//Simulate S-pass if we're offmission on an RE.
CPRINTLN(DEBUG_AUTOPLAY, "S-skipping...", m_iSkipWithoutChangeCounter+1)
FAKE_KEY_PRESS(KEY_S)
ENDIF
//If we're off mission and the phone has been left on screen, put it away.
IF NOT IS_CURRENTLY_ON_MISSION_TO_TYPE()
IF IS_PHONE_ONSCREEN()
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
HANG_UP_AND_PUT_AWAY_PHONE()
ENDIF
ENDIF
//496024: If the player is off mission in water set them to some safe place out of water.
IF NOT IS_ENTITY_DEAD(PLAYER_PED_ID())
IF IS_ENTITY_IN_WATER(PLAYER_PED_ID())
SET_ENTITY_COORDS(PLAYER_PED_ID(), << -325.4346, 1365.6914, 346.2589 >>)
ENDIF
ENDIF
ENDIF
//Log this key press.
Log_Skip_Keypress()
//Increment the skip counter.
m_iSkipWithoutChangeCounter++
m_iSimulateDebugPressTimer = GET_GAME_TIMER() + SIMULATE_KEYPRESS_DELAY_TIME
ENDIF
ELSE
IF bPauseTimer
m_iSimulateDebugPressTimer = GET_GAME_TIMER() + SIMULATE_KEYPRESS_DELAY_TIME
ENDIF
IF IS_CURRENTLY_ON_MISSION_TO_TYPE()
//Update how long until the next J-skip.
m_txtAutoplayMessage = "Simulating J-skip in "
m_txtAutoplayMessage += CEIL(TO_FLOAT(m_iSimulateDebugPressTimer - GET_GAME_TIMER()) / 1000.0)
ELIF IS_CURRENTLY_ON_MISSION_OF_TYPE(MISSION_TYPE_RANDOM_EVENT)
// Update how long until the next S-skip
m_txtAutoplayMessage = "Simulating S-skip in "
m_txtAutoplayMessage += CEIL(TO_FLOAT(m_iSimulateDebugPressTimer - GET_GAME_TIMER()) / 1000.0)
ELSE
//Update how long until the next Off-skip.
m_txtAutoplayMessage = "Simulating Off-skip in "
m_txtAutoplayMessage += CEIL(TO_FLOAT(m_iSimulateDebugPressTimer - GET_GAME_TIMER()) / 1000.0)
ENDIF
ENDIF
/*
// Skip any missions that we know crash the autoplay script.
IF ARE_STRINGS_EQUAL(g_txtFlowAutoplayRunningMission, "M_FAM1")
OR ARE_STRINGS_EQUAL(g_txtFlowAutoplayRunningMission, "M_LS1b") // Skipping these because they always crash.
OR ARE_STRINGS_EQUAL(g_txtFlowAutoplayRunningMission, "M_ARM1")
IF NOT bShownCutMessage
CPRINTLN(DEBUG_AUTOPLAY, "Skipping this mission because it always crashes - ", g_txtFlowAutoplayRunningMission)
ENDIF
FAKE_KEY_PRESS(KEY_S)
ELSE
IF bShownCutMessage
bShownCutMessage = FALSE
ENDIF
ENDIF
*/
//Have we reached our skip limit? Is the flow stuck?
IF m_iSkipWithoutChangeCounter >= SKIP_LIMIT
IF IS_CURRENTLY_ON_MISSION_TO_TYPE()
CPRINTLN(DEBUG_AUTOPLAY, "Hit J-skip limit!")
//Too many J-skips. Log this problem and try and auto-pass the mission.
OPEN_NAMED_DEBUG_FILE(m_strPath, m_strFile)
SAVE_STRING_TO_NAMED_DEBUG_FILE("[J-skips stuck!][Attempting S-skip]",m_strPath, m_strFile)
CLOSE_DEBUG_FILE()
//Simulate an S-skip to try and auto-pass this mission.
FAKE_KEY_PRESS(KEY_S)
WAIT(1000)
//Did the S-skip work?
IF ARE_STRINGS_EQUAL(m_txtLastRunningMissionName, g_txtFlowAutoplayRunningMission)
//No. Log this and reset the playthrough.
OPEN_NAMED_DEBUG_FILE(m_strPath, m_strFile)
SAVE_STRING_TO_NAMED_DEBUG_FILE("[S-skip failed]",m_strPath, m_strFile)
SAVE_NEWLINE_TO_NAMED_DEBUG_FILE(m_strPath, m_strFile)
SAVE_NEWLINE_TO_NAMED_DEBUG_FILE(m_strPath, m_strFile)
CLOSE_DEBUG_FILE()
m_bResetPlaythrough = TRUE
ELSE
//Yes, flow change noted. Reset counter.
m_iSkipWithoutChangeCounter = 0
ENDIF
ELSE
CPRINTLN(DEBUG_AUTOPLAY, "Hit Off-mission skip limit!")
//Too many Off-mission skips. Log this problem and then reset the playthrough.
OPEN_NAMED_DEBUG_FILE(m_strPath, m_strFile)
SAVE_STRING_TO_NAMED_DEBUG_FILE("[Off-skips stuck!]",m_strPath, m_strFile)
SAVE_NEWLINE_TO_NAMED_DEBUG_FILE(m_strPath, m_strFile)
SAVE_NEWLINE_TO_NAMED_DEBUG_FILE(m_strPath, m_strFile)
CLOSE_DEBUG_FILE()
m_bResetPlaythrough = TRUE
ENDIF
ELSE
IF m_iSkipWithoutChangeCounter >= SKIP_LIMIT-10
IF IS_SCREEN_FADED_OUT() AND NOT IS_SCREEN_FADING_IN()
CPRINTLN(DEBUG_AUTOPLAY, "Fading the screen in")
DO_SCREEN_FADE_IN(1000)
ENDIF
ENDIF
ENDIF
//Check if the current running mission has changed.
IF NOT ARE_STRINGS_EQUAL(m_txtLastRunningMissionName, g_txtFlowAutoplayRunningMission)
//The current running mission has changed. Log this.
IF ARE_STRINGS_EQUAL(g_txtFlowAutoplayRunningMission, "NO MISSION")
CPRINTLN(DEBUG_AUTOPLAY, "Noted that mission ", m_txtLastRunningMissionName, " just ended.")
//Was the mission passed or failed?
IF g_bFlowAutoplayJustPassed
OPEN_NAMED_DEBUG_FILE(m_strPath, m_strFile)
SAVE_STRING_TO_NAMED_DEBUG_FILE("[Passed]",m_strPath, m_strFile)
SAVE_NEWLINE_TO_NAMED_DEBUG_FILE(m_strPath, m_strFile)
CLOSE_DEBUG_FILE()
ELSE
OPEN_NAMED_DEBUG_FILE(m_strPath, m_strFile)
SAVE_STRING_TO_NAMED_DEBUG_FILE("[Failed]",m_strPath, m_strFile)
SAVE_NEWLINE_TO_NAMED_DEBUG_FILE(m_strPath, m_strFile)
CLOSE_DEBUG_FILE()
ENDIF
//Flow change noted. Reset counter.
m_iSkipWithoutChangeCounter = 0
ELSE
CPRINTLN(DEBUG_AUTOPLAY, "Noted that mission ", g_txtFlowAutoplayRunningMission, " just started.")
OPEN_NAMED_DEBUG_FILE(m_strPath, m_strFile)
SAVE_NEWLINE_TO_NAMED_DEBUG_FILE(m_strPath, m_strFile)
m_txtLogString = "["
m_txtLogString += g_txtFlowAutoplayRunningMission
m_txtLogString += "]"
SAVE_STRING_TO_NAMED_DEBUG_FILE(m_txtLogString,m_strPath, m_strFile)
CLOSE_DEBUG_FILE()
//Flow change noted. Reset counter.
m_iSkipWithoutChangeCounter = 0
ENDIF
ENDIF
//Check if the current character has changed.
enumCharacterList eCurrentChar = GET_CURRENT_PLAYER_PED_ENUM()
IF m_eLastChar <> eCurrentChar
//Character has changed. Log who we have switched to.
OPEN_NAMED_DEBUG_FILE(m_strPath, m_strFile)
m_txtLogString = "["
SWITCH(eCurrentChar)
CASE CHAR_MICHAEL
m_txtLogString += "M"
BREAK
CASE CHAR_FRANKLIN
m_txtLogString += "F"
BREAK
CASE CHAR_TREVOR
m_txtLogString += "T"
BREAK
DEFAULT
m_txtLogString += "Invalid Character Switch!"
BREAK
ENDSWITCH
m_txtLogString += "]"
SAVE_STRING_TO_NAMED_DEBUG_FILE(m_txtLogString,m_strPath, m_strFile)
CLOSE_DEBUG_FILE()
//Remember the new current character.
m_eLastChar = eCurrentChar
ENDIF
//Has a reset been requested?
IF m_bResetPlaythrough
CPRINTLN(DEBUG_AUTOPLAY, "Restting the playthrough.")
m_iSkipWithoutChangeCounter = 0
m_eLastChar = CHAR_BLANK_ENTRY
m_bResetPlaythrough = FALSE
//End smoketest if one is running.
IF g_bFlowSmoketestEnabled
CPRINTLN(DEBUG_AUTOPLAY, "Ending smoketest for this playthrough.")
SMOKETEST_END()
ENDIF
//Deactivate the game flow.
g_savedGlobals.sFlow.isGameflowActive = FALSE
//Trigger a singleplayer force cleanup as part of the reset procedure.
FORCE_CLEANUP(FORCE_CLEANUP_FLAG_DEBUG_MENU)
//Log the start of the new playthrough.
Log_New_Playthrough()
//Do we need to wait for the smoketest to be ready?
IF g_bFlowSmoketestEnabled
CPRINTLN(DEBUG_AUTOPLAY, "Waiting for smoketest to start.")
WHILE NOT SMOKETEST_STARTED()
WAIT(0)
ENDWHILE
CPRINTLN(DEBUG_AUTOPLAY, "New smoketest started.")
ENDIF
//Reactivate gameflow at the beginning.
Activate_Gameflow_At_First_Strand()
ENDIF
//CLEAN_UP_WIDGET()
//Store current running mission index as the last before stepping to the next frame.
m_txtLastRunningMissionName = g_txtFlowAutoplayRunningMission
//Draw the autoplay message box.
Draw_Autoplay_Message()
WAIT(0)
ENDWHILE
#ENDIF
//Will never be reached in debug mode.
//Clean up immediately in release mode.
TERMINATE_THIS_THREAD()
ENDSCRIPT