aboutsummaryrefslogtreecommitdiff
path: root/Northstar.CustomServers/mod/scripts/vscripts/ai/_ai_pilots.gnut
diff options
context:
space:
mode:
authorBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-08-31 23:14:58 +0100
committerBobTheBob <32057864+BobTheBob9@users.noreply.github.com>2021-08-31 23:14:58 +0100
commit9a96d0bff56f1969c68bb52a2f33296095bdc67d (patch)
tree4175928e488632705692e3cccafa1a38dd854615 /Northstar.CustomServers/mod/scripts/vscripts/ai/_ai_pilots.gnut
parent27bd240871b7c0f2f49fef137718b2e3c208e3b4 (diff)
downloadNorthstarMods-9a96d0bff56f1969c68bb52a2f33296095bdc67d.tar.gz
NorthstarMods-9a96d0bff56f1969c68bb52a2f33296095bdc67d.zip
move to new mod format
Diffstat (limited to 'Northstar.CustomServers/mod/scripts/vscripts/ai/_ai_pilots.gnut')
-rw-r--r--Northstar.CustomServers/mod/scripts/vscripts/ai/_ai_pilots.gnut808
1 files changed, 808 insertions, 0 deletions
diff --git a/Northstar.CustomServers/mod/scripts/vscripts/ai/_ai_pilots.gnut b/Northstar.CustomServers/mod/scripts/vscripts/ai/_ai_pilots.gnut
new file mode 100644
index 000000000..3c2e36ce0
--- /dev/null
+++ b/Northstar.CustomServers/mod/scripts/vscripts/ai/_ai_pilots.gnut
@@ -0,0 +1,808 @@
+untyped
+
+global const NPC_TITAN_PILOT_PROTOTYPE = 0
+global function AiPilots_Init
+
+global function CaptainThink
+
+
+#if NPC_TITAN_PILOT_PROTOTYPE
+global function NpcPilotCallTitanThink
+global function NpcPilotStopCallTitanThink
+global function NpcPilotCallsInAndEmbarksTitan
+global function NpcPilotRunsToAndEmbarksFallingTitan
+global function NpcPilotCallsInTitan
+global function NpcPilotRunsToEmbarkTitan
+global function NpcPilotEmbarksTitan
+global function NpcPilotDisembarksTitan
+global function NpcPilotBecomesTitan
+global function NpcTitanBecomesPilot
+global function TitanHasNpcPilot
+global function NpcPilotGetPetTitan
+global function NpcPilotSetPetTitan
+#endif
+
+global function NpcSetNextTitanRespawnAvailable
+global function NpcResetNextTitanRespawnAvailable
+
+global function AddCallback_OnNpcTitanBecomesPilot
+global function AddCallback_OnNpcPilotBecomesTitan
+
+global struct NPCPilotStruct
+{
+ bool isValid = false
+
+ int team
+ int spawnflags
+ float accuracy
+ float proficieny
+ float health
+ float physDamageScale
+ string weapon
+ string squadName
+
+ asset modelAsset
+ string title
+
+ bool isInvulnerable
+}
+
+const NPC_NEXT_TITANTIME_RESET = -1
+const NPC_NEXT_TITANTIME_MIN = 45
+const NPC_NEXT_TITANTIME_MAX = 60
+const NPC_NEXT_TITANTIME_INTERUPT = 15
+
+function AiPilots_Init()
+{
+ RegisterSignal( "grenade_throw" )
+ RegisterSignal( "NpcPilotBecomesTitan" )
+ RegisterSignal( "NpcTitanBecomesPilot" )
+ RegisterSignal( "StopCallTitanThink" )
+ RegisterSignal( "NpcTitanRespawnAvailableUpdated" )
+
+ level.onNpcPilotBecomesTitanCallbacks <- []
+ level.onNpcTitanBecomesPilotCallbacks <- []
+
+}
+
+function ScriptCallback_OnNpcPilotBecomesTitan( pilot, titan )
+{
+ local result = { pilot = pilot, titan = titan }
+ Signal( pilot, "NpcPilotBecomesTitan", result )
+ Signal( titan, "NpcPilotBecomesTitan", result )
+
+ foreach ( callbackFunc in level.onNpcPilotBecomesTitanCallbacks )
+ {
+ callbackFunc( pilot, titan )
+ }
+}
+
+function ScriptCallback_OnNpcTitanBecomesPilot( pilot, titan )
+{
+ local result = { pilot = pilot, titan = titan }
+ Signal( pilot, "NpcTitanBecomesPilot", result )
+ Signal( titan, "NpcTitanBecomesPilot", result )
+
+ foreach ( callbackFunc in level.onNpcTitanBecomesPilotCallbacks )
+ {
+ callbackFunc( pilot, titan )
+ }
+}
+
+function AddCallback_OnNpcPilotBecomesTitan( callbackFunc )
+{
+ Assert( "onNpcPilotBecomesTitanCallbacks" in level )
+ AssertParameters( callbackFunc, 2, "pilotNPC, titanNPC" )
+
+ level.onNpcPilotBecomesTitanCallbacks.append( callbackFunc )
+}
+
+function AddCallback_OnNpcTitanBecomesPilot( callbackFunc )
+{
+ Assert( "onNpcTitanBecomesPilotCallbacks" in level )
+ AssertParameters( callbackFunc, 2, "pilotNPC, titanNPC" )
+
+ level.onNpcTitanBecomesPilotCallbacks.append( callbackFunc )
+}
+
+function NpcSetNextTitanRespawnAvailable( npc, time )
+{
+ Assert( "nextTitanRespawnAvailable" in npc.s )
+ npc.s.nextTitanRespawnAvailable = time
+ npc.Signal( "NpcTitanRespawnAvailableUpdated" )
+}
+
+function NpcResetNextTitanRespawnAvailable( npc )
+{
+ Assert( "nextTitanRespawnAvailable" in npc.s )
+ npc.s.nextTitanRespawnAvailable = NPC_NEXT_TITANTIME_RESET
+ npc.Signal( "NpcTitanRespawnAvailableUpdated" )
+}
+
+function NpcPilotStopCallTitanThink( pilot )
+{
+ pilot.Signal( "StopCallTitanThink" )
+}
+
+/************************************************************************************************\
+
+######## #### ## ####### ######## ######## ## ## #### ## ## ## ##
+## ## ## ## ## ## ## ## ## ## ## ### ## ## ##
+## ## ## ## ## ## ## ## ## ## ## #### ## ## ##
+######## ## ## ## ## ## ## ######### ## ## ## ## #####
+## ## ## ## ## ## ## ## ## ## ## #### ## ##
+## ## ## ## ## ## ## ## ## ## ## ### ## ##
+## #### ######## ####### ## ## ## ## #### ## ## ## ##
+
+\************************************************************************************************/
+function CaptainThink( entity npc )
+{
+ npc.EndSignal( "OnDestroy" )
+ npc.EndSignal( "OnDeath" )
+
+ Assert( !( "nextTitanRespawnAvailable" in npc.s ) )
+ Assert( !( "petTitan" in npc.s ) )
+
+ npc.s.petTitan <- null
+ npc.s.nextTitanRespawnAvailable <- null
+
+ //wait for in combat...
+ WaitForNpcInCombat( npc )
+
+ //... before we call in a titan
+ if ( npc.s.nextTitanRespawnAvailable == null )
+ npc.s.nextTitanRespawnAvailable = Time() + RandomFloatRange( 2, 10 )
+
+ WaitEndFrame() //wait a frame for things like petTitan and nextTitanRespawnAvailable to have a chance to be set from custom scripts
+ #if NPC_TITAN_PILOT_PROTOTYPE
+ thread NpcPilotCallTitanThink( npc )
+ #endif
+}
+
+#if NPC_TITAN_PILOT_PROTOTYPE
+
+function NpcPilotCallTitanThink( entity pilot )
+{
+ Assert( pilot.IsNPC() )
+ Assert( IsAlive( pilot ) )
+ Assert ( !pilot.IsTitan() )
+
+ pilot.EndSignal( "OnDestroy" )
+ pilot.EndSignal( "OnDeath" )
+ pilot.Signal( "StopCallTitanThink" )
+ pilot.EndSignal( "StopCallTitanThink" )
+
+
+ string title = pilot.GetTitle() + "'s Titan"
+ local count = 1 //1 titan call in at a time
+
+ while ( true ) //this loop usually only happens once, unless the titan called in is destroyed before the living pilot can get to it
+ {
+ entity titan = NpcPilotGetPetTitan( pilot )
+ if ( !IsAlive( titan ) )
+ {
+ //wait for ready titan
+ waitthread __WaitforTitanCallinReady( pilot )
+
+ //ready to call in - look for a good spot
+ SpawnPointFP spawnPoint
+ while ( true )
+ {
+ wait ( RandomFloatRange( 1, 2 ) )
+
+ //dont do stuff when animating on a parent
+ if ( pilot.GetParent() )
+ continue
+
+ //Don't deploy if too close to an enemy
+ if ( HasEnemyWithinDist( pilot, 300.0 ) )
+ continue
+
+ // DO the opposite - only deploy if has an enemy within this distance
+ // if ( !HasEnemyWithinDist( pilot, 2000.0 ) )
+ // continue
+
+ //don't do stuff if you dont have a spawnPoint
+ spawnPoint = FindSpawnPointForNpcCallin( pilot, TITAN_MEDIUM_AJAX_MODEL, HOTDROP_TURBO_ANIM )
+ if ( !spawnPoint.valid )
+ continue
+
+ break
+ }
+
+ //call in a titan, run to it, and embark
+ //in SP by default, the friendlys do NOT do the beacon tell
+ titan = NpcPilotCallsInAndEmbarksTitan( pilot, spawnPoint.origin, spawnPoint.angles )
+ titan.SetTitle( title )
+ }
+ else
+ {
+ Assert( IsAlive( titan ) )
+
+ if ( HasEnemyRodeo( titan ) )
+ {
+ while ( HasEnemyRodeo( titan ) )
+ {
+ WaitSignal( titan.GetTitanSoul(), "RodeoRiderChanged", "OnDestroy" )
+ }
+
+ wait 4 //don't pop back in immediately
+ }
+
+ if ( !IsAlive( titan ) )
+ continue //the titan didn't make it, lets loop back up and try again
+
+ if ( titan.GetTitanSoul().IsDoomed() )
+ {
+ titan.WaitSignal( "OnDestroy" )
+ continue //the titan didn't make it, lets loop back up and try again
+ }
+
+ //start running to titan as it kneels
+ thread NpcPilotRunsToEmbarkTitan( pilot, titan )
+ thread __TitanKneelsForPilot( pilot, titan )
+ wait 2.0 //wait for titan to be in position
+
+ if ( !IsAlive( titan ) )
+ continue //the titan didn't make it, lets loop back up and try again
+
+ //run to the titan
+ waitthread NpcPilotRunsToEmbarkTitan( pilot, titan )
+
+ if ( !IsAlive( titan ) )
+ continue //the titan didn't make it, lets loop back up and try again
+
+ //embark titan
+ thread NpcPilotEmbarksTitan( pilot, titan )
+ }
+
+ local result = WaitSignal( titan, "NpcPilotBecomesTitan", "OnDeath", "OnDestroy" )
+ if ( result.signal != "NpcPilotBecomesTitan" )
+ continue //the titan didn't make it, lets loop back up and try again
+ }
+}
+
+/************************************************************************************************\
+
+ ###### ### ## ## #### ## ## ######## #### ######## ### ## ##
+## ## ## ## ## ## ## ### ## ## ## ## ## ## ### ##
+## ## ## ## ## ## #### ## ## ## ## ## ## #### ##
+## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
+## ######### ## ## ## ## #### ## ## ## ######### ## ####
+## ## ## ## ## ## ## ## ### ## ## ## ## ## ## ###
+ ###### ## ## ######## ######## #### ## ## ## #### ## ## ## ## ##
+
+\************************************************************************************************/
+
+
+entity function NpcPilotCallsInAndEmbarksTitan( entity pilot, vector origin, vector angles )
+{
+ entity titan = NpcPilotCallsInTitan( pilot, origin, angles )
+ thread NpcPilotRunsToAndEmbarksFallingTitan( pilot, titan )
+
+ return titan
+}
+
+function NpcPilotRunsToAndEmbarksFallingTitan( entity pilot, entity titan )
+{
+ titan.EndSignal( "OnDeath" )
+
+ //wait for it to land
+ waitthread WaitTillHotDropComplete( titan )
+ ShowName( titan )
+
+ if ( !IsAlive( titan ) )
+ return
+ titan.EndSignal( "OnDeath" )
+
+ //titan is alive on land so clean it up on thread end
+ OnThreadEnd(
+ function () : ( titan )
+ {
+ if ( !IsAlive( titan ) )
+ return
+
+ SetStanceStand( titan.GetTitanSoul() )
+
+ //the pilot never made it to embark - lets stand our titan up so he can fight
+ if ( !TitanHasNpcPilot( titan ) )
+ {
+ thread PlayAnimGravity( titan, "at_hotdrop_quickstand" )
+ HideName( titan )
+ }
+ }
+ )
+
+ //if the pilot has died, early out
+ if ( !IsAlive( pilot ) )
+ return
+
+ pilot.EndSignal( "OnDeath" )
+
+ //run to the titan
+ waitthread NpcPilotRunsToEmbarkTitan( pilot, titan )
+
+ //embark titan
+ waitthread NpcPilotEmbarksTitan( pilot, titan )
+}
+
+entity function NpcPilotCallsInTitan( entity pilot, vector origin, vector angles )
+{
+ Assert( !pilot.IsTitan() )
+ Assert( IsAlive( pilot ) )
+ Assert( !NpcPilotGetPetTitan( pilot ) )
+
+ //reset the next titan callin timer
+ NpcResetNextTitanRespawnAvailable( pilot )
+
+ //spawn a titan
+ array<string> settingsArray = GetAllowedTitanAISettings()
+
+ string titanSettings = settingsArray.getrandom()
+ entity titan = CreateNPC( "npc_titan", pilot.GetTeam(), origin, angles )
+ SetSpawnOption_AISettings( titan, titanSettings )
+ DispatchSpawn( titan )
+
+ NpcPilotSetPetTitan( pilot, titan )
+
+ //call it in
+ thread NPCTitanHotdrops( titan, false, "at_hotdrop_drop_2knee_turbo_upgraded" )
+ thread __TitanKneelOrStandAfterDropin( titan, pilot )
+
+ //get the titan ready to be embarked
+ SetStanceKneel( titan.GetTitanSoul() )
+ titan.SetTitle( pilot.GetTitle() + "'s Titan" )
+ UpdateEnemyMemoryFromTeammates( titan )
+
+ return titan
+}
+
+void function __TitanKneelOrStandAfterDropin( entity titan, entity pilot )
+{
+ Assert( IsAlive( titan ) )
+ titan.EndSignal( "OnDeath" )
+ titan.EndSignal( "OnDestroy" )
+
+ titan.WaitSignal( "TitanHotDropComplete" )
+
+ if ( IsAlive( pilot ) )
+ thread PlayAnimGravity( titan, "at_MP_embark_idle" )
+ //else the titan will automatically stand up
+}
+
+//HACK -> this behavior should be completely in code
+void function NpcPilotRunsToEmbarkTitan( entity pilot, entity titan )
+{
+ titan.EndSignal( "OnDeath" )
+ titan.EndSignal( "OnDestroy" )
+ pilot.EndSignal( "OnDeath" )
+ pilot.EndSignal( "OnDestroy" )
+
+ pilot.SetNoTarget( true )
+ pilot.Anim_Stop()
+ pilot.DisableNPCMoveFlag( NPCMF_INDOOR_ACTIVITY_OVERRIDE )
+ pilot.EnableNPCMoveFlag( NPCMF_IGNORE_CLUSTER_DANGER_TIME | NPCMF_PREFER_SPRINT )
+ pilot.DisableArrivalOnce( true )
+ bool canMoveAndShoot = pilot.GetCapabilityFlag( bits_CAP_MOVE_SHOOT )
+ pilot.SetCapabilityFlag( bits_CAP_MOVE_SHOOT, false )
+
+ OnThreadEnd(
+ function () : ( pilot, canMoveAndShoot )
+ {
+ if ( !IsAlive( pilot ) )
+ return
+
+ pilot.SetNoTarget( false )
+ pilot.EnableNPCMoveFlag( NPCMF_INDOOR_ACTIVITY_OVERRIDE )
+ pilot.DisableNPCMoveFlag( NPCMF_IGNORE_CLUSTER_DANGER_TIME | NPCMF_PREFER_SPRINT )
+ pilot.SetCapabilityFlag( bits_CAP_MOVE_SHOOT, canMoveAndShoot )
+ }
+ )
+
+ local titanSubClass = GetSoulTitanSubClass( titan.GetTitanSoul() )
+ local embarkSet = FindBestEmbarkForNpcAnim( pilot, titan )
+ string pilotAnim = GetAnimFromAlias( titanSubClass, embarkSet.animSet.thirdPersonKneelingAlias )
+
+ pilot.ClearAllEnemyMemory()
+ waitthread RunToAnimStartForced_Deprecated( pilot, pilotAnim, titan, "hijack" )
+}
+
+/************************************************************************************************\
+
+ ###### ## ## #### ######## ###### ## ##
+## ## ## ## ## ## ## ## ## ## ##
+## ## ## ## ## ## ## ## ##
+ ###### ## ## ## ## ## ## #########
+ ## ## ## ## ## ## ## ## ##
+## ## ## ## ## ## ## ## ## ## ##
+ ###### ### ### #### ## ###### ## ##
+
+\************************************************************************************************/
+function NpcPilotEmbarksTitan( entity pilot, entity titan )
+{
+ Assert( IsAlive( pilot ) )
+ Assert( IsAlive( titan ) )
+ Assert( !pilot.IsTitan() )
+ Assert( titan.IsTitan() )
+
+ titan.EndSignal( "OnDestroy" )
+ titan.EndSignal( "OnDeath" )
+
+ OnThreadEnd(
+ function () : ( titan, pilot )
+ {
+ if ( IsAlive( titan ) )
+ {
+ if ( titan.ContextAction_IsBusy() )
+ titan.ContextAction_ClearBusy()
+ titan.ClearInvulnerable()
+
+ Assert( !IsAlive( pilot ) )
+ }
+ }
+ )
+
+ local isInvulnerable = pilot.IsInvulnerable()
+ pilot.SetInvulnerable()
+ titan.SetInvulnerable()
+
+ local titanSubClass = GetSoulTitanSubClass( titan.GetTitanSoul() )
+ local embarkSet = FindBestEmbark( pilot, titan )
+
+ while ( embarkSet == null )
+ {
+ wait 1.0
+ embarkSet = FindBestEmbark( pilot, titan )
+ }
+
+ local pilotAnim = GetAnimFromAlias( titanSubClass, embarkSet.animSet.thirdPersonKneelingAlias )
+ local titanAnim = embarkSet.animSet.titanKneelingAnim
+
+ if ( !titan.ContextAction_IsBusy() ) //might be set from kneeling
+ titan.ContextAction_SetBusy()
+ pilot.ContextAction_SetBusy()
+
+ if ( IsCloaked( pilot ) )
+ pilot.SetCloakDuration( 0, 0, 1.5 )
+
+ //pilot.SetParent( titan, "hijack", false, 0.5 ) //the time is just in case their not exactly at the right starting position
+ EmitSoundOnEntity( titan, embarkSet.audioSet.thirdPersonKneelingAudioAlias )
+ thread PlayAnim( pilot, pilotAnim, titan, "hijack" )
+ waitthread PlayAnim( titan, titanAnim )
+
+ if ( !isInvulnerable )
+ pilot.ClearInvulnerable()
+
+ NpcPilotBecomesTitan( pilot, titan )
+}
+
+entity function NpcPilotDisembarksTitan( entity titan )
+{
+ Assert( titan.IsTitan() )
+ Assert( TitanHasNpcPilot( titan ) )
+
+ entity pilot = NpcTitanBecomesPilot( titan )
+ Assert( !pilot.IsTitan() )
+
+ NpcPilotSetPetTitan( pilot, titan )
+
+ thread __NpcPilotDisembarksTitan( pilot, titan )
+
+ return pilot
+}
+
+function __NpcPilotDisembarksTitan( pilot, titan )
+{
+ expect entity( pilot )
+ expect entity( titan )
+
+ titan.ContextAction_SetBusy()
+ pilot.ContextAction_SetBusy()
+
+ if ( pilot.GetTitle() != "" )
+ {
+ titan.SetTitle( pilot.GetTitle() + "'s Titan" )
+ }
+
+ local isInvulnerable = pilot.IsInvulnerable()
+ pilot.SetInvulnerable()
+ titan.SetInvulnerable()
+
+ local pilot3pAnim, pilot3pAudio, titanDisembarkAnim
+ local titanSubClass = GetSoulTitanSubClass( titan.GetTitanSoul() )
+ local standing = titan.GetTitanSoul().GetStance() >= STANCE_STANDING // STANCE_STANDING = 2, STANCE_STAND = 3
+
+ if ( standing )
+ {
+ titanDisembarkAnim = "at_dismount_stand"
+ pilot3pAnim = "pt_dismount_" + titanSubClass + "_stand"
+ pilot3pAudio = titanSubClass + "_Disembark_Standing_3P"
+ }
+ else
+ {
+ titanDisembarkAnim = "at_dismount_crouch"
+ pilot3pAnim = "pt_dismount_" + titanSubClass + "_crouch"
+ pilot3pAudio = titanSubClass + "_Disembark_Kneeling_3P"
+ }
+
+// pilot.SetParent( titan, "hijack" )
+ EmitSoundOnEntity( titan, pilot3pAudio )
+ thread PlayAnim( titan, titanDisembarkAnim )
+ waitthread PlayAnim( pilot, pilot3pAnim, titan, "hijack" )
+
+ //pilot.ClearParent()
+ titan.ContextAction_ClearBusy()
+ pilot.ContextAction_ClearBusy()
+ if ( !isInvulnerable )
+ pilot.ClearInvulnerable()
+ titan.ClearInvulnerable()
+
+ if ( !standing )
+ SetStanceKneel( titan.GetTitanSoul() )
+}
+
+void function NpcPilotBecomesTitan( entity pilot, entity titan )
+{
+ Assert( IsAlive( pilot ) )
+ Assert( IsAlive( titan ) )
+ Assert( IsGrunt( pilot ) || IsPilotElite( pilot ) )
+ Assert( titan.IsTitan() )
+
+ entity titanSoul = titan.GetTitanSoul()
+
+ titanSoul.soul.seatedNpcPilot.isValid = true
+
+ titanSoul.soul.seatedNpcPilot.team = pilot.GetTeam()
+ titanSoul.soul.seatedNpcPilot.spawnflags = expect int( pilot.kv.spawnflags )
+ titanSoul.soul.seatedNpcPilot.accuracy = expect float( pilot.kv.AccuracyMultiplier )
+ titanSoul.soul.seatedNpcPilot.proficieny = expect float( pilot.kv.WeaponProficiency )
+ titanSoul.soul.seatedNpcPilot.health = expect float( pilot.kv.max_health )
+ titanSoul.soul.seatedNpcPilot.physDamageScale = expect float( pilot.kv.physdamagescale )
+ titanSoul.soul.seatedNpcPilot.weapon = pilot.GetMainWeapons()[0].GetWeaponClassName()
+ titanSoul.soul.seatedNpcPilot.squadName = expect string( pilot.kv.squadname )
+
+ titanSoul.soul.seatedNpcPilot.modelAsset = pilot.GetModelName()
+ titanSoul.soul.seatedNpcPilot.title = pilot.GetTitle()
+
+ titanSoul.soul.seatedNpcPilot.isInvulnerable = pilot.IsInvulnerable()
+
+ titan.SetTitle( titanSoul.soul.seatedNpcPilot.title )
+
+ thread __TitanPilotRodeoCounter( titan )
+
+ ScriptCallback_OnNpcPilotBecomesTitan( pilot, titan )
+
+ pilot.Destroy()
+}
+
+entity function NpcTitanBecomesPilot( entity titan )
+{
+ Assert( IsValid( titan ) )
+ Assert( titan.IsTitan() )
+
+ entity titanSoul = titan.GetTitanSoul()
+ titanSoul.soul.seatedNpcPilot.isValid = false
+
+ string weapon = titanSoul.soul.seatedNpcPilot.weapon
+ string squadName = titanSoul.soul.seatedNpcPilot.squadName
+ asset model = titanSoul.soul.seatedNpcPilot.modelAsset
+ string title = titanSoul.soul.seatedNpcPilot.title
+ int team = titanSoul.soul.seatedNpcPilot.team
+ vector origin = titan.GetOrigin()
+ vector angles = titan.GetAngles()
+ entity pilot = CreateElitePilot( team, origin, angles )
+
+ SetSpawnOption_Weapon( pilot, weapon )
+ SetSpawnOption_SquadName( pilot, squadName )
+ pilot.SetValueForModelKey( model )
+ DispatchSpawn( pilot )
+ pilot.SetModel( model ) // this is a hack, trying to avoid having a model spawn option because its easy to abuse
+
+ NpcPilotSetPetTitan( pilot, titan )
+ NpcResetNextTitanRespawnAvailable( pilot )
+
+ pilot.kv.spawnflags = titanSoul.soul.seatedNpcPilot.spawnflags
+ pilot.kv.AccuracyMultiplier = titanSoul.soul.seatedNpcPilot.accuracy
+ pilot.kv.WeaponProficiency = titanSoul.soul.seatedNpcPilot.proficieny
+ pilot.kv.health = titanSoul.soul.seatedNpcPilot.health
+ pilot.kv.max_health = titanSoul.soul.seatedNpcPilot.health
+ pilot.kv.physDamageScale = titanSoul.soul.seatedNpcPilot.physDamageScale
+
+ if ( titanSoul.soul.seatedNpcPilot.isInvulnerable )
+ pilot.SetInvulnerable()
+
+ titan.SetOwner( pilot )
+ NPCFollowsNPC( titan, pilot )
+
+ UpdateEnemyMemoryFromTeammates( pilot )
+ thread __TitanStanceThink( pilot, titan )
+
+ ScriptCallback_OnNpcTitanBecomesPilot( pilot, titan )
+
+ return pilot
+}
+
+bool function TitanHasNpcPilot( entity titan )
+{
+ Assert( titan.IsTitan() )
+
+ entity titanSoul = titan.GetTitanSoul()
+ if ( !IsValid( titanSoul ) )
+ return false
+
+ if ( !titanSoul.soul.seatedNpcPilot.isValid )
+ return false
+
+ return true
+}
+
+entity function NpcPilotGetPetTitan( entity pilot )
+{
+ Assert( !pilot.IsTitan() )
+ Assert( "petTitan" in pilot.s )
+
+ if ( !IsAlive( expect entity( pilot.s.petTitan ) ) )
+ return null
+
+ Assert( pilot.s.petTitan.IsTitan() )
+ return expect entity( pilot.s.petTitan )
+}
+
+void function NpcPilotSetPetTitan( entity pilot, entity titan )
+{
+ Assert( !pilot.IsTitan() )
+ Assert( titan.IsTitan() )
+ Assert( "petTitan" in pilot.s )
+
+ pilot.s.petTitan = titan
+ pilot.Signal( "PetTitanUpdated" )
+}
+#endif // NPC_TITAN_PILOT_PROTOTYPE
+
+function __TitanStanceThink( entity pilot, entity titan )
+{
+ if ( !IsAlive( titan ) )
+ return
+
+ if ( titan.GetTitanSoul().IsDoomed() )
+ return
+
+ titan.EndSignal( "OnDeath" )
+ titan.EndSignal( "OnDestroy" )
+ titan.EndSignal( "NpcPilotBecomesTitan" )
+
+ WaittillAnimDone( titan ) //wait for disembark anim
+
+ // kneel in certain circumstances
+ while ( IsAlive( pilot ) )
+ {
+ if ( !ChangedStance( titan ) )
+ waitthread TitanWaitsToChangeStance_or_PilotDeath( pilot, titan )
+ }
+
+ if ( titan.GetTitanSoul().GetStance() < STANCE_STANDING )
+ {
+ while ( !TitanCanStand( titan ) )
+ wait 2
+
+ TitanStandUp( titan )
+ }
+}
+
+function TitanWaitsToChangeStance_or_PilotDeath( pilot, titan )
+{
+ pilot.EndSignal( "OnDeath" )
+ pilot.EndSignal( "OnDestroy" )
+
+ TitanWaitsToChangeStance( titan )
+}
+
+/************************************************************************************************\
+
+######## ####### ####### ## ######
+ ## ## ## ## ## ## ## ##
+ ## ## ## ## ## ## ##
+ ## ## ## ## ## ## ######
+ ## ## ## ## ## ## ##
+ ## ## ## ## ## ## ## ##
+ ## ####### ####### ######## ######
+
+\************************************************************************************************/
+
+function __WaitforTitanCallinReady( entity pilot )
+{
+ pilot.EndSignal( "OnDeath" )
+ pilot.EndSignal( "OnDestroy" )
+
+ //HACK TODO: handle eTitanAvailability.Default vs custom and none, AND ALSO make a way to kill this thread
+
+ while ( true )
+ {
+ if ( pilot.s.nextTitanRespawnAvailable == NPC_NEXT_TITANTIME_RESET )
+ pilot.s.nextTitanRespawnAvailable = Time() + RandomFloatRange( NPC_NEXT_TITANTIME_MIN, NPC_NEXT_TITANTIME_MAX ) //this is just a random number - maybe in the future it will be based on the npc's kills...maybe also on the players if it's a slot
+
+ if ( pilot.s.nextTitanRespawnAvailable <= Time() )
+ break
+
+ float delay = max( pilot.s.nextTitanRespawnAvailable - Time(), 0.1 ) //make sure min delay of 0.1 to account for floating point error
+
+ thread SetSignalDelayed( pilot, "NpcTitanRespawnAvailableUpdated", delay )
+ pilot.WaitSignal( "NpcTitanRespawnAvailableUpdated" )
+
+ //keep looping backup just in case this value changes outside this function, we get an update
+ continue
+ }
+
+ Assert( Time() >= pilot.s.nextTitanRespawnAvailable )
+ Assert( pilot.s.nextTitanRespawnAvailable != NPC_NEXT_TITANTIME_RESET )
+}
+
+function __TitanKneelsForPilot( pilot, titan )
+{
+ expect entity( pilot )
+ expect entity( titan )
+
+ pilot.EndSignal( "OnDeath" )
+ pilot.EndSignal( "OnDestroy" )
+ titan.EndSignal( "OnDeath" )
+ titan.EndSignal( "OnDestroy" )
+
+ OnThreadEnd(
+ function () : ( pilot, titan )
+ {
+ if ( !IsAlive( titan ) )
+ return
+
+ SetStanceStand( titan.GetTitanSoul() )
+
+ //the pilot never made it to embark - lets stand our titan up so he can fight
+ if ( !IsAlive( pilot ) )
+ {
+ thread PlayAnimGravity( titan, "at_hotdrop_quickstand" )
+ HideName( titan )
+ titan.ContextAction_ClearBusy()
+ }
+ }
+ )
+
+ if ( !titan.ContextAction_IsBusy() ) //might be set from kneeling
+ titan.ContextAction_SetBusy()
+ SetStanceKneel( titan.GetTitanSoul() )
+
+ waitthread PlayAnimGravity( titan, "at_MP_stand2knee_straight" )
+ waitthread PlayAnim( titan, "at_MP_embark_idle" )
+}
+
+function HasEnemyRodeo( titan )
+{
+ expect entity( titan )
+
+ if ( !IsAlive( titan ) )
+ return false
+
+ if ( IsValid( GetEnemyRodeoPilot( titan ) ) )
+ return true
+
+ return false
+}
+
+function __TitanPilotRodeoCounter( entity titan )
+{
+ titan.EndSignal( "OnDeath" )
+ titan.EndSignal( "OnDestroy" )
+
+ while ( true )
+ {
+ while ( !HasEnemyRodeo( titan ) )
+ titan.GetTitanSoul().WaitSignal( "RodeoRiderChanged" )
+
+ wait RandomFloatRange( 3, 6 ) //give some time for debounce in case the rider jumps right off
+ if ( !HasEnemyRodeo( titan ) )
+ continue
+
+ #if NPC_TITAN_PILOT_PROTOTYPE
+ thread NpcPilotDisembarksTitan( titan )
+ return
+ #endif
+ }
+} \ No newline at end of file