#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(); } }