‪Black Ops 3 Source Code Explorer  0.1
‪An script explorer for Black Ops 3 by ZeRoY
_gadget_clone.gsc
Go to the documentation of this file.
1 #using scripts\codescripts\struct;
2 
3 #using scripts\shared\callbacks_shared;
4 #using scripts\shared\clientfield_shared;
5 #using scripts\shared\flagsys_shared;
6 #using scripts\shared\visionset_mgr_shared;
7 #using scripts\shared\spawner_shared;
8 #using scripts\shared\util_shared;
9 #using scripts\shared\ai_shared;
10 #using scripts\shared\ai_puppeteer_shared;
11 #using scripts\shared\ai\systems\blackboard;
12 #using scripts\shared\ai\systems\shared;
13 #using scripts\shared\abilities\_ability_player;
14 #using scripts\shared\abilities\_ability_util;
15 #using scripts\shared\array_shared;
16 #using scripts\shared\_oob;
17 #using scripts\shared\scoreevents_shared;
18 
19 #insert scripts\shared\ai\systems\blackboard.gsh;
20 #insert scripts\shared\shared.gsh;
21 #insert scripts\shared\version.gsh;
22 #insert scripts\shared\abilities\_ability_util.gsh;
23 #insert scripts\shared\statstable_shared.gsh;
24 
25 #using scripts\shared\system_shared;
26 
27 ‪REGISTER_SYSTEM( "gadget_clone", &‪__init__, undefined )
28 
29 #define CLONE_COUNT 3
30 #define CLONE_HEIGHT 72
31 #define CLONE_MAX_CLONES_ALLOWED 20 // don't change this.
32 #define CLONE_HEALTH_MULTIPLIER 1.5
33 #define CLONE_SPAWN_DISTANCE 60
34 #define CLONE_SPAWN_HEIGHT_OFFSET 5
35 #define CLONE_SPAWN_VELOCITY_OFFSET 17
36 #define CLONE_SPAWN_FACING_OFFSET 17
37 #define CLONE_SPAWN_FROM_PLAYER_MAX 500
38 #define CLONE_FIRST_POINT_DISTANCE 1000
39 #define CLONE_MAX_SEARCH_RADIUS_INITIAL 450
40 #define CLONE_MIN_SEARCH_RADIUS 500
41 #define CLONE_MAX_SEARCH_RADIUS 750
42 #define CLONE_PATH_DISTANCE_THRESHOLD 120
43 #define CLONE_PATH_DISTANCE_THRESHOLD_SQ CLONE_PATH_DISTANCE_THRESHOLD * CLONE_PATH_DISTANCE_THRESHOLD
44 #define FAKEFIRE_MAX_RANGE 750
45 #define FAKEFIRE_MAX_RANGE_SQR ( FAKEFIRE_MAX_RANGE * FAKEFIRE_MAX_RANGE )
46 #define FAKEFIRE_MIN_SHOTS 1
47 #define FAKEFIRE_MAX_SHOTS 4
48 #define FAKEFIRE_MIN_WAIT_DURATION .5
49 #define FAKEFIRE_MAX_WAIT_DURATION 3
50 #define ORB_TRAVEL_VELOCITY 800 // ( Max time is equivalent to CLONE_SPAWN_FROM_PLAYER_MAX / ORB_TRAVEL_VELOCITY )
51 #define ORB_HEIGHT_OFFSET ( 40 - CLONE_SPAWN_HEIGHT_OFFSET )
52 #define CLONE_ORB_FX "player/fx_plyr_clone_reaper_orb"
53 #define CLONE_VANISH_FX "player/fx_plyr_clone_vanish"
54 #define CLONE_APPEAR_FX "player/fx_plyr_clone_reaper_appear"
55 
56 
57 #precache( "fx", CLONE_VANISH_FX );
58 #precache( "fx", CLONE_ORB_FX );
59 #precache( "fx", CLONE_APPEAR_FX );
60 
61 function ‪__init__()
62 {
68 
70 
71  ‪clientfield::register( "actor", "clone_activated", ‪VERSION_SHIP, 1, "int" );
72  ‪clientfield::register( "actor", "clone_damaged", ‪VERSION_SHIP, 1, "int" );
73  ‪clientfield::register( "allplayers", "clone_activated", ‪VERSION_SHIP, 1, "int" );
74 
75  level._clone = [];
76 }
77 
78 function ‪gadget_clone_is_inuse( slot )
79 {
80  return self GadgetIsActive( slot );
81 }
82 
84 {
85  // returns true when the gadget is flickering
86  return self GadgetFlickering( slot );
87 }
88 
89 function ‪gadget_clone_on_flicker( slot, weapon )
90 {
91  // excuted when the gadget flickers
92 }
93 
94 function ‪gadget_clone_on_give( slot, weapon )
95 {
96 
97 }
98 
99 function ‪gadget_clone_on_take( slot, weapon )
100 {
101 
102 }
103 
104 //self is the player
106 {
107  // setup up stuff on player connect
108 }
109 
110 function ‪killClones( player )
111 {
112  if( isDefined( player._clone ) )
113  {
114  foreach( clone in player._clone )
115  {
116  if( isDefined( clone ) )
117  {
118  clone notify( "clone_shutdown" );
119  }
120  }
121  }
122 }
123 
124 function ‪is_jumping()
125 {
126  // checking PMF_JUMPING in code would give more accurate results
127  ground_ent = self GetGroundEnt();
128  return (!isdefined(ground_ent));
129 }
130 
131 function ‪CalculateSpawnOrigin( origin, angles, cloneDistance )
132 {
133  player = self;
134 
135  startAngles = [];
136 
137  testangles = [];
138  testangles[0] = ( 0, 0, 0);
139  testangles[1] = ( 0, -30, 0);
140  testangles[2] = ( 0, 30, 0);
141  testangles[3] = ( 0, -60, 0);
142  testangles[4] = ( 0, 60, 0);
143  testangles[5] = ( 0, 90, 0);
144  testangles[6] = ( 0, -90, 0);
145  testangles[7] = ( 0, 120, 0);
146  testangles[8] = ( 0, -120, 0);
147  testangles[9] = ( 0, 150, 0);
148  testangles[10] = ( 0, -150, 0);
149  testangles[11] = ( 0, 180, 0);
150 
151  validSpawns = spawnStruct();
152 
153  validPositions = [];
154  validAngles = [];
155 
156  zoffests = [];
157  zoffests[0] = ‪CLONE_SPAWN_HEIGHT_OFFSET;
158  zoffests[1] = 0;
159  if( player ‪is_jumping() )
160  zoffests[2] = -‪CLONE_SPAWN_HEIGHT_OFFSET;
161 
162  foreach( zoff in zoffests )
163  {
164  for( i = 0; i < testangles.size; i++ )
165  {
166  startAngles[i] = ( 0, angles[1], 0 );
167  startPoint = origin + VectorScale( anglestoforward( startAngles[i] + testangles[i]), cloneDistance );
168  startPoint += ( 0, 0, zoff );
169 
170  if( PlayerPositionValidIgnoreEnt( startPoint, self ) )
171  {
172  closestNavMeshPoint = GetClosestPointOnNavMesh( startpoint, ‪CLONE_SPAWN_FROM_PLAYER_MAX );
173  if( isDefined( closestNavMeshPoint ) )
174  {
175  startPoint = closestNavMeshPoint;
176 
177  // Trace downward to find out the actual position on the terrain to spawn the clone.
178  const height_diff = 24;
179  ‪trace = GroundTrace( startPoint + (0, 0, height_diff), startPoint - ( 0, 0, height_diff ), false, false, false );
180 
181  if ( IsDefined( ‪trace[ "position" ] ) )
182  {
183  startPoint = ‪trace[ "position" ];
184  }
185  }
186  validpositions[ validpositions.size ] = startPoint;
187  validAngles[ validAngles.size ] = startAngles[i] + testangles[i];
188 
189  if( validAngles.size == ‪CLONE_COUNT )
190  {
191  break;
192  }
193  }
194  }
195 
196  if( validAngles.size == ‪CLONE_COUNT )
197  {
198  break;
199  }
200  }
201 
202  validspawns.validPositions = validpositions;
203  validspawns.validAngles = validAngles;
204  return validspawns;
205 }
206 
207 function ‪insertClone( clone )
208 {
209  insertedClone = false;
210  for( i = 0; i < ‪CLONE_MAX_CLONES_ALLOWED; i++ )
211  {
212  if( !isDefined( level._clone[i] ) )
213  {
214  level._clone[i] = clone;
215  insertedClone = true;
216 
217  /#
218  PrintLn( "inserted at index: " + i + " clone count is: " + level._clone.size );
219  #/
220  break;
221  }
222  }
223  assert( insertedClone );
224 }
225 
226 function ‪removeClone( clone )
227 {
228  for( i = 0; i < ‪CLONE_MAX_CLONES_ALLOWED; i++ )
229  {
230  if( isDefined( level._clone[i] ) && ( level._clone[i] == clone ) )
231  {
232  level._clone[i] = undefined;
233  ‪array::remove_undefined( level._clone );
234 
235  /#
236  PrintLn( "removed clone at index: " + i + " clone count is: " + level._clone.size );
237  #/
238  break;
239  }
240  }
241 }
242 
244 {
245  assert( level._clone.size == ‪CLONE_MAX_CLONES_ALLOWED );
246 
247  oldestClone = undefined;
248  for( i = 0; i < ‪CLONE_MAX_CLONES_ALLOWED; i++ )
249  {
250  if( !isDefined( oldestClone ) && isDefined( level._clone[i] ) )
251  {
252  oldestClone = level._clone[i];
253  oldestIndex = i;
254  }
255  else if( isDefined( level._clone[i] ) && ( level._clone[i].spawnTime < oldestClone.spawnTime ) )
256  {
257  oldestClone = level._clone[i];
258  oldestIndex = i;
259  }
260  }
261 
262 
263  /#
264  PrintLn( "Exceeded max clones: removing clone at index: " + i + " clone count is: " + level._clone.size );
265  #/
266 
267  level._clone[oldestIndex] notify ( "clone_shutdown" );
268  level._clone[oldestIndex] = undefined;
269 
270  ‪array::remove_undefined( level._clone );
271 }
272 
273 function ‪spawnClones() // self is player
274 {
275  self endon( "death" );
276 
277  self ‪killClones( self );
278 
279  self._clone = [];
280  velocity = self getvelocity();
281  velocity = velocity + ( 0, 0, -velocity[2] );
282  velocity = Vectornormalize( velocity );
283  origin = self.origin + velocity * ‪CLONE_SPAWN_VELOCITY_OFFSET + VectorScale( anglestoforward( self getangles() ), ‪CLONE_SPAWN_FACING_OFFSET );
284  validSpawns = ‪CalculateSpawnOrigin( origin, self getangles(), ‪CLONE_SPAWN_DISTANCE );
285 
286  // If there weren't enough valid spawn positions, try extending the spawn distance to find additional spawn points.
287  if ( validSpawns.validPositions.size < ‪CLONE_COUNT )
288  {
289  validExtendedSpawns = ‪CalculateSpawnOrigin( origin, self getangles(), ‪CLONE_SPAWN_DISTANCE * 3 );
290 
291  for ( index = 0; index < validExtendedSpawns.validPositions.size && validSpawns.validPositions.size < ‪CLONE_COUNT; index++ )
292  {
293  validSpawns.validPositions[ validSpawns.validPositions.size ] = validExtendedSpawns.validPositions[ index ];
294  validSpawns.validAngles[ validSpawns.validAngles.size ] = validExtendedSpawns.validAngles[ index ];
295  }
296  }
297 
298  for( i = 0; i < validSpawns.validpositions.size; i++ )
299  {
300  travelDistance = Distance( validSpawns.validpositions[i], self.origin );
301  validspawns.spawnTimes[i] = travelDistance / ‪ORB_TRAVEL_VELOCITY;
302  self thread ‪_CloneOrbFx( validSpawns.validpositions[i], validspawns.spawnTimes[i] );
303  }
304  for( i = 0; i < validSpawns.validpositions.size; i++ )
305  {
306  if( level._clone.size < ‪CLONE_MAX_CLONES_ALLOWED )
307  {
308  // do nothing here
309  }
310  else
311  {
313  }
314 
315  clone = SpawnActor(
316  "spawner_bo3_human_male_reaper_mp",
317  validSpawns.validpositions[i],
318  validSpawns.validAngles[i],
319  "",
320  true );
321 
322  /# RecordCircle( validSpawns.validpositions[i], 2, ‪ORANGE, "Animscript", clone ); #/
323 
324  ‪_ConfigureClone( clone, self, AnglesToForward( validSpawns.validAngles[i] ), validspawns.spawnTimes[i] );
325  self._clone[self._clone.size] = clone;
326  ‪insertClone( clone );
328  }
329  self notify( "reveal_clone" );
330 
331  if( self ‪oob::IsOutOfBounds() )
332  {
333  ‪gadget_clone_off( self, undefined );
334  }
335 }
336 
337 function ‪gadget_clone_on( slot, weapon )
338 {
339  self ‪clientfield::set( "clone_activated", 1 );
340  self ‪flagsys::set( "clone_activated" );
341  fx = ‪PlayFx( ‪CLONE_APPEAR_FX, self.origin, AnglesToForward( self getangles() ));
342  fx.team = self.team;
343  thread ‪spawnClones();
344 }
345 
346 #define CLONE_NOT_MOVING_DIST_SQ SQR( 24 )
347 #define CLONE_NOT_MOVING_POLL_TIME 2000
348 function private ‪_UpdateClonePathing() // self is clone
349 {
350  self endon( "death" );
351  while( true )
352  {
353  if ( GetDvarInt( "tu1_gadgetCloneSwimming", 1 ) )
354  {
355  if ( ( self.origin[2] + ‪CLONE_HEIGHT / 2.0 ) <= GetWaterHeight( self.origin ) )
356  {
358 
359  self SetGoal( self.origin, true );
360 
361  wait ( 0.5 );
362  continue;
363  }
364  }
365 
366  if ( GetDvarInt( "tu1_gadgetCloneCrouching", 1 ) )
367  {
368  if ( !IsDefined( self.lastKnownPos ) )
369  {
370  self.lastKnownPos = self.origin;
371  self.lastKnownPosTime = GetTime();
372  }
373 
374  // If the clone hasn't moved by atleast CLONE_NOT_MOVING_DIST_SQ within
375  // CLONE_NOT_MOVING_POLL_TIME then make the clone go crouched.
376  if ( DistanceSquared( self.lastKnownPos, self.origin ) < ‪CLONE_NOT_MOVING_DIST_SQ && !self HasPath() )
377  {
379 
380  wait ( 0.5 );
381  continue;
382  }
383 
384  if ( ( self.lastKnownPosTime + ‪CLONE_NOT_MOVING_POLL_TIME ) <= GetTime() )
385  {
386  self.lastKnownPos = self.origin;
387  self.lastKnownPosTime = GetTime();
388  }
389  }
390 
391  distance = 0;
392  if( isDefined( self._clone_goal ) )
393  {
394  distance = DistanceSquared( self._clone_goal, self.origin );
395  }
396 
397  if( distance < ‪CLONE_PATH_DISTANCE_THRESHOLD_SQ || !self HasPath() )
398  {
399  forward = AnglesToForward( self getangles() );
400  searchOrigin = self.origin + forward * ‪CLONE_MAX_SEARCH_RADIUS;
401  self._goal_center_point = searchOrigin;
402 
403  queryResult = PositionQuery_Source_Navigation(
404  self._goal_center_point,
408  100,
409  self );
410 
411  if( queryResult.data.size == 0 )
412  {
413  queryResult = PositionQuery_Source_Navigation(
414  self.origin,
418  100,
419  self );
420  }
421 
422  if( queryResult.data.size > 0 )
423  {
424  randIndex = RandomIntRange( 0, queryResult.data.size );
425 
426  self setgoalpos( queryResult.data[ randIndex ].origin, true );
427  self._clone_goal = queryresult.data[ randIndex ].origin;
428  self._clone_goal_max_dist = ‪CLONE_MAX_SEARCH_RADIUS;
429  }
430  }
431  //util::drawcylinder( self._goal_center_point, self._clone_goal_max_dist, 10, .5, "stop_notify_asdf" );
432  //util::debug_sphere( self._clone_goal, 10, ( 1, 0, 1 ), 1, 1 );
433  wait( .5 );
434  }
435 }
436 
437 function ‪_CloneOrbFx( endPos, travelTime ) //self is player
438 {
439  spawnPos = self GetTagOrigin( "j_spine4" );
440  fxOrg = ‪spawn( "script_model", spawnPos );
441  fxOrg SetModel( "tag_origin" );
442 
443  fx = PlayFxOnTag( ‪CLONE_ORB_FX, fxOrg, "tag_origin" );
444  fx.team = self.team;
445 
446  fxEndPos = endPos + ( 0, 0, ‪ORB_HEIGHT_OFFSET );
447  fxOrg MoveTo( fxEndPos, travelTime );
448  self ‪util::waittill_any_timeout( traveltime, "death", "disconnect" );
449 
450  fxOrg delete();
451 }
452 
453 function private ‪_CloneCopyPlayerLook( clone, player )
454 {
455  if ( GetDvarInt( "tu1_gadgetCloneCopyLook", 1 ) )
456  {
457  if ( IsPlayer( player ) && IsAI( clone ) )
458  {
459  bodyModel = player GetCharacterBodyModel();
460  if ( IsDefined( bodyModel ) )
461  {
462  clone SetModel( bodyModel );
463  }
464 
465  headModel = player GetCharacterHeadModel();
466  if ( IsDefined( headModel ) )
467  {
468  if ( IsDefined( clone.head ) )
469  {
470  clone Detach( clone.head );
471  }
472 
473  clone Attach( headModel );
474  }
475 
476  helmetModel = player GetCharacterHelmetModel();
477  if ( IsDefined( helmetModel ) )
478  {
479  clone Attach( helmetModel );
480  }
481  }
482  }
483 }
484 
485 function private ‪_ConfigureClone( clone, player, forward, spawnTime ) // self is player
486 {
487  clone.isAiClone = true;
488  clone.properName = "";
489  clone.ignoreTriggerDamage = true;
490  clone.minWalkDistance = 125;
491  clone.overrideActorDamage = &‪cloneDamageOverride;
492  clone.spawnTime = GetTime();
493  clone setmaxhealth( int(‪CLONE_HEALTH_MULTIPLIER * level.playerMaxHealth) );
494 
495  if ( GetDvarInt( "tu1_aiPathableMaterials", 0 ) )
496  {
497  if ( IsDefined( clone.pathablematerial ) )
498  {
499  clone.pathablematerial = clone.pathablematerial & ~‪NAVMESH_MATERIAL_WATER; // Don't path through water.
500  }
501  }
502  clone PushActors( true ); // Don't collide with other actors.
503  clone PushPlayer( true ); // Don't collide with players.
504  clone SetContents( ‪CONTENTS_CLIPSHOT ); // Collide with bullets.
505  clone SetAvoidanceMask( "avoid none" ); // Disable all avoidance.
506 
507  clone ASMSetAnimationRate( RandomFloatRange( 0.98, 1.02 ) );
508 
509  clone setclone();
510  clone ‪_CloneCopyPlayerLook( clone, player );
511  clone ‪_CloneSelectWeapon( player );
512  clone thread ‪_CloneWatchDeath();
513  clone thread ‪_CloneWatchOwnerDisconnect( player );
514  clone thread ‪_CloneWatchShutdown();
515  clone thread ‪_CloneFakeFire();
516  clone thread ‪_CloneBreakGlass();
517  clone._goal_center_point = forward * ‪CLONE_FIRST_POINT_DISTANCE + clone.origin;
518 
519  clone._goal_center_point = GetClosestPointOnNavMesh( clone._goal_center_point, 600 );
520 
521  queryResult = undefined;
522 
523  if ( IsDefined( clone._goal_center_point ) &&
524  clone FindPath( clone.origin, clone._goal_center_point, true, false ) )
525  {
526  queryResult = PositionQuery_Source_Navigation(
527  clone._goal_center_point,
528  0,
531  100,
532  clone );
533  }
534  else
535  {
536  queryResult = PositionQuery_Source_Navigation(
537  clone.origin,
541  50,
542  clone );
543  }
544 
545  if( queryResult.data.size > 0 )
546  {
547  clone setgoalpos( queryResult.data[0].origin, true );
548  clone._clone_goal = queryresult.data[0].origin;
549  clone._clone_goal_max_dist = ‪CLONE_MAX_SEARCH_RADIUS_INITIAL;
550  }
551  else
552  {
553  clone._goal_center_point = clone.origin;
554  }
555 
556  clone thread ‪_UpdateClonePathing();
557  clone ghost();
558  clone thread ‪_show( spawnTime );
559  ‪_ConfigureCloneTeam( clone, player, false );
560 }
561 
562 function private ‪_PlayDematerialization()
563 {
564  if( isDefined( self ) )
565  {
566  fx = ‪PlayFx( ‪CLONE_VANISH_FX, self.origin );
567  fx.team = self.team;
568  PlaySoundAtPosition( "mpl_clone_holo_death", self.origin );
569  }
570 }
571 
572 function private ‪_CloneWatchDeath()
573 {
574  self waittill( "death" );
575 
576  if( isDefined( self ) )
577  {
578  self stoploopsound();
580  ‪removeClone( self );
581  self delete();
582  }
583 }
584 
585 function private ‪_ConfigureCloneTeam( clone, player, ‪isHacked )
586 {
587  if ( ‪isHacked == false )
588  {
589  clone.originalteam = player.team;
590  }
591  clone.ignoreall = true;
592  clone.owner = player;
593  clone SetTeam( player.team );
594  clone.team = player.team;
595  clone SetEntityOwner( player );
596 }
597 
598 function private ‪_show( spawnTime )
599 {
600  self endon( "death" );
601  wait( spawnTime );
602  self show();
603  self ‪clientfield::set( "clone_activated", 1 );
604  fx = ‪PlayFx( ‪CLONE_APPEAR_FX, self.origin, AnglesToForward( self getangles() ));
605  fx.team = self.team;
606  self playloopsound( "mpl_clone_gadget_loop_npc" );
607 }
608 
609 function ‪gadget_clone_off( slot, weapon )
610 {
611  self ‪clientfield::set( "clone_activated", 0 );
612  self ‪flagsys::clear( "clone_activated" );
613  self ‪killClones( self );
615 
616  if ( IsAlive( self ) && isdefined( level.playGadgetSuccess ) )
617  {
618  self [[ level.playGadgetSuccess ]]( weapon, "cloneSuccessDelay" );
619  }
620 }
621 
622 function private ‪_cloneDamaged()
623 {
624  self endon ( "death" );
625  self ‪clientfield::set( "clone_damaged", 1 );
627  self ‪clientfield::set( "clone_damaged", 0 );
628 }
629 
630 function ‪ProcessCloneScoreEvent( clone, attacker, weapon )
631 {
632  if ( isdefined( attacker ) && isplayer( attacker ) )
633  {
634  if ( !level.teamBased || (clone.team != attacker.pers["team"]) )
635  {
636  if( isDefined( clone.isAiClone ) && clone.isAiClone )
637  {
638  ‪scoreevents::processScoreEvent( "killed_clone_enemy", attacker, clone, weapon );
639  }
640  }
641  }
642 }
643 
644 function ‪cloneDamageOverride( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, timeOffset, boneIndex, modelIndex, surfaceType, surfaceNormal )
645 {
646  self thread ‪_cloneDamaged();
647 
648  if( weapon.isEmp && sMeansOfDeath == "MOD_GRENADE_SPLASH" )
649  {
650  ‪ProcessCloneScoreEvent( self, eAttacker, weapon );
651  self notify( "clone_shutdown" );
652  }
653 
654  if( isDefined( level.weaponLightningGun ) && weapon == level.weaponLightningGun )
655  {
656  ‪ProcessCloneScoreEvent( self, eAttacker, weapon );
657  self notify( "clone_shutdown" );
658  }
659 
660  supplyDrop = GetWeapon( "supplydrop" );
661  if( isDefined( supplyDrop ) && supplyDrop == weapon )
662  {
663  ‪ProcessCloneScoreEvent( self, eAttacker, weapon );
664  self notify( "clone_shutdown" );
665  }
666 
667  return iDamage;
668 }
669 
671 {
672  clone = self;
673  clone notify( "WatchCloneOwnerDisconnect" );
674  clone endon( "WatchCloneOwnerDisconnect" );
675  clone endon( "clone_shutdown" );
676 
677  player ‪util::waittill_any( "joined_team", "disconnect", "joined_spectators" );
678  if( isDefined( clone ) )
679  {
680  clone notify( "clone_shutdown" );
681  }
682 }
683 
685 {
686  clone = self;
687  clone waittill( "clone_shutdown" );
688 
689  ‪removeClone( clone );
690 
691  if( isdefined( clone ) )
692  {
693  if( !level.gameEnded ) // kill and do damage do nothing after game end
694  {
695  clone Kill();
696  }
697  else
698  {
699  clone stoploopsound();
701  clone hide();
702  }
703  }
704 }
705 
707 {
708  clone = self;
709  clone endon( "clone_shutdown" );
710  clone endon( "death" );
711 
712  while( true )
713  {
714  clone ‪util::break_glass();
715 
716  wait 0.25;
717  }
718 }
719 
721 {
722  clone = self;
723  clone endon( "clone_shutdown" );
724  clone endon( "death" );
725 
726  while( true )
727  {
728  waitTime = RandomFloatRange( ‪FAKEFIRE_MIN_WAIT_DURATION, ‪FAKEFIRE_MAX_WAIT_DURATION );
729  wait( waitTime );
730  shotsFired = RandomIntRange( ‪FAKEFIRE_MIN_SHOTS, ‪FAKEFIRE_MAX_SHOTS );
731  if( isDefined( clone.fakeFireWeapon ) && ( clone.fakeFireWeapon != level.weaponnone ) )
732  {
733  players = GetPlayers();
734  foreach( player in players )
735  {
736  if( isDefined( player ) && isAlive( player ) && ( player getteam() != clone.team ) )
737  {
738  if( DistanceSquared( player.origin, clone.origin ) < ‪FAKEFIRE_MAX_RANGE_SQR )
739  {
740  if( clone cansee( player ) )
741  {
742  clone FakeFire( clone.owner, clone.origin, clone.fakeFireWeapon, shotsFired );
743  break;
744  }
745  }
746  }
747  }
748  }
749  //clone SetFakeFire( true );
750  wait( shotsFired / 2 ); // Either fire number of shots or simulate firing that duration. May need to adjust this to get it even
751  clone SetFakeFire( false );
752  }
753 }
754 
755 function ‪_CloneSelectWeapon( player )
756 {
757  clone = self;
758  items = ‪_CloneBuildItemList( player );
759  playerWeapon = player GetCurrentWeapon();
760  ball = GetWeapon( "ball" );
761  if( IsDefined( playerWeapon ) && IsDefined( ball ) && ( playerWeapon == ball ) )
762  {
763  weapon = ball;
764  }
765  else if( IsDefined( playerWeapon.worldModel ) && ( ‪_TestPlayerWeapon( playerWeapon, items["primary"] ) ) )
766  {
767  weapon = playerWeapon;
768  }
769  else
770  {
771  weapon = ‪_ChooseWeapon( player );
772  }
773 
774  if( isDefined( weapon ) )
775  {
776  clone ‪shared::placeWeaponOn( weapon, "right" );
777  clone.fakeFireWeapon = weapon;
778  }
779 }
780 
781 
782 function ‪_CloneBuildItemList( player )
783 {
784  pixbeginevent( "clone_build_item_list" );
785 
786  items = [];
787 
788  for( i = 0; i < ‪STATS_TABLE_MAX_ITEMS; i++ )
789  {
790  row = tableLookupRowNum( level.statsTableID, ‪STATS_TABLE_COL_NUMBERING, i );
791 
792  if ( row > -1 )
793  {
794  slot = tableLookupColumnForRow( level.statsTableID, row, ‪STATS_TABLE_COL_SLOT );
795 
796  if ( slot == "" )
797  {
798  continue;
799  }
800 
801  number = Int( tableLookupColumnForRow( level.statsTableID, row, ‪STATS_TABLE_COL_NUMBERING ) );
802 
803  if ( player IsItemLocked( number ) )
804  {
805  continue;
806  }
807 
808  allocation = Int( tableLookupColumnForRow( level.statsTableID, row, ‪STATS_TABLE_COL_ALLOCATION ) );
809 
810  if ( allocation < 0 )
811  {
812  continue;
813  }
814 
815  ‪name = tableLookupColumnForRow( level.statsTableID, row, ‪STATS_TABLE_COL_NAME );
816 
817  if ( !isdefined( items[slot] ) )
818  {
819  items[slot] = [];
820  }
821 
822  items[ slot ][ items[slot].size ] = ‪name;
823  }
824  }
825 
826  pixendevent();
827  return items;
828 }
829 
830 function private ‪_ChooseWeapon( player )
831 {
832  classNum = RandomInt( 10 );
833  for( i = 0; i < 10; i++ )
834  {
835  weapon = player GetLoadoutWeapon( ( i + classNum ) % 10, "primary" );
836  if( weapon != level.weaponnone )
837  {
838  break;
839  }
840  }
841  return weapon;
842 }
843 
844 function private ‪_TestPlayerWeapon( playerweapon, items )
845 {
846  if ( !isdefined( items ) || !items.size || !isdefined( playerweapon ) )
847  {
848  return false;
849  }
850 
851  for( i = 0; i < items.size; i++ )
852  {
853  displayName = items[ i ];
854 
855  if ( playerweapon.displayname == displayName )
856  {
857  return true;
858  }
859  }
860  return false;
861 }
‪CLONE_VANISH_FX
‪#define CLONE_VANISH_FX
Definition: _gadget_clone.gsc:53
‪processScoreEvent
‪function processScoreEvent(event, player, victim, weapon)
Definition: scoreevents_shared.gsc:19
‪register_gadget_is_flickering_callbacks
‪function register_gadget_is_flickering_callbacks(type, flickering_func)
Definition: _ability_player.gsc:299
‪CLONE_COUNT
‪#define CLONE_COUNT
Definition: _gadget_clone.gsc:29
‪CLONE_MAX_SEARCH_RADIUS_INITIAL
‪#define CLONE_MAX_SEARCH_RADIUS_INITIAL
Definition: _gadget_clone.gsc:39
‪CalculateSpawnOrigin
‪function CalculateSpawnOrigin(origin, angles, cloneDistance)
Definition: _gadget_clone.gsc:131
‪spawnClones
‪function spawnClones()
Definition: _gadget_clone.gsc:273
‪gadget_clone_on_give
‪function gadget_clone_on_give(slot, weapon)
Definition: _gadget_clone.gsc:94
‪ORB_TRAVEL_VELOCITY
‪#define ORB_TRAVEL_VELOCITY
Definition: _gadget_clone.gsc:50
‪register_gadget_possession_callbacks
‪function register_gadget_possession_callbacks(type, on_give, on_take)
Definition: _ability_player.gsc:221
‪_UpdateClonePathing
‪function private _UpdateClonePathing()
Definition: _gadget_clone.gsc:348
‪clear
‪function clear(str_flag)
Definition: flag_shared.csc:130
‪CLONE_SPAWN_FROM_PLAYER_MAX
‪#define CLONE_SPAWN_FROM_PLAYER_MAX
Definition: _gadget_clone.gsc:37
‪register_gadget_is_inuse_callbacks
‪function register_gadget_is_inuse_callbacks(type, inuse_func)
Definition: _ability_player.gsc:289
‪CLONE_HEIGHT
‪#define CLONE_HEIGHT
Definition: _gadget_clone.gsc:30
‪_PlayDematerialization
‪function private _PlayDematerialization()
Definition: _gadget_clone.gsc:562
‪FAKEFIRE_MIN_WAIT_DURATION
‪#define FAKEFIRE_MIN_WAIT_DURATION
Definition: _gadget_clone.gsc:48
‪VERSION_SHIP
‪#define VERSION_SHIP
Definition: version.gsh:36
‪gadget_clone_on_take
‪function gadget_clone_on_take(slot, weapon)
Definition: _gadget_clone.gsc:99
‪IsOutOfBounds
‪function IsOutOfBounds()
Definition: _oob.gsc:74
‪_CloneBreakGlass
‪function _CloneBreakGlass()
Definition: _gadget_clone.gsc:706
‪CLONE_FIRST_POINT_DISTANCE
‪#define CLONE_FIRST_POINT_DISTANCE
Definition: _gadget_clone.gsc:38
‪CLONE_NOT_MOVING_DIST_SQ
‪#define CLONE_NOT_MOVING_DIST_SQ
Definition: _gadget_clone.gsc:346
‪removeOldestClone
‪function removeOldestClone()
Definition: _gadget_clone.gsc:243
‪_ConfigureCloneTeam
‪function private _ConfigureCloneTeam(clone, player, isHacked)
Definition: _gadget_clone.gsc:585
‪FAKEFIRE_MAX_WAIT_DURATION
‪#define FAKEFIRE_MAX_WAIT_DURATION
Definition: _gadget_clone.gsc:49
‪_CloneWatchShutdown
‪function _CloneWatchShutdown()
Definition: _gadget_clone.gsc:684
‪_CloneOrbFx
‪function _CloneOrbFx(endPos, travelTime)
Definition: _gadget_clone.gsc:437
‪_CloneWatchOwnerDisconnect
‪function _CloneWatchOwnerDisconnect(player)
Definition: _gadget_clone.gsc:670
‪trace
‪function trace(from, to, target)
Definition: grapple.gsc:369
‪_ConfigureClone
‪function private _ConfigureClone(clone, player, forward, spawnTime)
Definition: _gadget_clone.gsc:485
‪SetBlackBoardAttribute
‪function SetBlackBoardAttribute(entity, attributeName, attributeValue)
Definition: blackboard.gsc:56
‪FAKEFIRE_MAX_RANGE_SQR
‪#define FAKEFIRE_MAX_RANGE_SQR
Definition: _gadget_clone.gsc:45
‪CLONE_MIN_SEARCH_RADIUS
‪#define CLONE_MIN_SEARCH_RADIUS
Definition: _gadget_clone.gsc:40
‪NAVMESH_MATERIAL_WATER
‪#define NAVMESH_MATERIAL_WATER
Definition: shared.gsh:140
‪spawn
‪function spawn(v_origin=(0, 0, 0), v_angles=(0, 0, 0))
Definition: struct.csc:23
‪waittill_any_timeout
‪function waittill_any_timeout(n_timeout, string1, string2, string3, string4, string5)
Definition: util_shared.csc:423
‪register_gadget_flicker_callbacks
‪function register_gadget_flicker_callbacks(type, on_flicker)
Definition: _ability_player.gsc:253
‪GADGET_TYPE_CLONE
‪#define GADGET_TYPE_CLONE
Definition: _ability_util.gsh:48
‪STATS_TABLE_COL_NUMBERING
‪#define STATS_TABLE_COL_NUMBERING
Definition: _statstable.gsh:3
‪STANCE
‪#define STANCE
Definition: blackboard.gsh:36
‪break_glass
‪function break_glass(n_radius=50)
Definition: util_shared.gsc:319
‪STATS_TABLE_COL_ALLOCATION
‪#define STATS_TABLE_COL_ALLOCATION
Definition: _statstable.gsh:14
‪_CloneCopyPlayerLook
‪function private _CloneCopyPlayerLook(clone, player)
Definition: _gadget_clone.gsc:453
‪CLONE_ORB_FX
‪#define CLONE_ORB_FX
Definition: _gadget_clone.gsc:52
‪_cloneDamaged
‪function private _cloneDamaged()
Definition: _gadget_clone.gsc:622
‪wait_network_frame
‪function wait_network_frame(n_count=1)
Definition: util_shared.gsc:64
‪CLONE_SPAWN_FACING_OFFSET
‪#define CLONE_SPAWN_FACING_OFFSET
Definition: _gadget_clone.gsc:36
‪CLONE_HEALTH_MULTIPLIER
‪#define CLONE_HEALTH_MULTIPLIER
Definition: _gadget_clone.gsc:32
‪is_jumping
‪function is_jumping()
Definition: _gadget_clone.gsc:124
‪register_gadget_activation_callbacks
‪function register_gadget_activation_callbacks(type, turn_on, turn_off)
Definition: _ability_player.gsc:237
‪__init__
‪function __init__()
Definition: _gadget_clone.gsc:61
‪CLONE_NOT_MOVING_POLL_TIME
‪#define CLONE_NOT_MOVING_POLL_TIME
Definition: _gadget_clone.gsc:347
‪STATS_TABLE_COL_SLOT
‪#define STATS_TABLE_COL_SLOT
Definition: _statstable.gsh:15
‪STATS_TABLE_MAX_ITEMS
‪#define STATS_TABLE_MAX_ITEMS
Definition: _statstable.gsh:1
‪waittill_any
‪function waittill_any(str_notify1, str_notify2, str_notify3, str_notify4, str_notify5)
Definition: util_shared.csc:375
‪CLONE_APPEAR_FX
‪#define CLONE_APPEAR_FX
Definition: _gadget_clone.gsc:54
‪STATS_TABLE_COL_NAME
‪#define STATS_TABLE_COL_NAME
Definition: _statstable.gsh:5
‪CLONE_MAX_SEARCH_RADIUS
‪#define CLONE_MAX_SEARCH_RADIUS
Definition: _gadget_clone.gsc:41
‪remove_undefined
‪function remove_undefined(array, b_keep_keys)
Definition: array_shared.csc:56
‪_CloneSelectWeapon
‪function _CloneSelectWeapon(player)
Definition: _gadget_clone.gsc:755
‪gadget_clone_on_connect
‪function gadget_clone_on_connect()
Definition: _gadget_clone.gsc:105
‪removeClone
‪function removeClone(clone)
Definition: _gadget_clone.gsc:226
‪REGISTER_SYSTEM
‪#define REGISTER_SYSTEM(__sys, __func_init_preload, __reqs)
Definition: shared.gsh:204
‪STANCE_SWIM
‪#define STANCE_SWIM
Definition: blackboard.gsh:278
‪gadget_clone_off
‪function gadget_clone_off(slot, weapon)
Definition: _gadget_clone.gsc:609
‪ProcessCloneScoreEvent
‪function ProcessCloneScoreEvent(clone, attacker, weapon)
Definition: _gadget_clone.gsc:630
‪FAKEFIRE_MAX_SHOTS
‪#define FAKEFIRE_MAX_SHOTS
Definition: _gadget_clone.gsc:47
‪set
‪function set(str_field_name, n_value)
Definition: clientfield_shared.gsc:34
‪gadget_clone_is_flickering
‪function gadget_clone_is_flickering(slot)
Definition: _gadget_clone.gsc:83
‪CLONE_SPAWN_DISTANCE
‪#define CLONE_SPAWN_DISTANCE
Definition: _gadget_clone.gsc:33
‪_show
‪function private _show(spawnTime)
Definition: _gadget_clone.gsc:598
‪cloneDamageOverride
‪function cloneDamageOverride(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, timeOffset, boneIndex, modelIndex, surfaceType, surfaceNormal)
Definition: _gadget_clone.gsc:644
‪CLONE_SPAWN_HEIGHT_OFFSET
‪#define CLONE_SPAWN_HEIGHT_OFFSET
Definition: _gadget_clone.gsc:34
‪CLONE_PATH_DISTANCE_THRESHOLD_SQ
‪#define CLONE_PATH_DISTANCE_THRESHOLD_SQ
Definition: _gadget_clone.gsc:43
‪ORB_HEIGHT_OFFSET
‪#define ORB_HEIGHT_OFFSET
Definition: _gadget_clone.gsc:51
‪insertClone
‪function insertClone(clone)
Definition: _gadget_clone.gsc:207
‪CLONE_SPAWN_VELOCITY_OFFSET
‪#define CLONE_SPAWN_VELOCITY_OFFSET
Definition: _gadget_clone.gsc:35
‪register
‪function register()
Definition: _ai_tank.gsc:126
‪CONTENTS_CLIPSHOT
‪#define CONTENTS_CLIPSHOT
Definition: shared.gsh:110
‪_CloneFakeFire
‪function _CloneFakeFire()
Definition: _gadget_clone.gsc:720
‪_CloneWatchDeath
‪function private _CloneWatchDeath()
Definition: _gadget_clone.gsc:572
‪STANCE_CROUCH
‪#define STANCE_CROUCH
Definition: blackboard.gsh:274
‪CLONE_MAX_CLONES_ALLOWED
‪#define CLONE_MAX_CLONES_ALLOWED
Definition: _gadget_clone.gsc:31
‪FAKEFIRE_MIN_SHOTS
‪#define FAKEFIRE_MIN_SHOTS
Definition: _gadget_clone.gsc:46
‪on_connect
‪function on_connect()
Definition: _arena.gsc:20
‪isHacked
‪function isHacked()
Definition: util_shared.gsc:2493
‪ORANGE
‪#define ORANGE
Definition: shared.gsh:179
‪killClones
‪function killClones(player)
Definition: _gadget_clone.gsc:110
‪name
‪class GroundFx name
‪gadget_clone_is_inuse
‪function gadget_clone_is_inuse(slot)
Definition: _gadget_clone.gsc:78
‪_TestPlayerWeapon
‪function private _TestPlayerWeapon(playerweapon, items)
Definition: _gadget_clone.gsc:844
‪_CloneBuildItemList
‪function _CloneBuildItemList(player)
Definition: _gadget_clone.gsc:782
‪placeWeaponOn
‪function placeWeaponOn(weapon, position)
Definition: shared.gsc:58
‪gadget_clone_on
‪function gadget_clone_on(slot, weapon)
Definition: _gadget_clone.gsc:337
‪gadget_clone_on_flicker
‪function gadget_clone_on_flicker(slot, weapon)
Definition: _gadget_clone.gsc:89
‪_ChooseWeapon
‪function private _ChooseWeapon(player)
Definition: _gadget_clone.gsc:830
‪WAIT_SERVER_FRAME
‪#define WAIT_SERVER_FRAME
Definition: shared.gsh:265
‪PlayFx
‪function PlayFx(name)
Definition: _counteruav.gsc:390