1 #using scripts\codescripts\struct;
3 #using scripts\shared\gameskill_shared;
4 #using scripts\shared\hostmigration_shared;
5 #using scripts\shared\math_shared;
6 #using scripts\shared\array_shared;
7 #using scripts\shared\statemachine_shared;
8 #using scripts\shared\system_shared;
9 #using scripts\shared\util_shared;
10 #using scripts\shared\vehicle_shared;
11 #using scripts\shared\vehicle_death_shared;
12 #using scripts\shared\vehicle_ai_shared;
13 #using scripts\shared\vehicles\_attack_drone;
14 #using scripts\shared\flag_shared;
15 #using scripts\shared\turret_shared;
16 #using scripts\shared\ai_shared;
17 #using scripts\shared\ai\systems\ai_interface;
20 #insert scripts\shared\shared.gsh;
21 #insert scripts\shared\statemachine.gsh;
22 #insert scripts\shared\archetype_shared\archetype_shared.gsh;
24 #insert scripts\shared\ai\utility.gsh;
26 #define HUNTER_RADIUS 200
27 #define HUNTER_HALFHEIGHT 100
30 #define HUNTER_UNAWARE_SPEED_RATIO 0.5
31 #define HUNTER_COMBAT_SPEED_RATIO 1.0
32 #define HUNTER_STRAFE_SPEED_RATIO 2.0
35 #define HUNTER_SCANNER_TAG "tag_gunner_flash3"
36 #define HUNTER_SCANNER_SCANSPEED 1
37 #define HUNTER_SCANNER_PITCH 50 // 0 as horizontal, 90 as straight down
38 #define HUNTER_SCANNER_RANGE_PITCH 20
39 #define HUNTER_SCANNER_RANGE_YAW 45
40 #define HUNTER_SCANNER_FOV 190 //90
41 #define HUNTER_SCANNER_VIEW_DISTANCE 10000
44 #define HUNTER_MISSILE_WEAPON "hunter_rocket_turret"
45 #define HUNTER_MISSILE_WEAPON_PLAYER "hunter_rocket_turret_player"
46 #define HUNTER_TURRET_RANGE 1500
47 #define HUNTER_MISSILE_LOCKON_DELAY 1.5 // tunable length of persistent sight on target before fire rocket
48 #define HUNTER_MISSILE_MINIMAL_INTERVAL 8
51 #define HUNTER_DRONE_SPAWNER "spawner_bo3_attack_drone_enemy"
52 #define HUNTER_WAITTIME_BEFORE_DEPLOYDRONE 2.2 // will wait this amount of time after player run into a non-reachable space to deploy attack drones
55 #define HUNTER_ATTACK_TARGET_RANGE 1200
56 #define HUNTER_FOLLOWING_TARGET_RANGE 1200
57 #define HUNTER_CHANGE_POSITION_TOATTACK_TARGET_DELAY 0.5
58 #define HUNTER_GOAL_POINT_STEP HUNTER_RADIUS * 4
61 #define HUNTER_DAMAGE_AMOUNT_TO_SHOW_PAIN 1000 // 0 to disable
64 #define HUNTER_ENABLE_SCANNER_FX false
65 #define HUNTER_ENABLE_SIDETURRETS_YIELD_MAINTURRET_TARGET true // if true, side turret will not choose same target as main turret (so the player being targeted won't get cross fired by all three turrets)
66 #define HUNTER_ENABLE_WEAKSPOTS false
67 #define HUNTER_ENABLE_ATTACK_DRONES false
69 #define HUNTER_SEEK_ENEMY_DELAY 1.0
71 #define HUNTER_STRAFE_COOLDOWN 2.0
72 #define HUNTER_STRAFE_DISTANCE_TO_ENEMY_DISTANCE_RATIO 0.08
78 #using_animtree( "generic" );
109 self.weakSpotTags = [];
112 ARRAY_ADD(
self.weakSpotTags,
"tag_target_l" );
113 ARRAY_ADD(
self.weakSpotTags,
"tag_target_r" );
116 self.explosiveWeakSpotTags = [];
119 ARRAY_ADD(
self.explosiveWeakSpotTags,
"tag_fan_base_l" );
120 ARRAY_ADD(
self.explosiveWeakSpotTags,
"tag_fan_base_r" );
123 self.missileTags = [];
124 ARRAY_ADD(
self.missileTags,
"tag_rocket1" );
125 ARRAY_ADD(
self.missileTags,
"tag_rocket2" );
127 self.droneAttachTags = [];
130 ARRAY_ADD(
self.droneAttachTags,
"tag_drone_attach_l" );
131 ARRAY_ADD(
self.droneAttachTags,
"tag_drone_attach_r" );
137 self.dronesOwned = [];
141 foreach( droneTag
in self.droneAttachTags )
143 origin =
self GetTagOrigin( droneTag );
144 angles =
self GetTagAngles( droneTag );
149 drone.attachTag = droneTag;
150 drone.team =
self.team;
159 self endon(
"death" );
161 self UseAnimTree( #animtree );
163 Target_Set(
self, ( 0, 0, 90 ) );
167 self.health =
self.healthdefault;
172 self SetNearGoalNotifyDist( 50 );
174 self SetHoverParams( 15, 100, 40 );
175 self.flyheight = GetDvarFloat(
"g_quadrotorFlyHeight" );
178 self.fovcosinebusy = 0.574;
180 self.vehAirCraftCollisionEnabled =
true;
182 self.original_vehicle_type =
self.vehicletype;
186 self.goalRadius = 999999;
187 self.goalHeight = 999999;
188 self SetGoal(
self.origin,
false,
self.goalRadius,
self.goalHeight );
197 if( IsDefined( level.vehicle_initializer_cb ) )
199 [[level.vehicle_initializer_cb]]( self );
202 self.ignoreFireFly =
true;
203 self.ignoreDecoy =
true;
233 self PathVariableOffset( (10, 10, -30), 1 );
288 self endon(
"death" );
290 self notify(
"death_shut_off" );
292 if ( isdefined(
self.frontScanner ) )
294 self.frontScanner.sndScanningEnt
delete();
295 self.frontScanner
delete();
301 self endon(
"death" );
303 foreach ( drone
in self.dronesOwned )
305 if ( isalive( drone ) && Distance2DSquared(
self.origin, drone.origin ) <
SQR( 80 ) )
307 damageOrigin =
self.origin + (0,0,1);
308 drone finishVehicleRadiusDamage(
self.death_info.attacker,
self.death_info.attacker, 32000, 32000, 10, 0,
"MOD_EXPLOSIVE", level.weaponNone, damageOrigin, 400, -1, (0,0,1), 0);
315 self endon(
"death" );
317 if ( isdefined(
self.fakeTargetEnt ) )
319 self.fakeTargetEnt Delete();
332 self endon(
"death" );
335 if ( !isdefined( death_type ) )
337 params.death_type =
"gibbed";
338 death_type = params.death_type;
344 self SetSpeedImmediate( 0 );
345 self SetVehVelocity( ( 0, 0, 0 ) );
346 self SetPhysAcceleration( ( 0, 0, 0 ) );
347 self SetAngularVelocity( ( 0, 0, 0 ) );
358 accel =
self GetDefaultAcceleration();
359 self SetSpeed( ratio *
self.settings.defaultmovespeed, ratio * accel, ratio * accel );
364 self endon(
"change_state" );
365 self endon(
"death" );
367 if ( isdefined(
self.enemy ) )
372 self ClearLookAtEnt();
378 self waittill(
"enemy" );
385 self notify(
"end_movement_thread" );
390 self endon(
"death" );
391 self notify(
"end_movement_thread" );
392 self endon(
"end_movement_thread" );
394 constMinSearchRadius = 120;
395 constMaxSearchRadius = 800;
397 minSearchRadius =
math::clamp( constMinSearchRadius, 0,
self.goalRadius );
398 maxSearchRadius =
math::clamp( constMaxSearchRadius, constMinSearchRadius,
self.goalRadius );
403 timeAtSamePosition = 2.5 + randomfloat( 1 );
407 queryResult = PositionQuery_Source_Navigation(
self.origin, minSearchRadius, maxSearchRadius, halfHeight, innerSpacing,
self, outerSpacing );
408 PositionQuery_Filter_DistanceToGoal( queryResult,
self );
413 stayAtGoal = timeAtSamePosition > 0.2;
416 for ( i = 0; i < queryResult.data.size && !foundpath; i++ )
418 goalPos = queryResult.data[i].origin;
419 foundpath =
self SetVehGoalPos( goalPos, stayAtGoal,
true );
428 wait randomFloatRange( 0.5 * timeAtSamePosition, timeAtSamePosition );
459 self SetTurretTargetRelativeAngles( (10, -90, 0), 1 );
460 self SetTurretTargetRelativeAngles( (10, 90, 0), 2 );
466 accel =
self GetDefaultAcceleration();
467 self SetSpeed( ratio *
self.settings.defaultmovespeed, ratio * accel, ratio * accel );
476 self endon(
"change_state" );
477 self endon(
"death" );
479 if ( !isdefined(
self.enemy ) )
490 self waittill(
"no_enemy" );
497 self notify(
"end_attack_thread" );
498 self notify(
"end_movement_thread" );
499 self ClearTurretTarget();
509 accel = ratio *
self GetDefaultAcceleration();
510 speed = ratio *
self.settings.defaultmovespeed;
512 if ( strafe_speed_attribute > 0 )
514 speed = strafe_speed_attribute;
516 self SetSpeed( speed , accel, accel );
521 self endon(
"change_state" );
522 self endon(
"death" );
524 self ClearVehGoalPos();
526 distanceToTarget = 0.5 * (
self.settings.engagementDistMin +
self.settings.engagementDistMax );
528 target =
self.origin + AnglesToForward(
self.angles ) * distanceToTarget;
529 if ( isdefined(
self.enemy ) )
531 distanceToTarget = Distance(
self.origin,
self.enemy.origin );
536 if ( strafe_distance_attribute > 0 )
538 distanceThreshold = strafe_distance_attribute;
541 maxSearchRadius = distanceThreshold * 1.5;
543 outerSpacing = maxSearchRadius * 0.05;
544 innerSpacing = outerSpacing * 2;
546 queryResult = PositionQuery_Source_Navigation(
self.origin, 0, maxSearchRadius, halfHeight, innerSpacing,
self, outerSpacing );
547 PositionQuery_Filter_Directness( queryResult,
self.origin, target );
548 PositionQuery_Filter_DistanceToGoal( queryResult,
self );
549 PositionQuery_Filter_InClaimedLocation( queryResult,
self );
552 foreach ( point
in queryResult.data )
554 distanceToPointSqr = distanceSquared( point.origin,
self.origin );
555 if( distanceToPointSqr < distanceThreshold * 0.5 )
557 ADD_POINT_SCORE( point,
"distAway", -distanceThreshold );
559 ADD_POINT_SCORE( point,
"distAway", sqrt( distanceToPointSqr ) );
561 diffToPreferedDirectness = abs( point.directness - 0 );
562 directnessScore = MapFloat( 0, 1, 1000, 0, diffToPreferedDirectness );
563 if ( diffToPreferedDirectness > 0.1 )
565 directnessScore -= 500;
567 ADD_POINT_SCORE( point,
"directnessRaw", point.directness );
568 ADD_POINT_SCORE( point,
"directness", directnessScore );
570 if ( point.directionChange < 0.6 )
572 ADD_POINT_SCORE( point,
"directionChange", -2000 );
574 ADD_POINT_SCORE( point,
"directionChangeRaw", point.directionChange );
580 foreach ( point
in queryResult.data )
582 self.current_pathto_pos = point.origin;
584 foundpath =
self SetVehGoalPos(
self.current_pathto_pos,
true,
true );
595 if ( !isdefined( previous_state ) || previous_state ==
"strafe" )
597 previous_state =
"combat";
612 if(
self.goalforced )
617 selfDistToEnemy = Distance2D(
self.origin, enemy.origin );
620 goodDist = 0.5 * (
self.settings.engagementDistMin +
self.settings.engagementDistMax );
622 tooCloseDist = 0.8 * goodDist;
623 closeDist = 1.2 * goodDist;
624 farDist = 3 * goodDist;
626 queryMultiplier = MapFloat( closeDist, farDist, 1, 3, selfDistToEnemy );
628 preferedDistAwayFromOrigin = 150;
630 maxSearchRadius = 1000 * queryMultiplier;
631 halfHeight = 300 * queryMultiplier;
632 innerSpacing = 80 * queryMultiplier;
633 outerSpacing = 80 * queryMultiplier;
635 queryResult = PositionQuery_Source_Navigation(
self.origin, 0, maxSearchRadius, halfHeight, innerSpacing,
self, outerSpacing );
636 PositionQuery_Filter_DistanceToGoal( queryResult,
self );
637 PositionQuery_Filter_InClaimedLocation( queryResult,
self );
638 PositionQuery_Filter_Sight( queryResult, enemy.origin,
self GetEye() -
self.origin,
self, 0, enemy );
643 goalHeight = enemy.origin[2] + 0.5 * (
self.settings.engagementHeightMin +
self.settings.engagementHeightMax );
645 foreach ( point
in queryResult.data )
647 if ( !point.visibility )
649 ADD_POINT_SCORE( point,
"no visibility", -600 );
652 ADD_POINT_SCORE( point,
"engagementDist", -point.distAwayFromEngagementArea );
655 ADD_POINT_SCORE( point,
"distToOrigin", MapFloat( 0, preferedDistAwayFromOrigin, 0, 600, point.distToOrigin2D ) );
657 if( point.inClaimedLocation )
659 ADD_POINT_SCORE( point,
"inClaimedLocation", -500 );
663 preferedHeightRange = 75;
664 distFromPreferredHeight = abs( point.origin[2] - goalHeight );
665 if ( distFromPreferredHeight > preferedHeightRange )
667 heightScore = -MapFloat( preferedHeightRange, 5000, 0, 9000, distFromPreferredHeight );
668 ADD_POINT_SCORE( point,
"height", heightScore );
675 if( queryResult.data.size )
677 return queryResult.data[0].origin;
685 self endon(
"death" );
686 self notify(
"end_movement_thread" );
687 self endon(
"end_movement_thread" );
696 if ( !isdefined( enemy ) )
702 usePathfinding =
true;
703 onNavVolume = IsPointInNavVolume(
self.origin,
"navvolume_big" );
707 getbackPoint = undefined;
708 pointOnNavVolume =
self GetClosestPointOnNavVolume(
self.origin, 500 );
709 if ( isdefined( pointOnNavVolume ) )
711 if ( SightTracePassed(
self.origin, pointOnNavVolume,
false,
self ) )
713 getbackPoint = pointOnNavVolume;
717 if ( !isdefined( getbackPoint ) )
719 queryResult = PositionQuery_Source_Navigation(
self.origin, 0, 800, 400, 1.5 *
self.radius );
720 PositionQuery_Filter_Sight( queryResult,
self.origin, (0, 0, 0),
self, 1 );
721 getbackPoint = undefined;
722 foreach( point
in queryResult.data )
724 if ( point.visibility ===
true )
726 getbackPoint = point.origin;
732 if ( isdefined( getbackPoint ) )
734 self.current_pathto_pos = getbackPoint;
735 usePathfinding =
false;
740 if ( stuckCount == 1 )
742 stuckLocation =
self.origin;
744 else if ( stuckCount > 10 )
747 assert(
false,
"Hunter fall outside of NavVolume at " +
self.origin );
748 v_box_min = ( -
self.radius, -
self.radius, -
self.radius );
749 v_box_max = (
self.radius,
self.radius,
self.radius );
750 Box(
self.origin, v_box_min, v_box_max,
self.angles[1], (1,0,0), 1,
false, 1000000 );
751 if ( isdefined( stuckLocation ) )
753 Line( stuckLocation,
self.origin, (1,0,0), 1,
true, 1000000 );
764 if(
self.goalforced )
766 goalpos =
self GetClosestPointOnNavVolume(
self.goalpos, 200 );
767 if ( isdefined( goalpos ) )
769 self.current_pathto_pos = goalpos;
770 usePathfinding =
true;
774 self.current_pathto_pos =
self.goalpos;
775 usePathfinding =
false;
781 usePathfinding =
true;
785 if ( !isDefined(
self.current_pathto_pos ) )
791 distanceToGoalSq = DistanceSquared(
self.current_pathto_pos,
self.origin );
793 if ( distanceToGoalSq >
SQR( 0.5 * (
self.settings.engagementDistMin +
self.settings.engagementDistMax ) ) )
795 self SetSpeed(
self.settings.defaultMoveSpeed * 2 );
799 self SetSpeed(
self.settings.defaultMoveSpeed );
802 self SetLookAtEnt( enemy );
804 foundpath =
self SetVehGoalPos(
self.current_pathto_pos,
true, usePathfinding );
809 if (
IS_TRUE( GetDvarInt(
"hkai_debugPositionQuery") ) )
811 recordLine(
self.origin,
self.current_pathto_pos, (0.3,1,0) );
812 recordLine(
self.origin, enemy.origin, (1,0,0.4) );
824 if ( isdefined( enemy ) )
826 goalHeight = enemy.origin[2] + 0.5 * (
self.settings.engagementHeightMin +
self.settings.engagementHeightMax );
827 distFromPreferredHeight = abs(
self.origin[2] - goalHeight );
829 farDist =
self.settings.engagementDistMax;
830 nearDist =
self.settings.engagementDistMin;
832 selfDistToEnemy = Distance2D(
self.origin, enemy.origin );
834 if (
self VehCanSee( enemy ) && selfDistToEnemy < farDist && selfDistToEnemy > nearDist && distFromPreferredHeight < 230 )
837 if ( msg ==
"enemy_not_visible" )
840 if ( msg !=
"timeout" )
856 self endon(
"death" );
857 self endon(
"change_state" );
858 self endon(
"end_attack_thread" );
859 self endon(
"faketarget_stop_moving" );
860 enemy endon(
"death" );
862 if ( !isdefined(
self.fakeTargetEnt ) )
864 self.fakeTargetEnt =
Spawn(
"script_origin", point );
867 self.fakeTargetEnt Unlink();
869 self.fakeTargetEnt.origin = point;
870 self SetTurretTargetEnt(
self.fakeTargetEnt );
871 self waittill(
"turret_on_target" );
873 timeStart = GetTime();
875 if( IsSentient( enemy ) )
877 offset = enemy GetEye() - enemy.origin;
880 while( GetTime() < timeStart + timeToHit * 1000 )
882 self.fakeTargetEnt.origin = LerpVector( point, enemy.origin + offset, ( GetTime() - timeStart ) / ( timeToHit * 1000 ) );
887 self.fakeTargetEnt.origin = enemy.origin + offset;
889 self.fakeTargetEnt LinkTo( enemy );
894 self endon(
"death" );
895 self endon(
"change_state" );
896 self endon(
"end_attack_thread" );
901 if( isdefined( enemy ) )
903 self SetLookAtEnt( enemy );
905 if(
self VehCanSee( enemy ) )
907 vectorFromEnemy = VectorNormalize(
FLAT_ORIGIN( (
self.origin - enemy.origin) ) );
911 self waittill(
"turret_on_target" );
914 self ClearTurretTarget();
915 self SetTurretTargetRelativeAngles( (15,0,0), 0 );
917 if( isdefined( enemy ) && IsAI( enemy ) )
919 wait( 2.5 + RandomFloat( 0.5 ) );
923 wait( 2.0 + RandomFloat( 0.4 ) );
933 self ClearTurretTarget();
934 self ClearLookAtEnt();
942 self endon(
"death" );
943 self endon(
"change_state" );
944 self endon(
"end_attack_thread" );
949 if ( !isdefined( enemy ) )
959 self notify(
"end_movement_thread" );
960 self ClearVehGoalPos();
961 self SetVehGoalPos(
self.origin,
true,
false );
963 target = enemy.origin;
964 self SetLookAtEnt( enemy );
969 eye =
self GetTagOrigin(
"tag_eye" );
970 if ( isdefined( enemy ) )
972 anglesToTarget = VectorToAngles( enemy.origin - eye );
973 angles = anglesToTarget -
self.angles;
974 if ( -30 < angles[0] && angles[0] < 60 && -70 < angles[1] && angles[1] < 70 )
976 target = enemy.origin;
980 anglesToTarget = VectorToAngles( target - eye );
985 anglesToTarget = VectorToAngles( target - eye );
988 rightDir = AnglesToRight( anglesToTarget );
992 offset[0] = -rightDir * randomRange * 2 + ( RandomFloatRange( -randomRange, randomRange ), RandomFloatRange( -randomRange, randomRange ), 0 );
993 offset[1] = rightDir * randomRange * 2 + ( RandomFloatRange( -randomRange, randomRange ), RandomFloatRange( -randomRange, randomRange ), 0 );
999 if ( isdefined( enemy ) )
1001 eye =
self GetTagOrigin(
"tag_eye" );
1002 angles = VectorToAngles( enemy.origin - eye ) -
self.angles;
1003 if ( -30 < angles[0] && angles[0] < 60 && -70 < angles[1] && angles[1] < 70 )
1005 target = enemy.origin;
1022 if (
self.ignoreall ===
true )
1029 main_turret_target =
self.enemy;
1031 if ( n_index === 2 )
1038 ArrayRemoveValue( a_potential_targets, main_turret_target );
1039 ArrayRemoveValue( a_potential_targets, other_turret_target );
1042 e_best_target = undefined;
1044 while ( !isdefined( e_best_target ) && ( a_potential_targets.size > 0 ) )
1046 e_closest_target = ArrayGetClosest(
self.origin, a_potential_targets );
1050 e_best_target = e_closest_target;
1054 ArrayRemoveValue( a_potential_targets, e_closest_target );
1058 return e_best_target;
1066 self endon(
"death" );
1072 if ( isdefined( waittimeAfterBlinkLights ) && waittimeAfterBlinkLights > 0 )
1074 wait waittimeAfterBlinkLights;
1078 if ( !isdefined( offset ) )
1080 offset = ( 0, 0, 0 );
1083 spawnTag =
self.missileTags[ launcher_index ];
1084 origin =
self GetTagOrigin( spawnTag );
1085 angles =
self GetTagAngles( spawnTag );
1086 forward = AnglesToForward( angles );
1087 up = AnglesToUp( angles );
1089 if ( isdefined( spawnTag ) && isdefined( target ) )
1094 if ( IsEntity( target ) )
1096 missile = MagicBullet( weapon, origin, target.origin + offset,
self, target, offset );
1099 else if ( IsVec( target ) )
1101 missile = MagicBullet( weapon, origin, target + offset,
self );
1105 missile = MagicBullet( weapon, origin, target.origin + offset,
self );
1112 self endon(
"death" );
1116 playFX( level.remote_mortar_fx[
"missileExplode"],
self.origin );
1117 self playlocalsound(
"mpl_ks_reaper_explosion" );
1124 self playsound(
"veh_hunter_alarm_target" );
1132 enemy_team =
"allies";
1136 aiArray = GetAITeamArray( enemy_team );
1137 enemyArray = ArrayCombine( enemyArray, aiArray,
false,
false );
1142 playerArray = GetPlayers( enemy_team );
1143 enemyArray = ArrayCombine( enemyArray, playerArray,
false,
false );
1156 if ( !isdefined( point ) )
1161 scanner =
self.frontScanner;
1162 vector_to_point = point - scanner.origin;
1170 if ( in_view &&
IS_TRUE( do_trace ) && isdefined(
self.enemy ) )
1172 in_view = SightTracePassed( scanner.origin, point,
false,
self.enemy );
1181 target_is_valid =
true;
1184 if (
IS_TRUE( target.ignoreme ) || ( target.health <= 0 ) )
1186 target_is_valid =
false;
1189 else if ( IsSentient( target ) && ( ( target IsNoTarget() ) || ( target
ai::is_dead_sentient() ) ) )
1191 target_is_valid =
false;
1194 else if ( isdefined( target.origin ) && !
is_point_in_view( target.origin, do_trace ) )
1196 target_is_valid =
false;
1199 return target_is_valid;
1205 validEnemyArray = [];
1208 foreach( enemy
in enemyArray )
1216 return validEnemyArray;
1223 self.frontScanner SetModel(
"tag_origin" );
1228 self.frontScanner.owner =
self;
1229 self.frontScanner.hasTargetEnt =
false;
1231 self.frontScanner.sndScanningEnt =
spawn(
"script_origin",
self.frontScanner.origin + anglesToForward(
self.angles ) * 1000 );
1232 self.frontScanner.sndScanningEnt linkto(
self.frontScanner );
1240 PlayFxOnTag(
self.settings.spotlightfx,
self.frontScanner,
"tag_origin" );
1247 if ( !isdefined( offset ) )
1249 offset = ( 0, 0, 0 );
1252 if( IsDefined( targetEnt ) )
1254 self.frontScanner.targetEnt = targetEnt;
1255 self.frontScanner.hasTargetEnt =
true;
1256 self SetGunnerTargetEnt(
self.frontScanner.targetEnt, offset, 2 );
1263 self.frontScanner.hasTargetEnt =
false;
1264 self ClearGunnerTarget( 2 );
1270 if( IsDefined( targetPos ) )
1272 self.frontScanner.targetPos = targetPos;
1273 self SetGunnerTargetVec(
self.frontScanner.targetPos, 2 );
1279 self endon(
"death_shut_off" );
1280 self endon(
"crash_done" );
1281 self endon(
"death" );
1285 offsetFactorPitch = 0;
1286 offsetFactorYaw = 0;
1295 scannerDirection = undefined;
1299 scannerOrigin =
self.frontScanner.origin;
1305 scannerDirection = anglesToForward(
self.angles + offset );
1307 else if ( !IsDefined(
self.enemy ) )
1311 self.frontScanner.sndScanningEnt playloopsound(
"veh_hunter_scanner_loop", 1 );
1314 offsetFactorPitch = offsetFactorPitch + pitchStep;
1315 offsetFactorYaw = offsetFactorYaw + yawStep;
1317 offset = (
HUNTER_SCANNER_PITCH, 0, 0 ) + ( Sin( offsetFactorPitch ) * pitchRange, Cos( offsetFactorYaw ) * yawRange, 0 );
1318 scannerDirection = anglesToForward(
self.angles + offset );
1322 if ( enemies.size > 0 )
1324 closest_enemy = ArrayGetClosest(
self.origin, enemies );
1326 self.favoriteEnemy = closest_enemy;
1327 /# line( scannerOrigin, closest_enemy.origin, ( 0, 1, 0 ), 1, 3 ); #/
1334 self notify (
"hunter_lockOnTargetInSight" );
1338 self notify (
"hunter_lockOnTargetOutSight" );
1341 scannerDirection = VectorNormalize(
self.enemy.origin - scannerOrigin );
1345 self.frontScanner.sndScanningEnt stoploopsound( 1 );
1349 targetLocation = scannerOrigin + scannerDirection * 1000;
1352 /# line( scannerOrigin,
self.frontScanner.targetPos, ( 0, 1, 0 ), 1, 1000 ); #/
1359 self waittill(
"exit_vehicle", player );
1361 player.ignoreme =
false;
1362 player DisableInvulnerability();
1364 self SetHeliHeightLock(
false );
1365 self EnableAimAssist();
1366 self SetVehicleType(
self.original_vehicle_type );
1367 self.attachedpath = undefined;
1368 self SetGoal(
self.origin,
false, 4096, 512 );
1374 params.driver =
self GetSeatOccupant( 0 );
1375 if ( isdefined( params.driver ) )
1377 self DisableAimAssist();
1381 params.driver.ignoreme =
true;
1382 params.driver EnableInvulnerability();
1384 if ( isdefined(
self.vehicle_weapon_override ) )
1386 self SetVehWeapon(
self.vehicle_weapon_override );
1398 if ( isdefined(
self.goal_node ) && isdefined(
self.goal_node.hunter_claimed ) )
1400 self.goal_node.hunter_claimed = undefined;
1403 self ClearTargetEntity();
1404 self ClearVehGoalPos();
1405 self PathVariableOffsetClear();
1406 self PathFixedOffsetClear();
1407 self ClearLookAtEnt();
1413 self endon(
"death" );
1414 self endon(
"exit_vehicle" );
1416 weapon =
self SeatGetWeapon( 1 );
1417 fireTime = weapon.fireTime;
1421 self SetGunnerTargetVec(
self GetTurretTargetVec( 0 ), 0 );
1422 if(
self IsDriverFiring( ) )
1424 self FireWeapon( 1 );
1432 self endon(
"death" );
1433 self endon(
"exit_vehicle" );
1435 weapon =
self SeatGetWeapon( 2 );
1436 fireTime = weapon.fireTime;
1440 self SetGunnerTargetVec(
self GetTurretTargetVec( 0 ), 1 );
1441 if(
self IsDriverFiring( ) )
1443 self FireWeapon( 2 );
1451 self endon(
"death" );
1452 self endon(
"exit_vehicle" );
1455 fireTime = weapon.fireTime;
1456 driver =
self GetSeatOccupant( 0 );
1461 if( driver ButtonPressed(
"BUTTON_A" ) )
1463 spawnTag0 =
self.missileTags[ 0 ];
1464 spawnTag1 =
self.missileTags[ 1 ];
1465 origin0 =
self GetTagOrigin( spawnTag0 );
1466 origin1 =
self GetTagOrigin( spawnTag1 );
1467 target =
self GetTurretTargetVec( 0 );
1469 MagicBullet( weapon, origin0, target );
1470 MagicBullet( weapon, origin1, target );
1481 self endon(
"change_state" );
1482 self endon(
"crash_done" );
1483 self endon(
"death" );
1487 self waittill(
"veh_collision", velocity, normal );
1488 driver =
self GetSeatOccupant( 0 );
1489 if ( isdefined( driver ) && LengthSquared( velocity ) > 70 * 70 )
1491 Earthquake( 0.25, 0.25, driver.origin, 50 );
1492 driver PlayRumbleOnEntity(
"damage_heavy" );
1500 self endon(
"death" );
1501 self endon(
"exit_vehicle" );
1505 vr = Abs(
self GetSpeed() /
self GetMaxSpeed() );
1509 level.player PlayRumbleOnEntity(
"hunter_fly" );
1514 time = RandomFloatRange( 0.1, 0.2 );
1515 Earthquake( RandomFloatRange( 0.1, 0.15 ), time,
self.origin, 200 );
1516 level.player PlayRumbleOnEntity(
"hunter_fly" );
1524 self endon(
"death" );
1525 self endon(
"exit_vehicle" );
1527 const max_self_destruct_time = 5;
1529 self_destruct =
false;
1530 self_destruct_time = 0;
1534 if ( !self_destruct )
1536 if ( level.player MeleeButtonPressed() )
1538 self_destruct =
true;
1539 self_destruct_time = max_self_destruct_time;
1547 IPrintLnBold( self_destruct_time );
1551 self_destruct_time -= 1;
1552 if ( self_destruct_time == 0 )
1554 driver =
self GetSeatOccupant( 0 );
1555 if ( isdefined( driver ) )
1557 driver DisableInvulnerability();
1560 Earthquake( 3, 1,
self.origin, 256 );
1561 RadiusDamage(
self.origin, 1000, 15000, 15000, level.player,
"MOD_EXPLOSIVE" );
1562 self DoDamage(
self.health + 1000,
self.origin );
1572 self endon(
"death" );
1573 self endon(
"emped" );
1574 self endon(
"landed" );
1576 while ( isdefined(
self.
emped ) )
1578 velocity =
self.velocity;
1579 self.angles = (
self.angles[0] * 0.85,
self.angles[1],
self.angles[2] * 0.85 );
1580 ang_vel =
self GetAngularVelocity() * 0.85;
1581 self SetAngularVelocity( ang_vel );
1582 self SetVehVelocity( velocity );
1589 self endon(
"death" );
1590 self endon(
"emped" );
1594 wait RandomFloatRange( 4, 7 );
1622 self endon(
"death" );
1623 self.painStartTime = GetTime();
1629 while ( GetTime() <
self.painStartTime + time * 1000 )
1631 self SetVehVelocity(
self.velocity * velocityStablizeParam );
1632 self SetAngularVelocity(
self GetAngularVelocity() * rotationStablizeParam );
1636 if ( isdefined( restoreLookPoint ) )
1638 restoreLookEnt =
Spawn(
"script_model", restoreLookPoint );
1639 restoreLookEnt SetModel(
"tag_origin" );
1641 self ClearLookAtEnt();
1642 self SetLookAtEnt( restoreLookEnt );
1643 self setTurretTargetEnt( restoreLookEnt );
1646 self ClearLookAtEnt();
1647 self ClearTurretTarget();
1648 restoreLookEnt
delete();
1651 self.inpain =
false;
1657 if( !isdefined( hitPoint ) || !isdefined( hitDirection ) )
1662 self SetVehVelocity(
self.velocity + VectorNormalize( hitDirection ) * 20 );
1666 vecRight = anglesToRight(
self.angles );
1668 yaw_vel =
sign * RandomFloatRange( 100, 140 );
1670 ang_vel =
self GetAngularVelocity();
1671 ang_vel += ( RandomFloatRange( -120, -100 ), yaw_vel, RandomFloatRange( -100, 100 ) );
1672 self SetAngularVelocity( ang_vel );
1680 function HunterCallback_VehicleDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal )
1682 driver =
self GetSeatOccupant( 0 );
1685 if ( isdefined( eAttacker ) && eAttacker.team ==
self.team )
1690 num_players = GetPlayers().size;
1691 maxDamage =
self.healthdefault * ( 0.35 - 0.025 * num_players );
1692 if ( sMeansOfDeath !==
"MOD_UNKNOWN" && iDamage > maxDamage )
1694 iDamage = maxDamage;
1697 if ( !isdefined(
self.damageLevel ) )
1699 self.damageLevel = 0;
1700 self.newDamageLevel =
self.damageLevel;
1704 if ( newDamageLevel >
self.damageLevel )
1706 self.newDamageLevel = newDamageLevel;
1709 if (
self.newDamageLevel >
self.damageLevel )
1711 self.damageLevel =
self.newDamageLevel;
1712 if (
self.pain_when_damagelevel_change ===
true )