diff options
Diffstat (limited to 'Northstar.Custom/scripts/vscripts/titan')
-rw-r--r-- | Northstar.Custom/scripts/vscripts/titan/sh_first_person_embark.gnut | 41 | ||||
-rw-r--r-- | Northstar.Custom/scripts/vscripts/titan/sh_titan_embark.gnut | 2253 |
2 files changed, 2294 insertions, 0 deletions
diff --git a/Northstar.Custom/scripts/vscripts/titan/sh_first_person_embark.gnut b/Northstar.Custom/scripts/vscripts/titan/sh_first_person_embark.gnut new file mode 100644 index 00000000..0c95ae4c --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/titan/sh_first_person_embark.gnut @@ -0,0 +1,41 @@ +global function FirstPersonEmbark_Init + +#if CLIENT + global function ServerCallback_HideHudForFPEmbark +#endif + +void function FirstPersonEmbark_Init() +{ + // atm do this no matter what playlist we're on since playlist overrides seem to get sent to clients after networkvar registration + // not nice but whatever lol + AddCallback_OnRegisteringCustomNetworkVars( FirstPersonEmbark_RegisterCustomNetworkFunctions ) + + // busted rn lol + if ( GetCurrentPlaylistVarInt( "fp_embark_enabled", 0 ) == 0 ) + return + + #if CLIENT + AddCallback_PlayerClassChanged( ShowHudOnEmbarkFinished ) + #endif +} + +void function FirstPersonEmbark_RegisterCustomNetworkFunctions() +{ + Remote_RegisterFunction( "ServerCallback_HideHudForFPEmbark" ) +} + +#if CLIENT +void function ServerCallback_HideHudForFPEmbark() +{ + thread MainHud_TurnOff_RUI( true ) + HidePermanentCockpitRui() +} + +void function ShowHudOnEmbarkFinished( entity player ) +{ + if ( !player.IsTitan() ) + return + + ShowPermanentCockpitRui() +} +#endif
\ No newline at end of file diff --git a/Northstar.Custom/scripts/vscripts/titan/sh_titan_embark.gnut b/Northstar.Custom/scripts/vscripts/titan/sh_titan_embark.gnut new file mode 100644 index 00000000..f9df2730 --- /dev/null +++ b/Northstar.Custom/scripts/vscripts/titan/sh_titan_embark.gnut @@ -0,0 +1,2253 @@ +untyped + +global function TitanEmbark_Init + +global function TitanCanStand +global function DebugEmbarkTimes +global function TitanIsCurrentlyEmbarkableForPlayer +global function PlayerCanEmbarkTitan +global function PlayerCanImmediatelyEmbarkTitan +global function FindBestEmbark +global function GenerateEmbarkActionTable +global function FindEmbarkActionForCriteria +global function GetRandomEmbarkAction +global function PlayerCanDisembarkTitan +global function IsPlayerDisembarking + +#if SERVER + global function ForceScriptedEmbark + global function PlayerCanEmbarkIntoTitan + global function IsPlayerEmbarking + global function PlayerEmbarksTitan + global function PlayerLungesToEmbark + global function FindBestEmbarkForNpcAnim + global function PlayerDisembarksTitan + global function ForcedTitanDisembark + global function ForcedTitanDisembarkCustomAnims + global function PhaseEmbarkPhaseStart + global function PhaseEmbarkPhaseStop + global function OverrideCockpitLightFX + global function StartCockpitLightThink + global function CockpitLightStop + global function Embark_DelayedFadeOut + global function PlayerIsFarOffTheGround + + global function SetSmallDisembarkFailSafeTeleportVector //TODO: Re-examine this for next game, probably should have different values for SP versus MP + global function SetLargeDisembarkFailSafeTeleportVector //TODO: Re-examine this for next game, probably should have different values for SP versus MP +#endif + +#if DEV + global function SetEmbarkDebugPrint +#endif + +const FAST_EMBARK = 1 +const SKIP_AHEAD_TIME = 2.0 + +#if MP +const EMBARK_FADE_TIME = 0.2 +#else +const EMBARK_FADE_TIME = 0.3 +#endif + +struct +{ + asset cockpitLightFX = $"xo_cockpit_dlight" + bool embarkDebugPrint = false + vector smallDisembarkFailSafeTeleportVector = < 400, 400, 200 > + vector largeDisembarkFailSafeTeleportVector = < 600, 600, 600 > +} file + +function TitanEmbark_Init() +{ + if ( reloadingScripts ) + return + + level.pilotDisembarkBounds <- {} + local end = {} + end.up <- 50.363811 + end.forward <- 110.146927 + end.right <- 13.045869 + end.yaw <- -8.381051 + + local start = {} + start.up <- 156.750015 + start.forward <- -13.429688 + start.right <- -11.374998 + start.yaw <- 0.409042 + + RefreshTitanEmbarkActions() + + level.pilotDisembarkBounds.end <- end + level.pilotDisembarkBounds.start <- start + + RegisterSignal( "OnComplete" ) + RegisterSignal( "startembark" ) // temp + + RegisterSignal( "DisembarkingTitan" ) + RegisterSignal( "player_embarks_titan" ) + + #if SERVER + // add all the embark anims with this suffix + AddEmbarkAnims( "titan_atlas", "atlas", true ) + AddEmbarkAnims( "titan_buddy", "buddy", true ) + AddEmbarkAnims( "titan_ogre", "ogre", true ) + AddEmbarkAnims( "titan_stryder", "stryder", true ) + + // AddEmbarkAudio( "titan_atlas", "atlas" ) + // AddEmbarkAudio( "titan_buddy", "buddy" ) + // AddEmbarkAudio( "titan_ogre", "ogre" ) + // AddEmbarkAudio( "titan_stryder", "stryder" ) + + RegisterSignal( "titanKneel" ) + RegisterSignal( "titanStand" ) + RegisterSignal( "titanEmbark" ) + RegisterSignal( "PhaseEmbarkPhaseStop" ) + + RegisterSignal( "CockpitLightStop" ) + PrecacheParticleSystem( $"xo_cockpit_dlight" ) + + AddClientCommandCallback( "TitanDisembark", ClientCommand_TitanDisembark ) // + AddClientCommandCallback( "TitanKneel", ClientCommand_TitanKneel ) // + //AddClientCommandCallback( "TitanStand", ClientCommand_TitanStand ) // + AddClientCommandCallback( "TitanNextMode", ClientCommand_TitanNextMode ) // + + AddCallback_OnTitanBecomesPilot( TitanBecomesPilot_UpdateRodeoRiderHud ) + AddCallback_OnPilotBecomesTitan( PilotBecomesTitan_UpdateRodeoRiderHud ) + #endif +} + +void function OverrideCockpitLightFX( asset fx ) +{ + file.cockpitLightFX = fx +} + +void function AddEmbarkAnims( string titan, string titanSubClass, bool thirdPersonOnly = false ) +{ + // anims are string-constructed from these types: + local Array = + [ + "kneel_front", + "kneel_behind", + "kneel_right", + "kneel_left", + "kneel_airgrab", + + "stand_front", + "stand_right", + "stand_left", + "stand_behind", + "stand_airgrab", + + "above_right", + "above_left", + "kneel_above_right", + "kneel_above_left", + ] + + + // force consistency in animation names + foreach ( item in Array ) + { + array<string> suffixes = [ "", "_fast" ] + foreach ( suffix in suffixes ) + { + //printt( "Adding base " + item + " to " + titan ) + local thirdPersonAlias = "pt_mount_" + item + suffix + local firstPersonAlias = "ptpov_mount_" + item + suffix + local thirdPersonAnim = "pt_mount_" + titanSubClass + "_" + item + suffix + local firstPersonAnim = "ptpov_mount_" + titanSubClass + "_" + item + suffix + + if ( thirdPersonOnly ) + firstPersonAnim = "" + + AddAnimAlias( titanSubClass, thirdPersonAlias, thirdPersonAnim ) + AddAnimAlias( titanSubClass, firstPersonAlias, firstPersonAnim ) + } + } +} + +function AddEmbarkAudio( titan, titanSubClass ) +{ + // audio files are string-constructed from these types: + local Array = + [ + "Kneeling_Front", + "Kneeling_Behind", + "Kneeling_Right", + "Kneeling_Left", + "Kneeling_AboveRight", + "Kneeling_AboveLeft", + + "Standing_Front", + "Standing_Behind", + "Standing_Airgrab", + "Standing_AboveRight", + "Standing_AboveLeft" + ] + + + // force consistency in audio file names + foreach ( item in Array ) + { + //printt( "Adding base " + item + " to " + titan ) + local thirdPersonAlias = "Embark_" + item + "_3P" + local firstPersonAlias = "Embark_" + item + "_1P" + local thirdPersonAnim = titanSubClass + "_Embark_" + item + "_3P" + local firstPersonAnim = titanSubClass + "_Embark_" + item + "_1P" + + AddAudioAlias( titanSubClass, thirdPersonAlias, thirdPersonAnim ) + AddAudioAlias( titanSubClass, firstPersonAlias, firstPersonAnim ) + } +} + +function RefreshTitanEmbarkActions() +{ + if ( "titanEmbarkActions" in level ) + { + delete level.titanEmbarkActions + delete level.titanEmbarkFarthestDistance + } + + local groundDist = 260 + + level.titanEmbarkActions <- [] + level.titanEmbarkFarthestDistance <- 0 + local action + + action = + { + direction = Vector( 1, 0, 0 ) + distance = groundDist + embark = "front" + minDot = 0.4 + priority = 1 // tried after priority 0 actions + titanCanStandRequired = false + //onGround = null // either + useAnimatedRefAttachment = true + alignFrontEnabled = true + canSkipAhead = true + + animSet = + { + firstPersonKneelingAlias = "ptpov_mount_kneel_front" + thirdPersonKneelingAlias = "pt_mount_kneel_front" + firstPersonStandingAlias = "ptpov_mount_stand_front" + thirdPersonStandingAlias = "pt_mount_stand_front" + titanKneelingAnim = "at_mount_kneel_front" + titanStandingAnim = "at_mount_stand_front" + + } + + audioSet = + { + firstPersonKneelingAudioAlias = "Embark_Kneeling_Front_1P" + thirdPersonKneelingAudioAlias = "Embark_Kneeling_Front_3P" + firstPersonStandingAudioAlias = "Embark_Standing_Front_1P" + thirdPersonStandingAudioAlias = "Embark_Standing_Front_3P" + + } + } + level.titanEmbarkActions.append( action ) + + action = + { + direction = Vector( 1, 0, 0 ) + distance = groundDist + embark = "front" + minDot = -1 + priority = 2 // tried after priority 1 actions + titanCanStandRequired = false + //onGround = null // either + useAnimatedRefAttachment = true + alignFrontEnabled = true + canSkipAhead = true + + animSet = + { + firstPersonKneelingAlias = "ptpov_mount_kneel_front" + thirdPersonKneelingAlias = "pt_mount_kneel_front" + firstPersonStandingAlias = "ptpov_mount_stand_front" + thirdPersonStandingAlias = "pt_mount_stand_front" + titanKneelingAnim = "at_mount_kneel_front" + titanStandingAnim = "at_mount_stand_front" + } + + audioSet = + { + firstPersonKneelingAudioAlias = "Embark_Kneeling_Front_1P" + thirdPersonKneelingAudioAlias = "Embark_Kneeling_Front_3P" + firstPersonStandingAudioAlias = "Embark_Standing_Front_1P" + thirdPersonStandingAudioAlias = "Embark_Standing_Front_3P" + } + } + level.titanEmbarkActions.append( action ) + + action = + { + direction = Vector( 0, 1, 0 ) + distance = groundDist + embark = "left" + minDot = 0.4 + priority = 1 // tried after priority 0 actions + titanCanStandRequired = true + useAnimatedRefAttachment = true + alignFrontEnabled = true + canSkipAhead = true + //onGround = null // either + + animSet = + { + firstPersonKneelingAlias = "ptpov_mount_kneel_left" + thirdPersonKneelingAlias = "pt_mount_kneel_left" + firstPersonStandingAlias = "ptpov_mount_stand_front" + thirdPersonStandingAlias = "pt_mount_stand_left" + titanKneelingAnim = "at_mount_kneel_left" + titanStandingAnim = "at_mount_stand_left" + } + + audioSet = + { + firstPersonKneelingAudioAlias = "Embark_Kneeling_Left_1P" + thirdPersonKneelingAudioAlias = "Embark_Kneeling_Left_3P" + firstPersonStandingAudioAlias = "Embark_Standing_Front_1P" + thirdPersonStandingAudioAlias = "Embark_Standing_Front_3P" + } + } + level.titanEmbarkActions.append( action ) + + action = + { + direction = Vector( 0, -1, 0 ) + distance = groundDist + embark = "right" + minDot = 0.4 + priority = 1 // tried after priority 0 actions + titanCanStandRequired = true + useAnimatedRefAttachment = true + alignFrontEnabled = true + canSkipAhead = true + //onGround = null // either + animSet = + { + firstPersonKneelingAlias = "ptpov_mount_kneel_right" + thirdPersonKneelingAlias = "pt_mount_kneel_right" + firstPersonStandingAlias = "ptpov_mount_stand_front" + thirdPersonStandingAlias = "pt_mount_stand_right" + titanKneelingAnim = "at_mount_kneel_right" + titanStandingAnim = "at_mount_stand_right" + } + + audioSet = + { + firstPersonKneelingAudioAlias = "Embark_Kneeling_Right_1P" + thirdPersonKneelingAudioAlias = "Embark_Kneeling_Right_3P" + firstPersonStandingAudioAlias = "Embark_Standing_Front_1P" + thirdPersonStandingAudioAlias = "Embark_Standing_Front_3P" + } + } + level.titanEmbarkActions.append( action ) + + + action = + { + direction = Vector( -1, 0, 0 ) + distance = groundDist + embark = "behind" + minDot = 0.4 + priority = 1 // tried after priority 0 actions + titanCanStandRequired = true + useAnimatedRefAttachment = true + canSkipAhead = false + //onGround = null // either + + animSet = + { + firstPersonKneelingAlias = "ptpov_mount_kneel_behind" + thirdPersonKneelingAlias = "pt_mount_kneel_behind" + firstPersonStandingAlias = "ptpov_mount_stand_behind" + thirdPersonStandingAlias = "pt_mount_stand_behind" + titanKneelingAnim = "at_mount_kneel_behind" + titanStandingAnim = "at_mount_stand_behind" + } + + audioSet = + { + firstPersonKneelingAudioAlias = "Embark_Kneeling_Behind_1P" + thirdPersonKneelingAudioAlias = "Embark_Kneeling_Behind_3P" + firstPersonStandingAudioAlias = "Embark_Standing_Behind_1P" + thirdPersonStandingAudioAlias = "Embark_Standing_Behind_3P" + } + } + level.titanEmbarkActions.append( action ) + + action = + { + direction = Vector( 0, 0, 1 ) // 0 -1 1 + distance = 350 + embark = "above_close" + minDot = 0.88 + canSkipAhead = false + priority = 0 // priority actions are checked first + + titanCanStandRequired = true + useAnimatedRefAttachment = true + //onGround = false // must be in air + + animSets = + { + right = + { + direction = Vector( 0, -1, 0 ) // 0 -1 1 + firstPersonKneelingAlias = "ptpov_mount_kneel_above_right" + thirdPersonKneelingAlias = "pt_mount_kneel_above_right" + firstPersonStandingAlias = "ptpov_mount_above_right" + thirdPersonStandingAlias = "pt_mount_above_right" + titanKneelingAnim = "at_mount_kneel_above" + titanStandingAnim = "at_mount_above_right" + + audioSet = //An annoying exception to how the audioSet is organized, better this than the alternative and having to find the "best audio set" + { + firstPersonKneelingAudioAlias = "Embark_Kneeling_AboveRight_1P" + thirdPersonKneelingAudioAlias = "Embark_Kneeling_AboveRight_3P" + firstPersonStandingAudioAlias = "Embark_Standing_AboveRight_1P" + thirdPersonStandingAudioAlias = "Embark_Standing_AboveRight_3P" + } + } + + left = + { + direction = Vector( 0, 1, 0 ) // 0 -1 1 + firstPersonKneelingAlias = "ptpov_mount_kneel_above_left" + thirdPersonKneelingAlias = "pt_mount_kneel_above_left" + firstPersonStandingAlias = "ptpov_mount_above_left" + thirdPersonStandingAlias = "pt_mount_above_left" + titanKneelingAnim = "at_mount_kneel_above" + titanStandingAnim = "at_mount_above_left" + + audioSet = //An annoying exception to how the audioSet is organized, better this than the alternative and having to find the "best audio set" + { + firstPersonKneelingAudioAlias = "Embark_Kneeling_AboveLeft_1P" + thirdPersonKneelingAudioAlias = "Embark_Kneeling_AboveLeft_3P" + firstPersonStandingAudioAlias = "Embark_Standing_AboveLeft_1P" + thirdPersonStandingAudioAlias = "Embark_Standing_AboveLeft_3P" + } + } + } + } + level.titanEmbarkActions.append( action ) + + action = + { + direction = Vector( 0, 0, 1 ) + distance = 275 + embark = "above_grab" + minDot = 0.3 + titanCanStandRequired = true + //onGround = null // false // must be in air + useAnimatedRefAttachment = true + //lungeCheck = true + canSkipAhead = true + + alignFrontEnabled = true + + priority = 0 // priority actions are checked first + + animSet = + { + firstPersonKneelingAlias = "ptpov_mount_kneel_airgrab" + thirdPersonKneelingAlias = "pt_mount_kneel_airgrab" + titanKneelingAnim = "at_mount_kneel_airgrab" + + firstPersonStandingAlias = "ptpov_mount_stand_airgrab" + thirdPersonStandingAlias = "pt_mount_stand_airgrab" + titanStandingAnim = "at_mount_stand_airgrab" + } + + audioSet = + { + firstPersonKneelingAudioAlias = "Embark_Kneeling_Front_1P" + thirdPersonKneelingAudioAlias = "Embark_Kneeling_Front_3P" + firstPersonStandingAudioAlias = "Embark_Standing_Front_1P" + thirdPersonStandingAudioAlias = "Embark_Standing_Front_3P" + } + } + level.titanEmbarkActions.append( action ) + + local autoParms = + [ + "lungeCheck" + "alignFrontEnabled" + ] + + foreach ( action in level.titanEmbarkActions ) + { + if ( action.distance > level.titanEmbarkFarthestDistance ) + { + level.titanEmbarkFarthestDistance = action.distance + } + foreach ( parm in autoParms ) + { + if ( !( parm in action ) ) + action[ parm ] <- false + } + } +} + +function DebugEmbarkTimes() +{ + local settings = [ "atlas", "ogre", "stryder" ] + + array< asset > models = [ $"models/Humans/imc_pilot/male_cq/imc_pilot_male_cq.mdl", $"models/humans/pilot/female_cq/pilot_female_cq.mdl" ] + local times = {} + + foreach ( model in models ) + { + times[ model ] <- [] + entity prop = CreatePropDynamic( model, Vector(0,0,0), Vector(0,0,0) ) + printt( "Human model: " + model ) + + foreach ( setting in settings ) + { + printt( "Titan: " + setting ) + foreach ( action in level.titanEmbarkActions ) + { + printt( "Embark Direction: " + action.embark ) + if ( "animSet" in action ) + { + local animation = GetAnimFromAlias( setting, action.animSet.thirdPersonKneelingAlias ) + local time = prop.GetSequenceDuration( animation ) + times[ model ].append( { time = time, animation = animation } ) + printt( "Kneeling: " + time ) + + animation = GetAnimFromAlias( setting, action.animSet.thirdPersonStandingAlias ) + time = prop.GetSequenceDuration( animation ) + times[ model ].append( { time = time, animation = animation } ) + printt( "Standing: " + time ) + } + + if ( "animSets" in action ) + { + local animation = GetAnimFromAlias( setting, action.animSets.left.thirdPersonKneelingAlias ) + local time = prop.GetSequenceDuration( animation ) + times[ model ].append( { time = time, animation = animation } ) + printt( "Kneeling Left: " + time ) + + animation = GetAnimFromAlias( setting, action.animSets.left.thirdPersonStandingAlias ) + time = prop.GetSequenceDuration( animation ) + times[ model ].append( { time = time, animation = animation } ) + printt( "Standing Left: " + time ) + + animation = GetAnimFromAlias( setting, action.animSets.right.thirdPersonKneelingAlias ) + time = prop.GetSequenceDuration( animation ) + times[ model ].append( { time = time, animation = animation } ) + printt( "Kneeling Right: " + time ) + + animation = GetAnimFromAlias( setting, action.animSets.right.thirdPersonStandingAlias ) + time = prop.GetSequenceDuration( animation ) + times[ model ].append( { time = time, animation = animation } ) + printt( "Standing Right: " + time ) + } + + printt( " " ) + } + } + + prop.Kill_Deprecated_UseDestroyInstead() + } + + printt( "Time comparison: " ) + bool wrong = false + for ( int i = 0; i < times[ models[0] ].len(); i++ ) + { + if ( times[models[0]][i].time == times[models[1]][i].time ) + { + printt( "MATCH: " + ( i + 1 ) + " times: " + times[models[0]][i].time + " " + times[models[1]][i].time + " " + times[models[1]][i].animation ) + } + else + { + printt( "MISMATCH: " + ( i + 1 ) + " times: " + times[models[0]][i].time + " " + times[models[1]][i].time + " " + times[models[1]][i].animation ) + wrong = true + } + } +// Assert( !wrong, "Times did not match between male and female, see above" ) +} + + +#if SERVER +bool function ClientCommand_TitanKneel( entity player, array<string> args ) +{ + entity titan = player.GetPetTitan() + if ( !IsAlive( titan ) ) + return true + + titan.Signal( "titanKneel" ) + titan.s.standQueued = false + return true +} + +/*bool function ClientCommand_TitanStand( entity player, array<string> args ) +{ + entity titan = player.GetPetTitan() + if ( !IsAlive( titan ) ) + return true + + titan.Signal( "titanStand" ) + titan.s.standQueued = true + titan.s.kneelQueued = false + return true +}*/ + +bool function ClientCommand_TitanNextMode( entity player, array<string> args ) +{ + if ( !IsAlive( player ) ) + return true + + entity titan = player.GetPetTitan() + if ( IsAlive( titan ) ) + NPCTitanNextMode( titan, player ) + + return true +} +#endif // SERVER + + +function EmbarkLine( player, titan ) +{ + player.EndSignal( "startembark" ) + local ref = player.LookupAttachment( "ref" ) + local hijack = titan.LookupAttachment( "hijack" ) + local origin + for ( ;; ) + { + origin = titan.GetAttachmentOrigin( hijack ) + DebugDrawLine( player.GetOrigin(), origin, 255, 0, 0, true, 0.15 ) + + origin = player.GetAttachmentOrigin( ref ) + DebugDrawLine( player.GetOrigin(), origin, 0, 255, 0, true, 0.15 ) + WaitFrame() + } +} + + +#if SERVER +function PlayerEmbarksTitan( entity player, entity titan, table embark ) +{ + //player.SetOrigin( Vector(314.971405, -1826.728638, 116.031250)) + //player.SetAngles( Vector(0.000000, 133.945892, 0.000000)) + //titan.SetOrigin( Vector(284.887970, -1622.180542, 112.093750)) + //titan.SetAngles(Vector(0.000000, 112.264252, 0)) + + entity soul = titan.GetTitanSoul() + string settings = GetSoulTitanSubClass( soul ) + printt( "TitanEmbarkDebug: Player ", player.GetOrigin(), player.GetAngles(), " Titan ", titan.GetOrigin(), titan.GetAngles(), settings, GetMapName() ) + + + Assert( IsAlive( titan ) ) + Assert( IsAlive( player ) ) + + player.SetInvulnerable() + + player.EndSignal( "OnDeath" ) + player.EndSignal( "TitanEjectionStarted" ) + titan.EndSignal( "OnDeath" ) + + titan.Signal( "player_embarks_titan" ) + player.Signal( "player_embarks_titan" ) + +// Assert( !InSolid( titan ), titan + " is in solid" ) + + DisableCloak( player ) + + entity groundEntity = titan.GetGroundEntity() + vector startOrigin = titan.GetGroundRelativePos() + vector startAngles = titan.GetAngles() + + OnThreadEnd( + function() : ( player, groundEntity, startOrigin, startAngles ) + { + if ( IsValid( player ) ) + { + player.SetCinematicEventFlags( player.GetCinematicEventFlags() & (~CE_FLAG_EMBARK) ) + TitanEmbark_PlayerCleanup( player ) + player.p.isEmbarking = false + + if ( IsAlive( player ) ) + { + if ( player.IsTitan() ) //Defensive fix, sometimes in SP (when game is shutting down etc ) this can run without the player being alive + { + TitanEmbarkFailsafe( player, null, groundEntity, startOrigin ) + Remote_CallFunction_Replay( player, "ServerCallback_TitanEmbark" ) + LetTitanPlayerShootThroughBubbleShield( player ) + } + else + { + player.Die() //Defensive fix, sometimes in SP (when game is shutting down etc ) this can run without the player being alive + } + + } + } + } + ) + + // track the embarking player so we can kill him if the titan dies + titan.s.embarkingPlayer <- player + player.p.isEmbarking = true + + #if HAS_STATS + UpdatePlayerStat( player, "misc_stats", "titanEmbarks", 1 ) + #endif + #if SERVER && MP + PIN_AddToPlayerCountStat( player, "embarks" ) + PIN_PlayerAbility( player, "", "embark", {}, 0 ) + #endif + + player.SetCinematicEventFlags( player.GetCinematicEventFlags() | CE_FLAG_EMBARK ) + + waitthread PlayerEmbarksTitan_PlayerBecomesTitan( player, titan, embark ) +} + +function PlayerEmbarksTitan_PlayerBecomesTitan( entity player, entity titan, table embark ) +{ + // a place to store the player finish time + table e + + e.threads <- 0 + e.embarkAction <- embark.action + e.animSet <- embark.animSet + e.audioSet <- embark.audioSet + + e.canStand <- TitanCanStand( titan ) + + e.shouldDoRegularEmbark <- ShouldDoRegularEmbark( titan ) + + // player and titan do their anims and wait for each other to finish + thread TitanEmbark_TitanEmbarks( player, titan, e ) + waitthread TitanEmbark_PlayerEmbarks( player, titan, e ) +} + +bool function ShouldDoRegularEmbark( entity titan ) +{ + entity soul = titan.GetTitanSoul() + + if ( IsSingleplayer() && GetSoulPlayerSettings( soul ) == "titan_buddy" ) + { + return titan.GetNPCState() == "combat" || titan.GetNPCState() == "alert" + } + + return true +} + +bool function TitanHasLeftAndRightEmbarkAnims( entity titan ) +{ + entity soul = titan.GetTitanSoul() + string settings = GetSoulPlayerSettings( soul ) + var hasAnims = Dev_GetPlayerSettingByKeyField_Global( settings, "hasLeftRightEmbarks" ) + if ( hasAnims != null && hasAnims == 1 ) + { + return true + } + + return false +} + +function TitanEmbark_PlayerCleanup( player ) +{ + expect entity( player ) + + player.SetSyncedEntity( null ) + DeployViewModelAndEnableWeapons( player ) + player.UnforceStand() + player.UnforceCrouch() + //printt("Clearing invulnerable") + player.ClearInvulnerable() + player.ClearParent() + //Let player jump in air after getting out if he wants to + player.TouchGround() + player.Anim_Stop() +} + + +void function ForceScriptedEmbark( entity player, entity titan ) +{ + HolsterViewModelAndDisableWeapons( player ) + ClearPlayerAnimViewEntity( player ) + player.ClearParent() + PilotBecomesTitan( player, titan ) + + thread PlayAnim( player, "cqb_idle_mp" ) + player.Anim_Stop() + + player.SetOrigin( titan.GetOrigin() ) + vector angles = titan.GetAngles() + angles.z = 0 + angles.x = 0 + player.SetAngles( angles ) + player.SnapEyeAngles( angles ) + + SetStanceStand( player.GetTitanSoul() ) + + TitanEmbark_PlayerCleanup( player ) + if ( player.ContextAction_IsBusy() ) + player.ContextAction_ClearBusy() +} + + +function TitanEmbarkFailsafe( entity player, entity titan, entity groundEnt, vector startOrigin ) +{ + if ( GetCurrentPlaylistVarInt( "player_embark_in_solid_checks", 0 ) != 1 ) + return + + #if DEV + if ( file.embarkDebugPrint ) + { + if ( IsValid( titan ) ) + printt( "TitanEmbarkFailsafe, before PutEntityInSafeSpot: player origin: " + player.GetOrigin() + ", titan origin: " + titan.GetOrigin() + " safeStartPoint: " + startOrigin ) + else + printt( "TitanEmbarkFailsafe, before PutEntityInSafeSpot: player origin: " + player.GetOrigin() + ", null titan, safeStartPoint: " + startOrigin ) + } + #endif + + if ( !PutEntityInSafeSpot( player, titan, groundEnt, startOrigin, player.GetOrigin() ) ) + player.SetOrigin( startOrigin ) + + #if DEV + if ( file.embarkDebugPrint ) + { + if ( IsValid( titan ) ) + printt( "TitanEmbarkFailsafe, after PutEntityInSafeSpot: player origin: " + player.GetOrigin() + ", titan origin: " + titan.GetOrigin() + " safeStartPoint: " + startOrigin ) + else + printt( "TitanEmbarkFailsafe, after PutEntityInSafeSpot: player origin: " + player.GetOrigin() + ", null titan, safeStartPoint: " + startOrigin ) + } + #endif +} + +function TitanEmbark_PlayerEmbarks( entity player, entity titan, table e ) +{ + player.EndSignal( "OnDeath" ) + player.EndSignal( "TitanEjectionStarted" ) + titan.EndSignal( "OnDeath" ) + + e.threads++ + OnThreadEnd( + function() : ( player, e ) + { + if ( IsValid( player ) ) + { + // ensure these are cleared regardless + ClearPlayerAnimViewEntity( player ) + + if ( player.ContextAction_IsBusy() ) + player.ContextAction_ClearBusy() + } + + e.threads-- + if ( !e.threads ) + { + Signal( e, "OnComplete" ) + } + } + ) + + player.ContextAction_SetBusy() + + bool standing = false + + if ( e.canStand ) + { + if ( e.shouldDoRegularEmbark ) + { + player.ForceStand() + switch ( titan.GetTitanSoul().GetStance() ) + { + case STANCE_KNEELING: + case STANCE_KNEEL: + standing = false + break + + default: + standing = true + break + } + } + else + { + standing = false + } + } + else + { + player.ForceCrouch() + } + + HolsterViewModelAndDisableWeapons( player ) + + FirstPersonSequenceStruct sequence + sequence.attachment = "hijack" + sequence.useAnimatedRefAttachment = expect bool ( e.embarkAction.useAnimatedRefAttachment ) + sequence.blendTime = 0.5 + + entity soul = titan.GetTitanSoul() + string settings = GetSoulTitanSubClass( soul ) + + local hasViewCone + + // string thirdPersonAudio + // string firstPersonAudio + + if ( standing ) + { + sequence.firstPersonAnim = GetAnimFromAlias( settings, e.animSet.firstPersonStandingAlias ) + sequence.thirdPersonAnim = GetAnimFromAlias( settings, e.animSet.thirdPersonStandingAlias ) + hasViewCone = false + // thirdPersonAudio = GetAudioFromAlias( settings, e.audioSet.thirdPersonStandingAudioAlias ) + // firstPersonAudio = GetAudioFromAlias( settings, e.audioSet.firstPersonStandingAudioAlias ) + + } + else + { + sequence.firstPersonAnim = GetAnimFromAlias( settings, e.animSet.firstPersonKneelingAlias ) + sequence.thirdPersonAnim = GetAnimFromAlias( settings, e.animSet.thirdPersonKneelingAlias ) + hasViewCone = true + // thirdPersonAudio = GetAudioFromAlias( settings, e.audioSet.thirdPersonKneelingAudioAlias ) + // firstPersonAudio = GetAudioFromAlias( settings, e.audioSet.firstPersonKneelingAudioAlias ) + } + + sequence.thirdPersonAnimIdle = "pt_mount_idle" + + bool doFirstPersonAnim = true + + if ( sequence.firstPersonAnim == "" ) + {// if there is no first person anim, then there must be a third person camera + sequence.thirdPersonCameraAttachments.append( "VDU" ) + sequence.thirdPersonCameraVisibilityChecks = true + doFirstPersonAnim = false + } + + if ( hasViewCone ) + { + sequence.viewConeFunction = EmbarkViewCone + thread DelayedClearViewCone( player ) + } + + //thread DelayedDisableEmbarkPlayerHud( player, sequence ) + + AddAnimEvent( player, "phase_shift_start", PhaseEmbarkPhaseStart ) + AddAnimEvent( player, "phase_shift_stop", PhaseEmbarkPhaseStop ) + AddAnimEvent( titan, "cockpit_light_start", CockpitLightStart ) + AddAnimEvent( titan, "cockpit_light_stop", CockpitLightStop ) + + OnThreadEnd( + function() : ( player, titan ) + { + if ( IsValid( player ) ) + { + Signal( player, "PhaseEmbarkPhaseStop" ) + DeleteAnimEvent( player, "phase_shift_start" ) + DeleteAnimEvent( player, "phase_shift_stop" ) + } + + if ( IsAlive( titan ) ) //Consider clearing titan.s.embarkingPlayer here? + titan.Die() + } + ) + + if ( GetCurrentPlaylistVarInt( "fp_embark_enabled", 0 ) == 1 ) + { + Remote_CallFunction_NonReplay( player, "ServerCallback_HideHudForFPEmbark" ) + + // fp embark hacks + entity viewControl = CreateEntity( "point_viewcontrol" ) + viewControl.kv.spawnflags = 56 + DispatchSpawn( viewControl ) + + viewControl.SetParent( player, "headshot" ) + viewControl.SetOrigin( < 4, 0, 0 > ) + viewControl.SetAngles( < 0, 0, 0 > ) + player.SetViewEntity( viewControl, false ) + } + + thread FirstPersonSequence( sequence, player, titan ) + // EmitDifferentSoundsOnEntityForPlayerAndWorld( firstPersonAudio, thirdPersonAudio, titan, player ) + + float animDuration = player.GetSequenceDuration( sequence.thirdPersonAnim ) + + if ( ShouldSkipAheadIntoEmbark( standing, player, titan, e ) ) + { + local duration = player.GetSequenceDuration( sequence.thirdPersonAnim ) + if ( duration >= SKIP_AHEAD_TIME ) + { + player.Anim_SetInitialTime( duration - SKIP_AHEAD_TIME ) + entity viewModel = player.GetFirstPersonProxy() + + if ( IsValid( viewModel ) && EntHasModelSet( viewModel ) && doFirstPersonAnim ) //JFS: Defensive fix for player not having view models sometimes + viewModel.Anim_SetInitialTime( duration - SKIP_AHEAD_TIME ) + + animDuration = SKIP_AHEAD_TIME + } + } + + thread Embark_DelayedFadeOut( player, titan, animDuration ) + + WaittillAnimDone( player ) + + Signal( player, "PhaseEmbarkPhaseStop" ) + + ClearPlayerAnimViewEntity( player ) + PilotBecomesTitan( player, titan ) + + thread PlayAnim( player, "cqb_idle_mp" ) + player.Anim_Stop() + player.SetVelocity( <0,0,0> ) + + player.SetOrigin( titan.GetOrigin() ) + local angles = titan.GetAngles() + angles.z = 0 + angles.x = 0 + player.SetAngles( angles ) + player.SnapEyeAngles( angles ) + + // soul stuff should be from anim event + Assert( IsServer() ) + SetStanceStand( player.GetTitanSoul() ) + titan.Destroy() +} + +void function Embark_DelayedFadeOut( entity player, entity titan, float delay ) +{ + if ( !IsAlive( player ) ) + return + + if ( !IsValid( titan ) ) + return + + player.EndSignal( "OnDeath" ) + titan.EndSignal( "OnDestroy" ) + + wait delay - EMBARK_FADE_TIME + + if ( GetCurrentPlaylistVarInt( "fp_embark_enabled", 0 ) == 0 ) + { + ScreenFadeToBlack( player, EMBARK_FADE_TIME, EMBARK_FADE_TIME + 0.2 ) // a little extra so we stay black + wait EMBARK_FADE_TIME + } + else + { + OnThreadEnd( function() : ( player ) + { + player.ClearViewEntity() + }) + + wait EMBARK_FADE_TIME - 0.2 + ScreenFadeToBlack( player, 0.2, 0.4 ) + wait 0.2 + player.ClearViewEntity() // make sure player is in normal first person again + } + + ScreenFadeFromBlack( player, EMBARK_FADE_TIME, EMBARK_FADE_TIME ) +} + +void function PlayStartupSounds( entity titan ) +{ + entity soul = titan.GetTitanSoul() + if ( !IsValid( soul ) ) + return + entity player = soul.GetBossPlayer() + if ( !IsValid( player ) ) + return + + player.EndSignal( "OnDeath" ) + + string titanSettings = GetSoulPlayerSettings( soul ) + var startupSound = Dev_GetPlayerSettingByKeyField_Global( titanSettings, "startup_sound" ) + + if ( startupSound != null ) + EmitSoundOnEntityOnlyToPlayer( player, player, expect string(startupSound) ) +} + + +void function CockpitLightStart( entity titan ) +{ + thread StartCockpitLightThink( titan, 5.0 ) +} + +void function StartCockpitLightThink( entity titan, float timeout ) +{ + titan.EndSignal( "OnDestroy" ) + titan.EndSignal( "CockpitLightStop" ) + + int attachID = titan.LookupAttachment( "HIJACK" ) + int fxID = GetParticleSystemIndex( file.cockpitLightFX ) + entity fx = StartParticleEffectOnEntity_ReturnEntity( titan, fxID, FX_PATTACH_POINT_FOLLOW, attachID ) + + OnThreadEnd( + function() : ( fx ) + { + if ( IsValid( fx ) ) + EffectStop( fx ) + } + ) + + if ( timeout < 0 ) + WaitForever() + else + wait timeout +} + +void function CockpitLightStop( entity titan ) +{ + titan.Signal( "CockpitLightStop" ) +} + +void function PhaseEmbarkPhaseStart( entity player ) +{ + player.MakeInvisible() + PlayPhaseShiftDisappearFX( player ) + EmitSoundOnEntity( player, "pilot_phaseembark_activate_3p" ) + + if ( GetCurrentPlaylistVarInt( "fp_embark_enabled", 0 ) == 1 ) + { + player.PhaseShiftBegin( 0.0, 0.2 ) + player.GetPetTitan().SetForceVisibleInPhaseShift( true ) // doesn't work for some reason + } + + thread PhaseEmbarkPhaseCleanup( player ) +} + +void function PhaseEmbarkPhaseCleanup( player ) +{ + EndSignal( player, "OnDeath" ) + + OnThreadEnd( + function() : ( player ) + { + if ( IsValid( player ) ) + { + player.MakeVisible() + } + } + ) + + WaitSignal( player, "PhaseEmbarkPhaseStop" ) +} + +void function PhaseEmbarkPhaseStop( entity player ) +{ + Signal( player, "PhaseEmbarkPhaseStop" ) + PlayPhaseShiftDisappearFX( player ) + EmitSoundOnEntity( player, "pilot_phaseembark_end_3p" ) + + if ( GetCurrentPlaylistVarInt( "fp_embark_enabled", 0 ) == 1 ) + player.PhaseShiftCancel() +} + +function ShouldSkipAheadIntoEmbark( standing, player, titan, e ) +{ + if ( !standing ) + return false + + if ( !e.embarkAction.canSkipAhead ) + return false + + local playerEye = player.EyePosition() + local titanOrg = titan.GetOrigin() + local vec = playerEye - titanOrg + vec.Norm() + vec.z = 0 + local start = playerEye + local end = playerEye + vec * 24 + + if ( Distance( player.GetOrigin(), titan.GetOrigin() ) >= 145 ) + return false + + local mask = TRACE_MASK_PLAYERSOLID + TraceResults result = TraceLine( start, end, [ titan, player ], mask, TRACE_COLLISION_GROUP_NONE ) + //DebugDrawLine( start, result.endPos, 0, 255, 0, true, 10.0 ) + //DebugDrawLine( result.endPos, end, 255, 0, 0, true, 10.0 ) + return result.fraction < 1.0 +} +#endif // SERVER + +function DelayedDisableEmbarkPlayerHud( player, sequence ) +{ + player.EndSignal( "OnDeath" ) + + local duration = player.GetSequenceDuration( sequence.thirdPersonAnim ) + wait duration - 1.0 + player.SetCinematicEventFlags( player.GetCinematicEventFlags() | CE_FLAG_EMBARK ) +} + +#if SERVER +function DelayedClearViewCone( player ) +{ + player.EndSignal( "OnDeath" ) + wait 1.0 + player.PlayerCone_SetLerpTime( 0.5 ) + player.PlayerCone_FromAnim() + player.PlayerCone_SetMinYaw( 0 ) + player.PlayerCone_SetMaxYaw( 0 ) + player.PlayerCone_SetMinPitch( 0 ) + player.PlayerCone_SetMaxPitch( 0 ) +} + +void function EmbarkViewCone( entity player ) +{ + player.PlayerCone_FromAnim() + player.PlayerCone_SetMinYaw( -70 ) + player.PlayerCone_SetMaxYaw( 60 ) + player.PlayerCone_SetMinPitch( -80 ) + player.PlayerCone_SetMaxPitch( 30 ) +} + +function TitanEmbark_TitanEmbarks( player, titan, e ) +{ + expect entity( player ) + expect entity( titan ) + + player.EndSignal( "OnDeath" ) + player.EndSignal( "TitanEjectionStarted" ) + titan.EndSignal( "OnDeath" ) + + titan.ContextAction_SetBusy() + + AddAnimEvent( titan, "play_startup_sound", PlayStartupSounds ) + + e.threads++ + OnThreadEnd( + function() : ( e, player, titan ) + { + e.threads-- + if ( !e.threads ) + { + Signal( e, "OnComplete" ) + } + + if ( !IsAlive( player ) && IsAlive( titan ) ) + { + titan.Anim_Stop() + titan.ContextAction_ClearBusy() + DeleteAnimEvent( titan, "play_startup_sound" ) + } + } + ) + + local soul = titan.GetTitanSoul() + + // dont let other players get in + + local animation +// local waittillAnimDone + local alignFront + bool standing = false + + if ( e.canStand ) + { + if ( e.shouldDoRegularEmbark ) + { + // default + switch ( titan.GetTitanSoul().GetStance() ) + { + case STANCE_KNEELING: + case STANCE_KNEEL: + animation = e.animSet.titanKneelingAnim + alignFront = false + break + + default: + animation = e.animSet.titanStandingAnim + if ( TitanHasLeftAndRightEmbarkAnims( titan ) ) + alignFront = false + else + alignFront = true + standing = true + break + } + } + else + { + // special for BT if he is in casual mode + animation = e.animSet.titanKneelingAnim + alignFront = false + } + } + else + { + animation = "at_mount_kneel_without_standing" + alignFront = false +// waittillAnimDone = false + } + + //sequence.blendTime = 0.5 + + printt("This is mount animation name: " + animation ) + if ( e.embarkAction.alignFrontEnabled && alignFront ) + { + local titanOrg = titan.GetOrigin() + local vec = player.GetOrigin() - titanOrg + local angles = VectorToAngles( vec ) + angles.x = 0 + angles.z = 0 + thread PlayAnimGravityClientSyncing( titan, animation, titanOrg, angles ) + } + else + { + thread PlayAnimGravityClientSyncing( titan, animation ) + } + + if ( ShouldSkipAheadIntoEmbark( standing, player, titan, e ) ) + { + local duration = titan.GetSequenceDuration( animation ) + + if ( duration >= SKIP_AHEAD_TIME ) // failsafe + titan.Anim_SetInitialTime( duration - SKIP_AHEAD_TIME ) + } + + // titan will become player now + WaitForever() +} + +bool function ClientCommand_TitanDisembark( entity player, array<string> args ) +{ + if ( !PlayerCanDisembarkTitan( player ) ) + return true + + ScreenFade( player, 0, 1, 0, 255, 0.2, 0.2, FFADE_IN | FFADE_PURGE ) + player.CockpitStartDisembark() + Remote_CallFunction_Replay( player, "ServerCallback_TitanDisembark" ) + + thread PlayerDisembarksTitan( player ) + + return true +} + +void function ForcedTitanDisembark( entity player ) +{ + Assert( PlayerCanDisembarkTitan( player ) ) + + player.CockpitStartDisembark() + Remote_CallFunction_Replay( player, "ServerCallback_TitanDisembark" ) + + waitthread PlayerDisembarksTitan( player ) +} + +function ForcedTitanDisembarkCustomAnims( entity player, FirstPersonSequenceStruct functionref( entity, entity ) playerSequenceFunc, FirstPersonSequenceStruct functionref( entity, entity ) titanSequenceFunc ) +{ + Assert( PlayerCanDisembarkTitan( player ) ) + + player.CockpitStartDisembark() + Remote_CallFunction_Replay( player, "ServerCallback_TitanDisembark" ) + + player.p.isCustomDisembark = true + waitthread PlayerDisembarksTitanWithSequenceFuncs( player, playerSequenceFunc, titanSequenceFunc ) + player.p.isCustomDisembark = false +} + +#endif // SERVER + +function PlayerCanDisembarkTitan( entity player ) +{ + if ( !player.IsTitan() ) + return false + + if ( !IsAlive( player ) ) + return false + + if ( IsValid( player.GetParent() ) ) + return false + + if ( Riff_TitanExitEnabled() == eTitanExitEnabled.Never ) + return false + + if ( !CanDisembark( player ) ) + return false + + #if SERVER + if ( player.IsNoclipping() ) + return false + // client doesn't know these things + if ( IsPlayerDisembarking( player ) ) + return false + if ( IsPlayerEmbarking( player ) ) + return false + #endif + + if ( !HasSoul( player ) ) + return false + + local soul = player.GetTitanSoul() + if ( soul.IsEjecting() ) + return false + + Assert( soul == player.GetTitanSoul() ) + + return true +} + +#if SERVER + +function PlayerDisembarksTitan( player ) +{ + expect entity( player ) + PlayerDisembarksTitanWithSequenceFuncs( player, GetDisembarkSequenceForPlayer, GetDisembarkSequenceForTitan ) +} + +void function PlayerDisembarksTitanWithSequenceFuncs( entity player, FirstPersonSequenceStruct functionref( entity, entity ) playerSequenceFunc, FirstPersonSequenceStruct functionref( entity, entity ) titanSequenceFunc ) +{ + //printt( "Player disembarking with origin " + player.GetOrigin() + " and yaw " + player.GetAngles().y ) + + //player.SetOrigin( Vector(420.847626, -5214.960938, 173.789520) ) + //player.SetAngles( Vector(0.000000, 179.572052, 0.000000 ) ) + + printt( "TitanDisembarkDebug: Player ", player.GetOrigin(), player.GetAngles(), GetMapName() ) + + player.EndSignal( "OnDeath" ) + player.Signal( "DisembarkingTitan" ) + + //Assert( !InSolid( player ), player + " is in solid" ) + + local e = {} + e.titan <- null + e.PilotCleanUpDone <- false + + e.startOrigin <- player.GetOrigin() + //e.startAngles <- player.GetAngles() + + player.p.isDisembarking = true + player.SetCinematicEventFlags( player.GetCinematicEventFlags() | CE_FLAG_DISEMBARK ) + + bool wasCustomDisembark = player.p.isCustomDisembark + + player.ContextAction_SetBusy() + + OnThreadEnd( + function() : ( player, e ) + { + if ( IsValid( player ) ) + { + PlayerEndsDisembark( player, e ) + + local titan = e.titan + if ( !IsValid( titan ) ) + titan = null + } + + if ( IsAlive( expect entity( e.titan ) ) ) + { + if ( IsAlive( player ) ) + { + thread PlayerOwnsTitanUntilSeparation( player, e.titan, 80 ) + } + + delete e.titan.s.disembarkingPlayer + ClearInvincible( expect entity( e.titan ) ) + + + //Make Titan get up immediately if he's kneeling and can stand + //If he can't get up, well, then since the titan doesn't move when crouched he's going to be stuck... + if ( !( "embarkingPlayer" in e.titan.s ) ) + { + thread TitanNPC_Think( expect entity( e.titan ) ) //titan.s.disableAutoTitanConversation is deleted inside here + } + } + } + ) + + bool standing = player.IsStanding() + + player.SetInvulnerable() + player.SnapFeetToEyes() + + entity titan = CreateAutoTitanForPlayer_ForTitanBecomesPilot( player ) + DispatchSpawn( titan ) + e.titan = titan + + if ( !PlayerIsFarOffTheGround( player, [ player,titan ] ) ) //PlayerIsFarOffTheGround() necessary now for R2 because we have Titans that can jump/geo where Titans can fall down from large heights. Without check, mid-air disembarking will cause player to be teleported to the ground + { + vector ornull clampedPos = NavMesh_ClampPointForAIWithExtents( titan.GetOrigin(), titan, file.smallDisembarkFailSafeTeleportVector ) + if ( clampedPos == null ) + clampedPos = NavMesh_ClampPointForAIWithExtents( titan.GetOrigin(), titan, file.largeDisembarkFailSafeTeleportVector ) + + if ( clampedPos != null ) + { + expect vector( clampedPos ) + vector titanOrigin = titan.GetOrigin() + + array<entity> ignoreEnts = [] + ignoreEnts.append( titan ) + + TraceResults result = TraceHull( titanOrigin, titanOrigin, titan.GetBoundingMins(), titan.GetBoundingMaxs(), ignoreEnts, TRACE_MASK_TITANSOLID, TRACE_COLLISION_GROUP_NONE ) + + // expensive checks to make sure titan doesn't teleport to navmesh on other side of wall usually in invalid places + if ( result.startSolid || + TraceLineSimple( titanOrigin + Vector( 0, 0, 128 ), clampedPos + Vector( 0, 0, 0 ), titan ) == 1.0 || + TraceLineSimple( titanOrigin + Vector( 0, 0, 200 ), clampedPos + Vector( 0, 0, 0 ), titan ) == 1.0 || + TraceLineSimple( titanOrigin + Vector( 0, 0, 200 ), clampedPos + Vector( 0, 0, 128 ), titan ) == 1.0 ) + { + #if DEV + if ( file.embarkDebugPrint ) + { + printt( "PlayerDisembarksTitanWithSequenceFuncs, player origin: " + player.GetOrigin()+ ", titan origin: " + titan.GetOrigin() + ", clampedPos: " + clampedPos ) + } + #endif + titan.SetOrigin( clampedPos ) + titan.ForceCheckGroundEntity() + } + } + } + else + { + #if DEV + if ( file.embarkDebugPrint ) + { + printt( "PlayerIsFarOffGround() returned true, skip doing NavMesh_ClampPointForAIWithExtents checks" ) + } + #endif + } + + titan.s.disembarkingPlayer <- player + titan.EndSignal( "OnDeath" ) + + player.SetSyncedEntity( titan ) + titan.s.disableAutoTitanConversation <- true + + Assert( titan.IsTitan() ) + Assert( IsAlive( player ) ) + Assert( player.IsTitan() ) + //Set player to be temporarily invulnerable. Will be removed at end of animation + //printt("Set player invulnerable") + HolsterViewModelAndDisableWeapons( player ) //Holstering weapon before becoming pilot so we don't play the holster animation as a pilot. Player as Titan won't play the holster animation either since it'll be interrupted by the disembark animation + + TitanBecomesPilot( player, titan ) + + string titanSubClass = GetSoulTitanSubClass( titan.GetTitanSoul() ) + switch ( titanSubClass ) + { + case "ogre": + ShowMainTitanWeapons( titan ) //JFS: Because we hide the Titan's weapons upon kneeling for ogre + break + } + + titan.s.disembarkTime <- Time() // disembark debounce + + // compound strings into these animations: + // pt_dismount_atlas_stand + // pt_dismount_ogre_stand + // pt_dismount_stryder_stand + // pt_dismount_atlas_crouch + // pt_dismount_ogre_crouch + // pt_dismount_stryder_crouch + // ptpov_dismount_atlas_stand + // ptpov_dismount_ogre_stand + // ptpov_dismount_stryder_stand + // ptpov_dismount_atlas_crouch + // ptpov_dismount_ogre_crouch + // ptpov_dismount_stryder_crouch + + FirstPersonSequenceStruct playerSequence = playerSequenceFunc( player, titan ) + FirstPersonSequenceStruct titanSequence = titanSequenceFunc( player, titan ) + + #if SERVER + StatusEffect_StopAll( player, eStatusEffect.lockon_detected_titan ) + #endif + + player.ForceStand() + + thread FirstPersonSequence( titanSequence, titan ) + thread FirstPersonSequence( playerSequence, player, titan ) + + if ( !wasCustomDisembark ) + thread ClearParentBeforeIntersect( player, titan, playerSequence.thirdPersonAnim, e ) + + if ( !standing ) + { + SetStanceKneel( titan.GetTitanSoul() ) + } + + //player.Anim_EnablePlanting() + + #if SERVER && MP + PIN_AddToPlayerCountStat( player, "disembarks" ) + PIN_PlayerAbility( player, "", "disembark", {}, 0 ) + #endif + + WaittillAnimDone( player ) +} + +bool function PlayerIsFarOffTheGround( entity player, array<entity> ignoreEnts ) +{ + vector boundingMaxs = player.GetBoundingMaxs() + float halfHeight = boundingMaxs.z / 2.0 + + vector startpos = player.GetOrigin() + vector endpos = startpos + endpos.z -= halfHeight + + TraceResults result = TraceHull( startpos, endpos, player.GetBoundingMins(), player.GetBoundingMaxs(), ignoreEnts, TRACE_MASK_PLAYERSOLID, TRACE_COLLISION_GROUP_PLAYER ) + //PrintTraceResults( result ) + + if ( result.startSolid ) + return false + + if ( result.allSolid ) + return false + + return ( result.fraction >= 1.0 ) +} + +FirstPersonSequenceStruct function GetDisembarkSequenceForPlayer( entity player, entity titan ) +{ + string titanSubClass = GetSoulTitanSubClass( titan.GetTitanSoul() ) + + string player3pAnim, player1pAnim + if ( player.IsStanding() ) + { + player3pAnim = "pt_dismount_" + titanSubClass + "_stand" + player1pAnim = "ptpov_dismount_" + titanSubClass + "_stand" + } + else + { + player3pAnim = "pt_dismount_" + titanSubClass + "_crouch" + player1pAnim = "ptpov_dismount_" + titanSubClass + "_crouch" + } + + if ( player.HasPassive( ePassives.PAS_FAST_EMBARK ) ) + { + player1pAnim += "_fast" + player3pAnim += "_fast" + } + + FirstPersonSequenceStruct playerSequence + playerSequence.blendTime = 0 + playerSequence.teleport = true + playerSequence.attachment = "hijack" + playerSequence.thirdPersonAnim = player3pAnim + playerSequence.firstPersonAnim = player1pAnim + playerSequence.useAnimatedRefAttachment = true + + return playerSequence +} + +FirstPersonSequenceStruct function GetDisembarkSequenceForTitan( entity player, entity titan ) +{ + bool standing = player.IsStanding() + + string titanDisembarkAnim + if ( standing ) + titanDisembarkAnim = "at_dismount_stand" + else + titanDisembarkAnim = "at_dismount_crouch" + + if ( player.HasPassive( ePassives.PAS_FAST_EMBARK ) ) + titanDisembarkAnim += "_fast" + + vector origin = titan.GetOrigin() + vector angles = titan.EyeAngles() + angles.z = 0 + angles.x = 0 + + FirstPersonSequenceStruct titanSequence + titanSequence.blendTime = 0.3 + titanSequence.thirdPersonAnim = titanDisembarkAnim + if ( !standing ) + titanSequence.thirdPersonAnimIdle = "at_MP_embark_idle_blended" + titanSequence.gravity = true + titanSequence.origin = origin + titanSequence.angles = angles + + return titanSequence +} + +function DelayedSafePlayerLocationForDisembark( entity player, entity titan ) +{ + float currentTime = Time() + float allowedTime = player.p.isCustomDisembark ? 10.0 : 2.0 + + player.EndSignal( "OnDestroy" ) + + while( IsPlayerDisembarking( player ) ) + { + Assert( Time() - currentTime < allowedTime ) // Failsafe of waiting 2 seconds in case SOMETHING REALLY GOES WRONG. + if ( !IsAlive( player ) ) + return + + WaitFrame() + } + + if ( player.ContextAction_IsActive() ) //Immediately after disembarking player might have gotten pulled into another context action e.g. embarking into evac dropship + return + + player.ClearParent() + player.PlayerCone_Disable() + player.ViewOffsetEntity_Clear() + player.GetFirstPersonProxy().HideFirstPersonProxy() + + vector safeStartPoint + + if ( IsValid( titan ) ) + { + vector titanBoundingMaxs = titan.GetBoundingMaxs() + float halfTitanHeight = titanBoundingMaxs.z * 0.5 + safeStartPoint = titan.GetOrigin() + < 0, 0, halfTitanHeight > //Let the start point of PutEntityInSafeSpot be closer to where the player is when disebmarking instead of the ground origin + } + else + { + titan = null + safeStartPoint = player.GetOrigin() + } + + #if DEV + if ( file.embarkDebugPrint ) + printt( "DelayedSafePlayerLocationForDisembark, before PutEntityInSafeSpot: player origin: " + player.GetOrigin() + ", titan origin: " + titan.GetOrigin() + " safeStartPoint: " + safeStartPoint ) + #endif + + + + if ( !PutEntityInSafeSpot( player, titan, null, safeStartPoint, player.GetOrigin() ) ) + player.SetOrigin( safeStartPoint ) + + #if DEV + if ( file.embarkDebugPrint ) + printt( "DelayedSafePlayerLocationForDisembark, after PutEntityInSafeSpot: player origin: " + player.GetOrigin() + ", titan origin: " + titan.GetOrigin() + " safeStartPoint: " + safeStartPoint ) + #endif + +} + +function ClearParentBeforeIntersect( entity player, entity titan, anim, e ) +{ + player.EndSignal( "OnDeath" ) + player.EndSignal( "OnAnimationDone" ) + player.EndSignal( "OnAnimationInterrupted" ) + //local mins = player.GetBoundingMins() + //local maxs = player.GetBoundingMaxs() + + OnThreadEnd( + function() : ( player, e ) + { + if ( IsValid( player ) ) + thread DelayedSafePlayerLocationForDisembark( player, expect entity( e.titan ) ) + } + ) + + wait 0.25 + + vector lastOrigin = player.GetOrigin() + for ( ;; ) + { + if ( EntityInSolid( player, titan, 24 ) ) + break + + lastOrigin = player.GetOrigin() + WaitFrame() + } + + player.SetOrigin( lastOrigin ) +} + +function LockedViewCone( human ) +{ + human.PlayerCone_FromAnim() + human.PlayerCone_SetMinYaw( 0 ) + human.PlayerCone_SetMaxYaw( 0 ) + human.PlayerCone_SetMinPitch( 0 ) + human.PlayerCone_SetMaxPitch( 0 ) +} + + +function PlayerOwnsTitanUntilSeparation( player, titan, dist ) +{ + titan.SetOwner( player ) + + player.EndSignal( "OnDeath" ) + titan.EndSignal( "OnDeath" ) + + OnThreadEnd( + function () : ( player, titan ) + { + if ( !IsValid( titan ) ) + return + + titan.SetOwner( null ) + } + ) + + // wait until player moves away + local distSqr = dist * dist + for ( ;; ) + { + if ( DistanceSqr( titan.GetOrigin(), player.GetOrigin() ) > distSqr ) + break + + wait 0.5 + } +} + +function PlayerEndsDisembark( player, e ) +{ + thread PlayerEndsDisembarkThread( player, e ) +} + +function PlayerEndsDisembarkThread( player, e ) +{ + expect entity( player ) + player.EndSignal( "OnDestroy" ) + + if ( e.PilotCleanUpDone ) + return + + e.PilotCleanUpDone = true + bool wasCustomDisembark = player.p.isCustomDisembark + + wait 0.1 + + ClearPlayerAnimViewEntity( player ) + if ( player.ContextAction_IsBusy() ) + player.ContextAction_ClearBusy() + + player.SetCinematicEventFlags( player.GetCinematicEventFlags() & (~CE_FLAG_DISEMBARK) ) + + player.Show() + TitanEmbark_PlayerCleanup( player ) + player.p.isDisembarking = false + + //// give a player a boost out the door + // + // + if ( IsAlive( player ) && !wasCustomDisembark ) + { + local angles = player.EyeAngles() + if ( IsValid( e.titan ) ) + angles = e.titan.GetAngles() + + angles.x = 0 + angles.z = 0 + local forward = AnglesToForward( angles ) + local up = AnglesToUp( angles ) + local vel = forward * 250 + up * 200 + player.SetVelocity( vel ) + //DebugDrawLine( player.GetOrigin(), player.GetOrigin() + forward * 500, 255, 0, 0, true, 5.0 ) + } +} + +function IsPlayerEmbarking( player ) +{ + expect entity ( player ) + return player.p.isEmbarking +} +#endif // SERVER + +function IsPlayerDisembarking( player ) +{ + expect entity ( player ) + return player.p.isDisembarking +} + +function PlayerCanEmbarkIntoTitan( entity player, entity titan ) //TODO: Collapse PlayerCanEmbarkTitan(), PlayerCanEmbarkIntoTitan(), PlayerCanImmediatelyEmbarkTitan() and TitanIsCurrentlyEmbarkableForPlayer() into one function +{ + if ( player.IsNoclipping() ) + return false + + if ( !TitanIsCurrentlyEmbarkableForPlayer( player, titan ) ) + return false + + return FindBestEmbark( player, titan ) != null +} + +bool function TitanIsCurrentlyEmbarkableForPlayer( entity player, entity titan ) //TODO: Collapse PlayerCanEmbarkTitan(), PlayerCanEmbarkIntoTitan(), PlayerCanImmediatelyEmbarkTitan() and TitanIsCurrentlyEmbarkableForPlayer() into one function +{ + if ( !CanEmbark( player ) ) + return false + + if ( player.Anim_IsActive() ) + return false + + if ( !player.IsHuman() ) + return false + + if ( player.ContextAction_IsActive() ) + return false + + if ( !titan.IsEntAlive() ) + return false + + if ( titan.ContextAction_IsActive() ) + return false + + if ( !titan.IsInterruptable() ) + return false + + if ( IsValid( titan.GetParent() ) ) + return false + + if ( !HasSoul( titan ) ) + return false + + local soul = titan.GetTitanSoul() + + if ( GetDoomedState( titan ) && !PROTO_AlternateDoomedState() ) + return false + + if ( soul.IsEjecting() ) + return false + + #if SERVER + // client doesn't know these things + if ( IsPlayerEmbarking( player ) ) + return false + + if ( IsPlayerDisembarking( player ) ) + return false + #endif + + if ( "disembarkTime" in titan.s ) + { + if ( Time() - titan.s.disembarkTime < 1.65 ) + return false + } + + return true +} + +function FindEmbarkActionForCriteria( criteria ) +{ + local embarkAction + foreach ( option in level.titanEmbarkActions ) + { + bool failed = false + foreach ( key, value in criteria ) + { + if ( value != option[key] ) + { + failed = true + break + } + } + + if ( !failed ) + { + embarkAction = option + break + } + } + + return embarkAction + +} + +function GetRandomEmbarkAction() +{ + return level.titanEmbarkActions[ RandomInt( level.titanEmbarkActions.len() ) ] +} + +function FindBestEmbark( entity player, entity titan, bool doDistCheck = true ) +{ +// if ( IsServer() ) +// printt( "finding best embark for " + player + " to " + titan ) + vector playerPos = player.GetOrigin() + vector titanPos = titan.GetOrigin() + + vector relTitanToPlayerDir = CalculateTitanToPlayerDir( titan, player ) + + local bestAction = null + float bestDot = -2 + float dist = 0 + + if ( doDistCheck ) + { + dist = Distance( playerPos, titanPos ) + if ( dist > level.titanEmbarkFarthestDistance ) + return null + } + //if ( IsServer() ) + // printt( "dist: " + dist ) + + for ( int i = 0; i < 3; i++ ) + { + bestAction = GetBestEmbarkAction( i, player, titan, dist, relTitanToPlayerDir ) + if ( bestAction != null ) + break + } + + if ( bestAction == null ) + return null + + return GenerateEmbarkActionTable( player, titan, bestAction, relTitanToPlayerDir ) +} + +vector function CalculateTitanToPlayerDir( entity titan, entity player ) +{ + vector playerPos = player.GetOrigin() + vector titanPos = titan.GetOrigin() + + vector absTitanToPlayerDir + if ( playerPos == titanPos ) + { + absTitanToPlayerDir = Vector( 1, 0, 0 ) + } + else + { + vector angles = player.EyeAngles() + vector forward = AnglesToForward( angles ) + + absTitanToPlayerDir = ( playerPos - titanPos ) + + + absTitanToPlayerDir.Norm() + +// not needed cause we can't get in without a legal use +// // is the target in my fov? +// if ( forward.Dot( absTitanToPlayerDir * -1 ) < 0.77 ) +// return null + } + + vector titanAngles = titan.GetAngles() + titanAngles.x = 0 + if ( titan.GetTitanSoul().GetStance() >= STANCE_STANDING ) + titanAngles = AnglesCompose( titanAngles, Vector( 0, -30, 0 ) ) + + vector relTitanToPlayerDir = CalcRelativeVector( titanAngles, absTitanToPlayerDir ) + return relTitanToPlayerDir +} + +function GenerateEmbarkActionTable( entity player, entity titan, bestAction, var relTitanToPlayerDir = null ) +{ + bool useFastAnims = player.IsPlayer() && player.HasPassive( ePassives.PAS_FAST_EMBARK ) + + if ( relTitanToPlayerDir == null ) + { + relTitanToPlayerDir = CalculateTitanToPlayerDir( titan, player ) + expect vector( relTitanToPlayerDir ) + } + else + { + expect vector( relTitanToPlayerDir ) + } + + local Table = {} + Table.action <- bestAction + + if ( "animSet" in bestAction ) + { + Table.animSet <- bestAction.animSet + Table.audioSet <- bestAction.audioSet + } + else + { + local bestAnimSet + local bestAudioSet + local bestDot = -2 + Assert( "animSets" in bestAction, "Table has no animSet and no animSets!" ) + foreach ( animSet in bestAction.animSets ) + { + local dot = relTitanToPlayerDir.Dot( animSet.direction ) + + if ( dot > bestDot ) + { + bestAnimSet = animSet + bestAudioSet = animSet.audioSet + bestDot = dot + } + } + + Table.animSet <- bestAnimSet + Table.audioSet <- bestAudioSet + } + + if ( useFastAnims ) + { + Table.animSet = clone Table.animSet + + foreach ( string idx, item in Table.animSet ) + { + if ( IsString( item ) ) + Table.animSet[ idx ] = item + "_fast" + } + } + + return Table +} + +function GetBestEmbarkAction( int priority, entity player, entity titan, float dist, vector relTitanToPlayerDir ) +{ + local bestAction = null + local bestDot = -2 + + foreach ( action in level.titanEmbarkActions ) + { + if ( action.priority != priority ) + continue + + if ( dist > action.distance ) + { + //if ( IsServer() ) + //printt( "Failed: Action " + action.embark + " had dist " + action.distance + " vs actual dist " + dist ) + continue + } + + if ( action.lungeCheck ) + { + if ( player.IsNPC() ) + continue + + if ( player.Lunge_IsActive() != action.lungeCheck ) + continue + } + + local dot = relTitanToPlayerDir.Dot( action.direction ) + + if ( dot < action.minDot ) + { + //if ( IsServer() ) + //printt( "Failed: Action " + action.embark + " had dot " + dot ) + continue + } + + if ( expect bool( action.titanCanStandRequired ) && !TitanCanStand( titan ) ) + { + //if ( IsServer() ) + //printt( "Failed: Action " + action.embark + " cant stand" ) + continue + } + + if ( dot > bestDot ) + { + //if ( IsServer() ) + //printt( "Action " + action.embark + " had dot " + dot ) + bestAction = action + bestDot = dot + } + } + + return bestAction +} + +function FindBestEmbarkForNpcAnim( entity npc, entity titan ) +{ + bool doDistCheck = false + return FindBestEmbark( npc, titan, doDistCheck ) +} + + + + +bool function TitanCanStand( entity titan ) +{ + #if SERVER + vector maxs = titan.GetBoundingMaxs() + vector mins = titan.GetBoundingMins() + + vector start = titan.GetOrigin() + vector end = titan.GetOrigin() + entity soul = titan.GetTitanSoul() + entity ignoreEnt = null + + if ( IsValid( soul.soul.bubbleShield ) ) + ignoreEnt = soul.soul.bubbleShield + int mask = titan.GetPhysicsSolidMask() + //printt( "mask has " + MaskTester( mask ) ) + TraceResults result = TraceHull( start, end, mins, maxs, ignoreEnt, mask, TRACE_COLLISION_GROUP_NONE ) + //printt( "start " + start + " end " + end ) + + //DebugDrawLine( start, result.endPos, 0, 255, 0, true, 5.0 ) + //DebugDrawLine( result.endPos, end, 255, 0, 0, true, 5.0 ) + + bool canStand = result.fraction >= 1.0 + titan.SetCanStand( canStand ) + return canStand + #else + return titan.GetCanStand() != 0 + #endif +} + +bool function PlayerCanEmbarkTitan( entity player, entity titan ) //TODO: Collapse PlayerCanEmbarkTitan(), PlayerCanEmbarkIntoTitan(), PlayerCanImmediatelyEmbarkTitan() and TitanIsCurrentlyEmbarkableForPlayer() into one function +{ + PerfStart( PerfIndexClient.PlayerCanEmbarkTitan1 ) + if ( !TitanIsCurrentlyEmbarkableForPlayer( player, titan ) ) + { + PerfEnd( PerfIndexClient.PlayerCanEmbarkTitan1 ) + return false + } + PerfEnd( PerfIndexClient.PlayerCanEmbarkTitan1 ) + + PerfStart( PerfIndexClient.FindBestEmbark ) + bool res = FindBestEmbark( player, titan ) != null + PerfEnd( PerfIndexClient.FindBestEmbark ) + + return res +} + +bool function PlayerCanImmediatelyEmbarkTitan( entity player, entity titan ) //TODO: Collapse PlayerCanEmbarkTitan(), PlayerCanEmbarkIntoTitan(), PlayerCanImmediatelyEmbarkTitan() and TitanIsCurrentlyEmbarkableForPlayer() into one function +{ + if ( "embarkingPlayer" in titan.s ) + return false + + if ( player.IsNoclipping() ) + return false + + if ( !IsAlive( player ) || !IsAlive( titan ) ) + return false + + return FindBestEmbark( player, titan ) != null +} + +#if SERVER +function PlayerLungesToEmbark( entity player, entity ent ) +{ + Assert( TitanIsCurrentlyEmbarkableForPlayer( player, ent ) ) + + if ( PlayerCanImmediatelyEmbarkTitan( player, ent ) ) + { + table embarkDirection = expect table( FindBestEmbark( player, ent ) ) + thread PlayerEmbarksTitan( player, ent, embarkDirection ) + return + } + + if ( player.IsNoclipping() ) + return + + // already lunging + if ( player.Lunge_IsActive() ) + return + + if ( ShouldStopLunging( player, ent ) ) + return + + player.Lunge_SetTargetEntity( ent, false ) + player.Lunge_SetSmoothTime( 3.0 ) +} + +void function TitanBecomesPilot_UpdateRodeoRiderHud( entity playerTitan, entity npc_titan ) +{ + entity rodeoPilot = GetRodeoPilot( npc_titan ) + if ( !IsValid( rodeoPilot ) ) + return + + Remote_CallFunction_Replay( rodeoPilot, "ServerCallback_UpdateRodeoRiderHud" ) +} + +void function PilotBecomesTitan_UpdateRodeoRiderHud( entity playerTitan, entity npc_titan ) +{ + entity rodeoPilot = GetRodeoPilot( playerTitan ) + if ( !IsValid( rodeoPilot ) ) + return + + Remote_CallFunction_Replay( rodeoPilot, "ServerCallback_UpdateRodeoRiderHud" ) + +} + +void function SetSmallDisembarkFailSafeTeleportVector( vector value ) //TODO: Re-examine this for next game, probably should have different values for SP versus MP +{ + file.smallDisembarkFailSafeTeleportVector = value +} + +void function SetLargeDisembarkFailSafeTeleportVector( vector value ) //TODO: Re-examine this for next game, probably should have different values for SP versus MP +{ + file.largeDisembarkFailSafeTeleportVector = value +} + +#endif // SERVER + +#if DEV +void function SetEmbarkDebugPrint( bool value ) +{ + file.embarkDebugPrint = value +} +#endif
\ No newline at end of file |