‪Black Ops 3 Source Code Explorer  0.1
‪An script explorer for Black Ops 3 by ZeRoY
archetype_aivsaimelee.gsc
Go to the documentation of this file.
1 /*
2  * AI VS AI MELEE SYSTEM
3  * The AI vs AI Melee system is initiated from the behavior tree.
4  * A set of conditions are used to check if an AI can initiate a scripted melee attack against its enemy.
5  * If these pass, the AI designates itself as the initiator of the setup. The initiator then picks the two animations, and sets AnimScripted on both itself and its enemy from the AIvsAIMeleeAction
6  * Threads are spawned on the entities to handle death during the sequence since it is outside the tree. The loser is killed and the survivor kicks back into the behavior tree.
7 TODO:
8 - Rethink the initiator system to see if melee can be triggered without it
9 - Add support for few more animations, including close range melee without runup
10 - Add more archetypes
11  */
12 
13 #using scripts\codescripts\struct;
14 
15 // COMMON AI SYSTEMS INCLUDES
16 #using scripts\shared\ai_shared;
17 #using scripts\shared\ai\systems\animation_state_machine_utility;
18 #using scripts\shared\ai\systems\behavior_tree_utility;
19 #using scripts\shared\ai\systems\blackboard;
20 #using scripts\shared\ai\archetype_utility;
21 
22 #insert scripts\shared\ai\systems\animation_state_machine.gsh;
23 #insert scripts\shared\ai\systems\blackboard.gsh;
24 #insert scripts\shared\ai\systems\behavior_tree.gsh;
25 #insert scripts\shared\ai\systems\behavior.gsh;
26 #insert scripts\shared\ai\aivsaimelee.gsh;
27 #insert scripts\shared\ai\utility.gsh;
28 #insert scripts\shared\archetype_shared\archetype_shared.gsh;
29 #insert scripts\shared\shared.gsh;
30 
31 function autoexec ‪main()
32 {
33  meleeBundles = ‪GET_MELEE_BUNDLES;
34 
35  level._aivsai_meleeBundles = [];
36 
37  foreach( meleeBundle in meleeBundles )
38  {
39  attacker_archetype = meleeBundle.attackerArchetype;
40  defender_archetype = meleeBundle.defenderArchetype;
41  attacker_variant = meleeBundle.attackerVariant;
42  defender_variant = meleeBundle.defenderVariant;
43 
44  if( !IsDefined( level._aivsai_meleeBundles[ attacker_archetype ] ) )
45  {
46  level._aivsai_meleeBundles[ attacker_archetype ] = [];
47  level._aivsai_meleeBundles[ attacker_archetype ][ defender_archetype ] = [];
48  level._aivsai_meleeBundles[ attacker_archetype ][ defender_archetype ][ attacker_variant ] = [];
49  }
50  else if( !IsDefined( level._aivsai_meleeBundles[ attacker_archetype ][ defender_archetype ] ) )
51  {
52  level._aivsai_meleeBundles[ attacker_archetype ][ defender_archetype ] = [];
53  level._aivsai_meleeBundles[ attacker_archetype ][ defender_archetype ][ attacker_variant ] = [];
54  }
55  else if( !IsDefined( level._aivsai_meleeBundles[ attacker_archetype ][ defender_archetype ][ attacker_variant ] ) )
56  {
57  level._aivsai_meleeBundles[ attacker_archetype ][ defender_archetype ][ attacker_variant ] = [];
58  }
59 
60  level._aivsai_meleeBundles[ attacker_archetype ][ defender_archetype ][ attacker_variant ][ defender_variant ] = meleeBundle;
61  }
62 }
63 
65 {
66  // ------- AI vs AI BEHAVIOR TREE FUNCTIONS -----------//
67  ‪BT_REGISTER_API( "hasAIvsAIEnemy", &‪hasAIvsAIEnemy );
68  ‪BT_REGISTER_API( "decideInitiator", &‪decideInitiator );
69  ‪BT_REGISTER_API( "isInitiator", &‪isInitiator );
70  ‪BT_REGISTER_API( "hasCloseAIvsAIEnemy", &‪hasCloseAIvsAIEnemy );
71  ‪BT_REGISTER_API( "chooseAIvsAIMeleeAnimations", &‪chooseAIvsAIMeleeAnimations );
72 
73  ‪BT_REGISTER_API( "isCloseEnoughForAIvsAIMelee", &‪isCloseEnoughForAIvsAIMelee );
74  ‪BT_REGISTER_API( "hasPotentalAIvsAIMeleeEnemy", &‪hasPotentalAIvsAIMeleeEnemy );
75 
76  ‪BT_REGISTER_ACTION( "AIvsAIMeleeAction", &‪AIvsAIMeleeInitialize, undefined, undefined );
77 }
78 
79 
80 // ------- AI vs AI BEHAVIOR TREE FUNCTIONS -----------//
81 
82 function ‪hasPotentalAIvsAIMeleeEnemy( behaviorTreeEntity )
83 {
84  if( !‪hasAIvsAIEnemy( behaviorTreeEntity ) )
85  return false;
86 
87  if( !‪chooseAIvsAIMeleeAnimations( behaviorTreeEntity ) )
88  return false;
89 
90  if( !‪hasCloseAIvsAIEnemy( behaviorTreeEntity ) )
91  return true;
92 
93  return false;
94 }
95 
96 function ‪isCloseEnoughForAIvsAIMelee( behaviorTreeEntity )
97 {
98  if( !‪hasAIvsAIEnemy( behaviorTreeEntity ) )
99  return false;
100 
101  if( !‪chooseAIvsAIMeleeAnimations( behaviorTreeEntity ) )
102  return false;
103 
104  if( !‪hasCloseAIvsAIEnemy( behaviorTreeEntity ) )
105  return false;
106 
107  return true;
108 }
109 
110 function private ‪shouldAquireMutexOnEnemyForAIvsAIMelee( behaviorTreeEntity )
111 {
112  if( IsPlayer ( behaviorTreeEntity.enemy ) )
113  return false;
114 
115  if( !IsDefined( behaviorTreeEntity.enemy ) )
116  return false;
117 
118  // if already meleeing, then see if the enemy we are meleeing is the same as
119  // the one we want to perform AI vs AI melee
120  if( IsDefined( behaviorTreeEntity.melee ) )
121  {
122  if( IsDefined( behaviorTreeEntity.melee.enemy ) && behaviorTreeEntity.melee.enemy == behaviorTreeEntity.enemy )
123  return true;
124  }
125 
126  if( IsDefined( behaviorTreeEntity.enemy.melee ) )
127  {
128  if( IsDefined( behaviorTreeEntity.enemy.melee.enemy ) && behaviorTreeEntity.enemy.melee.enemy != behaviorTreeEntity )
129  return false;
130  }
131 
132  return true;
133 }
134 
135 function private ‪hasAIvsAIEnemy( behaviorTreeEntity )
136 {
137  enemy = behaviorTreeEntity.enemy;
138 
139  if( GetDvarInt( "disable_aivsai_melee", 0 ) )
140  {
141  /#
142  Record3DText( "AI vs AI Melee Failure: Disable dvar set", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
143  #/
144  return false;
145  }
146  if( !IsDefined( enemy ) )
147  {
148  /#
149  Record3DText( "AI vs AI Melee Failure: No enemy", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
150  #/
151  return false;
152  }
153  if( !( IsAlive( behaviorTreeEntity ) && IsAlive( enemy ) ) )
154  {
155  /#
156  Record3DText( "AI vs AI Melee Failure: A participant may not be alive", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
157  #/
158  return false;
159  }
160 
161  // Only support AI Actors.
162  if( !IsAI( enemy ) || !IsActor( enemy ) )
163  {
164  /#
165  Record3DText( "AI vs AI Melee Failure: Not an actor nor AI", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
166  #/
167  return false;
168  }
169 
170  //for now only human vs human and riotshield and robot
171  //NOTE: dog cannot be on the losing side
172  if( IsDefined( enemy.archetype ) )
173  {
174  if(‪IS_BONUSZM)
175  {
176  if( enemy.archetype != ‪ARCHETYPE_HUMAN && enemy.archetype != ‪ARCHETYPE_HUMAN_RIOTSHIELD && enemy.archetype != ‪ARCHETYPE_ROBOT && enemy.archetype!= ‪ARCHETYPE_ZOMBIE )
177  {
178  /#
179  Record3DText( "AI vs AI Melee Failure: Non human/riotshield/robot/zombie enemy", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
180  #/
181  return false;
182  }
183  }
184  else
185  {
186  if( enemy.archetype != ‪ARCHETYPE_HUMAN && enemy.archetype != ‪ARCHETYPE_HUMAN_RIOTSHIELD && enemy.archetype != ‪ARCHETYPE_ROBOT )
187  {
188  /#
189  Record3DText( "AI vs AI Melee Failure: Non human/riotshield/robot enemy", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
190  #/
191  return false;
192  }
193  }
194  }
195 
196  if( enemy.team == behaviorTreeEntity.team )
197  {
198  /#
199  Record3DText( "AI vs AI Melee Failure: Enemy is on same team", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
200  #/
201  return false;
202  }
203 
204  if( enemy IsRagdoll() )
205  {
206  /#
207  Record3DText( "AI vs AI Melee Failure: Enemy is in ragdoll", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
208  #/
209  return false;
210  }
211 
212  //enemy should be ignored
213  if( ‪IS_TRUE( enemy.ignoreMe ) )
214  {
215  /#
216  Record3DText( "AI vs AI Melee Failure: Enemy should be ignored", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
217  #/
218  return false;
219  }
220  //already a part of an aivsai melee, _ai_melee_markedDead is used to designate that a decision has been made on the conflict
221  if( ‪IS_TRUE( enemy._ai_melee_markedDead ) )
222  {
223  /#
224  Record3DText( "AI vs AI Melee Failure: Enemy engaged in another AI vs AI melee", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
225  #/
226  return false;
227  }
228  //AI should not initiate aivsai melee
229  if ( behaviorTreeEntity ‪ai::has_behavior_attribute( "can_initiateaivsaimelee" ) &&
230  !behaviorTreeEntity ‪ai::get_behavior_attribute( "can_initiateaivsaimelee" ) )
231  {
232  /#
233  Record3DText( "AI vs AI Melee Failure: can_initiateaivsaimelee disabled for attacker", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
234  #/
235  return false;
236  }
237  // Check if the AI can melee.
238  if ( behaviorTreeEntity ‪ai::has_behavior_attribute( "can_melee" ) &&
239  !behaviorTreeEntity ‪ai::get_behavior_attribute( "can_melee" ) )
240  {
241  /#
242  Record3DText( "AI vs AI Melee Failure: can_melee disabled for attacker", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
243  #/
244  return false;
245  }
246 
247  // Check if the enemy can be meleed.
248  if ( enemy ‪ai::has_behavior_attribute( "can_be_meleed" ) &&
249  !enemy ‪ai::get_behavior_attribute( "can_be_meleed" ) )
250  {
251  /#
252  Record3DText( "AI vs AI Melee Failure: can_melee disabled for enemy", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
253  #/
254  return false;
255  }
256 
257  //to check if enemy is close by
258  if( Distance2DSquared( behaviorTreeEntity.origin, enemy.origin ) > ‪CLOSE_ENEMY_DISTANCE_SQ )
259  {
260  /#
261  Record3DText( "AI vs AI Melee Failure: Enemy too far", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
262  #/
263  behaviorTreeEntity._ai_melee_initiator = undefined;
264  return false;
265  }
266  //making sure our enemy is in front of us
267  forwardVec = VectorNormalize( AnglesToForward( behaviorTreeEntity.angles ) );
268  rightVec = VectorNormalize( AnglesToRight( behaviorTreeEntity.angles ) );
269  toEnemyVec = VectorNormalize( enemy.origin - behaviorTreeEntity.origin );
270 
271  fDot = VectorDot( toEnemyVec, forwardVec );
272 
273  if ( fDot < 0 )
274  {
275  /#
276  Record3DText( "AI vs AI Melee Failure: Enemy behind us", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
277  #/
278  return false;
279  }
280  //do not melee an enemy that is already playing scripted anim
281  if( enemy isInScriptedState() )
282  {
283  /#
284  Record3DText( "AI vs AI Melee Failure: Enemy in scripted state", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
285  #/
286  return false;
287  }
288  //only allow standing ai vs ai melee
289  currentStance = ‪Blackboard::GetBlackBoardAttribute( behaviorTreeEntity, ‪STANCE );
290  enemyStance = ‪Blackboard::GetBlackBoardAttribute( enemy, ‪STANCE );
291  if( currentStance != ‪STANCE_STAND || enemyStance != ‪STANCE_STAND )
292  {
293  /#
294  Record3DText( "AI vs AI Melee Failure: attacker or enemy not standing", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
295  #/
296  return false;
297  }
298  //return if we should not acquire melee mutex
299  if( !‪shouldAquireMutexOnEnemyForAIvsAIMelee( behaviorTreeEntity ) )
300  {
301  /#
302  Record3DText( "AI vs AI Melee Failure: should not acquire melee mutex", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
303  #/
304  return false;
305  }
306  //slope test
307  if( Abs( behaviorTreeEntity.origin[2] - behaviorTreeEntity.enemy.origin[2] ) > ‪SYNC_MELEE_SLOPE_THRESHOLD )
308  {
309  /#
310  Record3DText( "AI vs AI Melee Failure: Slope test failed", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
311  #/
312  return false;
313  }
314  //return if there is not enough open space
315  raisedEnemyEntOrigin = ( behaviorTreeEntity.enemy.origin[0], behaviorTreeEntity.enemy.origin[1], behaviorTreeEntity.enemy.origin[2] + ‪MELEE_TRACE_Z_OFFSET );
316  if( !behaviorTreeEntity MayMoveToPoint( raisedEnemyEntOrigin, false, true, behaviorTreeEntity.enemy ) )
317  {
318  /#
319  Record3DText( "AI vs AI Melee Failure: Trace failed", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
320  #/
321  return false;
322  }
323  //cannot kill an AI that is not supposed to die
324  if( IsDefined( enemy.allowDeath ) && !enemy.allowDeath )
325  {
326  if( IsDefined( behaviorTreeEntity.allowDeath ) && !behaviorTreeEntity.allowDeath )
327  {
328  /#
329  Record3DText( "AI vs AI Melee Failure: Enemy and attacker cannot die", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
330  #/
331 
332  self notify( "failed_melee_mbs", enemy );
333  return false;
334  }
335  behaviorTreeEntity._ai_melee_attacker_loser = true;
336  return true;
337  }
338  return true;
339 }
340 
341 function private ‪decideInitiator( behaviorTreeEntity )
342 {
343  if( !IsDefined( behaviorTreeEntity._ai_melee_initiator ) )
344  {
345  if( !IsDefined( behaviorTreeEntity.enemy._ai_melee_initiator ) )
346  {
347  behaviorTreeEntity._ai_melee_initiator = true;
348  return true;
349  }
350  }
351  return false;
352 }
353 
354 function private ‪isInitiator( behaviorTreeEntity )
355 {
356  if( !‪IS_TRUE( behaviorTreeEntity._ai_melee_initiator ) )
357  {
358  return false;
359  }
360  return true;
361 }
362 
363 function private ‪hasCloseAIvsAIEnemy( behaviorTreeEntity )
364 {
365  if( !( IsDefined( behaviorTreeEntity._ai_melee_animname ) && IsDefined( behaviorTreeEntity.enemy._ai_melee_animname ) ) )
366  {
367  /#
368  Record3DText( "AI vs AI Melee Failure: anim pair not found", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
369  #/
370  return false;
371  }
372 
373  animationStartOrigin = GetStartOrigin( behaviorTreeEntity.enemy GetTagOrigin( ‪TAG_SYNC ), behaviorTreeEntity.enemy GetTagAngles( ‪TAG_SYNC ), behaviorTreeEntity._ai_melee_animname );
374 
375 /#
376  Record3DText("Threshold Initiator distance from AnimStart = " + Sqrt( ‪SYNCED_MELEE_DIST_THRESHOLD_SQ ), behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
377  Record3DText("Initiatior distance from AnimStart = " + Distance( animationStartOrigin, behaviorTreeEntity.origin ), behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
378  RecordCircle( behaviorTreeEntity.enemy GetTagOrigin( ‪TAG_SYNC ), 8, ‪RED, "Animscript", behaviorTreeEntity );
379 
380  RecordCircle( animationStartOrigin, 8, ‪ORANGE, "Animscript", behaviorTreeEntity );
381  RecordLine( animationStartOrigin, behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity );
382 #/
383 
384  if( Distance2DSquared( behaviorTreeEntity.origin, animationStartOrigin ) <= ‪SYNCED_MELEE_DIST_THRESHOLD_SQ )
385  {
386  return true;
387  }
388 
389  //check predicted position of entity, and extrapolate position for back anims
390  if( behaviorTreeEntity HasPath() )
391  {
392  selfPredictedPos = behaviorTreeEntity.origin;
393  moveAngle = behaviorTreeEntity.angles[1] + behaviorTreeEntity getMotionAngle();
394  selfPredictedPos += (cos( moveAngle ), sin( moveAngle ), 0) * 200.0 * 0.2; //0.2 is the lookahead time
395 
396 /#
397  Record3DText("Initiator predicted distance from AnimStart= " + Distance( selfPredictedPos, animationStartOrigin ), behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
398 #/
399 
400  if( Distance2DSquared( selfPredictedPos, animationStartOrigin ) <= ‪SYNCED_MELEE_DIST_THRESHOLD_SQ )
401  {
402  return true;
403  }
404  }
405 
406  return false;
407 }
408 
409 function private ‪chooseAIvsAIMeleeAnimations( behaviorTreeEntity )
410 {
411  anglesToEnemy = VectorToAngles( behaviorTreeEntity.enemy.origin - behaviorTreeEntity.origin );
412  yawToEnemy = AngleClamp180( behaviorTreeEntity.enemy.angles[ 1 ] - anglesToEnemy[ 1 ] );
413 
414  /#
415  //record yaw to enemy for debugging
416  Record3DText("Yaw to enemy = " + abs( yawToEnemy ), behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
417  #/
418 
419  //clear out old anims
420  behaviorTreeEntity._ai_melee_animname = undefined;
421  behaviorTreeEntity.enemy._ai_melee_animname = undefined;
422 
423  //get the scriptbundle for these archetypes
424  attacker_variant = ‪chooseArchetypeVariant( behaviorTreeEntity );
425  defender_variant = ‪chooseArchetypeVariant( behaviorTreeEntity.enemy );
426 
427  //TODO: Change this into an assert and ensure a bundle is always available if we get here
428  if( !‪AIvsAIMeleeBundleExists( behaviorTreeEntity, attacker_variant, defender_variant ) )
429  {
430  /#
431  Record3DText( "AI vs AI melee Failure: Bundle does not exist for " + behaviorTreeEntity.archetype + " " + behaviorTreeEntity.enemy.archetype + " " + attacker_variant + " " + defender_variant, behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
432  #/
433  return false;
434  }
435 
436  animBundle = level._aivsai_meleeBundles[ behaviorTreeEntity.archetype ][ behaviorTreeEntity.enemy.archetype ][ attacker_variant ][ defender_variant ];
437 
438  /#
439  if( ‪IS_TRUE( behaviorTreeEntity._ai_melee_attacker_loser ) )
440  {
441  Record3DText( "AI vs AI Melee: Attacker selected as loser", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
442  }
443  #/
444 
445  foundAnims = false;
446  possibleMelees = [];
447 
448  if( abs( yawToEnemy ) > ‪FRONT_ANGLE_THRESHOLD )
449  {
450  if( IsDefined( behaviorTreeEntity.__forceAIFlipMelee ) )
451  {
452  possibleMelees[possibleMelees.size] = &‪chooseAIVsAIMeleeFrontFlipAnimations;
453  }
454  else if( IsDefined( behaviorTreeEntity.__forceAIWrestleMelee ) )
455  {
456  possibleMelees[possibleMelees.size] = &‪chooseAIVsAIMeleeFrontWrestleAnimations;
457  }
458  else
459  {
460  possibleMelees[possibleMelees.size] = &‪chooseAIVsAIMeleeFrontFlipAnimations;
461  possibleMelees[possibleMelees.size] = &‪chooseAIVsAIMeleeFrontWrestleAnimations;
462  }
463  }
464  else if( abs( yawToEnemy ) < ‪BACK_ANGLE_THRESHOLD )
465  {
466  possibleMelees[possibleMelees.size] = &‪chooseAIVsAIMeleeBackAnimations;
467  }
468  else
469  {
470  //possibly left/right directional
471  rightVec = VectorNormalize( AnglesToRight( behaviorTreeEntity.enemy.angles ) );
472  toAttackerVec = VectorNormalize( behaviorTreeEntity.origin - behaviorTreeEntity.enemy.origin );
473 
474  rDot = VectorDot( toAttackerVec, rightVec );
475 
476  if( rDot > 0 )
477  {
478  //coming in from the right
479  possibleMelees[possibleMelees.size] = &‪chooseAIVsAIMeleeRightAnimations;
480  }
481  else
482  {
483  //coming in from the left
484  possibleMelees[possibleMelees.size] = &‪chooseAIVsAIMeleeLeftAnimations;
485  }
486  }
487 
488  if( possibleMelees.size > 0 )
489  {
490  [[‪RANDOM( possibleMelees )]]( behaviorTreeEntity, animBundle );
491  }
492 
493  if( IsDefined( behaviorTreeEntity._ai_melee_animname ) )
494  {
495  ‪Debug_ChosenMeleeAnimations( behaviorTreeEntity );
496  return true;
497  }
498 
499  return false;
500 }
501 
502 function private ‪chooseArchetypeVariant( entity )
503 {
504  if( entity.archetype == ‪ARCHETYPE_ROBOT )
505  {
506  robot_state = entity ‪ai::get_behavior_attribute( "rogue_control" );
507 
508  if( IsInArray( ‪array( "forced_level_1", "level_1", "level_0" ), robot_state ) )
509  {
510  return ‪REGULAR_VARIANT;
511  }
512  if( IsInArray( ‪array( "forced_level_2", "level_2", "level_3", "forced_level_3" ), robot_state ) )
513  {
514  return ‪MELEE_VARIANT;
515  }
516  }
517 
518  return ‪REGULAR_VARIANT;
519 }
520 
521 function private ‪AIvsAIMeleeBundleExists( behaviorTreeEntity, attacker_variant, defender_variant )
522 {
523  if( !IsDefined( level._aivsai_meleeBundles[ behaviorTreeEntity.archetype ] ) )
524  {
525  return false;
526  }
527  else if( !IsDefined( level._aivsai_meleeBundles[ behaviorTreeEntity.archetype ][ behaviorTreeEntity.enemy.archetype ] ) )
528  {
529  return false;
530  }
531  else if( !IsDefined( level._aivsai_meleeBundles[ behaviorTreeEntity.archetype ][ behaviorTreeEntity.enemy.archetype ][ attacker_variant ] ) )
532  {
533  return false;
534  }
535  else if( !IsDefined( level._aivsai_meleeBundles[ behaviorTreeEntity.archetype ][ behaviorTreeEntity.enemy.archetype ][ attacker_variant ][ defender_variant ] ) )
536  {
537  return false;
538  }
539  return true;
540 }
541 
542 function ‪AIvsAIMeleeInitialize( behaviorTreeEntity, asmStateName )
543 {
544  behaviorTreeEntity.blockingPain = true;
545  behaviorTreeEntity.enemy.blockingPain = true;
546  //acquire the mutex since we are about to melee
547  ‪AiUtility::meleeAcquireMutex( behaviorTreeEntity );
548  //keep a reference to the other person incase one of the participants does not have the other selected as enemy (especially in case of back melees)
549  behaviorTreeEntity._ai_melee_opponent = behaviorTreeEntity.enemy;
550  behaviorTreeEntity.enemy._ai_melee_opponent = behaviorTreeEntity;
551 
552  //the winner will call playScriptedMeleeAnimations, since this function waits on winner's victory before resetting some flags
553  if( ‪IS_TRUE( behaviorTreeEntity._ai_melee_attacker_loser ) )
554  {
555  behaviorTreeEntity._ai_melee_markedDead = true;
556  behaviorTreeEntity.enemy thread ‪playScriptedMeleeAnimations();
557  }
558  else
559  {
560  behaviorTreeEntity.enemy._ai_melee_markedDead = true;
561  behaviorTreeEntity thread ‪playScriptedMeleeAnimations();
562  }
563 
564  return ‪BHTN_RUNNING;
565 }
566 
567 // ------- AI vs AI INTERNAL FUNCTIONS -----------//
568 
569 //Threaded call to animscripted so that anim is not blended out too early
571 {
572  self endon("death");
573 
574  Assert( IsDefined( self._ai_melee_opponent ) );
575  opponent = self._ai_melee_opponent;
576 
577  // In rare cases the enemy or AI could have died.
578  if ( !( IsAlive( self ) && IsAlive( opponent ) ) )
579  {
580  /#
581  Record3DText( "AI vs AI Melee Failsafe Failure: A participant may not be alive", self.origin, ‪ORANGE, "Animscript", self, 0.4 );
582  #/
583  return false;
584  }
585 
586  if( ‪IS_TRUE( opponent._ai_melee_attacker_loser ) )
587  {
588  //link to the defender
589 
590  //call animscripted on both
591  opponent AnimScripted( "aivsaimeleeloser", self GetTagOrigin(‪TAG_SYNC), self GetTagAngles(‪TAG_SYNC), opponent._ai_melee_animname, "normal", undefined, 1.0, ‪SYNC_MELEE_BLEND_TIME, ‪SYNC_MELEE_LERP_TIME );
592  self AnimScripted( "aivsaimeleewinner", self GetTagOrigin(‪TAG_SYNC), self GetTagAngles(‪TAG_SYNC), self._ai_melee_animname, "normal", undefined, 1.0, ‪SYNC_MELEE_BLEND_TIME, ‪SYNC_MELEE_LERP_TIME );
593 
594  /#
595  RecordCircle( self GetTagOrigin(‪TAG_SYNC), 2, ‪ORANGE, "Animscript" );
596  RecordLine( self GetTagOrigin(‪TAG_SYNC), opponent.origin, ‪ORANGE, "Animscript" );
597  #/
598  }
599  else
600  {
601  //link to the defender
602 
603  //call animscripted on both
604  self AnimScripted( "aivsaimeleewinner", opponent GetTagOrigin(‪TAG_SYNC), opponent GetTagAngles(‪TAG_SYNC), self._ai_melee_animname, "normal", undefined, 1.0, ‪SYNC_MELEE_BLEND_TIME, ‪SYNC_MELEE_LERP_TIME );
605  opponent AnimScripted( "aivsaimeleeloser", opponent GetTagOrigin(‪TAG_SYNC), opponent GetTagAngles(‪TAG_SYNC), opponent._ai_melee_animname, "normal", undefined, 1.0, ‪SYNC_MELEE_BLEND_TIME, ‪SYNC_MELEE_LERP_TIME );
606 
607  /#
608  RecordCircle( opponent GetTagOrigin(‪TAG_SYNC), 2, ‪ORANGE, "Animscript" );
609  RecordLine( opponent GetTagOrigin(‪TAG_SYNC), self.origin, ‪ORANGE, "Animscript" );
610  #/
611  }
612  //spawn a thread that kills off the loser at the end
613  opponent thread ‪handleDeath( opponent._ai_melee_animname, self );
614 
615  if ( GetDvarInt( "tu1_aivsaiMeleeDisableGib", 1 ) )
616  {
617  // TU1 fix to prevent vertex stretching when robots are killed in AI vs AI melee.
618 
619  // Don't gib robots upon an AI vs AI melee death, since robots don't have
620  // enough time post death to correctly hide/show tags and swap models.
621  if ( opponent ‪ai::has_behavior_attribute( "can_gib" ) )
622  {
623  opponent ‪ai::set_behavior_attribute( "can_gib", false );
624  }
625  }
626 
627  //process interrupted deaths
628  self thread ‪processInterruptedDeath();
629  opponent thread ‪processInterruptedDeath();
630 
631  //wait for winner to finish
632  self waittillmatch( "aivsaimeleewinner", "end" );
633 
634  self.fixedLinkYawOnly = false;
635 
636  //flag cleanup
637  //release the melee mutex
639  //remove knife model if necessary
640  if( ‪IS_TRUE( self._ai_melee_attachedKnife ) )
641  {
642  self Detach( ‪KNIFE_MODEL, "TAG_WEAPON_LEFT" );
643  self._ai_melee_attachedKnife = false;
644  }
645  self.blockingPain = false;
646  self._ai_melee_initiator = undefined;
647  self notify("meleeCompleted");
648 
649  self PathMode( "move delayed", true, 3 );
650 }
651 
652 function private ‪chooseAIVsAIMeleeFrontFlipAnimations( behaviorTreeEntity, animBundle )
653 {
654  /#
655  Record3DText( "AI vs AI Melee: Direction is front", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
656  #/
657 
658  assert( IsDefined( animBundle ) );
659 
660  if( ‪IS_TRUE( behaviorTreeEntity._ai_melee_attacker_loser ) )
661  {
662  behaviorTreeEntity._ai_melee_animname = animBundle.attackerLoserFrontAnim;
663  behaviorTreeEntity.enemy._ai_melee_animname = animBundle.defenderWinnerFrontAnim;
664  }
665  else
666  {
667  behaviorTreeEntity._ai_melee_animname = animBundle.attackerFrontAnim;
668  behaviorTreeEntity.enemy._ai_melee_animname = animBundle.victimFrontAnim;
669  }
670 
671  behaviorTreeEntity._ai_melee_animtype = ‪FRONT_FLIP_TYPE;
672  behaviorTreeEntity.enemy._ai_melee_animtype = ‪FRONT_FLIP_TYPE;
673 }
674 
675 function private ‪chooseAIVsAIMeleeFrontWrestleAnimations( behaviorTreeEntity, animBundle )
676 {
677  /#
678  Record3DText( "AI vs AI Melee: Direction is front", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
679  #/
680 
681  assert( IsDefined( animBundle ) );
682 
683  if( ‪IS_TRUE( behaviorTreeEntity._ai_melee_attacker_loser ) )
684  {
685  behaviorTreeEntity._ai_melee_animname = animBundle.attackerLoserAlternateFrontAnim;
686  behaviorTreeEntity.enemy._ai_melee_animname = animBundle.defenderWinnerAlternateFrontAnim;
687  }
688  else
689  {
690  behaviorTreeEntity._ai_melee_animname = animBundle.attackerAlternateFrontAnim;
691  behaviorTreeEntity.enemy._ai_melee_animname = animBundle.victimAlternateFrontAnim;
692  }
693 
694  behaviorTreeEntity._ai_melee_animtype = ‪FRONT_WRESTLE_TYPE;
695  behaviorTreeEntity.enemy._ai_melee_animtype = ‪FRONT_WRESTLE_TYPE;
696 }
697 
698 function private ‪chooseAIVsAIMeleeBackAnimations( behaviorTreeEntity, animBundle )
699 {
700  /#
701  Record3DText( "AI vs AI Melee: Direction is back", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
702  #/
703 
704  assert( IsDefined( animBundle ) );
705 
706  if( ‪IS_TRUE( behaviorTreeEntity._ai_melee_attacker_loser ) )
707  {
708  behaviorTreeEntity._ai_melee_animname = animBundle.attackerLoserBackAnim;
709  behaviorTreeEntity.enemy._ai_melee_animname = animBundle.defenderWinnerBackAnim;
710  }
711  else
712  {
713  behaviorTreeEntity._ai_melee_animname = animBundle.attackerBackAnim;
714  behaviorTreeEntity.enemy._ai_melee_animname = animBundle.victimBackAnim;
715  }
716 
717  behaviorTreeEntity._ai_melee_animtype = ‪BACK_TYPE;
718  behaviorTreeEntity.enemy._ai_melee_animtype = ‪BACK_TYPE;
719 }
720 
721 function private ‪chooseAIVsAIMeleeRightAnimations( behaviorTreeEntity, animBundle )
722 {
723  /#
724  Record3DText( "AI vs AI Melee: Direction is right", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
725  #/
726 
727  assert( IsDefined( animBundle ) );
728 
729  if( ‪IS_TRUE( behaviorTreeEntity._ai_melee_attacker_loser ) )
730  {
731  behaviorTreeEntity._ai_melee_animname = animBundle.attackerLoserRightAnim;
732  behaviorTreeEntity.enemy._ai_melee_animname = animBundle.defenderWinnerRightAnim;
733  }
734  else
735  {
736  behaviorTreeEntity._ai_melee_animname = animBundle.attackerRightAnim;
737  behaviorTreeEntity.enemy._ai_melee_animname = animBundle.victimRightAnim;
738  }
739 
740  behaviorTreeEntity._ai_melee_animtype = ‪RIGHT_TYPE;
741  behaviorTreeEntity.enemy._ai_melee_animtype = ‪RIGHT_TYPE;
742 }
743 
744 function private ‪chooseAIVsAIMeleeLeftAnimations( behaviorTreeEntity, animBundle )
745 {
746  /#
747  Record3DText( "AI vs AI Melee: Direction is left", behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
748  #/
749 
750  assert( IsDefined( animBundle ) );
751 
752  if( ‪IS_TRUE( behaviorTreeEntity._ai_melee_attacker_loser ) )
753  {
754  behaviorTreeEntity._ai_melee_animname = animBundle.attackerLoserLeftAnim;
755  behaviorTreeEntity.enemy._ai_melee_animname = animBundle.defenderWinnerLeftAnim;
756  }
757  else
758  {
759  behaviorTreeEntity._ai_melee_animname = animBundle.attackerLeftAnim;
760  behaviorTreeEntity.enemy._ai_melee_animname = animBundle.victimLeftAnim;
761  }
762 
763  behaviorTreeEntity._ai_melee_animtype = ‪LEFT_TYPE;
764  behaviorTreeEntity.enemy._ai_melee_animtype = ‪LEFT_TYPE;
765 }
766 
767 function private ‪Debug_ChosenMeleeAnimations( behaviorTreeEntity )
768 {
769  /#
770  if( IsDefined( behaviorTreeEntity._ai_melee_animname ) && IsDefined( behaviorTreeEntity.enemy._ai_melee_animname ) )
771  {
772  Record3DText("AI vs AI chosen anim = " + behaviorTreeEntity._ai_melee_animname, behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
773  Record3DText("AI vs AI enemy chosen anim = " + behaviorTreeEntity.enemy._ai_melee_animname, behaviorTreeEntity.origin, ‪ORANGE, "Animscript", behaviorTreeEntity, 0.4 );
774  }
775  #/
776 }
777 
778 //Need this to handle deaths for the loser
779 function ‪handleDeath( animationName, attacker )
780 {
781  self endon("death");
782  self endon("interruptedDeath");
783 
784  self.skipDeath = true;
785  self.diedInScriptedAnim = true;
786 
787  totalTime = GetAnimLength( animationName );
788  wait( totalTime - 0.2 );
789 
790  self ‪killWrapper( attacker );
791 }
792 
793 //To deal with deaths during animscripted
795 {
796  self endon("meleeCompleted");
797 
798  Assert( IsDefined( self._ai_melee_opponent ) );
799  opponent = self._ai_melee_opponent;
800  //do not interrupt and kill someone that is not supposed to die
801  if( !‪IS_TRUE( self.allowDeath ) )
802  {
803  return;
804  }
805  self waittill("death");
806 
807  //cleanup knife if required
808  if( IsDefined( self ) && ‪IS_TRUE( self._ai_melee_attachedKnife ) )
809  {
810  self Detach( ‪KNIFE_MODEL, "TAG_WEAPON_LEFT" );
811  }
812 
813  if( IsAlive( opponent ) )
814  {
815  //loser always dies
816  if( ‪IS_TRUE( opponent._ai_melee_markedDead ) )
817  {
818  opponent.diedInScriptedAnim = true;
819  opponent.skipDeath = true;
820  opponent notify ( "interruptedDeath" );
821  opponent notify ( "meleeCompleted" );
822 
823  opponent StopAnimScripted();
824  opponent ‪killWrapper();
825  opponent StartRagDoll();
826  }
827  else
828  {
829  opponent._ai_melee_initiator = undefined;
830  opponent.blockingPain = false;
831  opponent._ai_melee_markedDead = undefined;
832  opponent.skipDeath = false;
833  opponent.diedInScriptedAnim = false;
834  //release the melee mutex for opponent
836  opponent notify( "interruptedDeath" );
837  opponent notify( "meleeCompleted" );
838  opponent StopAnimScripted();
839  }
840  }
841 
842  if( IsDefined( self ) )
843  {
844  self.diedInScriptedAnim = true;
845  self.skipDeath = true;
846  self notify ( "interruptedDeath" );
847 
848  self StopAnimScripted();
849  self ‪killWrapper();
850  self StartRagDoll();
851  }
852 }
853 
854 function ‪killWrapper( attacker )
855 {
856  if( IsDefined( self.overrideActorDamage ) )
857  {
858  self.overrideActorDamage = undefined;
859  }
860 
861  self.TokubetsuKogekita = undefined; //this var reduces damage for certain cybercom affected robots
862 
863  //guarding against team changes
864  if( IsDefined( attacker ) && self.team != attacker.team )
865  {
866  self ‪kill(self.origin,attacker);
867  }
868  else
869  {
870  self ‪kill();
871  }
872 }
‪TAG_SYNC
‪#define TAG_SYNC
Definition: aivsaimelee.gsh:6
‪KNIFE_MODEL
‪#define KNIFE_MODEL
Definition: aivsaimelee.gsh:2
‪BT_REGISTER_API
‪#define BT_REGISTER_API(name, function)
Definition: behavior.gsh:1
‪RIGHT_TYPE
‪#define RIGHT_TYPE
Definition: aivsaimelee.gsh:18
‪chooseAIVsAIMeleeLeftAnimations
‪function private chooseAIVsAIMeleeLeftAnimations(behaviorTreeEntity, animBundle)
Definition: archetype_aivsaimelee.gsc:744
‪SYNCED_MELEE_DIST_THRESHOLD_SQ
‪#define SYNCED_MELEE_DIST_THRESHOLD_SQ
Definition: aivsaimelee.gsh:8
‪BACK_ANGLE_THRESHOLD
‪#define BACK_ANGLE_THRESHOLD
Definition: aivsaimelee.gsh:5
‪BHTN_RUNNING
‪#define BHTN_RUNNING
Definition: behavior_tree.gsh:9
‪SYNC_MELEE_BLEND_TIME
‪#define SYNC_MELEE_BLEND_TIME
Definition: aivsaimelee.gsh:12
‪SYNC_MELEE_SLOPE_THRESHOLD
‪#define SYNC_MELEE_SLOPE_THRESHOLD
Definition: aivsaimelee.gsh:10
‪hasCloseAIvsAIEnemy
‪function private hasCloseAIvsAIEnemy(behaviorTreeEntity)
Definition: archetype_aivsaimelee.gsc:363
‪main
‪function autoexec main()
Definition: archetype_aivsaimelee.gsc:31
‪killWrapper
‪function killWrapper(attacker)
Definition: archetype_aivsaimelee.gsc:854
‪RANDOM
‪#define RANDOM(__array)
Definition: shared.gsh:302
‪IS_TRUE
‪#define IS_TRUE(__a)
Definition: shared.gsh:251
‪GetBlackBoardAttribute
‪function GetBlackBoardAttribute(entity, attributeName)
Definition: blackboard.gsc:32
‪chooseArchetypeVariant
‪function private chooseArchetypeVariant(entity)
Definition: archetype_aivsaimelee.gsc:502
‪decideInitiator
‪function private decideInitiator(behaviorTreeEntity)
Definition: archetype_aivsaimelee.gsc:341
‪RED
‪#define RED
Definition: shared.gsh:175
‪hasAIvsAIEnemy
‪function private hasAIvsAIEnemy(behaviorTreeEntity)
Definition: archetype_aivsaimelee.gsc:135
‪chooseAIVsAIMeleeFrontWrestleAnimations
‪function private chooseAIVsAIMeleeFrontWrestleAnimations(behaviorTreeEntity, animBundle)
Definition: archetype_aivsaimelee.gsc:675
‪STANCE
‪#define STANCE
Definition: blackboard.gsh:36
‪chooseAIvsAIMeleeAnimations
‪function private chooseAIvsAIMeleeAnimations(behaviorTreeEntity)
Definition: archetype_aivsaimelee.gsc:409
‪isCloseEnoughForAIvsAIMelee
‪function isCloseEnoughForAIvsAIMelee(behaviorTreeEntity)
Definition: archetype_aivsaimelee.gsc:96
‪STANCE_STAND
‪#define STANCE_STAND
Definition: blackboard.gsh:273
‪has_behavior_attribute
‪function has_behavior_attribute(attribute)
Definition: ai_shared.gsc:198
‪AIvsAIMeleeInitialize
‪function AIvsAIMeleeInitialize(behaviorTreeEntity, asmStateName)
Definition: archetype_aivsaimelee.gsc:542
‪BACK_TYPE
‪#define BACK_TYPE
Definition: aivsaimelee.gsh:17
‪FRONT_WRESTLE_TYPE
‪#define FRONT_WRESTLE_TYPE
Definition: aivsaimelee.gsh:15
‪FRONT_FLIP_TYPE
‪#define FRONT_FLIP_TYPE
Definition: aivsaimelee.gsh:16
‪RegisterAIvsAIMeleeBehaviorFunctions
‪function RegisterAIvsAIMeleeBehaviorFunctions()
Definition: archetype_aivsaimelee.gsc:64
‪chooseAIVsAIMeleeBackAnimations
‪function private chooseAIVsAIMeleeBackAnimations(behaviorTreeEntity, animBundle)
Definition: archetype_aivsaimelee.gsc:698
‪Debug_ChosenMeleeAnimations
‪function private Debug_ChosenMeleeAnimations(behaviorTreeEntity)
Definition: archetype_aivsaimelee.gsc:767
‪AIvsAIMeleeBundleExists
‪function private AIvsAIMeleeBundleExists(behaviorTreeEntity, attacker_variant, defender_variant)
Definition: archetype_aivsaimelee.gsc:521
‪kill
‪function kill(ent_1, str_tag1, ent_2, str_tag2, str_beam_type)
Definition: beam_shared.csc:41
‪CLOSE_ENEMY_DISTANCE_SQ
‪#define CLOSE_ENEMY_DISTANCE_SQ
Definition: behavior.gsh:71
‪chooseAIVsAIMeleeRightAnimations
‪function private chooseAIVsAIMeleeRightAnimations(behaviorTreeEntity, animBundle)
Definition: archetype_aivsaimelee.gsc:721
‪MELEE_TRACE_Z_OFFSET
‪#define MELEE_TRACE_Z_OFFSET
Definition: aivsaimelee.gsh:9
‪cleanupChargeMeleeAttack
‪function cleanupChargeMeleeAttack(behaviorTreeEntity)
Definition: archetype_utility.gsc:2874
‪LEFT_TYPE
‪#define LEFT_TYPE
Definition: aivsaimelee.gsh:19
‪array
‪function filter array
Definition: array_shared.csc:16
‪shouldAquireMutexOnEnemyForAIvsAIMelee
‪function private shouldAquireMutexOnEnemyForAIvsAIMelee(behaviorTreeEntity)
Definition: archetype_aivsaimelee.gsc:110
‪SYNC_MELEE_LERP_TIME
‪#define SYNC_MELEE_LERP_TIME
Definition: aivsaimelee.gsh:13
‪ARCHETYPE_ZOMBIE
‪#define ARCHETYPE_ZOMBIE
Definition: archetype_shared.gsh:10
‪MELEE_VARIANT
‪#define MELEE_VARIANT
Definition: aivsaimelee.gsh:22
‪FRONT_ANGLE_THRESHOLD
‪#define FRONT_ANGLE_THRESHOLD
Definition: aivsaimelee.gsh:4
‪GET_MELEE_BUNDLES
‪#define GET_MELEE_BUNDLES
Definition: aivsaimelee.gsh:24
‪chooseAIVsAIMeleeFrontFlipAnimations
‪function private chooseAIVsAIMeleeFrontFlipAnimations(behaviorTreeEntity, animBundle)
Definition: archetype_aivsaimelee.gsc:652
‪set_behavior_attribute
‪function set_behavior_attribute(attribute, value)
Definition: ai_shared.gsc:159
‪meleeAcquireMutex
‪function meleeAcquireMutex(behaviorTreeEntity)
Definition: archetype_utility.gsc:2620
‪isInitiator
‪function private isInitiator(behaviorTreeEntity)
Definition: archetype_aivsaimelee.gsc:354
‪hasPotentalAIvsAIMeleeEnemy
‪function hasPotentalAIvsAIMeleeEnemy(behaviorTreeEntity)
Definition: archetype_aivsaimelee.gsc:82
‪handleDeath
‪function handleDeath(animationName, attacker)
Definition: archetype_aivsaimelee.gsc:779
‪REGULAR_VARIANT
‪#define REGULAR_VARIANT
Definition: aivsaimelee.gsh:21
‪ARCHETYPE_HUMAN_RIOTSHIELD
‪#define ARCHETYPE_HUMAN_RIOTSHIELD
Definition: archetype_shared.gsh:6
‪ARCHETYPE_HUMAN
‪#define ARCHETYPE_HUMAN
Definition: archetype_shared.gsh:4
‪processInterruptedDeath
‪function processInterruptedDeath()
Definition: archetype_aivsaimelee.gsc:794
‪ORANGE
‪#define ORANGE
Definition: shared.gsh:179
‪IS_BONUSZM
‪#define IS_BONUSZM
Definition: shared.gsh:532
‪BT_REGISTER_ACTION
‪#define BT_REGISTER_ACTION(name, initFunction, updateFunction, terminateFunction)
Definition: behavior.gsh:4
‪get_behavior_attribute
‪function get_behavior_attribute(attribute)
Definition: ai_shared.gsc:184
‪playScriptedMeleeAnimations
‪function playScriptedMeleeAnimations()
Definition: archetype_aivsaimelee.gsc:570
‪ARCHETYPE_ROBOT
‪#define ARCHETYPE_ROBOT
Definition: archetype_shared.gsh:8