iw6-scripts-dev/maps/mp/mp_ca_killstreaks_a10.gsc
2024-12-11 11:28:08 +01:00

1495 lines
34 KiB
Plaintext

#include maps\mp\_utility;
#include common_scripts\utility;
KS_NAME = "ca_a10_strafe";
kTransitionTime = 0.125;
kLockIconOffset = (0, 0, -70);
init(map_name)
{
level._effect["vfx_a10_missile_fire"] = LoadFX("vfx/moments/mp_ca_impact/vfx_a10_missile_fire");
//TODO Missiles and Control Surfaces
//vehicle_ca_a10_warthog
//vehicle_a10_warthog_iw6_mp
config = SpawnStruct();
config.modelNames = [];
config.modelNames[ "allies" ] = "vehicle_ca_a10_warthog";
config.modelNames[ "axis" ] = "vehicle_ca_a10_warthog";
config.vehicle = "ca_a10warthog_mp";
config.inboundSfx = "veh_mig29_dist_loop";
config.speed = 750;
config.halfDistance = 12500;
config.heightRange = 750;
config.chooseDirection = true;
config.selectLocationVO = "KS_hqr_airstrike";
config.inboundVO = "KS_ast_inbound";
config.cannonFireVfx = LoadFX( "fx/smoke/smoke_trail_white_heli" );
config.cannonRumble = "ac130_25mm_fire";
config.turretName = "a10_cammturret_mp";
config.turretAttachPoint = "tag_barrel";
config.rocketModelName = "iw6_a10impactmissile_mp";
config.numRockets = 4;
config.delayBetweenRockets = 0.125;
config.delayBetweenLockon = 0.4;
config.lockonIcon = "veh_hud_target_chopperfly"; // "veh_hud_target_lock"
config.maxHealth = 10000;
config.xpPopup = "destroyed_a10_strafe";
config.callout = "callout_destroyed_a10";
config.voDestroyed = undefined;
config.explodeVfx = LoadFX( "vfx/gameplay/explosions/vehicle/aas_mp/vfx_x_mpaas_primary");
// holy crap, lots of sfx
config.sfxCannonFireLoop_1p = "a10_plr_fire_gatling_lp";
config.sfxCannonFireStop_1p = "a10_plr_fire_gatling_cooldown";
config.sfxCannonFireLoop_3p = "a10_npc_fire_gatling_lp";
config.sfxCannonFireStop_3p = "a10_npc_fire_gatling_cooldown";
config.sfxCannonFireBurpTime = 500;
config.sfxCannonFireBurpShort_3p = "veh_a10_npc_fire_gatling_short_burst";
config.sfxCannonFireBurpLong_3p = "veh_a10_npc_fire_gatling_long_burst";
config.sfxCannonBulletImpact = "a10_bullet_impact_lp"; // loop, should play on moving entity
config.sfxMissileFire_1p = [];
config.sfxMissileFire_1p[0] = "veh_a10_plr_missile_ignition_left";
config.sfxMissileFire_1p[1] = "veh_a10_plr_missile_ignition_right";
config.sfxMissileFire_3p = "veh_a10_npc_missile_fire";
config.sfxMissile = "veh_a10_missile_loop";
config.sfxEngine_1p = "veh_a10_plr_engine_lp";
config.sfxEngine_3p = "veh_a10_dist_loop";
level.planeConfigs[ KS_NAME ] = config;
level.killstreakFuncs[KS_NAME] = ::onUse;
level.a10_active = 0;
level.curr_a10_index = 0;
// set up custom flight paths based on each map
if(map_name == "impact")
buildAllFlightPathsImpact();
else
buildAllFlightPathsDefault();
level.debug_prints = 0;
}
onUse( lifeId, streakName )
{
config = level.planeConfigs[KS_NAME];
level.killstreakWeildWeapons[config.rocketModelName] = "ca_a10_strafe";
level.killstreakWeildWeapons[config.turretName] = "ca_a10_strafe";
if(isAirDenied())
{
self IPrintLnBold(&"KILLSTREAKS_UNAVAILABLE_WHEN_AA");
return false;
}
// because a lot of things break when the A10 is called while the nuke is incoming
if(isDefined(level.nukeIncoming) && level.nukeIncoming)
{
self IPrintLnBold(&"MP_CA_KILLSTREAKS_A10_UNAVAIL_NUKE");
return false;
}
// make it unavailable to opposing team if the nuke just hit
if(self isNuked())
{
self iPrintlnBold(&"KILLSTREAKS_UNAVAILABLE_FOR_N_WHEN_NUKE", level.nukeEmpTimeRemaining);
return false;
}
if (level.a10_active || self isUsingRemote() || self isKillStreakDenied() || !isAlive(self))
{
return false;
}
if((level.littleBirds.size == 0) && !(IsDefined(level.chopper)))
{
self thread doStrike( lifeId, KS_NAME );
return true;
}
self IPrintLnBold( &"KILLSTREAKS_AIR_SPACE_TOO_CROWDED" );
return false;
}
doStrike( lifeId, streakName ) //self == player
{
self endon ("end_remote");
self endon ("disconnect");
level endon ("game_ended");
self endon("ca_a10_nuked");
self thread watchTeamSwitchPre();
self thread watchDisconnect();
self thread watchNukePlayer();
index_a = 0;
index_b = 1;
pathIndex = 0;
print( " A10 fly path (" + level.a10SplinesIn[ pathIndex ] + ", " + level.a10SplinesOut[ pathIndex ] + ")\n" );
result = self startStrafeSequence( streakName, lifeId );
if ( result )
{
if ( level.teamBased )
{
level.teamAirDenied[self.team] = true;
level.teamAirDenied[getOtherTeam(self.team)] = true;
}
else
level.airDeniedPlayer = self;
self thread watchGameEnd();
level.a10_active = 1;
level thread teamPlayerCardSplash( "used_ca_a10_strafe", self, self.team );
self.is_attacking = 0;
plane = spawnAircraft( streakName, lifeId, level.a10SplinesIn[ index_a ], "1" );
if ( IsDefined( plane ) )
{
//wait a little bit before starting the first line, otherwise an announcer vo line will get cut off
self thread a10_play_pilot_vo_with_wait( "mp_ca_impact_a10_pilot_01", 1.05 );
plane doOneFlyby();
self.is_attacking = 0;
self VisionSetNakedForPlayer( "black_bw", 0.75 );
wait( 0.80 );
self RemoteControlVehicleOff();
if ( IsDefined( plane ) )
{
plane.forceClean = true; // make it clean up faster since we're doing another pass
plane thread endFlyby( streakName );
}
plane = spawnAircraft( streakName, lifeId, level.a10SplinesIn[ index_b ], "2" );
if ( IsDefined( plane ) )
{
self thread maps\mp\killstreaks\_killstreaks::clearRideIntro( 1.0, kTransitionTime );
self thread a10_play_pilot_vo_with_wait( "mp_ca_impact_a10_pilot_02", 0.3 );
plane doOneFlyby();
if(isDefined(plane))
plane thread endFlyby( streakName );
self endStrafeSequence( streakName );
}
}
}
return result;
}
a10_play_pilot_vo_with_wait( aliasname, waittime )
{
wait ( waittime );
self PlayLocalSound( aliasname );
}
startStrafeSequence( streakName, lifeId ) // self == owner
{
self endon ("end_remote");
self endon ("disconnect");
self setUsingRemote( KS_NAME );
self freezeControlsWrapper( true );
if( GetDvarInt( "camera_thirdPerson" ) )
self setThirdPersonDOF( false );
self.restoreAngles = self.angles;
if( self isJuggernaut() && IsDefined( self.juggernautOverlay ) )
{
self.juggernautOverlay.alpha = 0;
}
result = self maps\mp\killstreaks\_killstreaks::initRideKillstreak( KS_NAME );
if( result != "success" )
{
if ( result != "disconnect" )
self clearUsingRemote();
if( IsDefined( self.disabledWeapon ) && self.disabledWeapon )
self _enableWeapon();
self notify( "death" );
return false;
}
self freezeControlsWrapper( false );
level.a10strafeActive = true;
self.using_remote_a10 = true;
return true;
}
endStrafeSequence( switch_state )
{
level endon("game_over");
//println("endStrafeSequence()::start");
// only do this if the player is still connected
if(IsDefined(self))
{
// if the player ends the killstreak by switching teams, they're dead and this breaks down.
if(switch_state != "team_switch")
{
// make sure the player is alive before trying to run the cleanup process
while(!IsAlive(self))
{
wait 0.5;
}
}
if( self isJuggernaut() && IsDefined( self.juggernautOverlay ) )
{
self.juggernautOverlay.alpha = 1;
}
self freezeControlsWrapper( false );
if(self isUsingRemote())
self clearUsingRemote();
self SetClientOmnvar( "ui_a10", false );
self SetClientOmnvar( "ui_a10_alt_warn", false );
self SetClientOmnvar( "ui_a10_cannon", false);
self ThermalVisionFOFOverlayOff();
if( GetDvarInt( "camera_thirdPerson" ) )
{
self setThirdPersonDOF( true );
}
//edge cases
if(IsDefined(self.restoreAngles))
{
self SetPlayerAngles( self.restoreAngles );
self.restoreAngles = undefined;
}
self thread a10_FreezeBuffer();
self.using_remote_a10 = undefined;
}
level.a10strafeActive = undefined;
level.a10_active = 0;
if ( level.teamBased )
{
level.teamAirDenied["axis"] = false;
level.teamAirDenied["allies"] = false;
}
else
level.airDeniedPlayer = undefined;
//println("endStafeSequence()::end");
}
attachMissiles()
{
i = 4;
self.missiles = [];
while(i)
{
missileTag = "tag_missile_" + i;
self.missiles[missileTag] = Spawn("script_model", self GetTagOrigin(missileTag));
self.missiles[missileTag] SetModel("veh_ca_a10_missile");
self.missiles[missileTag].angles = self GetTagAngles(missileTag);
self.missiles[missileTag] LinkTo(self, missileTag);
i--;
}
}
attachAnimatedFlaps()
{
self.animated_flaps = Spawn("script_model", self GetTagOrigin("tag_origin"));
self.animated_flaps SetModel("veh_ca_a10_flaps_animated");
self.animated_flaps.angles = self GetTagAngles("tag_origin");
self.animated_flaps LinkTo(self, "tag_origin");
}
spawnAircraft( streakName, lifeId, splineId, numPlane ) //self = player
{
self endon ("end_remote");
self endon ("disconnect");
self.plane = createPlaneAsHeli( streakName, lifeId, splineId );
if( !IsDefined( self.plane ) )
return undefined;
self.plane.streakName = streakName;
// plane endon( "death" );
self.plane attachMissiles();
self.plane attachAnimatedFlaps();
//self.plane attachTurret( streakName );
self CameraLinkTo(self.plane, "tag_player");
self RemoteControlVehicle( self.plane );
self thread watchIntroCleared( streakName, self.plane );
self.plane SetPlaneSplineId( self, splineId );
config = level.planeConfigs[ streakName ];
sound_name = "scn_impact_a10_passby_0" + numPlane;
self.plane playsoundonmovingent(sound_name);
self.plane.sound_name = sound_name;
// add damage handling
self.plane thread a10_handleDamage();
self.plane thread watchDisconnectPlane();
return self.plane;
}
SpawnAndPlaySoundForA10( plane, numPlane)
{
sound_ent = Spawn( "script_model", plane.origin );
sound_ent SetModel( "tag_origin" );
sound_alias = ( "scn_impact_a10_passby_0" + numPlane );
sound_ent linkto(plane);
sound_ent PlaySoundOnMovingEnt( sound_alias );
}
attachTurret( streakName ) // self == plane
{
config = level.planeConfigs[ streakName ];
turretPos = self GetTagOrigin( config.turretAttachPoint );
turret = SpawnTurret( "misc_turret", self.origin + turretPos, config.turretName, false );
turret.angles = self GetTagAngles( config.turretAttachPoint );
turret LinkTo( self, config.turretAttachPoint, ( 0, 0, 0 ), ( 0, 0, 0 ) );
turret.owner = self.owner;
turret MakeTurretInoperable();
turret SetTurretModeChangeWait( false );
turret SetMode( "sentry_offline" );
turret MakeUnusable();
turret SetCanDamage( false );
turret SetSentryOwner( self.owner );
self.owner RemoteControlTurret( turret );
self.turret = turret;
}
cleanupAircraft()
{
if ( IsDefined( self.turret ) )
{
self.turret Delete();
}
i = 4;
while(i)
{
missileTag = "tag_missile_" + i;
self.missiles[missileTag] Hide();
self.missiles[missileTag] Delete();
i--;
}
self.animated_flaps Hide();
self.animated_flaps Delete();
// check because in certain situations, this won't have been defined yet
// usually because of team-switch
if(IsDefined(self.targetList))
{
foreach ( targetInfo in self.targetList )
{
if ( IsDefined( targetInfo["icon"] ) )
{
targetInfo["icon"] Destroy();
targetInfo["icon"] = undefined;
}
}
}
self Delete();
}
getPathIndex()
{
return ( RandomInt(level.a10SplinesIn.size ) );
}
debug_print_movement(movement)
{
println(movement);
outString = "";
if(movement[0] > 0)
outString += "forward";
else if(movement[0] == 0)
outString += "-";
else if(movement[0] < 0)
outString += "backward";
outString += ", ";
if(movement[1] > 0)
outString += "right";
else if(movement[1] == 0)
outString += "-";
else if(movement[1] < 0)
outString += "left";
println(outString);
}
//xanims
/*
veh_ca_a10_roll_left
veh_ca_a10_roll_level
veh_ca_a10_roll_right
*/
#using_animtree("mp_ca_impact");
handle_anim(state)
{
self endon("a10_end_strafe");
self endon ("disconnect");
if(state == "left")
self.animated_flaps ScriptModelPlayAnim( "veh_ca_a10_roll_left" );
if(state == "right")
self.animated_flaps ScriptModelPlayAnim( "veh_ca_a10_roll_right" );
if(state == "level")
self.animated_flaps ScriptModelPlayAnim( "veh_ca_a10_roll_level" );
}
player_input_monitor() //self = plane
{
self endon("a10_end_strafe");
self.owner endon ("disconnect");
while(IsDefined(self))
{
// [1,1] = ['forward', 'right']
//movement = [ 0, 0, 0 ];
movement = self.owner GetNormalizedMovement();
if(movement[1] > 0)
{
self thread handle_anim("right");
}
else if(movement[1] == 0)
{
self thread handle_anim("level");
}
else if(movement[1] < 0)
{
self thread handle_anim("left");
}
wait 0.05;
}
}
doOneFlyby()// self = plane
{
self endon( "death" );
level endon( "game_ended" );
//TODO Missiles and Control Surfaces
self thread player_input_monitor();
while ( true )
{
// also wait for death of plane
self waittill ( "splinePlaneReachedNode", nodeLabel );
if ( IsDefined( nodeLabel ) && nodeLabel == "End" )
{
self notify( "a10_end_strafe" );
break;
}
}
self.owner notify("a10_cannon_stop");
self notify("a10_cannon_stop");
}
endFlyby( streakName ) //self = plane
{
//println("endFlyby()");
if( !IsDefined( self ) )
{
//IPrintLnBold("endFlyby()::self is not defined!");
return;
}
config = level.planeConfigs[streakName];
// disconnect the player from the plane
if(streakName != "disconnect")
{
//self.owner CameraUnlink (self);
self.owner.is_attacking = 0;
self.owner RemoteControlVehicleOff( self );
if ( IsDefined( self.turret ) )
{
self.owner RemoteControlTurretOff( self.turret );
}
}
self notify( "end_remote" );
// let it fly away if we're not cleaning up after an early-exit situation
if(!(IsDefined(self.forceClean)))
wait( 5 );
else
self StopSounds();
if (IsDefined( self ) )
{
//PrintLn("Moving into cleanupAircraft()");
self cleanupAircraft();
}
}
createPlaneAsHeli( streakName, lifeId, splineId ) // self == player
{
// get plane config
config = level.planeConfigs[ streakName ];
// get the start pos and tangent of the spline
startPos = GetCSplinePointPosition( splineId, 0 );
startTangent = GetCSplinePointTangent( splineId, 0 );
// calculate start angles
startAngles = VectorToAngles( startTangent );
// spawn plane
plane = SpawnHelicopter( self, startPos, startAngles, config.vehicle, config.modelNames[ self.team ] );
if ( !IsDefined( plane ) )
return undefined;
// set plane to be solid
plane MakeVehicleSolidCapsule( 18, -9, 18 );
// set plane owner/team
plane.owner = self;
plane.team = self.team;
// set plane life id
plane.lifeId = lifeId;
// start fx
plane thread maps\mp\killstreaks\_plane::playPlaneFX();
// return the plane
return plane;
}
handleDeath() // self == plane
{
//level endon( "game_ended" );
self endon( "delete" );
self waittill( "death" );
self.forceClean = true;
self.owner thread endStrafeSequence( KS_NAME );
self thread endFlyby(KS_NAME);
level.a10_active = 0;
level.a10strafeActive = undefined;
self.owner.using_remote_a10 = undefined;
}
a10_FreezeBuffer()
{
self endon( "disconnect" );
//self endon( "death" );
level endon( "game_ended" );
self freezeControlsWrapper( true );
wait( 0.5 );
self freezeControlsWrapper( false );
}
monitorRocketFire2(streakName, plane)
{
plane endon( "end_remote" );
plane endon( "death" );
self endon ("disconnect");
level endon( "game_ended" );
config = level.planeConfigs[ streakName ];
plane.numRocketsLeft = config.numRockets;
plane.targetList = [];
// bots don't press +/- buttons, so this falls apart if they try to use the A10 killstreak
if(!IsBot(self))
{
self NotifyOnPlayerCommand( "rocket_fire_pressed", "+speed_throw" );
self NotifyOnPlayerCommand( "rocket_fire_pressed", "+ads_akimbo_accessible" );
if( !level.console )
{
self NotifyOnPlayerCommand( "rocket_fire_pressed", "+toggleads_throw" );
}
self SetClientOmnvar( "ui_a10_rocket", plane.numRocketsLeft );
while ( plane.numRocketsLeft > 0 )
{
if ( !(self AdsButtonPressed()) )
{
self waittill( "rocket_fire_pressed" );
}
plane missileAcquireTargets();
if ( plane.targetList.size > 0 )
{
plane thread fireMissiles();
}
}
}
}
missileGetBestTarget() // self == plane
{
candidateList = [];
foreach (player in level.players)
{
if (self missileIsGoodTarget(player))
{
candidateList[ candidateList.size ] = player;
}
}
foreach (uplink in level.uplinks)
{
if (self missileIsGoodTarget(uplink))
{
candidateList[ candidateList.size ] = uplink;
}
}
// satcoms?
// ugvs?
if ( candidateList.size > 0 )
{
sortedCandidateList = SortByDistance(candidateList, self.origin);
return sortedCandidateList[0];
}
return undefined;
}
missileIsGoodTarget( target ) // self == plane
{
return ( IsAlive(target)
&& target.team != self.owner.team
&& !(self isMissileTargeted( target ))
&& (IsPlayer( target ) && !(target _hasPerk( "specialty_blindeye" )))
// && (self.owner WorldPointInReticle_Circle(target.origin, 65, 200))
&& self missileTargetAngle( target ) > 0.25
);
}
// this needs to be optimized
missileTargetAngle( target ) // self == plane
{
dirToTarget = VectorNormalize( target.origin - self.origin );
facingDir = AnglesToForward( self.angles );
return VectorDot( dirToTarget, facingDir );
}
missileAcquireTargets()
{
self endon ("disconnect");
self endon( "end_remote" );
level endon ("game_ended");
self endon ("a10_missiles_fired");
config = level.planeConfigs[ self.streakName ];
self.owner SetClientOmnvar( "ui_a10_rocket_lock", true );
self thread missileWaitForTriggerRelease();
currentTarget = undefined;
while ( self.targetList.size < self.numRocketsLeft )
{
if ( !IsDefined( currentTarget ) )
{
currentTarget = self missileGetBestTarget();
if ( IsDefined( currentTarget ) )
{
self thread missileLockTarget( currentTarget );
wait (config.delayBetweenLockon);
currentTarget = undefined;
continue;
}
}
wait (0.1);
}
self.owner SetClientOmnvar( "ui_a10_rocket_lock", false );
self notify( "a10_missiles_fired" );
}
missileWaitForTriggerRelease()
{
self endon( "end_remote" );
//self endon( "death" );
level endon( "game_ended" );
self endon ("a10_missiles_fired");
owner = self.owner;
owner NotifyOnPlayerCommand( "rocket_fire_released", "-speed_throw" );
owner NotifyOnPlayerCommand( "rocket_fire_released", "-ads_akimbo_accessible" );
if( !level.console )
{
owner NotifyOnPlayerCommand( "rocket_fire_released", "-toggleads_throw" );
}
self.owner waittill( "rocket_fire_released" );
owner SetClientOmnvar( "ui_a10_rocket_lock", false );
self notify( "a10_missiles_fired" );
}
missileLockTarget( target ) // self == plane
{
config = level.planeConfigs[ self.streakName ];
info = [];
// veh_hud_target_marked
//info["icon"] = target maps\mp\_entityheadIcons::setHeadIcon( self.owner, config.lockonIcon, kLockIconOffset, 10, 10, false, 0.05, true, false, false, false );
info["target"] = target;
self.targetList[ target GetEntityNumber() ] = info;
self.owner PlayLocalSound( "recondrone_lockon" );
// need to handle case where target dies before
}
isMissileTargeted( target ) // self == plane
{
return ( IsDefined( self.targetList[ target GetEntityNumber() ] ) );
}
fireMissiles() // self == plane
{
self endon( "death" );
self endon ("disconnect");
level endon( "game_ended" );
config = level.planeConfigs[ self.streakName ];
foreach ( targetInfo in self.targetList )
{
if ( self.numRocketsLeft > 0 )
{
// fire at one target
missile = self onFireHomingMissile( self.streakName, targetInfo["target"], kLockIconOffset );
if ( IsDefined( targetInfo["icon"] ) )
{
missile.icon = targetInfo["icon"];
targetInfo["icon"] = undefined;
}
wait (config.delayBetweenRockets);
}
else
{
break;
}
}
targetList = [];
}
onFireHomingMissile( streakName, target, targetOffset ) // self == plane
{
self endon ("disconnect");
// side = self.numRocketsLeft % 2;
// tagName = "tag_missile_" + (side + 1);
//TODO:FLAPS AND MISSILES
tagName = "tag_missile_" + self.numRocketsLeft;
rocketPos = self GetTagOrigin( tagName );
if ( IsDefined( rocketPos ) )
{
owner = self.owner;
config = level.planeConfigs[ streakName ];
// this is what fires the missile
rocket = MagicBullet( config.rocketModelName, rocketPos, rocketPos + 100 * AnglesToForward(self.angles), self.owner );
rocket thread a10_missile_set_target( target, targetOffset );
Earthquake (0.25, 0.05, self.origin, 512);
self.numRocketsLeft--;
self.owner SetClientOmnvar( "ui_a10_rocket", self.numRocketsLeft );
//rocket PlaySoundOnMovingEnt( config.sfxMissileFire_1p[ side ] );
//rocket PlayLoopSound( config.sfxMissile );
//play the missile fire sound on the a10
self PlaySoundOnMovingEnt( "a10p_missile_launch" );
// player the missile fire effect on the a10
PlayFXOnTag(level._effect["vfx_a10_missile_fire"], self, tagName);
// HidePart doesn't work with helicopters?
//self HidePart( tagName );
//TODO Missiles and Control Surfaces
self.missiles[tagName] Hide();
return rocket;
}
return undefined;
}
MISSILE_IMPACT_DIST_MAX = 15000;
MISSILE_IMPACT_DIST_MIN = 1000;
a10_missile_set_target( target, offset )
{
self thread a10_missile_cleanup();
wait 0.2;
if(IsDefined(self) && IsAlive(self))
self Missile_SetTargetEnt( target, offset );
// self Missile_SetFlightmodeDirect();
}
a10_missile_cleanup()
{
self waittill( "death" );
if ( IsDefined( self.icon ) )
{
self.icon Destroy();
}
}
bot_plane_watcher(plane)
{
level.end_a10_firing = 0;
plane waittill( "end_remote" );
level.end_a10_firing = 1;
}
//resetStreakCount()
bot_fire_controller(plane, config)
{
self BotPressButton("attack");
self thread bot_plane_watcher(plane);
self.is_attacking = 1;
plane thread update_hit_sound_ent();
plane thread update_gatling_sound_ent();
while(!level.end_a10_firing)
{
self BotPressButton("attack");
wait 0.01;
}
self.is_attacking = 0;
}
get_look_position()
{
eye = self.origin;
angles = self.angles;
forward = anglestoforward( angles );
end = eye + ( forward * 7000 );
trace = bullettrace( eye, end, true, self);
return trace["position"];
}
update_hit_sound_ent() //self == plane
{
//self endon( "end_remote" );
//self endon( "death" );
level endon( "game_ended" );
//self endon("a10_end_strafe");
//PrintLn("waiting for firing hits");
spawn_origin = self get_look_position();
moving_ent = Spawn("script_origin", spawn_origin);
moving_ent PlayLoopSound("a10_bullet_impact_lp");
while(IsDefined(self) && self.owner.is_attacking)
{
moving_ent MoveTo(self get_look_position(), 0.15);
wait 0.01;
}
moving_ent StopLoopSound();
moving_ent Delete();
}
update_gatling_sound_ent() //self = plane
{
//DR: This will play 1st person and 3rd person sounds - both - for the gatling gun while firing
level endon( "game_ended" );
//PrintLn("waiting for gatling gun to start");
spawn_origin = self.origin;
moving_gatling_ent = Spawn("script_origin", spawn_origin);
//player sound for gatling gun
self.owner PlayLocalSound("a10_plr_fire_gatling_lp");
//npc sound for gatling gun
moving_gatling_ent PlayLoopSound( "a10_npc_fire_gatling_lp" );
while( IsDefined( self ) && IsDefined( self.owner ) && IsDefined( moving_gatling_ent ) && self.owner.is_attacking )
{
moving_gatling_ent MoveTo( self.origin, 0.1 );
wait 0.01;
}
//start playing the release sound, then stop the loop sound - will need a new ent for the release sound
if( IsDefined( self ) )
self thread update_gatling_sound_release_ent();
//MO: cut the delay because of IWSIX-170454
//wait a short period of time, then stop the sounds
//wait 0.1;
//stop the player and npc sounds, and then play a release sound
//player
if( IsDefined( self ) && IsDefined( self.owner ) )
self.owner StopLocalSound("a10_plr_fire_gatling_lp");
//npc
if( IsDefined( moving_gatling_ent ) )
{
moving_gatling_ent StopLoopSound();
//moving_gatling_ent playsound ("a10_npc_fire_gatling_cooldown");
moving_gatling_ent Delete();
}
}
cutoff_gatling_sound_release()
{
self waittill_any("end_remote", "end_gatling");
self.owner StopLocalSound("a10_plr_fire_gatling_cooldown");
}
update_gatling_sound_release_ent()
{
//this spawns a new ent for the release sound, and plays it
//so that we have overlap between the loop and release
level endon( "game_ended" );
//self endon("a10_end_strafe");
//PrintLn("playing release sound for gatling gun");
spawn_origin = self.origin;
moving_gatling_release_ent = Spawn("script_origin", spawn_origin);
// so it shuts off correctly if it's playing while the killstreak is ending
self thread cutoff_gatling_sound_release();
self.owner PlayLocalSound("a10_plr_fire_gatling_cooldown");
moving_gatling_release_ent PlaySound( "a10_npc_fire_gatling_cooldown" );
wait 2.5;
if( IsDefined( moving_gatling_release_ent ) )
moving_gatling_release_ent Delete();
self notify("end_gatling");
}
CANNON_SHAKE_TIME = 0.5;
monitorWeaponFire( streakName, plane ) // self == player
{
self endon( "disconnect" );
level endon( "game_ended" );
plane endon("end_remote");
plane endon("death");
plane endon("a10_end_strafe");
config = level.planeConfigs[ streakName ];
plane.ammoCount = 1350;
self SetClientOmnvar( "ui_a10_cannon", plane.ammoCount );
if(isBot(self))
self thread bot_fire_controller(plane, config);
else
{
self thread monitor_attack_button(plane);
while ( plane.ammoCount > 0 )
{
while(!self.is_attacking)
{
wait 0.01;
}
cannonShortBurstTimeLimit = GetTime() + config.sfxCannonFireBurpTime;
plane thread update_hit_sound_ent();
plane thread update_gatling_sound_ent();
plane thread updateCannonShake( streakName );
while(self.is_attacking)
{
wait 0.1;
}
}
self.is_attacking = 0;
}
}
monitor_attack_button(plane)
{
plane endon("a10_end_strafe");
self endon("a10_end_strafe");
self endon("disconnect");
self.is_attacking = 0;
while(IsDefined(plane))
{
if(self AttackButtonPressed())
self.is_attacking = 1;
else
self.is_attacking = 0;
wait 0.1;
}
}
// should eventually unify
updateCannonShake( streakName ) // self == plane
{
self.owner endon( "a10_cannon_stop" );
self.owner endon ("disconnect");
self endon( "death" );
level endon( "game_ended" );
config = level.planeConfigs[ streakName ];
while (( self.ammoCount > 0 ) && self.owner.is_attacking && (IsDefined(self)))
{
Earthquake (0.2, CANNON_SHAKE_TIME, self.origin, 512);
self.ammoCount -= 10;
self.owner SetClientOmnvar( "ui_a10_cannon", self.ammoCount );
barrelPoint = self GetTagOrigin( "tag_flash_attach" ) + 20 * AnglesToForward( self.angles );
PlayFX( config.cannonFireVFX, barrelPoint );
self.owner PlayRumbleOnEntity( config.cannonRumble );
// this needs to match the cannon's fire rate in the gdt
wait( 0.1 );
}
if(IsDefined(self))
if(IsDefined(self.turret))
self.turret TurretFireDisable();
}
ALTITUDE_WARNING_LIMIT = 10;
monitorAltitude( streakName, plane )
{
plane endon( "end_remote" );
plane endon( "death" );
self endon( "disconnect" );
level endon( "game_ended" );
self SetClientOmnvar( "ui_a10_alt_warn", false );
while( true )
{
// the max is in omnvar
alt = Int( Clamp(plane.origin[2], 0, 16383) );
self SetClientOmnvar( "ui_a10_alt", alt );
if (alt <= ALTITUDE_WARNING_LIMIT && !IsDefined( plane.altWarning ) )
{
plane.altWarning = true;
self SetClientOmnvar( "ui_a10_alt_warn", true );
}
else if (alt > ALTITUDE_WARNING_LIMIT && IsDefined( plane.altWarning ) )
{
plane.altWarning = undefined;
self SetClientOmnvar( "ui_a10_alt_warn", false );
}
wait( 0.1 );
}
}
watchDisconnectPlane() //self = plane
{
level endon( "game_ended" );
level endon( "round_end_finished" );
self endon("end_remote");
self.owner waittill( "disconnect" );
//println("plane disconnect");
self.forceClean = true;
self thread a10_explode();
self thread endFlyby("disconnect");
thread endStrafeSequence( KS_NAME );
}
watchDisconnect()
{
level endon( "game_ended" );
level endon( "round_end_finished" );
self endon("cleared_intro");
self waittill( "disconnect" );
//self notify("a10_end_strafe");
//self notify("switch_team");
//self notify("end_remote");
//println("player disconnect");
if(IsDefined(self) && IsDefined(self.plane))
{
self.plane.forceClean = true;
self.plane thread endFlyby(KS_NAME);
}
if(IsDefined(self))
self endStrafeSequence( "team_switch" );
}
watchGameEnd()
{
level endon( "round_end_finished" );
self endon("end_remote");
level waittill( "game_ended" );
self notify("a10_end_strafe");
if(IsDefined(self) && IsDefined(self.plane))
{
self.plane.forceClean = true;
self.plane thread endFlyby(KS_NAME);
}
if(IsDefined(self))
self endStrafeSequence( "team_switch" );
}
watchTeamSwitchPre() // self == player
{
level endon( "game_ended" );
level endon( "round_end_finished" );
self endon("cleared_intro");
self waittill_any( "joined_team", "joined_spectators" );
//self notify("a10_end_strafe");
self notify("switch_team");
self notify("end_remote");
if(IsDefined(self.plane))
{
self.plane.forceClean = true;
self.plane thread endFlyby(KS_NAME);
}
self endStrafeSequence( "team_switch" );
}
watchIntroCleared( streakName, plane ) // self == player
{
self endon( "disconnect" );
level endon( "game_ended" );
self endon("end_remote");
self waittill( "intro_cleared" );
self thread monitorAltitude( streakname, plane );
self thread monitorRocketFire2( streakName, plane );
self thread monitorWeaponFire( streakName, plane );
self thread watchRoundEnd( plane, streakName );
self thread watchEarlyExit( plane );
self thread watchNuke( plane, KS_NAME);
self thread watchTeamSwitchPost(plane, KS_NAME);
//println("setting ui_a10");
self SetClientOmnvar( "ui_a10", true );
self ThermalVisionFOFOverlayOn();
}
watchEarlyExit( veh ) // self == player
{
level endon( "round_end_finished" );
level endon( "game_ended" );
veh endon( "death" );
veh endon( "a10_end_strafe" );
veh thread maps\mp\killstreaks\_killstreaks::allowRideKillstreakPlayerExit("killstreakExit");
veh waittill("killstreakExit");
// PrintLn("Player exited the killstreak early");
self notify("end_remote");
veh notify("end_remote");
veh.forceClean = true;
veh thread a10_explode();
self thread endStrafeSequence( KS_NAME );
veh thread endFlyby( KS_NAME );
}
watchNukePlayer() // self == player
{
self endon( "disconnect" );
self endon( "end_remote");
level waittill("nuke_death");
self notify("ca_a10_nuked");
// Println("player nuked");
self thread endStrafeSequence( KS_NAME );
}
watchNuke( plane, streakName) // self == player
{
//plane endon("death");
plane endon("a10_end_strafe");
self endon( "disconnect" );
self endon( "end_remote");
level waittill("nuke_death");
self notify("ca_a10_nuked");
// Println("plane nuked");
self thread endStrafeSequence( KS_NAME );
if(IsDefined(plane))
{
plane.forceClean = true;
plane thread a10_explode();
plane thread endFlyby( KS_NAME );
}
}
watchTeamSwitchPost( plane, streakName ) // self == player
{
plane endon( "death" );
plane endon( "leaving" );
self endon( "disconnect" );
level endon( "game_ended" );
level endon( "round_end_finished" );
// cut off the pre-team switch watcher
self notify("cleared_intro");
self waittill_any( "joined_team", "joined_spectators" );
self notify("a10_end_strafe");
self notify("end_remote");
// leave
plane.forceClean = true;
self thread endStrafeSequence( "team_switch" );
plane thread a10_explode();
plane thread endFlyby( streakName );
}
watchRoundEnd( plane, streakName ) // self == player
{
plane endon( "death" );
plane endon( "leaving" );
self endon( "disconnect" );
self endon( "joined_team" );
self endon( "joined_spectators" );
level waittill_any( "round_end_finished", "game_ended" );
// leave
plane.forceClean = true;
plane thread endFlyby( streakName );
plane thread a10_explode();
self endStrafeSequence( "team_switch" );
}
// SETUP FUNCS
buildAllFlightPathsImpact()
{
inBoundList = [];
inBoundList[0] = 1;
inBoundList[1] = 2;
outBoundList = [];
outBoundList[0] = 1;
outBoundList[1] = 2;
buildAllFlightPaths( inBoundList, outBoundList );
}
buildAllFlightPathsDefault()
{
// PrintLn("Building default flight paths");
// temp - do not check in - should be done per level
inBoundList = [];
inBoundList[0] = 1;
inBoundList[1] = 1;
outBoundList = [];
outBoundList[0] = 1;
outBoundList[1] = 1;
buildAllFlightPaths( inBoundList, outBoundList );
}
buildAllFlightPaths( inBoundList, outBoundList )
{
level.a10SplinesIn = inBoundList;
level.a10SplinesOut = outBoundList;
}
//DAMAGE FUNCS
a10_handleDamage()
{
self endon( "end_remote" );
config = level.planeConfigs[ self.streakName ];
self maps\mp\gametypes\_damage::monitorDamage(
config.maxHealth,
"helicopter",
::handleDeathDamage,
::modifyDamage,
true // isKillstreak
);
}
modifyDamage( attacker, weapon, type, damage )
{
modifiedDamage = damage;
// modifiedDamage = self maps\mp\gametypes\_damage::handleMeleeDamage( weapon, type, modifiedDamage );
modifiedDamage = self maps\mp\gametypes\_damage::handleEmpDamage( weapon, type, modifiedDamage );
modifiedDamage = self maps\mp\gametypes\_damage::handleMissileDamage( weapon, type, modifiedDamage );
modifiedDamage = self maps\mp\gametypes\_damage::handleAPDamage( weapon, type, modifiedDamage, attacker );
// do damage effects?
return modifiedDamage;
}
handleDeathDamage( attacker, weapon, type, damage ) // self == plane
{
config = level.planeConfigs[ self.streakName ];
// !!! need VO
self maps\mp\gametypes\_damage::onKillstreakKilled( attacker, weapon, type, damage, config.voDestroyed, config.xpPopup, config.callout );
self thread a10_explode();
}
// plane explode
a10_explode()
{
if(IsDefined(self))
{
config = level.planeConfigs[ self.streakName ];
PlayFX ( config.explodeVfx, self.origin );
self Hide();
// wait so killcams can work and things like that
wait 20.0;
//self Delete();
}
}