‪Black Ops 3 Source Code Explorer  0.1
‪An script explorer for Black Ops 3 by ZeRoY
vehicle_ai_shared.gsc
Go to the documentation of this file.
1 #using scripts\codescripts\struct;
2 
3 #using scripts\shared\flag_shared;
4 #using scripts\shared\math_shared;
5 #using scripts\shared\array_shared;
6 #using scripts\shared\sound_shared;
7 #using scripts\shared\system_shared;
8 #using scripts\shared\util_shared;
9 #using scripts\shared\statemachine_shared;
10 #using scripts\shared\vehicle_shared;
11 #using scripts\shared\vehicle_death_shared;
12 #using scripts\shared\turret_shared;
13 #using scripts\shared\ai\systems\ai_interface;
14 
15 #insert scripts\shared\shared.gsh;
16 #insert scripts\shared\statemachine.gsh;
17 #insert scripts\shared\archetype_shared\archetype_shared.gsh;
18 
19 #define ANIMTREE "generic"
20 #define STATE_PRIORITY_DEATH 100
21 #define STATE_PRIORITY_INCREASE 50
22 #define STATE_PRIORITY_DECREASE 20
23 
24 #namespace vehicle_ai;
25 
26 ‪REGISTER_SYSTEM( "vehicle_ai", &‪__init__, undefined )
27 
28 // _vehicle_ai.gsc - all things related to vehicles ai
29 
30 #using_animtree( ANIMTREE );
31 
32 function ‪__init__()
33 {
34 }
35 
37 {
38  /*
39  * Name: force_high_speed
40  * Summary: Always uses max speed instead of default speed when moving
41  * Example: ai::SetAiAttribute( vehicle, "sprint", true );"
42  */
44  archetype,
45  "force_high_speed",
46  false,
47  ‪array( true, false ) );
48 }
49 
50 //*****************************************************************************
51 //
52 // Vehicle AI Commands
53 //
54 // - These are used in conjunction with the vehicle ai system
55 // - If the vehicle is not setup to use the ai system they might do nothing
56 // - Please see James Snider or Ruoyao Ma if you have questions
57 //
58 //*****************************************************************************
59 
61 {
62  aiarray = GetAIArray();
63 
64  foreach( ai in aiarray )
65  {
66  if ( ai === self )
67  {
68  continue;
69  }
70 
71  if ( self.ignoreFireFly === true && ai.firefly === true )
72  {
73  self SetPersonalIgnore( ai );
74  }
75 
76  if ( self.ignoreDecoy === true && ai.isDecoy === true )
77  {
78  self SetPersonalIgnore( ai );
79  }
80  }
81 }
82 
83 function ‪EntityIsArchetype( entity, archetype )
84 {
85  if ( !isdefined( entity ) )
86  {
87  return false;
88  }
89  if ( isplayer( entity ) && entity.usingvehicle && isdefined( entity.viewlockedentity ) && entity.viewlockedentity.archetype === archetype )
90  {
91  return true;
92  }
93  if ( isvehicle( entity ) && entity.archetype === archetype )
94  {
95  return true;
96  }
97  return false;
98 }
99 
100 // self == AI vehicle
102 {
103  if( isdefined( self.enemy ) && self VehCanSee( self.enemy ) )
104  {
105  return self.enemy;
106  }
107  else if( isdefined( self.enemylastseenpos ) )
108  {
109  return self.enemylastseenpos;
110  }
111 
112  return undefined;
113 }
114 
115 function ‪GetTargetPos( target, geteye )
116 {
117  pos = undefined;
118 
119  if ( isdefined( target ) )
120  {
121  if ( IsVec( target ) )
122  {
123  pos = target;
124  }
125  else if ( ‪IS_TRUE( geteye ) && IsSentient( target ) )
126  {
127  pos = target GetEye();
128  }
129  else if ( IsEntity( target ) )
130  {
131  pos = target.origin;
132  }
133  else if ( isdefined( target.origin ) && IsVec( target.origin ) )
134  {
135  pos = target.origin;
136  }
137  }
138 
139  return pos;
140 }
141 
142 function ‪GetTargetEyeOffset( target )
143 {
144  offset = (0,0,0);
145 
146  if ( isdefined( target ) && IsSentient( target ) )
147  {
148  offset = target GetEye() - target.origin;
149  }
150 
151  return offset;
152 }
153 
164 function ‪fire_for_time( totalFireTime, turretIdx, target, intervalScale = 1.0 )
165 {
166  self endon( "death" );
167  self endon( "change_state" );
168  self notify( "fire_stop" );
169  self endon( "fire_stop" );
170 
171  if ( !isdefined( turretIdx ) )
172  {
173  turretIdx = 0;
174  }
175 
176  weapon = self SeatGetWeapon( turretIdx );
177 
178  assert( isdefined( weapon ) && weapon.name!="none" && weapon.fireTime>0 );
179 
180  firetime = weapon.firetime * intervalScale;
181 
182  fireCount = int( floor( totalFireTime / fireTime ) ) + 1;
183 
184  ‪__fire_for_rounds_internal( fireCount, fireTime, turretIdx, target );
185 }
186 
197 function ‪fire_for_rounds( fireCount, turretIdx, target )
198 {
199  self endon( "death" );
200  self endon( "fire_stop" );
201  self endon( "change_state" );
202 
203  if ( !isdefined( turretIdx ) )
204  {
205  turretIdx = 0;
206  }
207 
208  weapon = self SeatGetWeapon( turretIdx );
209 
210  assert( isdefined( weapon ) && weapon.name!="none" && weapon.fireTime>0 );
211 
212  ‪__fire_for_rounds_internal( fireCount, weapon.fireTime, turretIdx, target );
213 }
214 
215 function ‪__fire_for_rounds_internal( fireCount, fireInterval, turretIdx, target )
216 {
217  self endon( "death" );
218  self endon( "fire_stop" );
219  self endon( "change_state" );
220 
221  if( isdefined( target ) && IsSentient( target ) )
222  {
223  target endon( "death" );
224  }
225 
226  assert( isdefined( turretIdx ) );
227 
228  aiFireChance = 1;
229 
230  // fire less, unless shooting player or a bigdog or mentioned specifically from level script
231  if ( ( isdefined( target ) && !IsPlayer( target ) && isAI( target ) ) || isdefined( self.fire_half_blanks ) )
232  {
233  // 1 in 2 bullets will be real
234  aiFireChance = 2;
235  }
236 
237  counter = 0;
238  while ( counter < fireCount )
239  {
240  if ( self.avoid_shooting_owner === true && self ‪owner_in_line_of_fire() )
241  {
242  wait fireInterval;
243  continue;
244  }
245 
246  if ( isdefined( target ) && !isVec( target ) && isdefined( target.attackerAccuracy ) && target.attackerAccuracy == 0 )
247  {
248  self ‪FireTurret( turretIdx, true );
249  }
250  else if ( aiFireChance > 1 )
251  {
252  self ‪FireTurret( turretIdx, counter % aiFireChance );
253  }
254  else
255  {
256  self ‪FireTurret( turretIdx );
257  }
258 
259  counter++;
260  wait fireInterval;
261  }
262 }
263 
265 {
266  if ( !isdefined( self.owner ) )
267  return false;
268 
269  dist_squared_to_owner = DistanceSquared( self.owner.origin, self.origin );
270  line_of_fire_dot = ( ( dist_squared_to_owner < 96 * 96 ) ? 0.8660 : 0.9848 ); // cos( 30 degrees ) : cos ( 10 degrees );
271  gun_angles = self GetTagAngles( ‪VAL( self.avoid_shooting_owner_ref_tag , "tag_flash" ) );
272  gun_forward = AnglesToForward( gun_angles );
273  dot = VectorDot( gun_forward, VectorNormalize( self.owner.origin - self.origin ) ); // rough approximation, good enough
274  return ( dot > line_of_fire_dot );
275 }
276 
277 function ‪SetTurretTarget( target, turretIdx = 0, offset = (0,0,0) )
278 {
279  if ( isEntity( target ) )
280  {
281  if ( turretIdx == 0 )
282  {
283  self SetTurretTargetEnt( target, offset );
284  }
285  else
286  {
287  self SetGunnerTargetEnt( target, offset, turretIdx - 1 );
288  }
289  }
290  else if ( isVec( target ) )
291  {
292  origin = target + offset;
293 
294  if ( turretIdx == 0 )
295  {
296  self SetTurretTargetVec( target );
297  }
298  else
299  {
300  self SetGunnerTargetVec( target, turretIdx - 1 );
301  }
302  }
303  else
304  {
305  AssertMsg( "Turret target must be an entity or a vector." );
306  }
307 }
308 
309 function ‪FireTurret( turretIdx, isFake )
310 {
311  self FireWeapon( turretIdx, undefined, undefined, self );
312 }
313 
315 {
316  self endon( "death" );
317 
318  self waittill( "weapon_fired", proj );
319 
320  if( !isdefined( proj ) )
321  {
322  return;
323  }
324 
325  proj endon( "death" );
326 
327  wait 2;
328 
329  while( isdefined( target ) )
330  {
331  if( proj GetVelocity()[2] < -150 && DistanceSquared( proj.origin, target.origin ) < ‪SQR(1200) )
332  {
333  proj Missile_SetTarget( undefined );
334  break;
335  }
336  wait 0.1;
337  }
338 }
339 
340 function ‪waittill_pathing_done( maxtime = 15 )
341 {
342  self endon( "change_state" );
343 
344  self ‪util::waittill_any_ex( maxtime, "near_goal", "force_goal", "reached_end_node", "goal", "pathfind_failed", "change_state" );
345 }
346 
347 function ‪waittill_pathresult( maxtime = 0.5 )
348 {
349  self endon( "change_state" );
350 
351  ‪result = self ‪util::waittill_any_timeout( maxtime, "pathfind_failed", "pathfind_succeeded", "change_state" );
352  succeeded = ( ‪result === "pathfind_succeeded" );
353  return succeeded;
354 }
355 
357 {
358  self endon( "death" );
359  self notify( "end_asm_terminated_thread" );
360  self endon( "end_asm_terminated_thread" );
361  self waittill( "asm_terminated" );
362  self notify( "asm_complete", "__terminated__" );
363 }
364 
366 {
367  self endon( "death" );
368  self notify( "end_asm_timeout_thread" );
369  self endon( "end_asm_timeout_thread" );
370  wait ‪timeout;
371  self notify( "asm_complete", "__timeout__" );
372 }
373 
374 function ‪waittill_asm_complete( substate_to_wait, ‪timeout = 10 )
375 {
376  self endon( "death" );
377 
378  self thread ‪waittill_asm_terminated();
379  self thread ‪waittill_asm_timeout( ‪timeout );
380 
381  substate = undefined;
382  while ( !isdefined( substate ) || ( substate != substate_to_wait && substate != "__terminated__" && substate != "__timeout__" ) )
383  {
384  self waittill( "asm_complete", substate );
385  }
386  self notify( "end_asm_terminated_thread" );
387  self notify( "end_asm_timeout_thread" );
388 }
389 
390 // self == vehicle
391 function ‪throw_off_balance( damageType, hitPoint, hitDirection, hitLocationInfo )
392 {
393  if ( damageType == "MOD_EXPLOSIVE" || damageType == "MOD_GRENADE_SPLASH" || damageType == "MOD_PROJECTILE_SPLASH" )
394  {
395  self SetVehVelocity( self.velocity + VectorNormalize( hitDirection ) * 300 );
396  ang_vel = self GetAngularVelocity();
397  ang_vel += ( RandomFloatRange( -300, 300 ), RandomFloatRange( -300, 300 ), RandomFloatRange( -300, 300 ) );
398  self SetAngularVelocity( ang_vel );
399  }
400  else
401  {
402  ang_vel = self GetAngularVelocity();
403  yaw_vel = RandomFloatRange( -320, 320 );
404  yaw_vel += ‪math::sign( yaw_vel ) * 150;
405 
406  ang_vel += ( RandomFloatRange( -150, 150 ), yaw_vel, RandomFloatRange( -150, 150 ) );
407  self SetAngularVelocity( ang_vel );
408  }
409 }
410 
412 {
413  self endon( "crash_done" );
414  self endon( "death" );
415 
416  while ( 1 )
417  {
418  self waittill( "veh_predictedcollision", velocity, normal );
419  if ( normal[2] >= 0.6 )
420  {
421  self notify( "veh_collision", velocity, normal );
422  }
423  }
424 }
425 
426 // self == vehicle
427 function ‪collision_fx( normal )
428 {
429  tilted = ( normal[2] < 0.6 );
430  fx_origin = self.origin - normal * ( tilted ? 28 : 10 );
431 
432  self PlaySound( "veh_wasp_wall_imp" );
433  //PlayFX( level._effect[ "drone_nudge" ], fx_origin, normal );
434 }
435 
436 // self == vehicle
438 {
439  self endon( "crash_done" );
440  self endon( "power_off_done" );
441  self endon( "death" );
442  self notify( "end_nudge_collision" );
443  self endon( "end_nudge_collision" );
444 
445  if ( self.notsolid === true )
446  {
447  return;
448  }
449 
450  while ( 1 )
451  {
452  self waittill( "veh_collision", velocity, normal );
453  ang_vel = self GetAngularVelocity() * 0.5;
454  self SetAngularVelocity( ang_vel );
455 
456  empedOrOff = ( self ‪get_current_state() === "emped" || self ‪get_current_state() === "off" );
457 
458  // bounce off walls
459  if ( IsAlive( self ) && ( normal[2] < 0.6 || !empedOrOff ) ) // stable or active
460  {
461  self SetVehVelocity( self.velocity + normal * 90 );
462  self ‪collision_fx( normal );
463  }
464  else if ( empedOrOff )
465  {
466  if ( isdefined( self.bounced ) )
467  {
468  self playsound( "veh_wasp_wall_imp" );
469  self SetVehVelocity( ( 0, 0, 0 ) );
470  self SetAngularVelocity( ( 0, 0, 0 ) );
471 
472  pitch = self.angles[0];
473  pitch = ‪math::sign( pitch ) * ‪math::clamp( abs( pitch ), 10, 15 );
474  self.angles = ( pitch, self.angles[1], self.angles[2] );
475 
476  self.bounced = undefined;
477  self notify( "landed" );
478  return;
479  }
480  else
481  {
482  self.bounced = true;
483  self SetVehVelocity( self.velocity + normal * 30 );
484  self ‪collision_fx( normal );
485  }
486  }
487  else // tilted
488  {
489  impact_vel = abs( VectorDot( velocity, normal ) );
490 
491  if( normal[2] < 0.6 && impact_vel < 100 )
492  {
493  self SetVehVelocity( self.velocity + normal * 90 );
494  self ‪collision_fx( normal );
495  }
496  else
497  {
498  self playsound( "veh_wasp_ground_death" );
500  self notify( "crash_done" );
501  }
502  }
503  }
504 }
505 
506 //self is vehicle
508 {
509  self endon( "death" );
510  self endon( "change_state" );
511  self endon( "landed" );
512 
513  while( 1 )
514  {
515  velocity = self.velocity; // setting the angles clears the velocity so we save it off and set it back
516  self.angles = ( self.angles[0] * 0.85, self.angles[1], self.angles[2] * 0.85 );
517  ang_vel = self GetAngularVelocity() * 0.85;
518  self SetAngularVelocity( ang_vel );
519  self SetVehVelocity( velocity + (0,0,-60) );
521  }
522 }
523 
524 
525 //self is vehicle
526 function ‪immolate( attacker )
527 {
528  self endon( "death" );
529 
530  self thread ‪burning_thread( attacker, attacker );
531 }
532 
533 function ‪burning_thread( attacker, inflictor )
534 {
535  self endon( "death" );
536  self notify( "end_immolating_thread" );
537  self endon( "end_immolating_thread" );
538 
539  damagePerSecond = self.settings.burn_damagepersecond;
540  if ( !isdefined( damagePerSecond ) || damagePerSecond <= 0 )
541  {
542  return;
543  }
544 
545  secondsPerOneDamage = 1.0 / Float( damagePerSecond );
546 
547  if ( !isdefined( self.abnormal_status ) )
548  {
549  self.abnormal_status = spawnStruct();
550  }
551 
552  if ( self.abnormal_status.burning !== true )
553  {
554  self ‪vehicle::toggle_burn_fx( true );
555  }
556 
557  self.abnormal_status.burning = true;
558  self.abnormal_status.attacker = attacker;
559  self.abnormal_status.inflictor = inflictor;
560 
561  lastingTime = self.settings.burn_lastingtime;
562  if ( !isdefined( lastingTime ) )
563  {
564  lastingTime = 999999;
565  }
566 
567  ‪startTime = GetTime();
568  interval = max( secondsPerOneDamage, 0.5 );
569  ‪damage = 0.0;
570 
571  while ( ‪TimeSince( ‪startTime ) < lastingTime )
572  {
573  previousTime = GetTime();
574  wait interval;
575  ‪damage += ‪TimeSince( previousTime ) * damagePerSecond;
576  damageInt = Int( ‪damage );
577  self DoDamage( damageInt, self.origin, attacker, self, "none", "MOD_BURNED" );
578  ‪damage -= damageInt;
579  }
580 
581  self.abnormal_status.burning = false;
582  self ‪vehicle::toggle_burn_fx( false );
583 }
584 
585 //self is vehicle
586 function ‪iff_notifyMeInNSec(time,note)
587 {
588  self endon("death");
589  wait time;
590  self notify(note);
591 }
592 function ‪iff_override( owner,time = 60 )
593 {
594  self endon( "death" );
595 
596  //save off the old team
597  self._iffoverride_oldTeam = self.team;
598 
599  self ‪iff_override_team_switch_behavior( owner.team );
600  if(isDefined(self.iff_override_cb))
601  self [[self.iff_override_cb]](true);
602 
603  if(isDefined(self.settings) && !‪IS_TRUE( self.settings.iffshouldrevertteam ) )
604  {
605  //do not revert team
606  return;
607  }
608  ‪timeout = (isDefined(self.settings)?self.settings.ifftimetillrevert:time);
609  assert(‪timeout>10);
610  self thread ‪iff_notifyMeInNSec(‪timeout-10,"iff_override_revert_warn");//5 defined in _cybercom.gsh;
611  msg = self ‪util::waittill_any_timeout( ‪timeout,"iff_override_reverted", "death" );
612  if (msg == "timeout" )
613  {
614  self notify ("iff_override_reverted");
615  }
616 
617  self playsound ("gdt_iff_deactivate");
618 
619  self ‪iff_override_team_switch_behavior( self._iffoverride_oldTeam );
620  if(isDefined(self.iff_override_cb))
621  self [[self.iff_override_cb]](false);
622 
623 }
624 
626 {
627  self endon( "death" );
628 
629  old_ignoreme = self.ignoreme;
630  self.ignoreme = true;
631 
632  self ‪start_scripted();
633  self ‪vehicle::lights_off();
634  wait 0.1;
635 
636  wait( 1 );
637 
638  self SetTeam( team );
639 
640  self ‪blink_lights_for_time( 1 );
641 
642  self ‪stop_scripted();
643 
644  wait 1;
645  self.ignoreme = old_ignoreme;
646 }
647 
648 function ‪blink_lights_for_time( time )
649 {
650  self endon( "death" );
651 
652  ‪startTime = GetTime();
653 
654  self ‪vehicle::lights_off();
655  wait 0.1;
656 
657  while ( GetTime() < ‪startTime + time * 1000 )
658  {
659  self ‪vehicle::lights_off();
660  wait 0.2;
661  self ‪vehicle::lights_on();
662  wait 0.2;
663  }
664 
665  self ‪vehicle::lights_on();
666 }
667 
668 function ‪TurnOff()
669 {
670  self notify( "shut_off" );
671 }
672 
673 function ‪TurnOn()
674 {
675  self notify( "start_up" );
676 }
677 
679 {
680  self LaserOff();
681  self ‪vehicle::lights_off();
682  self ‪vehicle::toggle_lights_group( 1, false );
683  self ‪vehicle::toggle_lights_group( 2, false );
684  self ‪vehicle::toggle_lights_group( 3, false );
685  self ‪vehicle::toggle_lights_group( 4, false );
686  self ‪vehicle::toggle_burn_fx( false );
687  self ‪vehicle::toggle_emp_fx( false );
688 }
689 
691 {
692  self ‪vehicle::toggle_ambient_anim_group( 1, false );
693  self ‪vehicle::toggle_ambient_anim_group( 2, false );
694  self ‪vehicle::toggle_ambient_anim_group( 3, false );
695 }
696 
698 {
699  self ClearTargetEntity();
700  self ClearGunnerTarget(0);
701  self ClearGunnerTarget(1);
702  self ClearGunnerTarget(2);
703  self ClearGunnerTarget(3);
704  self ClearLookAtEnt();
705 }
706 
707 function ‪ClearAllMovement( zeroOutSpeed = false )
708 {
709  if( !IsAirborne( self ) )
710  {
711  self CancelAIMove();
712  }
713  self ClearVehGoalPos();
714  self PathVariableOffsetClear();
715  self PathFixedOffsetClear();
716 
717  if ( zeroOutSpeed === true )
718  {
719  self notify( "landed" );
720  self SetVehVelocity( ( 0, 0, 0 ) );
721  self SetPhysAcceleration( ( 0, 0, 0 ) );
722  self SetAngularVelocity( ( 0, 0, 0 ) );
723  }
724 }
725 
726 function ‪shared_callback_damage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal )
727 {
728  if ( ‪should_emp( self, weapon, sMeansOfDeath, eInflictor, eAttacker ) )
729  {
730  minEmpDownTime = 0.8 * self.settings.empdowntime;
731  maxEmpDownTime = 1.2 * self.settings.empdowntime;
732  self notify ( "emped", RandomFloatRange( minEmpDownTime, maxEmpDownTime ), eAttacker, eInflictor );
733  }
734 
735  if ( ‪should_burn( self, weapon, sMeansOfDeath, eInflictor, eAttacker ) )
736  {
737  self thread ‪burning_thread( eAttacker, eInflictor );
738  }
739 
740  if ( !isdefined( self.damageLevel ) )
741  {
742  self.damageLevel = 0;
743  self.newDamageLevel = self.damageLevel;
744  }
745 
746  newDamageLevel = ‪vehicle::should_update_damage_fx_level( self.health, iDamage, self.healthdefault );
747  if ( newDamageLevel > self.damageLevel )
748  {
749  self.newDamageLevel = newDamageLevel;
750  }
751 
752  if ( self.newDamageLevel > self.damageLevel )
753  {
754  self.damageLevel = self.newDamageLevel;
755  if ( self.pain_when_damagelevel_change === true )
756  {
757  self notify( "pain" );
758  }
759  ‪vehicle::set_damage_fx_level( self.damageLevel );
760  }
761 
762  return iDamage;
763 }
764 
765 function ‪should_emp( vehicle, weapon, meansOfDeath, eInflictor, eAttacker )
766 {
767  if( !isdefined( vehicle ) || meansOfDeath === "MOD_IMPACT" || vehicle.disableElectroDamage === true )
768  {
769  return false;
770  }
771 
772  if ( !( ( isdefined( weapon ) && weapon.isEmp ) || meansOfDeath === "MOD_ELECTROCUTED" ) )
773  {
774  return false;
775  }
776 
777  causer = ( isdefined( eAttacker ) ? eAttacker : eInflictor );
778  if ( !isdefined( causer ) )
779  {
780  return true;
781  }
782 
783  if ( IsAI( causer ) && IsVehicle( causer ) ) // don't make electro contagious between AI vehicles
784  {
785  return false;
786  }
787 
788  if ( level.teamBased )
789  {
790  return ( vehicle.team != causer.team );
791  }
792  else
793  {
794  if ( isdefined( vehicle.owner ) )
795  {
796  return ( vehicle.owner != causer );
797  }
798 
799  return ( vehicle != causer );
800  }
801 }
802 
803 function ‪should_burn( vehicle, weapon, meansOfDeath, eInflictor, eAttacker )
804 {
805  if ( level.disableVehicleBurnDamage === true || vehicle.disableBurnDamage === true )
806  {
807  return false;
808  }
809 
810  if( !isdefined( vehicle ) )
811  {
812  return false;
813  }
814 
815  if ( meansOfDeath !== "MOD_BURNED" )
816  {
817  return false;
818  }
819 
820  if ( vehicle === eInflictor ) // this is how we do continuesly burning damage
821  {
822  return false;
823  }
824 
825  causer = ( isdefined( eAttacker ) ? eAttacker : eInflictor );
826  if ( !isdefined( causer ) )
827  {
828  return true;
829  }
830 
831  if ( IsAI( causer ) && IsVehicle( causer ) ) // don't make burning contagious between AI vehicles
832  {
833  return false;
834  }
835 
836  if ( level.teamBased )
837  {
838  return ( vehicle.team != causer.team );
839  }
840  else
841  {
842  if ( isdefined( vehicle.owner ) )
843  {
844  return ( vehicle.owner != causer );
845  }
846 
847  return ( vehicle != causer );
848  }
849 }
850 
851 //*****************************************************************************
852 //
853 // Vehicle AI states
854 //
855 // - These are the default states if the vehicle type don't change the callback
856 // - To change a specific callback:
857 // self get_state_callbacks("combat").update_func = custom_update_func;
858 // - update_func will be called on a thread, generally have a loop inside and endon( "change_state" )
859 // * update_func can have wait, or set state in it
860 // - enter_func and exit_func are guaranteed to be called on state transition, so it's good for init and clean up for a state
861 // * enter_func and exit_func should not have wait, or set state in it
862 //
863 //*****************************************************************************
864 
865 // self == vehicle
866 function ‪StartInitialState( defaultState = "combat" )
867 {
868  // we expect script_startstate to be only changed from Radiant, not in script, so we don't wait here
869 
870  params = SpawnStruct();
871  params.isInitialState = true;
872 
873  // Set the first state
874  if ( isdefined( self.script_startstate ) )
875  {
876  self ‪set_state( self.script_startstate, params );
877  }
878  else
879  {
880  // Set the first state
881  self ‪set_state( defaultState, params );
882  }
883 }
884 
885 // self == vehicle
886 function ‪start_scripted( disable_death_state, no_clear_movement )
887 {
888  params = spawnStruct();
889  params.no_clear_movement = no_clear_movement;
890  self ‪set_state( "scripted", params );
891  self._no_death_state = disable_death_state;
892 }
893 
894 function ‪stop_scripted( statename )
895 {
896  if ( isAlive( self ) && ‪is_instate( "scripted" ) )
897  {
898  if ( isdefined( statename ) )
899  {
900  self ‪set_state( statename );
901  }
902  else
903  {
904  self ‪set_state( "combat" );
905  }
906  }
907 }
908 
909 // self == vehicle
910 function ‪set_role( rolename )
911 {
912  self.current_role = rolename;
913 }
914 
915 // self == vehicle
916 function ‪set_state( ‪name, params )
917 {
918  self.state_machines[ self.current_role ] thread ‪statemachine::set_state( ‪name, params );
919 }
920 
921 function ‪evaluate_connections( eval_func, params )
922 {
923  self.state_machines[ self.current_role ] ‪statemachine::evaluate_connections( eval_func, params );
924 }
925 
926 // self == vehicle
927 function ‪get_state_callbacks( statename )
928 {
929  rolename = "default";
930  if ( isdefined( self.current_role ) )
931  {
932  rolename = self.current_role;
933  }
934 
935  if ( IsDefined( self.state_machines[ rolename ] ) )
936  {
937  return self.state_machines[ rolename ].states[ statename ];
938  }
939 
940  return undefined;
941 }
942 
943 // self == vehicle
944 function ‪get_state_callbacks_for_role( rolename, statename )
945 {
946  if ( !isdefined( rolename ) )
947  {
948  rolename = "default";
949  }
950 
951  if ( IsDefined( self.state_machines[ rolename ] ) )
952  {
953  return self.state_machines[ rolename ].states[ statename ];
954  }
955 
956  return undefined;
957 }
958 
959 // self == vehicle
961 {
962  if ( IsDefined( self.current_role ) && IsDefined( self.state_machines[ self.current_role ].current_state ) )
963  {
964  return self.state_machines[ self.current_role ].current_state.name;
965  }
966 
967  return undefined;
968 }
969 
970 // self == vehicle
972 {
973  if ( IsDefined( self.current_role ) && IsDefined( self.state_machines[ self.current_role ].previous_state ) )
974  {
975  return self.state_machines[ self.current_role ].previous_state.name;
976  }
977 
978  return undefined;
979 }
980 
981 // self == vehicle
983 {
984  if ( IsDefined( self.current_role ) && IsDefined( self.state_machines[ self.current_role ].next_state ) )
985  {
986  return self.state_machines[ self.current_role ].next_state.name;
987  }
988 
989  return undefined;
990 }
991 
992 
993 // self == vehicle
994 function ‪is_instate( statename )
995 {
996  if ( IsDefined( self.current_role ) && IsDefined( self.state_machines[ self.current_role ].current_state ) )
997  {
998  return self.state_machines[ self.current_role ].current_state.name === statename;
999  }
1000 
1001  return false;
1002 }
1003 
1004 // self == vehicle
1005 function ‪add_state( ‪name, enter_func, update_func, exit_func )
1006 {
1007  if ( IsDefined( self.current_role ) )
1008  {
1009  statemachine = self.state_machines[ self.current_role ];
1010 
1011  if ( IsDefined( statemachine ) )
1012  {
1013  state = statemachine ‪statemachine::add_state( ‪name, enter_func, update_func, exit_func );
1014  return state;
1015  }
1016  }
1017 
1018  return undefined;
1019 }
1020 
1021 // self == vehicle
1022 function ‪add_interrupt_connection( from_state_name, to_state_name, on_notify, checkfunc )
1023 {
1024  self.state_machines[ self.current_role ] ‪statemachine::add_interrupt_connection( from_state_name, to_state_name, on_notify, checkfunc );
1025 }
1026 
1027 // self == vehicle
1028 function ‪add_utility_connection( from_state_name, to_state_name, checkfunc, defaultScore )
1029 {
1030  self.state_machines[ self.current_role ] ‪statemachine::add_utility_connection( from_state_name, to_state_name, checkfunc, defaultScore );
1031 }
1032 
1033 // self == vehicle
1035 {
1036  if ( !isdefined( rolename ) )
1037  {
1038  rolename = "default";
1039  }
1040 
1041  statemachine = ‪statemachine::create( rolename, self );
1042  statemachine.isRole = true;
1043 
1044  if ( !IsDefined( self.current_role ) )
1045  {
1046  ‪set_role( rolename );
1047  }
1048 
1049  // special states
1050  statemachine ‪statemachine::add_state( "suspend", // empty state used to "suspend" the state machine, not to be confused with "off" state
1051  undefined,
1052  undefined,
1053  undefined );
1054 
1055  statemachine ‪statemachine::add_state( "death",
1058  undefined );
1059 
1060  statemachine ‪statemachine::add_state( "scripted",
1062  undefined, // no update (scripter taking manual control or player controlling)
1064 
1065  // general states
1066  statemachine ‪statemachine::add_state( "combat",
1068  undefined,
1070 
1071  statemachine ‪statemachine::add_state( "emped",
1076 
1077  statemachine ‪statemachine::add_state( "surge",
1081 
1082  statemachine ‪statemachine::add_state( "off",
1084  undefined,
1086 
1087  statemachine ‪statemachine::add_state( "driving",
1089  undefined,
1091 
1092  statemachine ‪statemachine::add_state( "pain",
1094  undefined,
1096 
1097 
1098  statemachine ‪statemachine::add_interrupt_connection( "off", "combat", "start_up" );
1099  statemachine ‪statemachine::add_interrupt_connection( "driving", "combat", "exit_vehicle" );
1100  statemachine ‪statemachine::add_utility_connection( "emped", "combat" );
1101  statemachine ‪statemachine::add_utility_connection( "pain", "combat" );
1102 
1103  statemachine ‪statemachine::add_interrupt_connection( "combat", "emped", "emped" );
1104  statemachine ‪statemachine::add_interrupt_connection( "pain", "emped", "emped" );
1105  statemachine ‪statemachine::add_interrupt_connection( "emped", "emped", "emped" );// emped vehicle can be emped again, it will delay the recover
1106 
1107  statemachine ‪statemachine::add_interrupt_connection( "combat", "surge", "surge" );
1108  statemachine ‪statemachine::add_interrupt_connection( "off", "surge", "surge" );
1109  statemachine ‪statemachine::add_interrupt_connection( "pain", "surge", "surge" );
1110  statemachine ‪statemachine::add_interrupt_connection( "emped", "surge", "surge" );
1111 
1112  statemachine ‪statemachine::add_interrupt_connection( "combat", "off", "shut_off" );
1113  statemachine ‪statemachine::add_interrupt_connection( "emped", "off", "shut_off" );
1114  statemachine ‪statemachine::add_interrupt_connection( "pain", "off", "shut_off" );
1115  // no "driving" to "off". the player will need to be kicked out first.
1116 
1117  statemachine ‪statemachine::add_interrupt_connection( "combat", "driving", "enter_vehicle" );
1118  statemachine ‪statemachine::add_interrupt_connection( "emped", "driving", "enter_vehicle" );
1119  statemachine ‪statemachine::add_interrupt_connection( "off", "driving", "enter_vehicle" );
1120  statemachine ‪statemachine::add_interrupt_connection( "pain", "driving", "enter_vehicle" );
1121 
1122  statemachine ‪statemachine::add_interrupt_connection( "combat", "pain", "pain" );
1123  statemachine ‪statemachine::add_interrupt_connection( "emped", "pain", "pain" );
1124  statemachine ‪statemachine::add_interrupt_connection( "off", "pain", "pain" );
1125  statemachine ‪statemachine::add_interrupt_connection( "driving", "pain", "pain" );
1126 
1127  // no connection to "death". "death" state is handled in this callback as a special case
1128  self.overrideVehicleKilled = &‪Callback_VehicleKilled;
1129  self.overrideVehicleDeathPostGame = &‪Callback_VehicleKilled;
1130 
1131  // no connection to "scripted". start_scripted and stop_scripted are used to control the transition in and out of "scripted" state.
1132 
1133  statemachine thread ‪statemachine::set_state("suspend");
1134 
1135  self thread ‪on_death_cleanup();
1136 
1137  return statemachine;
1138 }
1139 
1140 //if levels or gamemodes want to add custom vehicle states, they can register them to this list
1141 
1143 {
1144  if( !IsDefined( level.level_specific_add_state_callbacks ) )
1145  {
1146  level.level_specific_add_state_callbacks = [];
1147  }
1148 
1149  level.level_specific_add_state_callbacks[level.level_specific_add_state_callbacks.size] = func;
1150 }
1151 
1153 {
1154  if( IsDefined( level.level_specific_add_state_callbacks ) )
1155  {
1156  for( i = 0; i < level.level_specific_add_state_callbacks.size; i++ )
1157  {
1158  self [[ level.level_specific_add_state_callbacks[i] ]]();
1159  }
1160  }
1161 }
1162 
1163 // ----------------------------------------------
1164 // State: death
1165 // ----------------------------------------------
1166 // self == vehicle
1167 function ‪Callback_VehicleKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, weapon, vDir, sHitLoc, psOffsetTime )
1168 {
1169  if ( ‪IS_TRUE( self._no_death_state ) )
1170  {
1171  return;
1172  }
1173 
1174  death_info = SpawnStruct();
1175  death_info.inflictor = eInflictor;
1176  death_info.attacker = eAttacker;
1177  death_info.damage = iDamage;
1178  death_info.meansOfDeath = sMeansOfDeath;
1179  death_info.weapon = weapon;
1180  death_info.dir = vDir;
1181  death_info.hitLoc = sHitLoc;
1182  death_info.timeOffset = psOffsetTime;
1183 
1184  self ‪set_state( "death", death_info );
1185 }
1186 
1188 {
1189  state_machines = self.state_machines;
1190 
1191  self waittill("free_vehicle");
1192 
1193  //Clear State Machine or we will have a script leak caused by cross reference
1194  foreach(stateMachine in state_machines)
1195  {
1196  stateMachine ‪statemachine::clear();
1197  }
1198 }
1199 
1201 {
1202  self ‪vehicle::toggle_tread_fx( false );
1203  self ‪vehicle::toggle_exhaust_fx( false );
1204  self ‪vehicle::toggle_sounds( false );
1205  self DisableAimAssist();
1206 
1211  self CancelAIMove();
1212 
1213  //self SetBrake( 1 );
1214  self.takedamage = 0;
1216 }
1217 
1219 {
1220  if ( isdefined( self.settings.burn_death_fx_1 ) && isdefined( self.settings.burn_death_tag_1 ) )
1221  {
1222  PlayFxOnTag( self.settings.burn_death_fx_1, self, self.settings.burn_death_tag_1 );
1223  }
1224 
1225  if ( isdefined( self.settings.burn_death_sound_1 ) )
1226  {
1227  self PlaySound( self.settings.burn_death_sound_1 );
1228  }
1229 }
1230 
1232 {
1233  if ( isdefined( self.settings.emp_death_fx_1 ) && isdefined( self.settings.emp_death_tag_1 ) )
1234  {
1235  PlayFxOnTag( self.settings.emp_death_fx_1, self, self.settings.emp_death_tag_1 );
1236  }
1237 
1238  if ( isdefined( self.settings.emp_death_sound_1 ) )
1239  {
1240  self PlaySound( self.settings.emp_death_sound_1 );
1241  }
1242 }
1243 
1244 function ‪death_radius_damage_special( radiusScale, meansOfDamage )
1245 {
1246  self endon( "death" );
1247 
1248  if ( !isdefined( self ) || self.abandoned === true || self.damage_on_death === false || self.radiusdamageradius <= 0 )
1249  {
1250  return;
1251  }
1252 
1253  position = self.origin + ( 0,0,15 );
1254  radius = self.radiusdamageradius * radiusScale;
1255  damageMax = self.radiusdamagemax;
1256  damageMin = self.radiusdamagemin;
1257 
1259 
1260  if ( isdefined( self ) )
1261  {
1262  self RadiusDamage( position, radius, damageMax, damageMin, undefined, meansOfDamage );
1263  }
1264 }
1265 
1266 function ‪burning_death( params )
1267 {
1268  self endon( "death" );
1269  self ‪burning_death_fx();
1270  self.skipFriendlyFireCheck = true; // burning explosion damage friendlies
1271  self thread ‪death_radius_damage_special( 2, "MOD_BURNED" );
1272  self ‪vehicle_death::set_death_model( self.deathmodel, self.modelswapdelay );
1273  self ‪vehicle::do_death_dynents( 3 );
1275 }
1276 
1277 function ‪emped_death( params )
1278 {
1279  self endon( "death" );
1280  self ‪emp_death_fx();
1281  self.skipFriendlyFireCheck = true; // emp explosion damage friendlies
1282  self thread ‪death_radius_damage_special( 2, "MOD_ELECTROCUTED");
1283  self ‪vehicle_death::set_death_model( self.deathmodel, self.modelswapdelay );
1284  self ‪vehicle::do_death_dynents( 2 );
1286 }
1287 
1288 function ‪gibbed_death( params )
1289 {
1290  self endon( "death" );
1293  self ‪vehicle_death::set_death_model( self.deathmodel, self.modelswapdelay );
1296 }
1297 
1298 function ‪default_death( params )
1299 {
1300  self endon( "death" );
1303  self ‪vehicle_death::set_death_model( self.deathmodel, self.modelswapdelay );
1304 
1305  if( isdefined( level.disable_thermal ) )
1306  {
1307  [[level.disable_thermal]]();
1308  }
1309 
1310  waittime = ‪VAL( self.waittime_before_delete, 0 );
1311 
1312  owner = self GetVehicleOwner();
1313  if ( isDefined( owner ) && self isRemoteControl() )
1314  {
1315  // make sure player see some destruction
1316  waittime = max( waittime, 4 );
1317  }
1318 
1319  ‪util::waitForTime( waittime );
1321 }
1322 
1323 function ‪get_death_type( params )
1324 {
1325  if ( self.‪delete_on_death === true )
1326  {
1327  death_type = "default";
1328  }
1329  else
1330  {
1331  death_type = self.death_type;
1332  }
1333 
1334  if ( !isdefined( death_type ) )
1335  {
1336  death_type = params.death_type;
1337  }
1338 
1339  // burning
1340  if( !isdefined( death_type ) && isdefined( self.abnormal_status ) && self.abnormal_status.burning === true )
1341  {
1342  death_type = "burning";
1343  }
1344 
1345  // emped
1346  if ( !isdefined( death_type ) && ( isdefined( self.abnormal_status ) && self.abnormal_status.emped === true ) ||
1347  ( isdefined( params.weapon ) && params.weapon.isEmp ) )
1348  {
1349  death_type = "emped";
1350  }
1351 
1352  return death_type;
1353 }
1354 
1356 {
1357  self endon( "death" );
1358 
1359  if( IsDefined( level.vehicle_destructer_cb ) )
1360  {
1361  [[level.vehicle_destructer_cb]]( self );
1362  }
1363 
1364  if ( self.‪delete_on_death === true )
1365  {
1366  ‪default_death( params );
1368  }
1369  else
1370  {
1371  death_type = ‪VAL( ‪get_death_type( params ), "default" );
1372 
1373  switch( death_type )
1374  {
1375  case "burning": ‪burning_death( params ); break;
1376  case "emped": ‪emped_death( params ); break;
1377  case "gibbed": ‪gibbed_death( params ); break;
1378  default: ‪default_death( params ); break;
1379  }
1380  }
1381 }
1382 
1383 // ----------------------------------------------
1384 
1385 // ----------------------------------------------
1386 // State: scripted
1387 // ----------------------------------------------
1389 {
1390  if ( params.no_clear_movement !== true )
1391  {
1394  if ( HasASM( self ) )
1395  {
1396  self ASMRequestSubstate( "locomotion@movement" );
1397  }
1398  self ResumeSpeed();
1399  }
1400 }
1401 
1402 // no defaultstate_scripted_update() function
1403 
1405 {
1406  if ( params.no_clear_movement !== true )
1407  {
1410  }
1411 }
1412 
1413 // ----------------------------------------------
1414 // State: combat
1415 // ----------------------------------------------
1417 {
1418 }
1419 
1421 {
1422 }
1423 
1424 // ----------------------------------------------
1425 // State: emped
1426 // ----------------------------------------------
1428 {
1429  self ‪vehicle::toggle_tread_fx( false );
1430  self ‪vehicle::toggle_exhaust_fx( false );
1431  self ‪vehicle::toggle_sounds( false );
1432 
1433  params.laserOn = IsLaserOn( self );
1434 
1435  self LaserOff();
1436  self ‪vehicle::lights_off();
1439 
1440  if( IsAirborne( self ) )
1441  {
1442  self SetRotorSpeed( 0 );
1443  }
1444 
1445  if ( !isdefined( self.abnormal_status ) )
1446  {
1447  self.abnormal_status = spawnStruct();
1448  }
1449 
1450  self.abnormal_status.emped = true;
1451  self.abnormal_status.attacker = params.notify_param[1];
1452  self.abnormal_status.inflictor = params.notify_param[2];
1453 
1454  self ‪vehicle::toggle_emp_fx( true );
1455 }
1456 
1458 {
1459  if ( isdefined( self.settings.emp_startup_fx_1 ) && isdefined( self.settings.emp_startup_tag_1 ) )
1460  {
1461  PlayFxOnTag( self.settings.emp_startup_fx_1, self, self.settings.emp_startup_tag_1 );
1462  }
1463 }
1464 
1466 {
1467  self endon ("death");
1468  self endon ("change_state");
1469 
1470  time = params.notify_param[0];
1471  assert( isdefined( time ) );
1472  ‪Cooldown( "emped_timer", time );
1473 
1474  while( !‪IsCooldownReady( "emped_timer" ) )
1475  {
1476  timeLeft = max( ‪GetCooldownLeft( "emped_timer" ), 0.5 );
1477  wait timeLeft;
1478  }
1479 
1480  self.abnormal_status.emped = false;
1481  self ‪vehicle::toggle_emp_fx( false );
1482  self ‪emp_startup_fx();
1483  wait 1;
1484 
1485  self ‪evaluate_connections();
1486 }
1487 
1488 function ‪defaultstate_emped_exit( params )
1489 {
1490  self ‪vehicle::toggle_tread_fx( true );
1491  self ‪vehicle::toggle_exhaust_fx( true );
1492  self ‪vehicle::toggle_sounds( true );
1493 
1494  if ( params.laserOn === true )
1495  {
1496  self LaserOn();
1497  }
1498  self ‪vehicle::lights_on();
1499  if( IsAirborne( self ) )
1500  {
1501  self SetPhysAcceleration( ( 0, 0, 0 ) );
1502  self thread ‪nudge_collision();
1503  self SetRotorSpeed( 1 );
1504  }
1505 }
1506 
1508 {
1509  return true;
1510 }
1511 
1512 // ----------------------------------------------
1513 // State: surge
1514 // ----------------------------------------------
1515 
1517 {
1518 }
1519 
1520 function ‪defaultstate_surge_exit( params )
1521 {
1522 }
1523 
1525 {
1526  self endon( "change_state" );
1527  self endon( "death" );
1528 
1529  if ( !isdefined( self.abnormal_status ) )
1530  {
1531  self.abnormal_status = spawnStruct();
1532  }
1533 
1534  self.abnormal_status.emped = true;
1535  //self.abnormal_status.attacker = params.notify_param[1];
1536  //self.abnormal_status.inflictor = params.notify_param[2];
1537 
1538  pathfailcount = 0;
1539  //blink the lights to make the vehicle look crazy
1540  self thread ‪flash_team_switching_lights();
1541 
1542  targets = GetAITeamArray( "axis", "team3" );
1543  ArrayRemoveValue( targets, self );
1544  closest = ArrayGetClosest( self.origin, targets );
1545 
1546  self SetSpeed( self.settings.surgespeedmultiplier * self.settings.defaultMoveSpeed );
1547 
1548  ‪startTime = GetTime();
1549  self thread ‪swap_team_after_time( params.notify_param[0] );
1550  while( GetTime() - ‪startTime < self.settings.surgetimetolive * 1000 )
1551  {
1552  if ( !IsDefined( closest ) )
1553  {
1554  self ‪detonate( params.notify_param[0] );
1555  }
1556  else
1557  {
1558  foundpath = false;
1559  targetPos = closest.origin + ( 0, 0, 32 );
1560 
1561  if ( IsDefined( targetPos ) )
1562  {
1563  queryResult = PositionQuery_Source_Navigation( targetPos, 0, 64, 35, 5, self );
1564 
1565  foreach ( point in queryResult.data )
1566  {
1567  self.current_pathto_pos = point.origin;
1568 
1569  foundpath = self SetVehGoalPos( self.current_pathto_pos, false, true );
1570  if ( foundpath )
1571  {
1572  self thread ‪path_update_interrupt( closest, params.notify_param[0] );
1573 
1574  pathfailcount = 0;
1575 
1576  self ‪vehicle_ai::waittill_pathing_done( self.settings.surgetimetolive );
1577 
1578  ‪try_detonate( closest, params.notify_param[0] );
1579 
1580  break;
1581  }
1582  waittillframeend;
1583  }
1584  }
1585 
1586  if ( !foundpath )
1587  {
1588  pathfailcount++;
1589  if ( pathfailcount > 10 )
1590  {
1591  self ‪detonate( params.notify_param[0] );
1592  }
1593  }
1594  wait 0.2;
1595  }
1596  }
1597  if( IsAlive( self ) )
1598  {
1599  self ‪detonate( params.notify_param[0] );
1600  }
1601 }
1602 
1603 function ‪path_update_interrupt( closest, attacker )
1604 {
1605  self endon( "death" );
1606  self endon( "change_state" );
1607  self endon( "near_goal" );
1608  self endon( "reached_end_node" );
1609 
1610  wait .1; // sometimes endons may get fired off so wait a bit for the goal to get updated
1611 
1612  while( !self ‪try_detonate( closest, attacker ) )
1613  {
1614  if( isdefined( self.current_pathto_pos ) )
1615  {
1616  if( distance2dSquared( self.current_pathto_pos, self.goalpos ) > ‪SQR( self.goalradius ) )
1617  {
1618  wait 0.5;
1619 
1620  self notify( "near_goal" );
1621  }
1622  }
1623  wait 0.1;
1624  }
1625 }
1626 
1627 function ‪swap_team_after_time( attacker )
1628 {
1629  self endon( "death" );
1630  self endon( "change_state" );
1631 
1632  wait( 0.25 * self.settings.surgetimetolive );
1633  self SetTeam( attacker.team );
1634 }
1635 
1636 function ‪try_detonate( closest, attacker )
1637 {
1638  if ( IsDefined( closest ) && IsAlive( closest ) )
1639  {
1640  if( distanceSquared( closest.origin, self.origin ) < ‪SQR( 80 ) )
1641  {
1642  self ‪detonate( attacker );
1643  return true;
1644  }
1645  }
1646 
1647  return false;
1648 }
1649 
1650 function ‪detonate( attacker )
1651 {
1652  self SetTeam( attacker.team );
1653  self RadiusDamage( self.origin + ( 0, 0, 5 ), self.settings.surgedamageradius, 1500, 1000, attacker, "MOD_EXPLOSIVE" );
1654  if( IsAlive( self ) )
1655  {
1656  self ‪kill();
1657  }
1658 }
1659 
1661 {
1662  self endon( "death" );
1663  self endon( "change_state" );
1664 
1665  while( 1 )
1666  {
1667  self ‪vehicle::lights_off();
1668  wait( 0.1 );
1669  self ‪vehicle::lights_on( "allies" );
1670  wait( 0.1 );
1671  self ‪vehicle::lights_off();
1672  wait( 0.1 );
1673  self ‪vehicle::lights_on( "axis" );
1674  wait( 0.1 );
1675  }
1676 }
1677 
1678 // ----------------------------------------------
1679 // State: off
1680 // ----------------------------------------------
1681 function ‪defaultstate_off_enter( params )
1682 {
1683  self ‪vehicle::toggle_tread_fx( false );
1684  self ‪vehicle::toggle_exhaust_fx( false );
1685  self ‪vehicle::toggle_sounds( false );
1686  self DisableAimAssist();
1687 
1688  params.laserOn = IsLaserOn( self );
1689 
1694 
1695  if( isdefined( level.disable_thermal ) )
1696  {
1697  [[level.disable_thermal]]();
1698  }
1699 
1700  if( IsAirborne( self ) )
1701  {
1702  if ( params.isInitialState !== true && params.no_falling !== true )
1703  {
1704  self SetPhysAcceleration( ( 0, 0, -300 ) );
1705  self thread ‪level_out_for_landing();
1706  }
1707  self SetRotorSpeed( 0 );
1708  }
1709 }
1710 
1711 function ‪defaultstate_off_exit( params )
1712 {
1713  self ‪vehicle::toggle_tread_fx( true );
1714  self ‪vehicle::toggle_exhaust_fx( true );
1715  self ‪vehicle::toggle_sounds( true );
1716  self EnableAimAssist();
1717  if( IsAirborne( self ) )
1718  {
1719  self SetPhysAcceleration( ( 0, 0, 0 ) );
1720  self thread ‪nudge_collision();
1721  self SetRotorSpeed( 1 );
1722  }
1723  if ( params.laserOn === true )
1724  {
1725  self LaserOn();
1726  }
1727 
1728  if( isdefined( level.enable_thermal ) )
1729  {
1730  if( self ‪get_next_state() !== "death" )
1731  {
1732  [[level.enable_thermal]]();
1733  }
1734  }
1735 
1736  self ‪vehicle::lights_on();
1737 }
1738 
1739 // ----------------------------------------------
1740 // State: driving
1741 // ----------------------------------------------
1743 {
1744  params.driver = self GetSeatOccupant( 0 );
1745  assert ( isdefined(params.driver) );
1746 
1747  self DisableAimAssist();
1748 
1749  if ( level.playersDrivingVehiclesBecomeInvulnerable )
1750  {
1751  params.driver EnableInvulnerability();
1752  params.driver.ignoreme = true;
1753  }
1754 
1755  self.turretRotScale = 1;
1756  self.team = params.driver.team;
1757  if ( HasASM( self ) )
1758  {
1759  self ASMRequestSubstate( "locomotion@movement" );
1760  }
1761 
1762  self SetHeliHeightCap( true );
1763 
1766  self CancelAIMove();
1767 
1768  if( isdefined( params.driver ) && !isdefined( self.customDamageMonitor ) )
1769  {
1770  self thread ‪vehicle::monitor_damage_as_occupant( params.driver );
1771  }
1772 }
1773 
1775 {
1776  self EnableAimAssist();
1777  if( isdefined( params.driver ) )
1778  {
1779  params.driver DisableInvulnerability();
1780  params.driver.ignoreme = false;
1781  }
1782  self.turretRotScale = 1;
1783 
1784  self SetHeliHeightCap( false );
1785 
1788 
1789  if( isdefined( params.driver ) )
1790  {
1792  }
1793 }
1794 
1795 // ----------------------------------------------
1796 // State: pain
1797 // ----------------------------------------------
1798 function ‪defaultstate_pain_enter( params )
1799 {
1802 }
1803 
1804 function ‪defaultstate_pain_exit( params )
1805 {
1808 }
1809 
1810 // ----------------------------------------------
1811 // Vehicle AI position finding
1812 
1813 #define POINTS_MAX_DIST 2000
1814 
1815 function ‪CanSeeEnemyFromPosition( position, enemy, sight_check_height )
1816 {
1817  sightCheckOrigin = position + (0,0,sight_check_height);
1818  return sighttracepassed( sightCheckOrigin, enemy.origin + (0,0,30), false, self );
1819 }
1820 
1821 function ‪FindNewPosition( sight_check_height )
1822 {
1823  if( self.goalforced )
1824  {
1825  goalpos = GetClosestPointOnNavMesh( self.goalpos, self.radius * 2, self.radius );
1826  return goalpos;
1827  }
1828 
1829  point_spacing = 90;
1830 
1831  PixBeginEvent( "vehicle_ai_shared::FindNewPosition" );
1832  queryResult = PositionQuery_Source_Navigation( self.origin, 0, ‪POINTS_MAX_DIST, 300/*half height*/, point_spacing, self, point_spacing * 2 );
1833  PixEndEvent();
1834 
1835  // filter
1836  PositionQuery_filter_Random( queryResult, 0, 50 );
1837  PositionQuery_Filter_DistanceToGoal( queryResult, self );
1839 
1840  origin = self.goalpos;
1841 
1842  best_point = undefined;
1843  best_score = -999999;
1844 
1845  if ( isdefined( self.enemy ) )
1846  {
1847  PositionQuery_Filter_Sight( queryResult, self.enemy.origin, self GetEye() - self.origin, self, 0, self.enemy );
1848  self ‪vehicle_ai::PositionQuery_Filter_EngagementDist( queryResult, self.enemy, self.settings.engagementDistMin, self.settings.engagementDistMax );
1849 
1850  if( ‪turret::has_turret( 1 ) )
1851  {
1852  side_turret_enemy = ‪turret::get_target( 1 );
1853  if( isdefined( side_turret_enemy ) && side_turret_enemy != self.enemy )
1854  {
1855  PositionQuery_Filter_Sight( queryResult, side_turret_enemy.origin, (0,0,sight_check_height), self, 20, self, "sight2" );
1856  }
1857  }
1858 
1859  if( ‪turret::has_turret( 2 ) )
1860  {
1861  side_turret_enemy = ‪turret::get_target( 2 );
1862  if( isdefined( side_turret_enemy ) && side_turret_enemy != self.enemy )
1863  {
1864  PositionQuery_Filter_Sight( queryResult, side_turret_enemy.origin, (0,0,sight_check_height), self, 20, self, "sight3" );
1865  }
1866  }
1867 
1868  foreach ( point in queryResult.data )
1869  {
1870  ADD_POINT_SCORE( point, "engagementDist", -point.distAwayFromEngagementArea );
1871 
1872  if( distance2dSquared( self.origin, point.origin ) < 170 * 170 )
1873  {
1874  ADD_POINT_SCORE( point, "tooCloseToSelf", -170 );
1875  }
1876 
1877  if( isdefined( point.sight ) && point.sight )
1878  {
1879  ADD_POINT_SCORE( point, "sight", 250 );
1880  }
1881  if( isdefined( point.sight2 ) && point.sight2 )
1882  {
1883  ADD_POINT_SCORE( point, "sight2", 150 );
1884  }
1885  if( isdefined( point.sight3 ) && point.sight3 )
1886  {
1887  ADD_POINT_SCORE( point, "sight3", 150 );
1888  }
1889 
1890  if ( point.score > best_score )
1891  {
1892  best_score = point.score;
1893  best_point = point;
1894  }
1895  }
1896  }
1897  else
1898  {
1899  foreach ( point in queryResult.data )
1900  {
1901  if( distance2dSquared( self.origin, point.origin ) < 170 * 170 )
1902  {
1903  ADD_POINT_SCORE( point, "tooCloseToSelf", -100 );
1904  }
1905 
1906  if( point.score > best_score )
1907  {
1908  best_score = point.score;
1909  best_point = point;
1910  }
1911  }
1912  }
1913 
1914  self ‪vehicle_ai::PositionQuery_DebugScores( queryResult );
1915 
1916  if( isdefined( best_point ) )
1917  {
1918  origin = best_point.origin;
1919  }
1920 
1921  return origin + (0,0,10);
1922 }
1923 
1924 // ----------------------------------------------
1925 // time handling
1926 // ----------------------------------------------
1927 
1928 // in seconds
1929 // usage: attackStart = GetTime(); if( TimeSince( attackStart ) > 5 ) ...
1930 function ‪TimeSince( startTimeInMilliseconds )
1931 {
1932  return ( GetTime() - startTimeInMilliseconds ) * 0.001;
1933 }
1934 
1936 {
1937  if( !isdefined( self._cooldown ) )
1938  {
1939  self._cooldown = [];
1940  }
1941 }
1942 
1943 function ‪Cooldown( ‪name, time_seconds )
1944 {
1945  ‪CooldownInit();
1946 
1947  self._cooldown[ ‪name ] = GetTime() + time_seconds * 1000;
1948 }
1949 
1951 {
1952  ‪CooldownInit();
1953 
1954  if ( !isdefined( self._cooldown[ ‪name ] ) )
1955  {
1956  self._cooldown[ ‪name ] = GetTime() - 1;
1957  }
1958  return self._cooldown[ ‪name ];
1959 }
1960 
1962 {
1963  ‪CooldownInit();
1964 
1965  return ( ‪GetCooldownTimeRaw( ‪name ) - GetTime() ) * 0.001;
1966 }
1967 
1968 function ‪IsCooldownReady( ‪name, timeForward_seconds )
1969 {
1970  ‪CooldownInit();
1971 
1972  if ( !isdefined( timeForward_seconds ) )
1973  {
1974  timeForward_seconds = 0;
1975  }
1976 
1977  cooldownReadyTime = self._cooldown[ ‪name ];
1978  return !isdefined( cooldownReadyTime ) || GetTime() + timeForward_seconds * 1000 > cooldownReadyTime;
1979 }
1980 
1982 {
1983  ‪CooldownInit();
1984 
1985  self._cooldown[ ‪name ] = GetTime() - 1;
1986 }
1987 
1988 function ‪AddCooldownTime( ‪name, time_seconds )
1989 {
1990  ‪CooldownInit();
1991 
1992  self._cooldown[ ‪name ] = ‪GetCooldownTimeRaw( ‪name ) + time_seconds * 1000;
1993 }
1994 
1996 {
1997  if( isdefined( self._cooldown ) )
1998  {
1999  foreach ( str_name, cooldown in self._cooldown )
2000  {
2001  self._cooldown[ str_name ] = GetTime() - 1;
2002  }
2003  }
2004 }
2005 
2006 // ----------------------------------------------
2007 // debug helpers for position query
2008 // ----------------------------------------------
2009 
2010 function ‪PositionQuery_DebugScores( queryResult )
2011 {
2012  if ( !‪IS_TRUE( GetDvarInt("hkai_debugPositionQuery") ) )
2013  {
2014  return;
2015  }
2016 
2017  foreach( point in queryResult.data )
2018  {
2019  point ‪DebugScore( self );
2020  }
2021 }
2022 
2023 // self == pointStruct
2024 function ‪DebugScore( entity )
2025 {
2026  /#
2027  if ( !isdefined( self._scoreDebug ) )
2028  {
2029  return;
2030  }
2031 
2032  if ( !‪IS_TRUE( GetDvarInt("hkai_debugPositionQuery") ) )
2033  {
2034  return;
2035  }
2036 
2037  step = 10;
2038  count = 1;
2039 
2040  color = (1,0,0);
2041  if ( self.score >= 0 )
2042  {
2043  color = (0,1,0);
2044  }
2045 
2046  RecordStar( self.origin, color );
2047  record3DText( "" + self.score + ":", self.origin - (0,0,step * count), color );
2048  foreach( ‪name, score in self._scoreDebug )
2049  {
2050  count++;
2051  record3DText( ‪name + " " + score, self.origin - (0,0,step * count), color );
2052  }
2053  #/
2054 }
2055 
2056 
2057 function ‪_less_than_val( left, right )
2058 {
2059  if ( !isdefined( left ) )
2060  {
2061  return false;
2062  }
2063  else if ( !isdefined( right ) )
2064  {
2065  return true;
2066  }
2067 
2068  return left < right;
2069 }
2070 
2071 function ‪_cmp_val( left, right, descending )
2072 {
2073  if ( descending )
2074  {
2075  return ‪_less_than_val( right, left );
2076  }
2077  else
2078  {
2079  return ‪_less_than_val( left, right );
2080  }
2081 }
2082 
2083 function ‪_sort_by_score( left, right, descending )
2084 {
2085  return ‪_cmp_val( left.score, right.score, descending );
2086 }
2087 
2088 function ‪PositionQuery_Filter_Random( queryResult, min, max )
2089 {
2090  foreach( point in queryResult.data )
2091  {
2092  score = RandomFloatRange( min, max );
2093  ADD_POINT_SCORE( point, "random", score );
2094  }
2095 }
2096 
2097 function ‪PositionQuery_PostProcess_SortScore( queryResult, descending = true )
2098 {
2099  sorted = array::merge_sort( queryResult.data, &‪_sort_by_score, descending );
2100 
2101  queryResult.data = sorted;
2102 }
2103 
2104 function ‪PositionQuery_Filter_OutOfGoalAnchor( queryResult, tolerance = 1 )
2105 {
2106  foreach( point in queryResult.data )
2107  {
2108  if ( point.distToGoal > tolerance )
2109  {
2110  score = -10000 - point.distToGoal * 10;
2111  ADD_POINT_SCORE( point, "outOfGoalAnchor", score );
2112  }
2113  }
2114 }
2115 
2116 function ‪PositionQuery_Filter_EngagementDist( queryResult, enemy, engagementDistanceMin, engagementDistanceMax )
2117 {
2118  if( !isdefined( enemy ) )
2119  return;
2120 
2121  engagementDistance = ( engagementDistanceMin + engagementDistanceMax ) * 0.5;
2122  half_engagement_width = Abs( engagementDistanceMax - engagementDistance );
2123 
2124  enemy_origin = ‪FLAT_ORIGIN( enemy.origin );
2125 
2126  vec_enemy_to_self = VectorNormalize( ‪FLAT_ORIGIN( self.origin ) - enemy_origin );
2127 
2128  foreach( point in queryResult.data )
2129  {
2130  point.distAwayFromEngagementArea = 0; // <------- result value initialization
2131 
2132  vec_enemy_to_point = ‪FLAT_ORIGIN( point.origin ) - enemy_origin;
2133 
2134 
2135  dist_in_front_of_enemy = VectorDot( vec_enemy_to_point, vec_enemy_to_self );
2136 
2137  if( abs(dist_in_front_of_enemy) < engagementDistanceMin )
2138  {
2139  dist_in_front_of_enemy = -engagementDistanceMin;
2140  }
2141 
2142  dist_away_from_sweet_line = Abs( dist_in_front_of_enemy - engagementDistance );
2143 
2144  if( dist_away_from_sweet_line > half_engagement_width )
2145  {
2146  point.distAwayFromEngagementArea = dist_away_from_sweet_line - half_engagement_width;
2147  }
2148 
2149  too_far_dist = engagementDistanceMax * 1.1;
2150  too_far_dist_sq = ‪SQR( too_far_dist );
2151 
2152  dist_from_enemy_sq = distance2dSquared( point.origin, enemy_origin );
2153 
2154  if( dist_from_enemy_sq > too_far_dist_sq )
2155  {
2156  ratioSq = dist_from_enemy_sq / too_far_dist_sq;
2157  dist = ratioSq * too_far_dist;
2158  dist_outside = dist - too_far_dist;
2159 
2160  if( dist_outside > point.distAwayFromEngagementArea )
2161  {
2162  point.distAwayFromEngagementArea = dist_outside;
2163  }
2164  }
2165  }
2166 }
2167 
2168 function ‪PositionQuery_Filter_DistAwayFromTarget( queryResult, targetArray, distance, tooClosePenalty )
2169 {
2170  if ( !isdefined( targetArray ) || !isArray( targetArray ) )
2171  {
2172  return;
2173  }
2174 
2175  foreach( point in queryResult.data )
2176  {
2177  tooClose = false;
2178  foreach( target in targetArray )
2179  {
2180  origin = undefined;
2181  if ( IsVec( target ) )
2182  {
2183  origin = target;
2184  }
2185  else if ( IsSentient( target ) && IsAlive( target ) )
2186  {
2187  origin = target.origin;
2188  }
2189  else if ( IsEntity( target ) )
2190  {
2191  origin = target.origin;
2192  }
2193 
2194  if ( isdefined( origin ) && distance2dSquared( point.origin, origin ) < ‪SQR( distance ) )
2195  {
2196  tooClose = true;
2197  break;
2198  }
2199  }
2200 
2201  if ( tooClose )
2202  {
2203  ADD_POINT_SCORE( point, "TooCloseToOthers", tooClosePenalty );
2204  }
2205  }
2206 }
2207 
2208 function ‪DistancePointToEngagementHeight( origin, enemy, engagementHeightMin, engagementHeightMax )
2209 {
2210  if( !isdefined( enemy ) )
2211  return undefined;
2212 
2213  ‪result = 0;
2214 
2215  engagementHeight = 0.5 * ( self.settings.engagementHeightMin + self.settings.engagementHeightMax );
2216  half_height = Abs( engagementHeightMax - engagementHeight );
2217 
2218  targetHeight = enemy.origin[2] + engagementHeight;
2219  distFromEngagementHeight = Abs( origin[2] - targetHeight );
2220 
2221  if ( distFromEngagementHeight > half_height )
2222  {
2223  ‪result = distFromEngagementHeight - half_height;
2224  }
2225 
2226  return ‪result;
2227 }
2228 
2229 function ‪PositionQuery_Filter_EngagementHeight( queryResult, enemy, engagementHeightMin, engagementHeightMax )
2230 {
2231  if( !isdefined( enemy ) )
2232  return;
2233 
2234  engagementHeight = 0.5 * ( engagementHeightMin + engagementHeightMax );
2235  half_height = Abs( engagementHeightMax - engagementHeight );
2236 
2237  foreach( point in queryResult.data )
2238  {
2239  point.distEngagementHeight = 0; // <------- result value initialization
2240 
2241  targetHeight = enemy.origin[2] + engagementHeight;
2242  distFromEngagementHeight = Abs( point.origin[2] - targetHeight );
2243 
2244  if ( distFromEngagementHeight > half_height )
2245  {
2246  point.distEngagementHeight = distFromEngagementHeight - half_height;
2247  }
2248  }
2249 }
2250 
2251 function ‪PositionQuery_PostProcess_RemoveOutOfGoalRadius( queryResult, tolerance = 1 )
2252 {
2253  for( i = 0; i < queryResult.data.size; i++ )
2254  {
2255  point = queryResult.data[i];
2256  if ( point.distToGoal > tolerance )
2257  {
2258  ArrayRemoveIndex( queryResult.data, i );
2259  i--;
2260  }
2261  }
2262 }
2263 
2264 function ‪UpdatePersonalThreatBias_AttackerLockedOnToMe( threat_bias, bias_duration, get_perfect_info, update_last_seen ) // self == sentient
2265 {
2266  ‪UpdatePersonalThreatBias_ViaClientFlags( self.locked_on, threat_bias, bias_duration, get_perfect_info, update_last_seen );
2267 }
2268 
2269 function ‪UpdatePersonalThreatBias_AttackerLockingOnToMe( threat_bias, bias_duration, get_perfect_info, update_last_seen ) // self == sentient
2270 {
2271  ‪UpdatePersonalThreatBias_ViaClientFlags( self.locking_on, threat_bias, bias_duration, get_perfect_info, update_last_seen );
2272 }
2273 
2274 function ‪UpdatePersonalThreatBias_ViaClientFlags( client_flags, threat_bias, bias_duration, get_perfect_info = true, update_last_seen = true ) // self == sentient
2275 {
2276  assert( isdefined( client_flags ) );
2277 
2278  remaining_flags_to_process = client_flags;
2279  for ( i = 0; remaining_flags_to_process && i < level.players.size; i++ )
2280  {
2281  attacker = level.players[ i ];
2282  if ( isdefined( attacker ) )
2283  {
2284  client_flag = ( 1 << attacker getEntityNumber() );
2285  if ( client_flag & remaining_flags_to_process )
2286  {
2287  self SetPersonalThreatBias( attacker, Int( threat_bias ), bias_duration );
2288 
2289  if ( get_perfect_info )
2290  self GetPerfectInfo( attacker, update_last_seen );
2291 
2292  remaining_flags_to_process &= ~client_flag;
2293  }
2294  }
2295  }
2296 }
2297 
2298 /#
2299 function ‪UpdatePersonalThreatBias_Bots( threat_bias, bias_duration ) // self == sentient
2300 {
2301  foreach( player in level.players )
2302  {
2303  if (player ‪util::is_bot())
2304  {
2305  self SetPersonalThreatBias( player, Int( threat_bias ), bias_duration );
2306  }
2307  }
2308 }
2309 #/
2310 
2311 //switch target when someone starts to hijack
2313 {
2314  self endon( "death" );
2315 
2316  while( 1 )
2317  {
2318  self waittill( "ccom_lock_being_targeted", hijackingPlayer );
2319 
2320  self GetPerfectInfo( hijackingPlayer, true );
2321 
2322  if( isPlayer( hijackingPlayer ) )
2323  {
2324  self SetPersonalThreatBias( hijackingPlayer, 1500, 4.0 );
2325  }
2326  }
2327 }
2328 
‪call_custom_add_state_callbacks
‪function call_custom_add_state_callbacks()
Definition: vehicle_ai_shared.gsc:1152
‪level_out_for_landing
‪function level_out_for_landing()
Definition: vehicle_ai_shared.gsc:507
‪timeout
‪function timeout(n_time, func, arg1, arg2, arg3, arg4, arg5, arg6)
Definition: util_shared.csc:762
‪set_death_model
‪function set_death_model(sModel, fDelay)
Definition: vehicle_death_shared.gsc:258
‪startTime
‪class AnimationAdjustmentInfoZ startTime
‪lights_off
‪function lights_off(localClientNum)
Definition: vehicle_shared.csc:652
‪Callback_VehicleKilled
‪function Callback_VehicleKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, weapon, vDir, sHitLoc, psOffsetTime)
Definition: vehicle_ai_shared.gsc:1167
‪stop_scripted
‪function stop_scripted(statename)
Definition: vehicle_ai_shared.gsc:894
‪gibbed_death
‪function gibbed_death(params)
Definition: vehicle_ai_shared.gsc:1288
‪is_bot
‪function is_bot()
Definition: util_shared.gsc:2488
‪ClearAllLookingAndTargeting
‪function ClearAllLookingAndTargeting()
Definition: vehicle_ai_shared.gsc:697
‪register_custom_add_state_callback
‪function register_custom_add_state_callback(func)
Definition: vehicle_ai_shared.gsc:1142
‪toggle_emp_fx
‪function toggle_emp_fx(on)
Definition: vehicle_shared.gsc:2863
‪ClearAllMovement
‪function ClearAllMovement(zeroOutSpeed=false)
Definition: vehicle_ai_shared.gsc:707
‪set_damage_fx_level
‪function set_damage_fx_level(damage_level)
Definition: vehicle_shared.gsc:3012
‪immolate
‪function immolate(attacker)
Definition: vehicle_ai_shared.gsc:526
‪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
‪default_death
‪function default_death(params)
Definition: vehicle_ai_shared.gsc:1298
‪get_current_state
‪function get_current_state()
Definition: vehicle_ai_shared.gsc:960
‪SetTurretTarget
‪function SetTurretTarget(target, turretIdx=0, offset=(0, 0, 0))
Definition: vehicle_ai_shared.gsc:277
‪on_death_cleanup
‪function on_death_cleanup()
Definition: vehicle_ai_shared.gsc:1187
‪path_update_interrupt
‪function path_update_interrupt(closest, attacker)
Definition: vehicle_ai_shared.gsc:1603
‪clear
‪function clear(str_flag)
Definition: flag_shared.csc:130
‪defaultstate_off_exit
‪function defaultstate_off_exit(params)
Definition: vehicle_ai_shared.gsc:1711
‪sign
‪function sign(x)
Definition: math_shared.csc:164
‪emp_startup_fx
‪function emp_startup_fx()
Definition: vehicle_ai_shared.gsc:1457
‪RegisterMatchedInterface
‪function RegisterMatchedInterface(archetype, attribute, defaultValue, possibleValues, callbackFunction)
Definition: ai_interface.gsc:143
‪nudge_collision
‪function nudge_collision()
Definition: vehicle_ai_shared.gsc:437
‪TurnOffAllLightsAndLaser
‪function TurnOffAllLightsAndLaser()
Definition: vehicle_ai_shared.gsc:678
‪start_scripted
‪function start_scripted(disable_death_state, no_clear_movement)
Definition: vehicle_ai_shared.gsc:886
‪detonate
‪function detonate(attacker)
Definition: vehicle_ai_shared.gsc:1650
‪PositionQuery_Filter_Random
‪function PositionQuery_Filter_Random(queryResult, min, max)
Definition: vehicle_ai_shared.gsc:2088
‪Javelin_LoseTargetAtRightTime
‪function Javelin_LoseTargetAtRightTime(target)
Definition: vehicle_ai_shared.gsc:314
‪POINTS_MAX_DIST
‪#define POINTS_MAX_DIST
Definition: vehicle_ai_shared.gsc:1813
‪defaultstate_surge_enter
‪function defaultstate_surge_enter(params)
Definition: vehicle_ai_shared.gsc:1516
‪FireTurret
‪function FireTurret(turretIdx, isFake)
Definition: vehicle_ai_shared.gsc:309
‪toggle_exhaust_fx
‪function toggle_exhaust_fx(on)
Definition: vehicle_shared.gsc:3335
‪defaultstate_scripted_enter
‪function defaultstate_scripted_enter(params)
Definition: vehicle_ai_shared.gsc:1388
‪defaultstate_driving_enter
‪function defaultstate_driving_enter(params)
Definition: vehicle_ai_shared.gsc:1742
‪VAL
‪#define VAL(__var, __default)
Definition: shared.gsh:272
‪add_utility_connection
‪function add_utility_connection(from_state_name, to_state_name, checkfunc, defaultScore)
Definition: vehicle_ai_shared.gsc:1028
‪RegisterSharedInterfaceAttributes
‪function RegisterSharedInterfaceAttributes(archetype)
Definition: vehicle_ai_shared.gsc:36
‪DeleteWhenSafe
‪function DeleteWhenSafe(time=4)
Definition: vehicle_death_shared.gsc:1761
‪defaultstate_driving_exit
‪function defaultstate_driving_exit(params)
Definition: vehicle_ai_shared.gsc:1774
‪UpdatePersonalThreatBias_AttackerLockingOnToMe
‪function UpdatePersonalThreatBias_AttackerLockingOnToMe(threat_bias, bias_duration, get_perfect_info, update_last_seen)
Definition: vehicle_ai_shared.gsc:2269
‪get_previous_state
‪function get_previous_state()
Definition: vehicle_ai_shared.gsc:971
‪fire_for_rounds
‪function fire_for_rounds(fireCount, turretIdx, target)
Definition: vehicle_ai_shared.gsc:197
‪defaultstate_death_update
‪function defaultstate_death_update(params)
Definition: vehicle_ai_shared.gsc:1355
‪death_cleanup_level_variables
‪function death_cleanup_level_variables()
Definition: vehicle_death_shared.gsc:1144
‪burning_death_fx
‪function burning_death_fx()
Definition: vehicle_ai_shared.gsc:1218
‪UpdatePersonalThreatBias_ViaClientFlags
‪function UpdatePersonalThreatBias_ViaClientFlags(client_flags, threat_bias, bias_duration, get_perfect_info=true, update_last_seen=true)
Definition: vehicle_ai_shared.gsc:2274
‪GetTargetPos
‪function GetTargetPos(target, geteye)
Definition: vehicle_ai_shared.gsc:115
‪waittill_any_timeout
‪function waittill_any_timeout(n_timeout, string1, string2, string3, string4, string5)
Definition: util_shared.csc:423
‪IS_TRUE
‪#define IS_TRUE(__a)
Definition: shared.gsh:251
‪TurnOn
‪function TurnOn()
Definition: vehicle_ai_shared.gsc:673
‪iff_override
‪function iff_override(owner, time=60)
Definition: vehicle_ai_shared.gsc:592
‪set_state
‪function set_state(name, params)
Definition: vehicle_ai_shared.gsc:916
‪defaultstate_pain_enter
‪function defaultstate_pain_enter(params)
Definition: vehicle_ai_shared.gsc:1798
‪SQR
‪#define SQR(__var)
Definition: shared.gsh:293
‪collision_fx
‪function collision_fx(normal)
Definition: vehicle_ai_shared.gsc:427
‪is_instate
‪function is_instate(statename)
Definition: vehicle_ai_shared.gsc:994
‪get_next_state
‪function get_next_state()
Definition: vehicle_ai_shared.gsc:982
‪has_turret
‪function has_turret(n_index)
Definition: turret_shared.gsc:1756
‪death_radius_damage
‪function death_radius_damage(meansOfDamage="MOD_EXPLOSIVE")
Definition: vehicle_death_shared.gsc:1196
‪shared_callback_damage
‪function shared_callback_damage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal)
Definition: vehicle_ai_shared.gsc:726
‪__fire_for_rounds_internal
‪function __fire_for_rounds_internal(fireCount, fireInterval, turretIdx, target)
Definition: vehicle_ai_shared.gsc:215
‪damage
‪function damage(trap)
Definition: _zm_trap_electric.gsc:116
‪waittill_asm_timeout
‪function waittill_asm_timeout(timeout)
Definition: vehicle_ai_shared.gsc:365
‪emp_death_fx
‪function emp_death_fx()
Definition: vehicle_ai_shared.gsc:1231
‪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
‪defaultstate_combat_enter
‪function defaultstate_combat_enter(params)
Definition: vehicle_ai_shared.gsc:1416
‪GetTargetEyeOffset
‪function GetTargetEyeOffset(target)
Definition: vehicle_ai_shared.gsc:142
‪PositionQuery_Filter_EngagementHeight
‪function PositionQuery_Filter_EngagementHeight(queryResult, enemy, engagementHeightMin, engagementHeightMax)
Definition: vehicle_ai_shared.gsc:2229
‪ClearAllCooldowns
‪function ClearAllCooldowns()
Definition: vehicle_ai_shared.gsc:1995
‪UpdatePersonalThreatBias_AttackerLockedOnToMe
‪function UpdatePersonalThreatBias_AttackerLockedOnToMe(threat_bias, bias_duration, get_perfect_info, update_last_seen)
Definition: vehicle_ai_shared.gsc:2264
‪defaultstate_emped_update
‪function defaultstate_emped_update(params)
Definition: vehicle_ai_shared.gsc:1465
‪FreeWhenSafe
‪function FreeWhenSafe(time=4)
Definition: vehicle_death_shared.gsc:1756
‪waittill_any_ex
‪function waittill_any_ex(...)
Definition: util_shared.csc:270
‪waittill_pathresult
‪function waittill_pathresult(maxtime=0.5)
Definition: vehicle_ai_shared.gsc:347
‪predicted_collision
‪function predicted_collision()
Definition: vehicle_ai_shared.gsc:411
‪monitor_damage_as_occupant
‪function monitor_damage_as_occupant(player)
Definition: vehicle_shared.gsc:3722
‪DistancePointToEngagementHeight
‪function DistancePointToEngagementHeight(origin, enemy, engagementHeightMin, engagementHeightMax)
Definition: vehicle_ai_shared.gsc:2208
‪defaultstate_pain_exit
‪function defaultstate_pain_exit(params)
Definition: vehicle_ai_shared.gsc:1804
‪evaluate_connections
‪function evaluate_connections(eval_func, params)
Definition: vehicle_ai_shared.gsc:921
‪flash_team_switching_lights
‪function flash_team_switching_lights()
Definition: vehicle_ai_shared.gsc:1660
‪_less_than_val
‪function _less_than_val(left, right)
Definition: vehicle_ai_shared.gsc:2057
‪kill
‪function kill(ent_1, str_tag1, ent_2, str_tag2, str_beam_type)
Definition: beam_shared.csc:41
‪defaultstate_emped_enter
‪function defaultstate_emped_enter(params)
Definition: vehicle_ai_shared.gsc:1427
‪TurnOffAllAmbientAnims
‪function TurnOffAllAmbientAnims()
Definition: vehicle_ai_shared.gsc:690
‪UpdatePersonalThreatBias_Bots
‪function UpdatePersonalThreatBias_Bots(threat_bias, bias_duration)
Definition: vehicle_ai_shared.gsc:2299
‪should_burn
‪function should_burn(vehicle, weapon, meansOfDeath, eInflictor, eAttacker)
Definition: vehicle_ai_shared.gsc:803
‪add_interrupt_connection
‪function add_interrupt_connection(from_state_name, to_state_name, on_notify, checkfunc)
Definition: vehicle_ai_shared.gsc:1022
‪Cooldown
‪function Cooldown(name, time_seconds)
Definition: vehicle_ai_shared.gsc:1943
‪init_state_machine_for_role
‪function init_state_machine_for_role(rolename)
Definition: vehicle_ai_shared.gsc:1034
‪death_fx
‪function death_fx()
Definition: _qrdrone.gsc:958
‪death_radius_damage_special
‪function death_radius_damage_special(radiusScale, meansOfDamage)
Definition: vehicle_ai_shared.gsc:1244
‪waittill_pathing_done
‪function waittill_pathing_done(maxtime=15)
Definition: vehicle_ai_shared.gsc:340
‪PositionQuery_DebugScores
‪function PositionQuery_DebugScores(queryResult)
Definition: vehicle_ai_shared.gsc:2010
‪create
‪function create(name, origin, team, shader, alpha, scale)
Definition: objpoints_shared.gsc:32
‪burning_death
‪function burning_death(params)
Definition: vehicle_ai_shared.gsc:1266
‪waitForTime
‪function waitForTime(time)
Definition: util_shared.gsc:2727
‪defaultstate_surge_update
‪function defaultstate_surge_update(params)
Definition: vehicle_ai_shared.gsc:1524
‪PositionQuery_PostProcess_SortScore
‪function PositionQuery_PostProcess_SortScore(queryResult, descending=true)
Definition: vehicle_ai_shared.gsc:2097
‪emped_death
‪function emped_death(params)
Definition: vehicle_ai_shared.gsc:1277
‪set_role
‪function set_role(rolename)
Definition: vehicle_ai_shared.gsc:910
‪PositionQuery_Filter_OutOfGoalAnchor
‪function PositionQuery_Filter_OutOfGoalAnchor(queryResult, tolerance=1)
Definition: vehicle_ai_shared.gsc:2104
‪defaultstate_emped_reenter
‪function defaultstate_emped_reenter(params)
Definition: vehicle_ai_shared.gsc:1507
‪get_state_callbacks_for_role
‪function get_state_callbacks_for_role(rolename, statename)
Definition: vehicle_ai_shared.gsc:944
‪iff_notifyMeInNSec
‪function iff_notifyMeInNSec(time, note)
Definition: vehicle_ai_shared.gsc:586
‪ClearCooldown
‪function ClearCooldown(name)
Definition: vehicle_ai_shared.gsc:1981
‪PositionQuery_PostProcess_RemoveOutOfGoalRadius
‪function PositionQuery_PostProcess_RemoveOutOfGoalRadius(queryResult, tolerance=1)
Definition: vehicle_ai_shared.gsc:2251
‪defaultstate_combat_exit
‪function defaultstate_combat_exit(params)
Definition: vehicle_ai_shared.gsc:1420
‪REGISTER_SYSTEM
‪#define REGISTER_SYSTEM(__sys, __func_init_preload, __reqs)
Definition: shared.gsh:204
‪burning_thread
‪function burning_thread(attacker, inflictor)
Definition: vehicle_ai_shared.gsc:533
‪defaultstate_surge_exit
‪function defaultstate_surge_exit(params)
Definition: vehicle_ai_shared.gsc:1520
‪iff_override_team_switch_behavior
‪function iff_override_team_switch_behavior(team)
Definition: vehicle_ai_shared.gsc:625
‪array
‪function filter array
Definition: array_shared.csc:16
‪blink_lights_for_time
‪function blink_lights_for_time(time)
Definition: vehicle_ai_shared.gsc:648
‪defaultstate_scripted_exit
‪function defaultstate_scripted_exit(params)
Definition: vehicle_ai_shared.gsc:1404
‪waittill_asm_terminated
‪function waittill_asm_terminated()
Definition: vehicle_ai_shared.gsc:356
‪__init__
‪function __init__()
Definition: vehicle_ai_shared.gsc:32
‪GetCooldownTimeRaw
‪function GetCooldownTimeRaw(name)
Definition: vehicle_ai_shared.gsc:1950
‪CooldownInit
‪function CooldownInit()
Definition: vehicle_ai_shared.gsc:1935
‪DebugScore
‪function DebugScore(entity)
Definition: vehicle_ai_shared.gsc:2024
‪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
‪_cmp_val
‪function _cmp_val(left, right, descending)
Definition: vehicle_ai_shared.gsc:2071
‪PositionQuery_Filter_EngagementDist
‪function PositionQuery_Filter_EngagementDist(queryResult, enemy, engagementDistanceMin, engagementDistanceMax)
Definition: vehicle_ai_shared.gsc:2116
‪GetCooldownLeft
‪function GetCooldownLeft(name)
Definition: vehicle_ai_shared.gsc:1961
‪should_emp
‪function should_emp(vehicle, weapon, meansOfDeath, eInflictor, eAttacker)
Definition: vehicle_ai_shared.gsc:765
‪fire_for_time
‪function fire_for_time(totalFireTime, turretIdx, target, intervalScale=1.0)
Definition: vehicle_ai_shared.gsc:164
‪defaultstate_off_enter
‪function defaultstate_off_enter(params)
Definition: vehicle_ai_shared.gsc:1681
‪get_target
‪function get_target(n_index)
Definition: turret_shared.gsc:760
‪swap_team_after_time
‪function swap_team_after_time(attacker)
Definition: vehicle_ai_shared.gsc:1627
‪TimeSince
‪function TimeSince(startTimeInMilliseconds)
Definition: vehicle_ai_shared.gsc:1930
‪try_detonate
‪function try_detonate(closest, attacker)
Definition: vehicle_ai_shared.gsc:1636
‪should_update_damage_fx_level
‪function should_update_damage_fx_level(currentHealth, damage, maxHealth)
Definition: vehicle_shared.gsc:2900
‪owner_in_line_of_fire
‪function owner_in_line_of_fire()
Definition: vehicle_ai_shared.gsc:264
‪toggle_lights_group
‪function toggle_lights_group(groupID, on)
Definition: vehicle_shared.gsc:2833
‪GetEnemyTarget
‪function GetEnemyTarget()
Definition: vehicle_ai_shared.gsc:101
‪PositionQuery_Filter_DistAwayFromTarget
‪function PositionQuery_Filter_DistAwayFromTarget(queryResult, targetArray, distance, tooClosePenalty)
Definition: vehicle_ai_shared.gsc:2168
‪add_state
‪function add_state(name, enter_func, update_func, exit_func)
Definition: vehicle_ai_shared.gsc:1005
‪EntityIsArchetype
‪function EntityIsArchetype(entity, archetype)
Definition: vehicle_ai_shared.gsc:83
‪FLAT_ORIGIN
‪#define FLAT_ORIGIN(__origin)
Definition: shared.gsh:256
‪defaultstate_death_enter
‪function defaultstate_death_enter(params)
Definition: vehicle_ai_shared.gsc:1200
‪do_death_dynents
‪function do_death_dynents(special_status=1)
Definition: vehicle_shared.gsc:2874
‪TurnOff
‪function TurnOff()
Definition: vehicle_ai_shared.gsc:668
‪throw_off_balance
‪function throw_off_balance(damageType, hitPoint, hitDirection, hitLocationInfo)
Definition: vehicle_ai_shared.gsc:391
‪clamp
‪function clamp(val, val_min, val_max)
Definition: math_shared.csc:16
‪death_fire_loop_audio
‪function death_fire_loop_audio()
Definition: vehicle_death_shared.gsc:1747
‪toggle_sounds
‪function toggle_sounds(on)
Definition: vehicle_shared.gsc:3379
‪stop_monitor_damage_as_occupant
‪function stop_monitor_damage_as_occupant()
Definition: vehicle_shared.gsc:3717
‪toggle_burn_fx
‪function toggle_burn_fx(on)
Definition: vehicle_shared.gsc:2868
‪result
‪function result(death, attacker, mod, weapon)
Definition: _zm_aat_blast_furnace.gsc:46
‪delete_on_death
‪function delete_on_death(ent)
Definition: util_shared.gsc:1796
‪name
‪class GroundFx name
‪lights_on
‪function lights_on(localClientNum, team)
Definition: vehicle_shared.csc:404
‪FindNewPosition
‪function FindNewPosition(sight_check_height)
Definition: vehicle_ai_shared.gsc:1821
‪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
‪_sort_by_score
‪function _sort_by_score(left, right, descending)
Definition: vehicle_ai_shared.gsc:2083
‪defaultstate_emped_exit
‪function defaultstate_emped_exit(params)
Definition: vehicle_ai_shared.gsc:1488
‪get_death_type
‪function get_death_type(params)
Definition: vehicle_ai_shared.gsc:1323
‪WAIT_SERVER_FRAME
‪#define WAIT_SERVER_FRAME
Definition: shared.gsh:265
‪AddCooldownTime
‪function AddCooldownTime(name, time_seconds)
Definition: vehicle_ai_shared.gsc:1988