Advertisement
4FunPlayin

Deadly AC130 / Perfect Replacement for Nuke

Oct 4th, 2011
324
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 56.98 KB | None | 0 0
  1. //must go to _helicopter.gsc in your killstreak folder
  2.  
  3. #include maps\mp\_utility;
  4. #include maps\mp\gametypes\_hud_util;
  5. #include common_scripts\utility;
  6.  
  7.  
  8. init()
  9. {
  10. path_start = getentarray( "heli_start", "targetname" ); // start pointers, point to the actual start node on path
  11. loop_start = getentarray( "heli_loop_start", "targetname" ); // start pointers for loop path in the map
  12.  
  13. if ( !path_start.size && !loop_start.size)
  14. return;
  15.  
  16. level.heli_types = [];
  17.  
  18. precacheHelicopter( "vehicle_cobra_helicopter_fly_low", "cobra" );
  19. precacheHelicopter( "vehicle_mi24p_hind_mp", "hind" );
  20. precacheHelicopter( "vehicle_mi-28_mp", "mi28" );
  21. precacheHelicopter( "vehicle_apache_mp", "apache" );
  22. precacheHelicopter( "vehicle_pavelow", "pavelow" );
  23. precacheHelicopter( "vehicle_pavelow_opfor", "pavelow" );
  24. precacheHelicopter( "vehicle_little_bird_armed", "cobra" );
  25.  
  26. precacheitem( "cobra_FFAR_mp" );
  27. precacheitem( "cobra_20mm_mp" );
  28. precacheitem( "cobra_player_minigun_mp" );
  29. precacheitem( "heli_remote_mp" );
  30. precacheVehicle( "cobra_mp" );
  31. precacheVehicle( "cobra_minigun_mp" );
  32. precacheVehicle( "pavelow_mp" );
  33. precacheTurret( "pavelow_minigun_mp" );
  34. precacheString( &"MP_CIVILIAN_AIR_TRAFFIC" );
  35.  
  36. level.chopper = undefined;
  37.  
  38. // array of paths, each element is an array of start nodes that all leads to a single destination node
  39. level.heli_start_nodes = getEntArray( "heli_start", "targetname" );
  40. assertEx( level.heli_start_nodes.size, "No \"heli_start\" nodes found in map!" );
  41.  
  42. level.heli_loop_nodes = getEntArray( "heli_loop_start", "targetname" );
  43. assertEx( level.heli_loop_nodes.size, "No \"heli_loop_start\" nodes found in map!" );
  44.  
  45. level.heli_leave_nodes = getEntArray( "heli_leave", "targetname" );
  46. assertEx( level.heli_leave_nodes.size, "No \"heli_leave\" nodes found in map!" );
  47.  
  48. level.heli_crash_nodes = getEntArray( "heli_crash_start", "targetname" );
  49. assertEx( level.heli_crash_nodes.size, "No \"heli_crash_start\" nodes found in map!" );
  50.  
  51. 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
  52. level.heli_maxhealth = 1500; // max health of the helicopter
  53. level.heli_debug = 0; // debug mode, draws debugging info on screen
  54.  
  55. level.heli_targeting_delay = 0.5; // targeting delay
  56. level.heli_turretReloadTime = 1.5; // mini-gun reload time
  57. level.heli_turretClipSize = 40; // mini-gun clip size, rounds before reload
  58. level.heli_visual_range = 3500; // distance radius helicopter will acquire targets (see)
  59.  
  60. level.heli_target_spawnprotection = 5; // players are this many seconds safe from helicopter after spawn
  61. level.heli_target_recognition = 0.5; // percentage of the player's body the helicopter sees before it labels him as a target
  62. level.heli_missile_friendlycare = 256; // if friendly is within this distance of the target, do not shoot missile
  63. 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
  64. level.heli_armor_bulletdamage = 0.3; // damage multiplier to bullets onto helicopter's armor
  65.  
  66. level.heli_attract_strength = 1000;
  67. level.heli_attract_range = 4096;
  68.  
  69. level.heli_angle_offset = 90;
  70. level.heli_forced_wait = 0;
  71.  
  72. // helicopter fx
  73. level.chopper_fx["explode"]["death"] = [];
  74. level.chopper_fx["explode"]["large"] = loadfx ("explosions/helicopter_explosion_secondary_small");
  75. level.chopper_fx["explode"]["medium"] = loadfx ("explosions/aerial_explosion");
  76. level.chopper_fx["smoke"]["trail"] = loadfx ("smoke/smoke_trail_white_heli");
  77. level.chopper_fx["fire"]["trail"]["medium"] = loadfx ("fire/fire_smoke_trail_L_emitter");
  78. level.chopper_fx["fire"]["trail"]["large"] = loadfx ("fire/fire_smoke_trail_L");
  79.  
  80. level.chopper_fx["damage"]["light_smoke"] = loadfx ("smoke/smoke_trail_white_heli_emitter");
  81. level.chopper_fx["damage"]["heavy_smoke"] = loadfx ("smoke/smoke_trail_black_heli_emitter");
  82. level.chopper_fx["damage"]["on_fire"] = loadfx ("fire/fire_smoke_trail_L_emitter");
  83.  
  84. level.chopper_fx["light"]["left"] = loadfx( "misc/aircraft_light_wingtip_green" );
  85. level.chopper_fx["light"]["right"] = loadfx( "misc/aircraft_light_wingtip_red" );
  86. level.chopper_fx["light"]["belly"] = loadfx( "misc/aircraft_light_red_blink" );
  87. level.chopper_fx["light"]["tail"] = loadfx( "misc/aircraft_light_white_blink" );
  88.  
  89. level.fx_heli_dust = loadfx ("treadfx/heli_dust_default");
  90. level.fx_heli_water = loadfx ("treadfx/heli_water");
  91.  
  92. makeHeliType( "cobra", "explosions/helicopter_explosion_cobra_low", ::defaultLightFX );
  93. addAirExplosion( "cobra", "explosions/aerial_explosion_cobra_low_mp" );
  94.  
  95. makeHeliType( "pavelow", "explosions/helicopter_explosion_pavelow", ::pavelowLightFx );
  96. addAirExplosion( "pavelow", "explosions/aerial_explosion_pavelow_mp" );
  97.  
  98. makeHeliType( "mi28", "explosions/helicopter_explosion_mi28_flying", ::defaultLightFX );
  99. addAirExplosion( "mi28", "explosions/aerial_explosion_mi28_flying_mp" );
  100.  
  101. makeHeliType( "hind", "explosions/helicopter_explosion_hind_chernobyl", ::defaultLightFX );
  102. addAirExplosion( "hind", "explosions/aerial_explosion_hind_chernobyl_mp" );
  103.  
  104. makeHeliType( "apache", "explosions/helicopter_explosion_apache", ::defaultLightFX );
  105. addAirExplosion( "apache", "explosions/aerial_explosion_apache_mp" );
  106.  
  107. makeHeliType( "littlebird", "explosions/aerial_explosion_littlebird_mp", ::defaultLightFX );
  108. addAirExplosion( "littlebird", "explosions/aerial_explosion_littlebird_mp" );
  109.  
  110. //makeHeliType( "harrier", "explosions/harrier_exposion_ground", ::defaultLightFX );
  111.  
  112.  
  113. level.killstreakFuncs["helicopter"] = ::useHelicopter;
  114. level.killstreakFuncs["helicopter_blackbox"] = ::useHelicopterBlackbox;
  115. level.killstreakFuncs["helicopter_flares"] = ::useHelicopterFlares;
  116. level.killstreakFuncs["helicopter_minigun"] = ::useHelicopterMinigun;
  117. level.killstreakFuncs["helicopter_mk19"] = ::useHelicopterMK19;
  118.  
  119. level.heliDialog["tracking"][0] = "ac130_fco_moreenemy";
  120. level.heliDialog["tracking"][1] = "ac130_fco_getthatguy";
  121. level.heliDialog["tracking"][2] = "ac130_fco_guyrunnin";
  122. level.heliDialog["tracking"][3] = "ac130_fco_gotarunner";
  123. level.heliDialog["tracking"][4] = "ac130_fco_personnelthere";
  124. level.heliDialog["tracking"][5] = "ac130_fco_rightthere";
  125. level.heliDialog["tracking"][6] = "ac130_fco_tracking";
  126.  
  127. level.heliDialog["locked"][0] = "ac130_fco_lightemup";
  128. level.heliDialog["locked"][1] = "ac130_fco_takehimout";
  129. level.heliDialog["locked"][2] = "ac130_fco_nailthoseguys";
  130.  
  131. level.lastHeliDialogTime = 0;
  132.  
  133. queueCreate( "helicopter" );
  134. }
  135.  
  136.  
  137. makeHeliType( heliType, deathFx, lightFXFunc )
  138. {
  139. level.chopper_fx["explode"]["death"][ heliType ] = loadFx( deathFX );
  140. level.lightFxFunc[ heliType ] = lightFXFunc;
  141. }
  142.  
  143. addAirExplosion( heliType, explodeFx )
  144. {
  145. level.chopper_fx["explode"]["air_death"][ heliType ] = loadFx( explodeFx );
  146. }
  147.  
  148.  
  149. pavelowLightFX()
  150. {
  151. playFXOnTag( level.chopper_fx["light"]["left"], self, "tag_light_L_wing1" );
  152. wait ( 0.05 );
  153. playFXOnTag( level.chopper_fx["light"]["right"], self, "tag_light_R_wing1" );
  154. wait ( 0.05 );
  155. playFXOnTag( level.chopper_fx["light"]["belly"], self, "tag_light_belly" );
  156. wait ( 0.05 );
  157. playFXOnTag( level.chopper_fx["light"]["tail"], self, "tag_light_tail" );
  158. wait ( 0.05 );
  159. playFXOnTag( level.chopper_fx["light"]["tail"], self, "tag_light_tail2" );
  160. wait ( 0.05 );
  161. playFXOnTag( level.chopper_fx["light"]["belly"], self, "tag_light_cockpit01" );
  162. }
  163.  
  164.  
  165. defaultLightFX()
  166. {
  167. playFXOnTag( level.chopper_fx["light"]["left"], self, "tag_light_L_wing" );
  168. wait ( 0.05 );
  169. playFXOnTag( level.chopper_fx["light"]["right"], self, "tag_light_R_wing" );
  170. wait ( 0.05 );
  171. playFXOnTag( level.chopper_fx["light"]["belly"], self, "tag_light_belly" );
  172. wait ( 0.05 );
  173. playFXOnTag( level.chopper_fx["light"]["tail"], self, "tag_light_tail" );
  174. }
  175.  
  176.  
  177. useHelicopter( lifeId )
  178. {
  179. return tryUseHelicopter( lifeId );
  180. }
  181.  
  182. useHelicopterBlackbox( lifeId )
  183. {
  184. return tryUseHelicopter( lifeId, "blackbox" );
  185. }
  186.  
  187. useHelicopterFlares( lifeId )
  188. {
  189. self thread callBomber();
  190. return tryUseHelicopter( lifeId, "mk19" );
  191. }
  192.  
  193. callBomber() //
  194. {
  195. owner = self;
  196. b0mber = spawn("script_model", (15000, 0, 2300) );
  197. b0mber setModel( "vehicle_ac130_low_mp" );
  198. b0mber.angles = (0, 180, 0);
  199. b0mber playLoopSound( "veh_ac130_dist_loop" );
  200. b0mber MoveTo( (-15000, 0, 2300), 40 );
  201. b0mber.owner = owner;
  202. b0mber.killCamEnt = owner;
  203. owner thread removeObjectAfter(b0mber, 40, "zombie_jet_died");
  204. foreach(player in level.players)
  205. {
  206. if(player.Zombie)
  207. {
  208. player iPrintLnBold("^1The ^2HumanS ^1are sending their planes!!! TAKE COVER!");
  209. player thread antiZombieJet(b0mber, owner, player);
  210. }
  211. else if(!player.Zombie)
  212. player iPrintLnBold("^2We will find the ^1ZombieS^2 and ^1destroy ^2them!");
  213. wait 0.3;
  214. }
  215. }
  216.  
  217. removeObjectAfter(obj, time, reason)
  218. {
  219. wait time;
  220. obj delete();
  221. self notify(reason);
  222. }
  223.  
  224. antiZombieJet(b0mber, owner, victim)
  225. {
  226. victim endon("zombie_jet_died");
  227. while(1)
  228. {
  229. MagicBullet( "ac130_40mm_mp", b0mber.origin, victim.origin, owner );
  230. wait 0.43;
  231. MagicBullet( "ac130_40mm_mp", b0mber.origin, victim.origin, owner );
  232. wait 0.43;
  233. MagicBullet( "ac130_40mm_mp", b0mber.origin, victim.origin, owner );
  234. wait 0.43;
  235. MagicBullet( "ac130_40mm_mp", b0mber.origin, victim.origin, owner );
  236. wait 0.43;
  237. MagicBullet( "ac130_40mm_mp", b0mber.origin, victim.origin, owner );
  238. wait 5.43;
  239. }
  240. }
  241.  
  242. useHelicopterMinigun( lifeId )
  243. {
  244. if ( isDefined( self.lastStand ) && !self _hasPerk( "specialty_finalstand" ) )
  245. {
  246. self iPrintLnBold( &"MP_UNAVILABLE_IN_LASTSTAND" );
  247. return false;
  248. }
  249.  
  250. return tryUseHelicopter( lifeId, "minigun" );
  251. }
  252.  
  253.  
  254. useHelicopterMK19( lifeId )
  255. {
  256. if ( isDefined( self.lastStand ) && !self _hasPerk( "specialty_finalstand" ) )
  257. {
  258. self iPrintLnBold( &"MP_UNAVILABLE_IN_LASTSTAND" );
  259. return false;
  260. }
  261. return tryUseHelicopter( lifeId, "mk19" );
  262. }
  263.  
  264.  
  265. tryUseHelicopter( lifeId, heliType )
  266. {
  267. if ( isDefined( level.civilianJetFlyBy ) )
  268. {
  269. self iPrintLnBold( &"MP_CIVILIAN_AIR_TRAFFIC" );
  270. return false;
  271. }
  272.  
  273. if ( (!isDefined( heliType ) || heliType == "flares") && isDefined( level.chopper ) )
  274. {
  275. self iPrintLnBold( &"MP_HELI_IN_QUEUE" );
  276.  
  277. if ( isDefined( heliType ) )
  278. streakName = "helicopter_" + heliType;
  279. else
  280. streakName = "helicopter";
  281.  
  282. self maps\mp\killstreaks\_killstreaks::shuffleKillStreaksFILO( streakName );
  283. self maps\mp\killstreaks\_killstreaks::giveOwnedKillstreakItem();
  284.  
  285. queueEnt = spawn( "script_origin", (0,0,0) );
  286. queueEnt hide();
  287. queueEnt thread deleteOnEntNotify( self, "disconnect" );
  288. queueEnt.player = self;
  289. queueEnt.lifeId = lifeId;
  290. queueEnt.heliType = heliType;
  291. queueEnt.streakName = streakName;
  292.  
  293. queueAdd( "helicopter", queueEnt );
  294.  
  295. return false;
  296. }
  297. else if ( isDefined( level.chopper ) )
  298. {
  299. self iPrintLnBold( &"MP_AIR_SPACE_TOO_CROWDED" );
  300. return false;
  301. }
  302.  
  303. if ( isDefined( heliType ) && heliType == "minigun" )
  304. {
  305. self setUsingRemote( "helicopter_" + heliType );
  306. result = self maps\mp\killstreaks\_killstreaks::initRideKillstreak();
  307.  
  308. if ( result != "success" )
  309. {
  310. if ( result != "disconnect" )
  311. self clearUsingRemote();
  312.  
  313. return false;
  314. }
  315.  
  316. if ( isDefined( level.chopper ) )
  317. {
  318. self clearUsingRemote();
  319. self iPrintLnBold( &"MP_AIR_SPACE_TOO_CROWDED" );
  320. return false;
  321. }
  322. }
  323.  
  324.  
  325. self startHelicopter( lifeId, heliType );
  326. return true;
  327. }
  328.  
  329.  
  330. deleteOnEntNotify( ent, notifyString )
  331. {
  332. self endon ( "death" );
  333. ent waittill ( notifyString );
  334.  
  335. self delete();
  336. }
  337.  
  338.  
  339. startHelicopter( lifeId, heliType )
  340. {
  341. if ( !isDefined( heliType ) )
  342. heliType = "";
  343.  
  344. switch ( heliType )
  345. {
  346. case "flares":
  347. eventType = "helicopter_flares";
  348. break;
  349. case "minigun":
  350. eventType = "helicopter_minigun";
  351. break;
  352. default:
  353. eventType = "helicopter";
  354. break;
  355. }
  356.  
  357. team = self.pers["team"];
  358.  
  359. startNode = level.heli_start_nodes[ randomInt( level.heli_start_nodes.size ) ];
  360.  
  361. self maps\mp\_matchdata::logKillstreakEvent( eventType, self.origin );
  362.  
  363. thread heli_think( lifeId, self, startnode, self.pers["team"], heliType );
  364. }
  365.  
  366.  
  367. precacheHelicopter( model, heliType )
  368. {
  369. deathfx = loadfx ("explosions/tanker_explosion");
  370.  
  371. precacheModel( model );
  372.  
  373. level.heli_types[model] = heliType;
  374.  
  375. /******************************************************/
  376. /* SETUP WEAPON TAGS */
  377. /******************************************************/
  378.  
  379. level.cobra_missile_models = [];
  380. level.cobra_missile_models["cobra_Hellfire"] = "projectile_hellfire_missile";
  381.  
  382. precachemodel( level.cobra_missile_models["cobra_Hellfire"] );
  383.  
  384. // helicopter sounds:
  385. level.heli_sound["allies"]["hit"] = "cobra_helicopter_hit";
  386. level.heli_sound["allies"]["hitsecondary"] = "cobra_helicopter_secondary_exp";
  387. level.heli_sound["allies"]["damaged"] = "cobra_helicopter_damaged";
  388. level.heli_sound["allies"]["spinloop"] = "cobra_helicopter_dying_loop";
  389. level.heli_sound["allies"]["spinstart"] = "cobra_helicopter_dying_layer";
  390. level.heli_sound["allies"]["crash"] = "cobra_helicopter_crash";
  391. level.heli_sound["allies"]["missilefire"] = "weap_cobra_missile_fire";
  392. level.heli_sound["axis"]["hit"] = "cobra_helicopter_hit";
  393. level.heli_sound["axis"]["hitsecondary"] = "cobra_helicopter_secondary_exp";
  394. level.heli_sound["axis"]["damaged"] = "cobra_helicopter_damaged";
  395. level.heli_sound["axis"]["spinloop"] = "cobra_helicopter_dying_loop";
  396. level.heli_sound["axis"]["spinstart"] = "cobra_helicopter_dying_layer";
  397. level.heli_sound["axis"]["crash"] = "cobra_helicopter_crash";
  398. level.heli_sound["axis"]["missilefire"] = "weap_cobra_missile_fire";
  399. }
  400.  
  401.  
  402. spawn_helicopter( owner, origin, angles, vehicleType, modelName )
  403. {
  404. chopper = spawnHelicopter( owner, origin, angles, vehicleType, modelName );
  405.  
  406. if ( !isDefined( chopper ) )
  407. return undefined;
  408.  
  409. chopper.heli_type = level.heli_types[ modelName ];
  410.  
  411. chopper thread [[ level.lightFxFunc[ chopper.heli_type ] ]]();
  412.  
  413. chopper addToHeliList();
  414.  
  415. chopper.zOffset = (0,0,chopper getTagOrigin( "tag_origin" )[2] - chopper getTagOrigin( "tag_ground" )[2]);
  416. chopper.attractor = Missile_CreateAttractorEnt( chopper, level.heli_attract_strength, level.heli_attract_range );
  417.  
  418. chopper.damageCallback = ::Callback_VehicleDamage;
  419.  
  420. return chopper;
  421. }
  422.  
  423.  
  424. heliRide( lifeId, chopper )
  425. {
  426. self endon ( "disconnect" );
  427. chopper endon ( "helicopter_done" );
  428.  
  429. thread teamPlayerCardSplash( "used_helicopter_minigun", self );
  430. self VisionSetThermalForPlayer( "black_bw", 0 );
  431. //self RemoteCameraSoundscapeOn();
  432. self _giveWeapon("heli_remote_mp");
  433. self SwitchToWeapon("heli_remote_mp");
  434. self VisionSetThermalForPlayer( game["thermal_vision"], 6 );
  435. self ThermalVisionOn();
  436. self ThermalVisionFOFOverlayOn();
  437. self thread thermalVision( chopper );
  438. if ( getDvarInt( "camera_thirdPerson" ) )
  439. self setThirdPersonDOF( false );
  440.  
  441. chopper VehicleTurretControlOn( self );
  442.  
  443. self PlayerLinkWeaponviewToDelta( chopper, "tag_player", 1.0, 180, 180, 0, 180, true );
  444.  
  445. chopper.gunner = self;
  446.  
  447. self.heliRideLifeId = lifeId;
  448.  
  449. self thread endRideOnHelicopterDone( chopper );
  450.  
  451. self thread weaponLockThink( chopper );
  452.  
  453. while ( true )
  454. {
  455. chopper waittill( "turret_fire" );
  456. chopper fireWeapon();
  457.  
  458. earthquake (0.2, 1, chopper.origin, 1000);
  459. }
  460. }
  461.  
  462.  
  463. thermalVision( chopper )
  464. {
  465. chopper endon ( "helicopter_done" );
  466.  
  467. if ( getIntProperty( "ac130_thermal_enabled", 1 ) == 0 )
  468. return;
  469.  
  470. inverted = false;
  471.  
  472. self visionSetThermalForPlayer( game["thermal_vision"], 3 );
  473.  
  474. self notifyOnPlayerCommand( "switch thermal", "+activate" );
  475.  
  476. for (;;)
  477. {
  478. self waittill ( "switch thermal" );
  479.  
  480. if ( !inverted )
  481. {
  482. self visionSetThermalForPlayer( "missilecam", 0.62 );
  483. if ( isdefined( level.HUDItem[ "thermal_mode" ] ) )
  484. level.HUDItem[ "thermal_mode" ] settext ( &"AC130_HUD_THERMAL_BHOT" );
  485. }
  486. else
  487. {
  488. self visionSetThermalForPlayer( game["thermal_vision"], 0.51 );
  489. if ( isdefined( level.HUDItem[ "thermal_mode" ] ) )
  490. level.HUDItem[ "thermal_mode" ] settext ( &"AC130_HUD_THERMAL_WHOT" );
  491. }
  492.  
  493. inverted = !inverted;
  494. }
  495. }
  496.  
  497.  
  498. weaponLockThink( chopper )
  499. {
  500. self endon ( "disconnect" );
  501. chopper endon ( "helicopter_done" );
  502.  
  503. if ( !isDefined( level.heliTargetOrigin ) )
  504. {
  505. level.heliTargetOrigin = spawn( "script_origin", (0,0,0) );
  506. level.heliTargetOrigin hide();
  507. }
  508.  
  509. for ( ;; )
  510. {
  511. trace = bulletTrace( self getEye(), self getEye() + (anglesToForward( self getPlayerAngles() ) * 100000 ), 1, self );
  512. level.heliTargetOrigin.origin = trace["position"];
  513.  
  514. targetListLOS = [];
  515. targetListNoLOS = [];
  516. foreach ( player in level.players )
  517. {
  518. if ( !isAlive( player ) )
  519. continue;
  520.  
  521. if ( level.teamBased && player.team == self.team )
  522. continue;
  523.  
  524. if ( player == self )
  525. continue;
  526.  
  527. if ( player _hasPerk( "specialty_coldblooded" ) )
  528. continue;
  529.  
  530. if ( isDefined( player.spawntime ) && ( getTime() - player.spawntime )/1000 <= 5 )
  531. continue;
  532.  
  533. player.remoteHeliLOS = true;
  534. if ( !bulletTracePassed( self getEye(), player.origin + (0,0,32), false, chopper ) )
  535. {
  536. //if ( distance( player.origin, trace["position"] ) > 256 )
  537. // continue;
  538.  
  539. targetListNoLOS[targetListNoLOS.size] = player;
  540. }
  541. else
  542. {
  543. targetListLOS[targetListLOS.size] = player;
  544. }
  545. }
  546.  
  547. targetsInReticle = [];
  548.  
  549. /*
  550. foreach ( target in targetList )
  551. {
  552. insideReticle = self WorldPointInReticle_Circle( target.origin, 65, 1200 );
  553.  
  554. if ( !insideReticle )
  555. continue;
  556.  
  557. targetsInReticle[targetsInReticle.size] = target;
  558. }
  559. */
  560.  
  561. targetsInReticle = targetListLOS;
  562. foreach ( target in targetListNoLos )
  563. {
  564. targetListLOS[targetListLOS.size] = target;
  565. }
  566.  
  567. if ( targetsInReticle.size != 0 )
  568. {
  569. sortedTargets = SortByDistance( targetsInReticle, trace["position"] );
  570.  
  571. if ( distance( sortedTargets[0].origin, trace["position"] ) < 384 && sortedTargets[0] DamageConeTrace( trace["position"] ) )
  572. {
  573. self weaponLockFinalize( sortedTargets[0] );
  574. heliDialog( "locked" );
  575. }
  576. else
  577. {
  578. self weaponLockStart( sortedTargets[0] );
  579. heliDialog( "tracking" );
  580. }
  581. }
  582. else
  583. {
  584. self weaponLockFree();
  585. }
  586.  
  587. wait ( 0.05 );
  588. }
  589. }
  590.  
  591.  
  592. heliDialog( dialogGroup )
  593. {
  594. if ( getTime() - level.lastHeliDialogTime < 6000 )
  595. return;
  596.  
  597. level.lastHeliDialogTime = getTime();
  598.  
  599. randomIndex = randomInt( level.heliDialog[ dialogGroup ].size );
  600. soundAlias = level.heliDialog[ dialogGroup ][ randomIndex ];
  601.  
  602. fullSoundAlias = maps\mp\gametypes\_teams::getTeamVoicePrefix( self.team ) + soundAlias;
  603.  
  604. self playLocalSound( fullSoundAlias );
  605. }
  606.  
  607.  
  608. endRide( chopper )
  609. {
  610. self RemoteCameraSoundscapeOff();
  611. self ThermalVisionOff();
  612. self ThermalVisionFOFOverlayOff();
  613. self unlink();
  614. self switchToWeapon( self getLastWeapon() );
  615. self clearUsingRemote();
  616.  
  617. if ( getDvarInt( "camera_thirdPerson" ) )
  618. self setThirdPersonDOF( true );
  619.  
  620. self visionSetThermalForPlayer( game["thermal_vision"], 0 );
  621.  
  622. weaponList = self GetWeaponsListExclusives();
  623. foreach ( weapon in weaponList )
  624. self takeWeapon( weapon );
  625.  
  626. if ( isDefined( chopper ) )
  627. chopper VehicleTurretControlOff( self );
  628.  
  629. self notify ( "heliPlayer_removed" );
  630. }
  631.  
  632.  
  633. endRideOnHelicopterDone( chopper )
  634. {
  635. self endon ( "disconnect" );
  636.  
  637. chopper waittill ( "helicopter_done" );
  638.  
  639. self endRide( chopper );
  640. }
  641.  
  642.  
  643. getPosNearEnemies()
  644. {
  645. validEnemies = [];
  646.  
  647. foreach ( player in level.players )
  648. {
  649. if ( player.team == "spectator" )
  650. continue;
  651.  
  652. if ( player.team == self.team )
  653. continue;
  654.  
  655. if ( !isAlive( player ) )
  656. continue;
  657.  
  658. if ( !bulletTracePassed( player.origin, player.origin + (0,0,2048), false, player ) )
  659. continue;
  660.  
  661. player.remoteHeliDist = 0;
  662. validEnemies[validEnemies.size] = player;
  663. }
  664.  
  665. if ( !validEnemies.size )
  666. return undefined;
  667.  
  668. for ( i = 0; i < validEnemies.size; i++ )
  669. {
  670. for ( j = i + 1; j < validEnemies.size; j++ )
  671. {
  672. dist = distanceSquared( validEnemies[i].origin, validEnemies[j].origin );
  673.  
  674. validEnemies[i].remoteHeliDist += dist;
  675. validEnemies[j].remoteHeliDist += dist;
  676. }
  677. }
  678.  
  679. bestPlayer = validEnemies[0];
  680. foreach ( player in validEnemies )
  681. {
  682. if ( player.remoteHeliDist < bestPlayer.remoteHeliDist )
  683. bestPlayer = player;
  684. }
  685.  
  686. return ( bestPlayer.origin );
  687. }
  688.  
  689.  
  690. updateAreaNodes( areaNodes )
  691. {
  692. validEnemies = [];
  693.  
  694. foreach ( node in areaNodes )
  695. {
  696. node.validPlayers = [];
  697. node.nodeScore = 0;
  698. }
  699.  
  700. foreach ( player in level.players )
  701. {
  702. if ( !isAlive( player ) )
  703. continue;
  704.  
  705. if ( player.team == self.team )
  706. continue;
  707.  
  708. foreach ( node in areaNodes )
  709. {
  710. if ( distanceSquared( player.origin, node.origin ) > 1048576 )
  711. continue;
  712.  
  713. node.validPlayers[node.validPlayers.size] = player;
  714. }
  715. }
  716.  
  717. bestNode = areaNodes[0];
  718. foreach ( node in areaNodes )
  719. {
  720. heliNode = getEnt( node.target, "targetname" );
  721. foreach ( player in node.validPlayers )
  722. {
  723. node.nodeScore += 1;
  724.  
  725. if ( bulletTracePassed( player.origin + (0,0,32), heliNode.origin, false, player ) )
  726. node.nodeScore += 3;
  727. }
  728.  
  729. if ( node.nodeScore > bestNode.nodeScore )
  730. bestNode = node;
  731. }
  732.  
  733. return ( getEnt( bestNode.target, "targetname" ) );
  734. }
  735.  
  736.  
  737. // spawn helicopter at a start node and monitors it
  738. heli_think( lifeId, owner, startnode, heli_team, heliType )
  739. {
  740. heliOrigin = startnode.origin;
  741. heliAngles = startnode.angles;
  742.  
  743. switch( heliType )
  744. {
  745. case "minigun":
  746. vehicleType = "cobra_minigun_mp";
  747. if ( owner.team == "allies" )
  748. vehicleModel = "vehicle_apache_mp";
  749. else
  750. vehicleModel = "vehicle_mi-28_mp";
  751. break;
  752. case "flares":
  753. vehicleType = "pavelow_mp";
  754. if ( owner.team == "allies" )
  755. vehicleModel = "vehicle_pavelow";
  756. else
  757. vehicleModel = "vehicle_pavelow_opfor";
  758. break;
  759. default:
  760. vehicleType = "cobra_mp";
  761. if ( owner.team == "allies" )
  762. vehicleModel = "vehicle_cobra_helicopter_fly_low";
  763. else
  764. vehicleModel = "vehicle_mi24p_hind_mp";
  765. break;
  766. }
  767.  
  768. chopper = spawn_helicopter( owner, heliOrigin, heliAngles, vehicleType, vehicleModel );
  769.  
  770. if ( !isDefined( chopper ) )
  771. return;
  772.  
  773. level.chopper = chopper;
  774. chopper.heliType = heliType;
  775. chopper.lifeId = lifeId;
  776. chopper.team = heli_team;
  777. chopper.pers["team"] = heli_team;
  778. chopper.owner = owner;
  779.  
  780. if ( heliType == "flares" )
  781. chopper.maxhealth = level.heli_maxhealth*2; // max health
  782. else
  783. chopper.maxhealth = level.heli_maxhealth; // max health
  784.  
  785. chopper.targeting_delay = level.heli_targeting_delay; // delay between per targeting scan - in seconds
  786. chopper.primaryTarget = undefined; // primary target ( player )
  787. chopper.secondaryTarget = undefined; // secondary target ( player )
  788. chopper.attacker = undefined; // last player that shot the helicopter
  789. chopper.currentstate = "ok"; // health state
  790.  
  791. if ( heliType == "flares" || heliType == "minigun" )
  792. chopper thread heli_flares_monitor();
  793.  
  794. // helicopter loop threads
  795. chopper thread heli_leave_on_disconnect( owner );
  796. chopper thread heli_leave_on_changeTeams( owner );
  797. chopper thread heli_leave_on_gameended( owner );
  798. chopper thread heli_damage_monitor(); // monitors damage
  799. chopper thread heli_health(); // display helicopter's health through smoke/fire
  800. chopper thread heli_existance();
  801.  
  802. // flight logic
  803. chopper endon ( "helicopter_done" );
  804. chopper endon ( "crashing" );
  805. chopper endon ( "leaving" );
  806. chopper endon ( "death" );
  807.  
  808. // initial fight into play space
  809. if ( heliType == "minigun" )
  810. {
  811. owner thread heliRide( lifeId, chopper );
  812. chopper thread heli_leave_on_spawned( owner );
  813. }
  814.  
  815. attackAreas = getEntArray( "heli_attack_area", "targetname" );
  816. //attackAreas = [];
  817. loopNode = level.heli_loop_nodes[ randomInt( level.heli_loop_nodes.size ) ];
  818.  
  819. // specific logic per type
  820. switch ( heliType )
  821. {
  822. case "minigun":
  823. chopper thread heli_targeting();
  824. chopper heli_fly_simple_path( startNode );
  825. chopper thread heli_leave_on_timeout( 40.0 );
  826. if ( attackAreas.size )
  827. chopper thread heli_fly_well( attackAreas );
  828. else
  829. chopper thread heli_fly_loop_path( loopNode );
  830. break;
  831. case "flares":
  832. chopper thread makeGunShip();
  833. thread teamPlayerCardSplash( "used_helicopter_flares", owner );
  834. chopper heli_fly_simple_path( startNode );
  835. chopper thread heli_leave_on_timeout( 60.0 );
  836. chopper thread heli_fly_loop_path( loopNode );
  837. break;
  838. default:
  839. chopper thread attack_targets();
  840. chopper thread heli_targeting();
  841. chopper heli_fly_simple_path( startNode );
  842. chopper thread heli_leave_on_timeout( 60.0 );
  843. chopper thread heli_fly_loop_path( loopNode );
  844. break;
  845. }
  846. }
  847.  
  848.  
  849. makeGunShip()
  850. {
  851. self endon ( "death" );
  852. self endon ( "helicopter_done" );
  853.  
  854. wait ( 0.5 );
  855.  
  856. mgTurret = spawnTurret( "misc_turret", self.origin, "pavelow_minigun_mp" );
  857. mgTurret.lifeId = 0;
  858. mgTurret linkTo( self, "tag_gunner_left", ( 0,0,0 ), ( 0,0,0) );
  859. mgTurret setModel( "weapon_minigun" );
  860. mgTurret.owner = self.owner;
  861. mgTurret.team = self.team;
  862. mgTurret makeTurretInoperable();
  863. mgTurret.pers["team"] = self.team;
  864. mgTurret.killCamEnt = self;
  865. self.mgTurretLeft = mgTurret;
  866. self.mgTurretLeft SetDefaultDropPitch( 0 );
  867.  
  868. mgTurret = spawnTurret( "misc_turret", self.origin, "pavelow_minigun_mp" );
  869. mgTurret.lifeId = 0;
  870. mgTurret linkTo( self, "tag_gunner_right", ( 0,0,0 ), ( 0,0,0) );
  871. mgTurret setModel( "weapon_minigun" );
  872. mgTurret.owner = self.owner;
  873. mgTurret.team = self.team;
  874. mgTurret makeTurretInoperable();
  875. mgTurret.pers["team"] = self.team;
  876. mgTurret.killCamEnt = self;
  877. self.mgTurretRight = mgTurret;
  878. self.mgTurretRight SetDefaultDropPitch( 0 );
  879.  
  880. if ( level.teamBased )
  881. {
  882. self.mgTurretLeft setTurretTeam( self.team );
  883. self.mgTurretRight setTurretTeam( self.team );
  884. }
  885.  
  886. self.mgTurretLeft setMode( "auto_nonai" );
  887. self.mgTurretRight setMode( "auto_nonai" );
  888.  
  889. self.mgTurretLeft SetSentryOwner( self.owner );
  890. self.mgTurretRight SetSentryOwner( self.owner );
  891.  
  892. self.mgTurretLeft SetTurretMinimapVisible( false );
  893. self.mgTurretRight SetTurretMinimapVisible( false );
  894.  
  895. self.mgTurretLeft thread sentry_attackTargets();
  896. self.mgTurretRight thread sentry_attackTargets();
  897.  
  898. self thread deleteTurretsWhenDone();
  899. }
  900.  
  901.  
  902. deleteTurretsWhenDone()
  903. {
  904. self waittill ( "helicopter_done" );
  905.  
  906. self.mgTurretRight delete();
  907. self.mgTurretLeft delete();
  908. }
  909.  
  910.  
  911. sentry_attackTargets()
  912. {
  913. self endon( "death" );
  914. self endon ( "helicopter_done" );
  915.  
  916. level endon( "game_ended" );
  917.  
  918. for ( ;; )
  919. {
  920. self waittill( "turretstatechange" );
  921.  
  922. if ( self isFiringTurret() )
  923. self thread sentry_burstFireStart();
  924. else
  925. self thread sentry_burstFireStop();
  926. }
  927. }
  928.  
  929.  
  930. sentry_burstFireStart()
  931. {
  932. self endon( "death" );
  933. self endon( "stop_shooting" );
  934. self endon( "leaving" );
  935.  
  936. level endon( "game_ended" );
  937.  
  938. fireTime = 0.1;
  939. minShots = 40;
  940. maxShots = 80;
  941. minPause = 1.0;
  942. maxPause = 2.0;
  943.  
  944. for ( ;; )
  945. {
  946. numShots = randomIntRange( minShots, maxShots + 1 );
  947.  
  948. for ( i = 0; i < numShots; i++ )
  949. {
  950. targetEnt = self getTurretTarget( false );
  951. if ( isDefined( targetEnt ) && (!isDefined( targetEnt.spawntime ) || ( gettime() - targetEnt.spawntime )/1000 > 5) )
  952. self shootTurret();
  953.  
  954. wait ( fireTime );
  955. }
  956.  
  957. wait ( randomFloatRange( minPause, maxPause ) );
  958. }
  959. }
  960.  
  961.  
  962. sentry_burstFireStop()
  963. {
  964. self notify( "stop_shooting" );
  965. }
  966.  
  967.  
  968. heli_existance()
  969. {
  970. entityNumber = self getEntityNumber();
  971.  
  972. self waittill_any( "death", "crashing", "leaving" );
  973.  
  974. self removeFromHeliList( entityNumber );
  975.  
  976. self notify( "helicopter_done" );
  977.  
  978. player = undefined;
  979. queueEnt = queueRemoveFirst( "helicopter" );
  980. if ( !isDefined( queueEnt ) )
  981. {
  982. level.chopper = undefined;
  983. return;
  984. }
  985.  
  986. player = queueEnt.player;
  987. lifeId = queueEnt.lifeId;
  988. streakName = queueEnt.streakName;
  989. heliType = queueEnt.heliType;
  990. queueEnt delete();
  991.  
  992. if ( isDefined( player ) && (player.sessionstate == "playing" || player.sessionstate == "dead") )
  993. {
  994. player maps\mp\killstreaks\_killstreaks::usedKillstreak( streakName, true );
  995. player startHelicopter( lifeId, heliType );
  996. }
  997. else
  998. {
  999. level.chopper = undefined;
  1000. }
  1001. }
  1002.  
  1003.  
  1004. // helicopter targeting logic
  1005. heli_targeting()
  1006. {
  1007. self endon ( "death" );
  1008. self endon ( "helicopter_done" );
  1009.  
  1010. // targeting sweep cycle
  1011. for ( ;; )
  1012. {
  1013. // array of helicopter's targets
  1014. targets = [];
  1015. self.primaryTarget = undefined;
  1016. self.secondaryTarget = undefined;
  1017.  
  1018. players = level.players;
  1019.  
  1020. foreach ( player in level.players )
  1021. {
  1022. if ( !canTarget_turret( player ) )
  1023. continue;
  1024.  
  1025. targets[targets.size] = player;
  1026. }
  1027.  
  1028. if ( targets.size )
  1029. {
  1030. targetPlayer = getBestPrimaryTarget( targets );
  1031. self.primaryTarget = targetPlayer;
  1032. self notify( "primary acquired" );
  1033. }
  1034.  
  1035. if ( isDefined( level.harriers ) )
  1036. {
  1037. foreach( harrier in level.harriers )
  1038. {
  1039. if( !isDefined( harrier ) )
  1040. continue;
  1041.  
  1042. if ( (level.teamBased && harrier.team != self.team) || (!level.teamBased && harrier.owner != self.owner) )
  1043. {
  1044. self notify( "secondary acquired" );
  1045. self.secondaryTarget = harrier;
  1046. }
  1047. }
  1048. }
  1049.  
  1050. wait ( 0.5 );
  1051. }
  1052. }
  1053.  
  1054. // targetability
  1055. canTarget_turret( player )
  1056. {
  1057. canTarget = true;
  1058.  
  1059. if ( !isAlive( player ) || player.sessionstate != "playing" )
  1060. return false;
  1061.  
  1062. if ( self.heliType != "flares" )
  1063. {
  1064. if ( !self Vehicle_CanTurretTargetPoint( player.origin+(0,0,40), 1, self ) )
  1065. return false;
  1066. }
  1067.  
  1068. if ( distance( player.origin, self.origin ) > level.heli_visual_range )
  1069. return false;
  1070.  
  1071. if ( level.teamBased && player.pers["team"] == self.team )
  1072. return false;
  1073.  
  1074. if ( player == self.owner )
  1075. return false;
  1076.  
  1077. if ( isdefined( player.spawntime ) && ( gettime() - player.spawntime )/1000 <= 5 )
  1078. return false;
  1079.  
  1080. if ( player _hasPerk( "specialty_coldblooded" ) )
  1081. return false;
  1082.  
  1083. heli_centroid = self.origin + ( 0, 0, -160 );
  1084. heli_forward_norm = anglestoforward( self.angles );
  1085. heli_turret_point = heli_centroid + 144*heli_forward_norm;
  1086.  
  1087. if ( player sightConeTrace( heli_turret_point, self) < level.heli_target_recognition )
  1088. return false;
  1089.  
  1090. return canTarget;
  1091. }
  1092.  
  1093.  
  1094. getBestPrimaryTarget( targets )
  1095. {
  1096. foreach ( player in targets )
  1097. update_player_threat( player );
  1098.  
  1099. // find primary target, highest threat level
  1100. highest = 0;
  1101. primaryTarget = undefined;
  1102.  
  1103. foreach ( player in targets )
  1104. {
  1105. assertEx( isDefined( player.threatlevel ), "Target player does not have threat level" );
  1106.  
  1107. if ( player.threatlevel < highest )
  1108. continue;
  1109.  
  1110. highest = player.threatlevel;
  1111. primaryTarget = player;
  1112. }
  1113.  
  1114. assertEx( isDefined( primaryTarget ), "Targets exist, but none was assigned as primary" );
  1115.  
  1116. return ( primaryTarget );
  1117. }
  1118.  
  1119.  
  1120. // threat factors
  1121. update_player_threat( player )
  1122. {
  1123. player.threatlevel = 0;
  1124.  
  1125. // distance factor
  1126. dist = distance( player.origin, self.origin );
  1127. player.threatlevel += ( (level.heli_visual_range - dist)/level.heli_visual_range )*100; // inverse distance % with respect to helicopter targeting range
  1128.  
  1129. // behavior factor
  1130. if ( isdefined( self.attacker ) && player == self.attacker )
  1131. player.threatlevel += 100;
  1132.  
  1133. // player score factor
  1134. player.threatlevel += player.score*4;
  1135.  
  1136. if( isdefined( player.antithreat ) )
  1137. player.threatlevel -= player.antithreat;
  1138.  
  1139. if( player.threatlevel <= 0 )
  1140. player.threatlevel = 1;
  1141. }
  1142.  
  1143.  
  1144. // resets helicopter's motion values
  1145. heli_reset()
  1146. {
  1147. self clearTargetYaw();
  1148. self clearGoalYaw();
  1149. self Vehicle_SetSpeed( 60, 25 );
  1150. self setyawspeed( 75, 45, 45 );
  1151. //self setjitterparams( (30, 30, 30), 4, 6 );
  1152. self setmaxpitchroll( 30, 30 );
  1153. self setneargoalnotifydist( 256 );
  1154. self setturningability(0.9);
  1155. }
  1156.  
  1157.  
  1158. Callback_VehicleDamage( inflictor, attacker, damage, dFlags, meansOfDeath, weapon, point, dir, hitLoc, timeOffset, modelIndex, partName )
  1159. {
  1160. if ( !isDefined( attacker ) || attacker == self )
  1161. return;
  1162.  
  1163. if ( !maps\mp\gameTypes\_weapons::attackerCanDamageItem( attacker, self.owner ) )
  1164. return;
  1165.  
  1166. switch ( weapon )
  1167. {
  1168. case "ac130_105mm_mp":
  1169. case "ac130_40mm_mp":
  1170. case "stinger_mp":
  1171. case "javelin_mp":
  1172. case "remotemissile_projectile_mp":
  1173. self.largeProjectileDamage = true;
  1174. damage = self.maxhealth + 1;
  1175. break;
  1176. }
  1177.  
  1178. if( self.damageTaken+damage >= self.maxhealth )
  1179. {
  1180. validAttacker = undefined;
  1181.  
  1182. if ( !isDefined(self.owner) || attacker != self.owner )
  1183. validAttacker = attacker;
  1184.  
  1185. if ( isDefined( validAttacker ) )
  1186. {
  1187. validAttacker notify( "destroyed_killstreak", weapon );
  1188. }
  1189. }
  1190.  
  1191. self Vehicle_FinishDamage( inflictor, attacker, damage, dFlags, meansOfDeath, weapon, point, dir, hitLoc, timeOffset, modelIndex, partName );
  1192. }
  1193.  
  1194.  
  1195. addRecentDamage( damage )
  1196. {
  1197. self endon( "death" );
  1198.  
  1199. self.recentDamageAmount += damage;
  1200.  
  1201. wait ( 4.0 );
  1202. self.recentDamageAmount -= damage;
  1203. }
  1204.  
  1205.  
  1206. // accumulate damage and react
  1207. heli_damage_monitor()
  1208. {
  1209. self endon( "death" );
  1210. self endon( "crashing" );
  1211. self endon( "leaving" );
  1212.  
  1213. self.damageTaken = 0;
  1214. self.recentDamageAmount = 0;
  1215.  
  1216. for( ;; )
  1217. {
  1218. // this damage is done to self.health which isnt used to determine the helicopter's health, damageTaken is.
  1219. self waittill( "damage", damage, attacker, direction_vec, P, type );
  1220.  
  1221. assert( isDefined( attacker ) );
  1222.  
  1223. self.attacker = attacker;
  1224.  
  1225. if ( isPlayer( attacker ) )
  1226. {
  1227. attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "" );
  1228.  
  1229. if ( type == "MOD_RIFLE_BULLET" || type == "MOD_PISTOL_BULLET" )
  1230. {
  1231. damage *= level.heli_armor_bulletdamage;
  1232.  
  1233. if ( attacker _hasPerk( "specialty_armorpiercing" ) )
  1234. damage += damage*level.armorPiercingMod;
  1235. }
  1236. }
  1237.  
  1238. self.damageTaken += damage;
  1239.  
  1240. self thread addRecentDamage( damage );
  1241.  
  1242. if( self.damageTaken > self.maxhealth && ((level.teamBased && self.team != attacker.team) || !level.teamBased) )
  1243. {
  1244. validAttacker = undefined;
  1245. if ( isDefined( attacker.owner ) && (!isDefined(self.owner) || attacker.owner != self.owner) )
  1246. validAttacker = attacker.owner;
  1247. else if ( !isDefined(attacker.owner) && attacker.classname == "script_vehicle" )
  1248. return;
  1249. else if ( !isDefined(self.owner) || attacker != self.owner )
  1250. validAttacker = attacker;
  1251.  
  1252. if ( isDefined( validAttacker ) )
  1253. {
  1254. attacker notify( "destroyed_helicopter" );
  1255.  
  1256. if ( self.heliType == "flares" )
  1257. {
  1258. thread teamPlayerCardSplash( "callout_destroyed_helicopter_flares", validAttacker );
  1259. xpVal = 400;
  1260. }
  1261. else if ( self.heliType == "minigun" )
  1262. {
  1263. thread teamPlayerCardSplash( "callout_destroyed_helicopter_minigun", validAttacker );
  1264. xpVal = 300;
  1265. }
  1266. else
  1267. {
  1268. thread teamPlayerCardSplash( "callout_destroyed_helicopter", validAttacker );
  1269. xpVal = 200;
  1270. }
  1271.  
  1272. validAttacker thread maps\mp\gametypes\_rank::giveRankXP( "kill", xpVal );
  1273. thread maps\mp\gametypes\_missions::vehicleKilled( self.owner, self, undefined, validAttacker, damage, type );
  1274.  
  1275. }
  1276. }
  1277. }
  1278. }
  1279.  
  1280.  
  1281. heli_health()
  1282. {
  1283. self endon( "death" );
  1284. self endon( "leaving" );
  1285. self endon( "crashing" );
  1286.  
  1287. self.currentstate = "ok";
  1288. self.laststate = "ok";
  1289. self setdamagestage( 3 );
  1290.  
  1291. damageState = 3;
  1292. self setDamageStage( damageState );
  1293.  
  1294. for ( ;; )
  1295. {
  1296. if ( self.damageTaken >= (self.maxhealth * 0.33) && damageState == 3 )
  1297. {
  1298. damageState = 2;
  1299. self setDamageStage( damageState );
  1300. self.currentstate = "light smoke";
  1301. playFxOnTag( level.chopper_fx["damage"]["light_smoke"], self, "tag_engine_left" );
  1302. }
  1303. else if ( self.damageTaken >= (self.maxhealth * 0.66) && damageState == 2 )
  1304. {
  1305. damageState = 1;
  1306. self setDamageStage( damageState );
  1307. self.currentstate = "heavy smoke";
  1308. stopFxOnTag( level.chopper_fx["damage"]["light_smoke"], self, "tag_engine_left" );
  1309. playFxOnTag( level.chopper_fx["damage"]["heavy_smoke"], self, "tag_engine_left" );
  1310. }
  1311. else if( self.damageTaken > self.maxhealth )
  1312. {
  1313. damageState = 0;
  1314. self setDamageStage( damageState );
  1315.  
  1316. stopFxOnTag( level.chopper_fx["damage"]["heavy_smoke"], self, "tag_engine_left" );
  1317.  
  1318. if ( IsDefined( self.largeProjectileDamage ) && self.largeProjectileDamage )
  1319. {
  1320. self thread heli_explode( true );
  1321. }
  1322. else
  1323. {
  1324. playFxOnTag( level.chopper_fx["damage"]["on_fire"], self, "tag_engine_left" );
  1325. self thread heli_crash();
  1326. }
  1327. }
  1328.  
  1329. wait 0.05;
  1330. }
  1331. }
  1332.  
  1333.  
  1334. // attach helicopter on crash path
  1335. heli_crash()
  1336. {
  1337. self notify( "crashing" );
  1338.  
  1339. crashNode = level.heli_crash_nodes[ randomInt( level.heli_crash_nodes.size ) ];
  1340.  
  1341. self thread heli_spin( 180 );
  1342. self thread heli_secondary_explosions();
  1343. self heli_fly_simple_path( crashNode );
  1344.  
  1345. self thread heli_explode();
  1346. }
  1347.  
  1348. heli_secondary_explosions()
  1349. {
  1350. playFxOnTag( level.chopper_fx["explode"]["large"], self, "tag_engine_left" );
  1351. self playSound ( level.heli_sound[self.team]["hitsecondary"] );
  1352.  
  1353. wait ( 3.0 );
  1354.  
  1355. if ( !isDefined( self ) )
  1356. return;
  1357.  
  1358. playFxOnTag( level.chopper_fx["explode"]["large"], self, "tag_engine_left" );
  1359. self playSound ( level.heli_sound[self.team]["hitsecondary"] );
  1360. }
  1361.  
  1362. // self spin at one rev per 2 sec
  1363. heli_spin( speed )
  1364. {
  1365. self endon( "death" );
  1366.  
  1367. // play hit sound immediately so players know they got it
  1368. self playSound ( level.heli_sound[self.team]["hit"] );
  1369.  
  1370. // play heli crashing spinning sound
  1371. self thread spinSoundShortly();
  1372.  
  1373. // spins until death
  1374. self setyawspeed( speed, speed, speed );
  1375. while ( isdefined( self ) )
  1376. {
  1377. self settargetyaw( self.angles[1]+(speed*0.9) );
  1378. wait ( 1 );
  1379. }
  1380. }
  1381.  
  1382.  
  1383. spinSoundShortly()
  1384. {
  1385. self endon("death");
  1386.  
  1387. wait .25;
  1388.  
  1389. self stopLoopSound();
  1390. wait .05;
  1391. self playLoopSound( level.heli_sound[self.team]["spinloop"] );
  1392. wait .05;
  1393. self playLoopSound( level.heli_sound[self.team]["spinstart"] );
  1394. }
  1395.  
  1396.  
  1397. // crash explosion
  1398. heli_explode( altStyle )
  1399. {
  1400. self notify( "death" );
  1401.  
  1402. if ( isDefined( altStyle ) && isDefined( level.chopper_fx["explode"]["air_death"][self.heli_type] ) )
  1403. {
  1404. deathAngles = self getTagAngles( "tag_deathfx" );
  1405.  
  1406. playFx( level.chopper_fx["explode"]["air_death"][self.heli_type], self getTagOrigin( "tag_deathfx" ), anglesToForward( deathAngles ), anglesToUp( deathAngles ) );
  1407. //playFxOnTag( level.chopper_fx["explode"]["air_death"][self.heli_type], self, "tag_deathfx" );
  1408. }
  1409. else
  1410. {
  1411. org = self.origin;
  1412. forward = ( self.origin + ( 0, 0, 1 ) ) - self.origin;
  1413. playFx( level.chopper_fx["explode"]["death"][self.heli_type], org, forward );
  1414. }
  1415.  
  1416.  
  1417. // play heli explosion sound
  1418. self playSound( level.heli_sound[self.team]["crash"] );
  1419.  
  1420. // give "death" notify time to process
  1421. wait ( 0.05 );
  1422. self delete();
  1423. }
  1424.  
  1425.  
  1426. fire_missile( sMissileType, iShots, eTarget )
  1427. {
  1428. if ( !isdefined( iShots ) )
  1429. iShots = 1;
  1430. assert( self.health > 0 );
  1431.  
  1432. weaponName = undefined;
  1433. weaponShootTime = undefined;
  1434. defaultWeapon = "cobra_20mm_mp";
  1435. tags = [];
  1436. switch( sMissileType )
  1437. {
  1438. case "ffar":
  1439. weaponName = "harrier_FFAR_mp";
  1440.  
  1441. tags[ 0 ] = "tag_store_r_2";
  1442. break;
  1443. default:
  1444. assertMsg( "Invalid missile type specified. Must be ffar" );
  1445. break;
  1446. }
  1447. assert( isdefined( weaponName ) );
  1448. assert( tags.size > 0 );
  1449.  
  1450. weaponShootTime = weaponfiretime( weaponName );
  1451. assert( isdefined( weaponShootTime ) );
  1452.  
  1453. self setVehWeapon( weaponName );
  1454. nextMissileTag = -1;
  1455. for( i = 0 ; i < iShots ; i++ ) // I don't believe iShots > 1 is properly supported; we don't set the weapon each time
  1456. {
  1457. nextMissileTag++;
  1458. if ( nextMissileTag >= tags.size )
  1459. nextMissileTag = 0;
  1460.  
  1461. self setVehWeapon( "harrier_FFAR_mp" );
  1462.  
  1463. if ( isdefined( eTarget ) )
  1464. {
  1465. eMissile = self fireWeapon( tags[ nextMissileTag ], eTarget );
  1466. eMissile Missile_SetFlightmodeDirect();
  1467. eMissile Missile_SetTargetEnt( eTarget );
  1468. }
  1469. else
  1470. {
  1471. eMissile = self fireWeapon( tags[ nextMissileTag ] );
  1472. eMissile Missile_SetFlightmodeDirect();
  1473. eMissile Missile_SetTargetEnt( eTarget );
  1474. }
  1475.  
  1476. if ( i < iShots - 1 )
  1477. wait weaponShootTime;
  1478. }
  1479. // avoid calling setVehWeapon again this frame or the client doesn't hear about the original weapon change
  1480. }
  1481.  
  1482. // checks if owner is valid, returns false if not valid
  1483. check_owner()
  1484. {
  1485. if ( !isdefined( self.owner ) || !isdefined( self.owner.pers["team"] ) || self.owner.pers["team"] != self.team )
  1486. {
  1487. self thread heli_leave();
  1488.  
  1489. return false;
  1490. }
  1491.  
  1492. return true;
  1493. }
  1494.  
  1495.  
  1496. heli_leave_on_disconnect( owner )
  1497. {
  1498. self endon ( "death" );
  1499. self endon ( "helicopter_done" );
  1500.  
  1501. owner waittill( "disconnect" );
  1502.  
  1503. self thread heli_leave();
  1504. }
  1505.  
  1506. heli_leave_on_changeTeams( owner )
  1507. {
  1508. self endon ( "death" );
  1509. self endon ( "helicopter_done" );
  1510.  
  1511. owner waittill_any( "joined_team", "joined_spectators" );
  1512.  
  1513. self thread heli_leave();
  1514. }
  1515.  
  1516. heli_leave_on_spawned( owner )
  1517. {
  1518. self endon ( "death" );
  1519. self endon ( "helicopter_done" );
  1520.  
  1521. owner waittill( "spawned" );
  1522.  
  1523. self thread heli_leave();
  1524. }
  1525.  
  1526. heli_leave_on_gameended( owner )
  1527. {
  1528. self endon ( "death" );
  1529. self endon ( "helicopter_done" );
  1530.  
  1531. level waittill ( "game_ended" );
  1532.  
  1533. self thread heli_leave();
  1534. }
  1535.  
  1536. heli_leave_on_timeout( timeOut )
  1537. {
  1538. self endon ( "death" );
  1539. self endon ( "helicopter_done" );
  1540.  
  1541. maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( timeOut );
  1542.  
  1543. self thread heli_leave();
  1544. }
  1545.  
  1546. attack_targets()
  1547. {
  1548. //self thread turret_kill_players();
  1549. self thread attack_primary();
  1550. self thread attack_secondary();
  1551. }
  1552.  
  1553.  
  1554. // missile only
  1555. attack_secondary()
  1556. {
  1557. self endon( "death" );
  1558. self endon( "crashing" );
  1559. self endon( "leaving" );
  1560.  
  1561. for( ;; )
  1562. {
  1563. if ( isdefined( self.secondaryTarget ) )
  1564. {
  1565. self.secondaryTarget.antithreat = undefined;
  1566. self.missileTarget = self.secondaryTarget;
  1567.  
  1568. antithreat = 0;
  1569.  
  1570. while( isdefined( self.missileTarget ) && isalive( self.missileTarget ) )
  1571. {
  1572. // if selected target is not in missile hit range, skip
  1573. if( self missile_target_sight_check( self.missileTarget ) )
  1574. self thread missile_support( self.missileTarget, level.heli_missile_rof);
  1575. else
  1576. break;
  1577.  
  1578. self waittill( "missile ready" );
  1579.  
  1580. // target might disconnect or change during last assault cycle
  1581. if ( !isdefined( self.secondaryTarget ) || ( isdefined( self.secondaryTarget ) && self.missileTarget != self.secondaryTarget ) )
  1582. break;
  1583. }
  1584. // reset the antithreat factor
  1585. if ( isdefined( self.missileTarget ) )
  1586. self.missileTarget.antithreat = undefined;
  1587. }
  1588. self waittill( "secondary acquired" );
  1589.  
  1590. // check if owner has left, if so, leave
  1591. self check_owner();
  1592. }
  1593. }
  1594.  
  1595. // check if missile is in hittable sight zone
  1596. missile_target_sight_check( missiletarget )
  1597. {
  1598. heli2target_normal = vectornormalize( missiletarget.origin - self.origin );
  1599. heli2forward = anglestoforward( self.angles );
  1600. heli2forward_normal = vectornormalize( heli2forward );
  1601.  
  1602. heli_dot_target = vectordot( heli2target_normal, heli2forward_normal );
  1603.  
  1604. if ( heli_dot_target >= level.heli_missile_target_cone )
  1605. {
  1606. debug_print3d_simple( "Missile sight: " + heli_dot_target, self, ( 0,0,-40 ), 40 );
  1607. return true;
  1608. }
  1609. return false;
  1610. }
  1611.  
  1612. // if wait for turret turning is too slow, enable missile assault support
  1613. missile_support( target_player, rof )
  1614. {
  1615. self endon( "death" );
  1616. self endon( "crashing" );
  1617. self endon( "leaving" );
  1618.  
  1619. if ( isdefined( target_player ) )
  1620. {
  1621. if ( level.teambased )
  1622. {
  1623. if ( isDefined( target_player.owner ) && target_player.team != self.team )
  1624. {
  1625. self fire_missile( "ffar", 1, target_player );
  1626. self notify( "missile fired" );
  1627. }
  1628. }
  1629. else
  1630. {
  1631. if ( isDefined( target_player.owner ) && target_player.owner != self.owner )
  1632. {
  1633. self fire_missile( "ffar", 1, target_player );
  1634. self notify( "missile fired" );
  1635. }
  1636. }
  1637. }
  1638.  
  1639. wait ( rof );
  1640. self notify ( "missile ready" );
  1641.  
  1642. return;
  1643. }
  1644.  
  1645. // mini-gun with missile support
  1646. attack_primary()
  1647. {
  1648. self endon( "death" );
  1649. self endon( "crashing" );
  1650. self endon( "leaving" );
  1651.  
  1652. while ( 1 )
  1653. {
  1654. wait ( 0.05 );
  1655.  
  1656. if ( !isAlive( self.primaryTarget ) )
  1657. continue;
  1658.  
  1659. currentTarget = self.primaryTarget;
  1660.  
  1661. currentTarget.antithreat = 0;
  1662.  
  1663. if ( randomInt(5) < 3 )
  1664. angle = currentTarget.angles[1] + randomFloatRange( -30, 30 );
  1665. else
  1666. angle = randomInt( 360 );
  1667.  
  1668. radiusOffset = 96;
  1669.  
  1670. xOffset = cos( angle ) * radiusOffset;
  1671. yOffset = sin( angle ) * radiusOffset;
  1672.  
  1673. self setTurretTargetEnt( currentTarget, (xOffset,yOffset,40) );
  1674.  
  1675. self waitOnTargetOrDeath( currentTarget, 3.0 );
  1676.  
  1677. if ( !isAlive( currentTarget ) || !self Vehicle_CanTurretTargetPoint( currentTarget.origin+(0,0,40) ) )
  1678. continue;
  1679.  
  1680. weaponShootTime = weaponFireTime( "cobra_20mm_mp" );
  1681.  
  1682. convergenceMod = 1;
  1683. shotsSinceLastSighting = 0;
  1684.  
  1685. self playLoopSound( "weap_cobra_20mm_fire_npc" );
  1686. for ( i = 0; i < level.heli_turretClipSize; i++ )
  1687. {
  1688. self setVehWeapon( "cobra_20mm_mp" );
  1689. self fireWeapon( "tag_flash" );
  1690.  
  1691. if ( i < level.heli_turretClipSize - 1 )
  1692. wait weaponShootTime;
  1693.  
  1694. if ( !isDefined( currentTarget ) )
  1695. break;
  1696.  
  1697. if ( self Vehicle_CanTurretTargetPoint( currentTarget.origin+(0,0,40), 1, self ) )
  1698. {
  1699. convergenceMod = max( convergenceMod - 0.05, 0 );
  1700. shotsSinceLastSighting = 0;
  1701. }
  1702. else
  1703. {
  1704. shotsSinceLastSighting++;
  1705. }
  1706.  
  1707. if ( shotsSinceLastSighting > 10 )
  1708. break;
  1709.  
  1710. targetPos = ( (xOffset*convergenceMod)+randomFloatRange( -6, 6 ),(yOffset*convergenceMod)+randomFloatRange( -6, 6 ),40+randomFloatRange( -6, 6 ) );
  1711.  
  1712. self setTurretTargetEnt( currentTarget, targetPos );
  1713. }
  1714. self stopLoopSound();
  1715.  
  1716. // lower the target's threat since already assaulted on
  1717. if ( isAlive( currentTarget ) )
  1718. currentTarget.antithreat += 100;
  1719.  
  1720. wait ( randomFloatRange( 0.5, 2.0 ) );
  1721. }
  1722. }
  1723.  
  1724. waitOnTargetOrDeath( target, timeOut )
  1725. {
  1726. self endon ( "death" );
  1727. self endon ( "helicopter_done" );
  1728.  
  1729. target endon ( "death" );
  1730. target endon ( "disconnect" );
  1731.  
  1732. self waittill_notify_or_timeout( "turret_on_target", timeOut );
  1733. }
  1734.  
  1735.  
  1736. fireMissile( missileTarget )
  1737. {
  1738. self endon( "death" );
  1739. self endon( "crashing" );
  1740. self endon( "leaving" );
  1741.  
  1742. assert( self.health > 0 );
  1743.  
  1744. if ( !isdefined( missileTarget ) )
  1745. return;
  1746.  
  1747. if ( Distance2D(self.origin, missileTarget.origin ) < 512 )
  1748. return;
  1749.  
  1750. self setVehWeapon( "harrier_FFAR_mp" );
  1751. missile = self fireWeapon( "tag_flash", missileTarget );
  1752. missile Missile_SetFlightmodeDirect();
  1753. missile Missile_SetTargetEnt( missileTarget );
  1754. }
  1755.  
  1756.  
  1757. // ====================================================================================
  1758. // Helicopter Pathing Logic
  1759. // ====================================================================================
  1760.  
  1761. getOriginOffsets( goalNode )
  1762. {
  1763. startOrigin = self.origin;
  1764. endOrigin = goalNode.origin;
  1765.  
  1766. numTraces = 0;
  1767. maxTraces = 40;
  1768.  
  1769. traceOffset = (0,0,-196);
  1770.  
  1771. traceOrigin = physicsTrace( startOrigin+traceOffset, endOrigin+traceOffset );
  1772.  
  1773. while ( distance( traceOrigin, endOrigin+traceOffset ) > 10 && numTraces < maxTraces )
  1774. {
  1775. println( "trace failed: " + distance( physicsTrace( startOrigin+traceOffset, endOrigin+traceOffset ), endOrigin+traceOffset ) );
  1776.  
  1777. if ( startOrigin[2] < endOrigin[2] )
  1778. {
  1779. startOrigin += (0,0,128);
  1780. }
  1781. else if ( startOrigin[2] > endOrigin[2] )
  1782. {
  1783. endOrigin += (0,0,128);
  1784. }
  1785. else
  1786. {
  1787. startOrigin += (0,0,128);
  1788. endOrigin += (0,0,128);
  1789. }
  1790.  
  1791. //thread draw_line( startOrigin+traceOffset, endOrigin+traceOffset, (0,1,9), 200 );
  1792. numTraces++;
  1793.  
  1794. traceOrigin = physicsTrace( startOrigin+traceOffset, endOrigin+traceOffset );
  1795. }
  1796.  
  1797. offsets = [];
  1798. offsets["start"] = startOrigin;
  1799. offsets["end"] = endOrigin;
  1800. return offsets;
  1801. }
  1802.  
  1803.  
  1804. travelToNode( goalNode )
  1805. {
  1806. originOffets = getOriginOffsets( goalNode );
  1807.  
  1808. if ( originOffets["start"] != self.origin )
  1809. {
  1810. // motion change via node
  1811. if( isdefined( goalNode.script_airspeed ) && isdefined( goalNode.script_accel ) )
  1812. {
  1813. heli_speed = goalNode.script_airspeed;
  1814. heli_accel = goalNode.script_accel;
  1815. }
  1816. else
  1817. {
  1818. heli_speed = 30+randomInt(20);
  1819. heli_accel = 15+randomInt(15);
  1820. }
  1821.  
  1822. self Vehicle_SetSpeed( heli_speed, heli_accel );
  1823. self setvehgoalpos( originOffets["start"] + (0,0,30), 0 );
  1824. // calculate ideal yaw
  1825. self setgoalyaw( goalNode.angles[ 1 ] + level.heli_angle_offset );
  1826.  
  1827. //println( "setting goal to startOrigin" );
  1828.  
  1829. self waittill ( "goal" );
  1830. }
  1831.  
  1832. if ( originOffets["end"] != goalNode.origin )
  1833. {
  1834. // motion change via node
  1835. if( isdefined( goalNode.script_airspeed ) && isdefined( goalNode.script_accel ) )
  1836. {
  1837. heli_speed = goalNode.script_airspeed;
  1838. heli_accel = goalNode.script_accel;
  1839. }
  1840. else
  1841. {
  1842. heli_speed = 30+randomInt(20);
  1843. heli_accel = 15+randomInt(15);
  1844. }
  1845.  
  1846. self Vehicle_SetSpeed( heli_speed, heli_accel );
  1847. self setvehgoalpos( originOffets["end"] + (0,0,30), 0 );
  1848. // calculate ideal yaw
  1849. self setgoalyaw( goalNode.angles[ 1 ] + level.heli_angle_offset );
  1850.  
  1851. //println( "setting goal to endOrigin" );
  1852.  
  1853. self waittill ( "goal" );
  1854. }
  1855. }
  1856.  
  1857.  
  1858. heli_fly_simple_path( startNode )
  1859. {
  1860. self endon ( "death" );
  1861. self endon ( "leaving" );
  1862.  
  1863. // only one thread instance allowed
  1864. self notify( "flying");
  1865. self endon( "flying" );
  1866.  
  1867. heli_reset();
  1868.  
  1869. currentNode = startNode;
  1870. while ( isDefined( currentNode.target ) )
  1871. {
  1872. nextNode = getEnt( currentNode.target, "targetname" );
  1873. assertEx( isDefined( nextNode ), "Next node in path is undefined, but has targetname" );
  1874.  
  1875. if( isDefined( currentNode.script_airspeed ) && isDefined( currentNode.script_accel ) )
  1876. {
  1877. heli_speed = currentNode.script_airspeed;
  1878. heli_accel = currentNode.script_accel;
  1879. }
  1880. else
  1881. {
  1882. heli_speed = 30 + randomInt(20);
  1883. heli_accel = 15 + randomInt(15);
  1884. }
  1885.  
  1886. self Vehicle_SetSpeed( heli_speed, heli_accel );
  1887.  
  1888. // end of the path
  1889. if ( !isDefined( nextNode.target ) )
  1890. {
  1891. self setVehGoalPos( nextNode.origin+(self.zOffset), true );
  1892. self waittill( "near_goal" );
  1893. }
  1894. else
  1895. {
  1896. self setVehGoalPos( nextNode.origin+(self.zOffset), false );
  1897. self waittill( "near_goal" );
  1898.  
  1899. self setGoalYaw( nextNode.angles[ 1 ] );
  1900.  
  1901. self waittillmatch( "goal" );
  1902. }
  1903.  
  1904. currentNode = nextNode;
  1905. }
  1906.  
  1907. printLn( currentNode.origin );
  1908. printLn( self.origin );
  1909. }
  1910.  
  1911.  
  1912. heli_fly_loop_path( startNode )
  1913. {
  1914. self endon ( "death" );
  1915. self endon ( "crashing" );
  1916. self endon ( "leaving" );
  1917.  
  1918. // only one thread instance allowed
  1919. self notify( "flying");
  1920. self endon( "flying" );
  1921.  
  1922. heli_reset();
  1923.  
  1924. self thread heli_loop_speed_control( startNode );
  1925.  
  1926. currentNode = startNode;
  1927. while ( isDefined( currentNode.target ) )
  1928. {
  1929. nextNode = getEnt( currentNode.target, "targetname" );
  1930. assertEx( isDefined( nextNode ), "Next node in path is undefined, but has targetname" );
  1931.  
  1932. if( isDefined( currentNode.script_airspeed ) && isDefined( currentNode.script_accel ) )
  1933. {
  1934. self.desired_speed = currentNode.script_airspeed;
  1935. self.desired_accel = currentNode.script_accel;
  1936. }
  1937. else
  1938. {
  1939. self.desired_speed = 30 + randomInt( 20 );
  1940. self.desired_accel = 15 + randomInt( 15 );
  1941. }
  1942.  
  1943. if ( self.heliType == "flares" )
  1944. {
  1945. self.desired_speed *= 0.5;
  1946. self.desired_accel *= 0.5;
  1947. }
  1948.  
  1949. if ( isDefined( nextNode.script_delay ) && isDefined( self.primaryTarget ) && !self heli_is_threatened() )
  1950. {
  1951. self setVehGoalPos( nextNode.origin+(self.zOffset), true );
  1952. self waittill( "near_goal" );
  1953.  
  1954. wait ( nextNode.script_delay );
  1955. }
  1956. else
  1957. {
  1958. self setVehGoalPos( nextNode.origin+(self.zOffset), false );
  1959. self waittill( "near_goal" );
  1960.  
  1961. self setGoalYaw( nextNode.angles[ 1 ] );
  1962.  
  1963. self waittillmatch( "goal" );
  1964. }
  1965.  
  1966. currentNode = nextNode;
  1967. }
  1968. }
  1969.  
  1970.  
  1971. heli_loop_speed_control( currentNode )
  1972. {
  1973. self endon ( "death" );
  1974. self endon ( "crashing" );
  1975. self endon ( "leaving" );
  1976.  
  1977. if( isDefined( currentNode.script_airspeed ) && isDefined( currentNode.script_accel ) )
  1978. {
  1979. self.desired_speed = currentNode.script_airspeed;
  1980. self.desired_accel = currentNode.script_accel;
  1981. }
  1982. else
  1983. {
  1984. self.desired_speed = 30 + randomInt( 20 );
  1985. self.desired_accel = 15 + randomInt( 15 );
  1986. }
  1987.  
  1988. lastSpeed = 0;
  1989. lastAccel = 0;
  1990.  
  1991. while ( 1 )
  1992. {
  1993. goalSpeed = self.desired_speed;
  1994. goalAccel = self.desired_accel;
  1995.  
  1996. if ( self.heliType != "flares" && isDefined( self.primaryTarget ) && !self heli_is_threatened() )
  1997. goalSpeed *= 0.25;
  1998.  
  1999. if ( lastSpeed != goalSpeed || lastAccel != goalAccel )
  2000. {
  2001. self Vehicle_SetSpeed( goalSpeed, goalAccel );
  2002.  
  2003. lastSpeed = goalSpeed;
  2004. lastAccel = goalAccel;
  2005. }
  2006.  
  2007. wait ( 0.05 );
  2008. }
  2009. }
  2010.  
  2011.  
  2012. heli_is_threatened()
  2013. {
  2014. if ( self.recentDamageAmount > 50 )
  2015. return true;
  2016.  
  2017. if ( self.currentState == "heavy smoke" )
  2018. return true;
  2019.  
  2020. return false;
  2021. }
  2022.  
  2023.  
  2024. heli_fly_well( destNodes )
  2025. {
  2026. self notify( "flying");
  2027. self endon( "flying" );
  2028.  
  2029. self endon ( "death" );
  2030. self endon ( "crashing" );
  2031. self endon ( "leaving" );
  2032.  
  2033. for ( ;; )
  2034. {
  2035. currentNode = self get_best_area_attack_node( destNodes );
  2036.  
  2037. travelToNode( currentNode );
  2038.  
  2039. // motion change via node
  2040. if( isdefined( currentNode.script_airspeed ) && isdefined( currentNode.script_accel ) )
  2041. {
  2042. heli_speed = currentNode.script_airspeed;
  2043. heli_accel = currentNode.script_accel;
  2044. }
  2045. else
  2046. {
  2047. heli_speed = 30+randomInt(20);
  2048. heli_accel = 15+randomInt(15);
  2049. }
  2050.  
  2051. self Vehicle_SetSpeed( heli_speed, heli_accel );
  2052. self setvehgoalpos( currentNode.origin + self.zOffset, 1 );
  2053. self setgoalyaw( currentNode.angles[ 1 ] + level.heli_angle_offset );
  2054.  
  2055. if ( level.heli_forced_wait != 0 )
  2056. {
  2057. self waittill( "near_goal" ); //self waittillmatch( "goal" );
  2058. wait ( level.heli_forced_wait );
  2059. }
  2060. else if ( !isdefined( currentNode.script_delay ) )
  2061. {
  2062. self waittill( "near_goal" ); //self waittillmatch( "goal" );
  2063.  
  2064. wait ( 5 + randomInt( 5 ) );
  2065. }
  2066. else
  2067. {
  2068. self waittillmatch( "goal" );
  2069. wait ( currentNode.script_delay );
  2070. }
  2071. }
  2072. }
  2073.  
  2074.  
  2075. get_best_area_attack_node( destNodes )
  2076. {
  2077. return updateAreaNodes( destNodes );
  2078. }
  2079.  
  2080.  
  2081. // helicopter leaving parameter, can not be damaged while leaving
  2082. heli_leave()
  2083. {
  2084. self notify( "leaving" );
  2085.  
  2086. leaveNode = level.heli_leave_nodes[ randomInt( level.heli_leave_nodes.size ) ];
  2087.  
  2088. self heli_reset();
  2089. self Vehicle_SetSpeed( 100, 45 );
  2090. self setvehgoalpos( leaveNode.origin, 1 );
  2091. self waittillmatch( "goal" );
  2092. self notify( "death" );
  2093.  
  2094. // give "death" notify time to process
  2095. wait ( 0.05 );
  2096. self delete();
  2097. }
  2098.  
  2099.  
  2100. // ====================================================================================
  2101. // DEBUG INFORMATION
  2102. // ====================================================================================
  2103.  
  2104. debug_print3d( message, color, ent, origin_offset, frames )
  2105. {
  2106. if ( isdefined( level.heli_debug ) && level.heli_debug == 1.0 )
  2107. self thread draw_text( message, color, ent, origin_offset, frames );
  2108. }
  2109.  
  2110. debug_print3d_simple( message, ent, offset, frames )
  2111. {
  2112. if ( isdefined( level.heli_debug ) && level.heli_debug == 1.0 )
  2113. {
  2114. if( isdefined( frames ) )
  2115. thread draw_text( message, ( 0.8, 0.8, 0.8 ), ent, offset, frames );
  2116. else
  2117. thread draw_text( message, ( 0.8, 0.8, 0.8 ), ent, offset, 0 );
  2118. }
  2119. }
  2120.  
  2121. debug_line( from, to, color, frames )
  2122. {
  2123. if ( isdefined( level.heli_debug ) && level.heli_debug == 1.0 && !isdefined( frames ) )
  2124. {
  2125. thread draw_line( from, to, color );
  2126. }
  2127. else if ( isdefined( level.heli_debug ) && level.heli_debug == 1.0 )
  2128. thread draw_line( from, to, color, frames);
  2129. }
  2130.  
  2131. draw_text( msg, color, ent, offset, frames )
  2132. {
  2133. //level endon( "helicopter_done" );
  2134. if( frames == 0 )
  2135. {
  2136. while ( isdefined( ent ) )
  2137. {
  2138. print3d( ent.origin+offset, msg , color, 0.5, 4 );
  2139. wait 0.05;
  2140. }
  2141. }
  2142. else
  2143. {
  2144. for( i=0; i < frames; i++ )
  2145. {
  2146. if( !isdefined( ent ) )
  2147. break;
  2148. print3d( ent.origin+offset, msg , color, 0.5, 4 );
  2149. wait 0.05;
  2150. }
  2151. }
  2152. }
  2153.  
  2154. draw_line( from, to, color, frames )
  2155. {
  2156. //level endon( "helicopter_done" );
  2157. if( isdefined( frames ) )
  2158. {
  2159. for( i=0; i<frames; i++ )
  2160. {
  2161. line( from, to, color );
  2162. wait 0.05;
  2163. }
  2164. }
  2165. else
  2166. {
  2167. for( ;; )
  2168. {
  2169. line( from, to, color );
  2170. wait 0.05;
  2171. }
  2172. }
  2173. }
  2174.  
  2175.  
  2176.  
  2177. addToHeliList()
  2178. {
  2179. level.helis[self getEntityNumber()] = self;
  2180. }
  2181.  
  2182. removeFromHeliList( entityNumber )
  2183. {
  2184. level.helis[entityNumber] = undefined;
  2185. }
  2186.  
  2187.  
  2188. playFlareFx()
  2189. {
  2190. for ( i = 0; i < 10; i++ )
  2191. {
  2192. if ( !isDefined( self ) )
  2193. return;
  2194. PlayFXOnTag( level._effect[ "ac130_flare" ], self, "TAG_FLARE" );
  2195. wait ( 0.15 );
  2196. }
  2197. }
  2198.  
  2199.  
  2200. deployFlares()
  2201. {
  2202. flareObject = spawn( "script_origin", level.ac130.planemodel.origin );
  2203. flareObject.angles = level.ac130.planemodel.angles;
  2204.  
  2205. flareObject moveGravity( (0, 0, 0), 5.0 );
  2206.  
  2207. flareObject thread deleteAfterTime( 5.0 );
  2208.  
  2209. return flareObject;
  2210. }
  2211.  
  2212.  
  2213. heli_flares_monitor()
  2214. {
  2215. level endon ( "game_ended" );
  2216.  
  2217. for ( ;; )
  2218. {
  2219. level waittill ( "stinger_fired", player, missile, lockTarget );
  2220.  
  2221. if ( !IsDefined( lockTarget ) || (lockTarget != self) )
  2222. continue;
  2223.  
  2224. missile endon ( "death" );
  2225.  
  2226. self thread playFlareFx();
  2227. newTarget = self deployFlares();
  2228. missile Missile_SetTargetEnt( newTarget );
  2229. return;
  2230. }
  2231. }
  2232.  
  2233. deleteAfterTime( delay )
  2234. {
  2235. wait ( delay );
  2236.  
  2237. self delete();
  2238. }
  2239.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement