‪Black Ops 3 Source Code Explorer  0.1
‪An script explorer for Black Ops 3 by ZeRoY
_weapons.gsc
Go to the documentation of this file.
1 #using scripts\codescripts\struct;
2 
3 #using scripts\shared\array_shared;
4 #using scripts\shared\bb_shared;
5 #using scripts\shared\callbacks_shared;
6 #using scripts\shared\challenges_shared;
7 #using scripts\shared\gameobjects_shared;
8 #using scripts\shared\killstreaks_shared;
9 #using scripts\shared\loadout_shared;
10 #using scripts\shared\scoreevents_shared;
11 #using scripts\shared\system_shared;
12 #using scripts\shared\util_shared;
13 #using scripts\shared\weapons_shared;
14 #using scripts\shared\weapons\_hive_gun;
15 #using scripts\shared\weapons\_empgrenade;
16 #using scripts\shared\weapons\_flashgrenades;
17 #using scripts\shared\weapons\_hacker_tool;
18 #using scripts\shared\weapons\_proximity_grenade;
19 #using scripts\shared\weapons\_sticky_grenade;
20 #using scripts\shared\weapons\_riotshield;
21 #using scripts\shared\weapons\_tabun;
22 #using scripts\shared\weapons\_trophy_system;
23 #using scripts\shared\weapons\_weapon_utils;
24 #using scripts\shared\weapons\_weaponobjects;
25 
26 #insert scripts\shared\shared.gsh;
27 
28 #insert scripts\shared\abilities\_ability_util.gsh;
29 
30 #precache( "material", "hud_scavenger_pickup" );
31 
32 #namespace weapons;
33 
34 function ‪init_shared()
35 {
36  level.weaponNone = GetWeapon( "none" );
37  level.weaponNull = GetWeapon( "weapon_null" );
38  level.weaponBaseMelee = GetWeapon( "knife" );
39  level.weaponBaseMeleeHeld = GetWeapon( "knife_held" );
40  level.weaponBallisticKnife = GetWeapon( "knife_ballistic" );
41  level.weaponRiotshield = GetWeapon( "riotshield" );
42  level.weaponFlashGrenade = GetWeapon( "flash_grenade" );
43  level.weaponSatchelCharge = GetWeapon( "satchel_charge" );
44 
45  if ( !IsDefined(level.trackWeaponStats) )
46  {
47  level.trackWeaponStats = true;
48  }
49 
50  level._effect["flashNineBang"] = "_t6/misc/fx_equip_tac_insert_exp";
51 
53 }
54 
55 function ‪init()
56 {
57  // assigns weapons with stat numbers from 0-99
58  // attachments are now shown here, they are per weapon settings instead
59 
60 
61  //TODO - add to statstable so its a regular weapon?
62 
63  level.missileEntities = [];
64  level.hackerToolTargets = [];
65 
66  level.missileDudDeleteDelay = GetDvarInt( "scr_missileDudDeleteDelay", 3 ); //Seconds
67 
68  if ( !isdefined(level.roundStartExplosiveDelay) )
69  level.roundStartExplosiveDelay = 0;
70 
73 }
74 
76 {
77  self.usedWeapons = false;
78  self.lastFireTime = 0;
79  self.hits = 0;
81 }
82 
84 {
85  self.concussionEndTime = 0;
86  self.scavenged = false;
87  self.hasDoneCombat = false;
88  self.shieldDamageBlocked = 0;
89  self thread ‪watch_usage();
90  self thread ‪watch_grenade_usage();
91  self thread ‪watch_missile_usage();
92  self thread ‪watch_weapon_change();
93 
94  if ( level.trackWeaponStats )
95  {
96  self thread ‪track();
97  }
98 
99  self.droppedDeathWeapon = undefined;
100  self.tookWeaponFrom = [];
101  self.pickedUpWeaponKills = [];
102 
103  self thread ‪update_stowed_weapon();
104 }
105 
107 {
108  self endon("death");
109  self endon("disconnect");
110 
111  self.lastDroppableWeapon = self GetCurrentWeapon();
112 
113  self.lastWeaponChange = 0;
114  while(1)
115  {
116  previous_weapon = self GetCurrentWeapon();
117  self waittill( "weapon_change", newWeapon );
118 
119  if ( ‪may_drop( newWeapon ) )
120  {
121  self.lastDroppableWeapon = newWeapon;
122  self.lastWeaponChange = getTime();
123  }
124 
125  if ( DoesWeaponReplaceSpawnWeapon( self.spawnWeapon, newWeapon ) )
126  {
127  self.spawnWeapon = newWeapon;
128  self.pers["spawnWeapon"] = newWeapon;
129  }
130  }
131 }
132 
133 
135 {
136  if ( isdefined( self.currentWeapon ) && isdefined( self.currentWeaponStartTime ) )
137  {
138  totalTime = int ( ( newTime - self.currentWeaponStartTime ) / 1000 );
139  if ( totalTime > 0 )
140  {
141  weaponPickedUp = false;
142  if( isdefined( self.pickedUpWeapons ) && isdefined( self.pickedUpWeapons[self.currentWeapon] ) )
143  {
144  weaponPickedUp = true;
145  }
146 
147  if ( isdefined( self.class_num ) )
148  {
149  self AddWeaponStat( self.currentWeapon, "timeUsed", totalTime, self.class_num, weaponPickedUp );
150  self.currentWeaponStartTime = newTime;
151  }
152  }
153  }
154 }
155 
156 function ‪update_timings( newTime )
157 {
158  if ( self ‪util::is_bot() )
159  {
160  return;
161  }
162 
164 
165  if ( !isdefined( self.staticWeaponsStartTime ) )
166  {
167  return;
168  }
169 
170  totalTime = int ( ( newTime - self.staticWeaponsStartTime ) / 1000 );
171 
172  if ( totalTime < 0 )
173  {
174  return;
175  }
176 
177  self.staticWeaponsStartTime = newTime;
178 
179 
180  // Record grenades and equipment
181  if( isdefined( self.weapon_array_grenade ) )
182  {
183  for( i=0; i<self.weapon_array_grenade.size; i++ )
184  {
185  self AddWeaponStat( self.weapon_array_grenade[i], "timeUsed", totalTime, self.class_num );
186  }
187  }
188  if( isdefined( self.weapon_array_inventory ) )
189  {
190  for( i=0; i<self.weapon_array_inventory.size; i++ )
191  {
192  self AddWeaponStat( self.weapon_array_inventory[i], "timeUsed", totalTime, self.class_num );
193  }
194  }
195 
196  // Record killstreaks
197  if( isdefined( self.killstreak ) )
198  {
199  for( i=0; i<self.killstreak.size; i++ )
200  {
201  killstreakType = level.menuReferenceForKillStreak[ self.killstreak[i] ];
202 
203  if ( isdefined( killstreakType ) )
204  {
205  killstreakWeapon = ‪killstreaks::get_killstreak_weapon( killstreakType );
206  self AddWeaponStat( killstreakWeapon, "timeUsed", totalTime, self.class_num );
207  }
208  }
209  }
210 
211  // Record all of the equipped perks
212  if ( level.rankedmatch && level.perksEnabled )
213  {
214  perksIndexArray = [];
215 
216  specialtys = self.specialty;
217 
218  if ( !isdefined( specialtys ) )
219  {
220  return;
221  }
222 
223  if ( !isdefined( self.curClass ) )
224  {
225  return;
226  }
227 
228  if ( isdefined( self.class_num ) )
229  {
230  for ( numSpecialties = 0; numSpecialties < level.maxSpecialties; numSpecialties++ )
231  {
232  perk = self GetLoadoutItem( self.class_num, "specialty" + ( numSpecialties + 1 ) );
233  if ( perk != 0 )
234  {
235  perksIndexArray[ perk ] = true;
236  }
237  }
238 
239  perkIndexArrayKeys = getarraykeys( perksIndexArray );
240  for ( i = 0; i < perkIndexArrayKeys.size; i ++ )
241  {
242  if ( perksIndexArray[ perkIndexArrayKeys[i] ] == true )
243  {
244  self AddDStat( "itemStats", perkIndexArrayKeys[i], "stats", "timeUsed", "statValue", totalTime );
245  }
246  }
247  }
248  }
249 }
250 
251 function ‪track()
252 {
253  currentWeapon = self getCurrentWeapon();
254  currentTime = getTime();
255  spawnid = getplayerspawnid( self );
256 
257  while(1)
258  {
259  event = self ‪util::waittill_any_return( "weapon_change", "death", "disconnect" );
260  newTime = getTime();
261 
262  if( event == "weapon_change" )
263  {
264  newWeapon = self getCurrentWeapon();
265  if( newWeapon != level.weaponNone && newWeapon != currentWeapon )
266  {
268  self ‪loadout::initWeaponAttachments( newWeapon );
269 
270  currentWeapon = newWeapon;
271  currentTime = newTime;
272  }
273  }
274  else
275  {
276  if( event != "disconnect" && isdefined( self ) )
277  {
278  ‪update_timings( newTime );
279  }
280 
281  return;
282  }
283  }
284 }
285 
286 function ‪may_drop( weapon )
287 {
288  if ( level.disableWeaponDrop == 1 )
289  return false;
290 
291  if ( weapon == level.weaponNone )
292  return false;
293 
294  if ( ‪killstreaks::is_killstreak_weapon( weapon ) )
295  return false;
296 
297  if ( weapon.isGameplayWeapon )
298  return false;
299 
300  if ( !weapon.isPrimary )
301  return false;
302 
303  return true;
304 }
305 
306 function ‪drop_for_death( attacker, sWeapon, sMeansOfDeath )
307 {
308  if ( level.disableWeaponDrop == 1 )
309  return;
310 
311  weapon = self.lastDroppableWeapon;
312 
313  if ( isdefined( self.droppedDeathWeapon ) )
314  return;
315 
316  if ( !isdefined( weapon ) )
317  {
318  /#
319  if ( GetDvarString( "scr_dropdebug") == "1" )
320  println( "didn't drop weapon: not defined" );
321  #/
322  return;
323  }
324 
325  if ( weapon == level.weaponNone )
326  {
327  /#
328  if ( GetDvarString( "scr_dropdebug") == "1" )
329  println( "didn't drop weapon: weapon == none" );
330  #/
331  return;
332  }
333 
334  if ( !self hasWeapon( weapon ) )
335  {
336  /#
337  if ( GetDvarString( "scr_dropdebug") == "1" )
338  println( "didn't drop weapon: don't have it anymore (" + weapon.name + ")" );
339  #/
340  return;
341  }
342 
343  if ( !(self AnyAmmoForWeaponModes( weapon )) )
344  {
345  /#
346  if ( GetDvarString( "scr_dropdebug") == "1" )
347  println( "didn't drop weapon: no ammo for weapon modes" );
348  #/
349  return;
350  }
351 
352  if ( !‪should_drop_limited_weapon( weapon, self ) )
353  {
354  return;
355  }
356 
357  if( weapon.isCarriedKillstreak )
358  return;
359 
360  clipAmmo = self GetWeaponAmmoClip( weapon );
361  stockAmmo = self GetWeaponAmmoStock( weapon );
362  clip_and_stock_ammo = clipAmmo + stockAmmo;
363 
364  //Check if the weapon has ammo
365  if( !clip_and_stock_ammo && !‪IS_TRUE( weapon.unlimitedammo ) )
366  {
367  /#
368  if ( GetDvarString( "scr_dropdebug") == "1" )
369  println( "didn't drop weapon: no ammo" );
370  #/
371  return;
372  }
373 
374  if( ‪IS_TRUE( weapon.isnotdroppable ) )
375  {
376  return;
377  }
378 
379  stockMax = weapon.maxAmmo;
380  if ( stockAmmo > stockMax )
381  stockAmmo = stockMax;
382 
383  item = self dropItem( weapon );
384 
385  if ( !isdefined( item ) )
386  {
387  /# iprintlnbold( "dropItem: was not able to drop weapon " + weapon.name ); #/
388  return;
389  }
390 
391  /#
392  if ( GetDvarString( "scr_dropdebug") == "1" )
393  println( "dropped weapon: " + weapon.name );
394  #/
395 
396  ‪drop_limited_weapon( weapon, self, item );
397 
398  self.droppedDeathWeapon = true;
399 
400  item ItemWeaponSetAmmo( clipAmmo, stockAmmo );
401 
402  item.owner = self;
403  item.ownersattacker = attacker;
404  item.sWeapon = sWeapon;
405  item.sMeansOfDeath = sMeansOfDeath;
406 
407  item thread ‪watch_pickup();
408 
409  item thread ‪delete_pickup_after_aWhile();
410 }
411 
413 {
414  self endon("death");
415 
416  wait 60;
417 
418  if ( !isdefined( self ) )
419  return;
420 
421  self delete();
422 }
423 
424 function ‪watch_pickup()
425 {
426  self endon("death");
427 
428  weapon = self.item;
429 
430  self waittill( "trigger", player, droppedItem, pickedUpOnTouch );
431 
432  // As far as the mp_match_record weapon stats are concerned, any weapon you pick up
433  // is a picked-up weapon, even if you were the previous owner. This way we can still
434  // map the weapons of your *current* loadout directly to loadoutWeaponStats slots.
435 
436  // Also, in contrast to the player.tookWeaponFrom array, you don't have to be the one who
437  // killed the previous owner.
438 
439  if( 1 ) // isdefined(self.owner) && self.owner != player ) <-- intentionally not checking this
440  {
441  if( isdefined( player ) && isPlayer( player ) )
442  {
443  if( isdefined( player.weaponPickupsCount ) )
444  {
445  player.weaponPickupsCount++;
446  }
447  else
448  {
449  player.weaponPickupsCount = 1;
450  }
451 
452  player incrementSpecificWeaponPickedUpCount( weapon );
453 
454  if( !isdefined( player.pickedUpWeapons) )
455  {
456  player.pickedUpWeapons = []; // this array will be reset on each death
457  }
458 
459  player.pickedUpWeapons[weapon] = 1; // value not important, just that the entry exists
460 
461  }
462  }
463  /#
464  if ( GetDvarString( "scr_dropdebug") == "1" )
465  println( "picked up weapon: " + weapon.name + ", " + isdefined( self.ownersattacker ) );
466  #/
467 
468  assert( isdefined( player.tookWeaponFrom ) );
469  assert( isdefined( player.pickedUpWeaponKills ) );
470 
471  if ( isdefined( droppedItem ) )
472  {
473  for ( i = 0; i < droppedItem.size; i++ )
474  {
475  if ( !IsDefined( droppedItem[i] ) )
476  {
477  continue;
478  }
479 
480  // make sure the owner information on the dropped item is preserved
481  droppedWeapon = droppedItem[i].item;
482  if ( isdefined( player.tookWeaponFrom[ droppedWeapon ] ) )
483  {
484  droppedItem[i].owner = player.tookWeaponFrom[ droppedWeapon ];
485  droppedItem[i].ownersattacker = player;
486  player.tookWeaponFrom[ droppedWeapon ] = undefined;
487  }
488  droppedItem[i] thread ‪watch_pickup();
489  }
490  }
491 
492  // take owner information from self and put it onto player
493  if ( !isdefined( pickedUpOnTouch ) || !pickedUpOnTouch )
494  {
495  if ( isdefined( self.ownersattacker ) && self.ownersattacker == player )
496  {
497  player.tookWeaponFrom[ weapon ] = SpawnStruct();
498 
499  player.tookWeaponFrom[ weapon ].previousOwner = self.owner;
500  player.tookWeaponFrom[ weapon ].sWeapon = self.sWeapon;
501  player.tookWeaponFrom[ weapon ].sMeansOfDeath = self.sMeansOfDeath;
502 
503  player.pickedUpWeaponKills[ weapon ] = 0;
504  }
505  else
506  {
507  player.tookWeaponFrom[ weapon ] = undefined;
508  player.pickedUpWeaponKills[ weapon ] = undefined;
509  }
510  }
511 }
512 
513 function ‪watch_usage()
514 {
515  self endon( "death" );
516  self endon( "disconnect" );
517  level endon ( "game_ended" );
518 
519  for ( ;; )
520  {
521  self waittill ( "weapon_fired", curWeapon );
522  self.lastFireTime = GetTime();
523 
524  self.hasDoneCombat = true;
525 
526  switch ( curWeapon.weapClass )
527  {
528  case "rifle":
529  case "pistol":
530  case "pistol spread":
531  case "mg":
532  case "smg":
533  case "spread":
534  self ‪track_fire( curWeapon );
535  level.globalShotsFired++;
536  break;
537  case "rocketlauncher":
538  case "grenade":
539  self AddWeaponStat( curWeapon, "shots", 1, self.class_num, false );
540  break;
541  default:
542  break;
543  }
544 
545  if( isDefined(curWeapon.gadget_type) && curWeapon.gadget_type == ‪GADGET_TYPE_HERO_WEAPON )
546  {
547  if( isDefined(self.heroweaponShots) )
548  {
549  self.heroweaponShots++;
550  }
551  }
552 
553  if( curWeapon.isCarriedKillstreak )
554  {
555  if ( IsDefined( self.pers["held_killstreak_ammo_count"][curWeapon] ) )
556  {
557  self.pers["held_killstreak_ammo_count"][curWeapon]--;
558  }
559  }
560  }
561 }
562 
563 #define TRACK_WEAPON_SHOT_FIRED 1
564 
565 function ‪track_fire( curWeapon )
566 {
567  pixbeginevent("trackWeaponFire");
568 
569  weaponPickedUp = false;
570  if( isdefined( self.pickedUpWeapons ) && isdefined( self.pickedUpWeapons[curWeapon] ) )
571  {
572  weaponPickedUp = true;
573  }
574 
575  self TrackWeaponFireNative( curWeapon, ‪TRACK_WEAPON_SHOT_FIRED, self.hits, 1, self.class_num, weaponPickedUp, self.primaryLoadoutGunSmithVariantIndex, self.secondaryLoadoutGunSmithVariantIndex );
576  // old:
577  //self AddWeaponStat( curWeapon, "shots", TRACK_WEAPON_SHOT_FIRED );
578  //self AddWeaponStat( curWeapon, "hits", self.hits );
579 
580  //self AddPlayerStat( "total_shots", TRACK_WEAPON_SHOT_FIRED );
581  //self AddPlayerStat( "hits", self.hits );
582  //self AddPlayerStat( "misses", int( max( 0, TRACK_WEAPON_SHOT_FIRED - self.hits ) ) );
583 
584  if ( isdefined( self.totalMatchShots ) )
585  self.totalMatchShots++;
586 
587  if( level.mpCustomMatch === true )
588  {
589  self.pers["shotsfired"]++;
590  self.shotsfired = self.pers["shotsfired"];
591 
592  self.pers["shotshit"] += self.hits;
593  self.shotshit = self.pers["shotshit"];
594 
595  self.pers["shotsmissed"] = self.shotsfired - self.shotshit;
596  self.shotsmissed = self.pers["shotsmissed"];
597  }
598 
599  self.hits = 0;
600  pixendevent();
601 }
602 
604 {
605  self endon( "death" );
606  self endon( "disconnect" );
607 
608  self.throwingGrenade = false;
609  self.gotPullbackNotify = false;
610 
611  self thread ‪begin_other_grenade_tracking();
612 
613  self thread ‪watch_for_throwbacks();
614  self thread ‪watch_for_grenade_duds();
616 
617  for ( ;; )
618  {
619  self waittill ( "grenade_pullback", weapon );
620 
621  self AddWeaponStat( weapon, "shots", 1, self.class_num );
622 
623  self.hasDoneCombat = true;
624 
625  self.throwingGrenade = true;
626  self.gotPullbackNotify = true;
627 
628  if ( weapon.drawOffhandModelInHand )
629  {
630  self SetOffhandVisible( true );
631  self thread ‪watch_offhand_end();
632  }
633 
634  self thread ‪begin_grenade_tracking();
635  }
636 }
637 
639 {
640  self endon( "death" );
641  self endon( "disconnect" );
642  level endon ( "game_ended" );
643 
644  for ( ;; )
645  {
646  self waittill ( "missile_fire", missile, weapon );
647 
648  self.hasDoneCombat = true;
649 
650  /#assert( isdefined( missile ));#/
651  level.missileEntities[ level.missileEntities.size ] = missile;
652  missile.weapon = weapon;
653  missile thread ‪watch_missile_death();
654  }
655 }
656 
657 function ‪watch_missile_death() // self == missile
658 {
659  self waittill( "death" );
660  ArrayRemoveValue( level.missileEntities, self );
661 }
662 
663 function ‪drop_all_to_ground( origin, radius )
664 {
665  weapons = GetDroppedWeapons();
666  for ( i = 0 ; i < weapons.size ; i++ )
667  {
668  if ( DistanceSquared( origin, weapons[i].origin ) < radius * radius )
669  {
670  ‪trace = bullettrace( weapons[i].origin, weapons[i].origin + (0,0,-2000), false, weapons[i] );
671  weapons[i].origin = ‪trace["position"];
672  }
673  }
674 }
675 
676 function ‪drop_grenades_to_ground( origin, radius )
677 {
678  grenades = getentarray( "grenade", "classname" );
679  for( i = 0 ; i < grenades.size ; i++ )
680  {
681  if( DistanceSquared( origin, grenades[i].origin )< radius * radius )
682  {
683  grenades[i] ‪launch( (5,5,5) );
684  }
685  }
686 }
687 
689 {
690  self endon ( "death" );
691  self endon ( "disconnect" );
692  self endon ( "grenade_fire" );
693 
694  waittillframeend;
695  weapon = level.weaponNone;
696 
697  while( self IsThrowingGrenade() && weapon == level.weaponNone )
698  {
699  self waittill( "weapon_change", weapon );
700  }
701 
702  self.throwingGrenade = false;
703  self.gotPullbackNotify = false;
704 
705  self notify( "grenade_throw_cancelled" );
706 }
707 
708 function ‪watch_offhand_end() // self == player
709 {
710  self notify( "watchOffhandEnd" );
711  self endon( "watchOffhandEnd" );
712 
713  while ( self ‪is_using_offhand_equipment() )
714  {
715  msg = self ‪util::waittill_any_return( "death", "disconnect", "grenade_fire", "weapon_change", "watchOffhandEnd" );
716 
717  if (( msg == "death" ) || ( msg == "disconnect" ))
718  {
719  break;
720  }
721  }
722 
723  if ( isdefined( self ) )
724  {
725  self SetOffhandVisible( false );
726  }
727 }
728 
729 function ‪is_using_offhand_equipment() // self == player
730 {
731  if ( self IsUsingOffhand() )
732  {
733  weapon = self GetCurrentOffhand();
734  if ( weapon.isEquipment )
735  {
736  return true;
737  }
738  }
739 
740  return false;
741 }
742 
743 
745 {
746  self endon ( "death" );
747  self endon ( "disconnect" );
748  self endon ( "grenade_throw_cancelled" );
749 
750  ‪startTime = getTime();
751 
752  self thread ‪watch_grenade_cancel();
753 
754  self waittill ( "grenade_fire", grenade, weapon, cookTime );
755 
756  /#assert( isdefined( grenade ));#/
757  level.missileEntities[ level.missileEntities.size ] = grenade;
758  grenade.weapon = weapon;
759  grenade thread ‪watch_missile_death();
760 
761  if( ‪IS_BONUSZM || ‪IS_TRUE( level.projectiles_should_ignore_world_pause ) )
762  {
763  grenade SetIgnorePauseWorld( true );
764  }
765 
766  if ( grenade ‪util::isHacked() )
767  {
768  return;
769  }
770 
771 
772  blackBoxEventName = "mpequipmentuses";
773 
774  if ( SessionModeIsCampaignGame() )
775  {
776  blackBoxEventName = "cpequipmentuses";
777  }
778  else if ( SessionModeIsZombiesGame() )
779  {
780  blackBoxEventName = "zmequipmentuses";
781  }
782 
783 
784  bbPrint( blackBoxEventName, "gametime %d spawnid %d weaponname %s", gettime(), getplayerspawnid( self ), weapon.name );
785 
786  cookedTime = getTime() - ‪startTime;
787 
788  if ( cookedTime > 1000 )
789  {
790  grenade.isCooked = true;
791  }
792 
793  if (isDefined(self.grenadesUsed))
794  {
795  self.grenadesUsed++;
796  }
797 
798  switch ( weapon.rootWeapon.name )
799  {
800  case "frag_grenade":
801  level.globalFragGrenadesFired++;
802  // fall through on purpose
803  case "sticky_grenade":
804  self AddWeaponStat( weapon, "used", 1 );
805  grenade SetTeam( self.pers["team"] );
806  grenade SetOwner( self );
807  // fall through on purpose
808  case "explosive_bolt":
809  grenade.originalOwner = self;
810  break;
811  case "satchel_charge":
812  level.globalSatchelChargeFired++;
813  break;
814  case "concussion_grenade":
815  case "flash_grenade":
816  self AddWeaponStat( weapon, "used", 1 );
817  break;
818  }
819 
820  self.throwingGrenade = false;
821 
822  if ( weapon.cookOffHoldTime > 0 )
823  {
824  grenade thread ‪track_cooked_detonation( self, weapon, cookTime );
825  }
826  else if ( weapon.multiDetonation > 0 )
827  {
828  grenade thread ‪track_multi_detonation( self, weapon, cookTime );
829  }
830 }
831 
833 {
834  self notify( "otherGrenadeTrackingStart" );
835 
836  self endon( "otherGrenadeTrackingStart" );
837  self endon( "disconnect" );
838 
839  for (;;)
840  {
841  self waittill ( "grenade_fire", grenade, weapon );
842 
843  if ( grenade ‪util::isHacked() )
844  {
845  continue;
846  }
847 
848  switch( weapon.rootweapon.name )
849  {
850  case "tabun_gas":
851  grenade thread ‪tabun::watchTabunGrenadeDetonation( self );
852  break;
853  case "sticky_grenade":
854  grenade thread ‪check_stuck_to_player( true, true, weapon );
855  grenade thread ‪riotshield::check_stuck_to_shield();
856  break;
857  case "satchel_charge":
858  case "c4":
859  grenade thread ‪check_stuck_to_player( true, false, weapon );
860  break;
861  case "hatchet":
862  grenade.lastWeaponBeforeToss = self ‪util::getLastWeapon();
863  grenade thread ‪check_hatchet_bounce();
864  grenade thread ‪check_stuck_to_player( false, false, weapon );
865  self AddWeaponStat( weapon, "used", 1 );
866  break;
867  default:
868  break;
869  }
870  }
871 }
872 
873 function ‪check_stuck_to_player( deleteOnTeamChange, awardScoreEvent, weapon )
874 {
875  self endon( "death" );
876 
877  self waittill( "stuck_to_player", player );
878  if ( isdefined ( player ) )
879  {
880  if ( deleteOnTeamChange )
881  self thread ‪stuck_to_player_team_change( player );
882 
883  if ( awardScoreEvent && isdefined ( self.originalOwner ) )
884  {
885  if ( self.originalOwner ‪util::IsEnemyPlayer( player ) )
886  {
887  ‪scoreevents::processScoreEvent( "stick_explosive_kill", self.originalOwner, player, weapon );
888  }
889  }
890 
891  self.stuckToPlayer = player;
892  }
893 }
894 
896 {
897  self endon( "stuck_to_player" );
898  self endon( "death");
899  self waittill( "grenade_bounce" );
900  self.bounced = true;
901 }
902 
904 {
905  self endon("death");
906  player endon("disconnect");
907  originalTeam = player.pers["team"];
908 
909  while(1)
910  {
911  player waittill("joined_team");
912 
913  if ( player.pers["team"] != originalTeam )
914  {
915  self ‪detonate();
916  return;
917  }
918  }
919 }
920 
922 {
923  self endon ( "death" );
924  self endon ( "disconnect" );
925 
926  for ( ;; )
927  {
928  self waittill ( "grenade_fire", grenade, weapon );
929 
930  if ( self.gotPullbackNotify )
931  {
932  self.gotPullbackNotify = false;
933  continue;
934  }
935 
936  if ( !isSubStr( weapon.name, "frag_" ) )
937  continue;
938 
939  // no grenade_pullback notify! we must have picked it up off the ground.
940  grenade.threwBack = true;
941 
942  grenade.originalOwner = self;
943  }
944 }
945 
946 function ‪wait_and_delete_dud( waitTime )
947 {
948  self endon( "death" );
949 
950  wait( waitTime );
951 
952  if( isDefined( self ) )
953  self delete();
954 }
955 
957 {
958  if ( !isdefined( level.startTime ) )
959  return 0;
960 
961  return (gettime() - level.startTime);
962 
963 }
964 
965 function ‪turn_grenade_into_a_dud( weapon, isThrownGrenade, player )
966 {
967  time = (‪getTimeFromLevelStart() / 1000);
968  if ( level.roundStartExplosiveDelay >= time )
969  {
970  if ( weapon.disallowatmatchstart || WeaponHasAttachment( weapon, "gl" ) )
971  {
972  timeLeft = Int( level.roundStartExplosiveDelay - time );
973 
974  if ( !timeLeft )
975  timeLeft = 1;
976 
977  // these prints need to be changed to the correct location and they should include the weapon name
978  if ( isThrownGrenade )
979  player iPrintLnBold( &"MP_GRENADE_UNAVAILABLE_FOR_N", " " + timeLeft + " ", &"EXE_SECONDS" );
980  else
981  player iPrintLnBold( &"MP_LAUNCHER_UNAVAILABLE_FOR_N", " " + timeLeft + " ", &"EXE_SECONDS" );
982 
983 
984  self makeGrenadeDud();
985  }
986  }
987 }
988 
990 {
991  self endon( "spawned_player" );
992  self endon( "disconnect" );
993 
994  while ( 1 )
995  {
996  self waittill( "grenade_fire", grenade, weapon );
997 
998  grenade ‪turn_grenade_into_a_dud( weapon, true, self );
999  }
1000 }
1001 
1003 {
1004  self endon( "spawned_player" );
1005  self endon( "disconnect" );
1006 
1007  while ( 1 )
1008  {
1009  self waittill( "grenade_launcher_fire", grenade, weapon );
1010  grenade ‪turn_grenade_into_a_dud( weapon, false, self );
1011 
1012  /#assert( isDefined( grenade ));#/
1013  level.missileEntities[ level.missileEntities.size ] = grenade;
1014  grenade.weapon = weapon;
1015  grenade thread ‪watch_missile_death();
1016  }
1017 }
1018 
1019 
1020 // these functions are used with scripted weapons (like satchels, shoeboxs, artillery)
1021 // returns an array of objects representing damageable entities (including players) within a given sphere.
1022 // each object has the property damageCenter, which represents its center (the location from which it can be damaged).
1023 // each object also has the property entity, which contains the entity that it represents.
1024 // to damage it, call damageEnt() on it.
1025 function ‪get_damageable_ents(pos, radius, doLOS, startRadius)
1026 {
1027  ents = [];
1028 
1029  if (!isdefined(doLOS))
1030  doLOS = false;
1031 
1032  if ( !isdefined( startRadius ) )
1033  startRadius = 0;
1034 
1035  // players
1036  players = level.players;
1037  for (i = 0; i < players.size; i++)
1038  {
1039  if (!isalive(players[i]) || players[i].sessionstate != "playing")
1040  continue;
1041 
1042  playerpos = players[i].origin + (0,0,32);
1043  distsq = distancesquared(pos, playerpos);
1044  if (distsq < radius*radius && (!doLOS || ‪damage_trace_passed(pos, playerpos, startRadius, undefined)))
1045  {
1046  newent = spawnstruct();
1047  newent.isPlayer = true;
1048  newent.isADestructable = false;
1049  newent.isADestructible = false;
1050  newent.isActor = false;
1051  newent.entity = players[i];
1052  newent.damageCenter = playerpos;
1053  ents[ents.size] = newent;
1054  }
1055  }
1056 
1057  // grenades
1058  grenades = getentarray("grenade", "classname");
1059  for (i = 0; i < grenades.size; i++)
1060  {
1061  entpos = grenades[i].origin;
1062  distsq = distancesquared(pos, entpos);
1063  if (distsq < radius*radius && (!doLOS || ‪damage_trace_passed(pos, entpos, startRadius, grenades[i])))
1064  {
1065  newent = spawnstruct();
1066  newent.isPlayer = false;
1067  newent.isADestructable = false;
1068  newent.isADestructible = false;
1069  newent.isActor = false;
1070  newent.entity = grenades[i];
1071  newent.damageCenter = entpos;
1072  ents[ents.size] = newent;
1073  }
1074  }
1075 
1076  // THIS IS NOT THE SAME AS THE destruct-A-bles BELOW
1077  destructibles = getentarray("destructible", "targetname");
1078  for (i = 0; i < destructibles.size; i++)
1079  {
1080  entpos = destructibles[i].origin;
1081  distsq = distancesquared(pos, entpos);
1082  if (distsq < radius*radius && (!doLOS || ‪damage_trace_passed(pos, entpos, startRadius, destructibles[i])))
1083  {
1084  newent = spawnstruct();
1085  newent.isPlayer = false;
1086  newent.isADestructable = false;
1087  newent.isADestructible = true;
1088  newent.isActor = false;
1089  newent.entity = destructibles[i];
1090  newent.damageCenter = entpos;
1091  ents[ents.size] = newent;
1092  }
1093  }
1094 
1095  // THIS IS NOT THE SAME AS THE destruct-I-bles ABOVE
1096  destructables = getentarray("destructable", "targetname");
1097  for (i = 0; i < destructables.size; i++)
1098  {
1099  entpos = destructables[i].origin;
1100  distsq = distancesquared(pos, entpos);
1101  if (distsq < radius*radius && (!doLOS || ‪damage_trace_passed(pos, entpos, startRadius, destructables[i])))
1102  {
1103  newent = spawnstruct();
1104  newent.isPlayer = false;
1105  newent.isADestructable = true;
1106  newent.isADestructible = false;
1107  newent.isActor = false;
1108  newent.entity = destructables[i];
1109  newent.damageCenter = entpos;
1110  ents[ents.size] = newent;
1111  }
1112  }
1113 
1114  dogs = [[level.dogManagerOnGetDogs]]();
1115 
1116  if ( isdefined( dogs ) )
1117  {
1118  foreach( dog in dogs )
1119  {
1120  if ( !IsAlive( dog ) )
1121  {
1122  continue;
1123  }
1124 
1125  entpos = dog.origin;
1126  distsq = distancesquared(pos, entpos);
1127  if (distsq < radius*radius && (!doLOS || ‪damage_trace_passed(pos, entpos, startRadius, dog)))
1128  {
1129  newent = spawnstruct();
1130  newent.isPlayer = false;
1131  newent.isADestructable = false;
1132  newent.isADestructible = false;
1133  newent.isActor = true;
1134  newent.entity = dog;
1135  newent.damageCenter = entpos;
1136  ents[ents.size] = newent;
1137  }
1138  }
1139  }
1140 
1141  return ents;
1142 }
1143 
1144 function ‪damage_trace_passed(from, to, startRadius, ‪ignore)
1145 {
1146  ‪trace = ‪damage_trace(from, to, startRadius, ‪ignore);
1147  return (‪trace["fraction"] == 1);
1148 }
1149 
1150 function ‪damage_trace(from, to, startRadius, ‪ignore)
1151 {
1152  midpos = undefined;
1153 
1154  diff = to - from;
1155  if ( lengthsquared( diff ) < startRadius*startRadius )
1156  midpos = to;
1157  dir = vectornormalize( diff );
1158  midpos = from + (dir[0]*startRadius, dir[1]*startRadius, dir[2]*startRadius);
1159 
1160  ‪trace = bullettrace(midpos, to, false, ‪ignore);
1161 
1162  if ( GetDvarint( "scr_damage_debug") != 0 )
1163  {
1164  if (‪trace["fraction"] == 1)
1165  {
1166  thread ‪debugline(midpos, to, (1,1,1));
1167  }
1168  else
1169  {
1170  thread ‪debugline(midpos, ‪trace["position"], (1,.9,.8));
1171  thread ‪debugline(‪trace["position"], to, (1,.4,.3));
1172  }
1173  }
1174 
1175  return ‪trace;
1176 }
1177 
1178 // eInflictor = the entity that causes the damage (e.g. a shoebox)
1179 // eAttacker = the player that is attacking
1180 // iDamage = the amount of damage to do
1181 // sMeansOfDeath = string specifying the method of death (e.g. "MOD_PROJECTILE_SPLASH")
1182 // weapon = the weapon used
1183 // damagepos = the position damage is coming from
1184 // damagedir = the direction damage is moving in
1185 function ‪damage_ent(eInflictor, eAttacker, iDamage, sMeansOfDeath, weapon, damagepos, damagedir)
1186 {
1187  if (self.isPlayer)
1188  {
1189  self.damageOrigin = damagepos;
1190  self.entity thread [[level.callbackPlayerDamage]](
1191  eInflictor, // eInflictor The entity that causes the damage.(e.g. a turret)
1192  eAttacker, // eAttacker The entity that is attacking.
1193  iDamage, // iDamage Integer specifying the amount of damage done
1194  0, // iDFlags Integer specifying flags that are to be applied to the damage
1195  sMeansOfDeath, // sMeansOfDeath Integer specifying the method of death
1196  weapon, // weapon The weapon used to inflict the damage
1197  damagepos, // vPoint The point the damage is from?
1198  damagedir, // vDir The direction of the damage
1199  "none", // sHitLoc The location of the hit
1200  damagepos, // sDamageOrigin
1201  0, // psOffsetTime The time offset for the damage
1202  0, // boneIndex
1203  undefined // surfaceNormal
1204  );
1205  }
1206  else if (self.isactor)
1207  {
1208  self.damageOrigin = damagepos;
1209  self.entity thread [[level.callbackActorDamage]](
1210  eInflictor, // eInflictor The entity that causes the damage.(e.g. a turret)
1211  eAttacker, // eAttacker The entity that is attacking.
1212  iDamage, // iDamage Integer specifying the amount of damage done
1213  0, // iDFlags Integer specifying flags that are to be applied to the damage
1214  sMeansOfDeath, // sMeansOfDeath Integer specifying the method of death
1215  weapon, // weapon The weapon used to inflict the damage
1216  damagepos, // vPoint The point the damage is from?
1217  damagedir, // vDir The direction of the damage
1218  "none", // sHitLoc The location of the hit
1219  damagepos, // vDamageOrigin the point the damage originates
1220  0, // psOffsetTime The time offset for the damage
1221  0, // boneIndex
1222  0, // modelIndex
1223  0, // surfaceType
1224  (1.0, 0, 0) // surfaceNormal
1225  );
1226  }
1227  else if (self.isADestructible)
1228  {
1229  self.damageOrigin = damagepos;
1230  self.entity DoDamage(
1231  iDamage, // iDamage Integer specifying the amount of damage done
1232  damagepos, // vPoint The point the damage is from?
1233  eAttacker, // eAttacker The entity that is attacking.
1234  eInflictor, // eInflictor The entity that causes the damage.(e.g. a turret)
1235  0,
1236  sMeansOfDeath, // sMeansOfDeath Integer specifying the method of death
1237  0, // iDFlags Integer specifying flags that are to be applied to the damage
1238  weapon // weapon The weapon used to inflict the damage
1239  );
1240  }
1241  else
1242  {
1243  self.entity ‪util::damage_notify_wrapper( iDamage, eAttacker, (0,0,0), (0,0,0), "mod_explosive", "", "" );
1244  }
1245 }
1246 
1247 function ‪debugline(‪a, b, color)
1248 {
1249  /#
1250  for (i = 0; i < 30*20; i++)
1251  {
1252  line(‪a,b, color);
1253  wait .05;
1254  }
1255  #/
1256 }
1257 
1258 
1259 function ‪on_damage( eAttacker, eInflictor, weapon, meansOfDeath, ‪damage )
1260 {
1261  self endon ( "death" );
1262  self endon ( "disconnect" );
1263 
1264  if ( isdefined( level._custom_weapon_damage_func ) )
1265  {
1266  is_weapon_registered = self [[level._custom_weapon_damage_func]]( eAttacker, eInflictor, weapon, meansOfDeath, ‪damage );
1267  if( is_weapon_registered )
1268  {
1269  return;
1270  }
1271  }
1272 
1273  switch ( weapon.rootWeapon.name )
1274  {
1275  case "concussion_grenade":
1276  radius = weapon.explosionRadius;
1277 
1278  if (self == eAttacker) // TFLAME 8/1/12 - reduce effects on attacker
1279  radius *= 0.5;
1280 
1281  scale = 1 - (distance( self.origin, eInflictor.origin ) / radius);
1282 
1283  if ( scale < 0 )
1284  scale = 0;
1285 
1286  time = 0.25 + (4 * scale);
1287 
1289 
1290  if ( meansOfDeath != "MOD_IMPACT" )
1291  {
1292  if ( self HasPerk ( "specialty_stunprotection" ) )
1293  {
1294  time *= 0.1;
1295  }
1296  else
1297  {
1298  if ( self ‪util::mayApplyScreenEffect() )
1299  {
1300  self shellShock( "concussion_grenade_mp", time, false );
1301  }
1302  }
1303 
1304  self thread ‪play_concussion_sound( time );
1305  self.concussionEndTime = getTime() + (time * 1000);
1306  self.lastConcussedBy = eAttacker;
1307  }
1308  break;
1309 
1310  default:
1311  // shellshock will only be done if meansofdeath is an appropriate type and if there is enough damage.
1312  if ( isdefined( level.shellshockOnPlayerDamage ) )
1313  {
1314  [[level.shellshockOnPlayerDamage]]( meansOfDeath, ‪damage, weapon );
1315  }
1316  break;
1317  }
1318 }
1319 
1320 function ‪play_concussion_sound( duration )
1321 {
1322  self endon( "death" );
1323  self endon( "disconnect" );
1324 
1325  concussionSound = ‪spawn ("script_origin",(0,0,1));
1326  concussionSound.origin = self.origin;
1327  concussionSound linkTo( self );
1328  concussionSound thread ‪delete_ent_on_owner_death( self );
1329  concussionSound playsound( "" );
1330  concussionSound playLoopSound ( "" );
1331  if ( duration > 0.5 )
1332  wait( duration - 0.5 );
1333  concussionSound playsound( "" );
1334  concussionSound StopLoopSound( .5);
1335  wait(0.5);
1336 
1337  concussionSound notify ( "delete" );
1338  concussionSound delete();
1339 }
1340 
1342 {
1343  self endon( "delete" );
1344  owner waittill( "death" );
1345  self delete();
1346 }
1347 
1348 // weapon stowing logic ===================================================================
1349 
1350 // thread loop life = player's life
1352 {
1353  self endon( "spawned" );
1354  self endon( "killed_player" );
1355  self endon( "disconnect" );
1356 
1357  //weapons::detach_all_weapons();
1358 
1359  self.tag_stowed_back = undefined;
1360  self.tag_stowed_hip = undefined;
1361 
1362  team = self.pers["team"];
1363  playerclass = self.pers["class"];
1364 
1365  while ( true )
1366  {
1367  self waittill( "weapon_change", newWeapon );
1368 
1369  if ( self IsMantling() )
1370  {
1371  continue;
1372  }
1373 
1374  currentStowed = self GetStowedWeapon();
1375  hasStowed = false;
1376 
1377  // weapon array reset, might have swapped weapons off the ground
1378  self.weapon_array_primary =[];
1379  self.weapon_array_sidearm = [];
1380  self.weapon_array_grenade = [];
1381  self.weapon_array_inventory =[];
1382 
1383  // populate player's weapon stock arrays
1384  weaponsList = self GetWeaponsList();
1385  for( idx = 0; idx < weaponsList.size; idx++ )
1386  {
1387  // we don't want these in the primary list
1388  switch( weaponsList[idx].‪name )
1389  {
1390  case "minigun": // death machine
1391  case "m32": // grenade launcher
1392  continue;
1393 
1394  default:
1395  break;
1396  }
1397 
1398  if ( !hasStowed || currentStowed == weaponsList[idx] )
1399  {
1400  currentStowed = weaponsList[idx];
1401  hasStowed = true;
1402  }
1403 
1404  if ( ‪weapons::is_primary_weapon( weaponsList[idx] ) )
1405  self.weapon_array_primary[self.weapon_array_primary.size] = weaponsList[idx];
1406  else if ( ‪weapons::is_side_arm( weaponsList[idx] ) )
1407  self.weapon_array_sidearm[self.weapon_array_sidearm.size] = weaponsList[idx];
1408  else if ( ‪weapons::is_grenade( weaponsList[idx] ) )
1409  self.weapon_array_grenade[self.weapon_array_grenade.size] = weaponsList[idx];
1410  else if ( ‪weapons::is_inventory( weaponsList[idx] ) )
1411  self.weapon_array_inventory[self.weapon_array_inventory.size] = weaponsList[idx];
1412  else if ( weaponsList[idx].isPrimary )
1413  self.weapon_array_primary[self.weapon_array_primary.size] = weaponsList[idx];
1414  }
1415 
1416  if ( newWeapon != level.weaponNone || !hasStowed )
1417  {
1421  }
1422  }
1423 }
1424 
1426 {
1427  if ( isdefined( level.giveCustomLoadout ) )
1428  {
1429  return level.weaponNone;
1430  }
1431 
1432  assert( isdefined( self.class_num ) );
1433  if ( isdefined( self.class_num ) )
1434  {
1435  index = self ‪loadout::getLoadoutItemFromDDLStats( self.class_num, stat );
1436 
1437  if ( isdefined( level.tbl_weaponIDs[index] ) && isdefined( level.tbl_weaponIDs[index]["reference"] ) )
1438  {
1439  return GetWeapon( level.tbl_weaponIDs[index]["reference"] );
1440  }
1441  }
1442 
1443  return level.weaponNone;
1444 }
1445 
1447 {
1448  count = 0;
1449  if ( isdefined( level.giveCustomLoadout ) )
1450  {
1451  return 0;
1452  }
1453 
1454  assert( isdefined( self.class_num ) );
1455  if ( isdefined( self.class_num ) )
1456  {
1457  count = self ‪loadout::getLoadoutItemFromDDLStats( self.class_num, stat );
1458  }
1459 
1460  return count;
1461 }
1462 
1463 // Self == player
1465 {
1466  self.scavenger_icon.alpha = 1;
1467  self.scavenger_icon fadeOverTime( 1.0 );
1468  self.scavenger_icon.alpha = 0;
1469 }
1470 
1472 {
1473  self endon( "death" );
1474  self waittill( "scavenger", player );
1475 
1476  primary_weapons = player GetWeaponsListPrimaries();
1477  offhand_weapons_and_alts = array::exclude( player GetWeaponsList( true ), primary_weapons );
1478 
1479  ArrayRemoveValue( offhand_weapons_and_alts, level.weaponBaseMelee );
1480  offhand_weapons_and_alts = ‪array::reverse( offhand_weapons_and_alts ); // Prioritize tacticals over lethals
1481 
1482  player playsound( "wpn_ammo_pickup" );
1483  player playlocalsound( "wpn_ammo_pickup" );
1484 
1485  player ‪flash_scavenger_icon();
1486 
1487  for ( i = 0; i < offhand_weapons_and_alts.size; i++ )
1488  {
1489  weapon = offhand_weapons_and_alts[i];
1490 
1491  if ( !weapon.isScavengable || ‪killstreaks::is_killstreak_weapon( weapon ) )
1492  {
1493  continue;
1494  }
1495 
1496  maxAmmo = 0;
1497 
1498  if ( weapon == player.grenadeTypePrimary && isdefined( player.grenadeTypePrimaryCount ) && player.grenadeTypePrimaryCount > 0 )
1499  {
1500  maxAmmo = player.grenadeTypePrimaryCount;
1501  }
1502  else if ( weapon == player.grenadeTypeSecondary && isdefined( player.grenadeTypeSecondaryCount ) && player.grenadeTypeSecondaryCount > 0 )
1503  {
1504  maxAmmo = player.grenadeTypeSecondaryCount;
1505  }
1506 
1507  if ( isdefined( level.customLoasdoutScavenge ) )
1508  {
1509  maxAmmo = self [[level.customLoadoutScavenge]]( weapon );
1510  }
1511 
1512  if ( maxAmmo == 0 )
1513  {
1514  continue;
1515  }
1516 
1517  if ( weapon.rootWeapon == level.weaponSatchelCharge )
1518  {
1519  if ( player ‪weaponobjects::anyObjectsInWorld( weapon.rootWeapon ) )
1520  {
1521  continue;
1522  }
1523  }
1524 
1525  stock = player GetWeaponAmmoStock( weapon );
1526 
1527  if ( stock < maxAmmo )
1528  {
1529  // just give 1 for each scavenger pick up
1530  ammo = stock + 1;
1531  if ( ammo > maxAmmo )
1532  {
1533  ammo = maxAmmo;
1534  }
1535  player SetWeaponAmmoStock( weapon, ammo );
1536  player.scavenged = true;
1537 
1538  player thread ‪challenges::scavengedGrenade();
1539  }
1540  else
1541  {
1542  if ( weapon.rootWeapon == GetWeapon( "trophy_system" ) )
1543  {
1544  player ‪trophy_system::ammo_scavenger( weapon );
1545  }
1546  }
1547  }
1548 
1549  for ( i = 0; i < primary_weapons.size; i++ )
1550  {
1551  weapon = primary_weapons[i];
1552 
1553  if ( !weapon.isScavengable || ‪killstreaks::is_killstreak_weapon( weapon ) )
1554  {
1555  continue;
1556  }
1557 
1558  stock = player GetWeaponAmmoStock( weapon );
1559  start = player GetFractionStartAmmo( weapon );
1560  clip = weapon.clipSize;
1561  clip *= GetDvarFloat( "scavenger_clip_multiplier", 1 );
1562  clip = Int( clip );
1563 
1564  if ( isdefined( level.weaponLauncherEx41 ) && ( weapon.statIndex == level.weaponLauncherEx41.statIndex ) )
1565  clip = 1;
1566 
1567  maxAmmo = weapon.maxAmmo;
1568 
1569  if ( stock < maxAmmo - clip )
1570  {
1571  ammo = stock + clip;
1572  player SetWeaponAmmoStock( weapon, ammo );
1573  player.scavenged = true;
1574  }
1575  else
1576  {
1577  player SetWeaponAmmoStock( weapon, maxAmmo );
1578  player.scavenged = true;
1579  }
1580  }
1581 }
1582 
1584 {
1585  self waittill("disconnect");
1586  if(isDefined(self.scavenger_icon))
1587  self.scavenger_icon ‪destroy();
1588 }
1589 
1591 {
1592  if( level.wagerMatch )
1593  return;
1594  if( ‪IS_TRUE(level.noScavenger))
1595  return;
1596 
1597  self.scavenger_icon = NewClientHudElem( self );
1598  if(isDefined(self.scavenger_icon))
1599  {
1601 
1602  self.scavenger_icon.horzAlign = "center";
1603  self.scavenger_icon.vertAlign = "middle";
1604  self.scavenger_icon.alpha = 0;
1605 
1606  width = 64;
1607  height = 64;
1608 
1609  if ( level.splitscreen )
1610  {
1611  width = Int( width * 0.5 );
1612  height = Int( height * 0.5 );
1613  }
1614 
1615  self.scavenger_icon.x = -width/2;
1616  self.scavenger_icon.y = 16;
1617 
1618  self.scavenger_icon setShader( "hud_scavenger_pickup", width, height );
1619  }
1620 }
1621 
1622 function ‪drop_scavenger_for_death( attacker )
1623 {
1624  if ( level.wagerMatch )
1625  return;
1626 
1627  if ( !isdefined( attacker ) )
1628  return;
1629 
1630  if ( attacker == self )
1631  return;
1632 
1633  if ( level.gameType == "hack" )
1634  {
1635  item = self dropScavengerItem( GetWeapon( "scavenger_item_hack" ) );
1636  }
1637  else if ( isplayer( Attacker ) )
1638  {
1639  item = self dropScavengerItem( GetWeapon( "scavenger_item" ) );
1640  }
1641  else
1642  {
1643  return;
1644  }
1645 
1646  item thread ‪scavenger_think();
1647 }
1648 
1649 
1650 // if we need to store multiple drop limited weapons, we'll need to store an array on the player entity
1651 function ‪add_limited_weapon( weapon, owner, num_drops )
1652 {
1653  limited_info = SpawnStruct();
1654  limited_info.weapon = weapon;
1655  limited_info.drops = num_drops;
1656 
1657  owner.limited_info = limited_info;
1658 }
1659 
1660 function ‪should_drop_limited_weapon( weapon, owner )
1661 {
1662  limited_info = owner.limited_info;
1663 
1664  if ( !isdefined( limited_info ) )
1665  {
1666  return true;
1667  }
1668 
1669  if ( limited_info.weapon != weapon )
1670  {
1671  return true;
1672  }
1673 
1674  if ( limited_info.drops <= 0 )
1675  {
1676  return false;
1677  }
1678 
1679  return true;
1680 }
1681 
1682 
1683 function ‪drop_limited_weapon( weapon, owner, item )
1684 {
1685  limited_info = owner.limited_info;
1686 
1687  if ( !isdefined( limited_info ) )
1688  {
1689  return;
1690  }
1691 
1692  if ( limited_info.weapon != weapon )
1693  {
1694  return;
1695  }
1696 
1697  limited_info.drops = limited_info.drops - 1;
1698  owner.limited_info = undefined;
1699 
1700  item thread ‪limited_pickup( limited_info );
1701 }
1702 
1703 
1704 function ‪limited_pickup( limited_info )
1705 {
1706  self endon( "death" );
1707  self waittill( "trigger", player, item );
1708 
1709  if ( !isdefined( item ) )
1710  {
1711  return;
1712  }
1713 
1714  player.limited_info = limited_info;
1715 }
1716 
1717 // self is the grenade
1718 function ‪track_cooked_detonation( attacker, weapon, cookTime )
1719 {
1720  self endon( "trophy_destroyed" );
1721 
1722  self waittill( "explode", origin, surface );
1723 
1724  if ( weapon.rootWeapon == level.weaponFlashGrenade )
1725  {
1726  level thread ‪nineBang_doNineBang( attacker, weapon, origin, cookTime );
1727  }
1728 }
1729 
1730 function ‪nineBang_doNineBang( attacker, weapon, pos, cookTime )
1731 {
1732  level endon( "game_ended" );
1733 
1734  maxStages = 4;
1735  maxRadius = 20;
1736  minDelay = 0.15;
1737  maxdelay = 0.3;
1738 
1739  explosionRadiusSq = weapon.explosionRadius * weapon.explosionRadius;
1740  explosionRadiusMinSq = weapon.explosionInnerRadius * weapon.explosionInnerRadius;
1741 
1742  cookStages = cookTime / weapon.cookOffHoldTime * maxStages + 1;
1743 
1744  detonations = 0;
1745 
1746  if ( cookStages < 2 )
1747  {
1748  return;
1749  }
1750  else if ( cookStages < 3 )
1751  {
1752  detonations = 3;
1753  }
1754  else if ( cookStages < 4 )
1755  {
1756  detonations = 6;
1757  }
1758  else // max
1759  {
1760  detonations = 9;
1761  //nineBang_DoEmpDamage( attacker, weapon, pos );
1762  }
1763 
1764  wait( RandomFloatRange( minDelay, maxDelay ) );
1765 
1766  // the nine bang will go off detonations-1 times as it already exploded once, and flash everything in the vacinity
1767  for( i = 1; i < detonations; i++ )
1768  {
1769  newPos = level ‪nineBang_getSubExplosionPos( pos, maxRadius );
1770 
1771  PlaySoundAtPosition( "wpn_flash_grenade_explode", newPos );
1772  PlayFX( level._effect["flashNineBang"], newPos );
1773 
1774  closestPlayers = ArraySort( level.players, newPos, true );
1775 
1776  // get players within the radius
1777  foreach( player in closestPlayers )
1778  {
1779  if ( !isdefined( player ) || !IsAlive( player ) )
1780  {
1781  continue;
1782  }
1783 
1784  if ( player.sessionstate != "playing" )
1785  {
1786  continue;
1787  }
1788 
1789  viewOrigin = player GetEye();
1790 
1791  // first make sure they are within distance
1792  dist = DistanceSquared( pos, viewOrigin );
1793 
1794  if( dist > explosionRadiusSq )
1795  {
1796  break;
1797  }
1798 
1799  // now make sure they can be hit by it
1800  if( !BulletTracePassed( pos, viewOrigin, false, player ) )
1801  continue;
1802 
1803  if ( dist <= explosionRadiusMinSq )
1804  percent_distance = 1.0;
1805  else
1806  percent_distance = 1.0 - ( dist - explosionRadiusMinSq ) / ( explosionRadiusSq - explosionRadiusMinSq );
1807 
1808  forward = AnglesToForward( player GetPlayerAngles() );
1809 
1810  toBlast = pos - viewOrigin;
1811  toBlast = VectorNormalize( toBlast );
1812 
1813  percent_angle = 0.5 * ( 1.0 + VectorDot( forward, toBlast ) );
1814 
1815  player notify( "flashbang", percent_distance, percent_angle, attacker );
1816  }
1817 
1818  wait( RandomFloatRange( minDelay, maxDelay ) );
1819  }
1820 }
1821 
1822 function ‪nineBang_getSubExplosionPos( startPos, range )
1823 {
1824  offset = ( RandomFloatRange( -1.0 * range, range ), RandomFloatRange( -1.0 * range, range ), 0 );
1825  newPos = startPos + offset;
1826 
1827  // make sure we don't spawn through walls
1828  if ( BulletTracePassed( startPos, newPos, false, undefined ) )
1829  {
1830  return newPos;
1831  }
1832 
1833  return startPos;
1834 }
1835 
1836 function ‪nineBang_DoEmpDamage( player, weapon, position )
1837 {
1838  kNineBangEmpRadius = 512;
1839  radiusSq = kNineBangEmpRadius * kNineBangEmpRadius;
1840 
1841  PlaySoundAtPosition( "wpn_emp_explode", position );
1842 
1843  level ‪empgrenade::empExplosionDamageEnts( player, weapon, position, kNineBangEmpRadius, false );
1844 
1845  foreach ( targetEnt in level.players )
1846  {
1847  if ( ‪nineBang_empCanDamage( targetEnt, position, radiusSq, false, 0 ) )
1848  {
1849  targetEnt notify( "emp_grenaded", player );
1850  }
1851  }
1852 }
1853 
1854 function ‪nineBang_empCanDamage( ent, pos, radiusSq, doLOS, startRadius )
1855 {
1856  entpos = ent.origin;
1857  distSq = DistanceSquared( pos, entpos );
1858  return ( distSq < radiusSq
1859  && ( !doLOS || ‪weapons::weaponDamageTracePassed( pos, entpos, startRadius, ent ) ) );
1860 }
1861 
1862 
1863 function ‪track_multi_detonation( ownerEnt, weapon, cookTime ) // self is the grenade
1864 {
1865  self endon( "trophy_destroyed" );
1866 
1867  self waittill( "explode", origin, surface );
1868 
1869  if ( weapon.rootWeapon == GetWeapon( "frag_grenade_grenade" ) )
1870  {
1871  for ( i = 0; i < weapon.multiDetonation; i++ )
1872  {
1873  // spawn a magic grenade
1874  if ( !isdefined( ownerEnt ) )
1875  {
1876  return;
1877  }
1878 
1879  multiblastWeapon = GetWeapon( "frag_multi_blast" );
1880 
1881  // get a velocity
1882  dir = level ‪multi_detonation_get_cluster_launch_dir( i, weapon.multiDetonation );
1883  vel = dir * multiblastWeapon.multiDetonationFragmentSpeed;
1884  fuseTime = multiblastWeapon.fusetime/1000;
1885 
1886  grenade = ownerEnt MagicGrenadeType( multiblastWeapon, origin, vel, fuseTime );
1887 
1889  }
1890  }
1891 }
1892 
1894 {
1895  pitch = 45;
1896  yaw = -180 + ( 360 / multiVal ) * index;
1897 
1898  angles = ( pitch, yaw, 45 );
1899 
1900  dir = AnglesToForward( angles );
1901 
1902  return dir;
1903 }
1904 
1905 function ‪should_suppress_damage( weapon, inflictor )
1906 {
1907  if ( !isdefined( weapon ) )
1908  return false;
1909 
1910  if ( !isdefined( self ) )
1911  return false;
1912 
1913  if ( isdefined( level.weaponSpecialDiscGun ) && weapon.statIndex == level.weaponSpecialDiscGun.statIndex )
1914  {
1915  if ( isdefined( inflictor ) )
1916  {
1917  ‪DEFAULT( inflictor.hit_info, [] );
1918 
1919  victimEntNum = self GetEntityNumber();
1920 
1921  if ( isdefined( inflictor.hit_info[ victimEntNum ] ) )
1922  return true;
1923 
1924  inflictor.hit_info[ victimEntNum ] = true;
1925  }
1926  }
1927 
1928  return false;
1929 }
‪processScoreEvent
‪function processScoreEvent(event, player, victim, weapon)
Definition: scoreevents_shared.gsc:19
‪drop_all_to_ground
‪function drop_all_to_ground(origin, radius)
Definition: _weapons.gsc:663
‪track_multi_detonation
‪function track_multi_detonation(ownerEnt, weapon, cookTime)
Definition: _weapons.gsc:1863
‪scavenger_think
‪function scavenger_think()
Definition: _weapons.gsc:1471
‪watchTabunGrenadeDetonation
‪function watchTabunGrenadeDetonation(owner)
Definition: _tabun.gsc:72
‪startTime
‪class AnimationAdjustmentInfoZ startTime
‪drop_grenades_to_ground
‪function drop_grenades_to_ground(origin, radius)
Definition: _weapons.gsc:676
‪damage_trace
‪function damage_trace(from, to, startRadius, ignore)
Definition: _weapons.gsc:1150
‪check_stuck_to_player
‪function check_stuck_to_player(deleteOnTeamChange, awardScoreEvent, weapon)
Definition: _weapons.gsc:873
‪initWeaponAttachments
‪function initWeaponAttachments(weapon)
Definition: loadout_shared.gsc:41
‪multi_detonation_get_cluster_launch_dir
‪function multi_detonation_get_cluster_launch_dir(index, multiVal)
Definition: _weapons.gsc:1893
‪is_bot
‪function is_bot()
Definition: util_shared.gsc:2488
‪is_side_arm
‪function is_side_arm(weapon)
Definition: weapons_shared.gsc:20
‪ammo_scavenger
‪function ammo_scavenger(weapon)
Definition: _trophy_system.gsc:444
‪on_player_connect
‪function on_player_connect()
Definition: _weapons.gsc:75
‪GADGET_TYPE_HERO_WEAPON
‪#define GADGET_TYPE_HERO_WEAPON
Definition: _ability_util.gsh:18
‪getLastWeapon
‪function getLastWeapon()
Definition: util_shared.gsc:2498
‪damage_ent
‪function damage_ent(eInflictor, eAttacker, iDamage, sMeansOfDeath, weapon, damagepos, damagedir)
Definition: _weapons.gsc:1185
‪damage_trace_passed
‪function damage_trace_passed(from, to, startRadius, ignore)
Definition: _weapons.gsc:1144
‪delete_pickup_after_aWhile
‪function delete_pickup_after_aWhile()
Definition: _weapons.gsc:412
‪ignore
‪function ignore(str_system)
Definition: system_shared.csc:165
‪scavengedGrenade
‪function scavengedGrenade()
Definition: challenges_shared.gsc:1484
‪detonate
‪function detonate(attacker)
Definition: vehicle_ai_shared.gsc:1650
‪launch
‪function launch(ent_1, str_tag1, ent_2, str_tag2, str_beam_type)
Definition: beam_shared.csc:11
‪on_start_gametype
‪function on_start_gametype(func, obj)
Definition: callbacks_shared.csc:285
‪delete_ent_on_owner_death
‪function delete_ent_on_owner_death(owner)
Definition: _weapons.gsc:1341
‪waittill_any_return
‪function waittill_any_return(string1, string2, string3, string4, string5, string6, string7)
Definition: util_shared.csc:212
‪stow_on_back
‪function stow_on_back(current)
Definition: weapons_shared.gsc:92
‪empExplosionDamageEnts
‪function empExplosionDamageEnts(owner, weapon, origin, radius, damagePlayers)
Definition: _empgrenade.gsc:302
‪getLoadoutItemFromDDLStats
‪function getLoadoutItemFromDDLStats(customClassNum, loadoutSlot)
Definition: loadout_shared.gsc:29
‪track
‪function track()
Definition: _weapons.gsc:251
‪trace
‪function trace(from, to, target)
Definition: grapple.gsc:369
‪nineBang_DoEmpDamage
‪function nineBang_DoEmpDamage(player, weapon, position)
Definition: _weapons.gsc:1836
‪spawn
‪function spawn(v_origin=(0, 0, 0), v_angles=(0, 0, 0))
Definition: struct.csc:23
‪nineBang_doNineBang
‪function nineBang_doNineBang(attacker, weapon, pos, cookTime)
Definition: _weapons.gsc:1730
‪watch_usage
‪function watch_usage()
Definition: _weapons.gsc:513
‪IS_TRUE
‪#define IS_TRUE(__a)
Definition: shared.gsh:251
‪a
‪function add_remove_list a
Definition: util_shared.csc:906
‪reverse
‪function reverse()
Definition: traps_shared.gsc:1071
‪init_shared
‪function init_shared()
Definition: _weapons.gsc:34
‪debugline
‪function debugline(a, b, color)
Definition: _weapons.gsc:1247
‪get_damageable_ents
‪function get_damageable_ents(pos, radius, doLOS, startRadius)
Definition: _weapons.gsc:1025
‪add_limited_weapon
‪function add_limited_weapon(weapon, owner, num_drops)
Definition: _weapons.gsc:1651
‪init
‪function init()
Definition: _weapons.gsc:55
‪damage_notify_wrapper
‪function damage_notify_wrapper(damage, attacker, direction_vec, point, type, modelName, tagName, partName, iDFlags)
Definition: util_shared.gsc:1076
‪IsEnemyPlayer
‪function IsEnemyPlayer(player)
Definition: util_shared.csc:1220
‪watch_missile_usage
‪function watch_missile_usage()
Definition: _weapons.gsc:638
‪damage
‪function damage(trap)
Definition: _zm_trap_electric.gsc:116
‪get_killstreak_weapon
‪function get_killstreak_weapon(killstreak)
Definition: killstreaks_shared.gsc:106
‪limited_pickup
‪function limited_pickup(limited_info)
Definition: _weapons.gsc:1704
‪on_damage
‪function on_damage(eAttacker, eInflictor, weapon, meansOfDeath, damage)
Definition: _weapons.gsc:1259
‪scavenger_hud_destroyOnDisconnect
‪function scavenger_hud_destroyOnDisconnect()
Definition: _weapons.gsc:1583
‪begin_grenade_tracking
‪function begin_grenade_tracking()
Definition: _weapons.gsc:744
‪getTimeFromLevelStart
‪function getTimeFromLevelStart()
Definition: _weapons.gsc:956
‪detach_all_weapons
‪function detach_all_weapons()
Definition: weapons_shared.gsc:55
‪DEFAULT
‪#define DEFAULT(__var, __default)
Definition: shared.gsh:270
‪should_suppress_damage
‪function should_suppress_damage(weapon, inflictor)
Definition: _weapons.gsc:1905
‪drop_for_death
‪function drop_for_death(attacker, sWeapon, sMeansOfDeath)
Definition: _weapons.gsc:306
‪on_spawned
‪function on_spawned(func, obj)
Definition: callbacks_shared.csc:245
‪update_stowed_weapon
‪function update_stowed_weapon()
Definition: _weapons.gsc:1351
‪is_primary_weapon
‪function is_primary_weapon(weapon)
Definition: weapons_shared.gsc:15
‪is_killstreak_weapon
‪function is_killstreak_weapon(weapon)
Definition: killstreaks_shared.gsc:16
‪wait_network_frame
‪function wait_network_frame(n_count=1)
Definition: util_shared.gsc:64
‪watch_grenade_usage
‪function watch_grenade_usage()
Definition: _weapons.gsc:603
‪play_concussion_sound
‪function play_concussion_sound(duration)
Definition: _weapons.gsc:1320
‪watch_for_grenade_launcher_duds
‪function watch_for_grenade_launcher_duds()
Definition: _weapons.gsc:1002
‪check_stuck_to_shield
‪function check_stuck_to_shield()
Definition: _riotshield.gsc:652
‪is_inventory
‪function is_inventory(weapon)
Definition: weapons_shared.gsc:25
‪should_drop_limited_weapon
‪function should_drop_limited_weapon(weapon, owner)
Definition: _weapons.gsc:1660
‪update_timings
‪function update_timings(newTime)
Definition: _weapons.gsc:156
‪watch_grenade_cancel
‪function watch_grenade_cancel()
Definition: _weapons.gsc:688
‪watch_missile_death
‪function watch_missile_death()
Definition: _weapons.gsc:657
‪begin_other_grenade_tracking
‪function begin_other_grenade_tracking()
Definition: _weapons.gsc:832
‪loadout_get_offhand_weapon
‪function loadout_get_offhand_weapon(stat)
Definition: _weapons.gsc:1425
‪stow_on_hip
‪function stow_on_hip()
Definition: weapons_shared.gsc:132
‪watch_for_throwbacks
‪function watch_for_throwbacks()
Definition: _weapons.gsc:921
‪watch_offhand_end
‪function watch_offhand_end()
Definition: _weapons.gsc:708
‪turn_grenade_into_a_dud
‪function turn_grenade_into_a_dud(weapon, isThrownGrenade, player)
Definition: _weapons.gsc:965
‪check_hatchet_bounce
‪function check_hatchet_bounce()
Definition: _weapons.gsc:895
‪wait_and_delete_dud
‪function wait_and_delete_dud(waitTime)
Definition: _weapons.gsc:946
‪may_drop
‪function may_drop(weapon)
Definition: _weapons.gsc:286
‪is_grenade
‪function is_grenade(weapon)
Definition: weapons_shared.gsc:30
‪watch_for_grenade_duds
‪function watch_for_grenade_duds()
Definition: _weapons.gsc:989
‪on_player_spawned
‪function on_player_spawned()
Definition: _weapons.gsc:83
‪update_last_held_weapon_timings
‪function update_last_held_weapon_timings(newTime)
Definition: _weapons.gsc:134
‪scavenger_hud_create
‪function scavenger_hud_create()
Definition: _weapons.gsc:1590
‪destroy
‪function destroy(watcher, owner)
Definition: _decoy.gsc:108
‪loadout_get_offhand_count
‪function loadout_get_offhand_count(stat)
Definition: _weapons.gsc:1446
‪track_cooked_detonation
‪function track_cooked_detonation(attacker, weapon, cookTime)
Definition: _weapons.gsc:1718
‪is_using_offhand_equipment
‪function is_using_offhand_equipment()
Definition: _weapons.gsc:729
‪on_connect
‪function on_connect()
Definition: _arena.gsc:20
‪watch_pickup
‪function watch_pickup()
Definition: _weapons.gsc:424
‪nineBang_empCanDamage
‪function nineBang_empCanDamage(ent, pos, radiusSq, doLOS, startRadius)
Definition: _weapons.gsc:1854
‪isHacked
‪function isHacked()
Definition: util_shared.gsc:2493
‪IS_BONUSZM
‪#define IS_BONUSZM
Definition: shared.gsh:532
‪drop_scavenger_for_death
‪function drop_scavenger_for_death(attacker)
Definition: _weapons.gsc:1622
‪mayApplyScreenEffect
‪function mayApplyScreenEffect()
Definition: util_shared.gsc:2613
‪stuck_to_player_team_change
‪function stuck_to_player_team_change(player)
Definition: _weapons.gsc:903
‪anyObjectsInWorld
‪function anyObjectsInWorld(weapon)
Definition: _weaponobjects.gsc:1690
‪name
‪class GroundFx name
‪nineBang_getSubExplosionPos
‪function nineBang_getSubExplosionPos(startPos, range)
Definition: _weapons.gsc:1822
‪flash_scavenger_icon
‪function flash_scavenger_icon()
Definition: _weapons.gsc:1464
‪TRACK_WEAPON_SHOT_FIRED
‪#define TRACK_WEAPON_SHOT_FIRED
Definition: _weapons.gsc:563
‪watch_weapon_change
‪function watch_weapon_change()
Definition: _weapons.gsc:106
‪drop_limited_weapon
‪function drop_limited_weapon(weapon, owner, item)
Definition: _weapons.gsc:1683
‪weaponDamageTracePassed
‪function weaponDamageTracePassed(from, to, startRadius, ignore)
Definition: weapons_shared.gsc:155
‪WAIT_SERVER_FRAME
‪#define WAIT_SERVER_FRAME
Definition: shared.gsh:265
‪track_fire
‪function track_fire(curWeapon)
Definition: _weapons.gsc:565