‪Black Ops 3 Source Code Explorer  0.1
‪An script explorer for Black Ops 3 by ZeRoY
_bot_combat.gsc
Go to the documentation of this file.
1 #using scripts\shared\array_shared;
2 #using scripts\shared\math_shared;
3 #using scripts\shared\util_shared;
4 
5 #insert scripts\shared\shared.gsh;
6 #insert scripts\shared\bots\_bot.gsh;
7 #insert scripts\shared\abilities\_ability_util.gsh;
8 
9 #using scripts\shared\bots\_bot;
10 #using scripts\shared\bots\bot_buttons;
11 
12 #define EXPLOSION_RADIUS_FRAG 256
13 #define EXPLOSION_RADIUS_FLASH 650
14 
15 #namespace bot_combat;
16 
17 // Combat Think
18 //========================================
19 
20 function ‪combat_think()
21 {
22  if ( self ‪has_threat() )
23  {
24  // Assume the 'death' notification is always going to come up for threats
25  if ( self ‪threat_is_alive() )
26  {
27  self ‪update_threat();
28  }
29  else
30  {
31  self thread [[level.botThreatDead]]();
32  }
33  }
34 
35  if ( !self ‪has_threat() && !self ‪get_new_threat() )
36  {
37  return;
38  }
39  else if ( self ‪has_threat() )
40  {
41  if ( !self ‪threat_visible() || self.bot.threat.lastDistanceSq > level.botSettings.threatRadiusMaxSq )
42  {
43  self ‪get_new_threat( level.botSettings.threatRadiusMin );
44  }
45  }
46 
47  if ( self ‪threat_visible() )
48  {
49  self thread [[level.botUpdateThreatGoal]]();
50  self thread [[level.botThreatEngage]]();
51  }
52  else
53  {
54  self thread [[level.botThreatLost]]();
55  }
56 }
57 
58 
59 // Default Combat Queries
60 //========================================
61 
62 function ‪is_alive( entity )
63 {
64  // TODO: Add killstreak checks for mp
65  return IsAlive( entity );
66 }
67 
68 function ‪get_bot_threats( maxDistance )
69 {
70  ‪DEFAULT( maxDistance, 0 );
71 
72  return self BotGetThreats( maxDistance );
73 }
74 
76 {
77  return GetAITeamArray( "axis" );
78 }
79 
80 function ‪ignore_none( entity )
81 {
82  return false;
83 }
84 
85 function ‪ignore_non_sentient( entity )
86 {
87  return !IsSentient( entity );
88 }
89 
90 // Threat CRUD
91 //========================================
92 
93 function ‪has_threat()
94 {
95  return ( isdefined( self.bot.threat.entity ) );
96 }
97 
99 {
100  return self ‪has_threat() && self.bot.threat.visible;
101 }
102 
104 {
105  if ( !self ‪has_threat() )
106  {
107  return false;
108  }
109 
110  if ( isdefined( level.botThreatIsAlive) )
111  {
112  return self [[level.botThreatIsAlive]]( self.bot.threat.entity );
113  }
114 
115  return IsAlive( self.bot.threat.entity );
116 }
117 
118 function ‪set_threat( entity )
119 {
120  self.bot.threat.entity = entity;
121  self.bot.threat.aimOffset = self ‪get_aim_offset( entity );
122 
123  self ‪update_threat( true );
124 }
125 
126 function ‪clear_threat()
127 {
128  self.bot.threat.entity = undefined;
129  self ‪clear_threat_aim();
130  self BotLookForward();
131 /*
132  self.bot.threat.lastPosition = ( 0, 0, 0 );
133  self.bot.threat.lastVisibleTime = 0;
134  self.bot.threat.lastUpdateTime = 0;
135 */
136 }
137 
138 function ‪update_threat( newThreat )
139 {
140  if ( ‪IS_TRUE( newThreat ) )
141  {
142  self.bot.threat.wasVisible = false;
143  self ‪clear_threat_aim();
144  }
145  else
146  {
147  self.bot.threat.wasVisible = self.bot.threat.visible;
148  }
149 
150  velocity = self.bot.threat.entity GetVelocity();
151  distanceSq = DistanceSquared( self GetEye(), self.bot.threat.entity.origin );
152  predictionTime = ‪VAL( level.botSettings.thinkInterval, 0.05 );
153  predictedPosition = self.bot.threat.entity.origin + ( velocity * predictionTime );
154  aimPoint = predictedPosition + self.bot.threat.aimOffset;
155 
156  dot = self ‪bot::fwd_dot( aimPoint );
157  fov = self BotGetFov();
158 
159  if ( ‪IS_TRUE( newThreat ) )
160  {
161  self.bot.threat.visible = true;
162  }
163  else if ( dot < fov || !self BotSightTrace( self.bot.threat.entity ) )
164  {
165  // TODO: Be able to pass the aimPoint into the trace
166  self.bot.threat.visible = false;
167  return;
168  }
169 
170  self.bot.threat.visible = true;
171  self.bot.threat.lastVisibleTime = GetTime();
172  self.bot.threat.lastDistanceSq = distanceSq;
173  self.bot.threat.lastVelocity = velocity;
174  self.bot.threat.lastPosition = predictedPosition;
175  self.bot.threat.aimPoint = aimPoint;
176  self.bot.threat.dot = dot;
177 
178  weapon = self GetCurrentWeapon();
179 
180  weaponRange = ‪weapon_range( weapon );
181  self.bot.threat.inRange = distanceSq < ( weaponRange * weaponRange );
182 
183  weaponRangeClose = ‪weapon_range_close( weapon );
184  self.bot.threat.inCloseRange = distanceSq < ( weaponRangeClose * weaponRangeClose );
185 }
186 
187 
188 // Get Threats
189 //========================================
190 
191 function ‪get_new_threat( maxDistance )
192 {
193  // TODO: New threat Difficult delay
194  entity = self ‪get_greatest_threat( maxDistance );
195 
196  if ( isdefined( entity ) && entity !== self.bot.threat.entity )
197  {
198  self ‪set_threat( entity );
199  return true;
200  }
201 
202  return false;
203 }
204 
205 function ‪get_greatest_threat( maxDistance )
206 {
207  // TODO: factor in current threat?
208  // TODO: Factor in damage
209 
210  threats = self [[level.botGetThreats]]( maxDistance );
211 
212  if ( !isdefined( threats ) )
213  {
214  return undefined;
215  }
216 
217  foreach( entity in threats )
218  {
219  if ( self [[level.botIgnoreThreat]]( entity ) )
220  {
221  continue;
222  }
223 
224  return entity;
225  }
226 
227  return undefined;
228 }
229 
230 
231 // Threat Engagement
232 //========================================
233 
235 {
236  if ( !self.bot.threat.wasVisible &&
237  self.bot.threat.visible &&
238  !self IsThrowingGrenade() &&
239  !self FragButtonPressed() &&
240  !self SecondaryOffhandButtonPressed() &&
241  !self IsSwitchingWeapons() )
242  {
243  visibleRoll = RandomInt( 100 );
244 
245  rollWeight = ‪VAL( level.botSettings.lethalWeight, 0 );
246  if ( visibleRoll < rollWeight &&
247  self.bot.threat.lastDistanceSq >= level.botSettings.lethalDistanceMinSq &&
248  self.bot.threat.lastDistanceSq <= level.botSettings.lethalDistanceMaxSq &&
249  self GetWeaponAmmoStock( self.grenadeTypePrimary ) )
250  {
251  self ‪clear_threat_aim();
252  self ‪throw_grenade( self.grenadeTypePrimary, self.bot.threat.lastPosition );
253  return;
254  }
255  visibleRoll -= rollWeight;
256 
257  rollWeight = ‪VAL( level.botSettings.tacticalWeight, 0 );
258  if ( visibleRoll >= 0 &&
259  visibleRoll < rollWeight &&
260  self.bot.threat.lastDistanceSq >= level.botSettings.tacticalDistanceMinSq &&
261  self.bot.threat.lastDistanceSq <= level.botSettings.tacticalDistanceMaxSq &&
262  self GetWeaponAmmoStock( self.grenadeTypeSecondary ) )
263  {
264  self ‪clear_threat_aim();
265  self ‪throw_grenade( self.grenadeTypeSecondary, self.bot.threat.lastPosition );
266  return;
267  }
268  //visbileRoll -= rollWeight;
269  // TODO: Fancier hero gadget stuff
270 
271  // Retarget
272  self.bot.threat.aimOffset = self ‪get_aim_offset( self.bot.threat.entity );
273  }
274 
275  if ( self FragButtonPressed() )
276  {
277  self ‪throw_grenade( self.grenadeTypePrimary, self.bot.threat.lastPosition );
278  return;
279  }
280  else if ( self SecondaryOffhandButtonPressed() )
281  {
282  self ‪throw_grenade( self.grenadeTypeSecondary, self.bot.threat.lastPosition );
283  return;
284  }
285 
286  self ‪update_weapon_aim();
287 
288  if ( self IsReloading() ||
289  self IsSwitchingWeapons() ||
290  self IsThrowingGrenade() ||
291  self FragButtonPressed() ||
292  self SecondaryOffhandButtonPressed() ||
293  self IsMeleeing() )
294  {
295  return;
296  }
297 
298  if ( ‪melee_attack() )
299  {
300  return;
301  }
302 
303  self ‪update_weapon_ads();
304  self ‪fire_weapon();
305 }
306 
307 // Combat Movment
308 //========================================
309 
311 {
312  if ( self BotUnderManualControl() )
313  {
314  return;
315  }
316 
317  if ( self BotGoalSet() &&
318  ( self.bot.threat.wasVisible || !self.bot.threat.visible ) )
319  {
320  return;
321  }
322 
323  radius = ‪get_threat_goal_radius();
324  radiusSq = radius * radius;
325 
326  threatDistSq = Distance2DSquared( self.origin, self.bot.threat.lastPosition );
327 
328  if ( threatDistSq < radiusSq || !self BotSetGoal( self.bot.threat.lastPosition, radius ) )
329  {
330  self ‪combat_strafe();
331  }
332 }
333 
335 {
336  weapon = self GetCurrentWeapon();
337 
338  if ( RandomInt( 100 ) < 10 ||
339  weapon.weapClass == "melee" ||
340  ( !self GetWeaponAmmoClip( weapon ) && !self GetWeaponAmmoStock( weapon ) ) )
341  {
342  return level.botSettings.meleeRange;
343  }
344 
345  return RandomIntRange( level.botSettings.threatRadiusMin, level.botSettings.threatRadiusMax );
346 }
347 
348 // Attack
349 //========================================
350 
351 function ‪fire_weapon()
352 {
353  if ( !self.bot.threat.inRange )
354  {
355  return;
356  }
357 
358  weapon = self GetCurrentWeapon();
359 
360  if ( weapon == level.weaponNone ||
361  !self GetWeaponAmmoClip( weapon ) ||
362  self.bot.threat.dot < ‪weapon_fire_dot( weapon ) )
363  {
364  return;
365  }
366 
367  if ( weapon.fireType == "Single Shot" ||
368  weapon.fireType == "Burst" ||
369  weapon.fireType == "Charge Shot" )
370  {
371  if ( self AttackButtonPressed() )
372  {
373  return;
374  }
375  }
376 
378 
379  if ( weapon.isDualWield )
380  {
382  }
383 }
384 
385 function ‪melee_attack()
386 {
387  // TODO: Check for shotgun/ammo and armblades
388 
389  if ( self.bot.threat.dot < level.botSettings.meleeDot )
390  {
391  return false;
392  }
393 
394  if ( DistanceSquared( self.origin, self.bot.threat.lastPosition ) > level.botSettings.meleeRangeSq )
395  {
396  return false;
397  }
398 
400 
401  return true;
402 }
403 
404 
405 // Threat Chase
406 //========================================
407 
408 function ‪chase_threat()
409 {
410  if ( self BotUnderManualControl() )
411  {
412  return;
413  }
414 
415  // TODO: factor in HasPerk( "specialty_tracker" ) and threat HasPerk( "specialty_trackerjammer" )
416 
417  if ( self.bot.threat.wasVisible && !self.bot.threat.visible )
418  {
419  self ‪clear_threat_aim();
420  self BotSetGoal( self.bot.threat.lastPosition );
421  self ‪bot::sprint_to_goal();
422 
423  return;
424  }
425 
426  if ( self.bot.threat.lastVisibleTime + ‪VAL( level.botSettings.chaseThreatTime, 0 ) < GetTime() )
427  {
428  // Give up looking for this threat
429  self ‪clear_threat();
430 
431  return;
432  }
433 
434  if ( !self BotGoalSet() )
435  {
436  self ‪bot::navmesh_wander( self.bot.threat.lastVelocity, self.botSettings.chaseWanderMin, self.botSettings.chaseWanderMax, self.botSettings.chaseWanderSpacing, self.botSettings.chaseWanderFwdDot );
437  self ‪clear_threat();
438  //self bot::sprint_to_goal();
439  }
440 }
441 
442 // Aim
443 //========================================
444 
445 function ‪get_aim_offset( entity )
446 {
447  if ( IsSentient( entity ) && RandomInt( 100 ) < ‪VAL( level.botSettings.headshotWeight, 0 ) )
448  {
449  return entity GetEye() - entity.origin;
450  }
451 
452  return entity GetCentroid() - entity.origin;
453 }
454 
456 {
457  if ( !isdefined( self.bot.threat.aimStartTime ) )
458  {
459  self ‪start_threat_aim();
460  }
461 
462  aimTime = GetTime() - self.bot.threat.aimStartTime;
463 
464  if ( aimTime < 0 )
465  {
466  return;
467  }
468 
469  if ( aimTime >= self.bot.threat.aimTime || !isdefined( self.bot.threat.aimError ) )
470  {
471  self BotLookAtPoint( self.bot.threat.aimPoint );
472  return;
473  }
474 
475  eyePoint = self GetEye();
476  threatAngles = VectorToAngles( self.bot.threat.aimPoint - eyePoint );
477  initialAngles = threatAngles + self.bot.threat.aimError;
478 
479  currAngles = VectorLerp( initialAngles, threatAngles, aimTime / self.bot.threat.aimTime );
480  playerAngles = self GetPlayerAngles();
481  self BotSetLookAngles( AnglesToForward( currAngles ) );
482 }
483 
485 {
486  self.bot.threat.aimStartTime = GetTime() + ‪VAL( level.botSettings.aimDelay, 0 ) * 1000;
487  self.bot.threat.aimTime = ‪VAL( level.botSettings.aimTime, 0 ) * 1000;
488 
489  pitchError = ‪angleError( ‪VAL( level.botSettings.aimErrorMinPitch, 0 ), ‪VAL( level.botSettings.aimErrorMaxPitch, 0 ) );
490  yawError = ‪angleError( ‪VAL( level.botSettings.aimErrorMinYaw, 0 ), ‪VAL( level.botSettings.aimErrorMaxYaw, 0 ) );
491 
492  self.bot.threat.aimError = ( pitchError, yawError, 0 );
493 }
494 
495 function ‪angleError( angleMin, angleMax )
496 {
497  angle = angleMax - angleMin;
498  angle *= RandomFloatRange( -1, 1 );
499 
500  if ( angle < 0 )
501  {
502  angle -= angleMin;
503  }
504  else
505  {
506  angle += angleMin;
507  }
508 
509  return angle;
510 }
511 
513 {
514  if ( !isdefined( self.bot.threat.aimStartTime ) )
515  {
516  return;
517  }
518 
519  self.bot.threat.aimStartTime = undefined;
520  self.bot.threat.aimTime = undefined;
521  self.bot.threat.aimError = undefined;
522 }
523 
524 // Pre Combat
525 //========================================
526 
528 {
529  if ( self ‪has_threat() )
530  {
531  return;
532  }
533 
534  // Look for whoever is shooting at the bot
535  if ( isdefined( self.bot.damage.time ) && self.bot.damage.time + 1500 > GetTime() )
536  {
537  if ( self ‪has_threat() && self.bot.damage.time > self.bot.threat.lastVisibleTime )
538  {
539  self ‪clear_threat();
540  }
541 
542  self ‪bot::navmesh_wander( self.bot.damage.attackDir, level.botSettings.damageWanderMin, level.botSettings.damageWanderMax, level.botSettings.damageWanderSpacing, level.botSettings.damageWanderFwdDot );
545  }
546 }
547 
548 // Post Combat
549 //========================================
550 
552 {
553 
554 }
555 
556 // Weapon Stuff
557 //========================================
558 
560 {
561  if ( !self.bot.threat.inRange || self.bot.threat.inCloseRange )
562  {
563  return;
564  }
565 
566  weapon = self GetCurrentWeapon();
567 
568  if ( weapon == level.weaponNone ||
569  weapon.isDualWield ||
570  weapon.weapClass == "melee" ||
571  self GetWeaponAmmoClip( weapon ) <= 0 )
572  {
573  return;
574  }
575 
576  if ( self.bot.threat.dot < ‪weapon_ads_dot( weapon ) )
577  {
578  return;
579  }
580 
581  // TODO: Set ADS time
583 }
584 
585 function ‪weapon_ads_dot( weapon )
586 {
587  if ( weapon.isSniperWeapon )
588  {
589  return level.botSettings.sniperAds;
590  }
591  else if ( weapon.isRocketLauncher )
592  {
593  return level.botSettings.rocketLauncherAds;
594  }
595 
596  switch( weapon.weapClass )
597  {
598  case "mg":
599  return level.botSettings.mgAds;
600  case "smg":
601  return level.botSettings.smgAds;
602  case "spread":
603  return level.botSettings.spreadAds;
604  case "pistol":
605  return level.botSettings.pistolAds;
606  case "rifle":
607  return level.botSettings.rifleAds;
608  }
609 
610  return level.botSettings.defaultAds;
611 }
612 
613 function ‪weapon_fire_dot( weapon )
614 {
615  if ( weapon.isSniperWeapon )
616  {
617  return level.botSettings.sniperFire;
618  }
619  else if ( weapon.isRocketLauncher )
620  {
621  return level.botSettings.rocketLauncherFire;
622  }
623 
624  switch( weapon.weapClass )
625  {
626  case "mg":
627  return level.botSettings.mgFire;
628  case "smg":
629  return level.botSettings.smgFire;
630  case "spread":
631  return level.botSettings.spreadFire;
632  case "pistol":
633  return level.botSettings.pistolFire;
634  case "rifle":
635  return level.botSettings.rifleFire;
636  }
637 
638  return level.botSettings.defaultFire;
639 }
640 
641 function ‪weapon_range( weapon )
642 {
643  if ( weapon.isSniperWeapon )
644  {
645  return level.botSettings.sniperRange;
646  }
647  else if ( weapon.isRocketLauncher )
648  {
649  return level.botSettings.rocketLauncherRange;
650  }
651 
652  switch( weapon.weapClass )
653  {
654  case "mg":
655  return level.botSettings.mgRange;
656  case "smg":
657  return level.botSettings.smgRange;
658  case "spread":
659  return level.botSettings.spreadRange;
660  case "pistol":
661  return level.botSettings.pistolRange;
662  case "rifle":
663  return level.botSettings.rifleRange;
664  }
665 
666  return level.botSettings.defaultRange;
667 }
668 
669 function ‪weapon_range_close( weapon )
670 {
671  if ( weapon.isSniperWeapon )
672  {
673  return level.botSettings.sniperRangeClose;
674  }
675  else if ( weapon.isRocketLauncher )
676  {
677  return level.botSettings.rocketLauncherRangeClose;
678  }
679 
680  switch( weapon.weapClass )
681  {
682  case "mg":
683  return level.botSettings.mgRangeClose;
684  case "smg":
685  return level.botSettings.smgRangeClose;
686  case "spread":
687  return level.botSettings.spreadRangeClose;
688  case "pistol":
689  return level.botSettings.pistolRangeClose;
690  case "rifle":
691  return level.botSettings.rifleRangeClose;
692  }
693 
694  return level.botSettings.defaultRangeClose;
695 }
696 
698 {
699  currentWeapon = self GetCurrentWeapon();
700 
701  if ( self IsSwitchingWeapons() ||
702  currentWeapon.isHeroWeapon ||
703  currentWeapon.isItem )
704  {
705  return false;
706  }
707 
708  weapon = ‪bot::get_ready_gadget();
709 
710  if ( weapon != level.weaponNone )
711  {
712  if ( !isdefined( level.enemyEmpActive ) || !self [[level.enemyEmpActive]]() )
713  {
714  self ‪bot::activate_hero_gadget( weapon );
715  return true;
716  }
717  }
718 
719  weapons = self GetWeaponsListPrimaries();
720 
721  // Switch away from a 'sidearm'
722  if ( currentWeapon == level.weaponNone ||
723  currentWeapon.weapClass == "melee" ||
724  currentWeapon.weapClass == "rocketLauncher" ||
725  currentWeapon.weapClass == "pistol" )
726  {
727  foreach( weapon in weapons )
728  {
729  if ( weapon == currentWeapon )
730  {
731  continue;
732  }
733 
734  if ( self GetWeaponAmmoClip( weapon ) || self GetWeaponAmmoStock( weapon ) )
735  {
736  self BotSwitchToWeapon( weapon );
737  return true;
738  }
739  }
740 
741  return false;
742  }
743 
744  currentAmmoStock = self GetWeaponAmmoStock( currentWeapon );
745  if ( currentAmmoStock )
746  {
747  return false;
748  }
749 
750  switchFrac = 0.3;
751 
752  currentClipFrac = self ‪weapon_clip_frac( currentWeapon );
753  if ( currentClipFrac > switchFrac )
754  {
755  return false;
756  }
757 
758  foreach( weapon in weapons )
759  {
760  if ( self GetWeaponAmmoStock( weapon ) || self ‪weapon_clip_frac( weapon ) > switchFrac )
761  {
762  self BotSwitchToWeapon( weapon );
763  return true;
764  }
765  }
766 
767  return false;
768 }
769 
771 {
772  currentWeapon = self GetCurrentWeapon();
773 
774  if ( self IsSwitchingWeapons() ||
775  self GetWeaponAmmoClip( currentWeapon ) ||
776  currentWeapon.isItem )
777  {
778  return;
779  }
780 
781  currentAmmoStock = self GetWeaponAmmoStock( currentWeapon );
782  weapons = self GetWeaponsListPrimaries();
783 
784  foreach( weapon in weapons )
785  {
786  // TODO: Check if the target is a scorestreak laong with .requireLockOnToFire
787  if ( weapon == currentWeapon || weapon.requireLockOnToFire )
788  {
789  continue;
790  }
791 
792  if ( weapon.weapClass == "melee" )
793  {
794  // if we have ammo, low chance to switch to melee weapon
795  if ( currentAmmoStock && RandomIntRange( 0, 100 ) < 75 )
796  {
797  continue;
798  }
799  }
800  else
801  {
802  // Dont' switch if we'd have to reload the new weapon, and we can reload this one
803  if( !self GetWeaponAmmoClip( weapon ) && currentAmmoStock )
804  {
805  continue;
806  }
807 
808  weaponAmmoStock = self GetWeaponAmmoStock( weapon );
809 
810  // Don't switch if neither has any ammo
811  if ( !currentAmmoStock && !weaponAmmoStock )
812  {
813  continue;
814  }
815 
816  // Maybe don't switch if it's not to a pistol
817  if ( weapon.weapClass != "pistol" && RandomIntRange( 0, 100 ) < 75 )
818  {
819  continue;
820  }
821  }
822 
823  self BotSwitchToWeapon( weapon );
824  }
825 }
826 
828 {
829  weapon = self GetCurrentWeapon();
830 
831  if ( !self GetWeaponAmmoStock( weapon ) )
832  {
833  return false;
834  }
835 
836  reloadFrac = 0.5;
837 
838  if ( weapon.weapClass == "mg" )
839  {
840  reloadFrac = 0.25;
841  }
842 
843  if ( self ‪weapon_clip_frac( weapon ) < reloadFrac )
844  {
846  return true;
847  }
848 
849  return false;
850 }
851 
852 function ‪weapon_clip_frac( weapon )
853 {
854  if ( weapon.clipSize <= 0 )
855  {
856  return 1;
857  }
858 
859  clipAmmo = self GetWeaponAmmoClip( weapon );
860 
861  return ( clipAmmo / weapon.clipSize );
862 }
863 
864 // Grenades
865 //========================================
866 
867 
868 function ‪throw_grenade( weapon, target )
869 {
870  if ( !isdefined( self.bot.threat.aimStartTime ) )
871  {
872  self ‪aim_grenade( weapon, target );
873  self ‪press_grenade_button( weapon );
874  return;
875  }
876 
877  if ( self.bot.threat.aimStartTime + self.bot.threat.aimTime > GetTime() )
878  {
879  return;
880  }
881 
882  if ( self ‪will_hit_target( weapon, target ) )
883  {
884  return;
885  }
886 
887  self ‪press_grenade_button( weapon );
888 }
889 
890 function ‪press_grenade_button( weapon )
891 {
892  if ( weapon == self.grenadeTypePrimary )
893  {
895  }
896  else if ( weapon == self.grenadeTypeSecondary )
897  {
899  }
900 }
901 
902 function ‪aim_grenade( weapon, target )
903 {
904  // Just look above the target some
905  aimPeak = target + ( 0, 0, 100 );
906 
907  self.bot.threat.aimStartTime = GetTime();
908 
909  self.bot.threat.aimTime = 1500;
910 
911  self BotSetLookAnglesFromPoint( aimPeak );
912 }
913 
914 function ‪will_hit_target( weapon, target )
915 {
916  velocity = ‪get_throw_velocity( weapon );
917 
918  throwOrigin = self GetEye();
919 
920  xyDist = Distance2D( throwOrigin, target );
921  xySpeed = Distance2D( velocity, ( 0, 0, 0 ) );
922 
923  t = xyDist / xySpeed;
924 
925  gravity = -GetDvarFloat( "bg_gravity" );
926 
927  tHeight = throwOrigin[2] + velocity[2] * t + ( gravity * t * t * .5 );
928 
929  return Abs( tHeight - target[2] ) < 20;
930 }
931 
932 function ‪get_throw_velocity( weapon )
933 {
934  angles = self GetPlayerAngles();
935  forward = AnglesToForward( angles );
936 
937  // Precomputed velocity of most grenades
938  return forward * 928;
939 
940 /* TODO: These weapon values aren't actually accessable
941  velocity = forward * weapon.projectileSpeed;
942 
943  velocity += AnglesToUp( angles ) * weapon.projectileSpeedRelativeUp;
944 
945  velocity = ( velocity[0], velocity[1], velocity[2] + weapon.projectileSpeedUp );
946 
947  if ( weapon.projectileSpeedForward )
948  {
949  xyForward = VectorNormalize( ( forward[0], forward[1], 0 ) );
950  velocity += xyForward * weapon.projectileSpeedForward;
951  }
952 
953  return velocity;
954 */
955 }
956 
958 {
959  weaponsList = self GetWeaponsList();
960 
961  foreach( weapon in weaponsList )
962  {
963  if ( weapon.type == "grenade" && self GetWeaponAmmoStock( weapon ) )
964  {
965  return weapon;
966  }
967  }
968 
969  return level.weaponNone;
970 }
971 
972 
973 // Damage
974 //========================================
975 
977 {
978  self endon( "death" );
979  level endon( "game_ended" );
980 
981  while( 1 )
982  {
983  self waittill( "damage", ‪damage, attacker, direction, point, mod, unused1, unused2, unused3, weapon, flags, inflictor );
984 
985  self.bot.damage.entity = attacker;
986  self.bot.damage.amount = ‪damage;
987  self.bot.damage.attackDir = VectorNormalize( attacker.origin - self.origin );
988  self.bot.damage.weapon = weapon;
989  self.bot.damage.mod = mod;
990  self.bot.damage.time = GetTime();
991 
992  self thread [[level.onBotDamage]]();
993  }
994 }
995 
996 function ‪clear_damage()
997 {
998  self.bot.damage.entity = undefined;
999  self.bot.damage.amount = undefined;
1000  self.bot.damage.direction = undefined;
1001  self.bot.damage.weapon = undefined;
1002  self.bot.damage.mod = undefined;
1003  self.bot.damage.time = undefined;
1004 }
1005 
1006 
1007 // Movement
1008 //========================================
1009 
1010 function ‪combat_strafe( radiusMin, radiusMax, spacing, sideDotMin, sideDotMax )
1011 {
1012  ‪DEFAULT( radiusMin, ‪VAL( level.botSettings.strafeMin, 0 ) );
1013  ‪DEFAULT( radiusMax, ‪VAL( level.botSettings.strafeMax, 0 ) );
1014  ‪DEFAULT( spacing, ‪VAL( level.botSettings.strafeSpacing, 0 ) );
1015  ‪DEFAULT( sideDotMin, ‪VAL( level.botSettings.strafeSideDotMin, 0 ) );
1016  ‪DEFAULT( sideDotMax, ‪VAL( level.botSettings.strafeSideDotMax, 0 ) );
1017 
1018  fwd = AnglesToForward( self.angles );
1019 
1020  queryResult = PositionQuery_Source_Navigation( self.origin, radiusMin, radiusMax, 64, spacing, self );
1021 
1022  best_point = undefined;
1023 
1024  foreach ( point in queryResult.data )
1025  {
1026  moveDir = VectorNormalize( point.origin - self.origin );
1027  dot = VectorDot( moveDir, fwd );
1028 
1029  // Don't even consider points outside of the strafe cones
1030  if ( dot >= sideDotMin && dot <= sideDotMax )
1031  {
1032  point.score = MapFloat( radiusMin, radiusMax, 0, 50, point.distToOrigin2D );
1033  point.score += randomFloatRange( 0, 50 );
1034  }
1035  if ( !isdefined( best_point ) || point.score > best_point.score )
1036  {
1037  best_point = point;
1038  }
1039  }
1040 
1041  if( isdefined( best_point ) )
1042  {
1043  self BotSetGoal( best_point.origin );
1045  }
1046 }
‪get_ai_threats
‪function get_ai_threats()
Definition: _bot_combat.gsc:75
‪press_offhand_button
‪function press_offhand_button()
Definition: bot_buttons.gsc:103
‪clear_damage
‪function clear_damage()
Definition: _bot_combat.gsc:996
‪reload_weapon
‪function reload_weapon()
Definition: _bot_combat.gsc:827
‪ignore_non_sentient
‪function ignore_non_sentient(entity)
Definition: _bot_combat.gsc:85
‪get_ready_gadget
‪function get_ready_gadget()
Definition: _bot.gsc:784
‪angleError
‪function angleError(angleMin, angleMax)
Definition: _bot_combat.gsc:495
‪melee_attack
‪function melee_attack()
Definition: _bot_combat.gsc:385
‪end_sprint_to_goal
‪function end_sprint_to_goal()
Definition: _bot.gsc:459
‪has_threat
‪function has_threat()
Definition: _bot_combat.gsc:93
‪tap_melee_button
‪function tap_melee_button()
Definition: bot_buttons.gsc:48
‪weapon_range
‪function weapon_range(weapon)
Definition: _bot_combat.gsc:641
‪clear_threat_aim
‪function clear_threat_aim()
Definition: _bot_combat.gsc:512
‪get_aim_offset
‪function get_aim_offset(entity)
Definition: _bot_combat.gsc:445
‪weapon_ads_dot
‪function weapon_ads_dot(weapon)
Definition: _bot_combat.gsc:585
‪VAL
‪#define VAL(__var, __default)
Definition: shared.gsh:272
‪get_bot_threats
‪function get_bot_threats(maxDistance)
Definition: _bot_combat.gsc:68
‪weapon_clip_frac
‪function weapon_clip_frac(weapon)
Definition: _bot_combat.gsc:852
‪activate_hero_gadget
‪function activate_hero_gadget(weapon)
Definition: _bot.gsc:847
‪fire_weapon
‪function fire_weapon()
Definition: _bot_combat.gsc:351
‪update_weapon_ads
‪function update_weapon_ads()
Definition: _bot_combat.gsc:559
‪bot_pre_combat
‪function bot_pre_combat()
Definition: _bot_combat.gsc:527
‪weapon_fire_dot
‪function weapon_fire_dot(weapon)
Definition: _bot_combat.gsc:613
‪get_new_threat
‪function get_new_threat(maxDistance)
Definition: _bot_combat.gsc:191
‪IS_TRUE
‪#define IS_TRUE(__a)
Definition: shared.gsh:251
‪press_throw_button
‪function press_throw_button()
Definition: bot_buttons.gsc:118
‪is_alive
‪function is_alive(entity)
Definition: _bot_combat.gsc:62
‪aim_grenade
‪function aim_grenade(weapon, target)
Definition: _bot_combat.gsc:902
‪start_threat_aim
‪function start_threat_aim()
Definition: _bot_combat.gsc:484
‪damage
‪function damage(trap)
Definition: _zm_trap_electric.gsc:116
‪combat_strafe
‪function combat_strafe(radiusMin, radiusMax, spacing, sideDotMin, sideDotMax)
Definition: _bot_combat.gsc:1010
‪wait_damage_loop
‪function wait_damage_loop()
Definition: _bot_combat.gsc:976
‪throw_grenade
‪function throw_grenade(weapon, target)
Definition: _bot_combat.gsc:868
‪DEFAULT
‪#define DEFAULT(__var, __default)
Definition: shared.gsh:270
‪will_hit_target
‪function will_hit_target(weapon, target)
Definition: _bot_combat.gsc:914
‪fwd_dot
‪function fwd_dot(point)
Definition: _bot.gsc:1281
‪switch_weapon
‪function switch_weapon()
Definition: _bot_combat.gsc:697
‪update_threat_goal
‪function update_threat_goal()
Definition: _bot_combat.gsc:310
‪chase_threat
‪function chase_threat()
Definition: _bot_combat.gsc:408
‪set_threat
‪function set_threat(entity)
Definition: _bot_combat.gsc:118
‪get_throw_velocity
‪function get_throw_velocity(weapon)
Definition: _bot_combat.gsc:932
‪update_weapon_aim
‪function update_weapon_aim()
Definition: _bot_combat.gsc:455
‪bot_post_combat
‪function bot_post_combat()
Definition: _bot_combat.gsc:551
‪combat_think
‪function combat_think()
Definition: _bot_combat.gsc:20
‪get_threat_goal_radius
‪function get_threat_goal_radius()
Definition: _bot_combat.gsc:334
‪threat_visible
‪function threat_visible()
Definition: _bot_combat.gsc:98
‪threat_is_alive
‪function threat_is_alive()
Definition: _bot_combat.gsc:103
‪press_attack_button
‪function press_attack_button()
Definition: bot_buttons.gsc:38
‪navmesh_wander
‪function navmesh_wander(fwd, radiusMin, radiusMax, spacing, fwdDot)
Definition: _bot.gsc:1000
‪ignore_none
‪function ignore_none(entity)
Definition: _bot_combat.gsc:80
‪clear_threat
‪function clear_threat()
Definition: _bot_combat.gsc:126
‪press_frag_button
‪function press_frag_button()
Definition: bot_buttons.gsc:88
‪tap_reload_button
‪function tap_reload_button()
Definition: bot_buttons.gsc:53
‪update_threat
‪function update_threat(newThreat)
Definition: _bot_combat.gsc:138
‪get_lethal_grenade
‪function get_lethal_grenade()
Definition: _bot_combat.gsc:957
‪get_greatest_threat
‪function get_greatest_threat(maxDistance)
Definition: _bot_combat.gsc:205
‪weapon_range_close
‪function weapon_range_close(weapon)
Definition: _bot_combat.gsc:669
‪threat_switch_weapon
‪function threat_switch_weapon()
Definition: _bot_combat.gsc:770
‪press_grenade_button
‪function press_grenade_button(weapon)
Definition: _bot_combat.gsc:890
‪engage_threat
‪function engage_threat()
Definition: _bot_combat.gsc:234
‪sprint_to_goal
‪function sprint_to_goal()
Definition: _bot.gsc:454
‪press_ads_button
‪function press_ads_button()
Definition: bot_buttons.gsc:148