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

424 lines
15 KiB
Python
Executable File

//Compile out Title Update changes to header functions.
//Must be before includes.
//CONST_INT USE_TU_CHANGES 0 // Removed by Kenneth R.
USING "RC_Setup_public.sch"
USING "cutscene_public.sch"
USING "RC_launcher_public.sch"
USING "RC_Setup_public.sch"
USING "initial_scenes_Nigel.sch"
// *****************************************************************************************
// SCRIPT NAME : launcher_Nigel.sc
//
// AUTHOR : David Roberts / Andrew Minghella
//
// DESCRIPTION : Launcher script that determines which RC mission scene to setup.
// This launcher script should be attached to appropriate world point.
//
// If multiple missions share same coord or are within close proximity,
// we should just add one world point, and adjust the tolerance float.
// *****************************************************************************************
// ------------Variables----------------------
CONST_FLOAT WORLD_POINT_COORD_TOLERANCE 1.0
INT iCutsceneLoadRequestID = NULL_OFFMISSION_CUTSCENE_REQUEST // ID to register off-mission cutscene load request with cutscene_controller.
BLIP_INDEX biLeadIn
ENUM convPauseState
CONVPS_READY,
CONVPS_PAUSED,
CONVPS_PLAYING,
CONVPS_COMPLETE
ENDENUM
convPauseState convState = CONVPS_READY
// ----------Functions -------------------------
/// PURPOSE: Play a conversation in full from two RC peds
FUNC BOOL PLAY_LAUNCHER_FULL_CONVERSATION_ONCE(g_structRCScriptArgs& sData, VECTOR v_ped_pos, STRING s_dialogue_slot, STRING s_dialogue_to_play, INT ped0_dialogue_number, STRING ped0_dialogue_name, INT ped1_dialogue_number, STRING ped1_dialogue_name, convPauseState& convPS)
IF NOT (convPS = CONVPS_COMPLETE)
IF IS_IT_SAFE_TO_TRIGGER_SCRIPT_TYPE(ST_RANDOM_CHARACTER)
IF NOT IS_ANY_CONVERSATION_ONGOING_OR_QUEUED()
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
AND IS_ENTITY_ALIVE(sData.pedID[0])
AND IS_ENTITY_ALIVE(sData.pedID[1])
AND GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(PLAYER_PED_ID(), v_ped_pos) < 20.0
PRINT_LAUNCHER_DEBUG("start conversation")
structPedsForConversation s_conversation
IF (convPS = CONVPS_READY)
ADD_PED_FOR_DIALOGUE(s_conversation, ped0_dialogue_number, sData.pedID[0], ped0_dialogue_name)
ADD_PED_FOR_DIALOGUE(s_conversation, ped1_dialogue_number, sData.pedID[1], ped1_dialogue_name)
ADD_NON_CRITICAL_STANDARD_CONVERSATION_TO_BUFFER(s_conversation, s_dialogue_slot, s_dialogue_to_play, CONV_PRIORITY_MEDIUM)
convPS = CONVPS_PLAYING
ELIF (convPS = CONVPS_PLAYING)
PRINT_LAUNCHER_DEBUG("conversation ended")
convPS = CONVPS_COMPLETE
RETURN TRUE
ENDIF
ENDIF
ELSE
IF RC_IS_THIS_CONVERSATION_ROOT_PLAYING(s_dialogue_to_play)
AND NOT (convPS = CONVPS_PAUSED)
AND IS_ENTITY_ALIVE(PLAYER_PED_ID())
AND GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(PLAYER_PED_ID(), v_ped_pos) >= 20.0
PRINT_LAUNCHER_DEBUG("pause conversation")
PAUSE_FACE_TO_FACE_CONVERSATION(TRUE)
convPS = CONVPS_PAUSED
ENDIF
ENDIF
IF (GET_DISTANCE_BETWEEN_ENTITY_AND_COORD(PLAYER_PED_ID(), v_ped_pos) < 20.0)
AND (convPS = CONVPS_PAUSED)
PRINT_LAUNCHER_DEBUG("unpause conversation")
PAUSE_FACE_TO_FACE_CONVERSATION(FALSE)
convPS = CONVPS_PLAYING
ENDIF
ENDIF
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Checks for if the player has damaged any peds in the RC initial scene
/// Used only for Nigel 1D, to fix B*1897651
/// PARAMS:
/// sData - launcher data struct
/// RETURNS:
/// True if player has damaged any of the peds, false otherwise
FUNC BOOL HAS_PLAYER_DAMAGED_ANY_N1D_PEDS(g_structRCScriptArgs& sData)
INT iCount = 0
IF IS_ENTITY_ALIVE(PLAYER_PED_ID())
FOR iCount = 0 to COUNT_OF(sData.pedID)-1
IF DOES_ENTITY_EXIST(sData.pedID[iCount])
IF IS_ENTITY_ALIVE(sData.pedID[iCount])
IF HAS_ENTITY_BEEN_DAMAGED_BY_ENTITY(sData.pedID[iCount], PLAYER_PED_ID())
OR IS_BULLET_IN_AREA(GET_ENTITY_COORDS(sData.pedID[iCount]), 7.5)
IF IS_IT_SAFE_TO_TRIGGER_SCRIPT_TYPE(ST_RANDOM_CHARACTER)
RC_STOP_PLAYER_VEHICLE_CHECK(sData.bAllowVehicleActivation, sData.eMissionID)
RETURN TRUE
ELSE
PRINT_LAUNCHER_DEBUG("Player damaged the N1D peds but it's not safe to trigger the mission!")
ENDIF
ENDIF
ELSE
IF IS_IT_SAFE_TO_TRIGGER_SCRIPT_TYPE(ST_RANDOM_CHARACTER)
RC_STOP_PLAYER_VEHICLE_CHECK(sData.bAllowVehicleActivation, sData.eMissionID)
RETURN TRUE
ELSE
PRINT_LAUNCHER_DEBUG("Player killed a N1D ped but it's not safe to trigger the mission!")
ENDIF
ENDIF
ENDIF
ENDFOR
ENDIF
RETURN FALSE
ENDFUNC
/// PURPOSE:
/// Does any necessary cleanup and terminates the launcher's thread.
/// PARAMS:
/// sData - launcher data struct
/// bCleanupEntities - do we want to cleanup the entities in the launcher struct
PROC Script_Cleanup(g_structRCScriptArgs& sData, BOOT_EVENT_VARS& beVars, BOOL bCleanupEntities = TRUE)
// Have to delete Al from boot before general cleanup
IF sData.eMissionID = RC_NIGEL_3
SAFE_DELETE_PED(sData.pedID[1])
ENDIF
// Remove lead-in blip
IF DOES_BLIP_EXIST(biLeadIn)
REMOVE_BLIP(biLeadIn)
ENDIF
// Remove scene entities
IF bCleanupEntities
PRINT_LAUNCHER_DEBUG("SCRIPT TERMINATING: Cleaning up entities in Launcher")
//B*1470358 - disable the interior in the launcher cleanup if the mission didn't launch (enabled in the initial scene)
IF sData.eMissionID = RC_NIGEL_1A
SET_INTERIOR_CAPPED(INTERIOR_V_ROCKCLUB, TRUE)
CLEANUP_NIGEL1A_SYNC_SCENE(sData)
PRINT_LAUNCHER_DEBUG(" Script_Cleanup - SET_INTERIOR_CAPPED(INTERIOR_V_ROCKCLUB, TRUE)")
ELIF sData.eMissionID = RC_NIGEL_1B
CLEANUP_NIGEL1B_SYNC_SCENE(sData)
ENDIF
RC_CleanupSceneEntities(sData, FALSE)
ENDIF
// Ensure mission areas and scenarios are restored
SETUP_AREA_FOR_MISSION(sData.eMissionID, FALSE)
// Mission specific cleanup
SWITCH sData.eMissionID
// Cleanup boot sound effects
CASE RC_NIGEL_3
IF beVars.iSoundID <> -1
RELEASE_SOUND_ID(beVars.iSoundID)
ENDIF
BREAK
ENDSWITCH
// Unload launcher animation dictionary
REMOVE_LAUNCHER_ANIM_DICT(sData.sAnims)
// Clear any cutscene requests with controller.
IF iCutsceneLoadRequestID != NULL_OFFMISSION_CUTSCENE_REQUEST
PRINT_LAUNCHER_DEBUG("SCRIPT TERMINATING: Ending off-mission cutscene request")
END_OFFMISSION_CUTSCENE_REQUEST(iCutsceneLoadRequestID)
ENDIF
// Stop launcher conversation
STRING sConversationRoot
SWITCH sData.eMissionID
CASE RC_NIGEL_1
sConversationRoot = "NIG1A_AMB"
BREAK
ENDSWITCH
RC_STOP_LAUNCHER_DIALOGUE(sConversationRoot)
//B*1574385 - Force update to blip in RandChar Controller if mission wasn't launched
IF bCleanupEntities
SET_RC_AWAITING_TRIGGER(sData.eMissionID)
ENDIF
RC_LAUNCHER_END()
// Kill the thread
PRINT_LAUNCHER_DEBUG("SCRIPT TERMINATED")
TERMINATE_THIS_THREAD()
ENDPROC
/// PURPOSE:
/// Creates the initial scene
/// PARAMS:
/// sData - launcher data struct
FUNC BOOL LOAD_INITIAL_SCENE(g_structRCScriptArgs& sData, TEST_POLY& pTrigger)
//Setup the correct initial scene
SWITCH sData.eMissionID
CASE RC_NIGEL_1
IF NOT SetupScene_NIGEL_1(sData)
RETURN FALSE
ENDIF
BREAK
CASE RC_NIGEL_1A
IF NOT SetupScene_NIGEL_1A(sData)
RETURN FALSE
ENDIF
BREAK
CASE RC_NIGEL_1B
IF NOT SetupScene_NIGEL_1B(sData)
RETURN FALSE
ENDIF
BREAK
CASE RC_NIGEL_1C
IF NOT SetupScene_NIGEL_1C(sData)
RETURN FALSE
ENDIF
BREAK
CASE RC_NIGEL_1D
IF NOT SetupScene_NIGEL_1D(sData, pTrigger)
RETURN FALSE
ENDIF
BREAK
CASE RC_NIGEL_2
IF NOT SetupScene_NIGEL_2(sData)
RETURN FALSE
ENDIF
BREAK
CASE RC_NIGEL_3
IF NOT SetupScene_NIGEL_3(sData)
RETURN FALSE
ENDIF
BREAK
ENDSWITCH
// Scene creation successful
PRINT_LAUNCHER_DEBUG("Created initial scene")
RETURN TRUE
ENDFUNC
/// PURPOSE:
/// Main script loops
/// PARAMS:
/// in_coords - world point co-ords
SCRIPT(coords_struct in_coords)
// Launcher priority for streaming requests
SET_THIS_IS_A_TRIGGER_SCRIPT(TRUE)
RC_LAUNCHER_START()
g_structRCScriptArgs sRCLauncherData // Scene information to pass to mission script
VECTOR vInCoords = <<0,0,0>> // Stores world point location
TEST_POLY pMissionTrigger // Custom area used for Nigel sub-missions
BOOT_EVENT_VARS bevNigel3 // Needed in Nigel3 initial scene
//Reset all basic values of the data, so each scene has to set them up correctly
RC_Reset_LauncherData(sRCLauncherData)
// Update world point
vInCoords = in_coords.vec_coord[0]
// Set soundID to -1 so we know whether it needs releasing
bevNigel3.iSoundID = -1
// Determine which RC mission we are attempting to launch (currently done on a launcher by launcher basis)
g_eRC_MissionIDs eRCMissions[7]
eRCMissions[0] = RC_NIGEL_1
eRCMissions[1] = RC_NIGEL_1A
eRCMissions[2] = RC_NIGEL_1B
eRCMissions[3] = RC_NIGEL_1C
eRCMissions[4] = RC_NIGEL_1D
eRCMissions[5] = RC_NIGEL_2
eRCMissions[6] = RC_NIGEL_3
// Setup callback when player is killed, arrested or goes to multiplayer
IF (HAS_FORCE_CLEANUP_OCCURRED(DEFAULT_FORCE_CLEANUP_FLAGS|FORCE_CLEANUP_FLAG_DEBUG_MENU|FORCE_CLEANUP_FLAG_REPEAT_PLAY))
PRINT_LAUNCHER_DEBUG("Force cleanup [TERMINATING]")
// Ensure candidate id is released in the event that the player has died
// or been arrested prior to mission launch
IF sRCLauncherData.eMissionID <> NO_RC_MISSION
IF (g_RandomChars[sRCLauncherData.eMissionID].rcMissionCandidateID <> NO_CANDIDATE_ID)
PRINT_LAUNCHER_DEBUG("Relinquishing candidate id...")
Mission_Over(g_RandomChars[sRCLauncherData.eMissionID].rcMissionCandidateID)
ENDIF
ENDIF
// Standard cleanup
Script_Cleanup(sRCLauncherData, bevNigel3)
ENDIF
//Pick which mission activated us
IF NOT DETERMINE_RC_TO_LAUNCH(eRCMissions, sRCLauncherData, vInCoords, WORLD_POINT_COORD_TOLERANCE)
RC_LAUNCHER_END()
PRINT_LAUNCHER_DEBUG("SCRIPT TERMINATED")
TERMINATE_THIS_THREAD() // B* 1510945 - don't call cleanup if nothing has been setup yet
ENDIF
// Check with the Random Character Controller to see if this script is allowed to launch
IF NOT CAN_RC_LAUNCH(sRCLauncherData.eMissionID)
RC_LAUNCHER_END()
PRINT_LAUNCHER_DEBUG("SCRIPT TERMINATED")
TERMINATE_THIS_THREAD() // B* 1510945 - don't call cleanup if nothing has been setup yet
ENDIF
// Shutdown launcher when we are incorrect character
IF Random_Character_Blocked_Due_To_Character(sRCLauncherData.eMissionID)
RC_LAUNCHER_END()
PRINT_LAUNCHER_DEBUG("SCRIPT TERMINATED")
TERMINATE_THIS_THREAD() // B* 1510945 - don't call cleanup if nothing has been setup yet
ENDIF
// The script is allowed to launch - set up the initial scene
WHILE NOT LOAD_INITIAL_SCENE(sRCLauncherData, pMissionTrigger)
WAIT(0)
IF NOT IS_WORLD_POINT_WITHIN_BRAIN_ACTIVATION_RANGE()
PRINT_LAUNCHER_DEBUG("Player out of range [TERMINATING]")
Script_Cleanup(sRCLauncherData, bevNigel3)
ENDIF
ENDWHILE
// Handle special case car boot thumps in Nigel 3
IF sRCLauncherData.eMissionID = RC_NIGEL_3
bevNigel3.timedSound = BE_INIT
ENDIF
// Clears area of non-mission entities and blood decals
CLEAR_AREA(vInCoords, sRCLauncherData.activationRange, TRUE)
// Loop to check conditions - should script terminate? or is player close enough to trigger mission?
WHILE (TRUE)
WAIT(0)
//Is the player still in activation range
IF NOT IS_RC_FINE_AND_IN_RANGE(sRCLauncherData)
Script_Cleanup(sRCLauncherData, bevNigel3)
ENDIF
SET_RC_AWAITING_TRIGGER(sRCLauncherData.eMissionID)
MANAGE_PRELOADING_RC_CUTSCENE(iCutsceneLoadRequestID, sRCLauncherData.sIntroCutscene, vInCoords)
// B*1347748 - set Di Napoli to trouser's variation
IF sRCLauncherData.eMissionID = RC_NIGEL_2
IF CAN_REQUEST_ASSETS_FOR_CUTSCENE_ENTITY()
SET_CUTSCENE_PED_COMPONENT_VARIATION(GET_AL_DI_NAPOLI_SCENE_HANDLE(), PED_COMP_LEG, 0, 0, GET_AL_DI_NAPOLI_PED_MODEL())
PRINT_LAUNCHER_DEBUG("Nigel 2 : set Di Napoli's cutscene trousers variation in launcher")
ENDIF
ENDIF
// Handle extra launcher anims for Nigel 1
IF IS_LAUNCHER_IDLE_ANIM_SETUP(extraAnims)
AND sRCLauncherData.eMissionID = RC_NIGEL_1
// Update idle and beckon animations on the second RC character.
UPDATE_LAUNCHER_ANIMS(sRCLauncherData.pedID[1], extraAnims, sRCLauncherData.eMissionID)
ENDIF
IF sRCLauncherData.eMissionID = RC_NIGEL_1 // B*1359585
PLAY_LAUNCHER_FULL_CONVERSATION_ONCE(sRCLauncherData, vInCoords, "NIG1AAU", "NIG1A_AMB", 0, "NIGEL", 1, "MRSTHORNHILL", convState)
// Handle ambient lead in dialogue for Nigel 2
ELIF sRCLauncherData.eMissionID = RC_NIGEL_2
PLAY_LAUNCHER_AMBIENT_DIALOGUE(sRCLauncherData, vInCoords, "NIG2AUD", "NIG2_AMB", 4, "NIGEL", DEFAULT, 14)
// Handle special case car boot thumps and pre-launch dialogue in Nigel 3
ELIF sRCLauncherData.eMissionID = RC_NIGEL_3
BOOT_THUMP_CYCLE(bevNigel3, sRCLauncherData.vehID[0], srcLauncherData.pedID[1])
PLAY_LAUNCHER_AMBIENT_DIALOGUE(sRCLauncherData, << -44.75, -1289.86, 29.16 >>, "NIGE3AU", "NIGEL3_AMB", 4, "NIGEL", 8000)
ENDIF
// Determine whether player is close enough to launch mission - Nigel 1D checks a poly
// Probably need to improve this somewhat
BOOL bLaunchMission = FALSE
IF sRCLauncherData.eMissionID = RC_NIGEL_1D
IF ARE_RC_TRIGGER_POLY_CONDITIONS_MET(sRCLauncherData, pMissionTrigger)
OR HAS_PLAYER_DAMAGED_ANY_N1D_PEDS(sRCLauncherData)
bLaunchMission = TRUE
ENDIF
ELSE
IF ARE_RC_TRIGGER_CONDITIONS_MET(sRCLauncherData)
bLaunchMission = TRUE
ENDIF
ENDIF
// Is the player close enough to start the mission off
IF bLaunchMission
//B* 2157539: Activate the multihead blinders instantly
IF sRCLauncherData.eMissionID = RC_NIGEL_2
SET_MULTIHEAD_SAFE(TRUE)
// Wait(500)
ENDIF
// Create mission blip that persists for lead-in scene
// Original will be removed when RC controller shuts down
IF DOES_RC_MISSION_HAVE_LEAD_IN(sRCLauncherData.eMissionID)
CREATE_BLIP_FOR_LEAD_IN(sRCLauncherData.eMissionID, biLeadIn)
ENDIF
//Launch the mission script
IF NOT LAUNCH_RC_MISSION(sRCLauncherData)
Script_Cleanup(sRCLauncherData, bevNigel3)
ENDIF
//Waits in a loop until we can terminate the launcher - flagged from mission at the moment
IF IS_RC_LAUNCHER_SAFE_TO_TERMINATE(sRCLauncherData.eMissionID)
Script_Cleanup(sRCLauncherData, bevNigel3, FALSE) //No need to clean up entities, as we should have passed them over to the mission script
ENDIF
ENDIF
ENDWHILE
// Script should never reach here. Always terminate with cleanup function.
ENDSCRIPT