1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
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<vector> pos = NavMesh_RandomPositions( generator.GetOrigin(), HULL_TITAN, 5, 250, 350 )
array<vector> pos = NavMesh_GetNeighborPositions( generator.GetOrigin(), HULL_TITAN, 5 )
pos = ArrayClosestVector( pos, titan.GetOrigin() )
array<vector> 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
}
}
|