//╒═════════════════════════════════════════════════════════════════════════════╕ //│ 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