global function CodeCallback_MapInit #if DEV global function skyboxchange global function GetOGPilot global function wallruntest global function Record_ZenGarden_Wallrun global function Record_ZenGarden_Slide global function Record_ZenGarden_DoubleJump global function shutdownscreentest global function SendTrainingGauntletStats global function FancyTeleport_EffectsAndSound global function Training_WeaponRacks_SetSolidity global function setfakeinstalldone global function Training_EnvArtColorCorrection_SetEnabled global function SimpleScreenShake global function GetSkitGuyInfo_ByName global function NudgeSkitGuy #endif const MARVIN_MODEL = $"models/robots/marvin/marvin.mdl" const asset FX_POD_LASER = $"P_pod_scan_laser_FP" const asset FX_POD_GLOWLIGHT = $"P_pod_door_glow_FP" const asset FX_POD_SCREEN_IN = $"P_pod_screen_lasers_IN" const asset FX_POD_SCREEN_OUT = $"P_pod_screen_lasers_OUT" const asset FX_POD_DLIGHT_CONSOLE1 = $"P_pod_Dlight_console1" const asset FX_POD_DLIGHT_CONSOLE2 = $"P_pod_Dlight_console2" //const asset FX_POD_DLIGHT_BACKLIGHT_SIDE = $"P_pod_Dlight_backlight_side" //const asset FX_POD_DLIGHT_BACKLIGHT_TOP = $"P_pod_Dlight_backlight_top" const asset FX_FANCY_TELEPORT_ENV_PULSE = $"P_ar_holopulse_CP" const asset FX_COCKPIT_LIGHT = $"xo_cockpit_dlight" const asset OG_PILOT_HELMET_MODEL = $"models/Humans/heroes/mlt_hero_anderson_helmet.mdl" const asset ANDERSON_PILOT_MODEL = $"models/humans/heroes/mlt_hero_anderson.mdl" const asset PILOT_MODEL_BAY1 = $"models/humans/pilots/sp_medium_geist_f.mdl" const asset PILOT_MODEL_BAY2 = $"models/humans/pilots/sp_medium_reaper_m.mdl" const asset BUDDY_MODEL_POSED_NO_ANIMS = $"models/Titans/buddy/BT_posed.mdl" const asset SAFETY_BATON_MODEL = $"models/industrial/safety_baton.mdl" const asset OG_PILOT_MODEL = $"models/humans/heroes/mlt_hero_lastimosa.mdl" const int OG_PILOT_MODEL_HEAD_IDX_BARE = 0 const int OG_PILOT_MODEL_HEAD_IDX_HELMET = 2 const int OG_PILOT_MODEL_DECAL_IDX = 0 const int OG_PILOT_MODEL_DECAL_IDX_BARE = 1 const string OG_WEAPON = "mp_weapon_rspn101" const string ANIM_OG_STANDING_IDLE = "OG_stand_upright_idle" const string ANIM_OG_STANDING_TALK = "OG_stand_upright_talk" const string ANIM_OG_SITTING_IDLE = "OG_sit_high_idle" const string ANIM_OG_SITTING_TALK = "OG_sit_high_talk" const string ANIM_OG_LEANING_IDLE = "OG_stand_lean_idle" const string ANIM_OG_LEANING_TALK = "OG_stand_lean_talk" const int SCRIPTED_PATH_WALK = 0 const int SCRIPTED_PATH_RUN = 1 const int MAX_RECREATED_OLD_WEAPONS = 16 const float TITANFALL_NAG_DURATION = 3.0 // extra time compensation for nag line playing when titanfall started const string TRAINING_PLAYER_SETTINGS = "pilot_solo_training" struct TrainingPod_dLightMapping { string scriptAlias asset fxName string attachName entity fxHandle } struct TrainingPod_LaserEmitter { entity ent string attachName vector ogAng bool sweepDone = false entity fxHandle } struct TrainingPod_GlowLightRow { array fxSpotsL array fxSpotsR } struct LoudspeakerVO_Info { string scriptAlias string soundAlias float duration } struct FiringRangeTarget { entity ent entity angleRefEnt entity mover bool wasDamaged vector ogAngles } struct SkitGuyInfo { int id string name string skitAnim entity guy entity skitRef } struct HangarTitanGroup { entity ref entity titan entity rack entity marvin entity pilot int titanSkin = -1 string titanAnim string rackAnim string marvinAnim string pilotAnim asset pilotModel vector rack_ogPos vector rack_ogAng float sequenceDuration float animInitialTime = 0.0 bool isInited = false } struct TrainingGauntletStats { bool didBeatRequiredTime = false int numRunsBeforeBeatRequiredTime = 0 int numChallengeRuns = 0 int numRestarts = 0 float bestTime = -1.0 int recommendedDifficulty = 0 } struct { #if DEV bool fakeInstallDone = false #endif bool gauntletMode = false entity player int playerInputType entity ogPilot entity ogTwin entity anderson entity titanTwin entity ogHelmet entity playerAnimWeapon entity ogPathMover entity animref_hangar entity animref_leaveGauntlet entity trainingPod array trainingPodGlowLightRows array trainingPodGlowLightFXHandles array trainingPodDLightMappings array trainingPodLaserEmitters float postWallrunVOEndTime = -1 float titanfallNagStartTime = -1 vector playerTitanCallInPos entity envArt_colorCorrectionEnt entity skycam_default entity skycam_glitch table loudspeakerVO = {} entity loudspeaker array firingRangeTargets = [] //table skitguys = {} array skitguys = [] TrainingGauntletStats trainingGauntletStats array scriptCreatedWeaponPickups = [] bool weaponPickupsHaveAmmo = false bool displayWeaponHUD = true } file void function CodeCallback_MapInit() { FlagSet( "FlightPath_TitanDrop" ) PrecacheParticleSystem( FX_POD_LASER ) PrecacheParticleSystem( FX_POD_GLOWLIGHT ) PrecacheParticleSystem( FX_POD_SCREEN_IN ) PrecacheParticleSystem( FX_POD_SCREEN_OUT ) PrecacheParticleSystem( FX_POD_DLIGHT_CONSOLE1 ) PrecacheParticleSystem( FX_POD_DLIGHT_CONSOLE2 ) //PrecacheParticleSystem( FX_POD_DLIGHT_BACKLIGHT_SIDE ) //PrecacheParticleSystem( FX_POD_DLIGHT_BACKLIGHT_TOP ) PrecacheParticleSystem( FX_FANCY_TELEPORT_ENV_PULSE ) PrecacheParticleSystem( FX_COCKPIT_LIGHT ) PrecacheModel( OG_PILOT_HELMET_MODEL ) PrecacheModel( OG_PILOT_MODEL ) PrecacheModel( ANDERSON_PILOT_MODEL ) PrecacheModel( PILOT_MODEL_BAY1 ) PrecacheModel( PILOT_MODEL_BAY2 ) PrecacheModel( BUDDY_MODEL_POSED_NO_ANIMS ) PrecacheModel( SAFETY_BATON_MODEL ) PrecacheModel( MARVIN_MODEL ) PrecacheModel( DATA_KNIFE_MODEL ) LoudspeakerVO_Setup() Training_SharedInit() RegisterSignal( "ButtonPressedJump" ) RegisterSignal( "ButtonPressedAttack" ) RegisterSignal( "PodIntro_OG_StartPodAnim" ) RegisterSignal( "PodInteriorSequenceDone" ) RegisterSignal( "FancyTeleportStart" ) RegisterSignal( "TargetRotate" ) RegisterSignal( "TargetDamaged" ) RegisterSignal( "Target_WaitForDamage_Start" ) RegisterSignal( "StopRepeatingGhostRecorder" ) RegisterSignal( "FiringRange_StopResettingTargets" ) RegisterSignal( "Gauntlet_StopTeleportingPlayerAtFinishLine" ) RegisterSignal( "FirstRun_OG_Creates_Ghost" ) RegisterSignal( "GauntletChallenge_FirstGhostAppear" ) RegisterSignal( "PlayerMadeSelection" ) RegisterSignal( "TrainingPod_BeginInteriorShutdown" ) RegisterSignal( "NPC_NewCommand" ) RegisterSignal( "LoudspeakerVO_Stop" ) RegisterSignal( "glitch_start" ) FlagInit( "PlayerPressedUse" ) FlagInit( "PlayerReloaded" ) FlagInit( "PodIntro_PodDoorsClosed" ) FlagInit( "PlayerLookedAtTopTarget" ) FlagInit( "PlayerLookedAtBottomTarget" ) FlagInit( "PodIntro_InteriorBootSequence_Starting" ) FlagInit( "OG_WhyWeFight_VO_Done" ) FlagInit( "FiringRange_Approach_OG_Sequence_Done" ) FlagInit( "ReloadTraining_PlayerPressedReload" ) FlagInit( "PlayerSprinted" ) FlagInit( "PlayerADSed" ) FlagInit( "FiringRangeWeaponSwapped" ) FlagInit( "FiringRange_AllTargetsKilled" ) FlagInit( "OG_MovedTo_GauntletEntrance" ) FlagInit( "Gauntlet_FirstRun_All_VO_Finished" ) FlagInit( "Gauntlet_FirstRun_Done" ) FlagInit( "ChallengeIntro_VO_Done" ) FlagInit( "Gauntlet_PlayingFeedbackVO" ) FlagInit( "PlayerUsedConversationInterface" ) FlagInit( "GauntletExitConvo_FinishedResponse" ) FlagInit( "TitanfallIntroConvo_FinishedResponse" ) FlagInit( "PlayerConfirmedGauntletExit" ) FlagInit( "PlayerLeavingGauntlet" ) FlagInit( "PlayerStartedTitanfall" ) FlagInit( "Titanfall_OG_FallingIn_VO_Start" ) FlagInit( "TitanfallGlitchStart" ) FlagInit( "PlayerWorldChangeThread" ) FlagInit( "Glitch_WorldChanging_Zen" ) FlagInit( "Glitch_WorldChanging_NonZen" ) FlagInit( "PodOutroStarted" ) FlagInit( "SimPodShutdown_LoudspeakerVO_Done" ) FlagInit( "MeetOG_StartScene" ) FlagInit( "CadillacMoment_MeetOG_Done" ) FlagInit( "CadillacMoment_MeetOG_StartFadeOut" ) FlagInit( "MeetOG_VO_Done" ) FlagClear( "AutomaticCheckpointsEnabled" ) AddClientCommandCallback( "Training_SetInputType", ClientCommand_Training_SetInputType ) AddClientCommandCallback( "Training_PlayerPressedUse", ClientCommand_Training_PlayerPressedUse ) AddClientCommandCallback( "Training_PlayerReloaded", ClientCommand_Training_PlayerReloaded ) AddClientCommandCallback( "topTarget", ClientCommand_LookTarget_Top ) AddClientCommandCallback( "bottomTarget", ClientCommand_LookTarget_Bottom ) AddCallback_EntitiesDidLoad( EntitiesDidLoad ) AddPlayerDidLoad( Training_PlayerDidLoad ) AddCallback_OnLoadSaveGame( Training_OnLoadSaveGame ) AddDamageCallback( "func_brush", Training_FuncBrush_OnDamaged ) TimerInit( "firingRangeNag", 15.0 ) TimerInit( "installWaitComment", 60.0 ) AddStartPoint( "Pod Intro", Training_PodIntro, null, Training_Skipped_PodIntro ) AddStartPoint( "Basic Movement", Training_BasicMovement, Training_Setup_BasicMovement, Training_Skipped_BasicMovement ) AddStartPoint( "Zen Garden", Training_ZenGarden, Training_Setup_ZenGarden, Training_Skipped_ZenGarden ) AddStartPoint( "Firing Range", Training_FiringRange, Training_Setup_FiringRange, Training_Skipped_FiringRange ) AddStartPoint( "Gauntlet", Training_Gauntlet, Training_Setup_Gauntlet, Training_Skipped_Gauntlet ) AddStartPoint( "Gauntlet Challenge", Training_GauntletChallenge, Training_Setup_GauntletChallenge, Training_Skipped_GauntletChallenge ) AddStartPoint( "Titanfall", Training_Titanfall, Training_Setup_Titanfall, Training_Skipped_Titanfall ) AddStartPoint( "Pod Outro", Training_PodOutro, Training_Setup_PodOutro, Training_Skipped_PodOutro ) AddStartPoint( "Meet OG", Training_MeetOG, Training_Setup_MeetOG, Training_Skipped_MeetOG ) AddStartPoint( "Gauntlet Mode", Training_GauntletModeStart, Training_Setup_GauntletMode, null ) #if DEV AddStartPoint( "DEV_GHOSTREC_GAUNTLET_FIRSTRUN", TrainingGauntlet_RecordGhostStart_FirstRun, null, null ) AddStartPoint( "DEV_GHOSTREC_GAUNTLET_CHAL_WIP", TrainingGauntlet_RecordGhostStart_Challenge_WIP, null, null ) AddStartPoint( "DEV_GHOSTREC_GAUNTLET_CHAL_01", TrainingGauntlet_RecordGhostStart_Challenge_01, null, null ) AddStartPoint( "DEV_GHOSTREC_GAUNTLET_CHAL_02", TrainingGauntlet_RecordGhostStart_Challenge_02, null, null ) AddStartPoint( "DEV_GHOSTREC_GAUNTLET_CHAL_03", TrainingGauntlet_RecordGhostStart_Challenge_03, null, null ) AddStartPoint( "DEV_GHOSTREC_GAUNTLET_CHAL_04", TrainingGauntlet_RecordGhostStart_Challenge_04, null, null ) AddStartPoint( "DEV_GHOSTREC_GAUNTLET_CHAL_05", TrainingGauntlet_RecordGhostStart_Challenge_05, null, null ) AddStartPoint( "DEV_GHOSTREC_GAUNTLET_CHAL_06", TrainingGauntlet_RecordGhostStart_Challenge_06, null, null ) AddStartPoint( "DEV_GHOSTREC_GAUNTLET_CHAL_07", TrainingGauntlet_RecordGhostStart_Challenge_07, null, null ) AddStartPoint( "DEV_GHOSTREC_GAUNTLET_CHAL_08", TrainingGauntlet_RecordGhostStart_Challenge_08, null, null ) AddStartPoint( "DEV_GHOSTREC_GAUNTLET_CHAL_09", TrainingGauntlet_RecordGhostStart_Challenge_09, null, null ) AddStartPoint( "Pod Intro DEV", DEV_PodIntro, null, null ) AddStartPoint( "Pod Outro DEV", DEV_PodOutro, null, null ) #endif } void function EntitiesDidLoad() { QuickDeathTrigger_SetIsPunitive( false ) SetupTrainingPod() TrainingPod_GlowLightsArraySetup() FiringRangeTargets_Init() file.animref_hangar = GetEntByScriptName( "animref_hangar" ) file.animref_hangar.DisableHibernation() file.skycam_default = GetEnt( "skybox_cam_level" ) file.skycam_glitch = GetEnt( "skybox_cam_glitch" ) } void function Training_PlayerDidLoad( entity player ) { player.SetNoTarget( true ) SetGlobalForcedDialogueOnly( true ) AddButtonPressedPlayerInputCallback( player, IN_JUMP, Training_ButtonPressedJump ) AddButtonPressedPlayerInputCallback( player, IN_ATTACK, Training_ButtonPressedAttack ) file.envArt_colorCorrectionEnt = GetEnt( "color_correction_1" ) file.player = player EnableDemigod( player ) player.ForceMPAimassist() // TODO doublecheck this player.SetSkyCamera( file.skycam_default ) Training_WeaponPickups_Init( player ) player.PreventWeaponDestroyNoAmmo() // makes it so when player swaps empty weapon for another pickup, doesn't destroy empty weapon thread Training_PlayerQuickdeathSFX( player ) DisableFriendlyHighlight() } void function Training_OnLoadSaveGame( entity player ) { thread Training_OnLoadSaveGame_Think( player ) } void function Training_OnLoadSaveGame_Think( entity player ) { EndSignal( player, "OnDestroy" ) wait 0.5 // HACK have to wait otherwise it doesn't work SetWeaponHUDEnabled( player, file.displayWeaponHUD ) } void function Training_ButtonPressedJump( entity player ) { player.Signal( "ButtonPressedJump" ) } void function Training_ButtonPressedAttack( entity player ) { player.Signal( "ButtonPressedAttack" ) } void function Training_FuncBrush_OnDamaged( entity ent, var damageInfo ) { if( !IsValid( ent ) ) return entity attacker = DamageInfo_GetAttacker( damageInfo ) if ( ent.GetScriptName() == "firingrange_target" ) { if ( IsValid( attacker ) && attacker.IsPlayer() ) { table resultTable = {} resultTable["damagePos"] <- DamageInfo_GetDamagePosition( damageInfo ) ent.Signal( "TargetDamaged", resultTable ) } } } // ============================== // ========= POD INTRO ========== // ============================== void function Training_Skipped_PodIntro( entity player ) { player.SetExtraWeaponMods( [ "low_ammo_disable" ] ) SetWeaponHUDEnabled( player, false ) } #if DEV // bare bones start in pod void function DEV_PodIntro( entity player ) { player.EndSignal( "OnDestroy" ) TakeAllWeapons( player ) player.SetExtraWeaponMods( [ "low_ammo_disable" ] ) SetWeaponHUDEnabled( player, false ) Training_EnvArtColorCorrection_SetEnabled( false ) SetDoF_Hangar( player ) thread PodIntro_BackgroundSkits( player ) entity pod = file.trainingPod TrainingPod_PlayerSequence_DoorsOpenIdle( player, false ) // anim starts at a slightly different spot player.SetOrigin( < 10564, -10235, -6056.9 > ) player.SetAngles( < -6, 90, 0 > ) WaitForever() } #endif //DEV void function Training_PodIntro( entity player ) { player.EndSignal( "OnDestroy" ) player.SetExtraWeaponMods( [ "low_ammo_disable" ] ) SetWeaponHUDEnabled( player, false ) Training_EnvArtColorCorrection_SetEnabled( false ) entity pod = file.trainingPod OnThreadEnd( function() : ( player, pod ) { if ( IsValid( player ) ) { player.Anim_Stop() ClearPlayerAnimViewEntity( player ) player.ClearParent() player.UnforceStand() Training_EnvArtColorCorrection_SetEnabled( true ) } if ( IsValid ( pod ) ) { pod.Anim_Stop() thread TrainingPod_ResetLaserEmitterRotation( pod ) thread TrainingPod_KillLasers( pod ) thread TrainingPod_KillGlowFX( pod ) TrainingPod_KillInteriorDLights() } } ) // NORMAL LEVEL START SetDoF_Hangar( player ) ShowIntroScreen( player ) thread PodIntro_MeetOG( player ) TrainingPod_PlayerSequence_DoorsOpenIdle( player ) FlagWait( "IntroScreenFading" ) wait 1.2 Remote_CallFunction_Replay( player, "ScriptCallback_LevelIntroText" ) wait 4.2 // matches fade time in sp_introscreen data thread PodIntro_BackgroundSkits( player ) player.Signal( "PodIntro_OG_StartPodAnim" ) // time for OG to animate before starting viewmodel anim wait 11.8 FirstPersonSequenceStruct playerSequence playerSequence.blendTime = 0.25 playerSequence.attachment = "REF" playerSequence.firstPersonAnim = "ptpov_trainingpod_doors_close" playerSequence.firstPersonAnimIdle = "ptpov_trainingpod_idle" playerSequence.thirdPersonAnim = "pt_trainingpod_doors_close" playerSequence.thirdPersonAnimIdle = "pt_trainingpod_idle" playerSequence.viewConeFunction = TrainingPod_ViewConeLock_SemiStrict playerSequence.renderWithViewModels = true FirstPersonSequenceStruct podSequence podSequence.blendTime = 0.25 podSequence.thirdPersonAnim = "trainingpod_doors_close" podSequence.thirdPersonAnimIdle = "trainingpod_doors_close_idle" podSequence.renderWithViewModels = true entity viewmodel = player.GetFirstPersonProxy() if ( !HasAnimEvent( viewmodel, "PlaySound_SimPod_DoorShut" ) ) AddAnimEvent( viewmodel, "PlaySound_SimPod_DoorShut", PlaySound_SimPod_DoorShut ) // HACK this should be based on an anim event thread TrainingPod_KillInteriorDLights_Delayed( player, 2.65 ) thread FirstPersonSequence( podSequence, pod ) waitthread FirstPersonSequence( playerSequence, player, pod ) FlagSet( "PodIntro_PodDoorsClosed" ) TrainingPod_ViewConeLock_PodClosed( player ) waitthread LookTraining( player ) // "Let's see how much you remember from last time." waitthread PlayDialogue( "og_how_much_you_remember", player ) // "Setting the neural link." waitthread PlayDialogue( "og_neural_link", player ) // "Not quite the same as a Titan link, but it's similar." waitthread PlayDialogue( "og_neural_link_2", player ) thread TrainingPod_Interior_BootSequence( player ) // "To learn new skills, we need to be in the right state of mind." thread PlayDialogue( "og_simulation_starting", player, 2.5 ) player.WaitSignal( "PodInteriorSequenceDone" ) printt( "POD SEQUENCE DONE" ) wait 2.0 // timed to match the screen effect white screen flash SetDoF_Default( player ) } void function PlaySound_SimPod_DoorShut( entity playerFirstPersonProxy ) //Hack, needed for wargames but has unfortunate side effect for Training. { entity player = playerFirstPersonProxy.GetOwner() if ( !IsValid( player ) ) return EmitSoundOnEntityOnlyToPlayer( player, player, "NPE_Scr_SimPod_DoorShut" ) } void function PodIntro_MeetOG( entity player ) { entity animref = file.animref_hangar vector animrefOrigin = animref.GetOrigin() vector animrefAngles = animref.GetAngles() vector btSpawnOrg = <0,0,0> // to avoid red text errors about BT spawning in solid entity animEnt = CreateScriptMover( animrefOrigin, animrefAngles ) // Spawn scene actors entity og = Training_SpawnOGPilot( animref ) Training_OGPilot_SetHelmetOn( og, false ) AddAnimEvent( file.ogPilot, "pod_slap", PodIntro_OG_Slaps_Pod ) TitanLoadoutDef loadout = GetTitanLoadoutForCurrentMap() entity bt = CreateAutoTitanForPlayer_FromTitanLoadout( player, loadout, btSpawnOrg, animrefAngles ) SetSpawnOption_AISettings( bt, "npc_titan_buddy" ) bt.kv.spawnflags = SF_NPC_ALLOW_SPAWN_SOLID DispatchSpawn( bt ) FreeAutoTitan( bt ) // HACK this disables the worldspace BT locator icon TakeAllWeapons( bt ) array actors = [ bt, og ] asset btWeaponModel = GetWeaponInfoFileKeyFieldAsset_Global( "mp_titanweapon_xo16_shorty", "playermodel" ) Assert( btWeaponModel != $"" ) entity btWeapon = CreatePropDynamic( btWeaponModel ) btWeapon.SetParent( bt, "PROPGUN" ) asset ogWeaponModel = GetWeaponInfoFileKeyFieldAsset_Global( OG_WEAPON, "playermodel" ) Assert( ogWeaponModel != $"" ) entity ogWeapon = CreatePropDynamic( ogWeaponModel ) ogWeapon.SetParent( og, "PROPGUN" ) entity ogHelmet = CreatePropDynamic( OG_PILOT_HELMET_MODEL ) ogHelmet.DisableHibernation() array props = [ btWeapon, ogWeapon, ogHelmet ] OnThreadEnd( function() : ( actors, props, animEnt ) { if ( IsValid( file.ogPilot ) ) DeleteAnimEvent( file.ogPilot, "pod_slap" ) foreach ( weapon in props ) { if ( IsValid( weapon ) ) { weapon.ClearParent() weapon.Destroy() } } foreach ( actor in actors ) { if ( !IsValid( actor ) ) continue actor.ClearParent() if ( IsInvincible( actor ) ) ClearInvincible( actor ) if ( !actor.IsPlayer() ) actor.Destroy() } if ( IsValid( animEnt ) ) animEnt.Destroy() } ) foreach ( guy in actors ) { MakeInvincible( guy ) guy.SetEfficientMode( true ) Highlight_ClearFriendlyHighlight( guy ) } string anim_og_idle = "pt_OG_training_rail_sit_idle" string anim_og_helmet_idle = "helmet_intro_scene_OG_idle" string anim_bt_idle = "BT_intro_scene_OG_idle" string anim_og = "pt_pod_setup_OG" string anim_bt = "BT_pod_setup_OG" thread PlayAnimTeleport( og, anim_og_idle, animEnt ) thread PlayAnimTeleport( ogHelmet, anim_og_helmet_idle, animEnt ) thread PlayAnimTeleport( bt, anim_bt_idle, animEnt ) player.WaitSignal( "PodIntro_OG_StartPodAnim" ) if ( !IsValid( og ) ) return og.Anim_Stop() thread PlayAnim( og, anim_og, animEnt ) bt.Anim_Stop() thread PlayAnim( bt, anim_bt, animEnt ) float animDuration = og.GetSequenceDuration( anim_og ) wait animDuration - 0.1 if ( !IsValid( og ) ) return og.Anim_Stop() thread PlayAnimTeleport( og, anim_og_idle, animEnt ) bt.Anim_Stop() thread PlayAnimTeleport( bt, anim_bt_idle, animEnt ) // wait for look training to get started before killing the scene FlagWaitAny( "PlayerLookedAtTopTarget", "PlayerLookedAtBottomTarget" ) } void function PodIntro_OG_Slaps_Pod( entity ogPilot ) { array players = GetPlayerArray() if ( !players.len() ) return entity player = players[0] if ( !IsValid( player ) ) return float shakeDuration = 0.45 float shakeAmplitude = 0.14 float screenBlurFrac = 0 SimpleScreenShake( player, shakeDuration, shakeAmplitude, screenBlurFrac ) } void function LookTraining( entity player ) { thread LookTraining_StartNag( player, "PlayerLookedAtTopTarget", "PlayerLookedAtBottomTarget" ) Remote_CallFunction_Replay( player, "ScriptCallback_ShowInvertCrosshair", true ) // LOOKAT SECTION Remote_CallFunction_Replay( player, "ScriptCallback_SetupLookTargets" ) wait 0.5 Remote_CallFunction_Replay( player, "ScriptCallback_LookTargets_WaitForLookat" ) DialogueGroup invertConfirmVO = GetDialogueGroup( "ogInvertConfirm" ) int numInverts = 0 int maxInverts = 2 //while ( numInverts < maxInverts ) while ( 1 ) { Remote_CallFunction_Replay( player, "ScriptCallback_SetupLookTargets" ) wait 0.5 Remote_CallFunction_Replay( player, "ScriptCallback_LookTargets_WaitForLookat" ) string hintAlias = "invert_look_at_lights" if ( numInverts > 0 ) hintAlias = "invert_look_at_lights_again" // only play this VO once if ( numInverts == 1 ) { // "You sure?" thread PlayDialogue( "og_invert_confirm_3", player ) } DisplayOnscreenHint( player, hintAlias ) printt( "Waiting for player to look at either target" ) FlagWaitAny( "PlayerLookedAtTopTarget", "PlayerLookedAtBottomTarget" ) printt( "Player looked at one of the targets" ) hintAlias = "invert_look_at_lights_1_left" if ( numInverts > 0 ) hintAlias = "invert_look_at_lights_again_1_left" DisplayOnscreenHint( player, hintAlias ) FlagWait( "PlayerLookedAtTopTarget" ) FlagWait( "PlayerLookedAtBottomTarget" ) printt( "Player looked at both targets" ) hintAlias = "invert_look_at_lights_0_left" if ( numInverts > 0 ) hintAlias = "invert_look_at_lights_again_0_left" DisplayOnscreenHint( player, hintAlias ) printt( "invert waiting for OG dialogue" ) // "Does that feel right to you?" // "How about now? Feel alright?" string askAlias = DialogueGroup_GetNextLine( invertConfirmVO ) waitthread PlayDialogue( askAlias, player ) printt( "invert OG dialogue done" ) ClearOnscreenHint( player ) string invertConvar = file.playerInputType == 0 ? INVERT_CONVAR_GAMEPAD : INVERT_CONVAR_MOUSE printt( "invertConvar:", invertConvar ) bool invertSettingBeforeMenu = GetConVarBool( invertConvar ) printt( "Opening invert look dialog" ) // THIS PAUSES THE GAME UNTIL MENU IS CLOSED Remote_CallFunction_UI( player, "ScriptCallback_OpenInvertLookDialog" ) wait 0.5 // let the game come back after menu is closed // if player didn't change setting, don't repeat invertConvar = file.playerInputType == 0 ? INVERT_CONVAR_GAMEPAD : INVERT_CONVAR_MOUSE printt( "invertConvar:", invertConvar ) if ( GetConVarBool( invertConvar ) == invertSettingBeforeMenu ) { printt( "player didn't change setting, not repeating" ) break } printt( "invert- player changed setting, repeating to confirm" ) numInverts++ // kill lights and reset flags Remote_CallFunction_Replay( player, "ScriptCallback_LookTargets_KillLights" ) FlagClear( "PlayerLookedAtTopTarget" ) FlagClear( "PlayerLookedAtBottomTarget" ) } ClearOnscreenHint( player ) Remote_CallFunction_Replay( player, "ScriptCallback_ShowInvertCrosshair", false ) // "Alright, we're good to go." waitthread PlayDialogue( "og_invert_complete", player ) TrainingPod_ViewConeLock_PodClosed( player ) TrainingPod_ViewConeLock_SemiStrict( player ) // recenter player view Remote_CallFunction_Replay( player, "ScriptCallback_LookTargets_KillLights" ) } void function LookTraining_StartNag( entity player, string flag1, string flag2 ) { EndSignal( player, "OnDestroy" ) if ( Flag( flag1 ) || Flag( flag2 ) ) return FlagEnd( flag1 ) FlagEnd( flag2 ) float nagInterval = 15.0 float nextNagTime = Time() + nagInterval while( 1 ) { wait 1 if ( Time() < nextNagTime ) continue // "We have to calibrate the pod. It won't boot up until you look at both of those lights." waitthread PlayDialogue( "og_pod_calibrate", player ) nextNagTime = Time() + nagInterval } } void function TrainingPod_PlayerSequence_DoorsOpenIdle( entity player, bool doPlayerAnim = true ) { entity pod = file.trainingPod // Have to do this first so the anim starts centered on the ref attachment angles string podAttach = "REF" int attachID = pod.LookupAttachment( podAttach ) vector podRefOrg = pod.GetAttachmentOrigin( attachID ) vector podRefAng = pod.GetAttachmentAngles( attachID ) player.SetOrigin( podRefOrg ) player.SetAngles( podRefAng ) player.ForceStand() player.DisableWeapon() // default start anim starts open void functionref( entity ) viewConeFunction_start = TrainingPod_ViewConeLock_PodOpen string podAnim_start = "trainingpod_doors_open_idle" // start open idle FirstPersonSequenceStruct playerSequence playerSequence.blendTime = 0.0 playerSequence.attachment = podAttach playerSequence.firstPersonAnimIdle = "ptpov_trainingpod_idle" playerSequence.thirdPersonAnimIdle = "pt_trainingpod_idle" playerSequence.viewConeFunction = viewConeFunction_start playerSequence.renderWithViewModels = true FirstPersonSequenceStruct podSequence podSequence.blendTime = 0.0 podSequence.thirdPersonAnimIdle = podAnim_start podSequence.renderWithViewModels = true thread FirstPersonSequence( podSequence, pod ) if ( doPlayerAnim ) thread FirstPersonSequence( playerSequence, player, pod ) TrainingPod_TurnOnInteriorDLight( "console1" ) TrainingPod_TurnOnInteriorDLight( "console2" ) //TrainingPod_TurnOnInteriorDLight( "backlight_side_L" ) //TrainingPod_TurnOnInteriorDLight( "backlight_side_R" ) } // ------ POD INTRO BACKGROUND SKITS ------ void function PodIntro_BackgroundSkits( entity player ) { string endFlag = "PodIntro_PodDoorsClosed" FlagEnd( endFlag ) thread PodIntro_LoudspeakerVO( player, "PodIntro_InteriorBootSequence_Starting" ) thread PodIntro_TitanRacks( endFlag ) thread PodIntro_Background_WalkingGuys() // ---- ANIMATING SKIT GUYS --- // script NudgeSkitGuy( "back_console_supervisor", 0, 5, 0 ) // guy sitting at console in back center of room SkitGuyInfo backConsoleGuy1 = SpawnSkitGuy( "back_console_sitting", "pt_console_idle", < 10521.1, -9679.33, -6044.1 >, < 0, 88.3541, 0 >, TEAM_MILITIA, "npc_soldier" ) SkitGuy_PlayAnim( backConsoleGuy1 ) SkitGuyInfo backConsoleGuy2 = SpawnSkitGuy( "back_console_supervisor", "pt_bored_interface_leanback", < 10527.6, -9656.36, -6043.97 >, < 0, -26.564, 0 >, TEAM_MILITIA, "npc_soldier_specialist_militia", "mp_weapon_g2" ) SkitGuy_PlayAnim( backConsoleGuy2 ) // extra marvins working on titans 1 and 2 SkitGuyInfo titanBay1_marvin = SpawnSkitGuy( "bay1_marvin", "mv_idle_weld", < 10711.2, -9781.04, -6080.65 >, < 0, 153.715, 0 > ) SkitGuy_PlayAnim( titanBay1_marvin, 3.0 ) // don't play same anim at same time SkitGuyInfo titanBay2_marvin = SpawnSkitGuy( "bay2_marvin", "mv_idle_weld", < 10316.2, -9730.23, -6079.97 >, < 0, -69.965, 0 > ) SkitGuy_PlayAnim( titanBay2_marvin ) // guys looking at console in back left SkitGuyInfo leftConsoleGuy1 = SpawnSkitGuy( "console_lean", "pt_bored_interface_leanin", < 10257.6, -9846.63, -6079.97 >, < 0, 34.5175, 0 >, TEAM_MILITIA, "npc_soldier" ) SkitGuy_PlayAnim( leftConsoleGuy1 ) SkitGuyInfo leftConsoleGuy2 = SpawnSkitGuy( "console_supervisor", "pt_bored_interface_leanback", < 10260.5, -9874.78, -6079.97 >, < 0, -42.534, 0 >, TEAM_MILITIA, "npc_soldier", "mp_weapon_mastiff" ) SkitGuy_PlayAnim( leftConsoleGuy2 ) OnThreadEnd( function() : () { DeleteAllSkitGuys() } ) WaitForever() } void function PodIntro_LoudspeakerVO( entity player, string endFlag ) { EndSignal( player, "OnDestroy" ) array aliases // "Inbound to Planet Typhon. Subspace rendezvous in approximately 20 minutes." aliases.append( "intro_0" ) // "Reminder to dock personnel: Titan ordnance is a Type 3 Hazardous Material." aliases.append( "intro_2" ) // "Running Lifeboat diagnostic test two point one. All Mark Eight lifeboats are in the green." aliases.append( "intro_4" ) // "3rd Militia Grenadiers - prep dropship MacAllan 17." aliases.append( "intro_5" ) // "Captain Cole to communications." aliases.append( "intro_3" ) // "Major Anderson, please report to the briefing room." aliases.append( "intro_1" ) thread LoopLoudspeakerVO( aliases, endFlag, 1.0, 2.0 ) } void function PodIntro_Background_WalkingGuys() { // group of guys walking left to right array path1 = [] ScriptedPath_AddPoint( path1, < 10327.9, -10000.28, -6079.97 >, < 0, 0.918, 0 > ) ScriptedPath_AddPoint( path1, < 10373.5, -9987.14, -6079.97 >, < 0, 0.093, 0 > ) ScriptedPath_AddPoint( path1, < 10688.3, -9973.76, -6079.97 >, < 0, 0, 0 > ) ScriptedPath_AddPoint( path1, < 10856.4, -9973.58, -6079.97 >, < 0, 0, 0 > ) SkitGuyInfo walkerInfo_1 = SpawnSkitGuy( "walker_1", "", path1[0].origin, path1[0].angles, TEAM_MILITIA, "npc_soldier_specialist_militia", "mp_weapon_hemlok_smg" ) thread ScriptedPath_Walk( walkerInfo_1, path1, 0.75 ) array path2 = [] ScriptedPath_AddPoint( path2, < 10248.6, -9925.06, -6079.97 >, < 0, 6.36392, 0 > ) ScriptedPath_AddPoint( path2, < 10373.5, -9939.14, -6079.97 >, < 0, 0.093, 0 > ) ScriptedPath_AddPoint( path2, < 10688.3, -9973.76, -6079.97 >, < 0, 0, 0 > ) ScriptedPath_AddPoint( path2, < 10856.4, -9973.58, -6079.97 >, < 0, 0, 0 > ) SkitGuyInfo walkerInfo_2 = SpawnSkitGuy( "walker_2", "", path2[0].origin, path2[0].angles, TEAM_MILITIA, "npc_soldier", "mp_weapon_hemlok" ) thread ScriptedPath_Walk( walkerInfo_2, path2, 0.85 ) } void function PodIntro_TitanRacks( string endFlag ) { FlagEnd( endFlag ) array titanGroups HangarTitanGroup bay1 bay1.ref = GetEntByScriptName( "animref_drop_hangar_titan_1" ) bay1.titan = GetEntByScriptName( "hangar_titan_1" ) bay1.rack = GetEntByScriptName( "hangar_titan_rack_1" ) bay1.titanAnim = "bt_TDay_drop_titan4" bay1.rackAnim = "rack_TDay_drop_rack4" bay1.marvinAnim = "mv_TDay_drop_marvin4" HangarTitanGroup_Init( bay1 ) titanGroups.append( bay1 ) // --- BAY 2 --- HangarTitanGroup bay2 bay2.ref = GetEntByScriptName( "animref_drop_hangar_titan_2" ) bay2.titan = GetEntByScriptName( "hangar_titan_2" ) bay2.rack = GetEntByScriptName( "hangar_titan_rack_2" ) bay2.titanAnim = "bt_TDay_drop_titan3" bay2.rackAnim = "rack_TDay_drop_rack3" bay2.marvinAnim = "mv_TDay_drop_marvin3" HangarTitanGroup_Init( bay2 ) titanGroups.append( bay2 ) float wait_earlyEnd = 27.0 thread HangarTitanGroup_Animate( bay1, endFlag, wait_earlyEnd ) thread HangarTitanGroup_Animate( bay2, endFlag, wait_earlyEnd ) wait wait_earlyEnd thread PodIntro_TitanRacks( endFlag ) } // =================================== // ========= BASIC MOVEMENT ========== // =================================== void function Training_Setup_BasicMovement( entity player ) { player.DisableWeapon() } void function Training_Skipped_BasicMovement( entity player ) { player.SetPlayerSettingsWithMods( TRAINING_PLAYER_SETTINGS, [ "disable_doublejump", "disable_wallrun" ] ) Objective_SetSilent( "#TRAINING_OBJ_DEFAULT" ) } void function Training_BasicMovement( entity player ) { entity standNearJumpRef = GetEntByScriptName( "basic_movement_og_stand_near_jump" ) standNearJumpRef.SetOrigin( OriginToGround( standNearJumpRef.GetOrigin() + <0,0,0.5> ) ) // HACK HACK the ref node is a tiny bit in the geo which causes OG to dip when blending between anims entity ogStart = GetEntByScriptName( "basic_movement_og_start" ) entity og = Training_SpawnOGPilot( ogStart ) player.SetPlayerSettingsWithMods( TRAINING_PLAYER_SETTINGS, [ "disable_doublejump", "disable_wallrun" ] ) player.ForceAutoSprintOff() entity playerStart = GetEntByScriptName( "startpoint_basic_movement" ) waitthread PlayerAndOGTeleport_Fancy( player, playerStart.GetOrigin(), "basic_movement_og_start", playerStart.GetAngles() ) Training_OG_Idles_Sitting( ogStart, "OG_base_move_A_idle" ) thread BasicMovement_DelayedWeaponDeploy( player, 1.5 ) Objective_SetSilent( "#TRAINING_OBJ_DEFAULT" ) // "Ah. Much better." waitthread Training_OG_ScriptedAnim( ogStart, "OG_base_move_A" ) thread Training_OG_Idles_Sitting( ogStart, "OG_base_move_B_idle" ) CheckPoint_Silent() thread OnscreenHint_DisplayUntilFlag( player, "move_hint", "BasicMovement_PlayerMovedForward", 10.0 ) FlagWait( "BasicMovement_PlayerMovedForward" ) if ( !Flag( "BasicMovement_PlayerJumped" ) ) { // "Technically, I'm not supposed to be training you. But in you, I see potential. waitthread Training_OG_ScriptedAnim( ogStart, "OG_base_move_B1") } if ( !Flag( "BasicMovement_PlayerJumped" ) ) { // "Besides, we're at war. Who's got time for classes, eh?" waitthread Training_OG_ScriptedAnim( ogStart, "OG_base_move_B2") thread Training_OG_Idles_Sitting( ogStart ) } if ( !Flag( "BasicMovement_PlayerJumped" ) && player.IsOnGround() ) { DisplayOnscreenHint( player, "jump_hint" ) // "Here you go, up and over." waitthread Training_OG_ScriptedAnim( ogStart, "OG_base_move_C" ) waitthread Training_OG_Moves( standNearJumpRef ) } if ( !Flag( "BasicMovement_PlayerJumped" ) ) { // "Cmon, schedule's tight today." // "Here you go, up and over." array jumpNags = [ "og_jump_nag_2", "og_jump_nag" ] thread Training_OG_NagPlayerUntilFlag( player, jumpNags, 20.0, standNearJumpRef, "BasicMovement_PlayerJumped" ) } FlagWait( "BasicMovement_PlayerJumped" ) ClearOnscreenHint( player ) waitthread BasicMovement_Sprint( player ) } void function BasicMovement_Sprint( entity player ) { thread BasicMovement_Sprint_OG_Moves( player ) //player.ForceAutoSprintOn() FlagClear( "PlayerSprinted" ) thread BasicMovement_PlayerSprintDetection( player ) // "Let's pick up the pace. Enabling jumpkit assist." float endVOTime = Time() + 3.5 thread PlayDialogue( "og_autosprint_on", file.ogPilot ) wait 2.5 // let the VO play a bit before showing the hint player.UnforceAutoSprint() thread BasicMovement_SprintHint_Think( player ) wait endVOTime - Time() // HACK- Create a fake VO speaker where OG will eventually move to/from so the lines emit in worldspace for audio entity ref = GetEntByScriptName( "basic_movement_og_mid_hallway" ) entity tempSpeaker = CreateScriptMover( ref.GetOrigin(), <0,0,0> ) thread HACK_MoveTempSpeaker_WithOGPathMover( player, tempSpeaker ) // "Jumpkits operate on the principle of relaxed stability." EmitSoundOnEntity( tempSpeaker, "diag_sp_addtional_TR411_53_mcor_og" ) // do this instead of PlayDialogue because it will follow the ent around wait 3.4 // HACK // "Once your jumpkit calibrates to your movement style, enhanced mobility becomes second nature." EmitSoundOnEntity( tempSpeaker, "diag_sp_movement_TR121_08_01_mcor_og" ) wait 6.0 // HACK tempSpeaker.Destroy() } void function HACK_MoveTempSpeaker_WithOGPathMover( entity player, entity tempSpeaker ) { EndSignal( player, "OnDestroy" ) EndSignal( tempSpeaker, "OnDestroy" ) vector prevOrg = < -1,-1,-1 > while ( 1 ) { WaitFrame() vector newOrg = < -1,-1,-1 > // if we are doing a path move, use the pathMover origin- otherwise use OG's origin if ( IsValid( file.ogPathMover ) ) newOrg = file.ogPathMover.GetOrigin() else if ( IsValid( file.ogPilot ) ) file.ogPilot.GetOrigin() if ( newOrg != < -1,-1,-1 > && newOrg != prevOrg ) { tempSpeaker.SetOrigin( newOrg ) prevOrg = newOrg } } } void function BasicMovement_SprintHint_Think( entity player ) { EndSignal( player, "OnDestroy" ) float autosprintHintTime = 10.0 // player with autosprint on will already be sprinting if ( GetAutosprintEnabled() ) { thread DisplayOnscreenHint( player, "autosprint_hint", autosprintHintTime ) return } // Below this point assume that autosprint is NOT enabled if ( !Flag( "PlayerSprinted" ) ) { thread OnscreenHint_DisplayUntilFlag( player, "sprint_button_hint", "PlayerSprinted", 0.0, true ) FlagWait( "PlayerSprinted" ) wait 1.0 } DisplayOnscreenHint( player, "autosprint_available", autosprintHintTime ) } void function BasicMovement_Sprint_OG_Moves( entity player ) { EndSignal( player, "OnDestroy" ) entity ogMidHallwayRef = GetEntByScriptName( "basic_movement_og_mid_hallway" ) waitthread Training_OG_Moves_ToSitting( ogMidHallwayRef, "", 0.8 ) FlagWait( "BasicMovement_PlayerReachedMidHallway" ) entity og_zenGarden_start = GetEntByScriptName( "basic_movement_og_zen_start" ) thread Training_OG_Moves( og_zenGarden_start, "OG_Beautiful_idle" ) } void function BasicMovement_PlayerSprintDetection( entity player ) { EndSignal( player, "OnDestroy" ) FlagEnd( "BasicMovement_PlayerReachedOpenArea" ) while ( 1 ) { WaitFrame() if ( player.IsSprinting() ) break } FlagSet( "PlayerSprinted" ) } void function BasicMovement_DelayedWeaponDeploy( entity player, float delay ) { player.EndSignal( "OnDestroy" ) wait delay player.EnableWeaponWithSlowDeploy() thread TakeAmmoFromPlayerASAP( player ) } // ================================================= // ========= BASIC MOVEMENT 2: ZEN GARDEN ========== // ================================================= void function Training_Setup_ZenGarden( entity player ) { entity ogStart = GetEntByScriptName( "basic_movement_og_zen_start" ) entity og = Training_SpawnOGPilot( ogStart ) Training_OG_Idles( ogStart, "OG_Beautiful_idle" ) thread TakeAmmoFromPlayerASAP( player ) TeleportPlayerAndBT( "startpoint_zen_garden" ) } void function Training_Skipped_ZenGarden( entity player ) { player.SetPlayerSettingsWithMods( TRAINING_PLAYER_SETTINGS, [] ) OpenZenGardenExitDoor() } void function Training_ZenGarden( entity player ) { entity og = GetOGPilot() Assert( IsValid( og ) ) entity ref_hillclimbFinish = GetEntByScriptName( "zengarden_hillclimb_finish_idle" ) entity ref_exitSpot = GetEntByScriptName( "zengarden_og_exit_ref" ) OpenZenGardenExitDoor() FlagWait( "BasicMovement_PlayerReachedOpenArea" ) player.SetPlayerSettingsWithMods( TRAINING_PLAYER_SETTINGS, ["disable_doublejump"] ) CheckPoint_Silent() thread ZenGarden_WhyWeFight_VO( player, og ) waitthread Training_ZenGarden_Wallrun( player ) waitthread Training_ZenGarden_Crouch( player ) player.SetPlayerSettingsWithMods( TRAINING_PLAYER_SETTINGS, [] ) CheckPoint_Silent() waitthread Training_ZenGarden_DoubleJump( player ) waitthread Training_ZenGarden_HillClimb( player, ref_hillclimbFinish, ref_exitSpot ) if ( IsValid( level ) ) Signal( level, "StopRepeatingGhostRecorder" ) } // ZEN GARDEN SHARED BETWEEN PATHS void function ZenGarden_WhyWeFight_VO( entity player, entity og ) { entity ogStart = GetEntByScriptName( "basic_movement_og_zen_start" ) entity firstRockRef = GetEntByScriptName( "zengarden_og_rock1_ref" ) entity pathRef = GetEntByScriptName( "zengarden_og_path_ref" ) entity treeRef = GetEntByScriptName( "zengarden_og_tree_ref" ) string stopFlag = "ZenGarden_PlayerReachedWallrunStart" string setFlag = "OG_WhyWeFight_VO_Done" if ( !Flag( stopFlag ) ) { // "Beautiful, isn't it?" waitthread Training_OG_ScriptedAnim( ogStart, "OG_Beautiful" ) } if ( !Flag( stopFlag ) ) { waitthread Training_OG_Moves( firstRockRef, "", 0.25 ) } if ( !Flag( stopFlag ) ) { // "It's inspired by my home planet of Harmony." waitthread Training_OG_ScriptedAnim( firstRockRef, "OG_harmony_A" ) } if ( !Flag( stopFlag ) ) { // "This is where I grew up." waitthread Training_OG_ScriptedAnim( firstRockRef, "OG_harmony_B" ) } if ( !Flag( stopFlag ) && !Flag( "ZenGarden_PlayerReachedFirstRock" ) ) { waitthread Training_OG_Moves( pathRef ) } FlagWaitAny( stopFlag, "ZenGarden_PlayerReachedFirstRock" ) if ( !Flag( stopFlag ) ) { waitthread Training_OG_Moves_ToSitting( treeRef, "", 0.5 ) } if ( !Flag( stopFlag ) ) { // "This is what we're fighting for, Cooper." waitthread Training_OG_ScriptedAnim( treeRef, "OG_freedom_A" ) } if ( !Flag( stopFlag ) ) { // "A world that's not metal and smoke." waitthread Training_OG_ScriptedAnim( treeRef, "OG_freedom_B" ) } if ( !Flag( stopFlag ) ) { // "The freedom to live in peace and prosperity." waitthread Training_OG_ScriptedAnim( treeRef, "OG_freedom_C" ) } FlagSet( setFlag ) } void function Training_ZenGarden_Wallrun( entity player ) { entity wallrunRef = GetEntByScriptName( "basic_movement_og_zen_wallrun_idle" ) entity recorderRef = GetEntByScriptName( "basic_movement_wallrun_start_ref" ) FlagWaitAny( "ZenGarden_PlayerReachedWallrunStart", "OG_WhyWeFight_VO_Done" ) FlagWait( "OG_WhyWeFight_VO_Done" ) waitthread Training_OG_Moves_ToSitting( wallrunRef, "OG_primed_idle", 0.5 ) FlagWait( "ZenGarden_PlayerReachedWallrunStart" ) thread GhostRecorder_RepeatUntilFlag( player, "ZenGarden_PlayerFinishedWallrun", recorderRef, $"anim_recording/training_record_zengarden_wallrun.rpak" ) if ( !Flag( "ZenGarden_PlayerFinishedWallrun" ) ) thread OnscreenHint_DisplayUntilFlag( player, "wallrun_hint", "ZenGarden_PlayerFinishedWallrun" ) wait 1.0 // wait to see if player starts wallrunning right away if ( !Flag( "ZenGarden_PlayerFinishedWallrun" ) && !Flag( "ZenGarden_PlayerTouchingWallrunPanel" ) ) { // "Let's make sure your jump kit is primed. Basic wallrun here, give it a try." waitthread Training_OG_ScriptedAnim( wallrunRef, "OG_primed" ) thread Training_OG_Idles_Sitting( wallrunRef, "OG_primed_idle" ) } if ( !Flag( "ZenGarden_PlayerFinishedWallrun" ) && !Flag( "ZenGarden_PlayerTouchingWallrunPanel" ) ) wait 1.0 if ( !Flag( "ZenGarden_PlayerFinishedWallrun" ) && !Flag( "ZenGarden_PlayerTouchingWallrunPanel" ) ) { // "Same routine as last time- watch the ghost pilot, and try to follow along." thread PlayDialogue( "og_wallrun_follow_ghost", file.ogPilot ) waitthread Training_OG_ScriptedAnim( wallrunRef, "OG_primed_generic" ) thread Training_OG_Idles_Sitting( wallrunRef, "OG_primed_idle" ) } FlagWait( "ZenGarden_PlayerFinishedWallrun" ) if ( !Flag( "ZenGarden_PlayerReachedCrouchArea" ) ) { // "Good! Now you're moving." file.postWallrunVOEndTime = Time() + 3.0 thread PlayDialogue( "og_wallrun_done", player ) wait 0.3 // min wait after } } void function Training_ZenGarden_Crouch( entity player ) { entity ogIdle_crouchSpot = GetEntByScriptName( "zengarden_og_slide_idle") entity ref_slideStart = GetEntByScriptName( "zengarden_slide_ref" ) if ( Flag( "ZenGarden_PlayerCrouched" ) ) return waitthread Training_OG_Moves_ToSitting( ogIdle_crouchSpot, "OG_low_idle", 0.8 ) thread GhostRecorder_RepeatUntilFlag( player, "ZenGarden_PlayerCrouched", ref_slideStart, $"anim_recording/training_record_zengarden_slide.rpak", 1.0 ) FlagWait( "ZenGarden_PlayerReachedCrouchArea" ) DisplayOnscreenHint( player, "crouch_hint", 5.0 ) while ( file.postWallrunVOEndTime > 0 && file.postWallrunVOEndTime - Time() > 0 ) wait 0.1 if ( Flag( "ZenGarden_PlayerCrouched" ) ) return // "Under here. Stay low." waitthread Training_OG_ScriptedAnim( ogIdle_crouchSpot, "OG_low" ) thread Training_OG_Idles_Sitting( ogIdle_crouchSpot, "OG_low_idle", true ) thread Training_ZenGarden_CrouchHint_WithNags( player, 10.0, ogIdle_crouchSpot, "OG_low_idle" ) FlagWait( "ZenGarden_PlayerCrouched" ) ClearOnscreenHint( player ) } void function Training_ZenGarden_CrouchHint_WithNags( entity player, float nagInterval, entity idleRef, string ogIdleAnim ) { player.EndSignal( "OnDestroy" ) string endFlag = "ZenGarden_PlayerCrouched" string activeFlag = "ZenGarden_PlayerAtCrouchStart" // "Crouch underneath, and we'll keep moving." // "You need to get low here." array nags = [ "og_crouch_nag_1", "og_crouch_nag_2" ] int nagIdx = 0 float nextNagTime = Time() + nagInterval bool showingHint = false while ( !Flag( endFlag ) ) { FlagWait( activeFlag ) while ( Flag( activeFlag ) ) { wait 0.1 if ( player.IsCrouched() ) { if ( showingHint ) { showingHint = false ClearOnscreenHint( player ) } continue } if ( !showingHint ) { DisplayOnscreenHint( player, "crouch_hint" ) showingHint = true } if ( Time() - nextNagTime >= nagInterval ) { thread Training_OG_Talks_Sitting( nags[nagIdx], idleRef, "", ogIdleAnim ) nextNagTime = Time() + nagInterval nagIdx++ if ( nagIdx >= nags.len() ) nagIdx = 0 } } if ( showingHint ) { showingHint = false ClearOnscreenHint( player ) } } } void function Training_ZenGarden_DoubleJump( entity player ) { entity pathRef = GetEntByScriptName( "zengarden_og_postslide_idle" ) entity ogIdleRef = GetEntByScriptName( "zengarden_og_doublejump_idle" ) entity recordedAnimRef = GetEntByScriptName( "zengarden_doublejump_ref" ) waitthread Training_OG_Moves( pathRef, "", 0.5 ) // "Simple double jump. Follow the ghost." waitthread Training_OG_Talks( "og_doublejump_hint", pathRef ) thread GhostRecorder_RepeatUntilFlag( player, "ZenGarden_PlayerDoubleJumped", recordedAnimRef, $"anim_recording/training_record_zengarden_doublejump.rpak", 1.0 ) waitthread Training_OG_Moves_ToSitting( ogIdleRef, "OG_doublejump_idle", 0.5 ) // "We've retaken over a quarter of Frontier space since the Battle of Demeter. The Militia's better organized now. More people join everyday to fight the IMC. People like you." waitthread Training_OG_ScriptedAnim( ogIdleRef, "OG_doublejump_A" ) Training_OG_Idles( ogIdleRef, "OG_doublejump_B_idle" ) FlagWait( "ZenGarden_PlayerReachedDoubleJumpArea" ) if ( !Flag( "ZenGarden_PlayerDoubleJumped" ) ) { DisplayOnscreenHint( player, "doublejump_hint", 5.0 ) thread OnscreenHint_NagUntilFlag( player, "doublejump_hint", "ZenGarden_PlayerDoubleJumped", 10.0, 5.0 ) wait 0.8 } FlagWait( "ZenGarden_PlayerDoubleJumped" ) } void function Training_ZenGarden_HillClimb( entity player, entity ref_hillclimbFinish, entity ref_exitSpot ) { // "We used to just run and hide from them. But now we chase them." string nextAnim = "OG_doublejump_B" if ( !Flag( "ZenGarden_PlayerClimbedHill" ) ) { waitthread Training_OG_Moves( ref_hillclimbFinish, "", 0.25 ) //waitthread Training_OG_Talks( nextLine, ref_hillclimbFinish ) waitthread Training_OG_ScriptedAnim( ref_hillclimbFinish, nextAnim ) FlagWait( "ZenGarden_PlayerClimbedHill" ) waitthread Training_OG_Moves( ref_exitSpot, ANIM_OG_LEANING_IDLE, 0.5 ) } else if ( !Flag( "ZenGarden_PlayerReachedExitArea" ) ) { waitthread Training_OG_Moves( ref_exitSpot, ANIM_OG_LEANING_IDLE, 0.5 ) if ( !Flag( "ZenGarden_PlayerReachedExitArea" ) ) waitthread Training_OG_ScriptedAnim( ref_exitSpot, nextAnim ) if ( !Flag( "ZenGarden_PlayerReachedExitArea" ) ) waitthread Training_OG_Moves( ref_exitSpot, ANIM_OG_LEANING_IDLE, 0.5 ) } FlagWait( "ZenGarden_PlayerReachedExitArea" ) } // ================================= // ========= FIRING RANGE ========== // ================================= void function Training_Setup_FiringRange( entity player ) { entity ogStart = GetEntByScriptName( "firingrange_og_needguns_start_sitting" ) entity og = Training_SpawnOGPilot( ogStart ) Training_OG_Idles_Sitting( ogStart ) thread TakeAmmoFromPlayerASAP( player ) TeleportPlayerAndBT( "startpoint_firing_range" ) } void function Training_Skipped_FiringRange( entity player ) { CloseZenGardenExitDoor() OpenGauntletDoor() FlagSet( "ineedguns" ) Training_WeaponRacks_SetSolidity( true ) player.SetExtraWeaponMods( [ "" ] ) // turns off low_ammo_disable SetWeaponHUDEnabled( player, true ) Training_SetWeaponPickupsFullAmmo() } void function Training_FiringRange( entity player ) { entity ref_og_needGunsStart = GetEntByScriptName( "firingrange_og_needguns_start_sitting" ) entity ref_og_firingRangeSpot = GetEntByScriptName( "firingrange_og_spot" ) entity ref_og_firingRangeAttractSpot = GetEntByScriptName( "firingrange_og_attract_spot" ) float ogMoveTime = -1 Training_WeaponRacks_SetSolidity( false ) Training_SetWeaponPickupsEmptyAmmo() thread FiringRange_CloseZenGardenDoor_WhenPlayerReachesRange( player ) waitthread Training_OG_Moves_ToSitting( ref_og_needGunsStart, "OG_Weapons_idle", ogMoveTime ) FlagWait( "PlayerApproachingFiringRange" ) thread FiringRange_DetectWeaponSwitch( player ) waitthread FiringRange_ApproachAndEntry( player, ref_og_needGunsStart, ref_og_firingRangeAttractSpot, ref_og_firingRangeSpot ) waitthread FiringRange_TrainReload( player, ref_og_firingRangeSpot ) Training_WeaponRacks_SetSolidity( true ) thread FiringRange_InfiniteAmmo_WhenNearRange( player, "PodOutroStarted" ) thread FiringRange_ResetTargets_Think( player ) thread Training_OG_Idles( ref_og_firingRangeSpot, "OG_firingrange_idle" ) waitthread FiringRange_TrainADS( player, ref_og_firingRangeSpot ) waitthread FiringRange_PlayerMustDamageAllTargets( player, ref_og_firingRangeSpot, true, false ) Training_SetWeaponPickupsFullAmmo() if ( !Flag( "FiringRangeWeaponSwapped" ) ) { waitthread FiringRange_TrainWeaponSwap( player, ref_og_firingRangeSpot ) FlagWait( "PlayerNearFiringRange" ) // if player moved away to get a weapon, wait for them to come back wait 0.25 // extra wait for player to see the targets reset waitthread FiringRange_PlayerMustDamageAllTargets( player, ref_og_firingRangeSpot, false ) } OpenGauntletDoor() CheckPoint_Silent() // "Good. Practice more if you want, then head to the Gauntlet." waitthread Training_OG_ScriptedAnim( ref_og_firingRangeSpot, "OG_firingrange_ending" ) array moveToGauntletNags = [ "og_moving_to_gauntlet_nag_1", "og_moving_to_gauntlet_nag_2", "og_moving_to_gauntlet_nag_3" ] if ( Flag( "PlayerNearFiringRange" ) ) { entity ref_og_midway2Gauntlet = GetEntByScriptName( "og_between_firingrange_and_gauntlet" ) thread Training_OG_NagPlayerUntilFlag_Sitting( player, moveToGauntletNags, 45.0, ref_og_midway2Gauntlet, "OG_MovedTo_GauntletEntrance" ) waitthread Training_OG_Moves_ToSitting( ref_og_midway2Gauntlet ) FlagWaitClear( "PlayerNearFiringRange" ) } FlagSet( "OG_MovedTo_GauntletEntrance" ) entity ref_og_gauntletEntrance = GetEntByScriptName( "og_gauntlet_entrance_attract_spot" ) thread Training_OG_NagPlayerUntilFlag( player, moveToGauntletNags, 45.0, ref_og_gauntletEntrance, "PlayerInGauntletEntryway" ) waitthread Training_OG_Moves( ref_og_gauntletEntrance ) } void function FiringRange_CloseZenGardenDoor_WhenPlayerReachesRange( entity player ) { EndSignal( player, "OnDestroy" ) vector doorFarEdge = < -6368, -952, 48 > // HACK this is the bottom edge of the door farthest from the firing range while ( 1 ) { WaitFrame() if ( !Flag( "PlayerNearFiringRange" ) ) continue if ( PlayerCanSeePos( player, doorFarEdge, true, 90 ) ) continue break } CloseZenGardenExitDoor() } void function FiringRange_ApproachAndEntry( entity player, entity ref_og_needGunsStart, entity ref_og_firingRangeAttractSpot, entity ref_og_firingRangeSpot ) { thread FiringRange_Approach_OG_Sequence( player, ref_og_needGunsStart ) FlagWait( "FiringRange_Approach_OG_Sequence_Done" ) if ( !Flag( "PlayerNearFiringRange" ) ) { // "Time to hit the range." waitthread Training_OG_ScriptedAnim( ref_og_needGunsStart, "OG_Weapons_D" ) } if ( !Flag( "PlayerNearFiringRange" ) ) { // "I'm over here at the range, Cooper." thread Training_OG_NagPlayerUntilFlag( player, [ "og_firingrange_attract_nag" ], 40.0, ref_og_firingRangeAttractSpot, "PlayerNearFiringRange" ) waitthread Training_OG_Moves( ref_og_firingRangeAttractSpot ) } FlagWait( "PlayerNearFiringRange" ) waitthread Training_OG_Moves( ref_og_firingRangeSpot, "OG_firingrange_idle" ) } void function FiringRange_Approach_OG_Sequence( entity player, entity ref_og_needGunsStart ) { EndSignal( player, "OnDestroy" ) if ( !Flag( "PlayerNearFiringRange" ) ) Training_OG_Idles_Sitting( ref_og_needGunsStart ) if ( Flag( "PlayerNearFiringRange" ) ) { // player is rushing forward FlagSet( "ineedguns" ) thread FiringRange_INeedGuns_SFX( player, 0.0 ) } else { FlagSetDelayed( "ineedguns", 2.0 ) thread FiringRange_INeedGuns_SFX( player, 2.0 ) } if ( !Flag( "PlayerNearFiringRange" ) ) { // "In combat, things never go as you expect." waitthread Training_OG_ScriptedAnim( ref_og_needGunsStart, "OG_Weapons_A" ) } if ( !Flag( "PlayerNearFiringRange" ) ) { // "You must be ready to use any weapon you can find on the field." waitthread Training_OG_ScriptedAnim( ref_og_needGunsStart, "OG_Weapons_B" ) } if ( !Flag( "PlayerNearFiringRange" ) ) { // "These are just a few of the weapons I've come across out there." waitthread Training_OG_ScriptedAnim( ref_og_needGunsStart, "OG_Weapons_C" ) } FlagSet( "FiringRange_Approach_OG_Sequence_Done" ) } void function FiringRange_INeedGuns_SFX( entity player, float delayTime ) { EndSignal( player, "OnDestroy" ) entity centerEmitter = GetEntByScriptName( "ref_firingrange_rack_sfx_center" ) entity backCenterEmitter = GetEntByScriptName( "ref_firingrange_rack_sfx_backcenter" ) entity leftEmitter = GetEntByScriptName( "ref_firingrange_rack_sfx_left" ) entity rightEmitter = GetEntByScriptName( "ref_firingrange_rack_sfx_right" ) entity moverCenter = CreateScriptMover( centerEmitter.GetOrigin(), centerEmitter.GetAngles() ) entity moverBackCenter = CreateScriptMover( backCenterEmitter.GetOrigin(), backCenterEmitter.GetAngles() ) entity moverLeft = CreateScriptMover( leftEmitter.GetOrigin(), leftEmitter.GetAngles() ) entity moverRight = CreateScriptMover( rightEmitter.GetOrigin(), rightEmitter.GetAngles() ) array movers = [ moverCenter, moverBackCenter, moverLeft, moverRight ] OnThreadEnd( function() : ( movers ) { foreach ( mover in movers ) { if( !IsValid( mover ) ) continue mover.Destroy() } } ) if ( delayTime > 0.0 ) wait delayTime EmitSoundOnEntity( moverCenter, "training_scr_center_racks" ) EmitSoundOnEntity( moverBackCenter, "training_scr_back_racks" ) EmitSoundOnEntity( moverLeft, "training_scr_left_racks" ) EmitSoundOnEntity( moverRight, "training_scr_right_racks" ) wait 15.0 // wait before cleaning up movers } void function FiringRange_DetectWeaponSwitch( entity player ) { player.EndSignal( "OnDestroy" ) entity ogWeapon = WaitForPlayerActiveWeapon( player ) string ogWeaponName = ogWeapon.GetWeaponClassName() while ( 1 ) { entity weapon = WaitForPlayerActiveWeapon( player ) string weaponName = weapon.GetWeaponClassName() if ( weaponName != "" && weaponName != ogWeaponName ) { FlagSet( "FiringRangeWeaponSwapped" ) break } wait 0.5 } } void function FiringRange_TrainReload( entity player, entity ogIdleSpot ) { FlagClear( "PlayerReloaded" ) SetWeaponHUDEnabled( player, true ) player.SetExtraWeaponMods( [ "" ] ) // turns off low_ammo_disable thread TakeAmmoFromPlayerASAP( player ) thread TrainReload_GivePlayerAmmoAfterButtonPressed( player ) // "Load your weapon." // "Swap in a fresh mag." array reloadNags = [ "og_reload_hint", "og_reload_nag" ] wait 1.1 // let pro players reload before prompting int nagIdx = 0 if ( !Flag( "PlayerReloaded" ) ) { thread OnscreenHint_DisplayUntilFlag( player, "reload_hint", "PlayerReloaded" ) float nagInterval = 15 float lastNagTime = -100 while ( !Flag( "PlayerReloaded" ) ) { wait 0.1 if ( Time() - lastNagTime >= nagInterval ) { waitthread Training_OG_Talks( reloadNags[nagIdx], ogIdleSpot, "OG_firingrange_talk", "OG_firingrange_idle", true ) lastNagTime = Time() nagIdx++ if ( nagIdx >= reloadNags.len() ) nagIdx = 0 } } } } void function TrainReload_GivePlayerAmmoAfterButtonPressed( entity player ) { player.EndSignal( "OnDestroy" ) FlagWait( "PlayerReloaded" ) FlagSet( "ReloadTraining_PlayerPressedReload" ) player.SetActiveWeaponPrimaryAmmoTotal( 50 ) } void function FiringRange_InfiniteAmmo_WhenNearRange( entity player, string endFlag ) { player.EndSignal( "OnDestroy" ) FlagEnd( endFlag ) while ( 1 ) { wait 0.5 if ( !Flag( "PlayerNearFiringRange" ) ) continue entity weapon = player.GetActiveWeapon() if ( weapon == null ) continue int currAmmo = player.GetWeaponAmmoStockpile( weapon ) int magSize = weapon.GetWeaponSettingInt( eWeaponVar.ammo_clip_size ) int maxAmmo = weapon.GetWeaponSettingInt( eWeaponVar.ammo_stockpile_max ) // let player reload before restocking if ( currAmmo > (maxAmmo-magSize + 1) ) continue printt( "firing range restock ammo" ) RestockPlayerAmmo_Silent( player ) } } void function FiringRange_TrainADS( entity player, entity ref_og_firingRangeSpot ) { thread FlagSetWhenPlayerADS( player, "PlayerADSed" ) FlagWaitWithTimeout( "PlayerADSed", 2.5 ) // give player time to ADS before hinting if ( !Flag( "PlayerADSed" ) ) { // "Aim down the sights when engaging more distant targets." // "You can focus more tightly on your targets if you aim down the sights." array adsNags = [ "og_ads_nag_1", "og_ads_nag_2" ] thread Training_OG_NagPlayerUntilFlag( player, adsNags, 10.0, ref_og_firingRangeSpot, "PlayerADSed", "OG_firingrange_talk", "OG_firingrange_idle" ) thread OnscreenHint_DisplayUntilFlag( player, "ads_hint", "PlayerADSed" ) // "To get more precision, aim down the sights of your weapon." waitthread Training_OG_Talks( "og_ads_hint", ref_og_firingRangeSpot, "OG_firingrange_talk", "OG_firingrange_idle", true ) } FlagWait( "PlayerADSed" ) } void function FiringRange_TrainWeaponSwap( entity player, entity ref_og_firingRangeSpot ) { Signal( player, "FiringRange_StopResettingTargets" ) if ( !Flag( "FiringRangeWeaponSwapped" ) ) { DisplayOnscreenHint( player, "weapon_pickup_hint", 5.0 ) thread OnscreenHint_NagUntilFlag( player, "weapon_pickup_hint", "FiringRangeWeaponSwapped", 10.0, 5.0 ) // "Use a different weapon this time. Grab another one off the rack." thread Training_OG_Talks( "og_weaponswap_hint", ref_og_firingRangeSpot, "OG_firingrange_talk", "OG_firingrange_idle", true ) // "Switch to a different weapon." array weaponSwapNags = [ "og_weaponswap_nag" ] waitthread Training_OG_NagPlayerUntilFlag( player, weaponSwapNags, 10.0, ref_og_firingRangeSpot, "FiringRangeWeaponSwapped", "OG_firingrange_talk", "OG_firingrange_idle" ) } } void function FiringRange_PlayerMustDamageAllTargets( entity player, entity ref_og_firingRangeSpot, bool firstTime, bool resetTargetsAtStart = true ) { EndSignal( player, "OnDestroy" ) FlagClear( "FiringRange_AllTargetsKilled" ) thread FiringRange_ResetTargets_Think( player, resetTargetsAtStart ) thread OnscreenHint_DisplayUntilFlag( player, "firingrange_dmg_targets_hint", "FiringRange_AllTargetsKilled" ) wait 2.0 // "Gotta take 'em all out before we can move on." // "Just aim, take a breath, and squeeze the trigger." // "In the real world, the targets don't just stand still. They shoot back." DialogueGroup nags = GetDialogueGroup( "shootTargetsNag" ) int numTargetsKilled = FiringRange_GetNumDamagedTargets() while ( !Flag( "FiringRange_AllTargetsKilled" ) ) { wait 1.0 // don't nag player if they recently damaged a target if ( FiringRange_GetNumDamagedTargets() > numTargetsKilled ) { numTargetsKilled = FiringRange_GetNumDamagedTargets() TimerReset( "firingRangeNag" ) continue } if ( !TimerCheck( "firingRangeNag" ) ) continue string nagLine = DialogueGroup_GetNextLine( nags ) waitthread Training_OG_Talks( nagLine, ref_og_firingRangeSpot, "OG_firingrange_talk", "OG_firingrange_idle", true ) TimerReset( "firingRangeNag" ) } } void function FiringRangeTargets_Init() { string targetScriptName = "firingrange_target" array targetEnts = GetEntArrayByScriptName( targetScriptName ) Assert( targetEnts.len(), "Couldn't get firing range targets with script_name " + targetScriptName ) foreach ( ent in targetEnts ) { ent.SetTakeDamageType( DAMAGE_EVENTS_ONLY ) ent.SetDamageNotifications( true ) entity angleRefEnt = ent.GetLinkEnt() Assert( IsValid( angleRefEnt ), "Firing range target needs an angle reference entity linked" ) angleRefEnt.SetParent( ent ) FiringRangeTarget target target.ent = ent target.angleRefEnt = angleRefEnt target.ogAngles = ent.GetAngles() target.mover = CreateScriptMover( ent.GetOrigin(), ent.GetAngles() ) ent.SetParent( target.mover ) file.firingRangeTargets.append( target ) } } int function FiringRange_GetNumDamagedTargets() { int numDamaged = 0 foreach ( target in file.firingRangeTargets ) if ( target.wasDamaged ) numDamaged++ return numDamaged } void function FiringRange_ResetAllTargets() { foreach ( target in file.firingRangeTargets ) FiringRangeTarget_Reset( target ) } void function FiringRangeTarget_Reset( FiringRangeTarget target ) { if ( !IsValid( target.ent ) ) return target.wasDamaged = false thread FiringRangeTarget_RotateBack( target ) } void function FiringRange_ResetTargets_Think( entity player, bool resetTargetsAtStart = true ) { Signal( player, "FiringRange_StopResettingTargets" ) EndSignal( player, "FiringRange_StopResettingTargets" ) EndSignal( player, "OnDestroy" ) FlagEnd( "PodOutroStarted" ) array firingRangeTargets = file.firingRangeTargets bool firstLoop = true float targetResetWait = 1.75 while ( 1 ) { bool resetTargets = true if ( firstLoop && !resetTargetsAtStart ) resetTargets = false if ( resetTargets ) FiringRange_ResetAllTargets() foreach ( target in firingRangeTargets ) thread FiringRangeTarget_WaitForDamage( target ) // wait for all targets to take damage while ( 1 ) { wait 0.2 bool allDamaged = true foreach ( target in firingRangeTargets ) { if ( !target.wasDamaged ) { allDamaged = false break } } if ( allDamaged ) break } FlagSet( "FiringRange_AllTargetsKilled" ) wait targetResetWait if ( firstLoop ) firstLoop = false } } void function FiringRangeTarget_WaitForDamage( FiringRangeTarget target ) { Signal( target.ent, "Target_WaitForDamage_Start" ) EndSignal( target.ent, "Target_WaitForDamage_Start" ) EndSignal( target.ent, "OnDestroy" ) bool first = true while ( 1 ) { table result = WaitSignal( target.ent, "TargetDamaged" ) vector damagePos = expect vector( result.damagePos ) if ( first ) { thread FiringRangeTarget_FirstDamage( target, damagePos ) first = false } } return } string function FiringRangeTarget_GetDamageSide( FiringRangeTarget target, vector damagePos ) { vector upEntPos = target.angleRefEnt.GetOrigin() vector upEntAngles = target.angleRefEnt.GetAngles() vector facingVec = AnglesToForward( upEntAngles ) vector vecToDamage = Normalize( damagePos - upEntPos ) float dot2Damage = DotProduct( vecToDamage, facingVec ) printt( "damage dot product:", dot2Damage ) #if DEV //float debugDrawTime = 3.0 //DebugDrawAngles( damagePos, upEntAngles, debugDrawTime ) //DebugDrawLine( upEntPos, upEntPos + (facingVec * 200), 200, 200, 0, true, debugDrawTime ) #endif vector frontFacingVec = AnglesToRight( upEntAngles ) bool isOnLeft = IsPointInFrontofLine( damagePos, upEntPos, frontFacingVec ) string returnStr = "left" if ( isOnLeft ) { printt( "LEFT SIDE" ) } else { printt( "RIGHT SIDE" ) returnStr = "right" } return returnStr } void function FiringRangeTarget_FirstDamage( FiringRangeTarget target, vector damagePos ) { //Assert( !target.wasDamaged, "Target not expected to be damaged yet" ) if ( target.wasDamaged ) return target.wasDamaged = true if ( IsAlive( file.player ) ) EmitSoundOnEntity( file.player, "training_scr_hit_target" ) // rotate left by default float rotateAngY = 179.9 if ( FiringRangeTarget_GetDamageSide( target, damagePos ) == "right" ) rotateAngY *= -1 vector newAngles = target.ogAngles + Vector( 0, rotateAngY, 0 ) thread FiringRangeTarget_Rotate( target, newAngles ) } void function FiringRangeTarget_Rotate( FiringRangeTarget target, vector targetAngles ) { Signal( target.mover, "TargetRotate" ) EndSignal( target.mover, "TargetRotate" ) EndSignal( target.mover, "OnDestroy" ) float rotateTime = 0.14 float accelTime = 0 float decelTime = 0.1 target.mover.NonPhysicsRotateTo( targetAngles, rotateTime, accelTime, decelTime ) wait rotateTime } void function FiringRangeTarget_RotateBack( FiringRangeTarget target ) { if ( target.ent.GetAngles() == target.ogAngles ) return EmitSoundAtPosition( TEAM_UNASSIGNED, target.ent.GetOrigin(), "training_scr_range_target_spin" ) waitthread FiringRangeTarget_Rotate( target, target.ogAngles ) } void function FlagSetWhenPlayerADS( entity player, string setFlag ) { player.EndSignal( "OnDestroy" ) while ( player.GetZoomFrac() < 0.9 ) wait 0.1 FlagSet( setFlag ) } // ============================= // ========= GAUNTLET ========== // ============================= void function Training_Setup_Gauntlet( entity player ) { entity ogStart = GetEntByScriptName( "og_gauntlet_entrance_attract_spot" ) entity og = Training_SpawnOGPilot( ogStart ) Training_OG_Idles( ogStart ) TeleportPlayerAndBT( "startpoint_gauntlet_entrance" ) } void function Training_Skipped_Gauntlet( entity player ) { FlagSet( "Gauntlet_FirstRun_Done" ) thread TrainingGauntlet_RemindPlayerAboutMobility( player ) thread TrainingGauntlet_CrouchHint( player ) thread TrainingGauntlet_SetsDifficulty( player ) } void function Training_Gauntlet( entity player ) { entity og = GetOGPilot() entity ref_og_gauntletStartPos = GetEntByScriptName( "og_near_gauntlet_start_pos" ) GauntletInfo trainingGauntlet = GetTrainingGauntlet() // HACK vector resultsBoardCenterPos = < -4899.15, 666.269, 107.187 > if ( !Flag( "PlayerInGauntletEntryway" ) && !trainingGauntlet.isActive && !PlayerCanSeePos( player, resultsBoardCenterPos, true, 90 ) ) DisableGauntlet( trainingGauntlet ) // let a rushing player start the gauntlet if ( !trainingGauntlet.isActive ) FlagWait( "PlayerInGauntletEntryway" ) if ( !trainingGauntlet.isActive ) CheckPoint_Silent() waitthread Training_OG_Moves( ref_og_gauntletStartPos, ANIM_OG_LEANING_IDLE ) thread Training_Gauntlet_FirstRunDialogue( player, trainingGauntlet, ref_og_gauntletStartPos ) wait 1.0 EnableGauntlet( trainingGauntlet ) thread Training_Gauntlet_FirstRunGhostPlaybackStart( player, trainingGauntlet ) thread TrainingGauntlet_TeleportPlayerAtFinishLine( player ) thread TrainingGauntlet_RemindPlayerAboutMobility( player, "Gauntlet_FirstRun_All_VO_Finished" ) thread TrainingGauntlet_CrouchHint( player ) thread TrainingGauntlet_SetsDifficulty( player ) thread Training_Gauntlet_WaitForRequiredTime( player, ref_og_gauntletStartPos ) FlagWait( "Gauntlet_FirstRun_Done" ) if ( !trainingGauntlet.isActive ) CheckPoint_Silent() Gauntlet_StopGhostPlayback( trainingGauntlet ) waitthread Training_Gauntlet_PostFirstRunDialogue( player, trainingGauntlet ) } void function Training_Gauntlet_FirstRunDialogue( entity player, GauntletInfo gauntlet, entity ogIdleSpot ) { player.EndSignal( "OnDestroy" ) if ( !gauntlet.isActive ) { // "Alright. Got a new gauntlet for you to run today." waitthread Training_OG_ScriptedAnim( ogIdleSpot, "OG_gauntlet_start_A" ) } if ( !gauntlet.isActive ) { // "Par time is a minute-forty-five." waitthread Training_OG_ScriptedAnim( ogIdleSpot, "OG_gauntlet_start_B" ) } if ( !gauntlet.isActive ) { // "Gotta do better than that to continue." waitthread Training_OG_ScriptedAnim( ogIdleSpot, "OG_gauntlet_start_C" ) } Objective_Set( "#TRAINING_OBJ_GAUNTLET_FIRSTRUN" ) //if ( gauntlet.isActive ) // Training_Gauntlet_OG_Creates_FirstRun_Ghost( file.ogPilot ) AddAnimEvent( file.ogPilot, "create_ghost", Training_Gauntlet_OG_Creates_FirstRun_Ghost ) // "Follow the ghost, or find your own path." waitthread Training_OG_ScriptedAnim( ogIdleSpot, "OG_gauntlet_start_D" ) thread Training_OG_Idles( ogIdleSpot, "OG_gauntlet_start_endidle" ) DeleteAnimEvent( file.ogPilot, "create_ghost" ) float minDelayEnd = 5.0 + Time() DialogueGroup firstRunGauntletLore = GetDialogueGroup( "firstRunGauntletLore" ) while ( !Flag( "Gauntlet_FirstRun_Done" ) && !firstRunGauntletLore.allPlayed ) { wait 1 if ( !gauntlet.isActive ) continue if ( Time() < minDelayEnd ) continue string line = DialogueGroup_GetNextLine( firstRunGauntletLore ) PlayDialogue( line, player ) } FlagSet( "Gauntlet_FirstRun_All_VO_Finished" ) } void function Training_Gauntlet_OG_Creates_FirstRun_Ghost( entity og ) { entity player = file.player if ( !IsValid( player ) ) return player.Signal( "FirstRun_OG_Creates_Ghost" ) } void function TrainingGauntlet_RemindPlayerAboutMobility( entity player, string waitFlag = "" ) { EndSignal( player, "OnDestroy" ) FlagEnd( "PlayerLeavingGauntlet" ) GauntletInfo trainingGauntlet = GetTrainingGauntlet() //if ( waitFlag != "" && !Flag( waitFlag ) ) // FlagWait( waitFlag ) float nagInterval = 300 // 5 minutes float nextNagTime = -1 float minSampleTime = 15.0 float lowWallrunningFrac = 0.05 bool hasSprinted = false FlagWait( "Gauntlet_FirstRun_All_VO_Finished" ) while ( !Flag( "PlayerLeavingGauntlet" ) ) { if ( !trainingGauntlet.isActive ) WaitSignal( player, "Gauntlet_RunStarted" ) float startTime = Time() float samplePoints = 0 float samplesWallrunning = 0 while ( trainingGauntlet.isActive ) { wait 0.1 if ( player.IsSprinting() && !hasSprinted ) hasSprinted = true samplePoints++ if ( player.IsWallRunning() ) samplesWallrunning++ if ( (Time() - startTime) < minSampleTime ) continue float wallrunningFrac = samplesWallrunning / samplePoints printt( "wallrunningFrac:", wallrunningFrac ) // Has player done all the stuff we would want to remind them about? if ( wallrunningFrac >= lowWallrunningFrac && hasSprinted ) break if ( Time() >= nextNagTime ) { if ( !hasSprinted ) thread TrainingGauntlet_SprintNag( player ) else if ( wallrunningFrac < lowWallrunningFrac ) thread TrainingGauntlet_WallrunNag( player, trainingGauntlet ) nextNagTime = Time() + nagInterval } break } if ( trainingGauntlet.isActive ) WaitSignal( player, "Gauntlet_RunStopped" ) } } void function TrainingGauntlet_SprintNag( entity player ) { EndSignal( player, "OnDestroy" ) float hintDuration = 10.0 float hintEndTime = Time() + hintDuration DisplayOnscreenHint( player, "sprint_button_hint", hintDuration ) EndSignal( player, "DisplayingOnscreenHint" ) while ( Time() < hintEndTime && !player.IsSprinting() ) WaitFrame() ClearOnscreenHint( player ) } void function TrainingGauntlet_WallrunNag( entity player, GauntletInfo trainingGauntlet ) { EndSignal( player, "OnDestroy" ) //printt( "playing mobility nag" ) DisplayOnscreenHint( player, "gauntlet_wallrun_hint", 8.0 ) DialogueGroup gauntletHints_wallrun = GetDialogueGroup( "gauntletHints_wallrun" ) string line = DialogueGroup_GetNextLine( gauntletHints_wallrun ) waitthread PlayDialogue( line, player ) if ( !trainingGauntlet.isActive ) return waitthread PlayDialogue( "og_gauntlet_hint_wallrun_capper", player ) } void function TrainingGauntlet_CrouchHint( entity player ) { EndSignal( player, "OnDestroy" ) string checkFlag = "Gauntlet_PlayerInCrouchHintZone" string nagTimer = "gauntlet_crouchHint" TimerInit( nagTimer, 2.5 ) float waitTime = 1.0 bool hintShowing = false while ( 1 ) { wait waitTime if ( !Flag( checkFlag ) ) continue TimerReset( nagTimer ) while ( Flag( checkFlag ) ) { wait waitTime if ( !TimerCheck( "gauntlet_crouchHint" ) ) continue if ( !hintShowing ) { DisplayOnscreenHint( player, "crouch_hint" ) hintShowing = true } } if ( hintShowing ) { ClearOnscreenHint( player ) hintShowing = false } } } void function Training_Gauntlet_PostFirstRunDialogue( entity player, GauntletInfo gauntlet ) { player.EndSignal( "OnDestroy" ) player.EndSignal( "Gauntlet_RunStarted" ) entity ref_og_gauntletResults = GetEntByScriptName( "og_ref_gauntlet_results_display" ) Objective_SetSilent( "#TRAINING_OBJ_DEFAULT" ) waitthread Training_OG_Moves( ref_og_gauntletResults, "", 1.0 ) AddAnimEvent( file.ogPilot, "highlight_results_board_tip", Gauntlet_PostFirstRun_SetRandomTip ) // "Nice run! See the results board on the wall? You set a new Best Time." // "Everyone has different strengths and weaknesses, so be sure to run this a few times with different weapons." // "Look at the results board for more tips on how to improve." waitthread Training_OG_ScriptedAnim( ref_og_gauntletResults, "OG_Gauntlet_return" ) DeleteAnimEvent( file.ogPilot, "highlight_results_board_tip" ) } // update tip while OG is talking about the tips void function Gauntlet_PostFirstRun_SetRandomTip( entity og ) { entity player = file.player if ( !IsValid( player ) ) return GauntletInfo gauntlet = GetTrainingGauntlet() Remote_CallFunction_Replay( player, "ScriptCallback_GauntletResultsDisplay_SetRandomTip", gauntlet.id ) } void function Training_Gauntlet_FirstRunGhostPlaybackStart( entity player, GauntletInfo gauntlet ) { if ( !gauntlet.isActive ) WaitSignal( player, "Gauntlet_RunStarted", "FirstRun_OG_Creates_Ghost" ) thread Gauntlet_StartGhostPlayback( gauntlet, GHOST_NAME_FIRSTRUN, "#GAUNTLET_GHOST_NAME_FIRSTRUN" ) } void function Training_Gauntlet_WaitForRequiredTime( entity player, entity ogIdleSpot ) { player.EndSignal( "OnDestroy" ) GauntletInfo gauntlet = GetTrainingGauntlet() // "The first run of the day is always the toughest." // "Too slow! I know you can do better. Give it another try." DialogueGroup firstRunFailedGroup = GetDialogueGroup( "firstRunFailed" ) int failCount = 0 while ( 1 ) { // keep setting this here because by default the gauntlet updates its random tip after each finished run Remote_CallFunction_Replay( player, "ScriptCallback_TrainingGauntlet_ResultsDisplay_SetTip", gauntlet.id, 0 ) player.WaitSignal( "Gauntlet_RunStopped" ) wait 0.1 // HACK let the gauntlet struct get updated before checking it TrainingGauntletStats_PostRunUpdate( player, gauntlet, 0 ) if ( !gauntlet.runFinished ) continue failCount++ if ( gauntlet.bestTime <= TRAINING_GAUNTLET_MAX_TIME_TO_PROGRESS || failCount == NUM_GAUNTLET_FAILS_BEFORE_FORCED_PROGRESS ) { if ( gauntlet.bestTime <= TRAINING_GAUNTLET_MAX_TIME_TO_PROGRESS ) printt( "Gauntlet moving on: beat required time" ) else if ( failCount == NUM_GAUNTLET_FAILS_BEFORE_FORCED_PROGRESS ) printt( "Gauntlet moving on: forced progress after", NUM_GAUNTLET_FAILS_BEFORE_FORCED_PROGRESS, "failures" ) break } wait 0.5 // let player get settled before VO starts EmitSoundOnEntityOnlyToPlayer( player, player, "training_scr_gaunlet_fail_01" ) string firstRunFailedLine = DialogueGroup_GetNextLine( firstRunFailedGroup ) thread Training_OG_Talks_Leaning( firstRunFailedLine, ogIdleSpot, "", "", true ) Objective_Remind() //DisplayOnscreenHint( player, "gauntlet_first_run_progression_hint", 9.0 ) if ( failCount == 2 ) thread DoSprintEnableDialogForGauntletIfNeeded_Thread( player, 3.5 ) } TrainingGauntletPostRun_TryAchievements( player, gauntlet ) FlagSet( "Gauntlet_FirstRun_Done" ) } void function DoSprintEnableDialogForGauntletIfNeeded_Thread( entity player, float waitTime ) { EndSignal( player, "OnDestroy" ) wait waitTime if ( GetAutosprintEnabled() ) return Remote_CallFunction_UI( player, "ScriptCallback_OpenAutosprintDialogForGauntlet" ) } void function TrainingGauntlet_TeleportPlayerAtFinishLine( entity player ) { Signal( player, "Gauntlet_StopTeleportingPlayerAtFinishLine" ) EndSignal( player, "Gauntlet_StopTeleportingPlayerAtFinishLine" ) EndSignal( player, "OnDestroy" ) entity startEnt = GetEntByScriptName( "results_room_teleport_refA" ) entity endEnt = GetEntByScriptName( "results_room_teleport_refB" ) vector startPos = startEnt.GetOrigin() vector endPos = endEnt.GetOrigin() while ( 1 ) { player.WaitSignal( "Gauntlet_PlayerHitFinishTrig" ) vector currentPos = player.GetOrigin() float offsetX = currentPos.x - startPos.x float offsetY = currentPos.y - startPos.y float offsetZ = currentPos.z - startPos.z float newPosX = endPos.x + offsetX float newPosY = endPos.y + offsetY float newPosZ = endPos.z + offsetZ vector newPos = < newPosX, newPosY, newPosZ > player.SetOrigin( newPos ) } } void function TrainingGauntlet_SetsDifficulty( entity player ) { if ( Flag( "PlayerLeavingGauntlet" ) ) return FlagEnd( "PlayerLeavingGauntlet" ) player.EndSignal( "OnDestroy" ) GauntletInfo gauntlet = GetTrainingGauntlet() bool diffSetOnce = false while ( 1 ) { WaitSignal( player, "Gauntlet_RunStopped" ) if ( file.gauntletMode ) return wait 0.2 // HACK let the gauntlet struct and file variables get set up after the run // extra wait for player to finish seeing their time, etc. wait 2.0 if ( !gauntlet.runFinished ) continue if ( !gauntlet.lastRunBestTime ) continue int prevDiff = -1 if ( diffSetOnce ) prevDiff = GetConVarInt( "sp_difficulty" ) Training_SetDifficultyForGauntletTime( gauntlet.bestTime ) if ( GetConVarInt( "sp_difficulty" ) > prevDiff ) Training_AnnounceDifficultyForGauntletTime( player, 8.0 ) if ( !diffSetOnce ) diffSetOnce = true } } void function Training_SetDifficultyForGauntletTime( float time ) { float time = file.trainingGauntletStats.bestTime if ( time == -1 ) time = TRAINING_GAUNTLET_MAX_TIME_TO_PROGRESS int difficulty = DIFFICULTY_EASY //if ( time <= REC_DIFF_GAUNTLET_TIME_MASTER ) // difficulty = DIFFICULTY_MASTER // else if if ( time <= REC_DIFF_GAUNTLET_TIME_HARD ) difficulty = DIFFICULTY_HARD else if ( time <= REC_DIFF_GAUNTLET_TIME_NORMAL ) difficulty = DIFFICULTY_NORMAL printt( "GetDifficultyForGauntletTime: diff", difficulty, "for time", time ) file.trainingGauntletStats.recommendedDifficulty = difficulty SetConVarInt( "sp_difficulty", difficulty ) } void function Training_AnnounceDifficultyForGauntletTime( entity player, float displayTime = 5.0 ) { string hintAlias = "hint_diff_" hintAlias += file.trainingGauntletStats.recommendedDifficulty.tostring() DisplayOnscreenHint( player, hintAlias, displayTime ) } // ====================================================== // ========= GAUNTLET CHALLENGE (post 1st run) ========== // ====================================================== void function Training_Setup_GauntletChallenge( entity player ) { entity ogStart = GetEntByScriptName( "og_ref_gauntlet_results_display" ) entity og = Training_SpawnOGPilot( ogStart ) Training_OG_Idles( ogStart ) TeleportPlayerAndBT( "playerstart_gauntlet_challenge" ) } void function Training_Skipped_GauntletChallenge( entity player ) { } void function Training_GauntletChallenge( entity player ) { GauntletInfo gauntlet = GetTrainingGauntlet() thread TrainingGauntlet_TeleportPlayerAtFinishLine( player ) entity ref_og_gauntletLeaderboardPos = GetEntByScriptName( "og_near_leaderboard" ) // in gauntlet mode OG spawns here before player fade finishes, no need to move him if ( !file.gauntletMode ) waitthread Training_OG_Moves( ref_og_gauntletLeaderboardPos, "" ) Gauntlet_ShowLeaderboard( gauntlet ) thread GauntletChallenge_GhostsThink( player, gauntlet, "GauntletChallenge_FirstGhostAppear", "PlayerLeavingGauntlet" ) thread GauntletChallenge_IntroDialogue( player, gauntlet, ref_og_gauntletLeaderboardPos ) thread GauntletChallege_RestartGauntletHint( player, gauntlet ) FlagWait( "ChallengeIntro_VO_Done" ) bool installRuiStarted = false if ( !Training_IsGameFullyInstalled() ) Remote_CallFunction_Replay( player, "ScriptCallback_ShowInstallProgress", true ) entity ref_og_gauntletExitPos = GetEntByScriptName( "og_gauntlet_exit_pos" ) thread Training_GauntletChallenge_DialogueThink( player, gauntlet, ref_og_gauntletExitPos ) thread Training_OG_Moves( ref_og_gauntletExitPos, "OG_all_done_idle" ) wait 1.0 thread Training_LeaveGauntletThink( player, ref_og_gauntletExitPos ) FlagWait( "PlayerConfirmedGauntletExit" ) Remote_CallFunction_Replay( player, "ScriptCallback_ShowInstallProgress", false ) Objective_SetSilent( "#TRAINING_OBJ_DEFAULT" ) DisableGauntlet( gauntlet ) FlagWait( "PlayerLeavingGauntlet" ) Signal( player, "Gauntlet_StopTeleportingPlayerAtFinishLine" ) if ( file.gauntletMode ) { thread Training_PodOutro( player ) WaitForever() // stall normal progression } else { thread DisableWeaponDelayed( player, 0.3 ) // disable weapon so crosshairs aren't visible during white screen entity destEnt = GetEntByScriptName( "startpoint_titanfall" ) waitthread PlayerAndOGTeleport_Fancy( player, destEnt.GetOrigin(), "og_titanfall_start_pos", destEnt.GetAngles() ) CheckPoint_Silent() } } void function DisableWeaponDelayed( entity player, float delay ) { EndSignal( player, "OnDestroy" ) if ( delay > 0 ) wait delay player.DisableWeapon() } void function GauntletChallege_RestartGauntletHint( entity player, GauntletInfo gauntlet ) { EndSignal( player, "OnDestroy" ) float displayTime = 5.0 float delayTime = 2.5 const int RUNS_BETWEEN_REMINDERS = 3 int runsSinceLastReminder = 3 // do reminder on first run while ( 1 ) { if ( !gauntlet.isActive ) WaitSignal( player, "Gauntlet_RunStarted" ) float displayEndTime = -1 // check if we should remind after run starts if ( runsSinceLastReminder >= RUNS_BETWEEN_REMINDERS ) { thread OnscreenHint_DisplayAfterDelay( player, "gauntlet_restart_hint", displayTime, delayTime ) displayEndTime = Time() + displayTime + delayTime } // wait for run to stop table result = WaitSignal( player, "Gauntlet_RunStopped", "Gauntlet_ForceRestart" ) string signal = expect string( result.signal ) // clear initial hint if player cancelled the run before it would auto clear if ( displayEndTime != -1 && Time() < displayEndTime ) ClearOnscreenHint( player ) // player went back through the starting gate? if ( signal == "Gauntlet_RunStopped" && !gauntlet.runFinished ) { // Don't care about how many runs between reminders here- if player does this it means they don't really get it yet thread DisplayOnscreenHint( player, "gauntlet_restart_hint", displayTime ) runsSinceLastReminder = 0 } // player used menu to restart? else if ( signal == "Gauntlet_ForceRestart" ) { runsSinceLastReminder = 0 // reset the counter when force restarted } if ( runsSinceLastReminder >= RUNS_BETWEEN_REMINDERS ) runsSinceLastReminder = 0 if ( gauntlet.runFinished ) // player restarted from menu or went back through the gate runsSinceLastReminder++ } } void function GauntletChallenge_IntroDialogue( entity player, GauntletInfo gauntlet, entity ogRef ) { if ( file.gauntletMode ) { thread GauntletChallengeModeOnly_IntroDialogue( player, gauntlet, ogRef ) return } player.EndSignal( "OnDestroy" ) if ( !gauntlet.isActive ) { // "Now that you're warmed up: if you want a REAL challenge, you can race against other Pilot ghosts." waitthread Training_OG_ScriptedAnim( ogRef, "OG_Leaderboard_A" ) } player.Signal( "GauntletChallenge_FirstGhostAppear" ) if ( !gauntlet.isActive ) { // "Word of warning, though- the Pilots who recorded these ghosts are the best in the SRS. waitthread Training_OG_ScriptedAnim( ogRef, "OG_Leaderboard_B" ) } if ( !gauntlet.isActive ) { // "If you can beat them, you'll be halfway to being a real Pilot." waitthread Training_OG_ScriptedAnim( ogRef, "OG_Leaderboard_C" ) } if ( !gauntlet.isActive ) { // "Go ahead and run the Gauntlet as much as you want." waitthread Training_OG_ScriptedAnim( ogRef, "OG_Leaderboard_D" ) } // "When you're done, I've got something special to show you." waitthread Training_OG_ScriptedAnim( ogRef, "OG_Leaderboard_E" ) if ( !Training_IsGameFullyInstalled() ) { Objective_Set( "#TRAINING_OBJ_GAUNTLET_CHALLENGE_INSTALLING" ) thread ChangeGauntletObjective_OnInstallComplete( player ) } else { Objective_Set( "#TRAINING_OBJ_GAUNTLET_CHALLENGE" ) } FlagSet( "ChallengeIntro_VO_Done" ) } void function GauntletChallengeModeOnly_IntroDialogue( entity player, GauntletInfo gauntlet, entity ogRef ) { player.EndSignal( "OnDestroy" ) SetSignalDelayed( player, "GauntletChallenge_FirstGhostAppear", 0.25 ) wait 1.5 // let player teleport and fade in before starting to talk if ( !gauntlet.isActive ) { // "Go ahead and run the Gauntlet as much as you want." waitthread Training_OG_ScriptedAnim( ogRef, "OG_Leaderboard_D", true ) } // "When you're done, I've got something special to show you." //waitthread Training_OG_ScriptedAnim( ogRef, "OG_Leaderboard_E" ) Objective_Set( "#TRAINING_OBJ_GAUNTLET_CHALLENGE" ) FlagSet( "ChallengeIntro_VO_Done" ) } void function ChangeGauntletObjective_OnInstallComplete( entity player ) { EndSignal( player, "OnDestroy" ) while ( !Training_IsGameFullyInstalled() ) wait 1.0 Objective_Set( "#TRAINING_OBJ_GAUNTLET_CHALLENGE" ) } void function GauntletChallenge_GhostsThink( entity player, GauntletInfo gauntlet, string signalWait, string endFlag ) { if ( Flag( endFlag ) ) return player.EndSignal( "OnDestroy" ) gauntlet.signalEnt.EndSignal( "OnDestroy" ) FlagEnd( endFlag ) if ( !gauntlet.isActive ) WaitSignal( player, "Gauntlet_RunStarted", signalWait ) thread Gauntlet_ChallengeLeaderboardGhosts( player, gauntlet, "PlayerLeavingGauntlet" ) } void function Training_GauntletChallenge_DialogueThink( entity player, GauntletInfo gauntlet, entity ogSpot ) { player.EndSignal( "OnDestroy" ) FlagWait( "ChallengeIntro_VO_Done" ) DialogueGroup clearedLeaderboard = GetDialogueGroup( "clearedLeaderboard" ) DialogueGroup defeatedGhost = GetDialogueGroup( "defeatedGhost" ) DialogueGroup notBestTime = GetDialogueGroup( "notBestTime" ) DialogueGroup newBestTime = GetDialogueGroup( "newBestTime" ) while ( 1 ) { FlagClear( "Gauntlet_PlayingFeedbackVO" ) WaitSignal( player, "Gauntlet_RunStopped" ) wait 0.1 // HACK let the gauntlet struct get updated before checking TrainingGauntletStats_PostRunUpdate( player, gauntlet, 1 ) if ( !gauntlet.runFinished ) continue TrainingGauntletPostRun_TryAchievements( player, gauntlet ) FlagSet( "Gauntlet_PlayingFeedbackVO" ) string feedbackLine = "" // SPECIAL- defeated a Hero Ghost if ( gauntlet.lastRunDefeatedGhost && !gauntlet.allGhostsDefeated ) { array leaderboard = Gauntlet_GetLeaderboard( gauntlet ) GauntletGhost playerGhost = Gauntlet_GetPlayerGhost( gauntlet ) array heroFileNames = [ GHOST_NAME_ALIAS_LASTIMOSA, GHOST_NAME_ALIAS_ANDERSON, GHOST_NAME_ALIAS_BRIGGS ] GauntletGhost loserGhost foreach ( ghost in leaderboard ) { if ( playerGhost.duration <= ghost.duration && heroFileNames.contains( ghost.fileName ) ) { loserGhost = ghost break } } switch ( loserGhost.fileName ) { case GHOST_NAME_ALIAS_LASTIMOSA: // "Hey - that was my best time! I must be getting slow." feedbackLine = "og_gauntlet_unlocked_leaderboard_entry_og" break case GHOST_NAME_ALIAS_ANDERSON: // "Heh. Can't wait to tell Anderson about that. Son of a bitch..." feedbackLine = "og_gauntlet_unlocked_leaderboard_entry_anderson" break case GHOST_NAME_ALIAS_BRIGGS: // "You just beat Commander Briggs. Might not stay that way for long though, she's very competitive." feedbackLine = "og_gauntlet_unlocked_leaderboard_entry_briggs" break } if ( feedbackLine != "" ) waitthread Training_OG_Talks( feedbackLine, ogSpot, "OG_all_done_talk", "OG_all_done_idle", true ) } // SPECIAL - cleared leaderboard if ( gauntlet.allGhostsDefeated && !clearedLeaderboard.allPlayed ) { while ( !clearedLeaderboard.allPlayed && !gauntlet.isActive ) { feedbackLine = DialogueGroup_GetNextLine( clearedLeaderboard ) waitthread Training_OG_Talks( feedbackLine, ogSpot, "OG_all_done_talk", "OG_all_done_idle", true ) } } // If we played a line by now, it was Special, so we don't want the generic ones below. if ( feedbackLine != "" ) continue // LAST RUN: DEFEATED GHOST (GENERIC) if ( gauntlet.lastRunDefeatedGhost && !gauntlet.allGhostsDefeated ) { feedbackLine = DialogueGroup_GetNextLine( defeatedGhost ) } // LAST RUN: BEAT BEST TIME else if ( gauntlet.lastRunBestTime ) { feedbackLine = DialogueGroup_GetNextLine( newBestTime ) } // LAST RUN: FAILED TO BEAT BEST TIME else { feedbackLine = DialogueGroup_GetNextLine( notBestTime ) } Assert( feedbackLine != "" ) waitthread Training_OG_Talks( feedbackLine, ogSpot, "OG_all_done_talk", "OG_all_done_idle", true ) } } void function TrainingGauntletPostRun_TryAchievements( entity player, GauntletInfo gauntlet ) { Assert( gauntlet.runFinished ) GauntletGhost andersonGhost = Gauntlet_GetGhostByFileName( gauntlet, GHOST_NAME_ALIAS_ANDERSON ) if ( gauntlet.lastRunTime < andersonGhost.duration ) { // Achievement - Beat Pilot Anderson's gauntlet ghost recorder time UnlockAchievement( player, achievements.GAUNTLET_BEAT_ANDERSON ) } GauntletGhost playerGhost = Gauntlet_GetPlayerGhost( gauntlet ) int playerLeaderboardPos = Gauntlet_GetLeaderboardPosition_ForGhostID( gauntlet, playerGhost.id ) if ( playerLeaderboardPos <= 2 ) { // Achievement - Get a top-3 spot on the Gauntlet scoreboard UnlockAchievement( player, achievements.GAUNTLET_TOPTHREE ) } } void function Training_LeaveGauntletThink( entity player, entity ogSpot ) { string gauntletExitConversation = "Gauntlet_Exit" if ( file.gauntletMode ) gauntletExitConversation = "Gauntlet_Exit_TechTest" GauntletInfo trainingGauntlet = GetTrainingGauntlet() // NOTE conversation callbacks need this defined file.animref_leaveGauntlet = ogSpot AddConversationCallback( gauntletExitConversation, ConvoCallback_TrainingExit ) AddConversationCallback( "Titanfall_Intro", ConvoCallback_TitanfallIntro) int numTimesInitiated = 0 while ( !Flag( "PlayerConfirmedGauntletExit" ) ) { FlagWait( "Gauntlet_PlayerInExitZone" ) FlagWaitClear( "Gauntlet_PlayingFeedbackVO" ) if ( !Flag( "Gauntlet_PlayerInExitZone" ) ) continue if ( !Training_IsGameFullyInstalled() ) { waitthread Gauntlet_GameNotFullyInstalled_Response( player, ogSpot ) wait 0.1 // HACK make sure we always wait before continuing continue } numTimesInitiated++ // All done with the gauntlet? waitthread Training_OG_ScriptedAnim( ogSpot, "OG_all_done", true ) Training_OG_Idles( ogSpot, "OG_all_done_idle", true ) // if player rushed to the gauntlet during the anim, don't wait for conversation if ( trainingGauntlet.isActive ) continue // hint if player can't figure out how to conversate bool displayedOnscreenHint = false if ( numTimesInitiated > 1 && !Flag( "PlayerUsedConversationInterface" ) ) { thread OnscreenHint_DisplayAfterDelay( player, "conversation_hint", 5.0, 1.5 ) displayedOnscreenHint = true } // interactive dialogue: Gauntlet Exit FlagClear( "GauntletExitConvo_FinishedResponse" ) thread PlayerConversation( gauntletExitConversation, player, file.ogPilot ) table result = WaitSignal( player, "ConversationEnded", "PlayerMadeSelection", "Gauntlet_RunStarted" ) string sig = expect string( result.signal ) // bug 202299: Also check flag for player confirmed gauntlet exit // - player can confirm exit, but if player starts a gauntlet run just before the response line ends, convo can "cancel" causing a prog break here if ( sig == "Gauntlet_RunStarted" ) { // HACK- wait a bit to make sure player didn't confirm exit before the conversation ended due to gauntlet run starting wait 0.5 if ( !Flag( "PlayerConfirmedGauntletExit" ) ) { StopConversationNow( player ) continue } } /* #if DEV if ( sig == "Gauntlet_RunStarted" && Flag( "PlayerConfirmedGauntletExit" ) ) printt( "BUG CONDITION DEFEATED" ) #endif */ if ( sig == "PlayerMadeSelection" ) FlagSet( "PlayerUsedConversationInterface" ) if ( displayedOnscreenHint ) ClearOnscreenHint( player ) // wait for callback function to finish doing its thing FlagWait( "GauntletExitConvo_FinishedResponse" ) if ( Flag( "PlayerConfirmedGauntletExit" ) ) break FlagWaitClear( "Gauntlet_PlayerInExitZone" ) StopConversationNow( player ) } if ( file.gauntletMode ) { GauntletMode_Finished( player ) } else { // interactive dialogue: Titanfall Intro FlagClear( "TitanfallIntroConvo_FinishedResponse" ) waitthread PlayerConversation( "Titanfall_Intro", player, file.ogPilot ) FlagWait( "TitanfallIntroConvo_FinishedResponse" ) } wait 0.2 FlagSet( "PlayerLeavingGauntlet" ) } void function Gauntlet_GameNotFullyInstalled_Response( entity player, entity ogRef ) { EndSignal( player, "OnDestroy" ) float progress = GetGameFullyInstalledProgress() printt( "Game is not fully installed- cannot continue past Gauntlet. Progress:", progress, "/ 1.0" ) DisplayOnscreenHint( player, "gauntlet_install_hint" ) OnThreadEnd( function() : ( player ) { if ( IsValid( player ) ) ClearOnscreenHint( player ) } ) if ( TimerCheck( "installWaitComment" ) ) { DialogueGroup installWait = GetDialogueGroup( "installWait" ) string line = DialogueGroup_GetNextLine( installWait ) waitthread Training_OG_Talks( line, ogRef, "OG_all_done_talk", "OG_all_done_idle", true ) TimerReset( "installWaitComment" ) } while ( Flag( "Gauntlet_PlayerInExitZone" ) && !Training_IsGameFullyInstalled() ) { wait 0.25 } } void function ConvoCallback_TrainingExit( int choice ) { EndSignal( file.player, "OnDestroy" ) OnThreadEnd( function() : ( ) { if ( IsValid( file.player ) ) FlagSet( "GauntletExitConvo_FinishedResponse" ) } ) printt( "TRAINING EXIT CALLBACK: player chose", choice ) Assert( choice >= 0 && choice <= 2, "Nothing set up for Training Exit convo choice " + choice ) // prompt timed out if ( choice == 0 ) return Signal( file.player, "PlayerMadeSelection" ) // player chooses to stay if ( choice == 2 ) { waitthread Training_OG_ScriptedAnim( file.animref_leaveGauntlet, "OG_all_done_respond", true ) Training_OG_Idles( file.animref_leaveGauntlet, "OG_all_done_idle", true ) return } // player chooses to leave FlagSet( "PlayerConfirmedGauntletExit" ) printt( "PLAYER CONFIRMED GAUNTLET EXIT" ) // Good. You're gonna like this. // It's time you learned the other half of being a Pilot: the Titan. Let's go call one in. string anim = "OG_gonna_like_this" if ( file.gauntletMode ) { // Good. You're gonna like this. // Let's get back to the real world. anim = "OG_gonna_like_this_tech_test_end" } waitthread Training_OG_ScriptedAnim( file.animref_leaveGauntlet, anim, true ) Training_OG_Idles( file.animref_leaveGauntlet, "OG_gonna_like_this_endidle", true ) } void function ConvoCallback_TitanfallIntro( int choice ) { EndSignal( file.player, "OnDestroy" ) OnThreadEnd( function() : ( ) { if ( IsValid( file.player ) ) FlagSet( "TitanfallIntroConvo_FinishedResponse" ) } ) printt( "TITANFALL INTRO CALLBACK: player chose", choice ) Assert( choice >= 0 && choice <= 2, "Nothing set up for Titanfall Intro convo choice " + choice ) // It's only a simulation, Cooper. It's not the real thing. // But first- We're gonna need a little more space string responseAnim = "OG_gonna_like_this_respond_A" if ( choice == 2 ) { // That's the spirit. // But first- We're gonna need a little more space responseAnim = "OG_gonna_like_this_respond_B" } thread Training_OG_ScriptedAnim( file.animref_leaveGauntlet, responseAnim, true ) float duration = GetOGPilot().GetSequenceDuration( responseAnim ) wait duration - 1.0 //Training_OG_Idles( file.animref_leaveGauntlet, "OG_gonna_like_this_endidle", true ) } // runType: 0 = before beating required time; 1 = in challenge mode void function TrainingGauntletStats_PostRunUpdate( entity player, GauntletInfo gauntlet, int runType ) { Assert( !gauntlet.isActive, "Can't update gauntlet stats reliably while gauntlet is active." ) // restarted gauntlet from menu if ( !gauntlet.runFinished ) { file.trainingGauntletStats.numRestarts++ printt( "training gauntlet stats updated: numRestarts", file.trainingGauntletStats.numRestarts ) SendTrainingGauntletStats( player ) return } if ( gauntlet.lastRunBestTime ) { file.trainingGauntletStats.bestTime = gauntlet.bestTime printt( "training gauntlet stats updated: bestTime", file.trainingGauntletStats.bestTime ) } if ( runType == 0 ) { file.trainingGauntletStats.numRunsBeforeBeatRequiredTime++ printt( "training gauntlet stats updated: numRunsBeforeBeatRequiredTime", file.trainingGauntletStats.numRunsBeforeBeatRequiredTime ) if ( gauntlet.bestTime > TRAINING_GAUNTLET_MAX_TIME_TO_PROGRESS ) { // failed to get required time SendTrainingGauntletStats( player ) return } else { file.trainingGauntletStats.didBeatRequiredTime = true printt( "training gauntlet stats updated: didBeatRequiredTime", file.trainingGauntletStats.didBeatRequiredTime ) } } else if ( runType == 1 ) { file.trainingGauntletStats.numChallengeRuns++ printt( "training gauntlet stats updated: numChallengeRuns", file.trainingGauntletStats.numChallengeRuns ) } SendTrainingGauntletStats( player ) } void function SendTrainingGauntletStats( entity player ) { printt("======== TRAINING GAUNTLET STATS =========" ) printt( "numRestarts:", file.trainingGauntletStats.numRestarts ) printt( "numRunsBeforeBeatRequiredTime:", file.trainingGauntletStats.numRunsBeforeBeatRequiredTime ) printt( "didBeatRequiredTime:", file.trainingGauntletStats.didBeatRequiredTime ) printt( "numChallengeRuns:", file.trainingGauntletStats.numChallengeRuns ) printt( "bestTime:", file.trainingGauntletStats.bestTime ) printt("========= END TRAINING GAUNTLET STATS ==========" ) SendTrainingGauntletStatsToBackend( player, file.trainingGauntletStats.didBeatRequiredTime ? file.trainingGauntletStats.numRunsBeforeBeatRequiredTime : 0, file.trainingGauntletStats.numChallengeRuns, file.trainingGauntletStats.bestTime ) } bool function Training_IsGameFullyInstalled() { #if DEV if ( INSTALL_DELAY_TEST ) return file.fakeInstallDone #endif return IsGameFullyInstalled() } #if DEV void function setfakeinstalldone( bool isDone ) { file.fakeInstallDone = isDone } #endif // =================================== // ============ TITANFALL ============ // =================================== void function Training_Setup_Titanfall( entity player ) { entity ogRef_titanfall = GetEntByScriptName( "og_titanfall_start_pos" ) entity og = Training_SpawnOGPilot( ogRef_titanfall ) Training_OG_Idles_Sitting( ogRef_titanfall, "OG_first_titan_idle" ) player.DisableWeapon() // player weapon is disabled before teleport to Titanfall area starts TeleportPlayerAndBT( "startpoint_titanfall" ) } void function Training_Skipped_Titanfall( entity player ) { player.SetExtraWeaponMods( ["training_low_ammo_disable"] ) SetWeaponHUDEnabled( player, false ) } void function Training_Titanfall( entity player ) { thread Titanfall_EnableWeaponDelayed( player, 0.9 ) thread TakeAmmoFromPlayerASAP( player ) thread Titanfall_SpecialWeaponRemove( player ) entity og = GetOGPilot() entity ogRef_titanfall = GetEntByScriptName( "og_titanfall_start_pos" ) Training_OG_Idles_Sitting( ogRef_titanfall, "OG_first_titan_idle" ) vector twinRefSpawnOrg = TitanfallGlitch_WorldChange_GetOtherWorldPos( ogRef_titanfall.GetOrigin(), true ) entity ogRef_titanfall_twin = CreateScriptMover( twinRefSpawnOrg, ogRef_titanfall.GetAngles() ) entity ogTwin = Training_SpawnOGTwin( ogRef_titanfall_twin ) Training_NPC_Idles_Sitting( ogTwin, ogRef_titanfall_twin, "OG_first_titan_idle" ) OnThreadEnd( function() : ( player, ogTwin, ogRef_titanfall_twin ) { if ( IsValid( player ) ) player.UnfreezeControlsOnServer() if ( IsValid( ogTwin ) ) ogTwin.Destroy() if ( IsValid( ogRef_titanfall_twin ) ) ogRef_titanfall_twin.Destroy() } ) //testglitch() //wait 1000000 thread Titanfall_BT_Think( player, 6.5 ) wait 2.0 // "That's my partner, BT. He's a Vanguard-class." // "Homegrown Militia technology." // "The first Titan chassis we designed ourselves. One we didn't have to steal from the IMC." waitthread Training_OG_ScriptedAnim( ogRef_titanfall, "OG_Partner_BT", true ) Training_OG_Idles_Sitting( ogRef_titanfall, "OG_first_titan_idle", true ) wait 1.1 // take a breath between lines thread Training_EnableTitanfallAndNag( player, ogRef_titanfall, 25.0 ) // "Go ahead Rifleman, call in your first Titan." Titanfall_ResetHintVOTimer() // in case player calls in Titan during this line waitthread Training_OG_ScriptedAnim( ogRef_titanfall, "OG_First_Titan", true ) Training_OG_Idles_Sitting( ogRef_titanfall, "OG_first_titan_idle", true ) FlagWait( "PlayerStartedTitanfall" ) // Don't start OG's next line until the nag is done, to avoid overlap wait Titanfall_GetHintVORemainingDuration() // "Look up, to the sky- there he is." waitthread Training_OG_ScriptedAnim( ogRef_titanfall, "OG_Look_Up", true ) Training_OG_Idles_Sitting( ogRef_titanfall, "OG_first_titan_idle", true ) thread Titanfall_QuickdeathCustomResetPlayerPos( player, file.playerTitanCallInPos, "PodOutroStarted" ) // wait for titan to appear while ( !GetPlayerTitanInMap( player ) ) wait 0.1 entity titan = GetPlayerTitanInMap( player ) // "glitch" interrupts titan drop FlagWait( "TitanfallGlitchStart" ) PlayMusic( "music_training_01_glitch" ) // Glitch ends when this function ends float totalGlitchTime = 2.9 float glitchEndTime = Time() + totalGlitchTime wait 1.0 player.FreezeControlsOnServer() thread Training_OG_ScriptedAnim( ogRef_titanfall, "OG_Pulling_You_Out", true ) wait glitchEndTime - Time() if ( IsValid( titan ) ) titan.Destroy() if ( IsValid( file.titanTwin ) ) file.titanTwin.Destroy() } void function Titanfall_EnableWeaponDelayed( entity player, float delay ) { EndSignal( player, "OnDestroy" ) if ( delay > 0 ) wait delay player.EnableWeapon() player.SetExtraWeaponMods( ["training_low_ammo_disable"] ) SetWeaponHUDEnabled( player, false ) } void function Titanfall_SpecialWeaponRemove( entity player ) { array weapons = player.GetMainWeapons() int numWeaponsStart = weapons.len() foreach ( equippedWeapon in weapons ) { string equippedWeaponClassName = equippedWeapon.GetWeaponClassName() if ( equippedWeaponClassName == "mp_weapon_lstar" ) // LSTAR display blinks annoyingly if ammo is gone { // give player a weapon if we are going to take their only weapon if ( numWeaponsStart <= 1 ) player.GiveWeapon( "mp_weapon_semipistol" ) player.TakeWeapon( equippedWeaponClassName ) break } } } void function Titanfall_BT_Think( entity player, float getUpDelay = 0.0 ) { EndSignal( player, "OnDestroy" ) entity spawnRef = GetEntByScriptName( "titanfall_bt_spawn_ref" ) vector spawnOrg = spawnRef.GetOrigin() vector spawnAng = spawnRef.GetAngles() // create BT TitanLoadoutDef loadout = GetTitanLoadoutForPlayer( player ) entity bt = CreateNPCTitan( loadout.setFile, player.GetTeam(), spawnOrg, spawnAng, loadout.setFileMods ) bt.ai.titanSpawnLoadout = loadout bt.kv.spawnflags = SF_NPC_ALLOW_SPAWN_SOLID DispatchSpawn( bt ) entity animref = CreateScriptMover( spawnOrg, spawnAng ) OnThreadEnd( function() : ( bt, animref ) { if ( IsValid( bt ) ) bt.Destroy() if ( IsValid( animref ) ) animref.Destroy() } ) bt.EnableNPCFlag( NPC_IGNORE_FRIENDLY_SOUND ) bt.SetEfficientMode( true ) bt.SetTouchTriggers( false ) bt.SetNoTarget( true ) bt.UnsetUsable() bt.SetInvulnerable() bt.EnableRenderAlways() bt.SetTitle( "#NPC_BT_NAME" ) ShowName( bt ) DisableTitanfallForLifetimeOfEntityNearOrigin( bt, bt.GetOrigin(), TITANHOTDROP_DISABLE_ENEMY_TITANFALL_RADIUS ) // HACK this anim pops if it's played off of the animref, // but looks good if played off BT before using the animref for the rest thread PlayAnim( bt, "bt_training_scripted_kneel_idle", bt ) if ( getUpDelay > 0 ) wait getUpDelay waitthread PlayAnim( bt, "bt_training_scripted_kneel2stand", animref ) thread PlayAnim( bt, "bt_training_scripted_stand_idle", animref ) FlagWait( "PodOutroStarted" ) } void function Titanfall_QuickdeathCustomResetPlayerPos( entity player, vector titanCallInPos, string endFlag ) { FlagEnd( endFlag ) entity resetEnt = GetEntByScriptName( "ref_titanfall_quickdeath_reset" ) vector resetPos = resetEnt.GetOrigin() while ( 1 ) { WaitSignal( player, "QuickDeath" ) vector angToCallInPos = VectorToAngles( titanCallInPos - resetPos ) float viewTiltUpAngX = -10.0 // goose the view up a little vector viewAng = player.p.quickDeathOrigin = resetPos player.p.quickDeathAngles = viewAng } } void function Training_EnableTitanfallAndNag( entity player, entity ogRef, float nagDelay ) { EndSignal( player, "OnDestroy" ) SetGlobalNetBool( "trainingTitanfallEnabled", true ) AddClientCommandCallback( "ClientCommand_TrainingRequestedTitanfall", Training_RequestTitanfall ) float onscreenHintDisplayTime = nagDelay * 0.5 thread OnscreenHint_DisplayAfterDelay( player, "titanfall_hint", onscreenHintDisplayTime, 3.5 ) // extra delay on first screen prompt so pros can call it in Objective_SetSilent( "#TRAINING_OBJ_CALL_TITAN" ) // "Titan's ready. Call it in." // "Titan's ready to drop. On your mark." array titanfallNags = [ "og_titanfall_nag_1", "og_titanfall_nag_2" ] DialogueGroup callInTitan = GetDialogueGroup( "callInTitan" ) while ( !Flag( "PlayerStartedTitanfall" ) ) { FlagWaitWithTimeout( "PlayerStartedTitanfall", nagDelay ) if ( Flag( "PlayerStartedTitanfall" ) ) break thread OnscreenHint_DisplayAfterDelay( player, "titanfall_hint", onscreenHintDisplayTime, 2.5 ) string line = DialogueGroup_GetNextLine( callInTitan ) Titanfall_ResetHintVOTimer() waitthread Training_OG_Talks_Sitting( line, ogRef, "OG_first_titan_talk", "OG_first_titan_idle" ) } Objective_SetSilent( "#TRAINING_OBJ_DEFAULT" ) ClearOnscreenHint( player ) } // CUSTOM HOTDROP bool function Training_RequestTitanfall( entity player, array args ) { Training_ReplacementTitan( player ) //Separate function because other functions will call ReplacementTitan return true } bool function Training_ReplacementTitan( entity player ) { Assert( IsAlive( player ) ) Assert ( !GetPlayerTitanInMap( player ) ) Point spawnPoint = GetTitanReplacementPoint( player, false ) vector origin = spawnPoint.origin vector angles = spawnPoint.angles file.playerTitanCallInPos = origin FlagSet( "PlayerStartedTitanfall" ) SetGlobalNetBool( "trainingTitanfallEnabled", false ) thread Training_CreateTitanForPlayerAndHotdrop( player, origin, angles ) return true } #if DEV void function testglitch() { thread Training_CreateTitanForPlayerAndHotdrop( GetPlayerArray()[0], <2048, -2852, -349>, <0,0,0> ) } #endif void function Training_CreateTitanForPlayerAndHotdrop( entity player, vector spawnOrg, vector spawnAng ) { Assert( IsValid( player ) ) OnThreadEnd( function() : ( player ) { if ( !IsValid( player ) ) return player.ClearHotDropImpactTime() } ) player.EndSignal( "OnDestroy" ) vector origin = spawnOrg vector angles if ( spawnAng != < 0.0, 0.0, 0.0 > ) angles = spawnAng else angles = VectorToAngles( FlattenVector( player.GetViewVector() ) * -1 ) // face the player printt( "Dropping replacement titan at " + origin + " with angles " + angles ) player.Signal( "CalledInReplacementTitan" ) int playerTeam = player.GetTeam() // spawn the Titan that will drop TitanLoadoutDef loadout = GetTitanLoadoutForPlayer( player ) entity titan = CreateAutoTitanForPlayer_FromTitanLoadout( player, loadout, origin, angles ) DispatchSpawn( titan ) titan.EndSignal( "OnDeath" ) titan.SetSkin( 2 ) // "Sarah Briggs" Vanguard skin titan.SetTitle( "#NPC_BT_SPARE_NAME" ) HideName( titan ) TakeAllWeapons( titan ) titan.SetEfficientMode( true ) titan.SetTouchTriggers( false ) titan.SetNoTarget( true ) titan.UnsetUsable() // Disable titan embark titan.Hide() OnThreadEnd( function() : ( titan ) { if ( !IsValid( titan ) ) return // removed so that model highlight always works for you autotitan // titan.DisableRenderAlways() } ) // based on "at_hotdrop_drop_2knee_turbo" string animation = "bt_hotdrop_glitch_descent" string postButtonPressSFX = "training_scr_titan_glitch_button_press" string hotdropToGlitchSFX = "training_anim_glitch_scene" EmitSoundOnEntity( player, postButtonPressSFX ) float hotDropAnimDuration = titan.GetSequenceDuration( animation ) float extraSpawnDelay = 1.5 // bit of extra padding time for "Look up, to the sky" VO to sink in extraSpawnDelay += Titanfall_GetHintVORemainingDuration() // Glitched hotdrop anim created by trimming the end from normal hotdrop anim (glitch starts just before the Titan hits the ground) // - add this little bit back so the timer looks "correct" (longer than actual anim duration) float arrivalTimerDuration = 0.7 arrivalTimerDuration += (hotDropAnimDuration + extraSpawnDelay) // now add the rest of the normal delays before arrival thread Training_DrawReplacementTitanLocation( player, origin, arrivalTimerDuration ) wait extraSpawnDelay titan.Show() ShowName( titan ) Attachment result = titan.Anim_GetAttachmentAtTime( animation, "OFFSET", (hotDropAnimDuration - 0.1) ) vector maxs = titan.GetBoundingMaxs() vector mins = titan.GetBoundingMins() int mask = titan.GetPhysicsSolidMask() origin = ModifyOriginForDrop( origin, mins, maxs, result.position, mask ) titan.SetInvulnerable() //Make Titan invulnerable until bubble shield is up. Cleared in OnTitanHotdropImpact titan.EnableRenderAlways() int teamNum = TEAM_UNASSIGNED if ( IsValid( player ) ) teamNum = player.GetTeam() vector fwdToPlayer = player.GetOrigin() - origin vector facingAngles = VectorToAngles( fwdToPlayer ) vector facingAngles2D = angles = facingAngles2D //FadeOutSoundOnEntity( player, postButtonPressSFX, 1.0 ) EmitSoundOnEntity( player, hotdropToGlitchSFX ) thread PlayAnimTeleport( titan, animation, origin, angles ) wait hotDropAnimDuration - 0.1 titan.SetTouchTriggers( true ) thread TitanfallGlitch( player, titan, origin, angles ) } void function Titanfall_ResetHintVOTimer() { file.titanfallNagStartTime = Time() } float function Titanfall_GetHintVORemainingDuration() { if ( file.titanfallNagStartTime == -1 ) return 0 float duration = TITANFALL_NAG_DURATION - (Time() - file.titanfallNagStartTime) if ( duration < 0 ) duration = 0 return duration } bool function Titanfall_IsHintVOStillPlaying() { return Titanfall_GetHintVORemainingDuration() >= 0 } void function TitanfallGlitch( entity player, entity titan, vector origin, vector angles ) { if ( Flag( "PodOutroStarted" ) ) return FlagEnd( "PodOutroStarted" ) player.EndSignal( "OnDestroy" ) titan.EndSignal( "OnDestroy" ) titan.Signal( "glitch_start" ) FlagSet( "TitanfallGlitchStart" ) if ( IsValid( file.titanTwin ) ) file.titanTwin.Destroy() entity titanTwin = CreatePropScript( BUDDY_MODEL, titan.GetOrigin(), titan.GetAngles() ) file.titanTwin = titanTwin vector twinOrigin = TitanfallGlitch_WorldChange_GetOtherWorldPos( origin, true ) array sceneTitans = [ titan, titanTwin ] // idle on last frame of descent anim titanTwin.Anim_Stop() titan.Anim_Stop() string endIdleAnim = GetDefaultTitanfallGlitchAnim() thread PlayAnimTeleport( titanTwin, endIdleAnim, twinOrigin, angles ) thread PlayAnimTeleport( titan, endIdleAnim, origin, angles ) // DEPRECATED no more glitchy anims // do this on the hotdropping titan to blend out of the hotdrop better (but makes the proxy on the client out of sync) // - in the future, need to play all the hotdrop anims on the proxy as well, to let it blend the same //thread PlayAnim( titan, endIdleAnim, origin, angles, 0.1 ) // START GLITCH SEQUENCE // setup for client extra flicker titan.Hide() titanTwin.Hide() int eHandle_titan = titan.GetEncodedEHandle() int eHandle_twin = titanTwin.GetEncodedEHandle() Remote_CallFunction_Replay( player, "ScriptCallback_TitanfallGlitch_ExtraFlicker", eHandle_titan ) Remote_CallFunction_Replay( player, "ScriptCallback_TitanfallGlitch_ExtraFlicker", eHandle_twin, true ) SetGlobalNetBool( "titanGlitch_extraFlicker", true ) // makes him flicker until the world starts changing // DEPRECATED Not doing the huge anim moves might work better for the glitch moment, trying it out //thread TitanfallGlitch_CycleTitanGlitchAnims_OnWorldChange( player, titan, titanTwin, origin, twinOrigin, angles ) float screenFXTime = 4.5 float screenFXStartDelay = 0.1 thread StartGlitchScreenFX_Delayed( player, screenFXTime, screenFXStartDelay ) // FIRST IMPACT float firstShakeDuration = 3.0 float shakeAmplitude = 14.0 float screenBlurFrac = 0.25 SimpleScreenShake( player, firstShakeDuration, shakeAmplitude, screenBlurFrac ) // juice it with extra rumble // note- this is not juicing it as much as I would expect... even cranking the amplitude doesn't really work that well float rumbleAmplitude = 50.0 float rumbleFrequency = 170 float rumbleDuration = 4.5 CreateShakeRumbleOnly( player.GetOrigin(), rumbleAmplitude, rumbleFrequency, rumbleDuration ) // let the titan idle for a moment float postDescentIdleTime = 1.0 wait postDescentIdleTime float firstShakeDuration_remaining = firstShakeDuration - postDescentIdleTime // DEPRECATED trying without world changes //thread TitanfallGlitch_PlayerWorldChange( player, firstShakeDuration_remaining * 0.9, 0.0 ) wait firstShakeDuration_remaining /* DEPRECATED don't need smaller shakes now that the scene only lasts a short time printt( "FIRST IMPACT DONE") // LOOPING SMALLER IMPACTS float shakeDuration_min = 2.5 float shakeDuration_max = 3.0 shakeAmplitude = 5.5 screenBlurFrac = 0.6 while ( 1 ) { float shakeDuration = RandomFloatRange( shakeDuration_min, shakeDuration_max ) SimpleScreenShake( player, shakeDuration, shakeAmplitude, screenBlurFrac ) thread TitanfallGlitch_PlayerWorldChange( player, shakeDuration * 0.85, shakeDuration * 0.1 ) wait shakeDuration } */ } void function StartGlitchScreenFX_Delayed( entity player, float screenFXTime, float delay ) { if ( Flag( "PodOutroStarted" ) ) return FlagEnd( "PodOutroStarted" ) EndSignal( player, "OnDestroy" ) if ( delay > 0 ) wait delay Remote_CallFunction_Replay( player, "ScriptCallback_PodGlitch_PlayerScreenFX", screenFXTime ) } const float GLITCH_WORLD_CHANGE_WAIT_MIN = 0.1 const float GLITCH_WORLD_CHANGE_WAIT_MAX = 0.35 const float GLITCH_WORLD_CHANGE_MINWAIT_FOR_EXTRA_FLICKER = 0.25 void function TitanfallGlitch_CycleTitanGlitchAnims_OnWorldChange( entity player, entity mainTitan, entity titanTwin, vector origin, vector twinOrigin, vector angles, float delay = 0.0 ) { player.EndSignal( "OnDestroy" ) mainTitan.EndSignal( "OnDestroy" ) titanTwin.EndSignal( "OnDestroy" ) array sceneTitans = [ mainTitan, titanTwin ] OnThreadEnd( function() : ( player, sceneTitans ) { if ( IsValid( player ) ) { FlagClear( "PlayerWorldChangeThread" ) SetGlobalNetBool( "titanGlitch_extraFlicker", false ) } foreach ( titan in sceneTitans ) if ( IsValid( titan ) ) titan.Show() } ) if ( delay > 0 ) wait delay array titanAnims = GetTitanfallGlitchAnims() array twinAnims = GetTitanfallGlitchTwinAnims() Assert( titanAnims.len() == twinAnims.len() ) int animIdx = GetGlobalNetInt( "titanfallGlitchAnimIdx" ) while ( 1 ) { table result = WaitSignal( player, "Glitch_WorldChanging_Zen", "Glitch_WorldChanging_NonZen" ) string signal = expect string( result.signal ) float worldChangeDuration = expect float( result.postChangeWait ) //printt( "titan anim:", titanAnims[animIdx] ) mainTitan.Anim_Stop() titanTwin.Anim_Stop() thread PlayAnimTeleport( mainTitan, titanAnims[animIdx], origin, angles ) thread PlayAnimTeleport( titanTwin, twinAnims[animIdx], twinOrigin, angles ) animIdx++ if ( animIdx >= titanAnims.len() ) animIdx = 0 SetGlobalNetInt( "titanfallGlitchAnimIdx", animIdx ) // update flicker settings & show/hide server versions of the titans if ( signal == "Glitch_WorldChanging_Zen" ) { // only do extra flicker if a longer world change pause is happening if ( worldChangeDuration >= GLITCH_WORLD_CHANGE_MINWAIT_FOR_EXTRA_FLICKER ) { SetGlobalNetBool( "titanGlitch_extraFlicker", true ) foreach ( titan in sceneTitans ) titan.Hide() } } else if ( signal == "Glitch_WorldChanging_NonZen" ) { SetGlobalNetBool( "titanGlitch_extraFlicker", false ) foreach ( titan in sceneTitans ) titan.Show() } else { Assert( false, "Couldn't find signal: " + signal ) } } } void function TitanfallGlitch_PlayerWorldChange( entity player, float duration, float delay = 0.0 ) { if ( Flag( "PodOutroStarted" ) ) return FlagEnd( "PodOutroStarted" ) EndSignal( player, "OnDestroy" ) if ( delay > 0 ) wait delay FlagWaitClear( "PlayerWorldChangeThread" ) FlagSet( "PlayerWorldChangeThread" ) OnThreadEnd( function() : ( player ) { if ( IsValid( player ) ) FlagClear( "PlayerWorldChangeThread" ) } ) bool isInZenWorld = true float endTime = Time() + duration float minWait = GLITCH_WORLD_CHANGE_WAIT_MIN float maxWait = GLITCH_WORLD_CHANGE_WAIT_MAX while ( 1 ) { // at end time, make sure player pos resets back to zen world before breaking if ( Time() >= endTime && isInZenWorld ) break vector newpos = TitanfallGlitch_WorldChange_GetOtherWorldPos( player.GetOrigin(), isInZenWorld ) float postChangeWait string changeSignal entity newSkyCam if ( isInZenWorld ) { // Switch to non zen world changeSignal = "Glitch_WorldChanging_NonZen" postChangeWait = minWait // always wait min time in non zen world newSkyCam = file.skycam_glitch isInZenWorld = false } else { // Switch back to zen world postChangeWait = RandomFloatRange( minWait, maxWait ) changeSignal = "Glitch_WorldChanging_Zen" newSkyCam = file.skycam_default isInZenWorld = true } table extraInfo = {} extraInfo["postChangeWait"] <- postChangeWait Signal( player, changeSignal, extraInfo ) player.SetSkyCamera( newSkyCam ) player.SetOrigin( newpos ) wait postChangeWait } } vector function TitanfallGlitch_WorldChange_GetOtherWorldPos( vector currentPos, bool startInZenWorld ) { entity zenEnt = GetEntByScriptName( "titan_glitch_swap_ref1" ) entity nonZenEnt = GetEntByScriptName( "titan_glitch_swap_ref2" ) Assert( zenEnt && nonZenEnt ) vector zenPos = zenEnt.GetOrigin() vector nonZenPos = nonZenEnt.GetOrigin() float offsetX float offsetY float offsetZ float newPosX float newPosY float newPosZ if ( startInZenWorld ) { offsetX = currentPos.x - zenPos.x offsetY = currentPos.y - zenPos.y offsetZ = currentPos.z - zenPos.z newPosX = nonZenPos.x + offsetX newPosY = nonZenPos.y + offsetY newPosZ = nonZenPos.z + offsetZ } else { offsetX = currentPos.x - nonZenPos.x offsetY = currentPos.y - nonZenPos.y offsetZ = currentPos.z - nonZenPos.z newPosX = zenPos.x + offsetX newPosY = zenPos.y + offsetY newPosZ = zenPos.z + offsetZ } vector newPos = < newPosX, newPosY, newPosZ > return newPos } void function Training_DrawReplacementTitanLocation( entity player, vector origin, float delay ) { float endTime = Time() + delay player.SetHotDropImpactDelay( endTime ) Remote_CallFunction_Replay( player, "ServerCallback_ReplacementTitanSpawnpoint", origin.x, origin.y, origin.z, endTime ) player.WaitSignal( "OnDeath" ) } // =================================== // ============ POD OUTRO ============ // =================================== void function Training_Setup_PodOutro( entity player ) { } void function Training_Skipped_PodOutro( entity player ) { Training_EnvArtColorCorrection_SetEnabled( false ) SetDoF_Hangar( player ) Objective_Clear() FlagSet( "PodOutroStarted" ) FlagSet( "SimPodShutdown_LoudspeakerVO_Done" ) } #if DEV // bare bones start in pod void function DEV_PodOutro( entity player ) { player.EndSignal( "OnDestroy" ) TakeAllWeapons( player ) player.SetExtraWeaponMods( [ "low_ammo_disable" ] ) SetWeaponHUDEnabled( player, false ) Training_EnvArtColorCorrection_SetEnabled( false ) SetDoF_Hangar( player ) thread MeetOG_BackgroundSkits( player ) entity pod = file.trainingPod TrainingPod_PlayerSequence_DoorsOpenIdle( player, false ) // anim starts at a slightly different spot player.SetOrigin( < 10564, -10235, -6056.9 > ) player.SetAngles( < -6, 90, 0 > ) WaitForever() } #endif //DEV void function Training_PodOutro( entity player ) { player.EndSignal( "OnDestroy" ) Objective_Clear() CheckPoint_Silent() entity pod = file.trainingPod FlagSet( "PodOutroStarted" ) OnThreadEnd( function() : ( player, pod ) { if ( IsValid( player ) ) { //StopSoundOnEntity( player, "NPE_Scr_SimPod_End" ) // Not sure if we need any of this //player.Anim_Stop() //ClearAnimViewEntity( player ) //player.ClearParent() //player.UnforceStand() } if ( IsValid ( pod ) ) { // Not sure if we need any of this //pod.Anim_Stop() //thread TrainingPod_ResetLaserEmitterRotation( pod ) //thread TrainingPod_KillLasers( pod ) //thread TrainingPod_KillGlowFX( pod ) //TrainingPod_KillInteriorDLights() } } ) // Have to do this first so the anim starts centered on the ref attachment angles string podAttach = "REF" int attachID = pod.LookupAttachment( podAttach ) vector podRefOrg = pod.GetAttachmentOrigin( attachID ) vector podRefAng = pod.GetAttachmentAngles( attachID ) player.SetOrigin( podRefOrg ) player.SetAngles( podRefAng ) player.ForceStand() TakeAllWeapons( player ) // start closed idle FirstPersonSequenceStruct playerSequence playerSequence.blendTime = 0.0 playerSequence.attachment = podAttach playerSequence.firstPersonAnimIdle = "ptpov_trainingpod_idle" playerSequence.thirdPersonAnimIdle = "pt_trainingpod_idle" playerSequence.viewConeFunction = TrainingPod_ViewConeLock_Strict // so player can't move camera under the pod shutdown screen playerSequence.renderWithViewModels = true FirstPersonSequenceStruct podSequence podSequence.blendTime = 0.0 podSequence.thirdPersonAnimIdle = "trainingpod_doors_close_idle" podSequence.renderWithViewModels = true thread FirstPersonSequence( playerSequence, player, pod ) thread FirstPersonSequence( podSequence, pod ) thread CadillacMoment_MeetOG( player ) //thread PodOutro_ScreenShake( player ) Training_EnvArtColorCorrection_SetEnabled( false ) SetDoF_Hangar( player ) // show the "pod shutdown screen" float shutdownScreenWait = 7.0 float screenFadeWait = 7.0 ScreenFade( player, 0, 0, 0, 255, 0, screenFadeWait, FFADE_OUT | FFADE_STAYOUT ) Remote_CallFunction_Replay( player, "ScriptCallback_SimPodShutdownScreen", shutdownScreenWait ) thread SimPodShutdown_Dialogue( player ) // HACK, need to wait a bit so player moves into pod before initing shutdown sequence // - otherwise, interior emitter angle setup math vs player eye position will crash the game float waitForPlayerToBeMoved = 1.0 wait waitForPlayerToBeMoved float shutdownSequence_waitBeforeAnimStart = 10.0 thread TrainingPod_Interior_ShutdownSequence( player, shutdownSequence_waitBeforeAnimStart ) wait screenFadeWait - waitForPlayerToBeMoved // HACK reparent the emitters so they look correct, I didn't expect to have to do this TrainingPod_SnapLaserEmittersToAttachPoints() // Transition screen FX thread PlayFXOnEntity( FX_POD_SCREEN_OUT, player ) float fadeInFromShutdownScreenTime = 0.2 ScreenFade( player, 0, 0, 0, 255, fadeInFromShutdownScreenTime, 1, FFADE_IN | FFADE_PURGE ) wait fadeInFromShutdownScreenTime TrainingPod_ViewConeLock_PodClosed( player ) // start shutdown sequence // HACK- eventually one sound will cover the whole sequence thread HACK_DelayedShutdownSequenceSFX( player, 3.0 ) player.Signal( "TrainingPod_BeginInteriorShutdown" ) wait shutdownSequence_waitBeforeAnimStart FirstPersonSequenceStruct playerSequence_podOpens playerSequence_podOpens.blendTime = 0.25 playerSequence_podOpens.attachment = podAttach playerSequence_podOpens.firstPersonAnim = "ptpov_trainingpod_doors_open" playerSequence_podOpens.firstPersonAnimIdle = "ptpov_trainingpod_idle" playerSequence_podOpens.thirdPersonAnim = "pt_trainingpod_doors_open" playerSequence_podOpens.thirdPersonAnimIdle = "pt_trainingpod_idle" playerSequence_podOpens.viewConeFunction = TrainingPod_ViewConeLock_SemiStrict playerSequence_podOpens.renderWithViewModels = true FirstPersonSequenceStruct podSequence_podOpens podSequence_podOpens.blendTime = 0.25 podSequence_podOpens.thirdPersonAnim = "trainingpod_doors_open" podSequence_podOpens.thirdPersonAnimIdle = "trainingpod_doors_open_idle" podSequence_podOpens.renderWithViewModels = true thread TrainingPod_TurnOnInteriorDLights_Delayed( player, 1.5 ) thread FirstPersonSequence( podSequence_podOpens, pod ) thread FirstPersonSequence( playerSequence_podOpens, player, pod ) wait 2.1 // wait until scene starts animating for Meet OG } void function HACK_DelayedShutdownSequenceSFX( entity player, float delayTime ) { EndSignal( player, "OnDestroy" ) wait delayTime EmitSoundOnEntityOnlyToPlayerWithSeek( player, player, "NPE_Scr_SimPod_End", 0.7 ) } void function PodOutro_ScreenShake( entity player ) { if ( Flag( "MeetOG_StartScene" ) ) return FlagEnd( "MeetOG_StartScene" ) player.EndSignal( "OnDestroy" ) float shakeDuration = 2.0 float shakeAmplitude = 0.2 float screenBlurFrac = 0.0 float shakeDelayMin = 1.75 float shakeDelayMax = 2.25 while ( 1 ) { SimpleScreenShake( player, shakeDuration, shakeAmplitude, screenBlurFrac ) wait shakeDuration - (shakeDuration * 0.25) // HACK shake dies down wait RandomFloatRange( shakeDelayMin, shakeDelayMax ) } } void function SimPodShutdown_LoudspeakerVO( entity player ) { player.EndSignal( "OnDestroy" ) wait 4.0 // wait for "I'm pulling you out" // "Powering down all non-essential systems." waitthread PlayLoudspeakerVO( "outro_01_1" ) // "Prepare for Typhon atmospheric entry in less than three minutes." //waitthread PlayLoudspeakerVO( "outro_01" ) wait 7.0 // "All personnel to battle stations." waitthread PlayLoudspeakerVO( "outro_01_2" ) // "This is not a drill." waitthread PlayLoudspeakerVO( "outro_01_3" ) FlagSet( "SimPodShutdown_LoudspeakerVO_Done" ) } void function SimPodShutdown_Dialogue( entity player ) { player.EndSignal( "OnDestroy" ) thread SimPodShutdown_LoudspeakerVO( player ) Assert( IsValid( file.ogPilot ), "Can't find scene entity ogPilot" ) // "Alright Rifleman, sounds like it's about to hit the fan." waitthread PlayDialogue( "og_hit_the_fan", file.ogPilot ) // "I'm pulling you out." waitthread PlayDialogue( "og_pulling_you_out", file.ogPilot ) wait 2.0 // "Cooper! Ready up!" waitthread PlayDialogue( "grunt_closed_pod_yell_at_player", file.ogPilot ) // HACK play it off OG until the animation idle is legit // "Easy Cole, he just left VR. Needs a minute to decompress. He'll be ready to go... trust me." waitthread PlayDialogue( "og_closed_pod_respond_to_grunt", file.ogPilot ) // "Yes sir." waitthread PlayDialogue( "grunt_yes_sir", file.ogPilot ) } #if DEV void function shutdownscreentest( float duration = 5.0 ) { entity player = file.player EndSignal( player, "OnDestroy" ) float fadeTime = 0.2 ScreenFade( player, 0, 0, 0, 255, fadeTime, duration, FFADE_OUT | FFADE_STAYOUT ) Remote_CallFunction_Replay( player, "ScriptCallback_SimPodShutdownScreen", duration ) wait duration ScreenFade( player, 0,0,0, 255, fadeTime, fadeTime, FFADE_IN | FFADE_PURGE ) } #endif // ================================= // ============ MEET OG ============ // ================================= void function Training_Setup_MeetOG( entity player ) { TakeAllWeapons( player ) thread CadillacMoment_MeetOG( player ) TrainingPod_PlayerSequence_DoorsOpenIdle( player ) wait 0.2 } void function Training_Skipped_MeetOG( entity player ) { // settings for checkpoints after the normal level progression SetDoF_Default( player ) player.SetExtraWeaponMods( [] ) // turn off low_ammo_disable SetWeaponHUDEnabled( player, true ) } void function Training_MeetOG( entity player ) { thread MeetOG_BackgroundSkits( player ) FlagSet( "MeetOG_StartScene" ) thread MeetOG_ControllerRumble( player ) FlagWait( "CadillacMoment_MeetOG_StartFadeOut" ) UnlockAchievement( player, achievements.COMPLETE_TRAINING ) // Free SP trial? Load Beacon next. if( Script_IsRunningTrialVersion() ) { thread FreeTrial_OutroPopup( player, 2.5 ) PickStartPoint( "sp_beacon", "Level Start" ) } else { thread OutroDifficultyPopup( player, 2.5 ) // load next level // NOTE this does a screen fade already PickStartPoint( "sp_crashsite", "LevelStart" ) } // don't ever progress from here to the dev functions beyond WaitForever() } void function FreeTrial_OutroPopup( entity player, float delay ) { EndSignal( player, "OnDestroy" ) wait delay Remote_CallFunction_UI( player, "ScriptCallback_Training_FreeTrialMessage" ) } void function OutroDifficultyPopup( entity player, float delay ) { EndSignal( player, "OnDestroy" ) wait delay Remote_CallFunction_UI( player, "ScriptCallback_Training_SelectSPDifficulty" ) } // can't do screen shake for this part because it looks bad (not connected) when player view is 1P animating while parented to the pod void function MeetOG_ControllerRumble( entity player ) { player.EndSignal( "OnDestroy" ) FlagEnd( "CadillacMoment_MeetOG_StartFadeOut" ) float shakeDuration = 2.8 float shakeAmplitudeMin = 13.5 float shakeAmplitudeMax = 14.5 float frequency = 155 float shakeDelayMin = 4.0 float shakeDelayMax = 7.0 while ( 1 ) { float amplitude = RandomFloatRange( shakeAmplitudeMin, shakeAmplitudeMax ) //vector org, float amplitude = 16, float frequency = 150, float duration = 1.5, float radius = 2048 CreateAirShakeRumbleOnly( player.GetOrigin(), amplitude, frequency, shakeDuration ) printt( "Shake Rumble:", amplitude ) wait shakeDuration * 0.75 // HACK shake dies down pretty early wait RandomFloatRange( shakeDelayMin, shakeDelayMax ) } } void function CadillacMoment_MeetOG( entity player ) { thread MeetOG_LoudspeakerVO( player ) int friendlyTeam = player.GetTeam() entity animref = file.animref_hangar vector animrefOrigin = animref.GetOrigin() vector animrefAngles = animref.GetAngles() vector btSpawnOrg = <0,0,0> // to avoid red text errors about BT spawning in solid entity animEnt = CreateScriptMover( animrefOrigin, animrefAngles ) // Spawn scene actors entity og = Training_SpawnOGPilot( animref ) Training_OGPilot_SetHelmetOn( og, false ) SetTeam( og, TEAM_SPECTATOR ) // turn off his glowy bits so they're accentuated when helmet turns on later entity anderson = CreateSoldier( friendlyTeam, animrefOrigin, animrefAngles ) DispatchSpawn( anderson ) anderson.SetModel( ANDERSON_PILOT_MODEL ) anderson.SetTitle( "#TRAINING_ANDERSON_NAME" ) //ShowName( anderson ) HideName( anderson ) entity redshirt = CreateSoldier( friendlyTeam, animrefOrigin, animrefAngles ) DispatchSpawn( redshirt ) redshirt.SetModel( TEAM_MIL_GRUNT_MODEL_LMG ) redshirt.SetTitle( "#CPT_COLE" ) //ShowName( redshirt ) HideName( redshirt ) TitanLoadoutDef loadout = GetTitanLoadoutForCurrentMap() entity bt = CreateAutoTitanForPlayer_FromTitanLoadout( player, loadout, btSpawnOrg, animrefAngles ) SetSpawnOption_AISettings( bt, "npc_titan_buddy" ) bt.kv.spawnflags = SF_NPC_ALLOW_SPAWN_SOLID DispatchSpawn( bt ) FreeAutoTitan( bt ) // HACK this disables the worldspace BT locator icon TakeAllWeapons( bt ) array actors = [ player, bt, og, anderson, redshirt ] // Setup scene props entity ogHelmet = CreatePropDynamic( OG_PILOT_HELMET_MODEL ) ogHelmet.DisableHibernation() file.ogHelmet = ogHelmet string knifeTag = "KNIFE" int knifeTagID = og.LookupAttachment( knifeTag ) entity dataKnife = CreatePropScript( DATA_KNIFE_MODEL, og.GetAttachmentOrigin( knifeTagID ), og.GetAttachmentAngles( knifeTagID ) ) dataKnife.DisableHibernation() dataKnife.SetParent( og, knifeTag, false, 0.0 ) asset btWeaponModel = GetWeaponInfoFileKeyFieldAsset_Global( "mp_titanweapon_xo16_shorty", "playermodel" ) Assert( btWeaponModel != $"" ) entity btWeapon = CreatePropDynamic( btWeaponModel ) btWeapon.SetParent( bt, "PROPGUN" ) string gruntRifleName = "mp_weapon_rspn101" string andersonWeaponName = "mp_weapon_car" asset gruntRifleModel = GetWeaponInfoFileKeyFieldAsset_Global( gruntRifleName, "playermodel" ) asset andersonWeaponModel = GetWeaponInfoFileKeyFieldAsset_Global( andersonWeaponName, "playermodel" ) asset ogWeaponModel = GetWeaponInfoFileKeyFieldAsset_Global( OG_WEAPON, "playermodel" ) Assert( gruntRifleModel != $"" ) Assert( andersonWeaponModel != $"" ) Assert( ogWeaponModel != $"" ) entity ogWeapon = CreatePropDynamic( ogWeaponModel ) ogWeapon.DisableHibernation() ogWeapon.SetParent( og, "PROPGUN" ) entity andersonWeapon = CreatePropDynamic( andersonWeaponModel ) andersonWeapon.DisableHibernation() andersonWeapon.SetParent( anderson, "PROPGUN" ) entity redshirtWeapon = CreatePropDynamic( gruntRifleModel ) redshirtWeapon.DisableHibernation() redshirtWeapon.SetParent( redshirt, "PROPGUN" ) array props = [ btWeapon, redshirtWeapon, andersonWeapon, ogWeapon, ogHelmet, dataKnife ] OnThreadEnd( function() : ( actors, props, animEnt ) { foreach ( weapon in props ) { if ( IsValid( weapon ) ) { weapon.ClearParent() weapon.Destroy() } } foreach ( actor in actors ) { if ( IsValid( actor ) ) actor.ClearParent() if ( IsInvincible( actor ) ) ClearInvincible( actor ) if ( !actor.IsPlayer() ) actor.Destroy() } if ( IsValid( animEnt ) ) animEnt.Destroy() } ) foreach ( guy in actors ) { if ( guy.IsPlayer() ) continue MakeInvincible( guy ) guy.SetEfficientMode( true ) Highlight_ClearFriendlyHighlight( guy ) } string anim_og = "pt_intro_scene_OG" string anim_og_helmet = "helmet_intro_scene_OG" string anim_bt = "BT_intro_scene_OG" string anim_anderson = "pt_intro_scene_Anderson" string anim_redshirt = "pt_intro_scene_grunt" string anim_og_idle = "pt_intro_scene_OG_idle" string anim_og_helmet_idle = "helmet_intro_scene_OG_idle" string anim_bt_idle = "BT_intro_scene_OG_idle" string anim_anderson_idle = "pt_intro_scene_Anderson_idle" string anim_redshirt_idle = "pt_intro_scene_grunt_idle" thread PlayAnimTeleport( og, anim_og_idle, animEnt ) thread PlayAnimTeleport( ogHelmet, anim_og_helmet_idle, animEnt ) thread PlayAnimTeleport( bt, anim_bt_idle, animEnt ) thread PlayAnimTeleport( anderson, anim_anderson_idle, animEnt ) thread PlayAnimTeleport( redshirt, anim_redshirt_idle, animEnt ) FlagWait( "MeetOG_StartScene" ) printt( "STARTING ANIMS: MEET OG" ) foreach ( guy in actors ) guy.Anim_Stop() AddAnimEvent( og, "helmet_on", AnimEventCallback_MeetOG_HelmetTurnsOn ) thread PlayAnimTeleport( og, anim_og, animEnt ) thread PlayAnimTeleport( ogHelmet, anim_og_helmet, animEnt ) thread PlayAnimTeleport( bt, anim_bt, animEnt ) thread PlayAnimTeleport( anderson, anim_anderson, animEnt ) thread PlayAnimTeleport( redshirt, anim_redshirt, animEnt ) player.Anim_Stop() // give player weapon for the toss moment entity fpProxy = player.GetFirstPersonProxy() int attachID = fpProxy.LookupAttachment( "PROPGUN" ) asset playerWeaponModel = GetWeaponInfoFileKeyFieldAsset_Global( "mp_weapon_vinson", "playermodel" ) entity playerWeapon = CreatePropDynamic( playerWeaponModel, fpProxy.GetAttachmentOrigin( attachID ), fpProxy.GetAttachmentAngles( attachID ) ) file.playerAnimWeapon = playerWeapon playerWeapon.DisableHibernation() playerWeapon.SetParent( fpProxy, "PROPGUN", false, 0.0 ) AddAnimEvent( player, "gun_catch", AnimEventCallback_MeetOG_PlayerCatchesWeapon ) FirstPersonSequenceStruct playerSequence_meetOG float viewAnimDelay = 4.0 //playerSequence_meetOG.setInitialTime = viewAnimDelay // DEPRECATED animators will adjust so start position is good again} playerSequence_meetOG.blendTime = 0.0 playerSequence_meetOG.teleport = false playerSequence_meetOG.attachment = "REF" playerSequence_meetOG.firstPersonAnim = "pov_intro_scene_player" playerSequence_meetOG.thirdPersonAnim = "pt_intro_scene_player" playerSequence_meetOG.viewConeFunction = TrainingPod_ViewConeLock_SemiStrict playerSequence_meetOG.renderWithViewModels = true // HACK delay sequence so it transitions better from previous anim TrainingPod_ViewConeLock_SemiStrict( player ) // set this to cover any time between viewmodel anims thread FirstPersonSequence_Delayed( viewAnimDelay, playerSequence_meetOG, player, animEnt ) //thread FirstPersonSequence( playerSequence_meetOG, player, animEnt ) // gradual DOF racking during the scene RackDoF_NearDepth( player, 0, 22, 12.0 ) RackDoF_FarDepth( player, 350, 950, 20.0 ) // find longest anim duration array sceneAnims = [ anim_og, anim_bt, anim_anderson, anim_redshirt ]//, "pt_intro_scene_player" ] float sceneDuration = -1 foreach ( anim in sceneAnims ) { entity animActor = og if ( anim.find( "BT_") != null ) animActor = bt else if ( anim.find( "_player") != null ) animActor = player float duration = animActor.GetSequenceDuration( anim ) if ( duration > sceneDuration ) sceneDuration = duration } Assert( sceneDuration > 0 ) float fadeTime = SP_LEVEL_TRANSITION_FADETIME wait sceneDuration - fadeTime FlagSet( "CadillacMoment_MeetOG_StartFadeOut" ) wait fadeTime FlagSet( "CadillacMoment_MeetOG_Done" ) } void function FirstPersonSequence_Delayed( float delay, FirstPersonSequenceStruct sequence, entity player, entity animEnt ) { EndSignal( player, "OnDestroy" ) if ( delay > 0 ) wait delay thread FirstPersonSequence( sequence, player, animEnt ) } void function MeetOG_LoudspeakerVO( entity player ) { EndSignal( player, "OnDestroy" ) FlagWait( "SimPodShutdown_LoudspeakerVO_Done" ) array aliases // "Incoming hostile ship - Designation: IMS Malta. Battle stations." aliases.append( "outro_08" ) // "Caution - Fuel leak in Cargo Bay eighty - five. Activating airlock procedures." aliases.append( "outro_12" ) // "Titan bays two through five, drop clearance confirmed." aliases.append( "outro_03" ) // "All available medical personnel, report to Med Central for tasking." aliases.append( "outro_05" ) // "We need all Riflemen to docking bay four. Dropships standing by." aliases.append( "outro_07" ) // "Infantry teams Stork three, Elk four, Shark six, Badger one, prepare for emergency atmospheric drop sequence" aliases.append( "outro_04" ) // "Titan mech team - Rabbit six, prep the Vanguards." aliases.append( "outro_06" ) // "Infantry teams 2nd Militia Fusiliers, Raptor three, target the IMS Malta." aliases.append( "outro_11" ) // "Special Recon Squad deploy from Drop Bay thirty-seven - now." aliases.append( "outro_10" ) thread LoopLoudspeakerVO( aliases ) } void function AnimEventCallback_MeetOG_PlayerCatchesWeapon( entity player ) { Assert( IsValid( file.playerAnimWeapon ) ) file.playerAnimWeapon.RenderWithViewModels( true ) thread MeetOG_PlayerCatchesWeapon_RackNearDOF( player ) } void function MeetOG_PlayerCatchesWeapon_RackNearDOF( entity player ) { EndSignal( player, "OnDestroy" ) wait 0.7 RackDOF_NearDepth_ToDefault( player, 1.5 ) wait 2.0 RackDoF_NearDepth( player, 0, 22, 1.25 ) } // ------ OG HELMET TURNING ON ------ void function AnimEventCallback_MeetOG_HelmetTurnsOn( entity og ) { thread MeetOG_HelmetTurnsOn( og ) } // script MeetOG_HelmetTurnsOn( GetOGPilot() ) void function MeetOG_HelmetTurnsOn( entity og ) { entity og = file.ogPilot entity ogHelmet = file.ogHelmet EndSignal( og, "OnDestroy" ) EndSignal( ogHelmet, "OnDestroy" ) // setting his team to friendly makes the helmet light up if ( og.GetTeam() == TEAM_MILITIA ) SetTeam( og, TEAM_SPECTATOR ) ogHelmet.Hide() Training_OGPilot_SetHelmetOn( og, true ) wait 0.2 SetTeam( og, TEAM_MILITIA ) wait 0.1 SetTeam( og, TEAM_SPECTATOR ) wait 0.4 SetTeam( og, TEAM_MILITIA ) wait 0.1 SetTeam( og, TEAM_SPECTATOR ) wait 0.1 SetTeam( og, TEAM_MILITIA ) } #if DEV void function MeetOG_HelmetTurnsOff( entity og ) { entity og = file.ogPilot entity ogHelmet = file.ogHelmet if ( og.GetTeam() == TEAM_SPECTATOR ) SetTeam( og, TEAM_MILITIA ) ogHelmet.Show() Training_OGPilot_SetHelmetOn( og, false ) } #endif // ------ POD OUTRO BACKGROUND SKITS ------ void function MeetOG_BackgroundSkits( entity player, float delay = 0.0 ) { string endFlag = "CadillacMoment_MeetOG_Done" FlagEnd( endFlag ) if ( delay > 0 ) wait delay printt( "!!!! Pod Outro Background Skits START !!!!" ) thread PodOutro_TitanRacks( endFlag ) thread PodOutro_Background_Runners( player ) thread PodOutro_Foreground_Runners( player ) thread PodOutro_Background_ATC_Marvins() OnThreadEnd( function() : () { DeleteAllSkitGuys() } ) WaitForever() } void function PodOutro_TitanRacks( string endFlag ) { FlagEnd( endFlag ) array hangarTitanGroups OnThreadEnd( function() : ( hangarTitanGroups ) { foreach ( group in hangarTitanGroups ) HangarTitanGroup_Cleanup( group ) } ) float wait_earlyEnd = 24.0 bool cleanupAfterAnim = false // --- BAY 1 --- HangarTitanGroup bay1 entity rack_bay1 = GetEntByScriptName( "hangar_titan_rack_1" ) bay1.ref = rack_bay1 bay1.rack = rack_bay1 bay1.titan = GetEntByScriptName( "hangar_titan_1" ) bay1.titanAnim = "bt_rack_prep_titan2" bay1.rackAnim = "rack_rack_prep_rack2" bay1.marvinAnim = "mv_rack_prep_marvin2" bay1.pilotAnim = "pt_rack_prep_pilot2" bay1.pilotModel = PILOT_MODEL_BAY1 HangarTitanGroup_Init( bay1 ) hangarTitanGroups.append( bay1 ) thread HangarTitanGroup_Animate( bay1, endFlag, -1, cleanupAfterAnim ) // --- BAY 2 --- //thread PodOutro_TitanBoot( endFlag, 15.0 ) HangarTitanGroup bay2 entity rack_bay2 = GetEntByScriptName( "hangar_titan_rack_2" ) bay2.ref = rack_bay2 bay2.rack = rack_bay2 bay2.titan = GetEntByScriptName( "hangar_titan_2" ) bay2.titanAnim = "bt_rack_prep_titan1" bay2.rackAnim = "rack_rack_prep_rack1" bay2.marvinAnim = "mv_rack_prep_marvin1" bay2.pilotAnim = "pt_rack_prep_pilot1" bay2.pilotModel = PILOT_MODEL_BAY2 HangarTitanGroup_Init( bay2 ) hangarTitanGroups.append( bay2 ) waitthread HangarTitanGroup_Animate( bay2, endFlag, -1, cleanupAfterAnim ) } void function PodOutro_TitanBoot( string endFlag, float delay ) { FlagEnd( endFlag ) entity refTitan = GetEntByScriptName( "hangar_titan_2" ) refTitan.Hide() vector refOrg = refTitan.GetOrigin() vector refAng = refTitan.GetAngles() refOrg += < -20, 40, 0> // HACK refOrg = OriginToGround( refOrg ) entity animref = CreateScriptRef( refOrg, refAng ) entity titan = CreatePropScript( BUDDY_MODEL, refOrg, refAng ) titan.DisableHibernation() AddAnimEvent( titan, "hatch_closed", TitanBoot_HatchClosed ) entity cockpitLightFX = PlayFXOnEntity( FX_COCKPIT_LIGHT, titan, "HIJACK" ) SkitGuyInfo titanInfo = AddSkitGuy_Manually( "bootup_titan", titan ) SkitGuyInfo titanPilotInfo = SpawnSkitGuy( "bootup_pilot", "", animref.GetOrigin(), animref.GetAngles(), TEAM_MILITIA, "npc_soldier" ) entity titanPilot = titanPilotInfo.guy titanPilot.SetParent( titan, "HIJACK", false, 0.0 ) titanPilot.MarkAsNonMovingAttachment() SkitGuyInfo crewInfo = SpawnSkitGuy( "bootup_crew", "", animref.GetOrigin(), animref.GetAngles(), TEAM_MILITIA, "npc_soldier_bish" ) entity crew = crewInfo.guy OnThreadEnd( function() : ( titanPilotInfo, crewInfo, titanInfo, cockpitLightFX, animref, refTitan ) { if ( IsValid_ThisFrame( cockpitLightFX ) ) { EntFireByHandle( cockpitLightFX, "Stop", "", 0, null, null ) cockpitLightFX.ClearParent() cockpitLightFX.Destroy() } if ( IsAlive( titanInfo.guy ) ) StopSoundOnEntity( titanInfo.guy, "Wargames_MCOR_TitanActivate" ) if ( IsValid( animref ) ) animref.Destroy() DeleteSkitGuy( titanPilotInfo ) DeleteSkitGuy( crewInfo ) DeleteSkitGuy( titanInfo ) if ( IsValid( refTitan ) ) refTitan.Show() } ) EmitSoundOnEntity( titan, "Wargames_MCOR_TitanActivate" ) string pilotAnim = "pt_titan_activation_pilot" string pilotAnim_idle = "pt_titan_activation_pilot_idle" string titanAnim = "at_titan_activation_training_meetOG" string titanAnim_idle = "at_titan_activation_idle" string titanAnim_endIdle = "at_titan_activation_training_meetOG_end_idle" string crewAnim = "pt_titan_activation_crew" string crewAnim_idle = "pt_titan_activation_crew_idle" if ( delay > 0 ) { thread PlayAnim( crew, crewAnim_idle, animref, null, 0.0 ) thread PlayAnim( titan, titanAnim_idle, animref, null, 0.0 ) titanPilot.Anim_ScriptedPlay( pilotAnim_idle ) titanPilot.Anim_EnableUseAnimatedRefAttachmentInsteadOfRootMotion() wait delay titanPilot.Anim_Stop() crew.Anim_Stop() titan.Anim_Stop() } titanPilot.Anim_ScriptedPlay( pilotAnim ) titanPilot.Anim_EnableUseAnimatedRefAttachmentInsteadOfRootMotion() thread PlayAnim( crew, crewAnim, animref, null, DEFAULT_SCRIPTED_ANIMATION_BLEND_TIME ) thread PlayAnim( titan, titanAnim, animref, null, DEFAULT_SCRIPTED_ANIMATION_BLEND_TIME ) // end it early so he doesn't make stomping sounds when the pod is closed //wait 15.0 float duration = titan.GetSequenceDuration( titanAnim ) wait duration - 0.1 printt( "Ending titan boot anim" ) thread PlayAnim( titan, titanAnim_endIdle, animref, null, DEFAULT_SCRIPTED_ANIMATION_BLEND_TIME ) crew.Freeze() WaitForever() } void function TitanBoot_HatchClosed( entity titan ) { SkitGuyInfo info = GetSkitGuyInfo_ByName( "bootup_pilot" ) DeleteSkitGuy( info ) } void function PodOutro_Background_Runners( entity player ) { EndSignal( player, "OnDestroy" ) // right to left, across the whole hangar array path1 = [] ScriptedPath_AddPoint( path1, < 11080.5, -10017.2, -6079.97 >, < 0, 177.587, 0 > ) ScriptedPath_AddPoint( path1, < 9619.14, -10004.8, -6079.97 >, < 0, 183.021, 0 > ) // right to left, starts closer to player POV array path2 = [] ScriptedPath_AddPoint( path2, < 11000, -9946.33, -6079.97 >, < 0, 177.587, 0 > ) ScriptedPath_AddPoint( path2, < 9619.14, -10004.8, -6079.97 >, < 0, 183.021, 0 > ) wait 2.0 // wait for OG's first line to be nearly over printt( "BACKGROUND RUNNER GROUP 1" ) thread SpawnSkitGuy_AndRun( "runner_1_1", path2, 0.85, TEAM_MILITIA, "npc_soldier_specialist_militia", "mp_weapon_hemlok_smg" ) wait 1.0 thread SpawnSkitGuy_AndRun( "runner_1_2", path2, 0.85, TEAM_MILITIA, "npc_soldier", "mp_weapon_rspn101" ) wait 6.5 // wait until OG hands the eye to BT printt( "BACKGROUND RUNNER GROUP 2" ) thread SpawnSkitGuy_AndRun( "runner_2_1", path2, 0.8, TEAM_MILITIA, "npc_soldier", "mp_weapon_rspn101" ) wait 1.0 thread SpawnSkitGuy_AndRun( "runner_2_2", path2, 0.8, TEAM_MILITIA, "npc_soldier", "mp_weapon_shotgun" ) wait 4.5 // wait until OG is shaking Anderson's hand printt( "BACKGROUND RUNNER GROUP 3" ) thread SpawnSkitGuy_AndRun( "runner_3_1", path2, 0.8, TEAM_MILITIA, "npc_soldier", "mp_weapon_rspn101" ) wait 1.0 thread SpawnSkitGuy_AndRun( "runner_3_2", path2, 0.8, TEAM_MILITIA, "npc_soldier", "mp_weapon_shotgun" ) wait 1.0 thread SpawnSkitGuy_AndRun( "runner_3_3", path2, 0.8, TEAM_MILITIA, "npc_soldier_specialist_militia", "mp_weapon_hemlok_smg" ) wait 1.0 thread SpawnSkitGuy_AndRun( "runner_3_4", path2, 0.8, TEAM_MILITIA, "npc_soldier", "mp_weapon_rspn101" ) wait 4.5 // wait until OG is mounting up printt( "BACKGROUND RUNNER GROUP 4" ) thread SpawnSkitGuy_AndRun( "runner_4_1", path2, 0.8, TEAM_MILITIA, "npc_soldier", "mp_weapon_rspn101" ) wait 1.0 thread SpawnSkitGuy_AndRun( "runner_4_2", path2, 0.8, TEAM_MILITIA, "npc_soldier", "mp_weapon_shotgun" ) wait 6.5 // wait until OG thumbs up printt( "BACKGROUND RUNNER GROUP 5" ) thread SpawnSkitGuy_AndRun( "runner_5_1", path2, 0.85, TEAM_MILITIA, "npc_soldier_specialist_militia", "mp_weapon_hemlok_smg" ) wait 1.2 thread SpawnSkitGuy_AndRun( "runner_5_2", path2, 0.8, TEAM_MILITIA, "npc_soldier", "mp_weapon_rspn101" ) wait 1.0 thread SpawnSkitGuy_AndRun( "runner_5_3", path2, 0.8, TEAM_MILITIA, "npc_soldier", "mp_weapon_shotgun" ) wait 1.5 thread SpawnSkitGuy_AndRun( "runner_5_4", path2, 0.85, TEAM_MILITIA, "npc_soldier", "mp_weapon_rspn101" ) wait 1.0 thread SpawnSkitGuy_AndRun( "runner_5_3", path2, 0.8, TEAM_MILITIA, "npc_soldier", "mp_weapon_shotgun" ) wait 1.5 thread SpawnSkitGuy_AndRun( "runner_5_4", path2, 0.85, TEAM_MILITIA, "npc_soldier", "mp_weapon_rspn101" ) /* wait 5.0 wait 4.0 thread SpawnSkitGuy_AndRun( "runner_2_1", path2, 0.8, TEAM_MILITIA, "npc_soldier", "mp_weapon_rspn101" ) wait 1.0 thread SpawnSkitGuy_AndRun( "runner_2_2", path2, 0.8, TEAM_MILITIA, "npc_soldier", "mp_weapon_shotgun" ) */ } void function PodOutro_Foreground_Runners( entity player ) { EndSignal( player, "OnDestroy" ) // right in front of pod array pathClose = [] ScriptedPath_AddPoint( pathClose, < 10728.2, -10194.2, -6055.97 >, < 0, 178.246, 0 > ) ScriptedPath_AddPoint( pathClose, < 10236.8, -10180.3, -6055.97 >, < 0, 178.41, 0 > ) wait 11.0 // wait for OG to hand eyeball to BT thread SpawnSkitGuy_AndRun( "close_runner_1", pathClose, 0.7, TEAM_MILITIA, "npc_soldier", "mp_weapon_rspn101" ) wait 1.2 thread SpawnSkitGuy_AndRun( "close_runner_2", pathClose, 0.75, TEAM_MILITIA, "npc_soldier", "mp_weapon_rspn101" ) wait 10.0 // wait for OG to start embarking BT thread SpawnSkitGuy_AndRun( "close_runner_4", pathClose, 0.75, TEAM_MILITIA, "npc_soldier", "mp_weapon_rspn101" ) wait 1.3 thread SpawnSkitGuy_AndRun( "close_runner_5", pathClose, 0.8, TEAM_MILITIA, "npc_soldier_specialist_militia", "mp_weapon_g2" ) wait 11 // wait for grunt to hand player the weapon thread SpawnSkitGuy_AndRun( "close_runner_10", pathClose, 0.8, TEAM_MILITIA, "npc_soldier", "mp_weapon_rspn101" ) wait 1.8 thread SpawnSkitGuy_AndRun( "close_runner_11", pathClose, 0.8, TEAM_MILITIA, "npc_soldier", "mp_weapon_rspn101" ) wait 1.0 thread SpawnSkitGuy_AndRun( "close_runner_12", pathClose, 0.8, TEAM_MILITIA, "npc_soldier", "mp_weapon_rspn101" ) } void function PodOutro_Background_ATC_Marvins() { // 1, 2, 3- matches titan bay numbering (right to left) // plain numbers = farther from camera // "a" variants = closer to camera //SkitGuyInfo marvin_1 = SpawnSkitGuy( "atc_marvin_1", "", < 10600, -9841.5, -6079.97 >, < 0, -20, 0 >, TEAM_MILITIA ) //thread ATC_Marvin_Think( marvin_1, "mv_trafic_controller_A", 0.0 ) //SkitGuyInfo marvin_1a = SpawnSkitGuy( "atc_marvin_1a", "", < 10683.8, -10077.7, -6094.65 >, < 0, 20, 0 >, TEAM_MILITIA ) //thread ATC_Marvin_Think( marvin_1a, "mv_trafic_controller_B", 0.0 ) SkitGuyInfo marvin_2 = SpawnSkitGuy( "atc_marvin_2", "", < 10460.6, -9861.5, -6079.97 >, < 0, -30, 0 >, TEAM_MILITIA ) thread ATC_Marvin_Think( marvin_2, "mv_trafic_controller_B", 0.25 ) //SkitGuyInfo marvin_2a = SpawnSkitGuy( "atc_marvin_2a", "", < 10410.6, -10075.6, -6079.97 >, < 0, 30, 0 >, TEAM_MILITIA ) //thread ATC_Marvin_Think( marvin_2a, "mv_trafic_controller_A", 0.25 ) SkitGuyInfo marvin_3 = SpawnSkitGuy( "atc_marvin_3", "", < 10214.8, -9891.5, -6079.97 >, < 0, -10, 0 >, TEAM_MILITIA ) thread ATC_Marvin_Think( marvin_3, "mv_trafic_controller_A", 0.5 ) SkitGuyInfo marvin_3a = SpawnSkitGuy( "atc_marvin_3a", "", < 10207.5, -10079.4, -6079.97 >, < 0, 10, 0 >, TEAM_MILITIA ) thread ATC_Marvin_Think( marvin_3a, "mv_trafic_controller_B", 0.5 ) } void function ATC_Marvin_Think( SkitGuyInfo marvinInfo, string anim, float skipAheadTime = 0.0 ) { entity marvin = marvinInfo.guy EndSignal( marvin, "OnDestroy" ) entity batonRight = CreatePropDynamic( SAFETY_BATON_MODEL ) batonRight.SetOrigin( marvin.GetAttachmentOrigin( marvin.LookupAttachment( "R_HAND" ) ) ) // HACK adjust the angles since the tags are different vector angs = marvin.GetAttachmentAngles( marvin.LookupAttachment( "R_HAND" ) ) + Vector( 0, 0, 180 ) batonRight.SetAngles( angs ) batonRight.SetParent( marvin, "R_HAND", true ) entity batonLeft = CreatePropDynamic( SAFETY_BATON_MODEL ) batonLeft.SetParent( marvin, "L_HAND" ) marvin.NotSolid() // don't want other NPCs to path around the ATC marvins array cleanupEnts = [ batonRight, batonLeft ] OnThreadEnd( function() : ( cleanupEnts ) { foreach ( ent in cleanupEnts ) { if ( IsValid( ent ) ) { ent.ClearParent() ent.Destroy() } } } ) WaitFrame() marvinInfo.skitAnim = anim /* array anims = [ "mv_trafic_controller_A", "mv_trafic_controller_B" ] while( 1 ) { marvin.Anim_Play( anims.getrandom() ) marvin.SetPlaybackRate( RandomFloatRange( 0.9, 1.0 ) ) waitthread WaittillAnimDone( marvin ) } */ marvin.SetPlaybackRate( RandomFloatRange( 0.9, 1.0 ) ) SkitGuy_PlayAnim( marvinInfo, skipAheadTime ) WaitForever() } // ======================================= // ============ GAUNTLET MODE ============ // ======================================= // Just runs the Gauntlet over and over. void function Training_Setup_GauntletMode( entity player ) { Training_EnvArtColorCorrection_SetEnabled( true ) entity ogStart = GetEntByScriptName( "og_near_leaderboard" ) entity og = Training_SpawnOGPilot( ogStart ) Training_OG_Idles( ogStart, "OG_Leaderboard_D_idle" ) //TeleportPlayerAndBT( "playerstart_gauntlet_challenge" ) // HACK better start position player.SetOrigin( < -5179, 279.129, 32.0313 > ) player.SetAngles( < 0, 80.7242, 0 > ) } void function Training_GauntletModeStart( entity player ) { file.gauntletMode = true waitthread Training_GauntletChallenge( player ) } void function GauntletMode_Finished( entity player ) { Assert( file.gauntletMode ) float fadeTime = 3.0 ScreenFade( player, 0, 0, 0, 255, fadeTime, -1, FFADE_OUT | FFADE_STAYOUT ) wait fadeTime // Dump player back to menu ClientCommand( player, "disconnect" ) WaitForever() // defensive- don't want any extra stuff happening right before level transition } #if DEV // =============================================== // ============ RECORD GAUNTLET GHOSTS =========== // =============================================== void function TrainingGauntlet_RecordGhostStart_FirstRun( entity player ) { TrainingGauntlet_RecordGhost_CommonStart( player, GetTrainingGauntlet(), GHOST_NAME_FIRSTRUN ) } void function TrainingGauntlet_RecordGhostStart_Challenge_WIP( entity player ) { TrainingGauntlet_RecordGhost_CommonStart( player, GetTrainingGauntlet(), GHOST_NAME_CHAL_WIP ) } void function TrainingGauntlet_RecordGhostStart_Challenge_01( entity player ) { TrainingGauntlet_RecordGhost_CommonStart( player, GetTrainingGauntlet(), GHOST_NAME_CHAL_01 ) } void function TrainingGauntlet_RecordGhostStart_Challenge_02( entity player ) { TrainingGauntlet_RecordGhost_CommonStart( player, GetTrainingGauntlet(), GHOST_NAME_CHAL_02 ) } void function TrainingGauntlet_RecordGhostStart_Challenge_03( entity player ) { TrainingGauntlet_RecordGhost_CommonStart( player, GetTrainingGauntlet(), GHOST_NAME_CHAL_03 ) } void function TrainingGauntlet_RecordGhostStart_Challenge_04( entity player ) { TrainingGauntlet_RecordGhost_CommonStart( player, GetTrainingGauntlet(), GHOST_NAME_CHAL_04 ) } void function TrainingGauntlet_RecordGhostStart_Challenge_05( entity player ) { TrainingGauntlet_RecordGhost_CommonStart( player, GetTrainingGauntlet(), GHOST_NAME_CHAL_05 ) } void function TrainingGauntlet_RecordGhostStart_Challenge_06( entity player ) { TrainingGauntlet_RecordGhost_CommonStart( player, GetTrainingGauntlet(), GHOST_NAME_CHAL_06 ) } void function TrainingGauntlet_RecordGhostStart_Challenge_07( entity player ) { TrainingGauntlet_RecordGhost_CommonStart( player, GetTrainingGauntlet(), GHOST_NAME_CHAL_07 ) } void function TrainingGauntlet_RecordGhostStart_Challenge_08( entity player ) { TrainingGauntlet_RecordGhost_CommonStart( player, GetTrainingGauntlet(), GHOST_NAME_CHAL_08 ) } void function TrainingGauntlet_RecordGhostStart_Challenge_09( entity player ) { TrainingGauntlet_RecordGhost_CommonStart( player, GetTrainingGauntlet(), GHOST_NAME_CHAL_09 ) } void function TrainingGauntlet_RecordGhost_CommonStart( entity player, GauntletInfo gauntlet, string ghostFileName ) { thread TrainingGauntlet_TeleportPlayerAtFinishLine( player ) TeleportPlayerAndBT( "gauntlet_startpoint" ) Gauntlet_Player_GhostRecordOrPlayback( player, gauntlet, ghostFileName ) while ( 1 ) wait 5 } #endif //DEV // ============================================ // ============ TRAINING POD STUFF ============ // ============================================ void function SetupTrainingPod() { file.trainingPod = GetEntByScriptName( "training_pod" ) file.trainingPod.DisableHibernation() TrainingPod_SetupInteriorDLights() array laserAttachNames = [ "fx_laser_L", "fx_laser_R" ] foreach ( attachName in laserAttachNames ) { entity emitterEnt = CreateScriptMover( file.trainingPod.GetOrigin() ) int attachID = file.trainingPod.LookupAttachment( attachName ) vector attachAng = file.trainingPod.GetAttachmentAngles( attachID ) TrainingPod_LaserEmitter emitter emitter.ent = emitterEnt emitter.attachName = attachName emitter.ogAng = attachAng file.trainingPodLaserEmitters.append( emitter ) } // HACK we do this later as well to reset the emitter positions, so it's a separate function TrainingPod_SnapLaserEmittersToAttachPoints() //file.trainingPod.SetAngles( Vector( 0, 109, 0 ) ) // these angles are a little better for seeing the room } void function TrainingPod_SetupInteriorDLights() { entity pod = file.trainingPod TrainingPod_dLightMapping m1 m1.scriptAlias = "console1" m1.fxName = FX_POD_DLIGHT_CONSOLE1 m1.attachName = "light_console1" file.trainingPodDLightMappings.append( m1 ) TrainingPod_dLightMapping m2 m2.scriptAlias = "console2" m2.fxName = FX_POD_DLIGHT_CONSOLE2 m2.attachName = "light_console2" file.trainingPodDLightMappings.append( m2 ) //TrainingPod_dLightMapping m3 //m3.scriptAlias = "backlight_side_L" //m3.fxName = FX_POD_DLIGHT_BACKLIGHT_SIDE //m3.attachName = "light_back1" //file.trainingPodDLightMappings.append( m3 ) //TrainingPod_dLightMapping m4 //m4.scriptAlias = "backlight_side_R" //m4.fxName = FX_POD_DLIGHT_BACKLIGHT_SIDE //m4.attachName = "light_back2" //file.trainingPodDLightMappings.append( m4 ) //TrainingPod_dLightMapping m5 //m5.scriptAlias = "backlight_top" //m5.fxName = FX_POD_DLIGHT_BACKLIGHT_TOP //m5.attachName = "light_backtop" //file.trainingPodDLightMappings.append( m5 ) } void function TrainingPod_TurnOnInteriorDLights_Delayed( entity player, float delay ) { player.EndSignal( "OnDestroy" ) wait delay TrainingPod_TurnOnInteriorDLight( "console1" ) TrainingPod_TurnOnInteriorDLight( "console2" ) } void function TrainingPod_TurnOnInteriorDLight( string scriptAlias ) { entity pod = file.trainingPod int idx TrainingPod_dLightMapping thisMapping foreach ( mappingIdx, mapping in file.trainingPodDLightMappings ) { if ( mapping.scriptAlias == scriptAlias ) { thisMapping = mapping idx = mappingIdx break } } Assert ( thisMapping.scriptAlias != "", "Couldn't find pod dlight mapping for alias " + scriptAlias ) entity fxHandle = PlayLoopFXOnEntity( thisMapping.fxName, pod, thisMapping.attachName ) file.trainingPodDLightMappings[ idx ].fxHandle = fxHandle } void function TrainingPod_KillInteriorDLights_Delayed( entity player, float delay ) { player.EndSignal( "OnDestroy" ) wait delay TrainingPod_KillInteriorDLights() } void function TrainingPod_KillInteriorDLights() { foreach ( idx, mapping in file.trainingPodDLightMappings ) { if ( !IsValid_ThisFrame( mapping.fxHandle ) ) continue KillFX( mapping.fxHandle ) file.trainingPodDLightMappings[ idx ].fxHandle = null } } void function TrainingPod_SnapLaserEmittersToAttachPoints() { foreach ( TrainingPod_LaserEmitter emitter in file.trainingPodLaserEmitters ) { int attachID = file.trainingPod.LookupAttachment( emitter.attachName ) vector attachOrg = file.trainingPod.GetAttachmentOrigin( attachID ) vector attachAng = file.trainingPod.GetAttachmentAngles( attachID ) emitter.ent.ClearParent() emitter.ent.SetOrigin( attachOrg ) // HACK set this to ANYTHING (even 0, 0, 0) and the position is correct, otherwise it's offset from the attachpoint when parented emitter.ent.SetParent( file.trainingPod, emitter.attachName ) } } void function TrainingPod_Interior_BootSequence( entity player ) { player.EndSignal( "OnDestroy" ) entity pod = file.trainingPod TrainingPod_InteriorFX_CommonSetup( pod ) FlagSet( "PodIntro_InteriorBootSequence_Starting" ) EmitSoundOnEntity( player, "NPE_Scr_SimPod_PowerUp" ) // Transition screen FX thread PlayFXOnEntity_Delayed( player, FX_POD_SCREEN_IN, player, 2.35 ) // GLOW LIGHTS TrainingPod_GlowLightsTurnOn() // LASERS float longestSweepTime = -1 foreach ( emitter in file.trainingPodLaserEmitters ) { float sweepTime = RandomFloatRange( 2.9, 3.15 ) if ( sweepTime > longestSweepTime ) longestSweepTime = sweepTime thread LaserSweep( player, sweepTime, emitter, pod, "top" ) } wait longestSweepTime player.Signal( "PodInteriorSequenceDone" ) } void function TrainingPod_InteriorFX_CommonSetup( entity pod ) { if ( file.trainingPodLaserEmitters.len() ) { TrainingPod_KillLasers( pod ) TrainingPod_ResetLaserEmitterRotation( pod ) } TrainingPod_KillGlowFX( pod ) } // NOTE startPosition is actually inverted from what I think it should be. Tag orientation issue, maybe? void function LaserSweep( entity player, float totalTime, TrainingPod_LaserEmitter emitter, entity pod, string startPosition = "bottom" ) { //float startTime = Time() player.EndSignal( "OnDestroy" ) emitter.ent.EndSignal( "OnDeath" ) emitter.sweepDone = false //printt( "emitter og angles:", emitter.GetAngles() ) vector vecToPlayerEye = ( player.EyePosition() + Vector( 0, 0, 7 ) ) - emitter.ent.GetOrigin() // eye position offset is a HACK, not sure why I need to do that here. vector centerAng = VectorToAngles( vecToPlayerEye ) vector topAng = centerAng + Vector( -270, 0, 0 ) vector bottomAng = centerAng + Vector( -90, 0, 0 ) //vector topAng = emitter.GetAngles() + < 90, -8, 0 > //vector bottomAng = emitter.GetAngles() + < -90, 8, 0 > //printt( "==== starting at:", startPosition ) //printt( "topAng:", topAng ) //printt( "bottomAng:", bottomAng ) //printt( "centerAng:", centerAng ) vector lastBigSweepAng if ( startPosition == "bottom") { emitter.ent.SetAbsAngles( bottomAng ) lastBigSweepAng = bottomAng } else { emitter.ent.SetAbsAngles( topAng ) lastBigSweepAng = topAng } //printt( "setting start angles to:", lastBigSweepAng ) entity fxHandle = PlayLoopFXOnEntity( FX_POD_LASER, emitter.ent ) emitter.fxHandle = fxHandle int numBigSweeps = 2 float finalCenterTime = totalTime * 0.15 float bigSweepTime = ( totalTime - finalCenterTime ) / numBigSweeps float bigSweep_AccelTime = 0 float bigSweep_DecelTime = bigSweepTime * 0.2 // do the big sweeps vector nextBigSweepAng for ( int i = 0; i < numBigSweeps; i++ ) { nextBigSweepAng = topAng if ( lastBigSweepAng == topAng ) nextBigSweepAng = bottomAng //printt( "rotating to", nextBigSweepAng ) emitter.ent.NonPhysicsRotateTo( nextBigSweepAng, bigSweepTime, bigSweep_AccelTime, bigSweep_DecelTime ) float waitTime = bigSweepTime if ( i < numBigSweeps - 1 ) waitTime = bigSweepTime - 0.1 wait waitTime lastBigSweepAng = nextBigSweepAng } // finish with centering move //printt( "centering to", centerAng ) float finalCenter_AccelTime = 0 float finalCenter_DecelTime = finalCenterTime * 0.2 emitter.ent.NonPhysicsRotateTo( centerAng, finalCenterTime, finalCenter_AccelTime, finalCenter_DecelTime ) wait finalCenterTime emitter.sweepDone = true //printt( "laser sweep done, total time", Time() - startTime, "should have been", totalTime ) } void function TrainingPod_KillLasers( entity pod, bool doEndCap = false ) { foreach ( emitter in file.trainingPodLaserEmitters ) { if ( IsValid_ThisFrame( emitter.fxHandle ) ) { if ( !doEndCap ) { //printt( "killing laser FX", emitter.fxHandle ) KillFX( emitter.fxHandle ) } else { //printt( "killing laser FX with endcap", emitter.fxHandle ) KillFXWithEndcap( emitter.fxHandle ) } } emitter.fxHandle = null } } void function TrainingPod_ResetLaserEmitterRotation( entity pod ) { if ( !file.trainingPodLaserEmitters.len() ) return foreach ( emitter in file.trainingPodLaserEmitters ) { //reset to start position emitter.ent.NonPhysicsRotateTo( emitter.ogAng, 0.05, 0.0, 0.0 ) } } void function TrainingPod_GlowLightsArraySetup() { array rows // rows are set up bottom to top // lights are set up outside to in (in = door close seam; opposite for each side) // process two rows per loop (one for each door side) TrainingPod_GlowLightRow row1 row1.fxSpotsL = [ "fx_glow_L_door012", "fx_glow_L_door013" ] row1.fxSpotsR = [ "fx_glow_R_door014", "fx_glow_R_door013" ] rows.append( row1 ) TrainingPod_GlowLightRow row2 row2.fxSpotsL = [ "fx_glow_L_door014", "fx_glow_L_door011" ] row2.fxSpotsR = [ "fx_glow_R_door012", "fx_glow_R_door011" ] rows.append( row2 ) TrainingPod_GlowLightRow row3 row3.fxSpotsL = [ "fx_glow_L_door09", "fx_glow_L_door010" ] row3.fxSpotsR = [ "fx_glow_R_door09", "fx_glow_R_door010" ] rows.append( row3 ) TrainingPod_GlowLightRow row4 row4.fxSpotsL = [ "fx_glow_L_door07", "fx_glow_L_door08" ] row4.fxSpotsR = [ "fx_glow_R_door07", "fx_glow_R_door08" ] rows.append( row4 ) TrainingPod_GlowLightRow row5 row5.fxSpotsL = [ "fx_glow_L_door05", "fx_glow_L_door06" ] row5.fxSpotsR = [ "fx_glow_R_door05", "fx_glow_R_door06" ] rows.append( row5 ) TrainingPod_GlowLightRow row6 row6.fxSpotsL = [ "fx_glow_L_door03", "fx_glow_L_door04" ] row6.fxSpotsR = [ "fx_glow_R_door03", "fx_glow_R_door04" ] rows.append( row6 ) TrainingPod_GlowLightRow row7 row7.fxSpotsL = [ "fx_glow_L_door01", "fx_glow_L_door02" ] row7.fxSpotsR = [ "fx_glow_R_door01", "fx_glow_R_door02" ] rows.append( row7 ) file.trainingPodGlowLightRows = rows } void function TrainingPod_GlowLightsTurnOn( bool instantOn = false ) { //float startTime = Time() entity pod = file.trainingPod foreach ( TrainingPod_GlowLightRow row in file.trainingPodGlowLightRows ) { float loopTime = Time() array group1 = [ row.fxSpotsL[0], row.fxSpotsR[0] ] array group2 = [ row.fxSpotsL[1], row.fxSpotsR[1] ] table< int, array < string > > lightgroups lightgroups[0] <- group1 lightgroups[1] <- group2 foreach ( idx, group in lightgroups ) { foreach ( attachName in group ) { entity fxHandle = PlayLoopFXOnEntity( FX_POD_GLOWLIGHT, pod, attachName ) file.trainingPodGlowLightFXHandles.append( fxHandle ) } if ( !instantOn ) wait 0.1 } /* // both sides have same number of lights int numLights = 2 for ( int i = 0; i < numLights; i++ ) { foreach ( var side in row ) { string attachName = side[ i ] entity fxHandle = PlayLoopFXOnEntity( FX_POD_GLOWLIGHT, pod, attachName ) file.trainingPodGlowLightFXHandles.append( fxHandle ) } if ( lightWait > 0 ) wait lightWait } if ( rowWait > 0) wait rowWait */ } //printt( "glow lights turn on took", Time() - startTime, "secs" ) } void function TrainingPod_KillGlowFX( entity pod ) { foreach ( fxHandle in file.trainingPodGlowLightFXHandles ) { if ( !IsValid_ThisFrame( fxHandle ) ) continue KillFX( fxHandle ) } file.trainingPodGlowLightFXHandles = [] } void function TrainingPod_Interior_ShutdownSequence( entity player, float shutdownTime ) { player.EndSignal( "OnDestroy" ) entity pod = file.trainingPod TrainingPod_InteriorFX_CommonSetup( pod ) // TURN ON GLOW LIGHTS TrainingPod_GlowLightsTurnOn( true ) // TURN ON LASERS TrainingPod_LasersInstantOn( player, pod ) player.WaitSignal( "TrainingPod_BeginInteriorShutdown" ) thread TrainingPod_LasersShutDown( player, pod, shutdownTime * 0.6 ) thread TrainingPod_GlowLightsShutDown( player, pod, shutdownTime ) wait shutdownTime printt( "interior shutdown done" ) } void function TrainingPod_LasersInstantOn( entity player, entity pod ) { foreach ( emitter in file.trainingPodLaserEmitters ) { float dist = Distance( player.EyePosition(), emitter.ent.GetOrigin() ) Assert( dist <= 30, "player is usually about 20 units away when we try to set the laser angles. If very far away, the math will crash the game. Dist: " + dist ) vector vecToPlayerEye = ( player.EyePosition() + Vector( 0, 0, 7 ) ) - emitter.ent.GetOrigin() // eye position offset is a HACK, not sure why I need to do that here. vector centerAng = VectorToAngles( vecToPlayerEye ) emitter.ent.NonPhysicsRotateTo( centerAng, 0.1, 0.0, 0.0 ) // SETANGLES DOES NOT WORK! You have to rotate it for the FX to follow. emitter.fxHandle = PlayLoopFXOnEntity( FX_POD_LASER, emitter.ent ) } } void function TrainingPod_LasersShutDown( entity player, entity pod, float shutdownTime ) { player.EndSignal( "OnDestroy" ) foreach ( emitter in file.trainingPodLaserEmitters ) { vector vecToPlayerEye = ( player.EyePosition() + Vector( 0, 0, 7 ) ) - emitter.ent.GetOrigin() // eye position offset is a HACK, not sure why I need to do that here. vector centerAng = VectorToAngles( vecToPlayerEye ) emitter.ent.NonPhysicsRotateTo( centerAng, 0.1, 0.0, 0.0 ) // SETANGLES DOES NOT WORK! You have to rotate it for the FX to follow. } wait shutdownTime * 0.25 float moveDownTime = shutdownTime * 0.75 float accelTime = moveDownTime * 0.25 float decelTime = moveDownTime * 0.1 foreach ( TrainingPod_LaserEmitter emitter in file.trainingPodLaserEmitters ) { vector finalAng = emitter.ent.GetAngles() + Vector( 30, 0, 0 ) // not sure why adding pitch makes them appear to drop down emitter.ent.NonPhysicsRotateTo( finalAng, moveDownTime, accelTime, decelTime ) } wait moveDownTime TrainingPod_KillLasers( pod, true ) } void function TrainingPod_GlowLightsShutDown( entity player, entity pod, float shutdownTime ) { player.EndSignal( "OnDestroy" ) float shutdownDelay = shutdownTime * 0.65 float finishEarly = shutdownTime * 0.1 float glowLightShutDownDuration = shutdownTime - shutdownDelay - finishEarly wait shutdownDelay Assert( glowLightShutDownDuration > 0.0 ) float timePerLight = glowLightShutDownDuration / file.trainingPodGlowLightFXHandles.len().tofloat() foreach ( entity fxHandle in file.trainingPodGlowLightFXHandles ) { if ( !IsValid_ThisFrame( fxHandle ) ) continue thread KillFXWithEndcap( fxHandle ) wait timePerLight } file.trainingPodGlowLightFXHandles = [] } void function TrainingPod_ViewConeLock_Shared( entity player ) { player.PlayerCone_FromAnim() player.PlayerCone_SetMinYaw( -25 ) player.PlayerCone_SetMaxYaw( 25 ) player.PlayerCone_SetMinPitch( -30 ) } void function TrainingPod_ViewConeLock_PodOpen( entity player ) { TrainingPod_ViewConeLock_Shared( player ) player.PlayerCone_SetMaxPitch( 35 ) } void function TrainingPod_ViewConeLock_PodClosed( entity player ) { TrainingPod_ViewConeLock_Shared( player ) player.PlayerCone_SetMaxPitch( 32 ) } void function TrainingPod_ViewConeLock_SemiStrict( entity player ) { player.PlayerCone_FromAnim() player.PlayerCone_SetMinYaw( -10 ) player.PlayerCone_SetMaxYaw( 10 ) player.PlayerCone_SetMinPitch( -10 ) player.PlayerCone_SetMaxPitch( 10 ) } void function TrainingPod_ViewConeLock_Strict( entity player ) { player.PlayerCone_FromAnim() player.PlayerCone_SetMinYaw( 0 ) player.PlayerCone_SetMaxYaw( 0 ) player.PlayerCone_SetMinPitch( 0 ) player.PlayerCone_SetMaxPitch( 0 ) } // ================================================== // ============ CLIENT COMMAND CALLBACKS ============ // ================================================== bool function ClientCommand_Training_SetInputType( entity player, array args ) { int inputType = args[0].tointeger() Assert( inputType == INPUT_TYPE_CONTROLLER || inputType == INPUT_TYPE_KBM ) //printt( "Training- client input type updated:", inputType ) file.playerInputType = inputType return true } bool function ClientCommand_Training_PlayerPressedUse( entity player, array args ) { FlagSet( "PlayerPressedUse" ) return true } bool function ClientCommand_Training_PlayerReloaded( entity player, array args ) { FlagSet( "PlayerReloaded" ) return true } bool function ClientCommand_LookTarget_Top( entity player, array args ) { player.ResetIdleTimer() printt( "ClientCommand_LookTarget_Top" ) FlagSet( "PlayerLookedAtTopTarget" ) return true } bool function ClientCommand_LookTarget_Bottom( entity player, array args ) { player.ResetIdleTimer() printt( "ClientCommand_LookTarget_Bottom" ) FlagSet( "PlayerLookedAtBottomTarget" ) return true } // =============================== // ============ DOORS ============ // =============================== void function DoorOpenFast( string doorEntName ) { entity door = GetEntByScriptName( doorEntName ) door.Hide() door.NotSolid() entity navBlocker = door.GetLinkEnt() navBlocker.NotSolid() ToggleNPCPathsForEntity( navBlocker, true ) } void function DoorCloseFast( string doorEntName ) { entity door = GetEntByScriptName( doorEntName ) door.Show() door.Solid() entity navBlocker = door.GetLinkEnt() navBlocker.Solid() ToggleNPCPathsForEntity( navBlocker, false ) } void function OpenZenGardenExitDoor() { DoorOpenFast( "zengarden_door" ) } void function CloseZenGardenExitDoor() { DoorCloseFast( "zengarden_door" ) } void function OpenGauntletDoor() { DoorOpenFast( "gauntlet_door" ) } // =================================== // ========= LOUDSPEAKER VO ========== // =================================== void function LoudspeakerVO_Setup() { vector loudspeakerPos = <10524, -9660, -5896> // HACK file.loudspeaker = CreateScriptMover( loudspeakerPos, <0,0,0> ) // ======= PA ANNOUNCEMENTS: POD INTRO ======= // "Inbound to Planet Typhon. Subspace rendezvous in approximately 20 minutes." RegisterLoudspeakerVO( "intro_0", "diag_sp_addtional_TR411_01_mcor_shipPA", 8.0 ) // "Major Anderson, please report to the briefing room." RegisterLoudspeakerVO( "intro_1", "diag_sp_addtional_TR411_02_mcor_grunt1", 3.0 ) // "Reminder to dock personnel: Titan ordnance is a Type 3 Hazardous Material." RegisterLoudspeakerVO( "intro_2", "diag_sp_addtional_TR411_03_mcor_shipPA", 6.0 ) // "Captain Cole to communications." RegisterLoudspeakerVO( "intro_3", "diag_sp_addtional_TR411_04_mcor_grunt1", 3.0 ) // "Running Lifeboat diagnostic test two point one. All Mark Eight lifeboats are in the green." RegisterLoudspeakerVO( "intro_4", "diag_sp_addtional_TR411_05_mcor_shipPA", 6.0 ) // "3rd Militia Grenadiers - prep dropship MacAllan 17." RegisterLoudspeakerVO( "intro_5", "diag_sp_addtional_TR411_06_mcor_grunt1", 4.0 ) // ======= PA ANNOUNCEMENTS: MEET OG ======= // "Prepare for Typhon atmospheric entry in less than three minutes." RegisterLoudspeakerVO( "outro_01", "diag_sp_addtional_TR411_07_mcor_shipPA", 7.0 ) // "Powering down all non-essential systems." RegisterLoudspeakerVO( "outro_01_1", "diag_sp_outro_TR171_01_01_mcor_shipPA", 4.0 ) // "All personnel to battle stations." RegisterLoudspeakerVO( "outro_01_2", "diag_sp_outro_TR171_02_01_mcor_shipPA", 3.0 ) // "This is not a drill." RegisterLoudspeakerVO( "outro_01_3", "diag_sp_outro_TR171_03_01_mcor_shipPA", 3.0 ) // "Titan bays two through five, drop clearance confirmed." RegisterLoudspeakerVO( "outro_03", "diag_sp_addtional_TR411_09_mcor_shipPA", 5.0 ) // "Infantry teams Stork three, Elk four, Shark six, Badger one, prepare for emergency atmospheric drop sequence" RegisterLoudspeakerVO( "outro_04", "diag_sp_addtional_TR411_10_mcor_grunt1", 7.0 ) // "All available medical personnel, report to Med Central for tasking." RegisterLoudspeakerVO( "outro_05", "diag_sp_addtional_TR411_11_mcor_grunt2", 5.0 ) // "Titan mech team - Rabbit six, prep the Vanguards." RegisterLoudspeakerVO( "outro_06", "diag_sp_addtional_TR411_12_mcor_grunt3", 4.0 ) // "We need all Riflemen to docking bay four. Dropships standing by." RegisterLoudspeakerVO( "outro_07", "diag_sp_addtional_TR411_13_mcor_grunt1", 5.0 ) // "Incoming hostile ship - Designation: IMS Malta. Battle stations." RegisterLoudspeakerVO( "outro_08", "diag_sp_addtional_TR411_14_mcor_shipPA", 6.0 ) // "Special Recon Squad deploy from Drop Bay thirty-seven - now." RegisterLoudspeakerVO( "outro_10", "diag_sp_addtional_TR411_16_mcor_grunt2", 5.0 ) // "Infantry teams 2nd Militia Fusiliers, Raptor three, target the IMS Malta." RegisterLoudspeakerVO( "outro_11", "diag_sp_addtional_TR411_17_mcor_grunt3", 6.0 ) // "Caution - Fuel leak in Cargo Bay eighty - five. Decompressing for fire suppression." RegisterLoudspeakerVO( "outro_12", "diag_sp_addtional_TR411_18_mcor_shipPA", 6.0 ) } void function RegisterLoudspeakerVO( string scriptAlias, string soundAlias, float duration = 6.0 ) { Assert( !( scriptAlias in file.loudspeakerVO ), "duplicate scriptAlias " + scriptAlias ) LoudspeakerVO_Info voInfo voInfo.scriptAlias = scriptAlias voInfo.soundAlias = soundAlias voInfo.duration = duration file.loudspeakerVO[ scriptAlias ] <- voInfo } void function PlayLoudspeakerVO( string scriptAlias, float delay = 0.0 ) { Assert( scriptAlias in file.loudspeakerVO ) LoudspeakerVO_Info voInfo = file.loudspeakerVO[ scriptAlias ] string soundAlias = voInfo.soundAlias if ( delay > 0 ) wait delay entity emitter = file.loudspeaker emitter.Signal( "LoudspeakerVO_Stop" ) emitter.EndSignal( "LoudspeakerVO_Stop" ) OnThreadEnd( function() : ( emitter, soundAlias ) { if ( IsValid( emitter ) ) FadeOutSoundOnEntity( emitter, soundAlias, 2.0 ) } ) //printt( "playing loudspeaker VO", scriptAlias, "/", soundAlias ) EmitSoundOnEntity( emitter, soundAlias ) wait voInfo.duration } void function LoopLoudspeakerVO( array scriptAliases, string endFlag = "", float minPause = -1, float maxPause = -1 ) { if ( endFlag != "" ) { if ( Flag( endFlag ) ) return FlagEnd( endFlag ) } while ( 1 ) { foreach ( scriptAlias in scriptAliases ) { waitthread PlayLoudspeakerVO( scriptAlias ) if ( minPause > 0 && maxPause >= minPause ) wait RandomFloatRange( minPause, maxPause ) } } } // ============================== // ============ MISC ============ // ============================== entity function Training_SpawnAnOG( entity startSpot ) { entity og = CreateSoldier( TEAM_MILITIA, startSpot.GetOrigin(), startSpot.GetAngles() ) og.kv.spawnflags = SF_NPC_ALLOW_SPAWN_SOLID DispatchSpawn( og ) TakeAllWeapons( og ) og.SetModel( OG_PILOT_MODEL ) og.SetTitle( "#TRAINING_OG_PILOT_NAME" ) ShowName( og ) Training_OGPilot_SetHelmetOn( og, true ) og.DisableHibernation() og.SetNoTarget( true ) og.UseSequenceBounds( true ) MakeInvincible( og ) og.kv.scriptedAnimForceInterrupt = true og.DisableNPCFlag( NPC_ALLOW_FLEE | NPC_ALLOW_HAND_SIGNALS ) og.SetHologram() return og } void function Training_OGPilot_SetHelmetOn( entity og, bool setOn ) { int headIdx = og.FindBodyGroup( "head" ) int submodelIdx = OG_PILOT_MODEL_HEAD_IDX_BARE if ( setOn ) submodelIdx = OG_PILOT_MODEL_HEAD_IDX_HELMET og.SetBodygroup( headIdx, submodelIdx ) /* int decalIdx = og.FindBodyGroup( "decal" ) if ( decalIdx == -1 ) return submodelIdx = OG_PILOT_MODEL_DECAL_IDX_BARE if ( setOn ) submodelIdx = OG_PILOT_MODEL_DECAL_IDX og.SetBodygroup( decalIdx, submodelIdx ) */ } entity function Training_SpawnOGPilot( entity startSpot ) { if ( IsValid( file.ogPilot ) ) file.ogPilot.Destroy() entity og = Training_SpawnAnOG( startSpot ) file.ogPilot = og return og } entity function Training_SpawnOGTwin( entity startSpot ) { if ( IsValid( file.ogTwin ) ) file.ogTwin.Destroy() entity ogTwin = Training_SpawnAnOG( startSpot ) file.ogTwin = ogTwin return ogTwin } entity function GetOGPilot() { Assert( IsValid( file.ogPilot ) ) return file.ogPilot } entity function GetOGTwin() { if ( !IsValid( file.ogTwin ) ) return null return file.ogTwin } void function Training_OG_NagPlayerUntilFlag_Sitting( entity player, array nagAliases, float nagInterval, entity idleRef, string endFlag, string talkAnim = "", string idleAnim = "" ) { entity og = GetOGPilot() Training_NPC_NagPlayerUntilFlag_Sitting( og, player, nagAliases, nagInterval, idleRef, endFlag, talkAnim, idleAnim ) } void function Training_OG_NagPlayerUntilFlag( entity player, array nagAliases, float nagInterval, entity idleRef, string endFlag, string talkAnim = "", string idleAnim = "" ) { entity og = GetOGPilot() Training_NPC_NagPlayerUntilFlag( og, player, nagAliases, nagInterval, idleRef, endFlag, talkAnim, idleAnim ) } void function Training_NPC_NagPlayerUntilFlag_Sitting( entity npc, entity player, array nagAliases, float nagInterval, entity idleRef, string endFlag, string talkAnim = "", string idleAnim = "" ) { if ( talkAnim == "" ) talkAnim = ANIM_OG_SITTING_TALK if ( idleAnim == "" ) idleAnim = ANIM_OG_SITTING_IDLE Training_OG_NagPlayerUntilFlag( player, nagAliases, nagInterval, idleRef, endFlag, talkAnim, idleAnim ) } void function Training_NPC_NagPlayerUntilFlag( entity npc, entity player, array nagAliases, float nagInterval, entity idleRef, string endFlag, string talkAnim = "", string idleAnim = "" ) { player.EndSignal( "OnDestroy" ) if ( talkAnim == "" ) talkAnim = ANIM_OG_STANDING_TALK if ( idleAnim == "" ) idleAnim = ANIM_OG_STANDING_IDLE int nagIdx = 0 float nextNagTime = Time() + nagInterval while ( !Flag( endFlag ) ) { if ( Time() > nextNagTime ) { waitthread Training_OG_Talks( nagAliases[nagIdx], idleRef, talkAnim, idleAnim, true ) nextNagTime = Time() + nagInterval nagIdx++ if ( nagIdx >= nagAliases.len() ) nagIdx = 0 } wait 0.1 } } void function Training_OG_Talks_Sitting( string voScriptAlias, entity idleRef, string talkAnim = "", string idleAnim = "", bool useBlend = false ) { entity og = GetOGPilot() Training_NPC_Talks_Sitting( og, voScriptAlias, idleRef, talkAnim, idleAnim, useBlend ) } void function Training_OG_Talks_Leaning( string voScriptAlias, entity idleRef, string talkAnim = "", string idleAnim = "", bool useBlend = false ) { entity og = GetOGPilot() Training_NPC_Talks_Leaning( og, voScriptAlias, idleRef, talkAnim, idleAnim, useBlend ) } void function Training_OG_Talks( string voScriptAlias, entity idleRef, string talkAnim = "", string idleAnim = "", bool useBlend = false ) { entity og = GetOGPilot() Training_NPC_Talks( og, voScriptAlias, idleRef, talkAnim, idleAnim, useBlend ) } void function Training_OG_Idles_Sitting( entity idleRef, string anim = "", bool useBlend = false ) { entity og = GetOGPilot() Training_NPC_Idles_Sitting( og, idleRef, anim, useBlend ) } void function Training_OG_Idles_SittingAndTalking( entity idleRef, string anim = "", bool useBlend = false ) { entity og = GetOGPilot() Training_NPC_Idles_SittingAndTalking( og, idleRef, anim, useBlend ) } void function Training_OG_Idles_Talking( entity idleRef, string anim = "", bool useBlend = false ) { entity og = GetOGPilot() Training_NPC_Idles_Talking( og, idleRef, anim, useBlend ) } void function Training_OG_Idles( entity idleRef, string anim = "", bool useBlend = false ) { entity og = GetOGPilot() thread Training_NPC_Idles( og, idleRef, anim, useBlend ) } void function Training_OG_ScriptedAnim( entity idleRef, string anim, bool useBlend = false ) { entity og = GetOGPilot() Training_NPC_ScriptedAnim( og, idleRef, anim, useBlend ) } void function Training_NPC_Talks_Sitting( entity npc, string voScriptAlias, entity idleRef, string talkAnim = "", string idleAnim = "", bool useBlend = false ) { if ( talkAnim == "" ) talkAnim = ANIM_OG_SITTING_TALK if ( idleAnim == "" ) idleAnim = ANIM_OG_SITTING_IDLE Training_NPC_Talks( npc, voScriptAlias, idleRef, talkAnim, idleAnim, useBlend ) } void function Training_NPC_Talks_Leaning( entity npc, string voScriptAlias, entity idleRef, string talkAnim = "", string idleAnim = "", bool useBlend = false ) { if ( talkAnim == "" ) talkAnim = ANIM_OG_LEANING_TALK if ( idleAnim == "" ) idleAnim = ANIM_OG_LEANING_IDLE Training_NPC_Talks( npc, voScriptAlias, idleRef, talkAnim, idleAnim, useBlend ) } void function Training_NPC_Talks( entity npc, string voScriptAlias, entity idleRef, string talkAnim = "", string idleAnim = "", bool useBlend = false ) { npc.EndSignal( "OnDestroy" ) npc.EndSignal( "NPC_NewCommand" ) if ( talkAnim == "" ) talkAnim = ANIM_OG_STANDING_TALK if ( idleAnim == "" ) idleAnim = ANIM_OG_STANDING_IDLE npc.Anim_Stop() if ( useBlend ) thread PlayAnim( npc, talkAnim, idleRef ) else thread PlayAnim( npc, talkAnim, idleRef, null, 0.0 ) OnThreadEnd( function() : ( npc, idleRef, idleAnim, useBlend ) { if ( IsValid( npc ) ) Training_NPC_Idles( npc, idleRef, idleAnim, useBlend ) } ) waitthread PlayDialogue( voScriptAlias, npc ) } void function Training_NPC_Idles_Sitting( entity npc, entity idleRef, string anim = "", bool useBlend = false ) { if ( anim == "" ) anim = ANIM_OG_SITTING_IDLE Training_NPC_Idles( npc, idleRef, anim, useBlend ) } void function Training_NPC_Idles_SittingAndTalking( entity npc, entity idleRef, string anim = "", bool useBlend = false ) { if ( anim == "" ) anim = ANIM_OG_SITTING_TALK Training_NPC_Idles( npc, idleRef, anim, useBlend ) } void function Training_NPC_Idles_Talking( entity npc, entity idleRef, string anim = "", bool useBlend = false ) { if ( anim == "" ) anim = ANIM_OG_STANDING_TALK thread Training_NPC_Idles( npc, idleRef, anim, useBlend ) } void function Training_NPC_Idles( entity npc, entity idleRef, string anim = "", bool useBlend = false ) { npc.Signal( "NPC_NewCommand" ) if ( anim == "" ) anim = ANIM_OG_STANDING_IDLE npc.Anim_Stop() if ( useBlend ) thread PlayAnim( npc, anim, idleRef ) else thread PlayAnim( npc, anim, idleRef, null, 0.0 ) } void function Training_NPC_ScriptedAnim( entity npc, entity idleRef, string anim, bool useBlend = false ) { npc.Signal( "NPC_NewCommand" ) npc.Anim_Stop() if ( useBlend ) PlayAnim( npc, anim, idleRef ) else PlayAnim( npc, anim, idleRef, null, 0.0 ) } void function Training_OG_Moves_ToSitting( entity moveToRef, string destAnim = "", float moveTimeOverride = -1 ) { if ( destAnim == "" ) destAnim = ANIM_OG_SITTING_IDLE Training_OG_Moves( moveToRef, destAnim, moveTimeOverride, true ) } void function Training_OG_Moves( entity moveToRef, string destAnim = "", float moveTimeOverride = -1, bool destAnim_isSitting = false ) { entity og = GetOGPilot() og.Signal( "NPC_NewCommand" ) int ogAttachIdx = og.LookupAttachment( "CHESTFOCUS" ) vector startOrigin = og.GetAttachmentOrigin( ogAttachIdx ) const vector standingOffset = <0, 0, 42> const vector sittingOffset = <0, 0, 20> vector destHeightOffset = destAnim_isSitting ? sittingOffset : standingOffset vector endOrigin = moveToRef.GetOrigin() + destHeightOffset og.Freeze() file.ogPilot = null if ( IsValid( og ) ) { og.NotSolid() DissolveGhost( og ) } entity newOG = Training_SpawnOGPilot( moveToRef ) newOG.Hide() entity mover = CreateScriptMover( startOrigin, <0,0,0> ) int moverAttachIdx = mover.LookupAttachment( "REF" ) EmitSoundOnEntity( mover, "og_dissolve_trail" ) file.ogPathMover = mover newOG.EndSignal( "OnDestroy" ) mover.EndSignal( "OnDestroy" ) OnThreadEnd( function() : ( mover, newOG, moveToRef, destAnim_isSitting, destAnim, ogAttachIdx ) { if ( IsValid( mover ) ) { StopSoundOnEntity( mover, "og_dissolve_trail" ) mover.Destroy() file.ogPathMover = null } if ( IsValid( newOG ) ) { StartParticleEffectOnEntity( newOG, GetParticleSystemIndex( GHOST_FLASH_EFFECT ), FX_PATTACH_POINT, ogAttachIdx ) newOG.Show() if ( destAnim_isSitting ) Training_OG_Idles_Sitting( moveToRef, destAnim ) else Training_OG_Idles( moveToRef, destAnim ) } } ) StartParticleEffectOnEntity( mover, GetParticleSystemIndex( GHOST_TRAIL_EFFECT ), FX_PATTACH_POINT_FOLLOW, moverAttachIdx ) StartParticleEffectOnEntity( mover, GetParticleSystemIndex( GHOST_FLASH_EFFECT ), FX_PATTACH_POINT, moverAttachIdx ) wait 0.5 float moveSpeed = 1350.0 float moveDist = Distance( startOrigin, endOrigin ) float moveTime = moveDist / moveSpeed if ( moveTimeOverride > 0 ) moveTime = moveTimeOverride float accel = moveTime * 0.1 float decel = moveTime * 0.1 mover.NonPhysicsMoveTo( endOrigin, moveTime, accel, decel ) wait moveTime - 0.1 EmitSoundAtPosition( TEAM_UNASSIGNED, endOrigin, "PathHologram_Materialized_training" ) StartParticleEffectOnEntity( mover, GetParticleSystemIndex( GHOST_FLASH_EFFECT ), FX_PATTACH_POINT, moverAttachIdx ) wait 0.1 } entity function TeleportOG( string entName ) { Assert( IsValid( file.ogPilot ) ) file.ogPilot.Signal( "NPC_NewCommand" ) file.ogPilot.Anim_Stop() entity teleportSpot = GetEntByScriptName( entName ) vector org = teleportSpot.GetOrigin() vector ang = teleportSpot.GetAngles() file.ogPilot.SetOrigin( org ) file.ogPilot.SetAngles( ang ) return teleportSpot } void function NPC_DisableArrivals( entity npc ) { printt( "disabling arrivals" ) npc.EnableNPCMoveFlag( NPCMF_DISABLE_ARRIVALS ) } void function NPC_EnableArrivals( entity npc ) { printt( "enabling arrivals" ) npc.DisableNPCMoveFlag( NPCMF_DISABLE_ARRIVALS ) } void function PlayerAndOGTeleport_Fancy( entity player, vector destPos, string ogTeleportSpotName, vector destAng = < -1, -1, -1 > ) { EndSignal( player, "OnDeath" ) thread FancyTeleport_EffectsAndSound( player, destPos ) player.WaitSignal( "FancyTeleportStart" ) entity ogTeleportSpot = TeleportOG( ogTeleportSpotName ) Training_OG_Idles_Sitting( ogTeleportSpot ) MakeInvincible( player ) WaitEndFrame() // player will take damage from random hazard triggers otherwise player.SetOrigin( destPos ) if ( destAng != < -1, -1, -1 > ) player.SetAngles( destAng ) ClearInvincible( player ) } void function FancyTeleport_EffectsAndSound( entity player, vector teleportPos ) { EndSignal( player, "OnDeath" ) float statusEffect_severity = 2.5 float statusEffect_totalDuration = 0.8 float statusEffect_easeOutTime = 0.1 StatusEffect_AddTimed( player, eStatusEffect.timeshift_visual_effect, statusEffect_severity, statusEffect_totalDuration, statusEffect_easeOutTime ) wait 0.1 Remote_CallFunction_Replay( player, "ScriptCallback_PodTransition_PlayerScreenFX" ) EmitSoundOnEntity( player, "Timeshift_Scr_DeviceShift2Present" ) wait 0.25 // let screen FX fade screen player.Signal( "FancyTeleportStart" ) //wait holdTime - 0.1 wait 0.5 // let white screen fade EmitSoundOnEntity( player, "training_scr_zen_player_fall" ) wait 0.2 // let screen clear before pulsing entity pulseFXHandle = PlayFX( FX_FANCY_TELEPORT_ENV_PULSE, teleportPos, <0,0,0> ) EffectSetControlPointVector( pulseFXHandle, 1, <2.5,50,0> ) thread KillFX_Delayed( pulseFXHandle, 0.5 ) } entity function WaitForPlayerActiveWeapon( entity player ) { player.EndSignal( "OnDestroy" ) entity weapon = null while ( !weapon ) { WaitFrame() weapon = player.GetActiveWeapon() } return weapon } void function GhostRecorder_RepeatUntilFlag( entity player, string endFlag, entity animRef, asset recordedAnim, float extraRepeatDelay = 0.0, bool silentDissolve = false ) { EndSignal( player, "OnDestroy" ) EndSignal( level, "StopRepeatingGhostRecorder" ) if ( Flag( endFlag ) ) return FlagEnd( endFlag ) string dissolveSFX = "object_dissolve_training" if ( silentDissolve ) dissolveSFX = "" table t = {} t[0] <- null OnThreadEnd( function() : ( t, dissolveSFX ) { if ( !t.len() ) return entity ghost = t[0] if ( IsValid( ghost ) ) { StopSoundOnEntity( ghost, "PathHologram_Sustain_Loop_3P" ) DissolveGhost( ghost, dissolveSFX ) } } ) var rec = LoadRecordedAnimation( recordedAnim ) float duration = GetRecordedAnimationDuration( rec ) const float ghostFadeTime = 1.2 while ( 1 ) { entity ghost = CreateGhost( animRef.GetOrigin() ) t[0] = ghost EmitSoundOnEntity( ghost, "PathHologram_Sustain_Loop_3P" ) ghost.PlayRecordedAnimation( rec, <0,0,0>, <0,0,0>, DEFAULT_SCRIPTED_ANIMATION_BLEND_TIME, animRef ) wait duration - ghostFadeTime DissolveGhost( ghost, dissolveSFX ) wait ghostFadeTime wait extraRepeatDelay } } void function Training_TeleportEffect( entity player ) { player.EndSignal( "OnDestroy" ) player.MovementDisable() OnThreadEnd( function() : ( player ) { if ( IsValid( player ) ) player.MovementEnable() } ) float fadeTime = 0.3 float holdTime = 0.5 ScreenFade( player, 255, 255, 255, 254, fadeTime, holdTime, FFADE_IN | FFADE_PURGE ) EmitSoundOnEntity( player, "NPE_VisualImpair" ) wait fadeTime wait holdTime FadeOutSoundOnEntity( player, "NPE_VisualImpair", fadeTime ) } void function TakeAmmoFromPlayerASAP( entity player ) { player.EndSignal( "OnDestroy" ) entity weapon = WaitForPlayerActiveWeapon( player ) array weapons = player.GetMainWeapons() foreach ( weapon in weapons ) { weapon.SetWeaponPrimaryAmmoCount( 0 ) weapon.SetWeaponPrimaryClipCount( 0 ) } // take offhand weapons player may have collected array offhands = player.GetOffhandWeapons() foreach ( index, weapon in clone offhands ) player.TakeOffhandWeapon( index ) } void function Training_WeaponPickups_Init( entity player ) { Assert( Flag( "EntitiesDidLoad" ) ) LeveledScriptedWeapons leveledScriptedWeapons = GetAllLeveledScriptWeapons() foreach ( ent in leveledScriptedWeapons.infoTargets ) thread Training_RecreateWeaponPickup_Think( ent, player ) } void function Training_SetWeaponPickupsEmptyAmmo() { file.weaponPickupsHaveAmmo = false LeveledScriptedWeapons leveledScriptedWeapons = GetAllLeveledScriptWeapons() foreach ( ent in leveledScriptedWeapons.infoTargets ) { // fix for player picking up a weapon right before calling this- attachedEnt is empty because it hasn't been recreated yet // - actual fix is to thread, wait for attachedEnts to get the recreated weapon again, and timeout, but going with less risk for now if ( !ent.e.attachedEnts.len() ) continue entity weaponEnt = ent.e.attachedEnts[0] if ( !IsValid( weaponEnt ) ) continue weaponEnt.SetWeaponPrimaryAmmoCount( 0 ) weaponEnt.SetWeaponPrimaryClipCount( 0 ) } } void function Training_SetWeaponPickupsFullAmmo() { file.weaponPickupsHaveAmmo = true LeveledScriptedWeapons leveledScriptedWeapons = GetAllLeveledScriptWeapons() foreach ( ent in leveledScriptedWeapons.infoTargets ) { // fix for player picking up a weapon right before calling this- attachedEnt is empty because it hasn't been recreated yet // - actual fix is to thread, wait for attachedEnts to get the recreated weapon again, and timeout, but going with less risk for now if ( !ent.e.attachedEnts.len() ) continue entity weaponEnt = ent.e.attachedEnts[0] if( !IsValid( weaponEnt ) ) continue string weaponClass = weaponEnt.GetWeaponClassName() int defaultTotal = GetWeaponInfoFileKeyField_GlobalInt( weaponClass, "ammo_default_total" ) int defaultMag = GetWeaponInfoFileKeyField_GlobalInt( weaponClass, "ammo_clip_size" ) weaponEnt.SetWeaponPrimaryAmmoCount( defaultTotal ) } } void function Training_RecreateWeaponPickup_Think( entity ent, entity player ) { EndSignal( player, "OnDestroy" ) EndSignal( ent, "OnDestroy" ) const float MATCHING_PICKUP_DIST = 0.5 const float NEARBY_SIMILAR_DIST = 200.0 string pickupEntWeaponClass = ent.GetValueForKey( "script_weapon" ) while ( ent.e.attachedEnts.len() && IsValid( ent.e.attachedEnts[0] ) ) { entity weaponEnt = ent.e.attachedEnts[0] while ( IsValid( weaponEnt ) && !weaponEnt.GetOwner() ) wait 0.1 // this is the most reliable way to get a good push vector for if we need to kick another weapon out (pickup ent angles are often not optimal) vector playerPos_onPickup = player.GetOrigin() vector vecToPlayer_whenPickedUp = Normalize( playerPos_onPickup - ent.GetOrigin() ) ent.e.attachedEnts.remove( 0 ) wait 2.2 // don't respawn it right away bool oldWeapon_similarPickupNearby = false bool pickupEnt_similarPickupNearby = false // previous player weapon may be here after swapping array allPickups = GetWeaponArray( true ) entity oldWeapon foreach ( pickup in allPickups ) { float distToThisPickup = Distance( pickup.GetOrigin(), ent.GetOrigin() ) if ( distToThisPickup <= MATCHING_PICKUP_DIST ) { oldWeapon = pickup break } } if ( IsValid( oldWeapon ) ) { foreach ( pickup in allPickups ) { if ( oldWeapon == pickup ) continue float distToThisPickup = Distance( pickup.GetOrigin(), ent.GetOrigin() ) if ( distToThisPickup <= NEARBY_SIMILAR_DIST ) { string pickupWeaponClass = pickup.GetWeaponClassName() if ( pickupWeaponClass == oldWeapon.GetWeaponClassName() && !oldWeapon_similarPickupNearby ) { //printt( "found similar pickup nearby to one the player dropped:", pickupWeaponClass ) oldWeapon_similarPickupNearby = true } if ( pickupWeaponClass == pickupEntWeaponClass && !pickupEnt_similarPickupNearby ) { //printt( "found similar pickup nearby to one that would be recreated:", pickupWeaponClass ) pickupEnt_similarPickupNearby = true } } } } bool recreatePickup = true bool destroyOldWeapon = false if ( IsValid( oldWeapon ) ) { if ( oldWeapon.GetWeaponClassName() == pickupEntWeaponClass ) { printt( "old weapon that is here is the same kind of weapon as we would spawn, so don't recreate:", pickupEntWeaponClass ) recreatePickup = false // old weapon that is here is the same kind of weapon as we would spawn, so don't recreate } if ( oldWeapon_similarPickupNearby ) { printt( "Old weapon can be destroyed, because a similar pickup is nearby:", oldWeapon.GetWeaponClassName() ) destroyOldWeapon = true } if ( !oldWeapon_similarPickupNearby && pickupEnt_similarPickupNearby ) { printt( "old weapon is unique to this area and pickup ent has a similar pickup nearby, so don't recreate pickup ent. Old weapon:", oldWeapon.GetWeaponClassName(), "/ pickup ent class:", pickupEntWeaponClass ) recreatePickup = false } } if ( recreatePickup ) { if ( IsValid( oldWeapon ) ) { if ( destroyOldWeapon ) { printt( "destroying old weapon because similar pickup is nearby:", oldWeapon.GetWeaponClassName() ) oldWeapon.Destroy() } else { MoveOldWeapon( ent, oldWeapon, vecToPlayer_whenPickedUp ) // kick the old weapon out of this spot } } // cover respawn with a flash effect EmitSoundAtPosition( TEAM_UNASSIGNED, ent.GetOrigin(), "training_scr_rack_weapon_appear" ) StartParticleEffectInWorld( GetParticleSystemIndex( GHOST_FLASH_EFFECT ), ent.GetOrigin(), ent.GetAngles() ) CreateScriptWeapon( ent ) // defensive checks if ( !ent.e.attachedEnts.len() || !IsValid( ent.e.attachedEnts[0] ) ) { printt( "WARNING! Recreated script pickup FAILED to recreate:", pickupEntWeaponClass, "on ent", ent ) continue } entity recreatedPickup = ent.e.attachedEnts[0] printt( "training: recreated weapon pickup:", pickupEntWeaponClass, "by spawning:", recreatedPickup ) if ( !file.weaponPickupsHaveAmmo ) { recreatedPickup.SetWeaponPrimaryAmmoCount( 0 ) recreatedPickup.SetWeaponPrimaryClipCount( 0 ) } } else { if ( IsValid( oldWeapon ) ) ent.e.attachedEnts.append( oldWeapon ) } } printt( "WARNING- Stopping think on pickupEntWeapon:", pickupEntWeaponClass ) } void function MoveOldWeapon( entity pickupEnt, entity oldWeapon, vector pushVec = <0,0,0> ) { // recreate weapon as unconstrained so we can physics push it entity recreatedOldWeapon = Training_RecreatePlayerWeaponPickup( oldWeapon ) string recreatedClassName = recreatedOldWeapon.GetWeaponClassName() float velocityScalar = 300.0 var hasSubClass = GetWeaponInfoFileKeyField_Global( recreatedClassName, "weaponSubClass" ) if ( hasSubClass ) { string weaponSubClass = GetWeaponInfoFileKeyField_GlobalString( recreatedClassName, "weaponSubClass" ) switch ( weaponSubClass ) { case "offhand": case "pistol": velocityScalar = 200 break case "smg": velocityScalar = 300 break case "rifle": velocityScalar = 400 break case "lmg": case "at": velocityScalar = 500 break } } if ( pushVec == <0,0,0> ) pushVec = AnglesToForward( pickupEnt.GetAngles() ) //vector pushAng = VectorToAngles( pushVec ) //vector addVec = AnglesToUp( pushAng ) * (velocityScalar * 0.2) //pushVec += addVec pushVec += <0,0,1> printt( "moving old weapon:", oldWeapon.GetWeaponClassName(), "with velocity scalar:", velocityScalar ) recreatedOldWeapon.SetVelocity( pushVec * velocityScalar ) } entity function Training_RecreatePlayerWeaponPickup( entity oldWeapon ) { if ( file.scriptCreatedWeaponPickups.len() >= MAX_RECREATED_OLD_WEAPONS ) { entity cleanupWeapon = file.scriptCreatedWeaponPickups[0] if ( IsValid( cleanupWeapon ) ) cleanupWeapon.Destroy() file.scriptCreatedWeaponPickups.remove( 0 ) } string oldWeaponClass = oldWeapon.GetWeaponClassName() entity weapon = CreateWeaponEntityByNameWithPhysics( oldWeaponClass, oldWeapon.GetOrigin(), oldWeapon.GetAngles() ) weapon.SetVelocity( <0,0,0> ) SetTargetName( weapon, "_old_player_weapon_" + oldWeaponClass ) weapon.kv.fadedist = -1 array existingMods = oldWeapon.GetMods() weapon.SetMods( existingMods ) bool doMarkAsLoadoutPickup = false if ( doMarkAsLoadoutPickup ) weapon.MarkAsLoadoutPickup() HighlightWeapon( weapon ) oldWeapon.Destroy() file.scriptCreatedWeaponPickups.append( weapon ) return weapon } void function Training_WeaponRacks_SetSolidity( bool doSolid ) { array racks = GetEntArrayByScriptName( "ineedguns_racks" ) foreach ( rack in racks ) { if ( doSolid ) rack.Solid() else rack.NotSolid() } } bool function GetAutosprintEnabled() { int autosprintSetting = GetConVarInt( AUTOSPRINT_CONVAR_NAME ) bool autoSprintEnabled = autosprintSetting > 0 && autosprintSetting < 3 // 0 = none, 3 = titans only return autoSprintEnabled } void function EmitSoundOnEntity_Delayed( entity ent, string alias, float delay ) { ent.EndSignal( "OnDestroy" ) if ( delay > 0 ) wait delay EmitSoundOnEntity( ent, alias ) } void function PlayFXOnEntity_Delayed( entity player, asset fxAsset, entity ent, float delay ) { player.EndSignal( "OnDestroy" ) ent.EndSignal( "OnDeath" ) wait delay PlayFXOnEntity( fxAsset, ent ) } void function KillFX_Delayed( entity fxHandle, float delay ) { fxHandle.EndSignal( "OnDestroy" ) if ( delay > 0.0 ) wait delay KillFX( fxHandle ) } void function KillFX( entity fxHandle ) { if ( !IsValid_ThisFrame( fxHandle ) ) return fxHandle.SetStopType( "DestroyImmediately" ) fxHandle.ClearParent() fxHandle.Destroy() } void function KillFXWithEndcap( entity fxHandle, float killDelay = 1.0 ) { if ( !IsValid_ThisFrame( fxHandle ) ) return EffectStop( fxHandle ) wait killDelay if ( !IsValid_ThisFrame( fxHandle ) ) return fxHandle.ClearParent() fxHandle.Destroy() } void function FlagSetDelayed( string setFlag, float delay ) { thread FlagSetDelayed_Think( setFlag, delay ) } void function FlagSetDelayed_Think( string setFlag, float delay ) { EndSignal( level, "OnDestroy" ) if ( delay > 0 ) wait delay FlagSet( setFlag ) } void function Training_PlayerQuickdeathSFX( entity player ) { EndSignal( player, "OnDestroy" ) while ( 1 ) { WaitSignal( player, "QuickDeath" ) EmitSoundOnEntity( player, "training_scr_zen_player_fall" ) } } void function Training_EnvArtColorCorrection_SetEnabled( bool isEnabled ) { Assert( IsValid( file.envArt_colorCorrectionEnt ), "Called too early?" ) string setEnabledStr = "Disable" if ( isEnabled) setEnabledStr = "Enable" EntFireByHandle( file.envArt_colorCorrectionEnt, setEnabledStr, "", 0, null, null ) } void function SetDoF_Hangar( entity player ) { Remote_CallFunction_Replay( player, "ScriptCallback_DoF_SetNearDepth", 0, 18 ) Remote_CallFunction_Replay( player, "ScriptCallback_DoF_SetFarDepth", 450, 1250 ) } void function SetDoF_Default( entity player ) { Remote_CallFunction_Replay( player, "ScriptCallback_DoF_SetNearDepthToDefault" ) Remote_CallFunction_Replay( player, "ScriptCallback_DoF_SetFarDepthToDefault" ) } void function RackDoF_NearDepth( entity player, float nearDepthStart, float nearDepthEnd, float rackTime ) { Remote_CallFunction_Replay( player, "ScriptCallback_DoF_SetNearDepth", nearDepthStart, nearDepthEnd, rackTime ) } void function RackDOF_NearDepth_ToDefault( entity player, float duration ) { Remote_CallFunction_Replay( player, "ScriptCallback_DoF_SetNearDepthToDefault", duration ) } void function RackDoF_FarDepth( entity player, float farDepthStart, float farDepthEnd, float rackTime ) { Remote_CallFunction_Replay( player, "ScriptCallback_DoF_SetFarDepth", farDepthStart, farDepthEnd, rackTime ) } void function SimpleScreenShake( entity player, float duration, float amplitude, float blurMaxIntensity = 0.75 ) { Remote_CallFunction_Replay( player, "ScriptCallback_SimpleScreenShake", duration, amplitude, blurMaxIntensity ) } void function SetWeaponHUDEnabled( entity player, bool setEnabled ) { file.displayWeaponHUD = setEnabled Remote_CallFunction_Replay( player, "ScriptCallback_SetWeaponHUDEnabled", setEnabled ) } // --------------------- // ----- SKIT GUYS ----- // --------------------- SkitGuyInfo function AddSkitGuy_Manually( string name, entity guy ) { if ( SkitGuyExists( name ) ) DeleteSkitGuy( GetSkitGuyInfo_ByName( name ) ) SkitGuyInfo info info.id = file.skitguys.len() info.guy = guy info.name = name file.skitguys.append( info ) return info } SkitGuyInfo function SpawnSkitGuy( string name, string anim, vector origin, vector angles, int team = TEAM_IMC, string aiSettings = "", string weapon = "mp_weapon_semipistol", bool isRunner = false ) { if ( SkitGuyExists( name ) ) DeleteSkitGuy( GetSkitGuyInfo_ByName( name ) ) string guyType = "grunt" if ( name.find( "marvin" ) != null ) guyType = "marvin" // spawn the guy entity guy if ( guyType == "marvin" ) { Assert( !isRunner, "Marvins don't run!" ) guy = CreateEntity( "npc_marvin" ) DispatchSpawn( guy ) SetTeam( guy, TEAM_SPECTATOR ) guy.SetNPCMoveSpeedScale( 0.6 ) //TakeAllJobs( guy ) } else { guy = CreateSoldier( team, <0,0,0>, <0,0,0> ) // spawn the guy at worldspawn to avoid "npc spawned in solid" red text` SetSpawnOption_Weapon( guy, weapon ) if ( aiSettings != "" ) SetSpawnOption_AISettings( guy, aiSettings ) if ( isRunner ) guy.kv.alwaysAlert = 1 DispatchSpawn( guy ) } guy.SetTitle( "" ) entity ref = CreateOwnedScriptMover( guy ) ref.SetOrigin( origin ) ref.SetAngles( angles ) guy.SetOrigin( ref.GetOrigin() ) guy.SetAngles( ref.GetAngles() ) MakeInvincible( guy ) guy.SetEfficientMode( true ) guy.EnableNPCFlag( NPC_IGNORE_ALL | NPC_DISABLE_SENSING ) guy.DisableNPCFlag( NPC_ALLOW_PATROL | NPC_ALLOW_INVESTIGATE | NPC_ALLOW_FLEE | NPC_ALLOW_HAND_SIGNALS ) SkitGuyInfo info info.id = file.skitguys.len() info.guy = guy info.skitRef = ref info.skitAnim = anim info.name = name file.skitguys.append( info ) return info } void function SpawnSkitGuy_AndRun( string name, array path, float moveSpeedScale, int team, string aiSettings = "", string weaponName = "" ) { SkitGuyInfo runnerInfo runnerInfo = SpawnSkitGuy( name, "", path[0].origin, path[0].angles, team, aiSettings, weaponName, true ) entity runner = runnerInfo.guy EndSignal( runner, "OnDestroy" ) OnThreadEnd( function() : ( runnerInfo ) { DeleteSkitGuy( runnerInfo ) } ) waitthread ScriptedPath_Run( runnerInfo, path, moveSpeedScale ) } void function SpawnSkitGuy_AndRunForever( string name, array path, float moveSpeedScale, int team, string aiSettings = "", string weaponName = "" ) { SkitGuyInfo runnerInfo while ( 1 ) { runnerInfo = SpawnSkitGuy( name, "", path[0].origin, path[0].angles, team, aiSettings, weaponName ) waitthread ScriptedPath_Run( runnerInfo, path, moveSpeedScale ) } } void function SkitGuy_PlayAnim( SkitGuyInfo info, float skipAheadTime = 0 ) { Assert( info.skitAnim != "" ) Assert( IsValid( info.skitRef ) ) entity guy = info.guy thread PlayAnim( guy, info.skitAnim, info.skitRef, null, 0.0, skipAheadTime ) } bool function SkitGuyExists( string name ) { foreach ( info in file.skitguys ) { if ( info.name == name ) return true } return false } SkitGuyInfo function GetSkitGuyInfo_ByName( string guyName ) { SkitGuyInfo thisInfo foreach ( info in file.skitguys ) { if ( info.name == guyName ) { thisInfo = info return thisInfo } } Assert( false, "couldn't find skit guy info by name: " + guyName ) unreachable } void function DeleteAllSkitGuys() { array deleteNames = [] foreach ( skitInfo in file.skitguys ) deleteNames.append( skitInfo.name ) foreach ( name in deleteNames ) { if ( !SkitGuyExists( name ) ) continue SkitGuyInfo deleteInfo = GetSkitGuyInfo_ByName( name ) DeleteSkitGuy( deleteInfo ) } } void function DeleteSkitGuy( SkitGuyInfo info ) { KillSkitGuy( info ) int removeIdx = -1 foreach ( idx, guyInfo in file.skitguys ) { if ( guyInfo.id == info.id ) { removeIdx = idx break } } if ( removeIdx == -1 ) { printt( "WARNING: SkitGuy was already deleted!" ) return } file.skitguys.remove( removeIdx ) } void function KillSkitGuy( SkitGuyInfo info ) { entity guy = info.guy entity skitRef = info.skitRef if ( IsValid( skitRef ) ) skitRef.Destroy() info.skitRef = null if ( IsAlive( guy ) ) { guy.Anim_Stop() ClearInvincible( guy ) } if ( IsValid( guy ) ) guy.Destroy() info.guy = null } #if DEV string function NudgeSkitGuy( string name, float offsetX, float offsetY = 0.0, float offsetZ = 0.0 ) { if ( !SkitGuyExists( name ) ) { return "WARNING: SKIT GUY NAME NOT RECOGNIZED: " + name } SkitGuyInfo info = GetSkitGuyInfo_ByName( name ) entity guy = info.guy entity skitRef = info.skitRef string name = info.name vector offset = if ( IsValid( skitRef ) ) skitRef.SetOrigin( skitRef.GetOrigin() + offset ) else guy.SetOrigin( guy.GetOrigin() + offset ) if ( info.skitAnim != "" ) SkitGuy_PlayAnim( info ) printt( "NUDGED:") return PrintSkitGuy( info ) } string function PrintSkitGuy( SkitGuyInfo info ) { entity guy = info.guy entity skitRef = info.skitRef string name = info.name string returnStr = name + " origin/angles: " + CreateOriginAnglesString( guy.GetOrigin(), guy.GetAngles() ) if ( IsValid( skitRef ) ) returnStr = name + " ref origin/angles: " + CreateOriginAnglesString( skitRef.GetOrigin(), skitRef.GetAngles() ) return returnStr } #endif //DEV // ------------------------------ // ----- SCRIPTED NPC PATHS ----- // ------------------------------ void function ScriptedPath_AddPoint( array pathpoints, vector origin, vector angles ) { Point pathpoint pathpoint.origin = origin pathpoint.angles = angles pathpoints.append( pathpoint ) } void function ScriptedPath_Walk( SkitGuyInfo info, array path, float moveSpeedScale = 0.8, string idleAnim = "" ) { NPC_ScriptedPath( SCRIPTED_PATH_WALK, info, path, moveSpeedScale, idleAnim ) } void function ScriptedPath_Run( SkitGuyInfo info, array path, float moveSpeedScale = 1.0, string idleAnim = "" ) { NPC_ScriptedPath( SCRIPTED_PATH_RUN, info, path, moveSpeedScale, idleAnim ) } void function NPC_ScriptedPath( int pathFollowType, SkitGuyInfo info, array path, float moveSpeedScale = 1.0, string idleAnim = "" ) { entity guy = info.guy guy.EndSignal( "OnDestroy" ) guy.Anim_Stop() guy.EnableNPCMoveFlag( NPCMF_DISABLE_MOVE_TRANSITIONS ) guy.EnableNPCMoveFlag( NPCMF_DISABLE_ARRIVALS ) guy.SetNPCMoveSpeedScale( moveSpeedScale ) if ( pathFollowType == SCRIPTED_PATH_RUN ) guy.SetAlert() // change his alert state so he will run if ( pathFollowType == SCRIPTED_PATH_WALK ) guy.SetMoveAnim( "patrol_walk_bored" ) string waitSignal = "OnEnterGoalRadius" //"OnFinishedAssault" float pathfindingFailTimeout = 20.0 guy.SetOrigin( path[0].origin ) guy.SetAngles( path[0].angles ) for ( int i = 1; i < path.len(); i++ ) { Point pathpoint = path[i] float goalradius = 64.0 // MINIMUM //guy.DisableArrivalOnce( true ) // always want arrivals disabled because they are blended from run anim, not walking guy.AssaultPoint( pathpoint.origin ) guy.AssaultSetGoalRadius( goalradius ) WaitSignalTimeout( guy, pathfindingFailTimeout, waitSignal ) if ( Distance( guy.GetOrigin(), pathpoint.origin ) >= goalradius ) { printt( guy, " scripted pathfinding stopped, quitting." ) break } } if ( idleAnim != "" ) { guy.DisableBehavior( "Assault" ) while ( !guy.IsInterruptable() ) wait 0.1 entity ref = CreateOwnedScriptMover( guy ) thread PlayAnim( guy, idleAnim, ref, null, 0.4 ) WaitForever() } else { DeleteSkitGuy( info ) } } // ----------------------------- // ----- TITAN GROUP SKITS ----- // ----------------------------- void function HangarTitanGroup_Init( HangarTitanGroup group ) { group.rack_ogPos = group.rack.GetOrigin() group.rack_ogAng = group.rack.GetAngles() if ( IsValid( group.titan ) ) { if ( group.titanSkin == -1 ) group.titanSkin = 1 } if ( group.marvinAnim != "" ) { group.marvin = CreatePropDynamic( MARVIN_MODEL ) group.marvin.DisableHibernation() } if ( group.pilotAnim != "" ) { group.pilot = CreatePropDynamic( group.pilotModel ) group.pilot.DisableHibernation() } HangarTitanGroup_SetMaxSequenceDuration( group ) group.isInited = true } void function HangarTitanGroup_SetMaxSequenceDuration( HangarTitanGroup group ) { table sceneActors = {} sceneActors[ group.titanAnim ] <- group.titan sceneActors[ group.rackAnim ] <- group.rack sceneActors[ group.marvinAnim ] <- group.marvin if ( IsValid( group.titan ) ) { // set titan to use non posed model group.titan.SetModel( BUDDY_MODEL ) group.titan.SetSkin( group.titanSkin ) } float maxDuration = 0 foreach ( anim, actor in sceneActors ) { if ( !IsValid( actor ) ) continue float animDuration = actor.GetSequenceDuration( anim ) if ( animDuration > maxDuration ) maxDuration = animDuration } group.sequenceDuration = maxDuration if ( IsValid( group.titan ) ) { // set titan back to posed anim group.titan.SetModel( BUDDY_MODEL_POSED_NO_ANIMS ) //group.titan.SetSkin( group.titanSkin ) } } void function HangarTitanGroup_Animate( HangarTitanGroup group, string endFlag = "", float duration = -1, bool doCleanup = true ) { if ( endFlag != "" ) FlagEnd( endFlag ) Assert( group.isInited, "Need to call HangarTitanGroup_Init on this group before using" ) entity ref = group.ref entity titan = group.titan entity rack = group.rack entity marvin = group.marvin entity pilot = group.pilot string titanAnim = group.titanAnim string rackAnim = group.rackAnim string marvinAnim = group.marvinAnim string pilotAnim = group.pilotAnim float animInitialTime = group.animInitialTime EndSignal( rack, "OnDestroy" ) if ( IsValid( marvin ) ) EndSignal( marvin, "OnDestroy") if ( IsValid( titan ) ) { EndSignal( titan, "OnDestroy" ) // set titan to use non posed model titan.SetModel( BUDDY_MODEL ) titan.SetSkin( group.titanSkin ) } OnThreadEnd( function() : ( doCleanup, group ) { if ( doCleanup ) HangarTitanGroup_Cleanup( group ) } ) if ( duration == -1 || group.sequenceDuration < duration ) duration = group.sequenceDuration thread PlayAnim( rack, rackAnim, ref, null, 0.0, animInitialTime ) if ( IsValid( titan) ) thread PlayAnim( titan, titanAnim, ref, null, 0.0, animInitialTime ) if ( IsValid( marvin ) ) thread PlayAnim( marvin, marvinAnim, ref, null, 0.0, animInitialTime ) if ( IsValid( pilot ) ) thread PlayAnim( pilot, pilotAnim, ref, null, 0.0, animInitialTime ) wait duration } void function HangarTitanGroup_Cleanup( HangarTitanGroup group ) { HangarTitanGroup_Reset( group ) if ( IsValid( group.marvin ) ) group.marvin.Destroy() if ( IsValid( group.pilot ) ) group.pilot.Destroy() } void function HangarTitanGroup_Reset( HangarTitanGroup group ) { entity titan = group.titan entity rack = group.rack if ( IsValid( titan ) ) { titan.Anim_Stop() titan.SetModel( BUDDY_MODEL_POSED_NO_ANIMS ) //titan.SetSkin( group.titanSkin ) } if ( IsValid( rack ) ) { rack.Anim_Stop() rack.SetOrigin( group.rack_ogPos ) rack.SetAngles( group.rack_ogAng ) } } #if DEV void function skyboxchange( string tName ) { entity cam = GetEnt( tName ) GetPlayerArray()[0].SetSkyCamera( cam ) } // ====================================================== // ============ GHOST RECORDER DEV FUNCTIONS ============ // ====================================================== void function wallruntest() { entity preWallrunRef = GetEntByScriptName( "basic_movement_wallrun_start_ref" ) var rec = LoadRecordedAnimation( $"anim_recording/training_record_zengarden_wallrun.rpak" ) file.ogPilot.Anim_Stop() file.ogPilot.PlayRecordedAnimation( rec, <0,0,0>, <0,0,0>, DEFAULT_SCRIPTED_ANIMATION_BLEND_TIME, preWallrunRef ) } void function Record_ZenGarden_Wallrun() { thread RecordAnimation_Think( "training_record_zengarden_wallrun", "basic_movement_wallrun_start_ref" ) } void function Record_ZenGarden_Slide() { thread RecordAnimation_Think( "training_record_zengarden_slide", "zengarden_slide_ref" ) } void function Record_ZenGarden_DoubleJump() { thread RecordAnimation_Think( "training_record_zengarden_doublejump", "zengarden_doublejump_ref" ) } void function RecordAnimation_Think( string filename, string refName ) { entity player = file.player player.Signal( "RecordAnimation_Start" ) player.EndSignal( "RecordAnimation_Start" ) TeleportPlayerAndBT( refName ) player.EndSignal( "OnDestroy" ) entity ref = GetEntByScriptName( refName ) printt( "READY TO RECORD: " + filename ) //start recording player.WaitSignal( "ButtonPressedAttack" ) printt( "RECORDING STARTED" ) player.StartRecordingAnimation( ref.GetOrigin(), ref.GetAngles() ) //stop player.WaitSignal( "ButtonPressedAttack" ) var recording = player.StopRecordingAnimation() #if PC_PROG SaveRecordedAnimation( recording, filename ) #endif printt( "STOP RECORD player org/ang:", player.GetOrigin(), player.GetAngles() ) } #endif //DEV