JoBoz

asds

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