Advertisement
Guest User

Untitled

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