‪Black Ops 3 Source Code Explorer  0.1
‪An script explorer for Black Ops 3 by ZeRoY
_quadtank.gsc
Go to the documentation of this file.
1 #using scripts\codescripts\struct;
2 
3 #using scripts\shared\clientfield_shared;
4 #using scripts\shared\gameskill_shared;
5 #using scripts\shared\math_shared;
6 #using scripts\shared\statemachine_shared;
7 #using scripts\shared\system_shared;
8 #using scripts\shared\util_shared;
9 #using scripts\shared\turret_shared;
10 #using scripts\shared\flag_shared;
11 #using scripts\shared\damagefeedback_shared;
12 #using scripts\shared\laststand_shared;
13 #using scripts\shared\gameobjects_shared;
14 
15 #insert scripts\shared\shared.gsh;
16 #insert scripts\shared\statemachine.gsh;
17 #insert scripts\shared\version.gsh;
18 #insert scripts\shared\archetype_shared\archetype_shared.gsh;
19 
20 #using scripts\shared\ai\systems\blackboard;
21 #using scripts\shared\ai\blackboard_vehicle;
22 #insert scripts\shared\ai\utility.gsh;
23 
24 #using scripts\shared\vehicle_shared;
25 #using scripts\shared\vehicle_ai_shared;
26 #using scripts\shared\vehicle_death_shared;
27 
28 #using scripts\mp\killstreaks\_killstreaks;
29 #using scripts\mp\killstreaks\_killstreak_bundles;
30 
31 #define SCAN_HEIGHT_OFFSET 40
32 
33 #define TURRET_STATE_SCAN_AT_ENEMY 0
34 #define TURRET_STATE_SCAN_FORWARD 1
35 #define TURRET_STATE_SCAN_RIGHT 2
36 #define TURRET_STATE_SCAN_FORWARD2 3
37 #define TURRET_STATE_SCAN_LEFT 4
38 #define NUM_TURRET_STATES 5
39 
40 #define DEFAULT_WEAK_SPOT_DAMAGE_LIMIT 600
41 #define TROPHY_DISABLE_LIMIT 1 //Number of times that a player can destroy the trophy weakspot before it is permanently destroyed
42 
43 #define SPIKE_HIT_LIMIT 5 //Used to limit the number of spikes that can hit a QT before trophy system gets re-enabled
44 
45 #define MELEE_RADIUS 270
46 #define MELEE_INNER_RADIUS_DAMAGE 400
47 #define MELEE_OUTER_RADIUS_DAMAGE 400
48 
49 #define ROCKET_LAUNCHER_MIN_DIST 350
50 
51 #define WEAPON_JAVELIN "quadtank_main_turret_rocketpods_javelin"
52 #define WEAPON_STRAIGHT "quadtank_main_turret_rocketpods_straight"
53 #define JAVELIN_MIN_USE_DISTANCE 800 //an actor target must be at least this range from the QT for the QT to use the javelin attack
54 
55 #define NEAR_GOAL_DIST 50
56 
57 #define WEAKSPOT_BONE_NAME "tag_target_lower"
58 #precache( "string", WEAKSPOT_BONE_NAME );
59 
60 #define QUADTANK_BUNDLE "quadtank"
61 
62 #namespace quadtank;
63 
64 ‪REGISTER_SYSTEM( "quadtank", &‪__init__, undefined )
65 
66 #using_animtree( "generic" );
67 
68 function ‪__init__()
69 {
70  vehicle::add_main_callback( "quadtank", &‪quadtank_initialize );
71 
72  ‪clientfield::register( "toplayer", "player_shock_fx", ‪VERSION_SHIP, 1, "int" );
73  ‪clientfield::register( "vehicle", "quadtank_trophy_state", ‪VERSION_SHIP, 1, "int" );
74 }
75 
77 {
78  self useanimtree( #animtree );
79 
80  self EnableAimAssist();
81  self SetNearGoalNotifyDist( ‪NEAR_GOAL_DIST );
82 
83  // AI SPECIFIC INITIALIZATION
86 
87  self.turret_state = ‪TURRET_STATE_SCAN_FORWARD;
88 
89  self.fovcosine = 0; // +/-90 degrees = 180 fov, err 0 actually means 360 degree view
90  self.fovcosinebusy = 0;
91  self.maxsightdistsqrd = ‪SQR( 10000 );
92 
93  self.weakpointobjective = 0;
94  self.combatactive = true; //used for weakpoint marker to make sure that objective is not added if tank is off
95  self.damage_during_trophy_down = 0;
96  self.spike_hits_during_trophy_down = 0;
97  self.trophy_disables = 0;
98  self.allow_movement = true;
99 
100  assert( isdefined( self.scriptbundlesettings ) );
101 
102  self.settings = ‪struct::get_script_bundle( "vehiclecustomsettings", self.scriptbundlesettings );
103 
104  self.variant = "cannon";
105 
106  if( IsSubStr( self.vehicleType, "mlrs" ) )
107  {
108  self.variant = "rocketpod";
109  }
110 
111  self.goalRadius = 9999999;
112  self.goalHeight = 512;
113  self SetGoal( self.origin, false, self.goalRadius, self.goalHeight );
114 
115  self SetSpeed( self.settings.defaultMoveSpeed, 10, 10 );
116  self SetMinDesiredTurnYaw( 45 );
117  self ‪show_weak_spots( false );
118 
121 
124 
126 
128  self.overrideVehicleDamage = &‪QuadtankCallback_VehicleDamage;
129 
130  //disable some cybercom abilities
131  if( IsDefined( level.vehicle_initializer_cb ) )
132  {
133  [[level.vehicle_initializer_cb]]( self );
134  }
135 
136  self.ignoreFireFly = true;
137  self.ignoreDecoy = true;
139 
140  self.disableElectroDamage = true;
141  self.disableBurnDamage = true;
142 
143  self thread ‪vehicle_ai::target_hijackers();
144 
145  self HidePart( "tag_defense_active" );
146 
147 
150 
153  self.heatlh = self.maxhealth;
154 
155  self thread ‪monitor_enter_vehicle();
156 }
157 
159 {
160 // testing out changing the turret parameters based solely upon the number of players, since damage
161 // is alread scaled based upon the difficulty of the individual player
162 // so, saving out current method until change has been tested
163 //
164 // value = gameskill::get_general_difficulty_level();
165 //
166 // scale_up = mapfloat( 0, 7, 0.8, 2.0, value );
167 // scale_down = mapfloat( 0, 7, 1.0, 0.5, value );
168 
169  if( isDefined( level.players) )
170  {
171  value = level.players.size;
172  }
173  else
174  {
175  value = 1;
176  }
177 
178 
179  scale_up = mapfloat( 1, 4, 1, 1.5, value );
180  scale_down = mapfloat( 1, 4, 1.0, 0.75, value );
181 
182  ‪turret::set_burst_parameters( 1.5, 2.5 * scale_up, 0.25 * scale_down, 0.75 * scale_down, 1 );
183  ‪turret::set_burst_parameters( 1.5, 2.5 * scale_up, 0.25 * scale_down, 0.75 * scale_down, 2 );
184 
185  self.difficulty_scale_up = scale_up;
186  self.difficulty_scale_down = scale_down;
187 }
188 
189 function ‪defaultRole()
190 {
191  self.state_machine = self ‪vehicle_ai::init_state_machine_for_role( "default" );
192 
193  self ‪vehicle_ai::get_state_callbacks( "pain" ).update_func = &‪pain_update;
194  self ‪vehicle_ai::get_state_callbacks( "emped" ).update_func = &‪quadtank_emped;
195 
196  self ‪vehicle_ai::get_state_callbacks( "off" ).enter_func = &‪state_off_enter;
197  self ‪vehicle_ai::get_state_callbacks( "off" ).exit_func = &‪state_off_exit;
198 
199  self ‪vehicle_ai::get_state_callbacks( "scripted" ).update_func = &‪state_scripted_update;
200  self ‪vehicle_ai::get_state_callbacks( "driving" ).update_func = &‪state_driving_update;
201 
202  self ‪vehicle_ai::get_state_callbacks( "combat" ).update_func = &‪state_combat_update;
203  self ‪vehicle_ai::get_state_callbacks( "combat" ).exit_func = &‪state_combat_exit;
204 
205  self ‪vehicle_ai::get_state_callbacks( "death" ).update_func = &‪quadtank_death;
206 
208 
210 }
211 
212 // ----------------------------------------------
213 // State: off
214 // ----------------------------------------------
215 function ‪quadtank_off()
216 {
217  self ‪vehicle_ai::set_state( "off" );
218  self.combatactive = false;
219 }
220 
221 function ‪quadtank_on()
222 {
223  self ‪vehicle_ai::set_state( "combat" );
224  self.combatactive = true;
225 }
226 
227 function ‪state_off_enter( params )
228 {
229  self playsound( "veh_quadtank_power_down" );
230 
231  self LaserOff();
232  self ClearTargetEntity();
233  self CancelAIMove();
234  self ClearVehGoalPos();
235 
238  self ‪vehicle::toggle_tread_fx( 0 );
239  self ‪vehicle::toggle_sounds( 0 );
241 
242  angles = self GetTagAngles( "tag_flash" );
243  target_vec = self.origin + AnglesToForward( ( 0, angles[1], 0 ) ) * 1000;
244  target_vec = target_vec + ( 0, 0, -500 );
245  self SetTargetOrigin( target_vec );
246  self ‪set_side_turrets_enabled( false );
247  self thread ‪quadtank_disabletrophy();
248 
249  if( !isdefined( self.‪emped ) )
250  {
251  self DisableAimAssist();
252  }
253 }
254 
255 function ‪state_off_exit( params )
256 {
257  self ‪vehicle::lights_on();
258  self ‪vehicle::toggle_tread_fx( 1 );
259  self ‪vehicle::toggle_sounds( 1 );
260  self thread ‪bootup();
262  self EnableAimAssist();
263 }
264 
265 function ‪bootup()
266 {
267  self endon("death");
268  self playsound( "veh_quadtank_power_up" );
270 
271  angles = self GetTagAngles( "tag_flash" );
272  target_vec = self.origin + AnglesToForward( ( 0, angles[1], 0 ) ) * 1000;
273  self.turretRotScale = 0.3;
274 
275  driver = self GetSeatOccupant( 0 );
276  if( !isdefined(driver) )
277  {
278  self SetTargetOrigin( target_vec );
279  }
280  wait 1;
281 
282  self.turretRotScale = 1 * self.difficulty_scale_up;
283 }
284 // State: off -----------------------------------
285 
286 // ----------------------------------------------
287 // State: pain
288 // ----------------------------------------------
289 function ‪pain_update( params )
290 {
291  self endon( "change_state" );
292  self endon( "death" );
293 
294  isTrophyDownPain = params.notify_param[0];
295 
296  if( isTrophyDownPain === true )
297  {
298  // trophy system must be going down now
299  asmState = "trophy_disabled@stationary";
300  }
301  else
302  {
303  // can only take pain when trophy is down
304  asmState = "pain@stationary";
305  }
306  self ASMRequestSubstate( asmState );
307  playsoundatposition ("prj_quad_impact", self.origin);
308 
309  self CancelAIMove();
310  self ClearVehGoalPos();
311  self ClearTurretTarget();
312  self SetBrake( 1 );
313 
314  self ‪vehicle_ai::waittill_asm_complete( asmState, 6 );
315 
316  self SetBrake( 0 );
317 
318  self ASMRequestSubstate( "locomotion@movement" );
319 
320  driver = self GetSeatOccupant( 0 );
321  if( !isdefined( driver ) )
322  {
323  self ‪vehicle_ai::set_state( "combat" );
324  }
325  else
326  {
327  self ‪vehicle_ai::set_state( "driving" );
328  }
329 }
330 // State: pain ----------------------------------
331 
332 // ----------------------------------------------
333 // State: scripted
334 // ----------------------------------------------
335 function ‪state_scripted_update( params )
336 {
337  self endon( "death" );
338  self endon( "change_state" );
339 
340  self ‪set_side_turrets_enabled( false );
341  self LaserOff();
342  self ClearTargetEntity();
343  self CancelAIMove();
344  self ClearVehGoalPos();
345 
347 }
348 // State: scripted ------------------------------
349 
350 // ----------------------------------------------
351 // State: driving
352 // ----------------------------------------------
353 function ‪state_driving_update( params )
354 {
355  self endon( "death" );
356  self endon( "change_state" );
357 
358  self ‪set_side_turrets_enabled( false );
359  self LaserOff();
360  self ClearTargetEntity();
361  self CancelAIMove();
362  self ClearVehGoalPos();
363 
365 
366  driver = self GetSeatOccupant( 0 );
367  if( isdefined(driver) )
368  {
369  self.turretRotScale = 1;
370  self DisableAimAssist();
371  self thread ‪quadtank_set_team( driver.team );
372  self SetBrake( 0 );
373  self ASMRequestSubstate( "locomotion@movement" );
374  self thread ‪quadtank_player_fireupdate();
375  self thread ‪footstep_handler();
376 
377  self.trophy_disables = ‪TROPHY_DISABLE_LIMIT - 1;
378  self thread ‪quadtank_disabletrophy();
379  }
380 }
381 
383 {
384  self SetGoal( self.origin );
385 }
386 // State: driving -------------------------------
387 
388 // ----------------------------------------------
389 // State: combat
390 // ----------------------------------------------
391 function ‪state_combat_update( params )
392 {
393  self endon( "death" );
394  self endon( "change_state" );
395 
396  if ( isalive( self ) )
397  {
398  }
399 
400  if ( isalive( self ) && !‪trophy_disabled() )
401  {
402  self thread ‪quadtank_enabletrophy();
403  }
404 
405  if ( self.allow_movement )
406  {
407  self thread ‪quadtank_movementupdate();
408  }
409  else
410  {
411  self SetBrake( 1 );
412  }
413 
414  switch ( self.variant )
415  {
416  case "cannon":
417  ‪vehicle_ai::Cooldown( "main_cannon", 4 ); // don't shoot cannon immediately
418  self thread ‪quadtank_weapon_think_cannon();
419  break;
420  case "rocketpod":
421  self thread ‪Attack_Thread_rocket();
422  break;
423  }
424 }
425 
426 function ‪state_combat_exit( params )
427 {
428  self notify( "end_attack_thread" );
429  self notify( "end_movement_thread" );
430  self ClearTurretTarget();
431  self ClearLookAtEnt();
432 }
433 // State: combat --------------------------------
434 
435 // ----------------------------------------------
436 // State: death
437 // ----------------------------------------------
438 function ‪quadtank_death( params )
439 {
440  self endon( "death" );
441  self endon( "nodeath_thread" );
442 
443  //self set_trophy_state( false );
444  self ‪quadtank_weakpoint_display( false );
445  self ‪remove_repulsor();
446  self HidePart( "tag_lidar_null", "", true );
448 
449  // Need to prep the death model
450  StreamerModelHint( self.deathmodel, 6 );
451 
452  if ( !isdefined( self.custom_death_sequence ) )
453  {
454  playsoundatposition ("prj_quad_impact", self.origin);
455  self playsound( "veh_quadtank_power_down" );
456  self playsound("veh_quadtank_sparks");
457  self ASMRequestSubstate( "death@stationary" );
458  self waittill( "explosion_c" );
459  }
460  else
461  {
462  self [[self.custom_death_sequence]]();
463  }
464 
465  if( isdefined( level.disable_thermal ) )
466  {
467  [[level.disable_thermal]]();
468  }
469 
470  if( isdefined( self.stun_fx ) )
471  {
472  self.stun_fx delete();
473  }
474 
475  BadPlace_Box( "", 0, self.origin, 90, "neutral" );
476  self ‪vehicle_death::set_death_model( self.deathmodel, self.modelswapdelay );
478 
479  ‪vehicle_ai::waittill_asm_complete( "death@stationary", 5 );
480 
481  self thread ‪vehicle_death::CleanUp();
483 }
484 
485 // ----------------------------------------------
486 // State: emped
487 // ----------------------------------------------
488 function ‪quadtank_emped( params )
489 {
490  self endon ("death");
491  self endon( "change_state" );
492  self endon( "emped" );
493 
494  if( isdefined( self.‪emped ) )
495  {
496  // already emped, just return for now.
497  return;
498  }
499 
500  self.emped = true;
501  PlaySoundAtPosition( "veh_quadtankemp_down", self.origin );
502  self.turretRotScale = 0.2;
503  if( !isdefined( self.stun_fx) )
504  {
505  self.stun_fx = ‪Spawn( "script_model", self.origin );
506  self.stun_fx SetModel( "tag_origin" );
507  self.stun_fx LinkTo( self, "tag_turret", (0,0,0), (0,0,0) );
508  //PlayFXOnTag( level._effect[ "quadtank_stun" ], self.stun_fx, "tag_origin" );
509  }
510 
511  time = params.notify_param[0];
512  assert( isdefined( time ) );
513  ‪vehicle_ai::Cooldown( "emped_timer", time );
514 
515  while( !‪vehicle_ai::IsCooldownReady( "emped_timer" ) )
516  {
517  timeLeft = max( ‪vehicle_ai::GetCooldownLeft( "emped_timer" ), 0.5 );
518  wait timeLeft;
519  }
520 
521  self.stun_fx delete();
522  self.emped = undefined;
523  self playsound ("veh_boot_quadtank");
524 
526 }
527 
528 // ----------------------------------------------
529 // trophy system
530 // ----------------------------------------------
532 {
533  if( self.trophy_down === true )
534  {
535  return true;
536  }
537 
538  if ( ‪trophy_destroyed() )
539  {
540  return true;
541  }
542 
543  return false;
544 }
545 
547 {
548  if ( self.trophy_disables >= ‪TROPHY_DISABLE_LIMIT )
549  {
550  return true;
551  }
552  return false;
553 }
554 
556 {
557  self endon( "death" );
558  self notify( "stop_disabletrophy" );
559  self endon( "stop_disabletrophy" );
560  self notify( "stop_enabletrophy" );
561 
562  //set_trophy_state( false );
563 
564  if( ‪trophy_disabled() )
565  return;
566 
567  self.trophy_down = true;
568 
569  driver = self GetSeatOccupant( 0 );
570  curr_state = self ‪vehicle_ai::get_current_state();
571  next_state = self ‪vehicle_ai::get_next_state();
572  if( !isdefined( driver ) && isdefined( curr_state ) && ( curr_state != "off" ) && isdefined( next_state ) && ( next_state != "off" ) )
573  {
574  self notify( "pain", true ); // Play a trophy system down animation using the pain state
575  }
576 
577  //Target_Set( self, ( 0, 0, 60 ) );7
578  self.targetOffset = ( 0, 0, 60 );
579 
580  self HidePart( "tag_defense_active" );
581  //self HidePart( "tag_target_upper" );
582 
583  self.attackerAccuracy = 0.5;
584  self.damage_during_trophy_down = 0;
585  self.spike_hits_during_trophy_down = 0;
586  self.trophy_disables += 1;
587 
588  self ‪quadtank_weakpoint_display( false );
589  self ‪remove_repulsor();
590 
591  driver = self GetSeatOccupant( 0 );
592 
593  self ‪set_side_turrets_enabled( false );
594 
595  if( IsDefined( level.vehicle_defense_cb ) )
596  {
597  [[level.vehicle_defense_cb]]( self, false );
598  }
599 
600  if( ‪trophy_destroyed() )
601  {
602  self notify("trophy_system_destroyed");
603  level notify("trophy_system_destroyed",self);
604  self playsound ("wpn_trophy_disable");
605  PlayFXOnTag( self.settings.trophydetonationfx, self, "tag_target_lower" );
606  self HidePart( "tag_lidar_null", "", true );
607  return;
608  }
609 
610  self notify("trophy_system_disabled");
611  level notify("trophy_system_disabled",self);
612  self playsound ("wpn_trophy_disable");
613 
614  self ‪vehicle_ai::Cooldown( "trophy_down", self.settings.trophySystemDownTime );
615  while( !self ‪vehicle_ai::IsCooldownReady("trophy_down") || self ‪vehicle_ai::get_current_state() === "off" )
616  {
617  if ( ‪vehicle_ai::GetCooldownLeft( "trophy_down" ) < 0.5 * self.settings.trophySystemDownTime && ( self.damage_during_trophy_down >= self.settings.trophysystemdisablethreshold ||
618  self.spike_hits_during_trophy_down >= ‪SPIKE_HIT_LIMIT ) )
619  {
620  self ‪vehicle_ai::ClearCooldown( "trophy_down" );
621  }
622 
623  wait 1;
624  }
625 
626  // player's trophy don't get back up
627  driver = self GetSeatOccupant( 0 );
628  if( isdefined( driver ) )
629  {
630  self.trophy_disables = ‪TROPHY_DISABLE_LIMIT;
631  }
632 
633  if( !‪trophy_destroyed() )
634  {
635  self thread ‪quadtank_enabletrophy();
636  }
637 }
638 
640 {
641  self endon( "death" );
642  self notify( "stop_enabletrophy" );
643  self endon( "stop_enabletrophy" );
644 
645  //set_trophy_state( true );
646  time = ‪VAL( self.settings.trophywarmup, 0.1 );
647  wait time;
648 
649  driver = self GetSeatOccupant( 0 );
650 
651  self.trophy_down = false;
652  self.attackerAccuracy = 1;
653  self ShowPart( "tag_defense_active" );
654  //self ShowPart( "tag_target_upper" );
655 
657  self thread ‪quadtank_automelee_update();
658 
659  if( !isdefined( driver ) )
660  {
661  self ‪quadtank_weakpoint_display( true );
662  }
663  else
664  {
665  self ‪quadtank_weakpoint_display( false );
666  }
667 
668  if ( Target_IsTarget( self ) )
669  {
670  //Target_Remove( self );
671  }
672 
673  if( !isdefined( driver ) )
674  {
675  self ‪set_side_turrets_enabled( true );
676  }
677  self.trophy_system_health = self.settings.trophySystemHealth;
678 
679  if( isDefined( level.players) && level.players.size > 0 )
680  {
681  num_players_trophy_health_modifier = 0.75;
682 
683  if( level.players.size == 2)
684  {
685  num_players_trophy_health_modifier = 1;
686  }
687  if( level.players.size == 3)
688  {
689  num_players_trophy_health_modifier = 1.25;
690  }
691  if( level.players.size >= 4)
692  {
693  num_players_trophy_health_modifier = 1.5;
694  }
695  self.trophy_system_health = self.trophy_system_health * num_players_trophy_health_modifier;
696  }
697 
698  if( IsDefined( level.vehicle_defense_cb ) )
699  {
700  [[level.vehicle_defense_cb]]( self, true );
701  }
702 
703  self notify("trophy_system_enabled");
704  level notify("trophy_system_enabled",self);
705 }
706 
707 // trophy system --------------------------------
708 
710 {
711  self SetTurretTargetRelativeAngles( (10, -90, 0), 1 );
712  self SetTurretTargetRelativeAngles( (10, 90, 0), 2 );
713  self.turretRotScale = 1 * self.difficulty_scale_up;
714 }
715 
716 // rotates the turret around until he can see his enemy
717 function ‪quadtank_turret_scan( scan_forever )
718 {
719  self endon( "death" );
720  self endon( "change_state" );
721 
722  self.turretRotScale = 0.3;
723 
724  while( scan_forever || ( !isdefined( self.enemy ) || !(self VehCanSee( self.enemy )) ) )
725  {
726  if( self.turretontarget && self.turret_state != ‪TURRET_STATE_SCAN_AT_ENEMY )
727  {
728  self.turret_state++;
729  if( self.turret_state >= ‪NUM_TURRET_STATES )
730  self.turret_state = ‪TURRET_STATE_SCAN_FORWARD;
731  }
732 
733  switch( self.turret_state )
734  {
735  // reserved for taking damage and looking responsive
737  if( isdefined( self.enemy ) )
738  {
739  self SetLookAtEnt( self.enemy );
740  target_vec = self.enemy.origin + ( 0, 0, ‪SCAN_HEIGHT_OFFSET );
741  self SetTargetOrigin( target_vec );
742  wait 1.0;
743  self ClearLookAtEnt();
744  self.turret_state++;
745  } // else fall through to FORWARD
746 
748  target_vec = self.origin + AnglesToForward( ( 0, self.angles[1], 0 ) ) * 1000;
749  break;
750 
752  target_vec = self.origin + AnglesToForward( ( 0, self.angles[1] + 30, 0 ) ) * 1000;
753  break;
754 
756  target_vec = self.origin + AnglesToForward( ( 0, self.angles[1], 0 ) ) * 1000;
757  break;
758 
760  target_vec = self.origin + AnglesToForward( ( 0, self.angles[1] - 30, 0 ) ) * 1000;
761  break;
762  }
763 
764  target_vec = target_vec + ( 0, 0, ‪SCAN_HEIGHT_OFFSET );
765  self SetTargetOrigin( target_vec );
766 
767  wait 0.2;
768  }
769 }
770 
772 {
773  if( on )
774  {
775  ‪turret::enable( 1, false );
776  ‪turret::enable( 2, false );
777  }
778  else
779  {
780  ‪turret::disable( 1 );
781  ‪turret::disable( 2 );
782  }
783 }
784 
785 function ‪show_weak_spots( show ) // vents on the sides that are exposed when firing the main gun
786 {
787  if( show )
788  {
790  }
791  else
792  {
794  }
795 }
796 
797 function ‪set_detonation_time( target )
798 {
799  self endon( "change_state" );
800 
801  self playsound("veh_quadtank_cannon_charge");
802 
803  self waittill( "weapon_fired", proj );
804 
805  self thread ‪railgun_sound(proj);
806 
807  if( isdefined( target ) && isdefined( proj ) )
808  {
809  vel = proj GetVelocity();
810 
811  proj_speed = length( vel );
812 
813  dist = Distance( proj.origin, target.origin ) + RandomFloatRange( 0, 40 );
814 
815  time_to_enemy = dist / proj_speed;
816 
817  proj ResetMissileDetonationTime( time_to_enemy );
818  }
819 }
820 
822 {
823  self endon( "death" );
824  self endon( "change_state" );
825 
826  cant_see_enemy_count = 0;
827 
828  self ‪set_side_turrets_enabled( true );
829  self SetOnTargetAngle( 10 ); // self.turretontarget will be true when the turret is aimed within this rage
830 
831  self.getreadytofire = undefined;
832 
833  while ( 1 )
834  {
835  {
836  if ( isdefined( self.enemy ) && self VehCanSee( self.enemy ) )
837  {
838  self SetTurretTargetEnt( self.enemy );
839  self SetLookAtEnt( self.enemy );
840  }
841 
842  wait 0.2;
843  continue;
844  }
845 
846  if ( isdefined( self.enemy ) && self VehCanSee( self.enemy ) )
847  {
848  self.turretRotScale = 1 * self.difficulty_scale_up;
849  self SetTurretTargetEnt( self.enemy );
850  self SetLookAtEnt( self.enemy );
851 
852  if( cant_see_enemy_count >= 2 )
853  {
854  wait 0.1; // let the self.turretontarget have time to update so we don't shoot in a bad direction
855 
856  // found enemy, react by changing goal positions
857  self CancelAIMove();
858  self ClearVehGoalPos();
859  self notify( "near_goal" );
860  }
861  cant_see_enemy_count = 0;
862  fired = false;
863 
864  if ( isdefined( self.enemy ) && self VehCanSee( self.enemy ) )
865  {
866  if( DistanceSquared( self.origin, self.enemy.origin ) > ‪MELEE_RADIUS * ‪MELEE_RADIUS && self.turretontarget )
867  {
868  v_my_forward = Anglestoforward( self.angles );
869  v_to_enemy = self.enemy.origin - self.origin;
870  v_to_enemy = VectorNormalize( v_to_enemy );
871  dot = VectorDot( v_to_enemy, v_my_forward );
872 
873  if( dot > 0.707 ) // body is facing within 45' of enemy
874  {
875  self ASMRequestSubstate( "fire@stationary" );
876  self SetTurretTargetEnt( self.enemy );
877  self thread ‪set_detonation_time( self.enemy );
878 
879  if( isDefined( level.players) && level.players.size < 3)
880  {
881  self ‪set_side_turrets_enabled( false );
882  }
883 
884  self ‪show_weak_spots( true );
885  self.getreadytofire = true;
886  fired = true;
887 
888  self CancelAIMove();
889  self ClearVehGoalPos();
890  self notify( "near_goal" );
891 
892  self.turretRotScale = 0.7;
893 
894  wait 1;
895 
896  level notify( "sndStopCountdown" );
897 
898  self ‪vehicle_ai::waittill_asm_complete( "fire@stationary", 6 );
899 
900  self ‪set_side_turrets_enabled( true );
901 
902  self.turretRotScale = 1;
903  }
904  }
905  }
906 
907  self.getreadytofire = undefined;
908 
909  if ( isdefined( self.enemy ) )
910  {
911  self SetTurretTargetEnt( self.enemy );
912  self SetLookAtEnt( self.enemy );
913  }
914 
915  if( fired )
916  {
917  self ‪show_weak_spots( false );
918 
919  ‪vehicle_ai::Cooldown( "main_cannon", RandomFloatRange( 5, 7.5 ) );
920  }
921  else
922  {
923  wait 0.25;
924  }
925  }
926  else
927  {
928  cant_see_enemy_count++;
929 
930  wait 0.5;
931 
932  if( cant_see_enemy_count > 40 )
933  {
934  self ‪quadtank_turret_scan( false );
935  }
936  else if( cant_see_enemy_count > 30 )
937  {
938  self ClearLookAtEnt();
939  self ClearTargetEntity();
940  }
941  else
942  {
943  if( isdefined( self.enemy ) )
944  {
945  self SetTurretTargetEnt( self.enemy );
946  self ClearLookAtEnt();
947  }
948  else
949  {
950  self ClearLookAtEnt();
951  self ‪quadtank_turret_scan( false );
952  }
953  }
954  }
955  }
956 }
957 
958 
960 {
961  self endon( "death" );
962  self endon( "end_attack_thread" );
963 
964  self ‪vehicle::toggle_ambient_anim_group( 2, false ); // close the weapon doors
965 
966  while( true )
967  {
968  useJavelin = false;
969 
970  if ( isdefined( self.enemy ) )
971  {
972  self SetTurretTargetEnt( self.enemy );
973  self SetLookAtEnt( self.enemy );
974  }
975 
976  if( isdefined( self.enemy) && ‪vehicle_ai::IsCooldownReady( "javelin_rocket_launcher", 0.5 ) )
977  {
978  if( isVehicle( self.enemy ) || Distance2DSquared( self.origin, self.enemy.origin) >= ‪SQR( ‪JAVELIN_MIN_USE_DISTANCE ) )
979  {
980  useJavelin = !self vehseenrecently( self.enemy, 3 ) || ( RandomInt( 100 ) < 3 );
981  }
982  }
983 
984  if ( isdefined( self.enemy ) && ‪vehicle_ai::IsCooldownReady( "rocket_launcher", 0.5 ) )
985  {
986  if( isDefined( level.players) && level.players.size < 3)
987  {
988  self ‪set_side_turrets_enabled( false );
989  }
990  self ClearVehGoalPos();
991  self notify( "near_goal" );
992  self ‪show_weak_spots( true );
994 
995  if( !useJavelin )
996  {
997  self SetVehWeapon( GetWeapon( ‪WEAPON_STRAIGHT ) );
998  offset = ( 0, 0, -50 );
999  if ( isPlayer( self.enemy ) )
1000  {
1001  origin = self.enemy.origin;
1002  eye = self.enemy GetEye();
1003  offset = ( 0, 0, origin[2] - eye[2] - 5 );
1004  }
1005  ‪vehicle_ai::SetTurretTarget( self.enemy, 0, offset );
1006  }
1007  else
1008  {
1009  self playsound ("veh_quadtank_mlrs_plant_start");
1010 
1011  self SetVehWeapon( GetWeapon( ‪WEAPON_JAVELIN ) );
1012 
1013  ‪vehicle_ai::SetTurretTarget( self.enemy, 0, (0,0,300) );
1014  }
1015 
1016  wait 1;
1017  msg = self ‪util::waittill_any_timeout( 2, "turret_on_target", "end_attack_thread" );
1018 
1019  if ( isdefined( self.enemy ) && Distance2DSquared( self.origin, self.enemy.origin ) > ‪SQR( ‪ROCKET_LAUNCHER_MIN_DIST ) )
1020  {
1021  fired = false;
1022  for( i = 0; i < 4 && isdefined( self.enemy ); i++ )
1023  {
1024  if( useJavelin )
1025  {
1026  if ( isPlayer( self.enemy ) )
1027  {
1028  self thread ‪vehicle_ai::Javelin_LoseTargetAtRightTime( self.enemy );
1029  }
1030  self thread ‪javeline_incoming(GetWeapon( ‪WEAPON_JAVELIN ));
1031  }
1032  self FireWeapon( 0, self.enemy );
1033 
1034  fired = true;
1035  wait 0.8;
1036  }
1037 
1038  if ( fired )
1039  {
1040  ‪vehicle_ai::Cooldown( "rocket_launcher", randomFloatRange( 8, 10 ) );
1041 
1042  if( useJavelin )
1043  {
1044  ‪vehicle_ai::Cooldown( "javelin_rocket_launcher", 20 );
1045  }
1046  }
1047  }
1048 
1049  self ‪set_side_turrets_enabled( true );
1050  self ‪vehicle::toggle_ambient_anim_group( 2, false );
1051  }
1052  wait 1;
1053  }
1054 }
1055 
1056 // self == player
1058 {
1059  if ( !isdefined( self._player_shock_fx_quadtank_melee ) )
1060  {
1061  self._player_shock_fx_quadtank_melee = 0;
1062  }
1063 
1064  self._player_shock_fx_quadtank_melee = !self._player_shock_fx_quadtank_melee;
1065  self ‪clientfield::set_to_player( "player_shock_fx", self._player_shock_fx_quadtank_melee );
1066 }
1067 
1069 {
1070  self endon( "death" );
1071  self endon( "change_state" );
1072  self endon( "near_goal" );
1073  self endon( "reached_end_node" );
1074 
1075  wait 1;
1076 
1077  cantSeeEnemyCount = 0;
1078 
1079  while( 1 )
1080  {
1081  if( isdefined( self.current_pathto_pos ) )
1082  {
1083  if( isdefined( self.enemy ) )
1084  {
1085  if( distance2dSquared( self.enemy.origin, self.current_pathto_pos ) < 250 * 250 )
1086  {
1087  self.move_now = true;
1088  self notify( "near_goal" );
1089  }
1090 
1091  if( !self VehCanSee( self.enemy ) )
1092  {
1093  if( !self ‪vehicle_ai::CanSeeEnemyFromPosition( self.current_pathto_pos, self.enemy, 80 ) )
1094  {
1095  cantSeeEnemyCount++;
1096  if( cantSeeEnemyCount > 5 )
1097  {
1098  self.move_now = true;
1099  self notify( "near_goal" );
1100  }
1101  }
1102  }
1103  }
1104 
1105  if( distance2dSquared( self.current_pathto_pos, self.goalpos ) > self.goalradius * self.goalradius )
1106  {
1107  wait 1;
1108 
1109  self.move_now = true;
1110  self notify( "near_goal" );
1111  }
1112  }
1113 
1114  wait 0.3;
1115  }
1116 }
1117 
1119 {
1120  self endon( "death" );
1121  self endon( "change_state" );
1122  self notify( "end_movement_thread" );
1123  self endon( "end_movement_thread" );
1124 
1125  if( self.goalforced )
1126  {
1127  return self.goalpos;
1128  }
1129 
1130  minSearchRadius = 0;
1131  maxSearchRadius = 2000;
1132  halfHeight = 300;
1133  innerSpacing = 90;
1134  outerSpacing = innerSpacing * 2;
1135  maxGoalTimeout = 15;
1136 
1137  self ASMRequestSubstate( "locomotion@movement" );
1138 
1139  wait 0.5;
1140  self SetBrake( 0 );
1141 
1142  while ( true )
1143  {
1144  self SetSpeed( self.settings.defaultMoveSpeed, 5, 5 );
1145 
1146  PixBeginEvent( "_quadtank::Movement_Thread_Wander" );
1147  queryResult = PositionQuery_Source_Navigation( self.origin, minSearchRadius, maxSearchRadius, halfHeight, innerSpacing, self, outerSpacing );
1148  PixEndEvent();
1149 
1150  // filter
1151  PositionQuery_Filter_DistanceToGoal( queryResult, self );
1153  ‪vehicle_ai::PositionQuery_Filter_Random( queryResult, 200, 250 );
1154 
1155  foreach ( point in queryResult.data )
1156  {
1157  if( distance2dSquared( self.origin, point.origin ) < 170 * 170 )
1158  {
1159  ADD_POINT_SCORE( point, "tooCloseToSelf", -100 );
1160  }
1161  }
1162  self ‪vehicle_ai::PositionQuery_DebugScores( queryResult );
1163 
1165 
1166  foundpath = false;
1167  goalPos = self.origin;
1168  count = queryResult.data.size;
1169  if( count > 3 )
1170  count = 3;
1171 
1172  for ( i = 0; i < count && !foundpath; i++ )
1173  {
1174  goalPos = queryResult.data[i].origin;
1175  foundpath = self SetVehGoalPos( goalPos, false, true );
1176  }
1177 
1178  if( foundpath )
1179  {
1180 
1181  self.current_pathto_pos = goalpos;
1182  self thread ‪path_update_interrupt();
1183  self ASMRequestSubstate( "locomotion@movement" );
1184 
1185  msg = self ‪util::waittill_any_timeout( maxGoalTimeout, "near_goal", "force_goal", "reached_end_node", "goal" );
1186  self CancelAIMove();
1187  self ClearVehGoalPos();
1188 
1189  if( isdefined( self.move_now ) )
1190  {
1191  self.move_now = undefined;
1192 
1193  wait 0.1;
1194  }
1195  else
1196  {
1197  wait 0.5;
1198  }
1199  }
1200  else
1201  {
1202  self.current_pathto_pos = undefined;
1203 
1204  goalYaw = self GetGoalYaw();
1205 
1206  wait 1;
1207  }
1208  }
1209 }
1210 
1212 {
1213  self endon( "death" );
1214  self endon( "change_state" );
1215 
1216  //if( distance2dSquared( self.origin, self.goalpos ) > 20 * 20 )
1217  // self SetVehGoalPos( self.goalpos, true, 2 );
1218 
1219  self ASMRequestSubstate( "locomotion@movement" );
1220  wait 0.5;
1221 
1222  self SetBrake( 0 );
1223 
1224  while ( self.allow_movement )
1225  {
1226  if ( self.getreadytofire !== true )
1227  {
1228  goalpos = ‪vehicle_ai::FindNewPosition( 80 );
1229 
1230  if ( isdefined( goalpos ) && ( Distance2DSquared( goalpos, self.origin ) > ‪SQR( ‪NEAR_GOAL_DIST ) || Abs( goalpos[2] - self.origin[2] ) > self.height ) )
1231  {
1232  self SetSpeed( self.settings.defaultMoveSpeed, 5, 5 );
1233  self SetVehGoalPos( goalpos, false, true );
1234  self.current_pathto_pos = goalpos;
1235  self thread ‪path_update_interrupt();
1236  self ASMRequestSubstate( "locomotion@movement" );
1237  ‪result = self ‪util::waittill_any_return( "near_goal", "reached_end_node", "force_goal" );
1238  }
1239  else
1240  {
1241  self notify( "goal" );
1242  }
1243 
1244  self CancelAIMove();
1245  self ClearVehGoalPos();
1246 
1247  if ( isdefined( self.move_now ) )
1248  {
1249  self.move_now = undefined;
1250 
1251  wait 0.1;
1252  }
1253  else
1254  {
1255  wait 0.5;
1256  }
1257  }
1258  else
1259  {
1260  while ( isdefined( self.getreadytofire ) )
1261  {
1262  wait 0.2;
1263  }
1264  }
1265 
1266  }
1267 }
1268 
1270 {
1271  self endon( "death" );
1272  self endon( "exit_vehicle" );
1273 
1274  weapon = self SeatGetWeapon( 1 );
1275  fireTime = weapon.fireTime;
1276 
1277  while( 1 )
1278  {
1279  self SetGunnerTargetVec( self GetGunnerTargetVec( 0 ), 1 );
1280  if( self IsGunnerFiring( 0 ) )
1281  {
1282  self FireWeapon( 2 );
1283  }
1284  wait fireTime;
1285  }
1286 }
1287 
1288 function ‪do_melee( shouldDoDamage, enemy )
1289 {
1290  if( !isAlive( enemy ) || distanceSquared( enemy.origin, self.origin ) > ‪SQR( ‪MELEE_RADIUS ) )
1291  {
1292  return false;
1293  }
1294 
1295  if ( ‪vehicle_ai::EntityIsArchetype( enemy, "quadtank" ) || ‪vehicle_ai::EntityIsArchetype( enemy, "raps" ) )
1296  {
1297  return false;
1298  }
1299 
1300  if ( isPlayer( enemy ) && enemy ‪laststand::player_is_in_laststand() )
1301  {
1302  return false;
1303  }
1304 
1305  self notify ( "play_meleefx" );
1306 
1307  if ( shouldDoDamage )
1308  {
1309  // don't damage player, but crush player vehicle
1310  players = GetPlayers();
1311  foreach( player in players )
1312  {
1313  player._takedamage_old = player.takedamage;
1314  player.takedamage = false;
1315  }
1316 
1317  RadiusDamage( self.origin + (0,0,40), ‪MELEE_RADIUS, ‪MELEE_INNER_RADIUS_DAMAGE, ‪MELEE_OUTER_RADIUS_DAMAGE, self );
1318 
1319  foreach( player in players )
1320  {
1321  player.takedamage = player._takedamage_old;
1322  player._takedamage_old = undefined;
1323  }
1324  }
1325 
1326  if ( isdefined( enemy ) && isPlayer( enemy ) )
1327  {
1328  direction = ‪FLAT_ORIGIN( ( enemy.origin - self.origin ) );
1329  if ( Abs( direction[0] ) < 0.01 && Abs( direction[1] ) < 0.01 )
1330  {
1331  direction = ( RandomFloatRange( 1, 2 ), RandomFloatRange( 1, 2 ), 0 );
1332  }
1333  direction = VectorNormalize( direction );
1334  strength = 1000;
1335  enemy SetVelocity( enemy GetVelocity() + direction * strength );
1337  enemy DoDamage( 15, self.origin, self );
1338  }
1339 
1340  self playsound( "veh_quadtank_emp" );
1341 
1342  return true;
1343 }
1344 
1346 {
1347  self endon( "death" );
1348 
1349  assert( isdefined( self.team ) );
1350 
1351  while( !‪trophy_disabled() )
1352  {
1353  enemies = self GetEnemies();
1354 
1355  meleed = false;
1356 
1357  foreach( enemy in enemies )
1358  {
1359  if( enemy IsNoTarget() )
1360  {
1361  continue;
1362  }
1363 
1364  meleed = meleed || self ‪do_melee( !meleed, enemy );
1365  if ( meleed )
1366  {
1367  break;
1368  }
1369  }
1370 
1371  wait 0.3;
1372  }
1373 }
1374 
1375 
1376 function ‪quadtank_destroyturret( index )
1377 {
1378  ‪turret::disable( index );
1379 
1380  if( index == 1 )
1381  {
1382  self HidePart( "tag_gunner_barrel1" );
1383  self HidePart( "tag_gunner_turret1" );
1384  }
1385  else if( index == 2 )
1386  {
1387  self HidePart( "tag_gunner_barrel2" );
1388  self HidePart( "tag_gunner_turret2" );
1389  }
1390 }
1391 
1392 
1394 {
1395  self endon( "death" );
1396 
1397  while( 1 )
1398  {
1399  self waittill( "enter_vehicle", player );
1400 
1401  if ( isdefined( player ) && isPlayer( player ) )
1402  {
1403  player ‪vehicle::update_damage_as_occupant( self.maxhealth - self.health, self.maxhealth );
1404  }
1405  }
1406 }
1407 
1408 function ‪QuadtankCallback_VehicleDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal )
1409 {
1410  if ( isdefined( eAttacker ) && ( eAttacker == self || isplayer( eAttacker ) && eAttacker.usingvehicle && eAttacker.viewlockedentity === self ) )
1411  {
1412  return 0;
1413  }
1414 
1415  if ( sMeansOfDeath === "MOD_MELEE" || sMeansOfDeath === "MOD_MELEE_WEAPON_BUTT" || sMeansOfDeath === "MOD_MELEE_ASSASSINATE" || sMeansOfDeath === "MOD_ELECTROCUTED" || sMeansOfDeath === "MOD_CRUSH" || weapon.isEmp )
1416  {
1417  return 0;
1418  }
1419 
1420  iDamage = self ‪killstreaks::OnDamagePerWeapon( ‪QUADTANK_BUNDLE, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, self.maxhealth, undefined, self.maxhealth * 0.4, undefined, 0, undefined, true, 1.0 );
1421 
1422 
1423  driver = self GetSeatOccupant( 0 );
1424  if ( isPlayer( driver ) )
1425  {
1426  driver ‪vehicle::update_damage_as_occupant( self.maxhealth - ( self.health - iDamage ), self.maxhealth );
1427  }
1428 
1429  return iDamage;
1430 }
1431 
1432 function ‪quadtank_set_team( team )
1433 {
1434  self.team = team;
1435 
1436  if( !self ‪vehicle_ai::is_instate( "off" ) )
1437  {
1439  }
1440 }
1441 
1443 {
1444  if( isdefined( self.missile_repulsor ) )
1445  {
1446  missile_deleteattractor( self.missile_repulsor );
1447  self.missile_repulsor = undefined;
1448  }
1449  self notify( "end_repulsor_fx" );
1450 }
1451 
1452 function ‪repulsor_fx()
1453 {
1454  self notify( "end_repulsor_fx" );
1455  self endon( "end_repulsor_fx" );
1456  self endon( "death" );
1457  self endon( "change_state" );
1458 
1459  while( 1 )
1460  {
1461  self ‪util::waittill_any( "projectile_applyattractor", "play_meleefx" );
1462  if ( ‪vehicle_ai::IsCooldownReady("repulsorfx_interval") )
1463  {
1464  PlayFxOnTag( self.settings.trophyrepulsefx, self, "tag_body" );
1465 
1466  self ‪vehicle::impact_fx( self.settings.trophyrepulsefx_ground );
1467 
1468  ‪vehicle_ai::Cooldown( "repulsorfx_interval", 0.5 );
1469 
1470  self PlaySound( "wpn_quadtank_shield_impact" );
1471  }
1472  }
1473 }
1474 
1476 {
1477  if( !isdefined( self.missile_repulsor ) )
1478  {
1479  self.missile_repulsor = missile_createrepulsorent( self, 40000, self.settings.trophysystemrange, true );
1480  }
1481  self thread ‪repulsor_fx();
1482 }
1483 
1484 function ‪turn_off_laser_after( time )
1485 {
1486  self notify( "turn_off_laser_thread" );
1487  self endon( "turn_off_laser_thread" );
1488  self endon( "death" );
1489 
1490  wait time;
1491 
1492  self LaserOff();
1493 }
1494 
1495 
1496 
1497 //self = turret/vehicle
1498 function ‪side_turret_is_target_in_view_score( v_target, n_index )
1499 {
1500  s_turret = ‪turret::_get_turret_data( n_index );
1501 
1502  v_pivot_pos = self GetTagOrigin( s_turret.str_tag_pivot );
1503  v_angles_to_target = VectorToAngles( v_target - v_pivot_pos );
1504 
1505  n_rest_angle_pitch = s_turret.n_rest_angle_pitch + self.angles[0];
1506  n_rest_angle_yaw = s_turret.n_rest_angle_yaw + self.angles[1];
1507 
1508  n_ang_pitch = AngleClamp180( v_angles_to_target[0] - n_rest_angle_pitch );
1509  n_ang_yaw = AngleClamp180( v_angles_to_target[1] - n_rest_angle_yaw );
1510 
1511  b_out_of_range = false;
1512 
1513  if ( n_ang_pitch > 0 )
1514  {
1515  if ( n_ang_pitch > s_turret.bottomarc )
1516  {
1517  b_out_of_range = true;
1518  }
1519  }
1520  else
1521  {
1522  if ( Abs( n_ang_pitch ) > s_turret.toparc )
1523  {
1524  b_out_of_range = true;
1525  }
1526  }
1527 
1528  if ( n_ang_yaw > 0 )
1529  {
1530  if ( n_ang_yaw > s_turret.leftarc )
1531  {
1532  b_out_of_range = true;
1533  }
1534  }
1535  else
1536  {
1537  if ( Abs( n_ang_yaw ) > s_turret.rightarc )
1538  {
1539  b_out_of_range = true;
1540  }
1541  }
1542 
1543  if( b_out_of_range )
1544  {
1545  return 0.0;
1546  }
1547 
1548  return ( Abs( n_ang_yaw ) / 90 * 800 );
1549 }
1550 
1551 function ‪_get_best_target_quadtank_side_turret( a_potential_targets, n_index )
1552 {
1553  takeEasyOnOneTarget = MapFloat( 0, 4, 800, 0, level.gameskill );
1554 
1555  if ( n_index === 1 )
1556  {
1557  other_turret_target = ‪turret::get_target( 2 );
1558  }
1559  else if ( n_index === 2 )
1560  {
1561  other_turret_target = ‪turret::get_target( 1 );
1562  }
1563 
1564  e_best_target = undefined;
1565  f_best_score = 100000; // lower is better
1566 
1567  s_turret = ‪turret::_get_turret_data( n_index );
1568 
1569  foreach( ‪e_target in a_potential_targets )
1570  {
1571  f_score = Distance( self.origin, ‪e_target.origin );
1572 
1573  b_current_target = ‪turret::is_target( ‪e_target, n_index );
1574 
1575  if( b_current_target )
1576  {
1577  f_score -= 100;
1578  }
1579 
1580  if( ‪e_target === self.enemy )
1581  {
1582  f_score += 300;
1583  }
1584 
1585  if ( ‪e_target === other_turret_target )
1586  {
1587  f_score += ( 100 + takeEasyOnOneTarget );
1588  }
1589 
1590  if( IsSentient( ‪e_target ) && ‪e_target AttackedRecently( self, 2 ) )
1591  {
1592  f_score -= 200;
1593  }
1594 
1595  if ( isAlive( self.lockon_owner ) && ‪e_target === self.lockon_owner )
1596  {
1597  f_score -= 1000;
1598  }
1599 
1600  v_offset = ‪turret::_get_default_target_offset( ‪e_target, n_index );
1601 
1602  view_score = ‪side_turret_is_target_in_view_score( ‪e_target.origin + v_offset, n_index );
1603 
1604  if( view_score != 0.0 )
1605  {
1606  f_score += view_score;
1607 
1608  b_trace_passed = ‪turret::trace_test( ‪e_target, v_offset, n_index );
1609 
1610  if ( b_current_target && !b_trace_passed && !isdefined( s_turret.n_time_lose_sight ) )
1611  {
1612  s_turret.n_time_lose_sight = GetTime();
1613  }
1614 
1615  if( b_trace_passed )
1616  {
1617  f_score -= 500;
1618  }
1619  }
1620  else if ( b_current_target )
1621  {
1622  s_turret.b_target_out_of_range = true;
1623  f_score += 5000;
1624  }
1625 
1626  if( f_score < f_best_score )
1627  {
1628  f_best_score = f_score;
1629  e_best_target = ‪e_target;
1630  }
1631  }
1632 
1633  return e_best_target;
1634 }
1635 
1637 {
1638  if( self.displayweakpoint !== state )
1639  {
1640  self.displayweakpoint = state;
1641 
1642  if( !self.displayweakpoint && self.weakpointobjective === 1 )
1643  {
1644  self.weakpointobjective = 0;
1645  }
1646 
1647  player = level.players[0];
1648  if( self.displayweakpoint && self.combatactive && self.weakpointobjective !== 1 && ( !isdefined( player ) || player.team !== self.team ) )
1649  {
1650  self.weakpointobjective = 1;
1651  }
1652  }
1653 }
1654 
1656 {
1657  self endon( "death" );
1658  self endon( "exit_vehicle" );
1659 
1660  while( 1 )
1661  {
1662  note = self ‪util::waittill_any_return( "footstep_front_left", "footstep_front_right", "footstep_rear_left", "footstep_rear_right" );
1663 
1664  switch( note )
1665  {
1666  case "footstep_front_left":
1667  {
1668  bone = "tag_foot_fx_left_front";
1669  break;
1670  }
1671  case "footstep_front_right":
1672  {
1673  bone = "tag_foot_fx_right_front";
1674  break;
1675  }
1676  case "footstep_rear_left":
1677  {
1678  bone = "tag_foot_fx_left_back";
1679  break;
1680  }
1681  case "footstep_rear_right":
1682  {
1683  bone = "tag_foot_fx_right_back";
1684  break;
1685  }
1686  }
1687 
1688  position = self GetTagOrigin( bone ) + (0,0,15);
1689 
1690  self RadiusDamage( position, 60, 50, 50, self, "MOD_CRUSH" );
1691  }
1692 }
1693 
1694 function ‪javeline_incoming(projectile)
1695 {
1696  self endon( "entityshutdown" );
1697  self endon ("death");
1698 
1699  self waittill( "weapon_fired", projectile );
1700 
1701  distance = 1400;
1702  alias = "prj_quadtank_javelin_incoming";
1703 
1704  wait(5);
1705 
1706  if(!isdefined( projectile ) )
1707  return;
1708 
1709  while(isdefined(projectile) && isdefined( projectile.origin ))
1710  {
1711  if ( isdefined( self.enemy ) && isdefined( self.enemy.origin ))
1712  {
1713  projectileDistance = DistanceSquared( projectile.origin, self.enemy.origin);
1714 
1715  if( projectileDistance <= distance * distance )
1716  {
1717  projectile playsound (alias);
1718  return;
1719  }
1720  }
1721 
1722  wait (.2);
1723  }
1724 }
1725 
1726 function ‪railgun_sound(projectile)
1727 {
1728  self endon( "entityshutdown" );
1729  self endon ("death");
1730 
1731  self waittill( "weapon_fired", projectile );
1732 
1733  distance = 900;
1734  alais = "wpn_quadtank_railgun_fire_rocket_flux";
1735  players = level.players;
1736 
1737  while(isdefined(projectile) && isdefined( projectile.origin ))
1738  {
1739  if ( isdefined( players[0] ) && isdefined( players[0].origin ))
1740  {
1741  projectileDistance = DistanceSquared( projectile.origin, players[0].origin);
1742 
1743  if( projectileDistance <= distance * distance )
1744  {
1745  projectile playsound (alais);
1746  return;
1747  }
1748  }
1749 
1750  wait (.2);
1751  }
1752 
1753 }
1754 
1755 
‪footstep_handler
‪function footstep_handler()
Definition: _quadtank.gsc:1655
‪quadtank_update_difficulty
‪function quadtank_update_difficulty()
Definition: _quadtank.gsc:158
‪call_custom_add_state_callbacks
‪function call_custom_add_state_callbacks()
Definition: vehicle_ai_shared.gsc:1152
‪trophy_disabled
‪function trophy_disabled()
Definition: _quadtank.gsc:531
‪set_death_model
‪function set_death_model(sModel, fDelay)
Definition: vehicle_death_shared.gsc:258
‪state_driving_update
‪function state_driving_update(params)
Definition: _quadtank.gsc:353
‪enable
‪function enable(handler)
Definition: _perplayer.gsc:54
‪is_target
‪function is_target(e_target, n_index)
Definition: turret_shared.gsc:769
‪SCAN_HEIGHT_OFFSET
‪#define SCAN_HEIGHT_OFFSET
Definition: _quadtank.gsc:31
‪repulsor_fx
‪function repulsor_fx()
Definition: _quadtank.gsc:1452
‪set_state
‪function set_state(name, state_params)
Definition: statemachine_shared.gsc:133
‪evaluate_connections
‪function evaluate_connections(eval_func, params)
Definition: statemachine_shared.gsc:266
‪e_target
‪var e_target
Definition: traps_shared.gsc:2029
‪quadtank_disabletrophy
‪function quadtank_disabletrophy()
Definition: _quadtank.gsc:555
‪_get_default_target_offset
‪function _get_default_target_offset(e_target, n_index)
Definition: turret_shared.gsc:703
‪QUADTANK_BUNDLE
‪#define QUADTANK_BUNDLE
Definition: _quadtank.gsc:60
‪quadtank_weakpoint_display
‪function quadtank_weakpoint_display(state)
Definition: _quadtank.gsc:1636
‪state_combat_exit
‪function state_combat_exit(params)
Definition: _quadtank.gsc:426
‪do_melee
‪function do_melee(shouldDoDamage, enemy)
Definition: _quadtank.gsc:1288
‪state_off_enter
‪function state_off_enter(params)
Definition: _quadtank.gsc:227
‪bootup
‪function bootup()
Definition: _quadtank.gsc:265
‪set_damage_fx_level
‪function set_damage_fx_level(damage_level)
Definition: vehicle_shared.gsc:3012
‪toggle_tread_fx
‪function toggle_tread_fx(on)
Definition: vehicle_shared.gsc:3357
‪IsCooldownReady
‪function IsCooldownReady(name, timeForward_seconds)
Definition: vehicle_ai_shared.gsc:1968
‪InitThreatBias
‪function InitThreatBias()
Definition: vehicle_ai_shared.gsc:60
‪_get_best_target_quadtank_side_turret
‪function _get_best_target_quadtank_side_turret(a_potential_targets, n_index)
Definition: _quadtank.gsc:1551
‪get_current_state
‪function get_current_state()
Definition: vehicle_ai_shared.gsc:960
‪set_to_player
‪function set_to_player(str_field_name, n_value)
Definition: clientfield_shared.gsc:58
‪SetTurretTarget
‪function SetTurretTarget(target, turretIdx=0, offset=(0, 0, 0))
Definition: vehicle_ai_shared.gsc:277
‪register_killstreak_bundle
‪function register_killstreak_bundle(killstreakType)
Definition: _killstreak_bundles.gsc:20
‪TurnOffAllLightsAndLaser
‪function TurnOffAllLightsAndLaser()
Definition: vehicle_ai_shared.gsc:678
‪PositionQuery_Filter_Random
‪function PositionQuery_Filter_Random(queryResult, min, max)
Definition: vehicle_ai_shared.gsc:2088
‪monitor_enter_vehicle
‪function monitor_enter_vehicle()
Definition: _quadtank.gsc:1393
‪quadtank_on
‪function quadtank_on()
Definition: _quadtank.gsc:221
‪Javelin_LoseTargetAtRightTime
‪function Javelin_LoseTargetAtRightTime(target)
Definition: vehicle_ai_shared.gsc:314
‪quadtank_projectile_watcher
‪function quadtank_projectile_watcher()
Definition: _quadtank.gsc:1475
‪VERSION_SHIP
‪#define VERSION_SHIP
Definition: version.gsh:36
‪SPIKE_HIT_LIMIT
‪#define SPIKE_HIT_LIMIT
Definition: _quadtank.gsc:43
‪waittill_any_return
‪function waittill_any_return(string1, string2, string3, string4, string5, string6, string7)
Definition: util_shared.csc:212
‪Attack_Thread_rocket
‪function Attack_Thread_rocket()
Definition: _quadtank.gsc:959
‪toggle_exhaust_fx
‪function toggle_exhaust_fx(on)
Definition: vehicle_shared.gsc:3335
‪set_side_turrets_enabled
‪function set_side_turrets_enabled(on)
Definition: _quadtank.gsc:771
‪VAL
‪#define VAL(__var, __default)
Definition: shared.gsh:272
‪remove_repulsor
‪function remove_repulsor()
Definition: _quadtank.gsc:1442
‪NUM_TURRET_STATES
‪#define NUM_TURRET_STATES
Definition: _quadtank.gsc:38
‪OnDamagePerWeapon
‪function OnDamagePerWeapon(killstreak_ref, attacker, damage, flags, type, weapon, max_health, destroyed_callback, low_health, low_health_callback, emp_damage, emp_callback, allow_bullet_damage, chargeLevel)
Definition: _killstreaks.gsc:2667
‪quadtank_initialize
‪function quadtank_initialize()
Definition: _quadtank.gsc:76
‪MELEE_RADIUS
‪#define MELEE_RADIUS
Definition: _quadtank.gsc:45
‪trace_test
‪function trace_test(e_target, v_offset=(0, 0, 0), n_index)
Definition: turret_shared.gsc:2535
‪state_off_exit
‪function state_off_exit(params)
Definition: _quadtank.gsc:255
‪waittill_any_timeout
‪function waittill_any_timeout(n_timeout, string1, string2, string3, string4, string5)
Definition: util_shared.csc:423
‪quadtank_set_team
‪function quadtank_set_team(team)
Definition: _quadtank.gsc:1432
‪set_burst_parameters
‪function set_burst_parameters(n_fire_min, n_fire_max, n_wait_min, n_wait_max, n_index)
Definition: turret_shared.gsc:609
‪turn_off_laser_after
‪function turn_off_laser_after(time)
Definition: _quadtank.gsc:1484
‪SQR
‪#define SQR(__var)
Definition: shared.gsh:293
‪JAVELIN_MIN_USE_DISTANCE
‪#define JAVELIN_MIN_USE_DISTANCE
Definition: _quadtank.gsc:53
‪TURRET_STATE_SCAN_FORWARD
‪#define TURRET_STATE_SCAN_FORWARD
Definition: _quadtank.gsc:34
‪disable
‪function disable(handler)
Definition: _perplayer.gsc:79
‪is_instate
‪function is_instate(statename)
Definition: vehicle_ai_shared.gsc:994
‪show_weak_spots
‪function show_weak_spots(show)
Definition: _quadtank.gsc:785
‪get_next_state
‪function get_next_state()
Definition: vehicle_ai_shared.gsc:982
‪death_radius_damage
‪function death_radius_damage(meansOfDamage="MOD_EXPLOSIVE")
Definition: vehicle_death_shared.gsc:1196
‪MELEE_INNER_RADIUS_DAMAGE
‪#define MELEE_INNER_RADIUS_DAMAGE
Definition: _quadtank.gsc:46
‪StartInitialState
‪function StartInitialState(defaultState="combat")
Definition: vehicle_ai_shared.gsc:866
‪CanSeeEnemyFromPosition
‪function CanSeeEnemyFromPosition(position, enemy, sight_check_height)
Definition: vehicle_ai_shared.gsc:1815
‪impact_fx
‪function impact_fx(fxname, surfaceTypes)
Definition: vehicle_shared.gsc:2765
‪quadtank_enabletrophy
‪function quadtank_enabletrophy()
Definition: _quadtank.gsc:639
‪TROPHY_DISABLE_LIMIT
‪#define TROPHY_DISABLE_LIMIT
Definition: _quadtank.gsc:41
‪FreeWhenSafe
‪function FreeWhenSafe(time=4)
Definition: vehicle_death_shared.gsc:1756
‪railgun_sound
‪function railgun_sound(projectile)
Definition: _quadtank.gsc:1726
‪TURRET_STATE_SCAN_FORWARD2
‪#define TURRET_STATE_SCAN_FORWARD2
Definition: _quadtank.gsc:36
‪_init_turret
‪function _init_turret(n_index=0)
Definition: turret_shared.gsc:1773
‪quadtank_destroyturret
‪function quadtank_destroyturret(index)
Definition: _quadtank.gsc:1376
‪set_best_target_func
‪function set_best_target_func(func_get_best_target, n_index)
Definition: turret_shared.gsc:1871
‪TurnOffAllAmbientAnims
‪function TurnOffAllAmbientAnims()
Definition: vehicle_ai_shared.gsc:690
‪Cooldown
‪function Cooldown(name, time_seconds)
Definition: vehicle_ai_shared.gsc:1943
‪TURRET_STATE_SCAN_AT_ENEMY
‪#define TURRET_STATE_SCAN_AT_ENEMY
Definition: _quadtank.gsc:33
‪init_state_machine_for_role
‪function init_state_machine_for_role(rolename)
Definition: vehicle_ai_shared.gsc:1034
‪ROCKET_LAUNCHER_MIN_DIST
‪#define ROCKET_LAUNCHER_MIN_DIST
Definition: _quadtank.gsc:49
‪PositionQuery_DebugScores
‪function PositionQuery_DebugScores(queryResult)
Definition: vehicle_ai_shared.gsc:2010
‪pain_update
‪function pain_update(params)
Definition: _quadtank.gsc:289
‪waittill_any
‪function waittill_any(str_notify1, str_notify2, str_notify3, str_notify4, str_notify5)
Definition: util_shared.csc:375
‪get_max_health
‪function get_max_health(killstreakType)
Definition: _killstreak_bundles.gsc:188
‪quadtank_player_fireupdate
‪function quadtank_player_fireupdate()
Definition: _quadtank.gsc:1269
‪PositionQuery_PostProcess_SortScore
‪function PositionQuery_PostProcess_SortScore(queryResult, descending=true)
Definition: vehicle_ai_shared.gsc:2097
‪quadtank_side_turrets_forward
‪function quadtank_side_turrets_forward()
Definition: _quadtank.gsc:709
‪javeline_incoming
‪function javeline_incoming(projectile)
Definition: _quadtank.gsc:1694
‪PositionQuery_Filter_OutOfGoalAnchor
‪function PositionQuery_Filter_OutOfGoalAnchor(queryResult, tolerance=1)
Definition: vehicle_ai_shared.gsc:2104
‪update_damage_as_occupant
‪function update_damage_as_occupant(damage_taken, max_health)
Definition: vehicle_shared.gsc:3709
‪ClearCooldown
‪function ClearCooldown(name)
Definition: vehicle_ai_shared.gsc:1981
‪quadtank_weapon_think_cannon
‪function quadtank_weapon_think_cannon()
Definition: _quadtank.gsc:821
‪quadtank_movementupdate
‪function quadtank_movementupdate()
Definition: _quadtank.gsc:1211
‪REGISTER_SYSTEM
‪#define REGISTER_SYSTEM(__sys, __func_init_preload, __reqs)
Definition: shared.gsh:204
‪blink_lights_for_time
‪function blink_lights_for_time(time)
Definition: vehicle_ai_shared.gsc:648
‪__init__
‪function __init__()
Definition: _quadtank.gsc:68
‪MELEE_OUTER_RADIUS_DAMAGE
‪#define MELEE_OUTER_RADIUS_DAMAGE
Definition: _quadtank.gsc:47
‪emped
‪function emped(down_time)
Definition: _siegebot.gsc:1359
‪Spawn
‪function Spawn(parent, onDeathCallback)
Definition: _flak_drone.gsc:427
‪quadtank_exit_vehicle
‪function quadtank_exit_vehicle()
Definition: _quadtank.gsc:382
‪Movement_Thread_Wander
‪function Movement_Thread_Wander()
Definition: _quadtank.gsc:1118
‪quadtank_off
‪function quadtank_off()
Definition: _quadtank.gsc:215
‪defaultRole
‪function defaultRole()
Definition: _quadtank.gsc:189
‪target_hijackers
‪function target_hijackers()
Definition: vehicle_ai_shared.gsc:2312
‪waittill_asm_complete
‪function waittill_asm_complete(substate_to_wait, timeout=10)
Definition: vehicle_ai_shared.gsc:374
‪_get_turret_data
‪function _get_turret_data(n_index)
Definition: turret_shared.gsc:1725
‪quadtank_turret_scan
‪function quadtank_turret_scan(scan_forever)
Definition: _quadtank.gsc:717
‪GetCooldownLeft
‪function GetCooldownLeft(name)
Definition: vehicle_ai_shared.gsc:1961
‪QuadtankCallback_VehicleDamage
‪function QuadtankCallback_VehicleDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal)
Definition: _quadtank.gsc:1408
‪RegisterVehicleBlackBoardAttributes
‪function RegisterVehicleBlackBoardAttributes()
Definition: blackboard_vehicle.gsc:24
‪TURRET_STATE_SCAN_LEFT
‪#define TURRET_STATE_SCAN_LEFT
Definition: _quadtank.gsc:37
‪get_target
‪function get_target(n_index)
Definition: turret_shared.gsc:760
‪state_combat_update
‪function state_combat_update(params)
Definition: _quadtank.gsc:391
‪path_update_interrupt
‪function path_update_interrupt()
Definition: _quadtank.gsc:1068
‪CreateBlackBoardForEntity
‪function CreateBlackBoardForEntity(entity)
Definition: blackboard.gsc:77
‪player_is_in_laststand
‪function player_is_in_laststand()
Definition: laststand_shared.gsc:18
‪CleanUp
‪function CleanUp()
Definition: vehicle_death_shared.gsc:1795
‪side_turret_is_target_in_view_score
‪function side_turret_is_target_in_view_score(v_target, n_index)
Definition: _quadtank.gsc:1498
‪WEAPON_JAVELIN
‪#define WEAPON_JAVELIN
Definition: _quadtank.gsc:51
‪quadtank_automelee_update
‪function quadtank_automelee_update()
Definition: _quadtank.gsc:1345
‪EntityIsArchetype
‪function EntityIsArchetype(entity, archetype)
Definition: vehicle_ai_shared.gsc:83
‪register
‪function register()
Definition: _ai_tank.gsc:126
‪FLAT_ORIGIN
‪#define FLAT_ORIGIN(__origin)
Definition: shared.gsh:256
‪get_script_bundle
‪function get_script_bundle(str_type, str_name)
Definition: struct.csc:45
‪trophy_destroyed
‪function trophy_destroyed()
Definition: _quadtank.gsc:546
‪NEAR_GOAL_DIST
‪#define NEAR_GOAL_DIST
Definition: _quadtank.gsc:55
‪trigger_player_shock_fx
‪function trigger_player_shock_fx()
Definition: _quadtank.gsc:1057
‪TURRET_STATE_SCAN_RIGHT
‪#define TURRET_STATE_SCAN_RIGHT
Definition: _quadtank.gsc:35
‪toggle_sounds
‪function toggle_sounds(on)
Definition: vehicle_shared.gsc:3379
‪state_scripted_update
‪function state_scripted_update(params)
Definition: _quadtank.gsc:335
‪quadtank_death
‪function quadtank_death(params)
Definition: _quadtank.gsc:438
‪result
‪function result(death, attacker, mod, weapon)
Definition: _zm_aat_blast_furnace.gsc:46
‪quadtank_emped
‪function quadtank_emped(params)
Definition: _quadtank.gsc:488
‪lights_on
‪function lights_on(localClientNum, team)
Definition: vehicle_shared.csc:404
‪set_detonation_time
‪function set_detonation_time(target)
Definition: _quadtank.gsc:797
‪FindNewPosition
‪function FindNewPosition(sight_check_height)
Definition: vehicle_ai_shared.gsc:1821
‪WEAPON_STRAIGHT
‪#define WEAPON_STRAIGHT
Definition: _quadtank.gsc:52
‪toggle_ambient_anim_group
‪function toggle_ambient_anim_group(groupID, on)
Definition: vehicle_shared.gsc:2844
‪get_state_callbacks
‪function get_state_callbacks(statename)
Definition: vehicle_ai_shared.gsc:927