482 lines
12 KiB
Plaintext
482 lines
12 KiB
Plaintext
#include maps\mp\_utility;
|
|
#include common_scripts\utility;
|
|
|
|
init()
|
|
{
|
|
precacheVehicle( "attack_littlebird_mp" );
|
|
|
|
//precacheString( &"HELICOPTER_FLOCK_INBOUND" );
|
|
|
|
precacheModel( "vehicle_apache_mp" );
|
|
precacheModel( "vehicle_apache_mg" );
|
|
precacheTurret( "apache_minigun_mp" );
|
|
precacheVehicle( "apache_strafe_mp" );
|
|
|
|
level.killStreakFuncs["littlebird_flock"] = ::tryUseLbFlock;
|
|
|
|
level.heli_flock = [];
|
|
}
|
|
|
|
|
|
tryUseLbFlock( lifeId, streakName )
|
|
{
|
|
numIncomingVehicles = 5;
|
|
if( heliFlockActive() || currentActiveVehicleCount() >= maxVehiclesAllowed() || level.fauxVehicleCount + numIncomingVehicles >= maxVehiclesAllowed() )
|
|
{
|
|
self iPrintLnBold( &"KILLSTREAKS_TOO_MANY_VEHICLES" );
|
|
return false;
|
|
}
|
|
|
|
// increment the faux vehicle count before we spawn the vehicle so no other vehicles try to spawn
|
|
incrementFauxVehicleCount();
|
|
incrementFauxVehicleCount();
|
|
incrementFauxVehicleCount();
|
|
incrementFauxVehicleCount();
|
|
incrementFauxVehicleCount();
|
|
|
|
result = self selectLbStrikeLocation( lifeId, "littlebird_flock" );
|
|
if ( ( !isDefined( result ) || !result ) )
|
|
{
|
|
// decrement the faux vehicle count since this failed to spawn
|
|
decrementFauxVehicleCount();
|
|
decrementFauxVehicleCount();
|
|
decrementFauxVehicleCount();
|
|
decrementFauxVehicleCount();
|
|
decrementFauxVehicleCount();
|
|
|
|
return false;
|
|
}
|
|
|
|
level thread teamPlayerCardSplash( "used_littlebird_flock", self, self.team );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
heliFlockActive()
|
|
{
|
|
result = false;
|
|
for ( i=0; i<level.heli_flock.size; i++ )
|
|
{
|
|
if ( isDefined( level.heli_flock[i] ) )
|
|
{
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
selectLbStrikeLocation( lifeId, streakName )
|
|
{
|
|
self PlayLocalSound( game[ "voice" ][ self.team ] + "KS_lbd_inposition" );
|
|
self _beginLocationSelection( streakName, "map_artillery_selector", true, 500 );
|
|
self endon( "stop_location_selection" );
|
|
|
|
// wait for the selection
|
|
self waittill( "confirm_location", location, locationYaw);
|
|
|
|
if( heliFlockActive() || currentActiveVehicleCount() >= maxVehiclesAllowed() || level.fauxVehicleCount >= maxVehiclesAllowed() )
|
|
{
|
|
self iPrintLnBold( &"KILLSTREAKS_TOO_MANY_VEHICLES" );
|
|
self notify( "cancel_location" );
|
|
return false;
|
|
}
|
|
|
|
level.heli_flock = [];
|
|
level.heli_flock_victims = [];
|
|
|
|
self thread littlebirdMadeSelectionVO();
|
|
self thread finishLbStrikeUsage( lifeId, location, ::callStrike, locationYaw );
|
|
|
|
self setblurforplayer( 0, 0.3 );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
littlebirdMadeSelectionVO()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "disconnect" );
|
|
|
|
self PlayLocalSound( game[ "voice" ][ self.team ] + "KS_hqr_littlebird" );
|
|
wait( 3.0 );
|
|
self PlayLocalSound( game[ "voice" ][ self.team ] + "KS_lbd_inbound" );
|
|
}
|
|
|
|
|
|
finishLbStrikeUsage( lifeId, location, usedCallback, locationYaw )
|
|
{
|
|
self notify( "used" );
|
|
|
|
wait ( 0.05 );
|
|
self thread stopLocationSelection( false );
|
|
|
|
if ( isDefined( self ) )
|
|
self thread [[usedCallback]]( lifeId, location, locationYaw );
|
|
}
|
|
|
|
|
|
callStrike( lifeId, location, locationYaw )
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon("disconnect");
|
|
|
|
self thread handleOwnerLeft();
|
|
|
|
// get flight paths all at once
|
|
flightPath1 = getFlightPath( location, locationYaw, 0 );
|
|
flightPath2 = getFlightPath( location, locationYaw, -520 );
|
|
flightPath3 = getFlightPath( location, locationYaw, 520 );
|
|
flightPath4 = getFlightPath( location, locationYaw, -1040 );
|
|
flightPath5 = getFlightPath( location, locationYaw, 1040 );
|
|
|
|
// leader
|
|
level thread doLbStrike( lifeId, self, flightPath1, 0 );
|
|
|
|
wait( 0.3 );
|
|
|
|
// left wingman
|
|
level thread doLbStrike( lifeId, self, flightPath2, 1 );
|
|
|
|
// right wingman
|
|
level thread doLbStrike( lifeId, self, flightPath3, 2 );
|
|
|
|
wait( 0.3 );
|
|
|
|
// left wingman
|
|
level thread doLbStrike( lifeId, self, flightPath4, 3 );
|
|
|
|
// right wingman
|
|
level thread doLbStrike( lifeId, self, flightPath5, 4 );
|
|
|
|
// log it
|
|
self maps\mp\_matchdata::logKillstreakEvent( "littlebird_flock", location );
|
|
}
|
|
|
|
|
|
getFlightPath( location, locationYaw, rightOffset )
|
|
{
|
|
location = location * (1,1,0);
|
|
initialDirection = ( 0, locationYaw, 0 );
|
|
planeHalfDistance = 12000;
|
|
flightPath = [];
|
|
|
|
if ( isDefined( rightOffset ) && rightOffset != 0 )
|
|
location = location + ( AnglesToRight( initialDirection ) * rightOffset ) + ( 0, 0, RandomInt( 300 ) );
|
|
|
|
// start point
|
|
startPoint = ( location + ( AnglesToForward( initialDirection ) * ( -1 * planeHalfDistance ) ) );
|
|
|
|
// end point
|
|
endPoint = ( location + ( AnglesToForward( initialDirection ) * planeHalfDistance ) );
|
|
|
|
// get height
|
|
flyHeight = self maps\mp\killstreaks\_airdrop::getFlyHeightOffset( location ) + 256;
|
|
flightPath["start"] = startPoint + ( 0, 0, flyHeight );
|
|
flightPath["end"] = endPoint + ( 0, 0, flyHeight );
|
|
|
|
return flightPath;
|
|
}
|
|
|
|
|
|
doLbStrike( lifeId, owner, flightPath, flockIndex )
|
|
{
|
|
level endon( "game_ended" );
|
|
|
|
if ( !isDefined( owner ) )
|
|
return;
|
|
|
|
forward = vectorToAngles( flightPath["end"] - flightPath["start"] );
|
|
lb = spawnAttackLittleBird( owner, flightPath["start"], forward, flockIndex );
|
|
lb.lifeId = lifeId;
|
|
lb.alreadyDead = false;
|
|
|
|
lb thread watchTimeOut();
|
|
lb thread watchDeath();
|
|
lb thread flock_handleDamage();
|
|
lb thread startLbFiring1();
|
|
lb thread monitorKills();
|
|
|
|
lb endon( "death" );
|
|
|
|
// initial target run
|
|
lb SetMaxPitchRoll( 120, 60 );
|
|
lb Vehicle_SetSpeed( 48, 48 );
|
|
lb setVehGoalPos( flightPath["end"], 0 );
|
|
lb waittill( "goal" );
|
|
|
|
// turn
|
|
lb SetMaxPitchRoll( 30, 40 );
|
|
lb Vehicle_SetSpeed( 32, 32 );
|
|
lb setVehGoalPos( flightPath["start"], 0 );
|
|
wait( 2 );
|
|
|
|
// return
|
|
lb SetMaxPitchRoll( 100, 60 );
|
|
lb Vehicle_SetSpeed( 64, 64 );
|
|
lb waittill ( "goal" );
|
|
lb notify( "gone" );
|
|
|
|
// remove
|
|
lb maps\mp\killstreaks\_helicopter::removeLittlebird();
|
|
}
|
|
|
|
|
|
spawnAttackLittleBird( owner, origin, forward, flockIndex )
|
|
{
|
|
lb = spawnHelicopter( owner, origin, forward, "apache_strafe_mp" , "vehicle_apache_mp" );
|
|
|
|
if ( !isDefined( lb ) )
|
|
return;
|
|
|
|
lb maps\mp\killstreaks\_helicopter::addToLittleBirdList();
|
|
lb thread maps\mp\killstreaks\_helicopter::removeFromLittleBirdListOnDeath();
|
|
|
|
lb.health = 999999; // keep it from dying anywhere in code
|
|
lb.maxHealth = 2000; // this is the health we'll check
|
|
lb.damageTaken = 0; // how much damage has it taken
|
|
lb setCanDamage( true );
|
|
lb.owner = owner;
|
|
lb.team = owner.team;
|
|
lb.killCount = 0;
|
|
lb.streakName = "littlebird_flock";
|
|
lb.heliType = "littlebird";
|
|
//lb ThermalDrawEnable();
|
|
|
|
lb.specialDamageCallback = ::Callback_VehicleDamage;
|
|
|
|
mgTurret1 = spawnTurret( "misc_turret", lb.origin, "apache_minigun_mp" );
|
|
mgTurret1 linkTo( lb, "tag_turret", (0,0,0), (0,0,0) );
|
|
mgTurret1 setModel( "vehicle_apache_mg" );
|
|
mgTurret1.angles = lb.angles;
|
|
mgTurret1.owner = lb.owner;
|
|
mgTurret1.team = mgTurret1.owner.team;
|
|
mgTurret1 makeTurretInoperable();
|
|
mgTurret1.vehicle = lb;
|
|
//mgTurret1 ThermalDrawEnable();
|
|
|
|
killCamOrigin = ( lb.origin + ( ( AnglesToForward( lb.angles ) * -200 ) + ( AnglesToRight( lb.angles ) * -200 ) ) ) + ( 0, 0, 50 );
|
|
mgTurret1.killCamEnt = Spawn( "script_model", killCamOrigin );
|
|
mgTurret1.killCamEnt SetScriptMoverKillCam( "explosive" );
|
|
mgTurret1.killCamEnt LinkTo( lb, "tag_origin" );
|
|
|
|
lb.mgTurret1 = mgTurret1;
|
|
lb.mgTurret1 SetDefaultDropPitch( 0 );
|
|
|
|
lb.mgTurret1 setMode( "auto_nonai" );
|
|
|
|
lb.mgTurret1 SetSentryOwner( lb.owner );
|
|
|
|
if ( level.teamBased )
|
|
{
|
|
lb.mgTurret1 setTurretTeam( lb.owner.team );
|
|
}
|
|
|
|
level.heli_flock[flockIndex] = lb;
|
|
|
|
return lb;
|
|
}
|
|
|
|
|
|
watchTimeOut()
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "gone" );
|
|
self endon( "death" );
|
|
|
|
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 60.0 );
|
|
|
|
self notify( "death" );
|
|
}
|
|
|
|
monitorKills()
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "gone" );
|
|
self endon( "death" );
|
|
self endon( "stopFiring" );
|
|
|
|
for(;;)
|
|
{
|
|
self waittill( "killedPlayer", player );
|
|
self.killCount++;
|
|
level.heli_flock_victims[level.heli_flock_victims.size] = player;
|
|
}
|
|
|
|
}
|
|
|
|
startLbFiring1( )
|
|
{
|
|
self endon( "gone" );
|
|
self endon( "death" );
|
|
self endon( "stopFiring" );
|
|
|
|
for( ;; )
|
|
{
|
|
self.mgTurret1 waittill( "turret_on_target" );
|
|
|
|
fireOnTarget = true;
|
|
targetPlayer = self.mgTurret1 GetTurretTarget( false );
|
|
foreach ( victim in level.heli_flock_victims )
|
|
{
|
|
if ( targetPlayer == victim )
|
|
{
|
|
self.mgTurret1 ClearTargetEntity();
|
|
fireOnTarget = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( fireOnTarget )
|
|
self.mgTurret1 ShootTurret();
|
|
}
|
|
}
|
|
|
|
handleOwnerLeft() // self == owner
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "flock_done" );
|
|
self thread notifyOnFlockDone();
|
|
|
|
self waittill( "killstreak_disowned" );
|
|
|
|
for ( i=0; i<level.heli_flock.size; i++ )
|
|
{
|
|
if ( isDefined( level.heli_flock[i] ) )
|
|
level.heli_flock[i] notify( "stopFiring" );
|
|
}
|
|
|
|
for ( i=0; i<level.heli_flock.size; i++ )
|
|
{
|
|
if ( isDefined( level.heli_flock[i] ) )
|
|
{
|
|
level.heli_flock[i] notify( "death" );
|
|
wait( 0.1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
notifyOnFlockDone() // self == owner
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "disconnect" );
|
|
if ( !bot_is_fireteam_mode() )
|
|
{
|
|
self endon( "joined_team" );
|
|
self endon( "joined_spectators" );
|
|
}
|
|
|
|
while( heliFlockActive() )
|
|
wait( 0.5 );
|
|
|
|
self notify( "flock_done" );
|
|
}
|
|
|
|
flock_handleDamage() // self == heli
|
|
{
|
|
self endon( "death" );
|
|
level endon( "game_ended" );
|
|
|
|
while( true )
|
|
{
|
|
self waittill( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon );
|
|
|
|
if( IsDefined( self.specialDamageCallback ) )
|
|
self [[self.specialDamageCallback]]( undefined, attacker, damage, iDFlags, meansOfDeath, weapon, point, direction_vec, undefined, undefined, modelName, partName );
|
|
}
|
|
}
|
|
|
|
Callback_VehicleDamage( inflictor, attacker, damage, iDFlags, meansOfDeath, weapon, point, dir, hitLoc, timeOffset, modelIndex, partName )
|
|
{
|
|
if( isDefined( self.alreadyDead ) && self.alreadyDead )
|
|
return;
|
|
|
|
if( !isDefined( attacker ) || attacker == self )
|
|
return;
|
|
|
|
// don't allow people to destroy things on their team if FF is off
|
|
if( !maps\mp\gametypes\_weapons::friendlyFireCheck( self.owner, attacker ) )
|
|
return;
|
|
|
|
if( isDefined( iDFlags ) && ( iDFlags & level.iDFLAGS_PENETRATION ) )
|
|
self.wasDamagedFromBulletPenetration = true;
|
|
|
|
self.wasDamaged = true;
|
|
|
|
modifiedDamage = damage;
|
|
|
|
if ( isPlayer( attacker ) )
|
|
{
|
|
attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "helicopter" );
|
|
|
|
if( meansOfDeath == "MOD_RIFLE_BULLET" || meansOfDeath == "MOD_PISTOL_BULLET" )
|
|
{
|
|
if ( attacker _hasPerk( "specialty_armorpiercing" ) )
|
|
modifiedDamage += damage * level.armorPiercingMod;
|
|
}
|
|
}
|
|
|
|
// in case we are shooting from a remote position, like being in the osprey gunner shooting this
|
|
if( IsDefined( attacker.owner ) && IsPlayer( attacker.owner ) )
|
|
{
|
|
attacker.owner maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "helicopter" );
|
|
}
|
|
|
|
if( IsDefined( weapon ) )
|
|
{
|
|
switch( weapon )
|
|
{
|
|
case "ac130_105mm_mp":
|
|
case "ac130_40mm_mp":
|
|
case "stinger_mp":
|
|
case "javelin_mp":
|
|
case "remote_mortar_missile_mp":
|
|
case "remotemissile_projectile_mp":
|
|
self.largeProjectileDamage = true;
|
|
modifiedDamage = self.maxHealth + 1;
|
|
break;
|
|
|
|
case "sam_projectile_mp":
|
|
self.largeProjectileDamage = true;
|
|
modifiedDamage = self.maxHealth * 0.25; // takes about 1 burst of sam rockets
|
|
break;
|
|
|
|
case "emp_grenade_mp":
|
|
self.largeProjectileDamage = false;
|
|
modifiedDamage = self.maxHealth + 1;
|
|
break;
|
|
}
|
|
|
|
maps\mp\killstreaks\_killstreaks::killstreakHit( attacker, weapon, self );
|
|
}
|
|
|
|
self.damageTaken += modifiedDamage;
|
|
|
|
if( self.damageTaken >= self.maxHealth )
|
|
{
|
|
if ( isPlayer( attacker ) && ( !isDefined( self.owner ) || attacker != self.owner ) )
|
|
{
|
|
self.alreadyDead = true;
|
|
attacker notify( "destroyed_helicopter" );
|
|
attacker notify( "destroyed_killstreak", weapon );
|
|
thread teamPlayerCardSplash( "callout_destroyed_helicopter", attacker );
|
|
attacker thread maps\mp\gametypes\_rank::giveRankXP( "kill", 300, weapon, meansOfDeath );
|
|
attacker thread maps\mp\gametypes\_rank::xpEventPopup( "destroyed_helicopter" );
|
|
thread maps\mp\gametypes\_missions::vehicleKilled( self.owner, self, undefined, attacker, damage, meansOfDeath, weapon );
|
|
}
|
|
|
|
self notify ( "death" );
|
|
}
|
|
}
|
|
|
|
watchDeath()
|
|
{
|
|
self endon( "gone" );
|
|
self waittill( "death" );
|
|
|
|
self thread maps\mp\killstreaks\_helicopter::lbOnKilled();
|
|
}
|