Results 1 to 14 of 14

Threaded View

  1. #1
    Arasonic's Avatar
    Join Date
    Jun 2010
    Gender
    male
    Posts
    569
    Reputation
    11
    Thanks
    115

    What happens if you combine AC130 and Attack Heli..

    This happens.
    (Excuse my bad aim )



    Add _helicopter.gsc to your killstreaks folder
    And yeah I went a bit crazy with the shaking, easy changeable
    Code:
    #include maps\mp\_utility;
    #include maps\mp\gametypes\_hud_util;
    #include common_scripts\utility;
    
    
    init()
    {
    	path_start = getentarray( "heli_start", "targetname" ); 		// start pointers, point to the actual start node on path
    	loop_start = getentarray( "heli_loop_start", "targetname" ); 	// start pointers for loop path in the map
    
    	if ( !path_start.size && !loop_start.size)
    		return;
    
    	level.heli_types = [];
    
    	precacheHelicopter( "vehicle_cobra_helicopter_fly_low", "cobra" );
    	precacheHelicopter( "vehicle_mi24p_hind_mp", "hind" );
    	precacheHelicopter( "vehicle_mi-28_mp", "mi28" );
    	precacheHelicopter( "vehicle_apache_mp", "apache" );
    	precacheHelicopter( "vehicle_pavelow", "pavelow" );
    	precacheHelicopter( "vehicle_pavelow_opfor", "pavelow" );
    	precacheHelicopter( "vehicle_little_bird_armed", "cobra" );
    	
    	precacheitem( "cobra_FFAR_mp" );
    	precacheitem( "cobra_20mm_mp" );
    	precacheitem( "cobra_player_minigun_mp" );
    	precacheitem( "heli_remote_mp" );
    	precacheVehicle( "cobra_mp" );
    	precacheVehicle( "cobra_minigun_mp" );
    	precacheVehicle( "pavelow_mp" );
    	precacheTurret( "pavelow_minigun_mp" );
    	precacheString( &"MP_CIVILIAN_AIR_TRAFFIC" );
    	
    	level.chopper = undefined;
    
    	// array of paths, each element is an array of start nodes that all leads to a single destination node
    	level.heli_start_nodes = getEntArray( "heli_start", "targetname" );
    	assertEx( level.heli_start_nodes.size, "No \"heli_start\" nodes found in map!" );
    
    	level.heli_loop_nodes = getEntArray( "heli_loop_start", "targetname" );
    	assertEx( level.heli_loop_nodes.size, "No \"heli_loop_start\" nodes found in map!" );
    
    	level.heli_leave_nodes = getEntArray( "heli_leave", "targetname" );
    	assertEx( level.heli_leave_nodes.size, "No \"heli_leave\" nodes found in map!" );
    
    	level.heli_crash_nodes = getEntArray( "heli_crash_start", "targetname" );
    	assertEx( level.heli_crash_nodes.size, "No \"heli_crash_start\" nodes found in map!" );
    	
    	level.heli_missile_rof 	= 5;	// missile rate of fire, one every this many seconds per target, could fire two at the same time to different targets
    	level.heli_maxhealth 	= 1500;	// max health of the helicopter
    	level.heli_debug 		= 0;	// debug mode, draws debugging info on screen
    	
    	level.heli_targeting_delay 	= 0.5;	// targeting delay
    	level.heli_turretReloadTime = 1.5;	// mini-gun reload time
    	level.heli_turretClipSize 	= 40;	// mini-gun clip size, rounds before reload
    	level.heli_visual_range 	= 3500;	// distance radius helicopter will acquire targets (see)
    			
    	level.heli_target_spawnprotection 	= 5;		// players are this many seconds safe from helicopter after spawn
    	level.heli_target_recognition 		= 0.5;		// percentage of the player's body the helicopter sees before it labels him as a target
    	level.heli_missile_friendlycare 	= 256;		// if friendly is within this distance of the target, do not shoot missile
    	level.heli_missile_target_cone 		= 0.3;		// dot product of vector target to helicopter forward, 0.5 is in 90 range, bigger the number, smaller the cone
    	level.heli_armor_bulletdamage 		= 0.3;		// damage multiplier to bullets onto helicopter's armor
    	
    	level.heli_attract_strength 		= 1000;
    	level.heli_attract_range 			= 4096;	
    	
    	level.heli_angle_offset 			= 90;
    	level.heli_forced_wait 				= 0;
    
    	// helicopter fx
    	level.chopper_fx["explode"]["death"] = [];
    	level.chopper_fx["explode"]["large"] = loadfx ("explosions/helicopter_explosion_secondary_small");
    	level.chopper_fx["explode"]["medium"] = loadfx ("explosions/aerial_explosion");
    	level.chopper_fx["smoke"]["trail"] = loadfx ("smoke/smoke_trail_white_heli");
    	level.chopper_fx["fire"]["trail"]["medium"] = loadfx ("fire/fire_smoke_trail_L_emitter");
    	level.chopper_fx["fire"]["trail"]["large"] = loadfx ("fire/fire_smoke_trail_L");
    
    	level.chopper_fx["damage"]["light_smoke"] = loadfx ("smoke/smoke_trail_white_heli_emitter");
    	level.chopper_fx["damage"]["heavy_smoke"] = loadfx ("smoke/smoke_trail_black_heli_emitter");
    	level.chopper_fx["damage"]["on_fire"] = loadfx ("fire/fire_smoke_trail_L_emitter");
    
    	level.chopper_fx["light"]["left"] = loadfx( "misc/aircraft_light_wingtip_green" );
    	level.chopper_fx["light"]["right"] = loadfx( "misc/aircraft_light_wingtip_red" );
    	level.chopper_fx["light"]["belly"] = loadfx( "misc/aircraft_light_red_blink" );
    	level.chopper_fx["light"]["tail"] = loadfx( "misc/aircraft_light_white_blink" );
    
    	level.fx_heli_dust = loadfx ("treadfx/heli_dust_default");
    	level.fx_heli_water = loadfx ("treadfx/heli_water");
    
    	makeHeliType( "cobra", "explosions/helicopter_explosion_cobra_low", ::defaultLightFX );
    	addAirExplosion( "cobra", "explosions/aerial_explosion_cobra_low_mp" );
    
    	makeHeliType( "pavelow", "explosions/helicopter_explosion_pavelow", ::pavelowLightFx );
    	addAirExplosion( "pavelow", "explosions/aerial_explosion_pavelow_mp" );
    
    	makeHeliType( "mi28", "explosions/helicopter_explosion_mi28_flying", ::defaultLightFX );
    	addAirExplosion( "mi28", "explosions/aerial_explosion_mi28_flying_mp" );
    
    	makeHeliType( "hind", "explosions/helicopter_explosion_hind_chernobyl", ::defaultLightFX );
    	addAirExplosion( "hind", "explosions/aerial_explosion_hind_chernobyl_mp" );
    
    	makeHeliType( "apache", "explosions/helicopter_explosion_apache", ::defaultLightFX );
    	addAirExplosion( "apache", "explosions/aerial_explosion_apache_mp" );
    
    	makeHeliType( "littlebird", "explosions/aerial_explosion_littlebird_mp", ::defaultLightFX );
    	addAirExplosion( "littlebird", "explosions/aerial_explosion_littlebird_mp" );
    
    	//makeHeliType( "harrier", "explosions/harrier_exposion_ground", ::defaultLightFX );
    
    
    	level.killstreakFuncs["helicopter"] = ::useHelicopter;
    	level.killstreakFuncs["helicopter_blackbox"] = ::useHelicopterBlackbox;
    	level.killstreakFuncs["helicopter_flares"] = ::useHelicopterFlares;
    	level.killstreakFuncs["helicopter_minigun"] = ::useHelicopterMinigun;
    	level.killstreakFuncs["helicopter_mk19"] = ::useHelicopterMK19;
    	
    	level.heliDialog["tracking"][0] = "ac130_fco_moreenemy";
    	level.heliDialog["tracking"][1] = "ac130_fco_getthatguy";
    	level.heliDialog["tracking"][2] = "ac130_fco_guyrunnin";
    	level.heliDialog["tracking"][3] = "ac130_fco_gotarunner";
    	level.heliDialog["tracking"][4] = "ac130_fco_personnelthere";
    	level.heliDialog["tracking"][5] = "ac130_fco_rightthere";
    	level.heliDialog["tracking"][6] = "ac130_fco_tracking";
    
    	level.heliDialog["locked"][0] = "ac130_fco_lightemup";
    	level.heliDialog["locked"][1] = "ac130_fco_takehimout";
    	level.heliDialog["locked"][2] = "ac130_fco_nailthoseguys";
    
    	level.lastHeliDialogTime = 0;	
    	
    	queueCreate( "helicopter" );
    }
    
    
    makeHeliType( heliType, deathFx, lightFXFunc )
    {
    	level.chopper_fx["explode"]["death"][ heliType ] = loadFx( deathFX );
    	level.lightFxFunc[ heliType ] = lightFXFunc;
    }
    
    addAirExplosion( heliType, explodeFx )
    {
    	level.chopper_fx["explode"]["air_death"][ heliType ] = loadFx( explodeFx );
    }
    
    
    pavelowLightFX()
    {
    	playFXOnTag( level.chopper_fx["light"]["left"], self, "tag_light_L_wing1" );
    	wait ( 0.05 );
    	playFXOnTag( level.chopper_fx["light"]["right"], self, "tag_light_R_wing1" );
    	wait ( 0.05 );
    	playFXOnTag( level.chopper_fx["light"]["belly"], self, "tag_light_belly" );
    	wait ( 0.05 );
    	playFXOnTag( level.chopper_fx["light"]["tail"], self, "tag_light_tail" );
    	wait ( 0.05 );
    	playFXOnTag( level.chopper_fx["light"]["tail"], self, "tag_light_tail2" );
    	wait ( 0.05 );
    	playFXOnTag( level.chopper_fx["light"]["belly"], self, "tag_light_cockpit01" );
    }
    
    
    defaultLightFX()
    {
    	playFXOnTag( level.chopper_fx["light"]["left"], self, "tag_light_L_wing" );
    	wait ( 0.05 );
    	playFXOnTag( level.chopper_fx["light"]["right"], self, "tag_light_R_wing" );
    	wait ( 0.05 );
    	playFXOnTag( level.chopper_fx["light"]["belly"], self, "tag_light_belly" );
    	wait ( 0.05 );
    	playFXOnTag( level.chopper_fx["light"]["tail"], self, "tag_light_tail" );
    }
    
    
    useHelicopter( lifeId )
    {
    	return tryUseHelicopter( lifeId );
    }
    
    useHelicopterBlackbox( lifeId )
    {
    	return tryUseHelicopter( lifeId, "blackbox" );
    }
    
    useHelicopterFlares( lifeId )
    {
    	return tryUseHelicopter( lifeId, "mk19" );
    }
    
    removeObjectAfter(obj, time, reason)
    {
        wait time;
        obj delete();
    	self notify(reason);
    }
    
    useHelicopterMinigun( lifeId )
    {
    	if ( isDefined( self.lastStand ) && !self _hasPerk( "specialty_finalstand" ) )
    	{
    		self iPrintLnBold( &"MP_UNAVILABLE_IN_LASTSTAND" );
    		return false;
    	}
    
    	return tryUseHelicopter( lifeId, "minigun" );
    }
    
    
    useHelicopterMK19( lifeId )
    {
    	if ( isDefined( self.lastStand ) && !self _hasPerk( "specialty_finalstand" ) )
    	{
    		self iPrintLnBold( &"MP_UNAVILABLE_IN_LASTSTAND" );
    		return false;
    	}
    	return tryUseHelicopter( lifeId, "mk19" );
    }
    
    
    tryUseHelicopter( lifeId, heliType )
    {
    	if ( isDefined( level.civilianJetFlyBy ) )
    	{
    		self iPrintLnBold( &"MP_CIVILIAN_AIR_TRAFFIC" );
    		return false;
    	}
    	
    	if ( (!isDefined( heliType ) || heliType == "flares") && isDefined( level.chopper ) )
    	{
    		self iPrintLnBold( &"MP_HELI_IN_QUEUE" );
    		
    		if ( isDefined( heliType ) )
    			streakName = "helicopter_" + heliType;
    		else
    			streakName = "helicopter";
    		
    		self maps\mp\killstreaks\_killstreaks::shuffleKillStrea  ksFILO( streakName );
    		self maps\mp\killstreaks\_killstreaks::giveOwnedKillstr  eakItem();		
    		
    		queueEnt = spawn( "script_origin", (0,0,0) );
    		queueEnt hide();
    		queueEnt thread deleteOnEntNotify( self, "disconnect" );
    		queueEnt.player = self;
    		queueEnt.lifeId = lifeId;
    		queueEnt.heliType = heliType;
    		queueEnt.streakName = streakName;
    		
    		queueAdd( "helicopter", queueEnt );
    		
    		return false;
    	}
    	else if ( isDefined( level.chopper ) )
    	{
    		self iPrintLnBold( &"MP_AIR_SPACE_TOO_CROWDED" );
    		return false;
    	}		
    
    	if ( isDefined( heliType ) && heliType == "minigun" )
    	{
    		self setUsingRemote( "helicopter_" + heliType );
    		result = self maps\mp\killstreaks\_killstreaks::initRideKillstre  ak();
    
    		if ( result != "success" )
    		{
    			if ( result != "disconnect" )
    				self clearUsingRemote();
    	
    			return false;
    		}
    
    		if ( isDefined( level.chopper ) )
    		{
    			self iPrintLnBold( &"MP_AIR_SPACE_TOO_CROWDED" );
    			return false;
    		}
    	}
    
    
    	self startHelicopter( lifeId, heliType );
    	return true;
    }
    
    
    deleteOnEntNotify( ent, notifyString )
    {
    	self endon ( "death" );
    	ent waittill ( notifyString );
    	
    	self delete();
    }
    
    
    startHelicopter( lifeId, heliType )
    {
    	if ( !isDefined( heliType ) )
    		heliType = "";
    
    	switch ( heliType )
    	{
    		case "flares":
    			eventType = "helicopter_flares";
    			break;
    		case "minigun":
    			eventType = "helicopter_minigun";
    			break;
    		default:
    			eventType = "helicopter";
    			break;
    	}
    	
    	team = self.pers["team"];
    	
    	startNode = level.heli_start_nodes[ randomInt( level.heli_start_nodes.size ) ];
    
    	self maps\mp\_matchdata::logKillstreakEvent( eventType, self.origin );
    	
    	thread heli_think( lifeId, self, startnode, self.pers["team"], heliType );
    }
    
    
    precacheHelicopter( model, heliType )
    {
    	deathfx = loadfx ("explosions/tanker_explosion");
    
    	precacheModel( model );
    	
    	level.heli_types[model] = heliType;
    	
    	/**************************************************  ****/
    	/*					SETUP WEAPON TAGS				  */
    	/**************************************************  ****/
    	
    	level.cobra_missile_models = [];
    	level.cobra_missile_models["cobra_Hellfire"] = "projectile_hellfire_missile";
    
    	precachemodel( level.cobra_missile_models["cobra_Hellfire"] );
    	
    	// helicopter sounds:
    	level.heli_sound["allies"]["hit"] = "cobra_helicopter_hit";
    	level.heli_sound["allies"]["hitsecondary"] = "cobra_helicopter_secondary_exp";
    	level.heli_sound["allies"]["damaged"] = "cobra_helicopter_damaged";
    	level.heli_sound["allies"]["spinloop"] = "cobra_helicopter_dying_loop";
    	level.heli_sound["allies"]["spinstart"] = "cobra_helicopter_dying_layer";
    	level.heli_sound["allies"]["crash"] = "cobra_helicopter_crash";
    	level.heli_sound["allies"]["missilefire"] = "weap_cobra_missile_fire";
    	level.heli_sound["axis"]["hit"] = "cobra_helicopter_hit";
    	level.heli_sound["axis"]["hitsecondary"] = "cobra_helicopter_secondary_exp";
    	level.heli_sound["axis"]["damaged"] = "cobra_helicopter_damaged";
    	level.heli_sound["axis"]["spinloop"] = "cobra_helicopter_dying_loop";
    	level.heli_sound["axis"]["spinstart"] = "cobra_helicopter_dying_layer";
    	level.heli_sound["axis"]["crash"] = "cobra_helicopter_crash";
    	level.heli_sound["axis"]["missilefire"] = "weap_cobra_missile_fire";
    }
    
    
    spawn_helicopter( owner, origin, angles, vehicleType, modelName )
    {
    	chopper = spawnHelicopter( owner, origin, angles, vehicleType, modelName );
    	
    	if ( !isDefined( chopper ) )
    		return undefined;
    
    	chopper.heli_type = level.heli_types[ modelName ];
    	
    	chopper thread [[ level.lightFxFunc[ chopper.heli_type ] ]]();
    	
    	chopper addToHeliList();
    		
    	chopper.zOffset = (0,0,chopper getTagOrigin( "tag_origin" )[2] - chopper getTagOrigin( "tag_ground" )[2]);
    	chopper.attractor = Missile_CreateAttractorEnt( chopper, level.heli_attract_strength, level.heli_attract_range );
    	
    	chopper.damageCallback = ::Callback_VehicleDamage;
    	
    	return chopper;
    }
    
    
    heliRide( lifeId, chopper )
    {
    	self endon ( "disconnect" );
          self endon ( "death" );
          self endon ( "helicopter_done" );
          	chopper endon ( "helicopter_done" );
    
          self thread endRideOnHelicopterDone( chopper );
    	thread teamPlayerCardSplash( "used_helicopter_minigun", self );
    	self VisionSetThermalForPlayer( "black_bw", 2 );
          wait 2;
          self takeAllWeapons();
    	self _giveWeapon("ac130_105mm_mp");
          self _giveWeapon("ac130_40mm_mp");
          self _giveWeapon("ac130_25mm_mp");
    	self SwitchToWeapon("ac130_105mm_mp");
    	self VisionSetThermalForPlayer( game["thermal_vision"], 10 );
    	self ThermalVisionOn();
    	self ThermalVisionFOFOverlayOn();
    	self thread thermalVision( chopper );
          self thread weaponFiredThread( chopper );
    	if ( getDvarInt( "camera_thirdPerson" ) )
    		self setThirdPersonDOF( false );
    
    	chopper VehicleTurretControlOn( self );
          chopper thread engineSmoke();
    
    	self PlayerLinkWeaponviewToDelta( chopper, "tag_player", 1.0, 360, 360, 360, 360, true );
    	
    	chopper.gunner = self;	
    
    	self.heliRideLifeId = lifeId;
    	
    	self thread weaponLockThink( chopper );
    
    	while ( true )
    	{
    		chopper waittill( "turret_fire" );
    
                //thing
    	}
    }
    
    weaponFiredThread( chopper )
    {
          self endon ( "death" );
          self endon ( "helicopter_done" );
    
    	for(;;)
    	{
    		self waittill( "weapon_fired" );
    		
    		weapon = self getCurrentWeapon();
    		
    		if ( weapon == "ac130_105mm_mp" )
    		{		
    			earthquake (2, 1, chopper.origin, 1000);
    		}
    		else if ( weapon == "ac130_40mm_mp" )
    		{
    			earthquake (0.5, 1, chopper.origin, 1000);
    		}
    
    		if ( self getWeaponAmmoClip( weapon ) )
    			continue;
    			
    		self thread weaponReload( weapon );
    	}
    }
    
    weaponReload( weapon )
    {
          self endon ( "death" );
          self endon ( "helicopter_done" );
    
    	wait level.weaponReloadTime[ weapon ];
    	
    	self setWeaponAmmoClip( weapon, 9999 );
    	
    	if ( self getCurrentWeapon() == weapon )
    	{
    		self takeWeapon( weapon );
    		self _giveWeapon( weapon );
    		self switchToWeapon( weapon );
    	}
    }
    
    engineSmoke()
    {
          self endon ( "death" );
          self endon ( "helicopter_done" );
    
          playFxOnTag( level.fx_airstrike_contrail, self, "tag_engine_left" );
          playFxOnTag( level.fx_airstrike_contrail, self, "tag_engine_right" );
          playFxOnTag( level.fx_airstrike_contrail, self, "tag_engine_left" );
          playFxOnTag( level.fx_airstrike_contrail, self, "tag_engine_right" );
          playFxOnTag( level.fx_airstrike_contrail, self, "tag_engine_left" );
          playFxOnTag( level.fx_airstrike_contrail, self, "tag_engine_right" );
          playFxOnTag( level.fx_airstrike_contrail, self, "tag_engine_left" );
          playFxOnTag( level.fx_airstrike_contrail, self, "tag_engine_right" );
    }
    
    thermalVision( chopper )
    {
    	chopper endon ( "helicopter_done" );
    	
    	if ( getIntProperty( "ac130_thermal_enabled", 1 ) == 0 )
    		return;
    	
    	inverted = false;
    
    	self visionSetThermalForPlayer( game["thermal_vision"], 3 );
    
    	self notifyOnPlayerCommand( "switch thermal", "+activate" );
    
    	for (;;)
    	{
    		self waittill ( "switch thermal" );
    		
    		if ( !inverted )
    		{
    			self visionSetThermalForPlayer( "missilecam", 0.62 );
    			if ( isdefined( level.HUDItem[ "thermal_mode" ] ) )
    				level.HUDItem[ "thermal_mode" ] settext ( &"AC130_HUD_THERMAL_BHOT" );
    		}
    		else
    		{
    			self visionSetThermalForPlayer( game["thermal_vision"], 0.51 );
    			if ( isdefined( level.HUDItem[ "thermal_mode" ] ) )
    				level.HUDItem[ "thermal_mode" ] settext ( &"AC130_HUD_THERMAL_WHOT" );
    		}
    
    		inverted = !inverted;
    	}
    }
    
    
    weaponLockThink( chopper )
    {
    	self endon ( "disconnect" );
    	chopper endon ( "helicopter_done" );
    
    	if ( !isDefined( level.heliTargetOrigin ) )
    	{
    		level.heliTargetOrigin = spawn( "script_origin", (0,0,0) );
    		level.heliTargetOrigin hide();
    	}
    
    	for ( ;; )
    	{
    		trace = bulletTrace( self getEye(), self getEye() + (anglesToForward( self getPlayerAngles() ) * 100000 ), 1, self );
    		level.heliTargetOrigin.origin = trace["position"];
    
    		targetListLOS = [];
    		targetListNoLOS = [];
    		foreach ( player in level.players )
    		{
    			if ( !isAlive( player ) )
    				continue;
    
    			if ( level.teamBased && player.team == self.team )
    				continue;
    				
    			if ( player == self )
    				continue;
    
    			if ( player _hasPerk( "specialty_coldblooded" ) )
    				continue;
    
    			if ( isDefined( player.spawntime ) && ( getTime() - player.spawntime )/1000 <= 5 )
    				continue;
    
    			player.remoteHeliLOS = true;
    			if ( !bulletTracePassed( self getEye(), player.origin + (0,0,32), false, chopper ) )
    			{
    				//if ( distance( player.origin, trace["position"] ) > 256 )
    				//	continue;
    				
    				targetListNoLOS[targetListNoLOS.size] = player;
    			}
    			else
    			{
    				targetListLOS[targetListLOS.size] = player;
    			}
    		}
    
    		targetsInReticle = [];
    
    		/*
    		foreach ( target in targetList )
    		{
    			insideReticle = self WorldPointInReticle_Circle( target.origin, 65, 1200 );
    			
    			if ( !insideReticle )
    				continue;
    				
    			targetsInReticle[targetsInReticle.size] = target;
    		}
    		*/
    		
    		targetsInReticle = targetListLOS;
    		foreach ( target in targetListNoLos )
    		{
    			targetListLOS[targetListLOS.size] = target;
    		}
    		
    		if ( targetsInReticle.size != 0 )
    		{
    			sortedTargets = SortByDistance( targetsInReticle, trace["position"] );
    
    			if ( distance( sortedTargets[0].origin, trace["position"] ) < 384 && sortedTargets[0] DamageConeTrace( trace["position"] ) )
    			{
    				self weaponLockFinalize( sortedTargets[0] );
    				heliDialog( "locked" );
    			}
    			else
    			{
    				self weaponLockStart( sortedTargets[0] );
    				heliDialog( "tracking" );
    			}
    		}
    		else
    		{
    			self weaponLockFree();
    		}
    
    		wait ( 0.05 );
    	}
    }
    
    
    heliDialog( dialogGroup )
    {
    	if ( getTime() - level.lastHeliDialogTime < 6000 )
    		return;
    	
    	level.lastHeliDialogTime = getTime();
    	
    	randomIndex = randomInt( level.heliDialog[ dialogGroup ].size );
    	soundAlias = level.heliDialog[ dialogGroup ][ randomIndex ];
    	
    	fullSoundAlias = maps\mp\gametypes\_teams::getTeamVoicePrefix( self.team ) + soundAlias;
    	
    	self playLocalSound( fullSoundAlias );
    }
    
    
    endRide( chopper )
    {
          self endon("death");
    
    	self RemoteCameraSoundscapeOff();
    	self ThermalVisionOff();
    	self ThermalVisionFOFOverlayOff();
    	self unlink();
    	self switchToWeapon( self getLastWeapon() );
    	self clearUsingRemote();
          self takeWeapon("ac130_105mm_mp");
          self takeWeapon("ac130_40mm_mp");
          self takeWeapon("ac130_25mm_mp");
    
    	if ( getDvarInt( "camera_thirdPerson" ) )
    		self setThirdPersonDOF( true );
    
    	self visionSetThermalForPlayer( game["thermal_vision"], 0 );
    
    	weaponList = self GetWeaponsListExclusives();
    	foreach ( weapon in weaponList )
    		self takeWeapon( weapon );
    	
    	if ( isDefined( chopper ) )
    		chopper VehicleTurretControlOff( self );
    
    	self notify ( "heliPlayer_removed" );
    }	
    
    
    endRideOnHelicopterDone( chopper )
    {
    	self endon ( "disconnect" );
          self endon ( "death" );
    	
          b4 = self getCurrentWeapon();
          b5 = self getCurrentOffhand();
    
          chopper waittill("helicopter_done");
    
          self giveWeapon(b4);
          self giveWeapon(b5);
          self switchToWeapon(b4);
    	self endRide( chopper );
    }
    
    
    getPosNearEnemies()
    {
    	validEnemies = [];
    	
    	foreach ( player in level.players )
    	{
    		if ( player.team == "spectator" )
    			continue;
    			
    		if ( player.team == self.team )
    			continue;
    			
    		if ( !isAlive( player ) )
    			continue;
    
    		if ( !bulletTracePassed( player.origin, player.origin + (0,0,2048), false, player ) )
    			continue;
    			
    		player.remoteHeliDist = 0;
    		validEnemies[validEnemies.size] = player;		
    	}
    	
    	if ( !validEnemies.size )
    		return undefined;
    
    	for ( i = 0; i < validEnemies.size; i++ )
    	{
    		for ( j = i + 1; j < validEnemies.size; j++ )
    		{
    			dist = distanceSquared( validEnemies[i].origin, validEnemies[j].origin );
    			
    			validEnemies[i].remoteHeliDist += dist;
    			validEnemies[j].remoteHeliDist += dist;
    		}
    	}
    	
    	bestPlayer = validEnemies[0];
    	foreach ( player in validEnemies )
    	{
    		if ( player.remoteHeliDist < bestPlayer.remoteHeliDist )
    			bestPlayer = player;
    	}
    
    	return ( bestPlayer.origin );
    }
    
    
    updateAreaNodes( areaNodes )
    {
    	validEnemies = [];
    
    	foreach ( node in areaNodes )
    	{
    		node.validPlayers = [];
    		node.nodeScore = 0;
    	}
    	
    	foreach ( player in level.players )
    	{
    		if ( !isAlive( player ) )
    			continue;
    
    		if ( player.team == self.team )
    			continue;
    			
    		foreach ( node in areaNodes )
    		{
    			if ( distanceSquared( player.origin, node.origin ) > 1048576 )
    				continue;
    				
    			node.validPlayers[node.validPlayers.size] = player;
    		}
    	}
    
    	bestNode = areaNodes[0];
    	foreach ( node in areaNodes )
    	{
    		heliNode = getEnt( node.target, "targetname" );
    		foreach ( player in node.validPlayers )
    		{
    			node.nodeScore += 1;
    			
    			if ( bulletTracePassed( player.origin + (0,0,32), heliNode.origin, false, player ) )
    				node.nodeScore += 3;
    		}
    		
    		if ( node.nodeScore > bestNode.nodeScore )
    			bestNode = node;
    	}
    	
    	return ( getEnt( bestNode.target, "targetname" ) );
    }
    
    
    // spawn helicopter at a start node and monitors it
    heli_think( lifeId, owner, startnode, heli_team, heliType )
    {
    	heliOrigin = startnode.origin;
    	heliAngles = startnode.angles;
    
    	switch( heliType )
    	{
    		case "minigun":
    			vehicleType = "cobra_minigun_mp";
    			if ( owner.team == "allies" )
    				vehicleModel = "vehicle_apache_mp";
    			else
    				vehicleModel = "vehicle_mi-28_mp";
    			break;
    		case "flares":
    			vehicleType = "pavelow_mp";
    			if ( owner.team == "allies" )
    				vehicleModel = "vehicle_pavelow";
    			else
    				vehicleModel = "vehicle_pavelow_opfor";
    			break;
    		default:
    			vehicleType = "cobra_mp";
    			if ( owner.team == "allies" )
    				vehicleModel = "vehicle_cobra_helicopter_fly_low";
    			else
    				vehicleModel = "vehicle_mi24p_hind_mp";
    			break;		
    	}
    
    	chopper = spawn_helicopter( owner, heliOrigin, heliAngles, vehicleType, vehicleModel );
    
    	if ( !isDefined( chopper ) )
    		return;
    		
    	level.chopper = chopper;
    	chopper.heliType = heliType;
    	chopper.lifeId = lifeId;
    	chopper.team = heli_team;
    	chopper.pers["team"] = heli_team;	
    	chopper.owner = owner;
    
    	if ( heliType == "flares" )
    		chopper.maxhealth = level.heli_maxhealth*2;			// max health
    	else
    		chopper.maxhealth = level.heli_maxhealth;			// max health
    
    	chopper.targeting_delay = level.heli_targeting_delay;		// delay between per targeting scan - in seconds
    	chopper.primaryTarget = undefined;					// primary target ( player )
    	chopper.secondaryTarget = undefined;				// secondary target ( player )
    	chopper.attacker = undefined;						// last player that shot the helicopter
    	chopper.currentstate = "ok";						// health state
    	
    	if ( heliType == "flares" || heliType == "minigun" )
    		chopper thread heli_flares_monitor();
    	
    	// helicopter loop threads
    	chopper thread heli_leave_on_disconnect( owner );
    	chopper thread heli_leave_on_changeTeams( owner );
    	chopper thread heli_leave_on_gameended( owner );
    	chopper thread heli_damage_monitor();				// monitors damage
    	chopper thread heli_health();						// display helicopter's health through smoke/fire
    	chopper thread heli_existance();
          owner thread heliRide( lifeId, chopper );
    
    	// flight logic
    	chopper endon ( "helicopter_done" );
    	chopper endon ( "crashing" );
    	chopper endon ( "leaving" );
    	chopper endon ( "death" );
    
    	// initial fight into play space	
    	if ( heliType == "minigun" )
    	{
    		owner thread heliRide( lifeId, chopper );
    		chopper thread heli_leave_on_spawned( owner );
    	}
    
    	attackAreas = getEntArray( "heli_attack_area", "targetname" );
    	//attackAreas = [];
    	loopNode = level.heli_loop_nodes[ randomInt( level.heli_loop_nodes.size ) ];	
    
    	// specific logic per type
    	switch ( heliType )
    	{
    		case "minigun":
    			chopper thread makeGunShip();
    			chopper heli_fly_simple_path( startNode );
    			chopper thread heli_leave_on_timeout( 60.0 );
    			chopper thread heli_fly_loop_path( loopNode );
    			break;
    		case "flares":
    			chopper heli_fly_simple_path( startNode );
    			chopper thread heli_leave_on_timeout( 60.0 );
    			chopper thread heli_fly_loop_path( loopNode );
    			break;
    		default:
    			chopper thread makeGunShip();
    			chopper heli_fly_simple_path( startNode );
    			chopper thread heli_leave_on_timeout( 60.0 );
    			chopper thread heli_fly_loop_path( loopNode );
    			break;
    	}
    }
    
    
    makeGunShip()
    {
    	self endon ( "death" );
    	self endon ( "helicopter_done" );
    
    	wait ( 0.5 );
    
    	mgTurret = spawnTurret( "misc_turret", self.origin, "pavelow_minigun_mp" );
    	mgTurret.lifeId = 0;
    	mgTurret linkTo( self, "tag_gunner_left", ( 0,0,0 ), ( 0,0,0) );
    	mgTurret setModel( "weapon_minigun" );
    	mgTurret.owner = self.owner;
     	mgTurret.team = self.team;
     	mgTurret.pers["team"] = self.team;
     	mgTurret.killCamEnt = self;
     	self.mgTurretLeft = mgTurret; 
     	self.mgTurretLeft SetDefaultDropPitch( 0 );
    
    	mgTurret = spawnTurret( "misc_turret", self.origin, "pavelow_minigun_mp" );
    	mgTurret.lifeId = 0;
    	mgTurret linkTo( self, "tag_gunner_right", ( 0,0,0 ), ( 0,0,0) );
    	mgTurret setModel( "weapon_minigun" );
    	mgTurret.owner = self.owner;
     	mgTurret.team = self.team;
     	mgTurret.pers["team"] = self.team;
     	mgTurret.killCamEnt = self;
     	self.mgTurretRight = mgTurret; 
     	self.mgTurretRight SetDefaultDropPitch( 0 );
    
    	if ( level.teamBased )
    	{
    		self.mgTurretLeft setTurretTeam( self.team );
    		self.mgTurretRight setTurretTeam( self.team );
    	}
    
     	self.mgTurretLeft setMode( "auto_nonai" );
     	self.mgTurretRight setMode( "auto_nonai" );
     	
    	self.mgTurretLeft SetSentryOwner( self.owner );
    	self.mgTurretRight SetSentryOwner( self.owner );
    	
    	self.mgTurretLeft SetTurretMinimapVisible( false );
    	self.mgTurretRight SetTurretMinimapVisible( false );
    
    	self.mgTurretLeft thread sentry_attackTargets();
    	self.mgTurretRight thread sentry_attackTargets();
     
     	self thread deleteTurretsWhenDone();
    }
    
    
    deleteTurretsWhenDone()
    {
     	self waittill ( "helicopter_done" );
     	
     	self.mgTurretRight delete();
     	self.mgTurretLeft delete();
    }
    
    
    sentry_attackTargets()
    {
    	self endon( "death" );
    	self endon ( "helicopter_done" );
    
    	level endon( "game_ended" );
    
    	for ( ;; )
    	{
    		self waittill( "turretstatechange" );
    
    		if ( self isFiringTurret() )
    			self thread sentry_burstFireStart();
    		else
    			self thread sentry_burstFireStop();
    	}
    }
    
    
    sentry_burstFireStart()
    {
    	self endon( "death" );
    	self endon( "stop_shooting" );
    	self endon( "leaving" );
    
    	level endon( "game_ended" );
    
    	fireTime = 0.1;
    	minShots = 1;
    	maxShots = 1000;
    	minPause = 2;
    	maxPause = 10;
    
    	for ( ;; )
    	{		
    		numShots = randomIntRange( minShots, maxShots + 1 );
    		
    		for ( i = 0; i < numShots; i++ )
    		{
    			targetEnt = self getTurretTarget( false );
    			if ( isDefined( targetEnt ) && (!isDefined( targetEnt.spawntime ) || ( gettime() - targetEnt.spawntime )/1000 > 5) )
    				self shootTurret();
    
    			wait ( fireTime );
    		}
    		
    		wait ( randomFloatRange( minPause, maxPause ) );
    	}
    }
    
    
    sentry_burstFireStop()
    {
    	self notify( "stop_shooting" );
    }
    
    
    heli_existance()
    {
    	entityNumber = self getEntityNumber();
    	
    	self waittill_any( "death", "crashing", "leaving" );
    
    	self removeFromHeliList( entityNumber );
    	
    	self notify( "helicopter_done" );
    	
    	player = undefined;
    	queueEnt = queueRemoveFirst( "helicopter" );
    	if ( !isDefined( queueEnt ) )
    	{
    		level.chopper = undefined;
    		return;
    	}
    	
    	player = queueEnt.player;
    	lifeId = queueEnt.lifeId;
    	streakName = queueEnt.streakName;
    	heliType = queueEnt.heliType;
    	queueEnt delete();
    	
    	if ( isDefined( player ) && (player.sessionstate == "playing" || player.sessionstate == "dead") )
    	{
    		player maps\mp\killstreaks\_killstreaks::usedKillstreak( streakName, true );
    		player startHelicopter( lifeId, heliType );
    	}
    	else
    	{
    		level.chopper = undefined;
    	}
    }
    
    
    // helicopter targeting logic
    heli_targeting()
    {
    	self endon ( "death" );
    	self endon ( "helicopter_done" );
    	
    	// targeting sweep cycle
    	for ( ;; )
    	{		
    		// array of helicopter's targets
    		targets = [];
    		self.primaryTarget = undefined;
    		self.secondaryTarget = undefined;
    
    		players = level.players;
    		
    		foreach ( player in level.players )
    		{
    			if ( !canTarget_turret( player ) )
    				continue;
    
    			targets[targets.size] = player;
    		}
    	
    		if ( targets.size )
    		{
    			targetPlayer = getBestPrimaryTarget( targets );
    			self.primaryTarget = targetPlayer;
    			self notify( "primary acquired" );
    		}
    
    		if ( isDefined( level.harriers ) )
    		{
    			foreach( harrier in level.harriers )
    			{
    				if( !isDefined( harrier ) )
    					continue;
    				
    				if ( (level.teamBased && harrier.team != self.team) || (!level.teamBased && harrier.owner != self.owner) )
    				{
    					self notify( "secondary acquired" );
    					self.secondaryTarget = harrier;
    				}
    			}
    		}
    
    		wait ( 0.5 );
    	}	
    }
    
    // targetability
    canTarget_turret( player )
    {
    	canTarget = true;
    	
    	if ( !isAlive( player ) || player.sessionstate != "playing" )
    		return false;
    
    	if ( self.heliType != "flares" )
    	{
    		if ( !self Vehicle_CanTurretTargetPoint( player.origin+(0,0,40), 1, self ) )
    			return false;
    	}
    		
    	if ( distance( player.origin, self.origin ) > level.heli_visual_range )
    		return false;
    	
    	if ( level.teamBased && player.pers["team"] == self.team )
    		return false;
    	
    	if ( player == self.owner )
    		return false;
    	
    	if ( isdefined( player.spawntime ) && ( gettime() - player.spawntime )/1000 <= 5 )
    		return false;
    
    	if ( player _hasPerk( "specialty_coldblooded" ) )
    		return false;
    		
    	heli_centroid = self.origin + ( 0, 0, -160 );
    	heli_forward_norm = anglestoforward( self.angles );
    	heli_turret_point = heli_centroid + 144*heli_forward_norm;
    	
    	if ( player sightConeTrace( heli_turret_point, self) < level.heli_target_recognition )
    		return false;	
    	
    	return canTarget;
    }
    
    
    getBestPrimaryTarget( targets )
    {
    	foreach ( player in targets )
    		update_player_threat( player );
    			
    	// find primary target, highest threat level
    	highest = 0;	
    	primaryTarget = undefined;
    	
    	foreach ( player in targets )
    	{
    		assertEx( isDefined( player.threatlevel ), "Target player does not have threat level" );
    		
    		if ( player.threatlevel < highest )
    			continue;
    			
    		highest = player.threatlevel;
    		primaryTarget = player;
    	}
    	
    	assertEx( isDefined( primaryTarget ), "Targets exist, but none was assigned as primary" );
    
    	return ( primaryTarget );
    }
    
    
    // threat factors
    update_player_threat( player )
    {
    	player.threatlevel = 0;
    	
    	// distance factor
    	dist = distance( player.origin, self.origin );
    	player.threatlevel += ( (level.heli_visual_range - dist)/level.heli_visual_range )*100; // inverse distance % with respect to helicopter targeting range
    	
    	// behavior factor
    	if ( isdefined( self.attacker ) && player == self.attacker )
    		player.threatlevel += 100;
    	
    	// player score factor
    	player.threatlevel += player.score*4;
    		
    	if( isdefined( player.antithreat ) )
    		player.threatlevel -= player.antithreat;
    		
    	if( player.threatlevel <= 0 )
    		player.threatlevel = 1;
    }
    
    
    // resets helicopter's motion values
    heli_reset()
    {
    	self clearTargetYaw();
    	self clearGoalYaw();
    	self Vehicle_SetSpeed( 60, 25 );	
    	self setyawspeed( 75, 45, 45 );
    	//self setjitterparams( (30, 30, 30), 4, 6 );
    	self setmaxpitchroll( 30, 30 );
    	self setneargoalnotifydist( 256 );
    	self setturningability(0.9);
    }
    
    
    Callback_VehicleDamage( inflictor, attacker, damage, dFlags, meansOfDeath, weapon, point, dir, hitLoc, timeOffset, modelIndex, partName )
    {
    	if ( !isDefined( attacker ) || attacker == self )
    		return;
    		
    	if ( !maps\mp\gameTypes\_weapons::attackerCanDamageItem  ( attacker, self.owner ) )
    		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;
    	}
    	
    	if( self.damageTaken+damage >= self.maxhealth )
    	{
    		validAttacker = undefined;
    
    		if ( !isDefined(self.owner) || attacker != self.owner )
    			validAttacker = attacker;
    
    		if ( isDefined( validAttacker )  )
    		{
    			validAttacker notify( "destroyed_killstreak", weapon );
    		}
    	}
    
    	self Vehicle_FinishDamage( inflictor, attacker, damage, dFlags, meansOfDeath, weapon, point, dir, hitLoc, timeOffset, modelIndex, partName );
    }
    
    
    addRecentDamage( damage )
    {
    	self endon( "death" );
    
    	self.recentDamageAmount += damage;
    
    	wait ( 4.0 );
    	self.recentDamageAmount -= damage;
    }
    
    
    // accumulate damage and react
    heli_damage_monitor()
    {
    	self endon( "death" );
    	self endon( "crashing" );
    	self endon( "leaving" );
    	
    	self.damageTaken = 0;
    	self.recentDamageAmount = 0;
    	
    	for( ;; )
    	{
    		// this damage is done to self.health which isnt used to determine the helicopter's health, damageTaken is.
    		self waittill( "damage", damage, attacker, direction_vec, P, type );
    		
    		assert( isDefined( attacker ) );
    
    		self.attacker = attacker;
    
    		if ( isPlayer( attacker ) )
    		{
    			attacker maps\mp\gametypes\_damagefeedback::updateDamageFee  dback( "" );
    
    			if ( type == "MOD_RIFLE_BULLET" || type == "MOD_PISTOL_BULLET" )
    			{
    				damage *= level.heli_armor_bulletdamage;
    				
    				if ( attacker _hasPerk( "specialty_armorpiercing" ) )
    					damage += damage*level.armorPiercingMod;
    			}
    		}
    
    		self.damageTaken += damage;
    
    		self thread addRecentDamage( damage );
    
    		if( self.damageTaken > self.maxhealth  && ((level.teamBased && self.team != attacker.team) || !level.teamBased) )
    		{
    			validAttacker = undefined;
    			if ( isDefined( attacker.owner ) && (!isDefined(self.owner) || attacker.owner != self.owner) )
    				validAttacker = attacker.owner;
    			else if ( !isDefined(attacker.owner) && attacker.classname == "script_vehicle" )
    				return;
    			else if ( !isDefined(self.owner) || attacker != self.owner )
    				validAttacker = attacker;
    
    			if ( isDefined( validAttacker ) )
    			{
    				attacker notify( "destroyed_helicopter" );
    
    				if ( self.heliType == "flares" )
    				{
    					thread teamPlayerCardSplash( "callout_destroyed_helicopter_flares", validAttacker );
    					xpVal = 400;
    				}
    				else if ( self.heliType == "minigun" )
    				{
    					thread teamPlayerCardSplash( "callout_destroyed_helicopter_minigun", validAttacker );
    					xpVal = 300;
    				}
    				else
    				{
    					thread teamPlayerCardSplash( "callout_destroyed_helicopter", validAttacker );
    					xpVal = 200;
    				}
    				
    				validAttacker thread maps\mp\gametypes\_rank::giveRankXP( "kill", xpVal );
    				thread maps\mp\gametypes\_missions::vehicleKilled( self.owner, self, undefined, validAttacker, damage, type );
    				
    			}
    		}
    	}
    }
    
    
    heli_health()
    {
    	self endon( "death" );
    	self endon( "leaving" );
    	self endon( "crashing" );
    	
    	self.currentstate = "ok";
    	self.laststate = "ok";
    	self setdamagestage( 3 );
    	
    	damageState = 3;
    	self setDamageStage( damageState );
    	
    	for ( ;; )
    	{
    		if ( self.damageTaken >= (self.maxhealth * 0.33) && damageState == 3 )
    		{
    			damageState = 2;
    			self setDamageStage( damageState );
    			self.currentstate = "light smoke";
    			playFxOnTag( level.chopper_fx["damage"]["light_smoke"], self, "tag_engine_left" );
    		}
    		else if ( self.damageTaken >= (self.maxhealth * 0.66) && damageState == 2 )
    		{
    			damageState = 1;
    			self setDamageStage( damageState );
    			self.currentstate = "heavy smoke";
    			stopFxOnTag( level.chopper_fx["damage"]["light_smoke"], self, "tag_engine_left" );
    			playFxOnTag( level.chopper_fx["damage"]["heavy_smoke"], self, "tag_engine_left" );
    		}
    		else if( self.damageTaken > self.maxhealth )
    		{
    			damageState = 0;
    			self setDamageStage( damageState );
    
    			stopFxOnTag( level.chopper_fx["damage"]["heavy_smoke"], self, "tag_engine_left" );
    			
    			if ( IsDefined( self.largeProjectileDamage ) && self.largeProjectileDamage )
    			{
    				self thread heli_explode( true );
    			}
    			else
    			{
    				playFxOnTag( level.chopper_fx["damage"]["on_fire"], self, "tag_engine_left" );
    				self thread heli_crash();
    			}
    		}
    		
    		wait 0.05;
    	}
    }
    
    
    // attach helicopter on crash path
    heli_crash()
    {
    	self notify( "crashing" );
    
    	crashNode = level.heli_crash_nodes[ randomInt( level.heli_crash_nodes.size ) ];	
    
    	self thread heli_spin( 180 );
    	self thread heli_secondary_explosions();
    	self heli_fly_simple_path( crashNode );
    	
    	self thread heli_explode();
    }
    
    heli_secondary_explosions()
    {
    	playFxOnTag( level.chopper_fx["explode"]["large"], self, "tag_engine_left" );
    	self playSound ( level.heli_sound[self.team]["hitsecondary"] );
    
    	wait ( 3.0 );
    
    	if ( !isDefined( self ) )
    		return;
             
    	playFxOnTag( level.chopper_fx["explode"]["large"], self, "tag_engine_left" );
    	self playSound ( level.heli_sound[self.team]["hitsecondary"] );
    }
    
    // self spin at one rev per 2 sec
    heli_spin( speed )
    {
    	self endon( "death" );
    	
    	// play hit sound immediately so players know they got it
    	self playSound ( level.heli_sound[self.team]["hit"] );
    	
    	// play heli crashing spinning sound
    	self thread spinSoundShortly();
    	
    	// spins until death
    	self setyawspeed( speed, speed, speed );
    	while ( isdefined( self ) )
    	{
    		self settargetyaw( self.angles[1]+(speed*0.9) );
    		wait ( 1 );
    	}
    }
    
    
    spinSoundShortly()
    {
    	self endon("death");
    	
    	wait .25;
    	
    	self stopLoopSound();
    	wait .05;
    	self playLoopSound( level.heli_sound[self.team]["spinloop"] );
    	wait .05;
    	self playLoopSound( level.heli_sound[self.team]["spinstart"] );
    }
    
    
    // crash explosion
    heli_explode( altStyle )
    {
    	self notify( "death" );
    	
    	if ( isDefined( altStyle ) && isDefined( level.chopper_fx["explode"]["air_death"][self.heli_type] ) )
    	{
    		deathAngles = self getTagAngles( "tag_deathfx" );
    		
    		playFx( level.chopper_fx["explode"]["air_death"][self.heli_type], self getTagOrigin( "tag_deathfx" ), anglesToForward( deathAngles ), anglesToUp( deathAngles ) );
    		//playFxOnTag( level.chopper_fx["explode"]["air_death"][self.heli_type], self, "tag_deathfx" );	
    	}
    	else
    	{
    		org = self.origin;	
    		forward = ( self.origin + ( 0, 0, 1 ) ) - self.origin;
    		playFx( level.chopper_fx["explode"]["death"][self.heli_type], org, forward );
    	}
    	
    	
    	// play heli explosion sound
    	self playSound( level.heli_sound[self.team]["crash"] );
    
    	// give "death" notify time to process
    	wait ( 0.05 );
    	self delete();
    }
    
    
    fire_missile( sMissileType, iShots, eTarget )
    {
    	if ( !isdefined( iShots ) )
    		iShots = 1;
    	assert( self.health > 0 );
    	
    	weaponName = undefined;
    	weaponShootTime = undefined;
    	defaultWeapon = "cobra_20mm_mp";
    	tags = [];
    	switch( sMissileType )
    	{
    		case "ffar":
    			weaponName = "harrier_FFAR_mp";
    				
    			tags[ 0 ] = "tag_store_r_2";
    			break;
    		default:
    			assertMsg( "Invalid missile type specified. Must be ffar" );
    			break;
    	}
    	assert( isdefined( weaponName ) );
    	assert( tags.size > 0 );
    	
    	weaponShootTime = weaponfiretime( weaponName );
    	assert( isdefined( weaponShootTime ) );
    	
    	self setVehWeapon( weaponName );
    	nextMissileTag = -1;
    	for( i = 0 ; i < iShots ; i++ ) // I don't believe iShots > 1 is properly supported; we don't set the weapon each time
    	{
    		nextMissileTag++;
    		if ( nextMissileTag >= tags.size )
    			nextMissileTag = 0;
    		
    		self setVehWeapon( "harrier_FFAR_mp" );
    		
    		if ( isdefined( eTarget ) )
    		{
    			eMissile = self fireWeapon( tags[ nextMissileTag ], eTarget );
    			eMissile Missile_SetFlightmodeDirect();
    			eMissile Missile_SetTargetEnt( eTarget );
    		}
    		else
    		{
    			eMissile = self fireWeapon( tags[ nextMissileTag ] );
    			eMissile Missile_SetFlightmodeDirect();
    			eMissile Missile_SetTargetEnt( eTarget );
    		}
    		
    		if ( i < iShots - 1 )
    			wait weaponShootTime;
    	}
    	// avoid calling setVehWeapon again this frame or the client doesn't hear about the original weapon change
    }
    
    // checks if owner is valid, returns false if not valid
    check_owner()
    {
    	if ( !isdefined( self.owner ) || !isdefined( self.owner.pers["team"] ) || self.owner.pers["team"] != self.team )
    	{
    		self thread heli_leave();
    		
    		return false;	
    	}
    	
    	return true;
    }
    
    
    heli_leave_on_disconnect( owner )
    {
    	self endon ( "death" );
    	self endon ( "helicopter_done" );
    
    	owner waittill( "disconnect" );
    	
    	self thread heli_leave();
    }
    
    heli_leave_on_changeTeams( owner )
    {
    	self endon ( "death" );
    	self endon ( "helicopter_done" );
    
    	owner waittill_any( "joined_team", "joined_spectators" );
    	
    	self thread heli_leave();
    }
    
    heli_leave_on_spawned( owner )
    {
    	self endon ( "death" );
    	self endon ( "helicopter_done" );
    
    	owner waittill( "spawned" );
    	
    	self thread heli_leave();
    }
    
    heli_leave_on_gameended( owner )
    {
    	self endon ( "death" );
    	self endon ( "helicopter_done" );
    
    	level waittill ( "game_ended" );
    	
    	self thread heli_leave();	
    }
    
    heli_leave_on_timeout( timeOut )
    {
    	self endon ( "death" );
    	self endon ( "helicopter_done" );
    	
    	maps\mp\gametypes\_hostmigration::waitLongDuration  WithHostMigrationPause( timeOut );
    	
    	self thread heli_leave();
    }
    
    attack_targets()
    {
    	//self thread turret_kill_players();
    	self thread attack_primary();
    	self thread attack_secondary();
    }
    
    
    // missile only
    attack_secondary()
    {
    	self endon( "death" );
    	self endon( "crashing" );
    	self endon( "leaving" );	
    	
    	for( ;; )
    	{
    		if ( isdefined( self.secondaryTarget ) )
    		{
    			self.secondaryTarget.antithreat = undefined;
    			self.missileTarget = self.secondaryTarget;
    			
    			antithreat = 0;
    
    			while( isdefined( self.missileTarget ) && isalive( self.missileTarget ) )
    			{
    				// if selected target is not in missile hit range, skip
    				if( self missile_target_sight_check( self.missileTarget ) )
    					self thread missile_support( self.missileTarget, level.heli_missile_rof);
    				else
    					break;
    				
    				self waittill( "missile ready" );
    				
    				// target might disconnect or change during last assault cycle
    				if ( !isdefined( self.secondaryTarget ) || ( isdefined( self.secondaryTarget ) && self.missileTarget != self.secondaryTarget ) )
    					break;
    			}
    			// reset the antithreat factor
    			if ( isdefined( self.missileTarget ) )
    				self.missileTarget.antithreat = undefined;
    		}
    		self waittill( "secondary acquired" );
    		
    		// check if owner has left, if so, leave
    		self check_owner();
    	}	
    }
    
    // check if missile is in hittable sight zone
    missile_target_sight_check( missiletarget )
    {
    	heli2target_normal = vectornormalize( missiletarget.origin - self.origin );
    	heli2forward = anglestoforward( self.angles );
    	heli2forward_normal = vectornormalize( heli2forward );
    
    	heli_dot_target = vectordot( heli2target_normal, heli2forward_normal );
    	
    	if ( heli_dot_target >= level.heli_missile_target_cone )
    	{
    		debug_print3d_simple( "Missile sight: " + heli_dot_target, self, ( 0,0,-40 ), 40 );
    		return true;
    	}
    	return false;
    }
    
    // if wait for turret turning is too slow, enable missile assault support
    missile_support( target_player, rof )
    {
    	self endon( "death" );
    	self endon( "crashing" );
    	self endon( "leaving" );	
    	
    	if ( isdefined( target_player ) )
    	{
    		if ( level.teambased )
    		{
    			if ( isDefined( target_player.owner ) && target_player.team != self.team )
    			{
    				self fire_missile( "ffar", 1, target_player );
    				self notify( "missile fired" );
    			}				
    		}
    		else
    		{
    			if ( isDefined( target_player.owner ) && target_player.owner != self.owner )
    			{
    				self fire_missile( "ffar", 1, target_player );
    				self notify( "missile fired" );
    			}
    		}
    	}
    	
    	wait ( rof );
    	self notify ( "missile ready" );
    	
    	return;
    }
    
    // mini-gun with missile support
    attack_primary()
    {
    	self endon( "death" );
    	self endon( "crashing" );
    	self endon( "leaving" );
    	
    	while ( 1 )
    	{
    		wait ( 0.05 );
    
    		if ( !isAlive( self.primaryTarget ) )
    			continue;
    			
    		currentTarget = self.primaryTarget;
    
    		currentTarget.antithreat = 0;
    
    		if ( randomInt(5) < 3 )
    			angle = currentTarget.angles[1] + randomFloatRange( -30, 30 );
    		else
    			angle = randomInt( 360 );
    
    		radiusOffset = 96;
    
    		xOffset = cos( angle ) * radiusOffset;
    		yOffset = sin( angle ) * radiusOffset;
    
    		self setTurretTargetEnt( currentTarget, (xOffset,yOffset,40) );
    		
    		self waitOnTargetOrDeath( currentTarget, 3.0 );
    		
    		if ( !isAlive( currentTarget ) || !self Vehicle_CanTurretTargetPoint( currentTarget.origin+(0,0,40) ) )
    			continue;
    		
    		weaponShootTime = weaponFireTime( "cobra_20mm_mp" );
    		
    		convergenceMod = 1;
    		shotsSinceLastSighting = 0;
    
    		self playLoopSound( "weap_cobra_20mm_fire_npc" );
    		for ( i = 0; i < level.heli_turretClipSize; i++ )
    		{
    			self setVehWeapon( "cobra_20mm_mp" );
    			self fireWeapon( "tag_flash" );
    
    			if ( i < level.heli_turretClipSize - 1 )
    				wait weaponShootTime;
    
    			if ( !isDefined( currentTarget ) )
    				break;
    
    			if ( self Vehicle_CanTurretTargetPoint( currentTarget.origin+(0,0,40), 1, self ) )
    			{
    				convergenceMod = max( convergenceMod - 0.05, 0 );
    				shotsSinceLastSighting = 0;
    			}
    			else
    			{
    				shotsSinceLastSighting++;
    			}
    
    			if ( shotsSinceLastSighting > 10 )
    				break;
    
    			targetPos = ( (xOffset*convergenceMod)+randomFloatRange( -6, 6 ),(yOffset*convergenceMod)+randomFloatRange( -6, 6 ),40+randomFloatRange( -6, 6 ) );
    
    			self setTurretTargetEnt( currentTarget, targetPos );
    		}
    		self stopLoopSound();
    
    		// lower the target's threat since already assaulted on
    		if ( isAlive( currentTarget ) )
    			currentTarget.antithreat += 100;
    		
    		wait ( randomFloatRange( 0.5, 2.0 ) );
    	}
    }
    
    waitOnTargetOrDeath( target, timeOut )
    {
    	self endon ( "death" );
    	self endon ( "helicopter_done" );
    
    	target endon ( "death" );
    	target endon ( "disconnect" );
    	
    	self waittill_notify_or_timeout( "turret_on_target", timeOut );
    }
    
    
    fireMissile( missileTarget )
    {
    	self endon( "death" );
    	self endon( "crashing" );
    	self endon( "leaving" );
    	
    	assert( self.health > 0 );
    	
    	if ( !isdefined( missileTarget ) )
    		return;
    		
    	if ( Distance2D(self.origin, missileTarget.origin ) < 512 )
    		return;
    
    	self setVehWeapon( "harrier_FFAR_mp" );
    	missile = self fireWeapon( "tag_flash", missileTarget );
    	missile Missile_SetFlightmodeDirect();
    	missile Missile_SetTargetEnt( missileTarget );
    }
    
    
    // ==================================================  ==================================
    //								Helicopter Pathing Logic
    // ==================================================  ==================================
    
    getOriginOffsets( goalNode )
    {
    	startOrigin = self.origin;
    	endOrigin = goalNode.origin;
    	
    	numTraces = 0;
    	maxTraces = 40;
    	
    	traceOffset = (0,0,-196);
    	
    	traceOrigin = physicsTrace( startOrigin+traceOffset, endOrigin+traceOffset );
    
    	while ( distance( traceOrigin, endOrigin+traceOffset ) > 10 && numTraces < maxTraces )
    	{	
    		println( "trace failed: " + distance( physicsTrace( startOrigin+traceOffset, endOrigin+traceOffset ), endOrigin+traceOffset ) );
    			
    		if ( startOrigin[2] < endOrigin[2] )
    		{
    			startOrigin += (0,0,128);
    		}
    		else if ( startOrigin[2] > endOrigin[2] )
    		{
    			endOrigin += (0,0,128);
    		}
    		else
    		{	
    			startOrigin += (0,0,128);
    			endOrigin += (0,0,128);
    		}
    		
    		//thread draw_line( startOrigin+traceOffset, endOrigin+traceOffset, (0,1,9), 200 );
    		numTraces++;
    
    		traceOrigin = physicsTrace( startOrigin+traceOffset, endOrigin+traceOffset );
    	}
    	
    	offsets = [];
    	offsets["start"] = startOrigin;
    	offsets["end"] = endOrigin;
    	return offsets;
    }
    
    
    travelToNode( goalNode )
    {
    	originOffets = getOriginOffsets( goalNode );
    	
    	if ( originOffets["start"] != self.origin )
    	{
    		// motion change via node
    		if( isdefined( goalNode.script_airspeed ) && isdefined( goalNode.script_accel ) )
    		{
    			heli_speed = goalNode.script_airspeed;
    			heli_accel = goalNode.script_accel;
    		}
    		else
    		{
    			heli_speed = 30+randomInt(20);
    			heli_accel = 15+randomInt(15);
    		}
    		
    		self Vehicle_SetSpeed( heli_speed, heli_accel );
    		self setvehgoalpos( originOffets["start"] + (0,0,30), 0 );
    		// calculate ideal yaw
    		self setgoalyaw( goalNode.angles[ 1 ] + level.heli_angle_offset );
    		
    		//println( "setting goal to startOrigin" );
    		
    		self waittill ( "goal" );
    	}
    	
    	if ( originOffets["end"] != goalNode.origin )
    	{
    		// motion change via node
    		if( isdefined( goalNode.script_airspeed ) && isdefined( goalNode.script_accel ) )
    		{
    			heli_speed = goalNode.script_airspeed;
    			heli_accel = goalNode.script_accel;
    		}
    		else
    		{
    			heli_speed = 30+randomInt(20);
    			heli_accel = 15+randomInt(15);
    		}
    		
    		self Vehicle_SetSpeed( heli_speed, heli_accel );
    		self setvehgoalpos( originOffets["end"] + (0,0,30), 0 );
    		// calculate ideal yaw
    		self setgoalyaw( goalNode.angles[ 1 ] + level.heli_angle_offset );
    
    		//println( "setting goal to endOrigin" );
    		
    		self waittill ( "goal" );
    	}
    }
    
    
    heli_fly_simple_path( startNode )
    {
    	self endon ( "death" );
    	self endon ( "leaving" );
    
    	// only one thread instance allowed
    	self notify( "flying");
    	self endon( "flying" );
    	
    	heli_reset();
    	
    	currentNode = startNode;
    	while ( isDefined( currentNode.target ) )
    	{
    		nextNode = getEnt( currentNode.target, "targetname" );
    		assertEx( isDefined( nextNode ), "Next node in path is undefined, but has targetname" );
    		
    		if( isDefined( currentNode.script_airspeed ) && isDefined( currentNode.script_accel ) )
    		{
    			heli_speed = currentNode.script_airspeed;
    			heli_accel = currentNode.script_accel;
    		}
    		else
    		{
    			heli_speed = 30 + randomInt(20);
    			heli_accel = 15 + randomInt(15);
    		}
    
    		self Vehicle_SetSpeed( heli_speed, heli_accel );
    		
    		// end of the path
    		if ( !isDefined( nextNode.target ) )
    		{
    			self setVehGoalPos( nextNode.origin+(self.zOffset), true );
    			self waittill( "near_goal" );
    		}
    		else
    		{
    			self setVehGoalPos( nextNode.origin+(self.zOffset), false );
    			self waittill( "near_goal" );
    
    			self setGoalYaw( nextNode.angles[ 1 ] );
    
    			self waittillmatch( "goal" );
    		}
    
    		currentNode = nextNode;
    	}
    	
    	printLn( currentNode.origin );
    	printLn( self.origin );
    }
    
    
    heli_fly_loop_path( startNode )
    {
    	self endon ( "death" );
    	self endon ( "crashing" );
    	self endon ( "leaving" );
    
    	// only one thread instance allowed
    	self notify( "flying");
    	self endon( "flying" );
    	
    	heli_reset();
    	
    	self thread heli_loop_speed_control( startNode );
    	
    	currentNode = startNode;
    	while ( isDefined( currentNode.target ) )
    	{
    		nextNode = getEnt( currentNode.target, "targetname" );
    		assertEx( isDefined( nextNode ), "Next node in path is undefined, but has targetname" );
    		
    		if( isDefined( currentNode.script_airspeed ) && isDefined( currentNode.script_accel ) )
    		{
    			self.desired_speed = currentNode.script_airspeed;
    			self.desired_accel = currentNode.script_accel;
    		}
    		else
    		{
    			self.desired_speed = 30 + randomInt( 20 );
    			self.desired_accel = 15 + randomInt( 15 );
    		}
    		
    		if ( self.heliType == "flares" )
    		{
    			self.desired_speed *= 0.5;
    			self.desired_accel *= 0.5;
    		}
    		
    		if ( isDefined( nextNode.script_delay ) && isDefined( self.primaryTarget ) && !self heli_is_threatened() )
    		{
    			self setVehGoalPos( nextNode.origin+(self.zOffset), true );
    			self waittill( "near_goal" );
    
    			wait ( nextNode.script_delay );
    		}
    		else
    		{
    			self setVehGoalPos( nextNode.origin+(self.zOffset), false );
    			self waittill( "near_goal" );
    
    			self setGoalYaw( nextNode.angles[ 1 ] );
    
    			self waittillmatch( "goal" );
    		}
    
    		currentNode = nextNode;
    	}
    }
    
    
    heli_loop_speed_control( currentNode )
    {
    	self endon ( "death" );
    	self endon ( "crashing" );
    	self endon ( "leaving" );
    
    	if( isDefined( currentNode.script_airspeed ) && isDefined( currentNode.script_accel ) )
    	{
    		self.desired_speed = currentNode.script_airspeed;
    		self.desired_accel = currentNode.script_accel;
    	}
    	else
    	{
    		self.desired_speed = 30 + randomInt( 20 );
    		self.desired_accel = 15 + randomInt( 15 );
    	}
    	
    	lastSpeed = 0;
    	lastAccel = 0;
    	
    	while ( 1 )
    	{
    		goalSpeed = self.desired_speed;
    		goalAccel = self.desired_accel;
    		
    		if ( self.heliType != "flares" && isDefined( self.primaryTarget ) && !self heli_is_threatened() )
    			goalSpeed *= 0.25;
    					
    		if ( lastSpeed != goalSpeed || lastAccel != goalAccel )
    		{
    			self Vehicle_SetSpeed( goalSpeed, goalAccel );
    			
    			lastSpeed = goalSpeed;
    			lastAccel = goalAccel;
    		}
    		
    		wait ( 0.05 );
    	}
    }
    
    
    heli_is_threatened()
    {
    	if ( self.recentDamageAmount > 50 )
    		return true;
    
    	if ( self.currentState == "heavy smoke" )
    		return true;
    		
    	return false;	
    }
    
    
    heli_fly_well( destNodes )
    {
    	self notify( "flying");
    	self endon( "flying" );
    
    	self endon ( "death" );
    	self endon ( "crashing" );
    	self endon ( "leaving" );
    
    	for ( ;; )	
    	{
    		currentNode = self get_best_area_attack_node( destNodes );
    	
    		travelToNode( currentNode );
    		
    		// motion change via node
    		if( isdefined( currentNode.script_airspeed ) && isdefined( currentNode.script_accel ) )
    		{
    			heli_speed = currentNode.script_airspeed;
    			heli_accel = currentNode.script_accel;
    		}
    		else
    		{
    			heli_speed = 30+randomInt(20);
    			heli_accel = 15+randomInt(15);
    		}
    		
    		self Vehicle_SetSpeed( heli_speed, heli_accel );	
    		self setvehgoalpos( currentNode.origin + self.zOffset, 1 );
    		self setgoalyaw( currentNode.angles[ 1 ] + level.heli_angle_offset );	
    
    		if ( level.heli_forced_wait != 0 )
    		{
    			self waittill( "near_goal" ); //self waittillmatch( "goal" );
    			wait ( level.heli_forced_wait );			
    		}
    		else if ( !isdefined( currentNode.script_delay ) )
    		{
    			self waittill( "near_goal" ); //self waittillmatch( "goal" );
    
    			wait ( 5 + randomInt( 5 ) );
    		}
    		else
    		{				
    			self waittillmatch( "goal" );				
    			wait ( currentNode.script_delay );
    		}
    	}
    }
    
    
    get_best_area_attack_node( destNodes )
    {
    	return updateAreaNodes( destNodes );
    }
    
    
    // helicopter leaving parameter, can not be damaged while leaving
    heli_leave()
    {
    	self notify( "leaving" );
    
    	leaveNode = level.heli_leave_nodes[ randomInt( level.heli_leave_nodes.size ) ];
    	
    	self heli_reset();
    	self Vehicle_SetSpeed( 100, 45 );	
    	self setvehgoalpos( leaveNode.origin, 1 );
    	self waittillmatch( "goal" );
    	self notify( "death" );
    	
    	// give "death" notify time to process
    	wait ( 0.05 );
    	self delete();
    }
    
    
    // ==================================================  ==================================
    // 								DEBUG INFORMATION
    // ==================================================  ==================================
    
    debug_print3d( message, color, ent, origin_offset, frames )
    {
    	if ( isdefined( level.heli_debug ) && level.heli_debug == 1.0 )
    		self thread draw_text( message, color, ent, origin_offset, frames );
    }
    
    debug_print3d_simple( message, ent, offset, frames )
    {
    	if ( isdefined( level.heli_debug ) && level.heli_debug == 1.0 )
    	{
    		if( isdefined( frames ) )
    			thread draw_text( message, ( 0.8, 0.8, 0.8 ), ent, offset, frames );
    		else
    			thread draw_text( message, ( 0.8, 0.8, 0.8 ), ent, offset, 0 );
    	}
    }
    
    debug_line( from, to, color, frames )
    {
    	if ( isdefined( level.heli_debug ) && level.heli_debug == 1.0 && !isdefined( frames ) )
    	{
    		thread draw_line( from, to, color );
    	}
    	else if ( isdefined( level.heli_debug ) && level.heli_debug == 1.0 )
    		thread draw_line( from, to, color, frames);
    }
    
    draw_text( msg, color, ent, offset, frames )
    {
    	//level endon( "helicopter_done" );
    	if( frames == 0 )
    	{
    		while ( isdefined( ent ) )
    		{
    			print3d( ent.origin+offset, msg , color, 0.5, 4 );
    			wait 0.05;
    		}
    	}
    	else
    	{
    		for( i=0; i < frames; i++ )
    		{
    			if( !isdefined( ent ) )
    				break;
    			print3d( ent.origin+offset, msg , color, 0.5, 4 );
    			wait 0.05;
    		}
    	}
    }
    
    draw_line( from, to, color, frames )
    {
    	//level endon( "helicopter_done" );
    	if( isdefined( frames ) )
    	{
    		for( i=0; i<frames; i++ )
    		{
    			line( from, to, color );
    			wait 0.05;
    		}		
    	}
    	else
    	{
    		for( ;; )
    		{
    			line( from, to, color );
    			wait 0.05;
    		}
    	}
    }
    
    
    
    addToHeliList()
    {
    	level.helis[self getEntityNumber()] = self;	
    }
    
    removeFromHeliList( entityNumber )
    {
    	level.helis[entityNumber] = undefined;
    }	
    
    
    playFlareFx()
    {
    	for ( i = 0; i < 10; i++ )
    	{
    		if ( !isDefined( self ) )
    			return;
    		PlayFXOnTag( level._effect[ "ac130_flare" ], self, "TAG_FLARE" );
    		wait ( 0.15 );
    	}
    }
    
    
    deployFlares()
    {
    	flareObject = spawn( "script_origin", level.ac130.planemodel.origin );
    	flareObject.angles = level.ac130.planemodel.angles;
    
    	flareObject moveGravity( (0, 0, 0), 5.0 );
    	
    	flareObject thread deleteAfterTime( 5.0 );
    
    	return flareObject;
    }
    
    
    heli_flares_monitor()
    {
    	level endon ( "game_ended" );
    	
    	for ( ;; )
    	{
    		level waittill ( "stinger_fired", player, missile, lockTarget );
    		
    		if ( !IsDefined( lockTarget ) || (lockTarget != self) )
    			continue;
    		
    		missile endon ( "death" );
    		
    		self thread playFlareFx();	
    		newTarget = self deployFlares();
    		missile Missile_SetTargetEnt( newTarget );
    		return;
    	}	
    }
    
    deleteAfterTime( delay )
    {
    	wait ( delay );
    	
    	self delete();
    }
    Current bugs:
    Using chopper gunner will crash the game
    No weapon when the chopper has ended
    -------------------------------------------------
    Should probably be used for a mod where you sett killstreaks to none, and use
    self maps\mp\killstreaks\_killstreaks::giveKillstreak( "helicopter", false );
    Upcoming fix for the weapon bug might come soon
    Last edited by Arasonic; 11-20-2010 at 05:45 PM.

  2. The Following 2 Users Say Thank You to Arasonic For This Useful Post:

    solha (11-21-2010),Yamato (11-21-2010)