1156 lines
26 KiB
Plaintext
1156 lines
26 KiB
Plaintext
#include maps\mp\_utility;
|
|
#include common_scripts\utility;
|
|
|
|
/*******************************************************************
|
|
// _harrier.gsc
|
|
//
|
|
// Holds all the harrier specific functions
|
|
//
|
|
// Jordan Hirsh Dec. 18th 2008
|
|
********************************************************************/
|
|
|
|
beginHarrier( lifeId, startPoint, pos )
|
|
{
|
|
heightEnt = GetEnt( "airstrikeheight", "targetname" );
|
|
|
|
if ( isDefined( heightEnt ) )
|
|
trueHeight = heightEnt.origin[2];
|
|
else if( isDefined( level.airstrikeHeightScale ) )
|
|
trueHeight = 850 * level.airstrikeHeightScale;
|
|
else
|
|
trueHeight = 850;
|
|
|
|
pos *= (1,1,0);
|
|
pathGoal = pos + (0,0, trueHeight );
|
|
|
|
harrier = self spawnDefensiveHarrier( lifeId, self, startPoint, pathGoal );
|
|
harrier.pathGoal = pathGoal;
|
|
|
|
return harrier;
|
|
}
|
|
|
|
getCorrectHeight( x, y, rand )
|
|
{
|
|
offGroundHeight = 1200;
|
|
groundHeight = self traceGroundPoint(x,y);
|
|
trueHeight = groundHeight + offGroundHeight;
|
|
|
|
if( isDefined( level.airstrikeHeightScale ) && trueHeight < ( 850 * level.airstrikeHeightScale ) )
|
|
trueHeight = ( 950 * level.airstrikeHeightScale );
|
|
|
|
trueHeight += RandomInt( rand );
|
|
|
|
return trueHeight;
|
|
}
|
|
|
|
spawnDefensiveHarrier( lifeId, owner, pathStart, pathGoal )
|
|
{
|
|
forward = vectorToAngles( pathGoal - pathStart );
|
|
|
|
if ( owner.team == "allies" )
|
|
harrier = spawnHelicopter( owner, pathStart, forward, "harrier_mp" , "vehicle_av8b_harrier_jet_mp" );
|
|
else
|
|
harrier = spawnHelicopter( owner, pathStart, forward, "harrier_mp" , "vehicle_av8b_harrier_jet_opfor_mp" );
|
|
|
|
if ( !isDefined( harrier ) )
|
|
return;
|
|
|
|
harrier addToHeliList();
|
|
harrier thread removeFromHeliListOnDeath();
|
|
|
|
harrier.speed = 250;
|
|
harrier.accel = 175;
|
|
harrier.health = 3000;
|
|
harrier.maxhealth = harrier.health;
|
|
harrier.team = owner.team;
|
|
harrier.owner = owner;
|
|
harrier setCanDamage( true );
|
|
harrier.owner = owner;
|
|
harrier thread harrierDestroyed();
|
|
harrier SetMaxPitchRoll( 0, 90 );
|
|
harrier Vehicle_SetSpeed( harrier.speed, harrier.accel );
|
|
harrier thread playHarrierFx();
|
|
harrier setdamagestage( 3 );
|
|
harrier.missiles = 6;
|
|
harrier.pers["team"] = harrier.team;
|
|
harrier SetHoverParams( 50, 100, 50 );
|
|
harrier setTurningAbility( 0.05 );
|
|
harrier setYawSpeed(45,25,25,.5);
|
|
harrier.defendLoc = pathGoal;
|
|
harrier.lifeId = lifeId;
|
|
|
|
harrier.damageCallback = ::Callback_VehicleDamage;
|
|
|
|
level.harriers = remove_undefined_from_array( level.harriers );
|
|
|
|
level.harriers[level.harriers.size] = harrier;
|
|
|
|
return harrier;
|
|
}
|
|
|
|
defendLocation( harrier )
|
|
{
|
|
harrier endon( "death" );
|
|
assert ( isDefined( harrier ) );
|
|
|
|
harrier thread harrierTimer();
|
|
|
|
harrier setVehGoalPos( harrier.pathGoal, 1 );
|
|
harrier thread closeToGoalCheck( harrier.pathGoal );
|
|
|
|
harrier waittill ( "goal" );
|
|
harrier stopHarrierWingFx();
|
|
harrier engageGround();
|
|
}
|
|
|
|
closeToGoalCheck( pathGoal )
|
|
{
|
|
self endon( "goal" );
|
|
self endon( "death" );
|
|
|
|
for( ;; )
|
|
{
|
|
if ( distance2d( self.origin, pathGoal ) < 768 )
|
|
{
|
|
self SetMaxPitchRoll( 45, 25 );
|
|
break;
|
|
}
|
|
|
|
wait .05;
|
|
}
|
|
}
|
|
|
|
engageGround()
|
|
{
|
|
self notify( "engageGround" );
|
|
self endon("engageGround");
|
|
self endon("death");
|
|
|
|
self thread harrierGetTargets();
|
|
self thread randomHarrierMovement();
|
|
|
|
pathGoal = self.defendLoc;
|
|
|
|
self Vehicle_SetSpeed( 15, 5 );
|
|
self setVehGoalPos( pathGoal, 1 );
|
|
self waittill ( "goal" );
|
|
}
|
|
|
|
harrierLeave()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self SetMaxPitchRoll( 0, 0 );
|
|
self notify( "leaving" );
|
|
self breakTarget( true );
|
|
self notify("stopRand");
|
|
|
|
for ( ;; )
|
|
{
|
|
self Vehicle_SetSpeed( 35, 25 );
|
|
pathGoal = self.origin + ( vector_multiply( anglestoforward( (0,RandomInt(360),0) ), 500 ) );
|
|
pathGoal += ( 0,0,900);
|
|
|
|
leaveTrace = BulletTrace(self.origin, self.origin+(0,0,900), false, self );
|
|
if( leaveTrace["surfacetype"] == "none" )
|
|
break;
|
|
|
|
wait( 0.10 );
|
|
}
|
|
|
|
self setVehGoalPos( pathGoal, 1 );
|
|
self thread startHarrierWingFx();
|
|
self waittill ( "goal" );
|
|
self playSound( "harrier_fly_away" );
|
|
pathEnd = self getPathEnd();
|
|
self Vehicle_SetSpeed( 250, 75 );
|
|
self setVehGoalPos( pathEnd, 1 );
|
|
self waittill ( "goal" );
|
|
|
|
level.airPlane[level.airPlane.size - 1] = undefined;
|
|
|
|
self notify ( "harrier_gone" );
|
|
self thread harrierDelete();
|
|
}
|
|
|
|
|
|
harrierDelete()
|
|
{
|
|
self delete();
|
|
}
|
|
|
|
harrierTimer()
|
|
{
|
|
self endon( "death" );
|
|
|
|
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 45 );
|
|
self harrierLeave();
|
|
}
|
|
|
|
randomHarrierMovement()
|
|
{
|
|
self notify( "randomHarrierMovement" );
|
|
self endon("randomHarrierMovement");
|
|
|
|
self endon("stopRand");
|
|
self endon("death");
|
|
self endon( "acquiringTarget" );
|
|
self endon( "leaving" );
|
|
|
|
pos = self.defendloc;
|
|
|
|
for ( ;; )
|
|
{
|
|
newpos = self GetNewPoint(self.origin); //crazy blocking call
|
|
self setVehGoalPos( newpos, 1 );
|
|
self waittill ("goal");
|
|
wait( randomIntRange( 3, 6) );
|
|
self notify( "randMove" );
|
|
}
|
|
}
|
|
|
|
getNewPoint( pos, targ )
|
|
{
|
|
self endon("stopRand");
|
|
self endon("death");
|
|
self endon( "acquiringTarget" );
|
|
self endon( "leaving" );
|
|
|
|
if ( !isDefined( targ ) )
|
|
{
|
|
enemyPoints = [];
|
|
|
|
foreach( player in level.players )
|
|
{
|
|
if ( player == self )
|
|
continue;
|
|
|
|
if ( !level.teambased || player.team != self.team )
|
|
enemyPoints[enemyPoints.size] = player.origin;
|
|
}
|
|
|
|
if ( enemyPoints.size > 0 )
|
|
{
|
|
gotoPoint = AveragePoint( enemyPoints );
|
|
|
|
pointX = gotoPoint[0];
|
|
pointY = gotoPoint[1];
|
|
}
|
|
else
|
|
{
|
|
center = level.mapCenter;
|
|
movementDist = ( level.mapSize / 6 ) - 200;
|
|
|
|
pointX = RandomFloatRange( center[0]-movementDist, center[0]+movementDist );
|
|
pointY = RandomFloatRange( center[1]-movementDist, center[1]+movementDist );
|
|
}
|
|
|
|
newHeight = self getCorrectHeight( pointX, PointY, 20 );
|
|
}
|
|
else
|
|
{
|
|
if( coinToss() )
|
|
{
|
|
directVector = self.origin - self.bestTarget.origin;
|
|
pointX = directVector[0];
|
|
pointY = directVector[1] * -1;
|
|
newHeight = self getCorrectHeight( pointX, PointY, 20 );
|
|
perpendicularVector = ( pointY,pointX,newHeight );
|
|
|
|
if ( distance2D( self.origin, perpendicularVector ) > 1200 )
|
|
{
|
|
pointY *= .5;
|
|
pointX *= .5;
|
|
perpendicularVector = ( pointY,pointX,newHeight );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( distance2D( self.origin, self.bestTarget.origin ) < 200 )
|
|
return;
|
|
|
|
yaw = self.angles[1];
|
|
direction = (0,yaw,0);
|
|
moveToPoint = self.origin + vector_multiply( anglestoforward( direction ), randomIntRange( 200, 400 ) );
|
|
newHeight = self getCorrectHeight( moveToPoint[0], moveToPoint[1], 20 );
|
|
|
|
pointX = moveToPoint[0];
|
|
pointY = moveToPoint[1];
|
|
}
|
|
}
|
|
for ( ;; )
|
|
{
|
|
point = traceNewPoint( pointX, PointY, newHeight );
|
|
|
|
if ( point != 0 )
|
|
return point;
|
|
|
|
pointX = RandomFloatRange( pos[0]-1200, pos[0]+1200 );
|
|
pointY = RandomFloatRange( pos[1]-1200, pos[1]+1200 );
|
|
newHeight = self getCorrectHeight( pointX, PointY, 20 );
|
|
}
|
|
}
|
|
|
|
traceNewPoint(x,y,z)
|
|
{
|
|
self endon("stopRand");
|
|
self endon("death");
|
|
self endon( "acquiringTarget" );
|
|
self endon( "leaving" );
|
|
self endon( "randMove" );
|
|
|
|
for( i = 1 ; i <= 10 ; i++ )
|
|
{
|
|
|
|
switch( i )
|
|
{
|
|
case 1:
|
|
trc = BulletTrace( self.origin, (x,y,z), false, self );
|
|
break;
|
|
case 2:
|
|
trc = BulletTrace( (self getTagOrigin( "tag_left_wingtip" )), (x,y,z), false, self );
|
|
//self thread drawLine( (self getTagOrigin( "tag_left_wingtip" )), (x,y,z), 4 );
|
|
break;
|
|
case 3:
|
|
trc = BulletTrace( (self getTagOrigin( "tag_right_wingtip" )), (x,y,z), false, self );
|
|
//self thread drawLine( (self getTagOrigin( "tag_right_wingtip" )), (x,y,z), 4 );
|
|
break;
|
|
case 4:
|
|
trc = BulletTrace( (self getTagOrigin( "tag_engine_left2" )), (x,y,z), false, self );
|
|
//self thread drawLine( (self getTagOrigin( "tag_engine_left2" )), (x,y,z), 4 );
|
|
break;
|
|
case 5:
|
|
trc = BulletTrace( (self getTagOrigin( "tag_engine_right2" )), (x,y,z), false, self );
|
|
//self thread drawLine( (self getTagOrigin( "tag_engine_right2" )), (x,y,z), 4 );
|
|
break;
|
|
case 6:
|
|
trc = BulletTrace( (self getTagOrigin( "tag_right_alamo_missile" )), (x,y,z), false, self );
|
|
//self thread drawLine( (self getTagOrigin( "tag_right_alamo_missile" )), (x,y,z), 4 );
|
|
break;
|
|
case 7:
|
|
trc = BulletTrace( (self getTagOrigin( "tag_left_alamo_missile" )), (x,y,z), false, self );
|
|
//self thread drawLine( (self getTagOrigin( "tag_left_alamo_missile" )), (x,y,z), 4 );
|
|
break;
|
|
case 8:
|
|
trc = BulletTrace( (self getTagOrigin( "tag_right_archer_missile" )), (x,y,z), false, self );
|
|
//self thread drawLine( (self getTagOrigin( "tag_right_archer_missile" )), (x,y,z), 4 );
|
|
break;
|
|
case 9:
|
|
trc = BulletTrace( (self getTagOrigin( "tag_left_archer_missile" )), (x,y,z), false, self );
|
|
//self thread drawLine( (self getTagOrigin( "tag_left_archer_missile" )), (x,y,z), 4 );
|
|
break;
|
|
case 10:
|
|
trc = BulletTrace( (self getTagOrigin( "tag_light_tail" )), (x,y,z), false, self );
|
|
//self thread drawLine( (self getTagOrigin( "tag_light_tail" )), (x,y,z), 4 );
|
|
break;
|
|
default:
|
|
trc = BulletTrace( self.origin, (x,y,z), false, self );
|
|
}
|
|
|
|
if ( trc["surfacetype"] != "none" )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
wait(0.05);
|
|
}
|
|
|
|
pathGoal = ( x, y, z );
|
|
return pathGoal;
|
|
}
|
|
|
|
|
|
traceGroundPoint(x,y)
|
|
{
|
|
self endon("death");
|
|
self endon( "acquiringTarget" );
|
|
self endon( "leaving" );
|
|
|
|
highTrace = -9999999;
|
|
lowTrace = 9999999;
|
|
z = -9999999;
|
|
highz = self.origin[2];
|
|
trace = undefined;
|
|
lTrace = undefined;
|
|
|
|
for( i = 1 ; i <= 5 ; i++ )
|
|
{
|
|
|
|
switch( i )
|
|
{
|
|
case 1:
|
|
trc = BulletTrace( (x,y,highz), (x,y,z), false, self );
|
|
//self thread drawLine( ( x,y,highz ), (x,y,z), 4 );
|
|
break;
|
|
case 2:
|
|
trc = BulletTrace( (x+20,y+20,highz), (x+20,y+20,z), false, self );
|
|
//self thread drawLine( ( x+20,y+20,highz ), (x+20,y+20,z), 4 );
|
|
break;
|
|
case 3:
|
|
trc = BulletTrace( (x-20,y-20,highz), (x-20,y-20,z), false, self );
|
|
//self thread drawLine( ( x-20,y-20,highz ), (x-20,y-20,z), 4 );
|
|
break;
|
|
case 4:
|
|
trc = BulletTrace( (x+20,y-20,highz), (x+20,y-20,z), false, self );
|
|
//self thread drawLine( ( x+20,y-20,highz ), (x+20,y-20,z), 4 );
|
|
break;
|
|
case 5:
|
|
trc = BulletTrace( (x-20,y+20,highz), (x-20,y+20,z), false, self );
|
|
//self thread drawLine( ( x-20,y+20,highz ), (x-20,y+20,z), 4 );
|
|
break;
|
|
default:
|
|
trc = BulletTrace( self.origin, (x,y,z), false, self );
|
|
}
|
|
|
|
if ( trc["position"][2] > highTrace )
|
|
{
|
|
highTrace = trc["position"][2];
|
|
trace = trc;
|
|
}
|
|
else if ( trc["position"][2] < lowTrace )
|
|
{
|
|
lowTrace = trc["position"][2];
|
|
lTrace = trc;
|
|
}
|
|
|
|
wait(0.05);
|
|
}
|
|
|
|
//thread drawLine( self.origin, lTrace["position"], 5, (0,1,0) );
|
|
//thread drawLine( self.origin, trace["position"], 5, (1,0,0) );
|
|
|
|
return highTrace;
|
|
}
|
|
|
|
|
|
playHarrierFx()
|
|
{
|
|
self endon ( "death" );
|
|
|
|
wait( 0.2 );
|
|
playfxontag( level.fx_airstrike_contrail, self, "tag_right_wingtip" );
|
|
playfxontag( level.fx_airstrike_contrail, self, "tag_left_wingtip" );
|
|
wait( 0.2 );
|
|
playfxontag( level.harrier_afterburnerfx, self, "tag_engine_right" );
|
|
playfxontag( level.harrier_afterburnerfx, self, "tag_engine_left" );
|
|
wait( 0.2 );
|
|
playfxontag( level.harrier_afterburnerfx, self, "tag_engine_right2" );
|
|
playfxontag( level.harrier_afterburnerfx, self, "tag_engine_left2" );
|
|
wait( 0.2 );
|
|
playFXOnTag( level.chopper_fx["light"]["left"], self, "tag_light_L_wing" );
|
|
wait ( 0.2 );
|
|
playFXOnTag( level.chopper_fx["light"]["right"], self, "tag_light_R_wing" );
|
|
wait ( 0.2 );
|
|
playFXOnTag( level.chopper_fx["light"]["belly"], self, "tag_light_belly" );
|
|
wait ( 0.2 );
|
|
playFXOnTag( level.chopper_fx["light"]["tail"], self, "tag_light_tail" );
|
|
|
|
}
|
|
|
|
stopHarrierWingFx()
|
|
{
|
|
stopfxontag( level.fx_airstrike_contrail, self, "tag_right_wingtip" );
|
|
stopfxontag( level.fx_airstrike_contrail, self, "tag_left_wingtip" );
|
|
}
|
|
|
|
startHarrierWingFx()
|
|
{
|
|
wait ( 3.0);
|
|
|
|
if ( !isDefined( self ) )
|
|
return;
|
|
|
|
playfxontag( level.fx_airstrike_contrail, self, "tag_right_wingtip" );
|
|
playfxontag( level.fx_airstrike_contrail, self, "tag_left_wingtip" );
|
|
}
|
|
|
|
getPathStart( coord )
|
|
{
|
|
pathRandomness = 100;
|
|
harrierHalfDistance = 15000;
|
|
harrierFlyHeight = 850;
|
|
|
|
yaw = randomFloat( 360 );
|
|
direction = (0,yaw,0);
|
|
|
|
startPoint = coord + vector_multiply( anglestoforward( direction ), -1 * harrierHalfDistance );
|
|
startPoint += ( (randomfloat(2) - 1)*pathRandomness, (randomfloat(2) - 1)*pathRandomness, 0 );
|
|
|
|
return startPoint;
|
|
}
|
|
|
|
getPathEnd()
|
|
{
|
|
pathRandomness = 150;
|
|
harrierHalfDistance = 15000;
|
|
harrierFlyHeight = 850;
|
|
|
|
yaw = self.angles[1];
|
|
direction = (0,yaw,0);
|
|
|
|
endPoint = self.origin + vector_multiply( anglestoforward( direction ), harrierHalfDistance );
|
|
return endPoint;
|
|
}
|
|
|
|
fireOnTarget( facingTolerance, zOffset )
|
|
{
|
|
self endon("leaving");
|
|
self endon("stopfiring");
|
|
self endon("explode");
|
|
self endon("death");
|
|
self.bestTarget endon( "death" );
|
|
|
|
acquiredTime = getTime();
|
|
missileTime = getTime();
|
|
missileReady = false;
|
|
|
|
self setVehWeapon( "harrier_20mm_mp" );
|
|
|
|
if ( !isDefined( zOffset ) )
|
|
zOffset = 50;
|
|
|
|
for ( ;; )
|
|
{
|
|
if ( self isReadyToFire( facingTolerance ) )
|
|
break;
|
|
else
|
|
wait ( .25 );
|
|
}
|
|
self SetTurretTargetEnt( self.bestTarget, ( 0,0,50 ) );
|
|
|
|
numShots = 25;
|
|
|
|
for ( ;; )
|
|
{
|
|
if ( numShots == 25 )
|
|
self playLoopSound( "weap_cobra_20mm_fire_npc" );
|
|
|
|
numShots--;
|
|
self FireWeapon( "tag_flash", self.bestTarget, (0,0,0), .05 );
|
|
wait ( .10);
|
|
|
|
if ( numShots <= 0 )
|
|
{
|
|
self stopLoopSound();
|
|
wait (1);
|
|
numShots = 25;
|
|
}
|
|
}
|
|
}
|
|
|
|
isReadyToFire( tolerance )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
|
|
if (! isdefined(tolerance) )
|
|
tolerance = 10;
|
|
|
|
harrierForwardVector = anglesToForward( self.angles );
|
|
harrierToTarget = self.bestTarget.origin - self.origin;
|
|
harrierForwardVector *= (1,1,0);
|
|
harrierToTarget *= (1,1,0 );
|
|
|
|
harrierToTarget = VectorNormalize( harrierToTarget );
|
|
harrierForwardVector = VectorNormalize( harrierForwardVector );
|
|
|
|
targetCosine = VectorDot( harrierToTarget, harrierForwardVector );
|
|
facingCosine = Cos( tolerance );
|
|
|
|
if ( targetCosine >= facingCosine )
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
acquireGroundTarget( targets )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
|
|
if ( targets.size == 1 )
|
|
self.bestTarget = targets[0];
|
|
else
|
|
self.bestTarget = self getBestTarget( targets );
|
|
|
|
self backToDefendLocation( false );
|
|
|
|
self notify( "acquiringTarget" );
|
|
|
|
self SetTurretTargetEnt( self.bestTarget );
|
|
self SetLookAtEnt( self.bestTarget );
|
|
|
|
newpos = self GetNewPoint(self.origin, true);
|
|
self setVehGoalPos( newpos, 1 );
|
|
|
|
self thread watchTargetDeath();
|
|
self thread watchTargetLOS();
|
|
|
|
self setVehWeapon( "harrier_20mm_mp" );
|
|
self thread fireOnTarget(); // fires on current target.
|
|
}
|
|
|
|
backToDefendLocation( forced )
|
|
{
|
|
self setVehGoalPos( self.defendloc, 1 );
|
|
|
|
if ( isDefined( forced ) && forced )
|
|
self waittill ( "goal" );
|
|
}
|
|
|
|
|
|
wouldCollide( destination )
|
|
{
|
|
trace = BulletTrace( self.origin, destination, true, self );
|
|
|
|
if ( trace["position"] == destination )
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
watchTargetDeath()
|
|
{
|
|
self notify( "watchTargetDeath" );
|
|
self endon( "watchTargetDeath" );
|
|
self endon( "newTarget" );
|
|
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
|
|
self.bestTarget waittill( "death" );
|
|
self thread breakTarget();
|
|
}
|
|
|
|
watchTargetLOS( tolerance )
|
|
{
|
|
self endon( "death" );
|
|
self.bestTarget endon( "death" );
|
|
self.bestTarget endon( "disconnect" );
|
|
self endon( "leaving" );
|
|
self endon( "newTarget" );
|
|
lostTime = undefined;
|
|
|
|
if ( !isDefined( tolerance ) )
|
|
tolerance = 1000;
|
|
|
|
for ( ;; )
|
|
{
|
|
if ( !isTarget( self.bestTarget ) )
|
|
{
|
|
self thread breakTarget();
|
|
return;
|
|
}
|
|
|
|
if ( !isDefined( self.bestTarget ) )//hack to cover host migration vehicle targets
|
|
{
|
|
self thread breakTarget();
|
|
return;
|
|
}
|
|
|
|
if ( self.bestTarget sightConeTrace( self.origin, self ) < 1 )
|
|
{
|
|
if ( !isDefined(lostTime) )
|
|
lostTime = getTime();
|
|
|
|
if ( getTime() - lostTime > tolerance )
|
|
{
|
|
self thread breakTarget();
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lostTime = undefined;
|
|
}
|
|
|
|
wait( .25 );
|
|
}
|
|
}
|
|
|
|
breakTarget( noNewTarget )
|
|
{
|
|
self endon( "death" );
|
|
|
|
self ClearLookAtEnt();
|
|
self stopLoopSound();
|
|
self notify("stopfiring");
|
|
|
|
if ( isDefined(noNewTarget) && noNewTarget )
|
|
return;
|
|
|
|
self thread randomHarrierMovement();
|
|
self notify( "newTarget" );
|
|
self thread harrierGetTargets();
|
|
|
|
}
|
|
|
|
harrierGetTargets()
|
|
{
|
|
self notify( "harrierGetTargets" );
|
|
self endon("harrierGetTargets");
|
|
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
targets = [];
|
|
|
|
for ( ;; )
|
|
{
|
|
targets = [];
|
|
players = level.players;
|
|
|
|
if ( isDefined( level.chopper ) && level.chopper.team != self.team && isAlive( level.chopper ) )
|
|
{
|
|
if ( !isDefined( level.chopper.nonTarget ) || ( isDefined( level.chopper.nonTarget ) && !level.chopper.nonTarget ) )
|
|
{
|
|
self thread engageVehicle( level.chopper );
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
backToDefendLocation( true );
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < players.size; i++)
|
|
{
|
|
potentialTarget = players[i];
|
|
if ( isTarget( potentialTarget ) )
|
|
{
|
|
if( isdefined( players[i] ) )
|
|
targets[targets.size] = players[i];
|
|
}
|
|
else
|
|
continue;
|
|
|
|
wait( .05 );
|
|
}
|
|
if ( targets.size > 0 )
|
|
{
|
|
self acquireGroundTarget( targets );
|
|
return;
|
|
}
|
|
wait( 1 );
|
|
}
|
|
}
|
|
|
|
isTarget( potentialTarget )
|
|
{
|
|
self endon( "death" );
|
|
|
|
if ( !isalive( potentialTarget ) || potentialTarget.sessionstate != "playing" )
|
|
return false;
|
|
|
|
if ( isDefined( self.owner ) && potentialTarget == self.owner )
|
|
return false;
|
|
|
|
if ( distance( potentialTarget.origin, self.origin ) > 8192 )
|
|
return false;
|
|
|
|
if ( Distance2D( potentialTarget.origin , self.origin ) < 768 )
|
|
return false;
|
|
|
|
if ( !isdefined( potentialTarget.pers["team"] ) )
|
|
return false;
|
|
|
|
if ( level.teamBased && potentialTarget.pers["team"] == self.team )
|
|
return false;
|
|
|
|
if ( potentialTarget.pers["team"] == "spectator" )
|
|
return false;
|
|
|
|
if ( isdefined( potentialTarget.spawntime ) && ( gettime() - potentialTarget.spawntime )/1000 <= 5 )
|
|
return false;
|
|
|
|
if ( potentialTarget _hasPerk( "specialty_coldblooded" ) )
|
|
return false;
|
|
|
|
harrier_centroid = self.origin + ( 0, 0, -160 );
|
|
harrier_forward_norm = anglestoforward( self.angles );
|
|
harrier_turret_point = harrier_centroid + 144 * harrier_forward_norm;
|
|
harrier_canSeeTarget = potentialTarget sightConeTrace( self.origin, self );
|
|
|
|
if ( harrier_canSeeTarget < 1 )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
getBestTarget( targets )
|
|
{
|
|
self endon( "death" );
|
|
mainGunPointOrigin = self getTagOrigin( "tag_flash" );
|
|
harrierOrigin = self.origin;
|
|
harrier_forward_norm = anglestoforward( self.angles );
|
|
|
|
bestYaw = undefined;
|
|
bestTarget = undefined;
|
|
targetHasRocket = false;
|
|
|
|
foreach ( targ in targets )
|
|
{
|
|
angle = abs ( vectorToAngles ( ( targ.origin - self.origin ) )[1] );
|
|
noseAngle = abs( self getTagAngles( "tag_flash" )[1] );
|
|
angle = abs ( angle - noseAngle );
|
|
|
|
// in this calculation having a rocket removes 40d of rotation cost from best target calculation
|
|
// to prioritize targeting dangerous targets.
|
|
weaponsArray = targ GetWeaponsListItems();
|
|
foreach ( weapon in weaponsArray )
|
|
{
|
|
if ( isSubStr( weapon, "at4" ) || isSubStr( weapon, "stinger" ) || isSubStr( weapon, "jav" ) )
|
|
angle -= 40;
|
|
}
|
|
|
|
if ( Distance( self.origin, targ.origin ) > 2000 )
|
|
angle += 40;
|
|
|
|
if ( !isDefined( bestYaw ) )
|
|
{
|
|
bestYaw = angle;
|
|
bestTarget = targ;
|
|
}
|
|
else if ( bestYaw > angle )
|
|
{
|
|
bestYaw = angle;
|
|
bestTarget = targ;
|
|
}
|
|
}
|
|
|
|
return ( bestTarget );
|
|
}
|
|
|
|
fireMissile( missileTarget )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
|
|
assert( self.health > 0 );
|
|
|
|
if ( self.missiles <= 0 )
|
|
return;
|
|
|
|
friendlyInRadius = self checkForFriendlies( missileTarget, 256 );
|
|
|
|
if ( !isdefined( missileTarget ) )
|
|
return;
|
|
|
|
if ( Distance2D(self.origin, missileTarget.origin ) < 512 )
|
|
return;
|
|
|
|
if ( isDefined ( friendlyInRadius ) && friendlyInRadius )
|
|
return;
|
|
|
|
self.missiles--;
|
|
self setVehWeapon( "harrier_FFAR_mp" );
|
|
|
|
if ( isDefined( missileTarget.targetEnt ) )
|
|
missile = self fireWeapon( "tag_flash", missileTarget.targetEnt, (0,0,-250) );
|
|
else
|
|
missile = self fireWeapon( "tag_flash", missileTarget, (0,0,-250) );
|
|
|
|
missile Missile_SetFlightmodeDirect();
|
|
missile Missile_SetTargetEnt( missileTarget );
|
|
}
|
|
|
|
checkForFriendlies( missileTarget, radiusSize )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
|
|
targets = [];
|
|
players = level.players;
|
|
strikePosition = missileTarget.origin;
|
|
|
|
for (i = 0; i < players.size; i++)
|
|
{
|
|
potentialCollateral = players[i];
|
|
|
|
if ( potentialCollateral.team != self.team )
|
|
continue;
|
|
|
|
potentialPosition = potentialCollateral.origin;
|
|
|
|
if ( distance2D( potentialPosition, strikePosition ) < 512 )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
///-------------------------------------------------------
|
|
//
|
|
// Health Functions
|
|
//
|
|
///------------------------------------------------------
|
|
|
|
|
|
Callback_VehicleDamage( inflictor, attacker, damage, dFlags, meansOfDeath, weapon, point, dir, hitLoc, timeOffset, modelIndex, partName )
|
|
{
|
|
if ( ( attacker == self || ( isDefined( attacker.pers ) && attacker.pers["team"] == self.team ) && level.teamBased ) && ( attacker != self.owner ) )
|
|
return;
|
|
|
|
if ( self.health <= 0 )
|
|
return;
|
|
|
|
switch ( weapon )
|
|
{
|
|
case "ac130_105mm_mp":
|
|
case "ac130_40mm_mp":
|
|
case "stinger_mp":
|
|
case "javelin_mp":
|
|
case "remotemissile_projectile_mp":
|
|
self.largeProjectileDamage = true;
|
|
damage = self.maxhealth + 1;
|
|
break;
|
|
case "rpg_mp":
|
|
case "at4_mp":
|
|
self.largeProjectileDamage = true;
|
|
damage = self.maxhealth - 900;
|
|
break;
|
|
default:
|
|
if ( weapon != "none" )
|
|
damage = Int(damage/2);
|
|
self.largeProjectileDamage = false;
|
|
break;
|
|
}
|
|
|
|
attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "" );
|
|
|
|
if ( isPlayer( attacker ) && attacker _hasPerk( "specialty_armorpiercing" ) )
|
|
{
|
|
damageAdd = int( damage*level.armorPiercingMod );
|
|
damage += damageAdd;
|
|
}
|
|
|
|
if ( self.health <= damage )
|
|
{
|
|
if ( isPlayer( attacker ) && (!isDefined(self.owner) || attacker != self.owner) )
|
|
{
|
|
thread teamPlayerCardSplash( "callout_destroyed_harrier", attacker );
|
|
attacker thread maps\mp\gametypes\_rank::giveRankXP( "kill", 300 );
|
|
thread maps\mp\gametypes\_missions::vehicleKilled( self.owner, self, undefined, attacker, damage, meansOfDeath );
|
|
attacker notify( "destroyed_killstreak" );
|
|
}
|
|
|
|
self notify("death");
|
|
}
|
|
|
|
if ( self.health - damage <= 900 && ( !isDefined( self.smoking ) || !self.smoking ) )
|
|
{
|
|
self thread playDamageEfx();
|
|
self.smoking = true;
|
|
}
|
|
|
|
self Vehicle_FinishDamage( inflictor, attacker, damage, dFlags, meansOfDeath, weapon, point, dir, hitLoc, timeOffset, modelIndex, partName );
|
|
}
|
|
|
|
playDamageEfx()
|
|
{
|
|
self endon( "death" );
|
|
|
|
stopFxOnTag( level.harrier_afterburnerfx, self, "tag_engine_left" );
|
|
playFxOnTag( level.harrier_smoke, self, "tag_engine_left" );
|
|
|
|
stopFxOnTag( level.harrier_afterburnerfx, self, "tag_engine_right" );
|
|
playFxOnTag( level.harrier_smoke, self, "tag_engine_right" );
|
|
wait( 0.15 );
|
|
|
|
stopFxOnTag( level.harrier_afterburnerfx, self, "tag_engine_left2" );
|
|
playFxontag( level.harrier_smoke, self, "tag_engine_left2" );
|
|
|
|
stopFxOnTag( level.harrier_afterburnerfx, self, "tag_engine_right2" );
|
|
playFxontag( level.harrier_smoke, self, "tag_engine_right2" );
|
|
playFxOnTag( level.chopper_fx["damage"]["heavy_smoke"], self, "tag_engine_left" );
|
|
}
|
|
|
|
harrierDestroyed()
|
|
{
|
|
self endon( "harrier_gone" );
|
|
|
|
self waittill( "death" );
|
|
|
|
if (! isDefined(self) )
|
|
return;
|
|
|
|
if ( !isDefined( self.largeProjectileDamage ) )
|
|
{
|
|
self Vehicle_SetSpeed( 25, 5 );
|
|
self thread harrierSpin( RandomIntRange(180, 220) );
|
|
|
|
wait( RandomFloatRange( .5, 1.5 ) );
|
|
}
|
|
|
|
harrierExplode();
|
|
}
|
|
|
|
// crash explosion
|
|
harrierExplode()
|
|
{
|
|
self playSound( "harrier_jet_crash" );
|
|
level.airPlane[level.airPlane.size - 1] = undefined;
|
|
|
|
deathAngles = self getTagAngles( "tag_deathfx" );
|
|
playFx( level.harrier_deathfx, self getTagOrigin( "tag_deathfx" ), anglesToForward( deathAngles ), anglesToUp( deathAngles ) );
|
|
|
|
self notify ( "explode" );
|
|
|
|
wait ( 0.05 );
|
|
|
|
self thread harrierDelete();
|
|
}
|
|
|
|
|
|
harrierSpin( speed )
|
|
{
|
|
self endon( "explode" );
|
|
|
|
playfxontag( level.chopper_fx["explode"]["medium"], self, "tag_origin" );
|
|
|
|
self setyawspeed( speed, speed, speed );
|
|
while ( isdefined( self ) )
|
|
{
|
|
self settargetyaw( self.angles[1]+(speed*0.9) );
|
|
wait ( 1 );
|
|
}
|
|
}
|
|
|
|
engageVehicle( vehTarget )
|
|
{
|
|
vehTarget endon("death");
|
|
vehTarget endon("leaving");
|
|
vehTarget endon("crashing");
|
|
self endon("death");
|
|
|
|
self acquireVehicleTarget( vehTarget );
|
|
|
|
self thread fireOnVehicleTarget();
|
|
}
|
|
|
|
fireOnVehicleTarget()
|
|
{
|
|
self endon("leaving");
|
|
self endon("stopfiring");
|
|
self endon("explode");
|
|
self.bestTarget endon ("crashing");
|
|
self.bestTarget endon ("leaving");
|
|
self.bestTarget endon ("death");
|
|
|
|
acquiredTime = getTime();
|
|
|
|
if ( isDefined( self.bestTarget ) && self.bestTarget.classname == "script_vehicle" )
|
|
{
|
|
self SetTurretTargetEnt( self.bestTarget );
|
|
|
|
for ( ;; )
|
|
{
|
|
curDist = distance2D( self.origin, self.bestTarget.origin );
|
|
|
|
if ( getTime() - acquiredTime > 2500 && curDist > 1000 )
|
|
{
|
|
self fireMissile( self.bestTarget );
|
|
acquiredTime = getTime();
|
|
}
|
|
|
|
wait ( .10);
|
|
}
|
|
}
|
|
}
|
|
|
|
acquireVehicleTarget( vehTarget )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
|
|
self notify( "newTarget" );
|
|
self.bestTarget = vehTarget;
|
|
self notify( "acquiringVehTarget" );
|
|
self SetLookAtEnt( self.bestTarget );
|
|
self thread watchVehTargetDeath();
|
|
self thread watchVehTargetCrash();
|
|
|
|
self SetTurretTargetEnt( self.bestTarget );
|
|
}
|
|
|
|
watchVehTargetCrash()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
self.bestTarget endon ( "death" );
|
|
self.bestTarget endon ( "drop_crate" );
|
|
|
|
self.bestTarget waittill( "crashing" );
|
|
self breakVehTarget();
|
|
}
|
|
|
|
watchVehTargetDeath()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
self.bestTarget endon ( "crashing" );
|
|
self.bestTarget endon ( "drop_crate" );
|
|
|
|
self.bestTarget waittill( "death" );
|
|
breakVehTarget();
|
|
}
|
|
|
|
breakVehTarget()
|
|
{
|
|
self ClearLookAtEnt();
|
|
|
|
if ( isDefined( self.bestTarget ) && !isDefined( self.bestTarget.nonTarget ) )
|
|
self.bestTarget.nonTarget = true;
|
|
|
|
self notify("stopfiring");
|
|
self notify( "newTarget" );
|
|
self thread stopHarrierWingFx();
|
|
self thread engageGround();
|
|
}
|
|
|
|
evasiveManuverOne()
|
|
{
|
|
self SetMaxPitchRoll( 15, 80);
|
|
self Vehicle_SetSpeed( 50, 100 );
|
|
self setYawSpeed(90,30,30,.5);
|
|
|
|
curOrg = self.origin;
|
|
|
|
yaw = self.angles[1];
|
|
if( cointoss() )
|
|
direction = (0,yaw+90,0);
|
|
else
|
|
direction = (0,yaw-90,0);
|
|
|
|
moveToPoint = self.origin + vector_multiply( anglestoforward( direction ), 500 );
|
|
|
|
self setVehGoalPos( moveToPoint, 1 );
|
|
//println( "evasive manuver one" );
|
|
self waittill ("goal");
|
|
}
|
|
|
|
drawLine( start, end, timeSlice, color )
|
|
{
|
|
if( !isdefined( color ) )
|
|
color = ( 1,1,1 );
|
|
|
|
drawTime = int(timeSlice * 20);
|
|
for( time = 0; time < drawTime; time++ )
|
|
{
|
|
line( start, end, color,false, 1 );
|
|
wait ( 0.05 );
|
|
}
|
|
}
|
|
|
|
addToHeliList()
|
|
{
|
|
level.helis[self getEntityNumber()] = self;
|
|
}
|
|
|
|
removeFromHeliListOnDeath()
|
|
{
|
|
entityNumber = self getEntityNumber();
|
|
|
|
self waittill ( "death" );
|
|
|
|
level.helis[entityNumber] = undefined;
|
|
}
|