1 #using scripts\codescripts\struct;
3 #using scripts\shared\math_shared;
4 #using scripts\shared\statemachine_shared;
5 #using scripts\shared\callbacks_shared;
6 #using scripts\shared\clientfield_shared;
7 #using scripts\shared\system_shared;
8 #using scripts\shared\array_shared;
9 #using scripts\shared\util_shared;
10 #using scripts\shared\ai_shared;
12 #using scripts\shared\vehicle_shared;
13 #using scripts\shared\vehicle_death_shared;
14 #using scripts\shared\vehicle_ai_shared;
15 #using scripts\shared\ai\systems\blackboard;
16 #using scripts\shared\ai\blackboard_vehicle;
17 #using scripts\shared\ai\systems\ai_interface;
19 #insert scripts\shared\shared.gsh;
20 #insert scripts\shared\version.gsh;
21 #insert scripts\shared\statemachine.gsh;
22 #insert scripts\shared\archetype_shared\archetype_shared.gsh;
24 #insert scripts\shared\ai\utility.gsh;
25 #insert scripts\shared\ai\systems\blackboard.gsh;
26 #using scripts\shared\ai\zombie_utility;
28 #define PARASITE_RADIUS 20
30 #define PARASITE_MAX_TIME_AT_SAME_POSITION 1.0
31 #define PARASITE_CHANGE_POSITION_TOATTACK_TARGET_DELAY 0.5
33 #define PARASITE_AWAY_FROM_CHARACTER 300
35 #define PARASITE_ENEMY_TOO_CLOSE_DIST 250
38 #define PARASITE_TOO_CLOSE_TO_SELF_DIST 75
39 #define PARASITE_MOVE_DIST_MAX 225
40 #define PARASITE_JUKE_MOVE_DIST_MAX 300
42 #define PARASITE_REPATH_RANGE 100
44 #define PARASITE_FIRE_CHANCE 30
45 #define PARASITE_MELEE_CHANCE 50
46 #define PARASITE_MELEE_DIST 64
52 #using_animtree( "generic" );
68 array(
"slow",
"medium",
"fast" ) );
73 self notify(
"parasite_damage_thread" );
74 self endon(
"parasite_damage_thread" );
76 self endon(
"death" );
80 self waittill(
"damage", n_ammount, e_attacker );
82 if ( IsDefined( e_attacker ) &&
IS_TRUE( e_attacker.is_parasite ) && !
IS_TRUE(e_attacker.squelch_damage_overlay) )
91 if( !IsDefined( target ) )
96 if( !IsAlive( target ) )
101 if( IsPlayer( target ) && target.sessionstate ==
"spectator" )
106 if( IsPlayer( target ) && target.sessionstate ==
"intermission" )
116 if( target IsNoTarget() )
121 if( IsDefined(
self.is_target_valid_cb ) )
123 return self [[
self.is_target_valid_cb ]]( target );
131 parasite_targets = GetPlayers();
132 least_hunted = parasite_targets[0];
133 for( i = 0; i < parasite_targets.size; i++ )
135 if ( !isdefined( parasite_targets[i].hunted_by ) )
137 parasite_targets[i].hunted_by = 0;
147 least_hunted = parasite_targets[i];
150 if( parasite_targets[i].hunted_by < least_hunted.hunted_by )
152 least_hunted = parasite_targets[i];
174 if( IsDefined(
self.parasiteEnemy ) )
176 if( !IsDefined(
self.parasiteEnemy.hunted_by ) )
178 self.parasiteEnemy.hunted_by = 0;
180 if(
self.parasiteEnemy.hunted_by > 0 )
182 self.parasiteEnemy.hunted_by--;
186 self.parasiteEnemy = enemy;
187 if( !IsDefined(
self.parasiteEnemy.hunted_by ) )
189 self.parasiteEnemy.hunted_by = 0;
191 self.parasiteEnemy.hunted_by++;
192 self SetLookAtEnt(
self.parasiteEnemy );
193 self SetTurretTargetEnt(
self.parasiteEnemy );
198 self endon(
"change_state" );
199 self endon(
"death" );
217 if( !isDefined( target ) )
219 self.parasiteEnemy = undefined;
223 self.parasiteEnemy = target;
225 self.parasiteEnemy.hunted_by += 1;
226 self SetLookAtEnt(
self.parasiteEnemy );
227 self SetTurretTargetEnt(
self.parasiteEnemy );
236 self useanimtree( #animtree );
245 self.health =
self.healthdefault;
249 self EnableAimAssist();
250 self SetNearGoalNotifyDist( 25 );
252 self SetDrawInfrared(
true );
255 self.fovcosinebusy = 0;
257 self.vehAirCraftCollisionEnabled =
true;
259 assert( isdefined(
self.scriptbundlesettings ) );
262 self.goalRadius = 999999;
263 self.goalHeight = 4000;
264 self SetGoal(
self.origin,
false,
self.goalRadius,
self.goalHeight );
266 self.is_parasite =
true;
270 if( IsDefined( level.vehicle_initializer_cb ) )
272 [[level.vehicle_initializer_cb]]( self );
305 self endon(
"death" );
307 self ASMRequestSubstate(
"death@stationary" );
309 if( IsDefined(
self.parasiteEnemy ) && IsDefined(
self.parasiteEnemy.hunted_by ) )
311 self.parasiteEnemy.hunted_by--;
314 self SetPhysAcceleration( ( 0, 0, -300 ) );
315 self.vehcheckforpredictedcrash =
true;
318 self playsound(
"zmb_parasite_explo" );
335 if ( IsDefined(
self.owner ) && IsDefined(
self.owner.enemy ) )
337 self.parasiteEnemy =
self.owner.enemy;
344 self endon(
"change_state" );
345 self endon(
"death" );
347 lastTimeChangePosition = 0;
348 self.shouldGotoNewPosition =
false;
349 self.lastTimeTargetInSight = 0;
350 self.lastTimeJuked =
false;
353 self ASMRequestSubstate(
"locomotion@movement" );
357 if( IsDefined(
self._override_parasite_combat_speed ) )
359 self SetSpeed(
self._override_parasite_combat_speed );
363 self SetSpeed(
self.settings.defaultMoveSpeed );
370 else if ( !IsDefined(
self.parasiteEnemy ) )
377 if(
self.goalforced )
380 returnData[
"origin" ] =
self GetClosestPointOnNavVolume(
self.goalpos, 100 );
381 returnData[
"centerOnNav" ] = IsPointInNavVolume(
self.origin,
"navvolume_small" );
384 else if( ( RandomInt( 100 ) <
self.settings.jukeprobability && !
IS_TRUE(
self.lastTimeJuked ) ) ||
IS_TRUE(
self._override_juke ) )
387 self.lastTimeJuked =
true;
388 self._override_juke = undefined;
393 self.lastTimeJuked =
false;
395 self.current_pathto_pos = returnData[
"origin" ];
396 if ( IsDefined(
self.current_pathto_pos ) )
398 if( IsDefined(
self.stuckTime ) )
400 self.stuckTime = undefined;
402 if (
self SetVehGoalPos(
self.current_pathto_pos,
true, returnData[
"centerOnNav" ] ) )
407 self playsound(
"zmb_vocals_parasite_juke" );
419 if( !
IS_TRUE( returnData[
"centerOnNav" ] ) )
422 if( !IsDefined(
self.stuckTime ) )
424 self.stuckTime = GetTime();
426 if( GetTime() -
self.stuckTime > 10000 )
428 self DoDamage(
self.health + 100,
self.origin );
433 if(
IS_TRUE(
self.lastTimeJuked ) )
438 self.parasiteEnemy DoDamage(
self.settings.meleedamage,
self.parasiteEnemy.origin,
self );
458 if( IsDefined(
self.parasiteEnemy ) &&
self VehCanSee(
self.parasiteEnemy ) && Distance2DSquared(
self.parasiteEnemy.origin,
self.origin ) <
SQR( 0.5 * (
self.settings.engagementDistMin +
self.settings.engagementDistMax ) * 3 ) )
461 self ASMRequestSubstate(
"fire@stationary" );
464 self PlaySound(
"zmb_vocals_parasite_preattack" );
468 self waittill(
"pre_fire" );
470 if( IsDefined(
self.parasiteEnemy ) &&
self VehCanSee(
self.parasiteEnemy ) && Distance2DSquared(
self.parasiteEnemy.origin,
self.origin ) <
SQR( 0.5 * (
self.settings.engagementDistMin +
self.settings.engagementDistMax ) * 3 ) )
473 self SetTurretTargetEnt(
self.parasiteEnemy,
self.parasiteEnemy GetVelocity() * 0.3 );
479 self ASMRequestSubstate(
"locomotion@movement" );
486 wait RandomFloatRange( 0.25, 0.5 );
491 wait RandomFloatRange( 1, 2 );
497 self endon(
"change_state" );
498 self endon(
"death" );
501 selfDistToTarget = Distance2D(
self.origin,
self.parasiteEnemy.origin );
503 goodDist = 0.5 * (
self.settings.engagementDistMin +
self.settings.engagementDistMax );
505 closeDist = 1.2 * goodDist;
506 farDist = 3 * goodDist;
508 queryMultiplier = MapFloat( closeDist, farDist, 1, 3, selfDistToTarget );
511 preferedHeightRange = 0.5 * (
self.settings.engagementHeightMax -
self.settings.engagementHeightMin );
517 if( !
IS_TRUE( queryResult.centerOnNav ) )
519 self.vehAirCraftCollisionEnabled =
false;
523 self.vehAirCraftCollisionEnabled =
true;
526 PositionQuery_Filter_DistanceToGoal( queryResult,
self );
530 goalHeight =
self.parasiteEnemy.origin[2] + 0.5 * (
self.settings.engagementHeightMin +
self.settings.engagementHeightMax );
533 best_point = undefined;
534 best_score = -999999;
536 foreach ( point
in queryResult.data )
538 if( !
IS_TRUE( queryResult.centerOnNav ) )
540 if( SightTracePassed(
self.origin, point.origin,
false, undefined ) )
543 if( trace_count > 3 )
548 if( !BulletTracePassed(
self.origin, point.origin,
false,
self ) )
559 ADD_POINT_SCORE( point,
"random", randomFloatRange( 0, randomness ) );
561 ADD_POINT_SCORE( point,
"engagementDist", -point.distAwayFromEngagementArea );
564 distFromPreferredHeight = abs( point.origin[2] - goalHeight );
565 if ( distFromPreferredHeight > preferedHeightRange )
567 heightScore = MapFloat( 0, 500, 0, 2000, distFromPreferredHeight );
568 ADD_POINT_SCORE( point,
"height", -heightScore );
571 if ( point.score > best_score )
573 best_score = point.score;
580 if (
IS_TRUE( GetDvarInt(
"hkai_debugPositionQuery") ) )
582 recordLine(
self.origin, best_point.origin, (0.3,1,0) );
583 recordLine(
self.origin,
self.parasiteEnemy.origin, (1,0,0.4) );
587 returnData[
"origin" ] = ( ( IsDefined( best_point ) ) ? best_point.origin : undefined );
588 returnData[
"centerOnNav" ] = queryResult.centerOnNav;
595 self endon(
"change_state" );
596 self endon(
"death" );
599 selfDistToTarget = Distance2D(
self.origin,
self.parasiteEnemy.origin );
601 goodDist = 0.5 * (
self.settings.forwardJukeEngagementDistMin +
self.settings.forwardJukeEngagementDistMax );
603 closeDist = 1.2 * goodDist;
604 farDist = 3 * goodDist;
606 queryMultiplier = MapFloat( closeDist, farDist, 1, 3, selfDistToTarget );
608 preferedHeightRange = 0.5 * (
self.settings.forwardJukeEngagementHeightMax -
self.settings.forwardJukeEngagementHeightMin );
613 if( !
IS_TRUE( queryResult.centerOnNav ) )
615 self.vehAirCraftCollisionEnabled =
false;
619 self.vehAirCraftCollisionEnabled =
true;
622 PositionQuery_Filter_DistanceToGoal( queryResult,
self );
626 goalHeight =
self.parasiteEnemy.origin[2] + 0.5 * (
self.settings.forwardJukeEngagementHeightMin +
self.settings.forwardJukeEngagementHeightMax );
629 best_point = undefined;
630 best_score = -999999;
633 foreach ( point
in queryResult.data )
635 if( !
IS_TRUE( queryResult.centerOnNav ) )
637 if( SightTracePassed(
self.origin, point.origin,
false, undefined ) )
640 if( trace_count > 3 )
645 if( !BulletTracePassed(
self.origin, point.origin,
false,
self ) )
656 ADD_POINT_SCORE( point,
"random", randomFloatRange( 0, randomness ) );
658 ADD_POINT_SCORE( point,
"engagementDist", -point.distAwayFromEngagementArea );
661 distFromPreferredHeight = abs( point.origin[2] - goalHeight );
662 if ( distFromPreferredHeight > preferedHeightRange )
664 heightScore = MapFloat( 0, 500, 0, 2000, distFromPreferredHeight );
665 ADD_POINT_SCORE( point,
"height", -heightScore );
668 if ( point.score > best_score )
670 best_score = point.score;
677 if (
IS_TRUE( GetDvarInt(
"hkai_debugPositionQuery") ) )
679 recordLine(
self.origin, best_point.origin, (0.3,1,0) );
680 recordLine(
self.origin,
self.parasiteEnemy.origin, (1,0,0.4) );
685 returnData[
"origin" ] = ( ( IsDefined( best_point ) ) ? best_point.origin : undefined );
686 returnData[
"centerOnNav" ] = queryResult.centerOnNav;
692 self endon(
"death" );
693 self endon(
"change_state" );
694 self endon(
"near_goal" );
695 self endon(
"reached_end_node" );
701 if( isdefined(
self.current_pathto_pos ) )
703 if( distance2dSquared(
self.current_pathto_pos,
self.goalpos ) >
SQR(
self.goalradius ) )
706 self._override_juke =
true;
707 self notify(
"near_goal" );
720 self endon(
"death" );
722 self.painStartTime = GetTime();
728 self PlaySound(
"zmb_vocals_parasite_pain" );
730 while ( GetTime() <
self.painStartTime + time * 1000 )
732 self SetVehVelocity(
self.velocity * stablizeParam );
733 self SetAngularVelocity(
self GetAngularVelocity() * stablizeParam );
737 if ( isdefined( restoreLookPoint ) )
739 restoreLookEnt =
Spawn(
"script_model", restoreLookPoint );
740 restoreLookEnt SetModel(
"tag_origin" );
742 self ClearLookAtEnt();
743 self SetLookAtEnt( restoreLookEnt );
744 self setTurretTargetEnt( restoreLookEnt );
747 self ClearLookAtEnt();
748 self ClearTurretTarget();
749 restoreLookEnt
delete();
756 function drone_pain( eAttacker, damageType, hitPoint, hitDirection, hitLocationInfo, partName )
762 ang_vel =
self GetAngularVelocity();
763 ang_vel += ( RandomFloatRange( -120, -100 ), yaw_vel, RandomFloatRange( -200, 200 ) );
764 self SetAngularVelocity( ang_vel );