‪Black Ops 3 Source Code Explorer  0.1
‪An script explorer for Black Ops 3 by ZeRoY
_raps.gsc
Go to the documentation of this file.
1 #using scripts\codescripts\struct;
2 
3 #using scripts\shared\math_shared;
4 #using scripts\shared\statemachine_shared;
5 #using scripts\shared\system_shared;
6 #using scripts\shared\array_shared;
7 #using scripts\shared\util_shared;
8 
9 #using scripts\shared\vehicle_shared;
10 #using scripts\shared\vehicle_death_shared;
11 #using scripts\shared\vehicle_ai_shared;
12 #using scripts\shared\audio_shared;
13 #using scripts\shared\clientfield_shared;
14 
15 #insert scripts\shared\version.gsh;
16 #insert scripts\shared\shared.gsh;
17 #insert scripts\shared\statemachine.gsh;
18 #insert scripts\shared\archetype_shared\archetype_shared.gsh;
19 
20 #insert scripts\shared\ai\utility.gsh;
21 
22 // Distance at which raps repath range is doubled and they check for others claimed locations. Needs to match.
23 #define RAPS_FAR_DISTANCE 400
24 
25 #define RAPS_JUMP_HEIGHT 20
26 
27 #namespace raps;
28 
29 #using_animtree( "generic" );
30 
31 ‪REGISTER_SYSTEM( "raps", &‪__init__, undefined )
32 
33 function ‪__init__()
34 {
35  ‪clientfield::register( "vehicle", "raps_side_deathfx", ‪VERSION_SHIP, 1, "int" );
36 
37  vehicle::add_main_callback( "raps", &‪raps_initialize );
38 
39  slow_triggers = GetEntArray( "raps_slow", "targetname" );
40  array::thread_all( slow_triggers, &‪slow_raps_trigger );
41 }
42 
44 {
45  self.fovcosine = 0;
46  self.fovcosinebusy = 0;
47  self.delete_on_death = true;
48  self.health = self.healthdefault;
49  self.last_jump_chance_time = 0;
50 
51  self UseAnimTree( #animtree );
52 
54 
55  assert( isdefined( self.scriptbundlesettings ) );
56  self.settings = ‪struct::get_script_bundle( "vehiclecustomsettings", self.scriptbundlesettings );
57 
58  if( self.settings.aim_assist )
59  {
60  self EnableAimAssist();
61  }
62 
63  self SetNearGoalNotifyDist( self.settings.near_goal_notify_dist );
64  self.goalRadius = 999999;
65  self.goalHeight = 999999;
66  self SetGoal( self.origin, false, self.goalRadius, self.goalHeight );
67 
68  self.overrideVehicleDamage = &‪raps_callback_damage;
69  self.allowFriendlyFireDamageOverride = &‪raps_AllowFriendlyFireDamage;
70  self.do_death_fx = &‪do_death_fx;
71 
72  self thread ‪vehicle_ai::nudge_collision();
73 
74  self thread ‪sndFunctions();
75 
76 // self thread play_rumble_on_raps();
77 
78  //disable some cybercom abilities
79  if( IsDefined( level.vehicle_initializer_cb ) )
80  {
81  [[level.vehicle_initializer_cb]]( self );
82  }
83 
85 }
86 
87 function ‪defaultRole()
88 {
90 
91  self ‪vehicle_ai::get_state_callbacks( "combat" ).update_func = &‪state_combat_update;
92 
93  self ‪vehicle_ai::get_state_callbacks( "driving" ).update_func = &‪state_scripted_update;
94 
95  self ‪vehicle_ai::get_state_callbacks( "death" ).update_func = &‪state_death_update;
96 
97  self ‪vehicle_ai::get_state_callbacks( "emped" ).update_func = &‪state_emped_update;
98 
100 
102 }
103 
104 function ‪state_scripted_update( params )
105 {
106  self endon( "change_state" );
107  self endon("death");
108 
109  driver = self GetSeatOccupant( 0 );
110 
111  if( isPlayer( driver ) )
112  {
113  driver endon( "disconnect" );
114 
116 
117  self Kill( self.origin, driver );
118  }
119 }
120 
121 // ----------------------------------------------
122 // State: death
123 // ----------------------------------------------
124 function ‪state_death_update( params )
125 {
126  self endon ( "death" );
127 
128  attacker = params.inflictor;
129  if( !isdefined( attacker ) )
130  {
131  attacker = params.attacker;
132  }
133 
134  if( attacker !== self && ( !isdefined( self.owner ) || ( self.owner !== attacker ) ) && ( IsAI( attacker) || IsPlayer( attacker ) ) )
135  {
136  self.damage_on_death = false;
138 
139  // need to retest for attacker validity because of the wait
140  attacker = params.inflictor;
141  if( !isdefined( attacker ) )
142  {
143  attacker = params.attacker;
144  }
145  if ( isdefined( attacker ) && !isdefined( self.detonate_sides_disabled ) )
146  {
147  self ‪detonate_sides( attacker );
148  }
149  else
150  {
151  self.damage_on_death = true;
152  }
153  }
154 
156 }
157 
158 // ----------------------------------------------
159 // State: emped
160 // ----------------------------------------------
161 function ‪state_emped_update( params )
162 {
163  self endon ( "death" );
164  self endon ( "change_state" );
165 
166  if ( self.serverShortout === true )
167  {
168  forward = VectorNormalize( ‪FLAT_ORIGIN( ( self GetVelocity() ) ) );
169  side = VectorCross( forward, (0,0,1) ) * math::randomsign();
170  self SetVehGoalPos( self.origin + side * 500 + forward * randomFloat(400), false, false );
171  wait 0.6;
172  self ClearVehGoalPos();
173  self ‪util::waittill_any_timeout( 1.5, "veh_collision", "change_state", "death" );
174  self Kill( self.origin, self.abnormal_status.attacker, self.abnormal_status.inflictor, GetWeapon( "emp" ) );
175  }
176  else
177  {
179  }
180 }
181 
182 // ----------------------------------------------
183 // State: combat
184 // ----------------------------------------------
185 function ‪state_combat_update( params )
186 {
187  self endon( "change_state" );
188  self endon( "death" );
189 
190  pathfailcount = 0;
191  foundpath = true;
192 
193  self thread ‪prevent_stuck();
194  self thread ‪detonation_monitor();
195  self thread ‪nudge_collision();
196 
197  for( ;; )
198  {
199  if ( ‪IS_TRUE( self.inpain ) )
200  {
201  wait 0.1;
202  }
203  else if ( !IsDefined( self.enemy ) || ‪IS_TRUE( self.raps_force_patrol_behavior ) )
204  {
205  if( isdefined( self.settings.all_knowing ) )
206  {
207  self ‪force_get_enemies();
208  }
209 
210  // go slower when you don't have an enemy, patrolling
211  self SetSpeed( self.settings.defaultMoveSpeed * 0.35 );
212 
213  PixBeginEvent( "_raps::state_combat_update 1" );
214  queryResult = PositionQuery_Source_Navigation( self.origin, 0, self.settings.max_move_dist * 3, self.settings.max_move_dist * 3, self.radius * 2, self, self.radius * 4 );
215  PixEndEvent();
216 
217  PositionQuery_Filter_InClaimedLocation( queryResult, self );
218  PositionQuery_Filter_DistanceToGoal( queryResult, self );
219  ‪vehicle_ai::PositionQuery_Filter_OutOfGoalAnchor( queryResult ); // This already adds score
220 
221  best_point = undefined;
222  best_score = -999999;
223 
224  foreach ( point in queryResult.data )
225  {
226  // distance from origin
227  ADD_POINT_SCORE( point, "distToOrigin", MapFloat( 0, 200, 0, 100, point.distToOrigin2D ) );
228 
229  if( point.inClaimedLocation )
230  {
231  ADD_POINT_SCORE( point, "inClaimedLocation", -500 );
232  }
233 
234  ADD_POINT_SCORE( point, "random", randomFloatRange( 0, 50 ) );
235 
236  // Wander in a somewhat continuous direction
237  if( isdefined( self.prevMoveDir ) )
238  {
239  moveDir = VectorNormalize( point.origin - self.origin );
240  if( VectorDot( moveDir, self.prevMoveDir ) > 0.64 ) // cos 50'
241  {
242  ADD_POINT_SCORE( point, "currentMoveDir", randomFloatRange( 50, 150 ) );
243  }
244  }
245 
246  if ( point.score > best_score )
247  {
248  best_score = point.score;
249  best_point = point;
250  }
251  }
252 
253  self ‪vehicle_ai::PositionQuery_DebugScores( queryResult );
254 
255  foundpath = false;
256 
257  if( isdefined( best_point ) )
258  {
259  foundpath = self SetVehGoalPos( best_point.origin, false, true );
260  }
261  else
262  {
263  wait 1;
264  }
265 
266  if ( foundpath )
267  {
268  self.prevMoveDir = VectorNormalize( best_point.origin - self.origin );
269  self.current_pathto_pos = undefined;
270  self thread ‪path_update_interrupt();
271 
272  pathfailcount = 0;
273 
275  }
276  else
277  {
278  // avoid infinite loop when failinig to find path
279  wait 1;
280  }
281  }
282  else if ( !self.enemy.allowdeath && self GetPersonalThreatBias( self.enemy ) == 0 ) // avoid magic bullet shielded enemy
283  {
284  self SetPersonalThreatBias( self.enemy, -2000, 30.0 );
286  }
287  else
288  {
289  foundpath = false;
290  targetPos = ‪raps_get_target_position();
291 
292  if ( isdefined( targetPos ) )
293  {
294  // Prevent training by not sending every raps to the same location unless they are getting close
295  if( DistanceSquared( self.origin, targetPos ) > ‪SQR( ‪RAPS_FAR_DISTANCE ) && self IsPosInClaimedLocation( targetPos ) )
296  {
297  PixBeginEvent( "_raps::state_combat_update 2" );
298  queryResult = PositionQuery_Source_Navigation( targetPos, 0, self.settings.max_move_dist, self.settings.max_move_dist, self.radius, self );
299  PixEndEvent();
300 
301  PositionQuery_Filter_InClaimedLocation( queryResult, self.enemy );
302 
303  best_point = undefined;
304  best_score = -999999;
305  foreach ( point in queryResult.data )
306  {
307  ADD_POINT_SCORE( point, "distToOrigin", MapFloat( 0, 200, 0, -200, Distance( point.origin, queryResult.origin ) ) );
308  ADD_POINT_SCORE( point, "heightToOrigin", MapFloat( 50, 200, 0, -200, Abs( point.origin[2] - queryResult.origin[2] ) ) );
309 
310  if( point.inClaimedLocation === true )
311  {
312  ADD_POINT_SCORE( point, "inClaimedLocation", -500 );
313  }
314 
315  if ( point.score > best_score )
316  {
317  best_score = point.score;
318  best_point = point;
319  }
320  }
321 
322  self ‪vehicle_ai::PositionQuery_DebugScores( queryResult );
323 
324  if( isdefined( best_point ) )
325  {
326  targetPos = best_point.origin;
327  }
328  }
329 
330  foundpath = self SetVehGoalPos( targetPos, false, true );
331 
332  if ( self.test_failed_path === true )
333  {
334  foundpath = ‪vehicle_ai::waittill_pathresult( 0.5 );
335  }
336 
337  if ( foundpath )
338  {
339  self.current_pathto_pos = targetPos;
340  self thread ‪path_update_interrupt();
341 
342  pathfailcount = 0;
343 
345  }
346  else
347  {
349  }
350  }
351 
352  if ( !foundpath )
353  {
354  pathfailcount++;
355 
356  if ( pathfailcount > 2 )
357  {
358  /#
359  // recordLine( self.origin, self.origin + (0,0,3000), (0.3,1,0) );
360  // RecordSphere( self.origin, 30, (1,1,0) );
361  #/
362 
363  // Try to change enemies
364  if( IsDefined( self.enemy ) )
365  {
366  self SetPersonalThreatBias( self.enemy, -2000, 5.0 );
367  }
368 
369  if ( pathfailcount > self.settings.max_path_fail_count )
370  {
371  ‪detonate();
372  }
373  }
374 
375  wait .2;
376 
377  // just try to path strait to a nearby position on the path
378  PixBeginEvent( "_raps::state_combat_update 3" );
379  queryResult = PositionQuery_Source_Navigation( self.origin, 0, self.settings.max_move_dist, self.settings.max_move_dist, self.radius, self );
380  PixBeginEvent( "_raps::state_combat_update 3" );
381 
382  if( queryResult.data.size )
383  {
384  point = queryResult.data[ randomint( queryResult.data.size ) ];
385 
386  self SetVehGoalPos( point.origin, false, false );
387  self.current_pathto_pos = undefined;
388  self thread ‪path_update_interrupt(); // keep trying to detonate
389  wait 2;
390  self notify( "near_goal" ); // kill the path_update_interrupt just in case
391  }
392  }
393 
394  wait 0.2;
395  }
396  }
397 }
398 
400 {
401  self endon( "change_state" );
402  self endon( "death" );
403  self notify( "end_prevent_stuck" );
404  self endon( "end_prevent_stuck" );
405 
406  wait 2;
407 
408  count = 0;
409  previous_origin = undefined;
410 
411  // detonate if position hasn't change for N counts
412  while( true )
413  {
414  if ( isdefined( previous_origin ) && DistanceSquared( previous_origin, self.origin ) < ‪SQR( 0.1 ) && !‪IS_TRUE( level.bzm_worldPaused ) )
415  {
416  count++;
417  }
418  else
419  {
420  previous_origin = self.origin;
421  count = 0;
422  }
423 
424  if ( count > 10 )
425  {
426  ‪detonate();
427  }
428 
429  wait 1;
430  }
431 }
432 
433 function ‪check_detonation_dist( origin, enemy )
434 {
435  if ( isdefined( enemy ) && IsAlive( enemy ) )
436  {
437  // try to detonate when more in front of the enemy, frustrating to get killed from behind
438  enemy_look_dir_offst = AnglesToForward( enemy.angles ) * 30;
439  targetPoint = enemy.origin + enemy_look_dir_offst;
440 
441  if( distance2DSquared( targetPoint, origin ) < ‪SQR( self.settings.detonation_distance ) && (
442  Abs( targetPoint[2] - origin[2] ) < self.settings.detonation_distance ||
443  Abs( targetPoint[2] - ‪RAPS_JUMP_HEIGHT - origin[2] ) < self.settings.detonation_distance ) )
444  {
445  return true;
446  }
447  }
448 
449  return false;
450 }
451 
453 {
454  if( isdefined( self.sndAlias["jump_up"] ) )
455  self PlaySound( self.sndAlias["jump_up"] );
456 
457  self LaunchVehicle( (0,0,1) * self.jumpforce, (0,0,0), true );
458 
459  self.is_jumping = true;
460 
461  // Spin the Raps too
462  /*
463  if( math::cointoss() )
464  {
465  side_dir = (0,-1,0);
466  }
467  else
468  {
469  side_dir = (0,1,0);
470  }
471  self LaunchVehicle( side_dir * 5, (400,0,0), true, 1 );
472  */
473  wait 0.4;
474 
475  time_to_land = 0.6;
476  while( time_to_land > 0 )
477  {
478  if( ‪check_detonation_dist( self.origin, self.enemy ) )
479  {
480  self ‪detonate();
481  }
482  wait 0.05;
483  time_to_land -= 0.05;
484  }
485 
486  if( IsAlive( self ) )
487  {
488  self.is_jumping = false;
489 
490  ‪trace = PhysicsTrace( self.origin + ( 0, 0, self.radius * 2 ), self.origin - ( 0, 0, 1000 ), ( -10, -10, -10 ), ( 10, 10, 10 ), self, ‪PHYSICS_TRACE_MASK_VEHICLE );
491 
492  willFall = true;
493  if ( ‪trace[ "fraction" ] < 1 )
494  {
495  pos = ‪trace[ "position" ];
496  pos_on_navmesh = GetClosestPointOnNavMesh( pos, 100, self.radius, ‪NMMF_ALL & ~‪NMMF_NOVEHICLE );
497  if ( isdefined( pos_on_navmesh ) )
498  {
499  willFall = false;
500  }
501  }
502 
503  if ( willFall )
504  {
505  self ‪detonate();
506  }
507  }
508 
509  // re-enable stabilizers
510  //self LaunchVehicle( (0,0,1), (0,0,0), true, 2 );
511 }
512 
513 function ‪detonate( attacker )
514 {
515  if( !isdefined( attacker ) )
516  attacker = self;
517 
518  self stopsounds();
519 
520  self DoDamage( self.health + 1000, self.origin, attacker, self, "none", "MOD_EXPLOSIVE", 0, self.turretWeapon );
521 }
522 
523 function ‪detonate_damage_monitored( enemy, weapon )
524 {
525  self.selfDestruct = true;
526  self DoDamage( 1000, self.origin, enemy, self, "none", "MOD_EXPLOSIVE", 0, self.turretWeapon );
527 }
528 
530 {
531  self endon( "death" );
532  self endon( "change_state" );
533 
534  lastEnemy = undefined;
535 
536  while( 1 )
537  {
538  ‪try_detonate();
539 
540  wait 0.2;
541 
542  // targeting audio
543  if( isdefined( self.enemy ) && IsPlayer( self.enemy ) )
544  {
545  // enemy change
546  if( lastEnemy !== self.enemy )
547  {
548  lastDistToEnemySquared = 10000.0 * 10000.0;
549  lastEnemy = self.enemy;
550  }
551 
552  if( !IsDefined( self.looping_targeting_sound ) )
553  {
554  if( isdefined( self.sndAlias["vehRapsAlarm"] ) )
555  {
556  self.looping_targeting_sound = ‪spawn( "script_origin", self.origin );
557  self.looping_targeting_sound linkto(self);
558 
559  //set the client mask on the looping targeting sound to play only on the enemy
560  self.looping_targeting_sound SetInvisibleToAll();
561  self.looping_targeting_sound SetVisibleToPlayer( self.enemy );
562  self.looping_targeting_sound playloopsound (self.sndAlias["vehRapsAlarm"]);
563 
564  self.looping_targeting_sound thread ‪raps_audio_cleanup( self );
565  }
566  }
567 
568  distToEnemySquared = distanceSquared( self.origin, self.enemy.origin );
569 
570  if( distToEnemySquared < ‪SQR(250) )
571  {
572  if( lastDistToEnemySquared > ‪SQR(250) && !‪IS_TRUE( self.serverShortout ) && isdefined( self.sndAlias["vehRapsClose250"] ) )
573  self PlaySoundToPlayer( self.sndAlias["vehRapsClose250"], self.enemy );
574  }
575  else if( distToEnemySquared < ‪SQR(750) )
576  {
577  if( lastDistToEnemySquared > ‪SQR(750) && !‪IS_TRUE( self.serverShortout ) && isdefined( self.sndAlias["vehRapsTargeting"] ) )
578  self PlaySoundToPlayer( self.sndAlias["vehRapsTargeting"], self.enemy );
579  }
580  else if( distToEnemySquared < ‪SQR(1500) )
581  {
582  if( lastDistToEnemySquared > ‪SQR(1500) && !‪IS_TRUE( self.serverShortout ) && isdefined( self.sndAlias["vehRapsClose1500"] ) )
583  self PlaySoundToPlayer( self.sndAlias["vehRapsClose1500"], self.enemy );
584  }
585 
586  if( distToEnemySquared < lastDistToEnemySquared )
587  {
588  lastDistToEnemySquared = distToEnemySquared;
589  }
590  // let it slowly grow so we can play the sounds again if the player gets away
591  lastDistToEnemySquared += ‪SQR( 50 * 0.2 );
592 
593  }
594  }
595 }
596 function ‪raps_audio_cleanup( owner )
597 {
598  owner waittill( "death" );
599 
600  if ( isdefined( owner ) )
601  {
602  owner stopsounds();
603  }
604 
605  if ( isdefined( self ) )
606  {
607  self StopLoopSound();
608  self Delete();
609  }
610 }
611 
612 function ‪try_detonate()
613 {
614  if( ‪IS_TRUE( self.disableAutoDetonation ) )
615  return;
616 
617  jump_time = 0.5;
618 
619  cur_time = GetTime();
620 
621  can_jump = (cur_time - self.last_jump_chance_time > 1500);
622 
623  if( can_jump )
624  {
625  predicted_origin = self.origin + self GetVelocity() * jump_time;
626  }
627 
628  if( isdefined( predicted_origin ) && ‪check_detonation_dist( predicted_origin, self.enemy ) )
629  {
630  ‪trace = BulletTrace( predicted_origin + (0,0,self.radius), self.enemy.origin + (0,0,self.radius), true, self );
631  if ( ‪trace["fraction"] === 1.0 || isdefined( ‪trace["entity"] ) )
632  {
633  self.last_jump_chance_time = cur_time;
634  jump_chance = self.settings.jump_chance;
635  if ( self.enemy.origin[2] - ‪RAPS_JUMP_HEIGHT > predicted_origin[2] )
636  {
637  jump_chance = self.settings.jump_chance * 2;
638  }
639 
640  if( RandomFloat( 1 ) < jump_chance )
641  {
642  self ‪jump_detonate();
643  }
644  }
645  }
646  else if( ‪check_detonation_dist( self.origin, self.enemy ) )
647  {
648  ‪trace = BulletTrace( self.origin + (0,0,self.radius), self.enemy.origin + (0,0,self.radius), true, self );
649  if ( ‪trace["fraction"] === 1.0 || isdefined( ‪trace["entity"] ) )
650  {
651  self ‪detonate();
652  }
653  }
654 
655  // just blow up if close to other enemies
656  if( isdefined( self.owner ) )
657  {
658  foreach( player in level.players )
659  {
660  if( self.owner ‪util::IsEnemyPlayer( player ) && ( !isdefined( self.enemy ) || player != self.enemy ) )
661  {
662  if( player IsNoTarget() || !IsAlive( player ) )
663  {
664  continue;
665  }
666 
667  if ( player.ignoreme === true )
668  {
669  continue;
670  }
671 
672  if( !SessionModeIsCampaignGame() && !SessionModeIsZombiesGame() && player hasPerk( "specialty_nottargetedbyraps" ) )
673  {
674  continue;
675  }
676 
677  if( distanceSquared( player.origin, self.origin ) < ‪SQR( self.settings.detonation_distance ) )
678  {
679  ‪trace = BulletTrace( self.origin + (0,0,self.radius), player.origin + (0,0,self.radius), true, self );
680  if ( ‪trace["fraction"] === 1.0 || isdefined( ‪trace["entity"] ) )
681  {
682  self ‪raps::detonate();
683  }
684  }
685  }
686  }
687  }
688 }
689 
691 {
692  if( isdefined( self.settings.all_knowing ) )
693  {
694  if( isdefined( self.enemy ) )
695  {
696  target_pos = self.enemy.origin;
697  }
698  }
699  else
700  {
702  }
703 
704  enemy = self.enemy;
705 
706  if( isdefined( target_pos ) )
707  {
708  target_pos_onnavmesh = GetClosestPointOnNavMesh( target_pos, self.settings.detonation_distance * 1.5, self.radius, ‪NMMF_ALL & ~‪NMMF_NOVEHICLE );
709  }
710 
711  // if we can't find a position on the navmesh then just keep going to the current position
712  if( !isdefined( target_pos_onnavmesh ) )
713  {
714  if( isdefined( self.enemy ) )
715  {
716  self SetPersonalThreatBias( self.enemy, -2000, 5.0 );
717  }
718 
719  if ( isdefined( self.current_pathto_pos ) )
720  {
721  target_pos_onnavmesh = GetClosestPointOnNavMesh( self.current_pathto_pos, self.settings.detonation_distance * 2, self.settings.detonation_distance * 1.5, ‪NMMF_ALL & ~‪NMMF_NOVEHICLE );
722  }
723 
724  if ( isdefined( target_pos_onnavmesh ) )
725  {
726  return target_pos_onnavmesh;
727  }
728  else
729  {
730  return self.current_pathto_pos;
731  }
732  }
733  else if ( isdefined( self.enemy ) )
734  {
735  if ( DistanceSquared( target_pos, target_pos_onnavmesh ) > ‪SQR( self.settings.detonation_distance * 0.9 ) )
736  {
737  self SetPersonalThreatBias( self.enemy, -2000, 5.0 );
738  }
739  }
740 
741  if( isdefined( enemy ) && IsPlayer( enemy ) )
742  {
743  enemy_vel_offset = enemy GetVelocity() * 0.5;
744 
745  enemy_look_dir_offset = AnglesToForward( enemy.angles );
746  if( distance2dSquared( self.origin, enemy.origin ) > ‪SQR( 500 ) )
747  {
748  enemy_look_dir_offset *= 110;
749  }
750  else
751  {
752  enemy_look_dir_offset *= 35;
753  }
754 
755  offset = enemy_vel_offset + enemy_look_dir_offset;
756  offset = ‪FLAT_ORIGIN( offset ); // just 2d
757 
758  if( TracePassedOnNavMesh( target_pos_onnavmesh, target_pos + offset ) )
759  {
760  target_pos += offset;
761  }
762  else
763  {
764  target_pos = target_pos_onnavmesh;
765  }
766  }
767  else
768  {
769  target_pos = target_pos_onnavmesh;
770  }
771 
772  return target_pos;
773 }
774 
776 {
777  self endon( "death" );
778  self endon( "change_state" );
779  self endon( "near_goal" );
780  self endon( "reached_end_node" );
781 
782  //ensure only one path_update_interrupt is running
783  self notify( "clear_interrupt_threads" );
784  self endon( "clear_interrupt_threads" );
785 
786  wait .1; // sometimes endons may get fired off so wait a bit for the goal to get updated
787 
788  while( 1 )
789  {
790  if( isdefined( self.current_pathto_pos ) )
791  {
792  if( distance2dSquared( self.current_pathto_pos, self.goalpos ) > ‪SQR( self.goalRadius ) )
793  {
794  wait 0.5;
795 
796  self notify( "near_goal" );
797  }
798 
799  targetPos = ‪raps_get_target_position();
800  if ( isdefined( targetPos ) )
801  {
802  // optimization, don't keep repathing as often when far away
803  if( DistanceSquared( self.origin, targetPos ) > ‪SQR( ‪RAPS_FAR_DISTANCE ) )
804  {
805  repath_range = self.settings.repath_range * 2;
806  wait 0.1;
807  }
808  else
809  {
810  repath_range = self.settings.repath_range;
811  }
812 
813  if( distance2dSquared( self.current_pathto_pos, targetPos ) > ‪SQR( repath_range ) )
814  {
815  if( isdefined( self.sndAlias ) && isdefined( self.sndAlias["direction"] ) )
816  {
817  self playsound( self.sndAlias["direction"] );
818  }
819 
820  self notify( "near_goal" );
821  }
822  }
823 
824  if( isdefined( self.enemy ) && IsPlayer( self.enemy ) && !isdefined(self.slow_trigger) )
825  {
826  forward = AnglesToForward( self.enemy GetPlayerAngles() );
827  dir_to_raps = self.origin - self.enemy.origin;
828 
829  speedToUse = self.settings.defaultMoveSpeed;
830  if( IsDefined( self._override_raps_combat_speed ) )
831  {
832  speedToUse = self._override_raps_combat_speed;
833  }
834 
835  if( VectorDot( forward, dir_to_raps ) > 0 )
836  {
837  self SetSpeed( speedToUse );
838  }
839  else
840  {
841  self SetSpeed( speedToUse * 0.75 );
842  }
843  }
844  else
845  {
846  speedToUse = self.settings.defaultMoveSpeed;
847  if( IsDefined( self._override_raps_combat_speed ) )
848  {
849  speedToUse = self._override_raps_combat_speed;
850  }
851  self SetSpeed( speedToUse );
852  }
853 
854  wait 0.2;
855  }
856  else
857  {
858  wait 0.4;
859  }
860  }
861 }
862 
863 function ‪collision_fx( normal )
864 {
865  tilted = ( normal[2] < 0.6 );
866  fx_origin = self.origin - normal * ( tilted ? 28 : 10 );
867 
868  if( isdefined( self.sndAlias["vehRapsCollision"] ) )
869  self PlaySound( self.sndAlias["vehRapsCollision"] );
870 
871  //PlayFX( level._effect[ "drone_nudge" ], fx_origin, normal );
872 }
873 
875 {
876  self endon( "death" );
877  self endon( "change_state" );
878  self notify( "end_nudge_collision" );
879  self endon( "end_nudge_collision" );
880 
881  while ( 1 )
882  {
883  self waittill( "veh_collision", velocity, normal );
884  ang_vel = self GetAngularVelocity() * 0.8;
885  self SetAngularVelocity( ang_vel );
886 
887  // bounce off walls
888  if ( IsAlive( self ) && VectorDot( normal, (0,0,1) ) < 0.5 ) // angle is more than 60 degree away from up direction
889  {
890  self SetVehVelocity( self.velocity + normal * 400 );
891  self ‪collision_fx( normal );
892  }
893  }
894 }
895 
896 function ‪raps_AllowFriendlyFireDamage( eInflictor, eAttacker, sMeansOfDeath, weapon )
897 {
898  if( isdefined( self.owner ) && ( eAttacker == self.owner ) && isdefined( self.settings.friendly_fire ) && int( self.settings.friendly_fire ) && !weapon.isEmp )
899  {
900  return true;
901  }
902 
903  if ( isdefined( eAttacker ) && isdefined( eAttacker.archetype ) && isdefined( sMeansOfDeath )
904  && eAttacker.archetype == ‪ARCHETYPE_RAPS && sMeansOfDeath == "MOD_EXPLOSIVE" )
905  {
906  return true;
907  }
908 
909  return false;
910 }
911 
912 function ‪detonate_sides(eInflictor)
913 {
914  forward_direction = AnglesToForward( self.angles );
915  up_direction = AnglesToUp( self.angles );
916 
917  origin = self.origin + VectorScale(up_direction, 15);
918 
919  right_direction = VectorCross(forward_direction, up_direction);
920  right_direction = VectorNormalize(right_direction);
921 
922  left_direction = VectorScale(right_direction, -1);
923 
924  eInflictor CylinderDamage(VectorScale(right_direction, 140), origin, 15, 50, self.radiusdamagemax, self.radiusdamagemax / 5, self, "MOD_EXPLOSIVE", self.turretWeapon);
925  eInflictor CylinderDamage(VectorScale(left_direction, 140), origin, 15, 50, self.radiusdamagemax, self.radiusdamagemax / 5, self, "MOD_EXPLOSIVE", self.turretWeapon);
926 
927  self.bSideDetonation = true;
928 }
929 
930 function ‪raps_callback_damage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal )
931 {
932  if ( self.drop_deploying === true && sMeansOfDeath == "MOD_TRIGGER_HURT" && ( !isdefined( self.hurt_trigger_immune_end_time ) || ( GetTime() < self.hurt_trigger_immune_end_time ) ) )
933  return 0;
934 
935  if ( isdefined( eAttacker ) && isdefined( eAttacker.archetype ) && isdefined( sMeansOfDeath )
936  && eAttacker.archetype == ‪ARCHETYPE_RAPS && sMeansOfDeath == "MOD_EXPLOSIVE" )
937  {
938  if ( eAttacker != self && isdefined( vDir ) && lengthSquared( vDir ) > 0.1 && ( !isdefined( eAttacker ) || eAttacker.team === self.team ) && ( !isdefined( eInflictor ) || eInflictor.team === self.team ) )
939  {
940  self SetVehVelocity( self.velocity + VectorNormalize( vDir ) * 300 );
941  return 1;
942  }
943  }
944 
945  if ( ‪vehicle_ai::should_emp( self, weapon, sMeansOfDeath, eInflictor, eAttacker ) )
946  {
947  minEmpDownTime = 0.8 * self.settings.empdowntime;
948  maxEmpDownTime = 1.2 * self.settings.empdowntime;
949  self notify ( "emped", RandomFloatRange( minEmpDownTime, maxEmpDownTime ), eAttacker, eInflictor );
950  }
951 
952  if ( ‪vehicle_ai::should_burn( self, weapon, sMeansOfDeath, eInflictor, eAttacker ) )
953  {
954  self thread ‪vehicle_ai::burning_thread( eAttacker, eInflictor );
955  }
956 
957  return iDamage;
958 }
959 
961 {
962  self endon( "death" );
963 
964  while( 1 )
965  {
966  self waittill( "trigger", other );
967 
968  if( IsVehicle( other ) && IsDefined( other.archetype ) && other.archetype == "raps" )
969  {
970  other thread ‪slow_raps( self );
971  }
972 
973  wait(0.1);
974  }
975 }
976 
977 function ‪slow_raps( trigger )
978 {
979  self notify( "slow_raps" );
980  self endon( "slow_raps" );
981  self endon( "death" );
982 
983  self.slow_trigger = true;
984  if( IsDefined( trigger.script_int ) )
985  {
986  if( IsDefined( self._override_raps_combat_speed ) && self._override_raps_combat_speed < trigger.script_int )
987  {
988  self SetSpeedImmediate( self._override_raps_combat_speed );
989  }
990  else
991  {
992  self SetSpeedImmediate( trigger.script_int, 200, 200 );
993  }
994  }
995  else
996  {
997  if( IsDefined( self._override_raps_combat_speed ) && self._override_raps_combat_speed < 0.5 * self.settings.defaultMoveSpeed )
998  {
999  self SetSpeed( self._override_raps_combat_speed );
1000  }
1001  else
1002  {
1003  self SetSpeed( 0.5 * self.settings.defaultMoveSpeed );
1004  }
1005  }
1006 
1007  wait 1;
1008 
1009  self ResumeSpeed();
1010  self.slow_trigger = undefined;
1011 }
1012 
1014 {
1015  if( isdefined( level.raps_force_get_enemies ) )
1016  {
1017  return self [[level.raps_force_get_enemies]]();
1018  }
1019 
1020  foreach( player in level.players )
1021  {
1022  if( self ‪util::IsEnemyPlayer( player ) && !player.ignoreme )
1023  {
1024  self GetPerfectInfo( player );
1025  return;
1026  }
1027  }
1028 }
1029 
1031 {
1032  self.sndAlias = [];
1033  self.sndAlias["inAir"] = "veh_raps_in_air";
1034  self.sndAlias["land"] = "veh_raps_land";
1035  self.sndAlias["spawn"] = "veh_raps_spawn";
1036  self.sndAlias["direction"] = "veh_raps_direction";
1037  self.sndAlias["jump_up"] = "veh_raps_jump_up";
1038  self.sndAlias["vehRapsClose250"] = "veh_raps_close_250";
1039  self.sndAlias["vehRapsClose1500"] = "veh_raps_close_1500";
1040  self.sndAlias["vehRapsTargeting"] = "veh_raps_targeting";
1041  self.sndAlias["vehRapsAlarm"] = "evt_raps_alarm";
1042  self.sndAlias["vehRapsCollision"] = "veh_wasp_wall_imp";
1043 
1044  if( isdefined( self.vehicletype ) && (self.vehicletype == "spawner_enemy_zombie_vehicle_raps_suicide" || self.vehicletype == "spawner_zombietron_veh_meatball" || self.vehicletype == "spawner_zombietron_veh_meatball_med" || self.vehicletype == "spawner_zombietron_veh_meatball_small") )
1045  {
1046  self.sndAlias["inAir"] = "zmb_meatball_in_air";
1047  self.sndAlias["land"] = "zmb_meatball_land";
1048  self.sndAlias["spawn"] = undefined;
1049  self.sndAlias["direction"] = undefined;
1050  self.sndAlias["jump_up"] = "zmb_meatball_jump_up";
1051  self.sndAlias["vehRapsClose250"] = "zmb_meatball_close_250";
1052  self.sndAlias["vehRapsClose1500"] = undefined;
1053  self.sndAlias["vehRapsTargeting"] = "zmb_meatball_targeting";
1054  self.sndAlias["vehRapsAlarm"] = undefined;
1055  self.sndAlias["vehRapsCollision"] = "zmb_meatball_collision";
1056  }
1057 
1058  if( self ‪isDrivablePlayerVehicle() )
1059  {
1060  self thread ‪drivableRapsInAir();
1061  }
1062  else
1063  {
1064  self thread ‪raps_in_air_audio();
1065 
1066  if( SessionModeIsCampaignGame() || SessionModeIsZombiesGame() )
1067  self thread ‪raps_spawn_audio();
1068  }
1069 }
1071 {
1072  self endon ("death");
1073 
1074  while(1)
1075  {
1076  self waittill ("veh_landed");
1077  if( isdefined( self.sndAlias["land"] ) )
1078  self playsound (self.sndAlias["land"]);
1079  }
1080 }
1081 function ‪raps_in_air_audio() //need to move to client at some point
1082 {
1083  self endon ("death");
1084 
1085  if( !SessionModeIsCampaignGame() && !SessionModeIsZombiesGame() )
1086  self waittill( "veh_landed" );
1087 
1088  while(1)
1089  {
1090  self waittill ("veh_inair"); //notify sent from the vehicle system when all four 'tires' are off the scripts\cp\cp_mi_zurich_newworld_underground
1091 
1092  if( isdefined( self.sndAlias["inAir"] ) )
1093  self playsound (self.sndAlias["inAir"]);
1094 
1095  self waittill ("veh_landed");
1096 
1097  if( isdefined( self.sndAlias["land"] ) )
1098  self playsound (self.sndAlias["land"]);
1099  }
1100 
1101 }
1102 function ‪raps_spawn_audio() //need to move to client at some point
1103 {
1104  self endon( "death" );
1105  wait randomfloatrange (0.25, 1.5);
1106 
1107  if( isdefined( self.sndAlias["spawn"] ) )
1108  self playsound (self.sndAlias["spawn"]);
1109 }
1111 {
1112  str_vehicletype = self.vehicletype;
1113  if (isdefined( str_vehicletype ) && StrEndsWith( str_vehicletype, "_player" ) )
1114  {
1115  return true;
1116  }
1117  return false;
1118 }
1119 
1120 function ‪do_death_fx()
1121 {
1123 
1124  if(isdefined(self.bSideDetonation ))
1125  self ‪clientfield::set( "raps_side_deathfx", 1 );
1126  else
1127  self ‪clientfield::set( "deathfx", 1 );
1128 }
1129 
1130 
1131 //function play_rumble_on_raps()
1132 //{
1133 // self endon( "death" );
1134 //
1135 // while( 1 )
1136 // {
1137 // vr = Abs( self GetSpeed() / self GetMaxSpeed() );
1138 //
1139 // if( vr >= 0.1 )
1140 // {
1141 // self PlayRumbleOnEntity( "damage_heavy" );
1142 // }
1143 //
1144 // Wait 0.1;
1145 // }
1146 //
1147 //}
‪call_custom_add_state_callbacks
‪function call_custom_add_state_callbacks()
Definition: vehicle_ai_shared.gsc:1152
‪raps_audio_cleanup
‪function raps_audio_cleanup(owner)
Definition: _raps.gsc:596
‪defaultRole
‪function defaultRole()
Definition: _raps.gsc:87
‪raps_callback_damage
‪function raps_callback_damage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal)
Definition: _raps.gsc:930
‪detonate_damage_monitored
‪function detonate_damage_monitored(enemy, weapon)
Definition: _raps.gsc:523
‪NMMF_NOVEHICLE
‪#define NMMF_NOVEHICLE
Definition: archetype_shared.gsh:49
‪prevent_stuck
‪function prevent_stuck()
Definition: _raps.gsc:399
‪raps_get_target_position
‪function raps_get_target_position()
Definition: _raps.gsc:690
‪VERSION_SHIP
‪#define VERSION_SHIP
Definition: version.gsh:36
‪detonation_monitor
‪function detonation_monitor()
Definition: _raps.gsc:529
‪trace
‪function trace(from, to, target)
Definition: grapple.gsc:369
‪state_emped_update
‪function state_emped_update(params)
Definition: _raps.gsc:161
‪defaultstate_death_update
‪function defaultstate_death_update(params)
Definition: vehicle_ai_shared.gsc:1355
‪GetTargetPos
‪function GetTargetPos(target, geteye)
Definition: vehicle_ai_shared.gsc:115
‪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
‪IS_TRUE
‪#define IS_TRUE(__a)
Definition: shared.gsh:251
‪raps_initialize
‪function raps_initialize()
Definition: _raps.gsc:43
‪SQR
‪#define SQR(__var)
Definition: shared.gsh:293
‪check_detonation_dist
‪function check_detonation_dist(origin, enemy)
Definition: _raps.gsc:433
‪force_get_enemies
‪function force_get_enemies()
Definition: _raps.gsc:1013
‪IsEnemyPlayer
‪function IsEnemyPlayer(player)
Definition: util_shared.csc:1220
‪StartInitialState
‪function StartInitialState(defaultState="combat")
Definition: vehicle_ai_shared.gsc:866
‪sndFunctions
‪function sndFunctions()
Definition: _raps.gsc:1030
‪defaultstate_emped_update
‪function defaultstate_emped_update(params)
Definition: vehicle_ai_shared.gsc:1465
‪raps_in_air_audio
‪function raps_in_air_audio()
Definition: _raps.gsc:1081
‪waittill_pathresult
‪function waittill_pathresult(maxtime=0.5)
Definition: vehicle_ai_shared.gsc:347
‪state_death_update
‪function state_death_update(params)
Definition: _raps.gsc:124
‪friendly_fire_shield
‪function friendly_fire_shield()
Definition: vehicle_shared.gsc:2194
‪raps_AllowFriendlyFireDamage
‪function raps_AllowFriendlyFireDamage(eInflictor, eAttacker, sMeansOfDeath, weapon)
Definition: _raps.gsc:896
‪jump_detonate
‪function jump_detonate()
Definition: _raps.gsc:452
‪NMMF_ALL
‪#define NMMF_ALL
Definition: archetype_shared.gsh:50
‪slow_raps_trigger
‪function slow_raps_trigger()
Definition: _raps.gsc:960
‪should_burn
‪function should_burn(vehicle, weapon, meansOfDeath, eInflictor, eAttacker)
Definition: vehicle_ai_shared.gsc:803
‪init_state_machine_for_role
‪function init_state_machine_for_role(rolename)
Definition: vehicle_ai_shared.gsc:1034
‪RAPS_FAR_DISTANCE
‪#define RAPS_FAR_DISTANCE
Definition: _raps.gsc:23
‪waittill_pathing_done
‪function waittill_pathing_done(maxtime=15)
Definition: vehicle_ai_shared.gsc:340
‪PositionQuery_DebugScores
‪function PositionQuery_DebugScores(queryResult)
Definition: vehicle_ai_shared.gsc:2010
‪do_death_fx
‪function do_death_fx()
Definition: _raps.gsc:1120
‪nudge_collision
‪function nudge_collision()
Definition: _raps.gsc:874
‪ARCHETYPE_RAPS
‪#define ARCHETYPE_RAPS
Definition: archetype_shared.gsh:33
‪RAPS_JUMP_HEIGHT
‪#define RAPS_JUMP_HEIGHT
Definition: _raps.gsc:25
‪PositionQuery_Filter_OutOfGoalAnchor
‪function PositionQuery_Filter_OutOfGoalAnchor(queryResult, tolerance=1)
Definition: vehicle_ai_shared.gsc:2104
‪drivableRapsInAir
‪function drivableRapsInAir()
Definition: _raps.gsc:1070
‪try_detonate
‪function try_detonate()
Definition: _raps.gsc:612
‪REGISTER_SYSTEM
‪#define REGISTER_SYSTEM(__sys, __func_init_preload, __reqs)
Definition: shared.gsh:204
‪state_scripted_update
‪function state_scripted_update(params)
Definition: _raps.gsc:104
‪burning_thread
‪function burning_thread(attacker, inflictor)
Definition: vehicle_ai_shared.gsc:533
‪PHYSICS_TRACE_MASK_VEHICLE
‪#define PHYSICS_TRACE_MASK_VEHICLE
Definition: shared.gsh:131
‪detonate_sides
‪function detonate_sides(eInflictor)
Definition: _raps.gsc:912
‪waittill_attack_button_pressed
‪function waittill_attack_button_pressed()
Definition: util_shared.gsc:2413
‪raps_spawn_audio
‪function raps_spawn_audio()
Definition: _raps.gsc:1102
‪should_emp
‪function should_emp(vehicle, weapon, meansOfDeath, eInflictor, eAttacker)
Definition: vehicle_ai_shared.gsc:765
‪__init__
‪function __init__()
Definition: _raps.gsc:33
‪set
‪function set(str_field_name, n_value)
Definition: clientfield_shared.gsc:34
‪state_combat_update
‪function state_combat_update(params)
Definition: _raps.gsc:185
‪isDrivablePlayerVehicle
‪function isDrivablePlayerVehicle()
Definition: _raps.gsc:1110
‪GetEnemyTarget
‪function GetEnemyTarget()
Definition: vehicle_ai_shared.gsc:101
‪detonate
‪function detonate(attacker)
Definition: _raps.gsc:513
‪collision_fx
‪function collision_fx(normal)
Definition: _raps.gsc:863
‪register
‪function register()
Definition: _ai_tank.gsc:126
‪FLAT_ORIGIN
‪#define FLAT_ORIGIN(__origin)
Definition: shared.gsh:256
‪get_script_bundle
‪function get_script_bundle(str_type, str_name)
Definition: struct.csc:45
‪do_death_dynents
‪function do_death_dynents(special_status=1)
Definition: vehicle_shared.gsc:2874
‪path_update_interrupt
‪function path_update_interrupt()
Definition: _raps.gsc:775
‪slow_raps
‪function slow_raps(trigger)
Definition: _raps.gsc:977
‪get_state_callbacks
‪function get_state_callbacks(statename)
Definition: vehicle_ai_shared.gsc:927
‪WAIT_SERVER_FRAME
‪#define WAIT_SERVER_FRAME
Definition: shared.gsh:265