untyped global function NukeTitanThink global function AutoTitan_SelfDestruct const NUKE_TITAN_PLAYER_DETECT_RANGE = 500 const NUKE_TITAN_RANGE_CHECK_SLEEP_SECS = 1.0 void function AutoTitan_SelfDestruct( entity titan ) { if ( titan.ContextAction_IsBusy() ) titan.ContextAction_ClearBusy() thread TitanEjectPlayer( titan ) } void function NukeTitanThink( entity titan, entity generator ) { //Function assumes that given Titan is spawned as npc_titan_ogre_meteor_nuke. Changing the Titan's AISettings post-spawn //disrupts the Titan's titanfall animations and can result in the Titan landing outside the level. NPC_SetNuclearPayload( titan ) AddEntityCallback_OnPostDamaged( titan, AutoTitan_NuclearPayload_PostDamageCallback ) WaitTillHotDropComplete( titan ) thread NukeTitanSeekOutGenerator( titan, generator ) } void function NukeTitanSeekOutGenerator( entity titan, entity generator ) { titan.EndSignal( "OnDeath" ) titan.EndSignal( "OnDestroy" ) titan.EndSignal( "Doomed" ) WaitSignal( titan, "FD_ReachedHarvester", "OnFailedToPath" ) float goalRadius = 100 float checkRadiusSqr = 400 * 400 //array pos = NavMesh_RandomPositions( generator.GetOrigin(), HULL_TITAN, 5, 250, 350 ) array pos = NavMesh_GetNeighborPositions( generator.GetOrigin(), HULL_TITAN, 5 ) pos = ArrayClosestVector( pos, titan.GetOrigin() ) array validPos foreach ( point in pos ) { if ( DistanceSqr( generator.GetOrigin(), point ) <= checkRadiusSqr && NavMesh_IsPosReachableForAI( titan, point ) ) { validPos.append( point ) //DebugDrawSphere( point, 32, 255, 0, 0, true, 60 ) } } int posLen = validPos.len() while( posLen >= 1 ) { titan.SetEnemy( generator ) thread AssaultOrigin( titan, validPos[0], goalRadius ) titan.AssaultSetFightRadius( goalRadius ) wait 0.5 if ( DistanceSqr( titan.GetOrigin(), generator.GetOrigin() ) > checkRadiusSqr ) continue break } thread AutoTitan_SelfDestruct( titan ) } // intercept damage to nuke titans in damage callback so we can nuke them before death 100% of the time void function AutoTitan_NuclearPayload_PostDamageCallback( entity titan, var damageInfo ) { if ( !IsAlive( titan ) ) return entity titanOwner = titan.GetBossPlayer() if ( IsValid( titanOwner ) ) { Assert( titanOwner.IsPlayer() ) Assert( GetPlayerTitanInMap( titanOwner ) == titan ) return } int nuclearPayload = NPC_GetNuclearPayload( titan ) if ( nuclearPayload == 0 ) return if ( !GetDoomedState( titan ) ) return if ( titan.GetTitanSoul().IsEjecting() ) return // Nuke eject as soon as the titan enters doom state. if ( !( "doomedStateNukeTriggerHealth" in titan.s ) ) { titan.s.doomedStateNukeTriggerHealth <- titan.GetMaxHealth() } if ( titan.GetHealth() > titan.s.doomedStateNukeTriggerHealth ) { //printt( "titan health:", titan.GetHealth(), "health to nuke:", titan.s.doomedStateNukeTriggerHealth ) return } printt( "NUKE TITAN DOOMED TRIGGER HEALTH REACHED, NUKING! Health:", titan.s.doomedStateNukeTriggerHealth ) thread AutoTitan_SelfDestruct( titan ) } function AutoTitan_CanDoRangeCheck( autoTitan ) { if ( !( "nextPlayerTitanRangeCheckTime" in autoTitan.s ) ) autoTitan.s.nextPlayerTitanRangeCheckTime <- -1 if ( Time() < autoTitan.s.nextPlayerTitanRangeCheckTime ) { return false } else { autoTitan.s.nextPlayerTitanRangeCheckTime = Time() + NUKE_TITAN_RANGE_CHECK_SLEEP_SECS return true } }