1 #using scripts\codescripts\struct;
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;
9 #using scripts\shared\weapons\_heatseekingmissile;
11 #using scripts\shared\vehicle_shared;
12 #using scripts\shared\vehicle_death_shared;
13 #using scripts\shared\vehicle_ai_shared;
15 #insert scripts\shared\shared.gsh;
16 #insert scripts\shared\statemachine.gsh;
17 #insert scripts\shared\archetype_shared\archetype_shared.gsh;
19 #using scripts\shared\ai\systems\blackboard;
20 #using scripts\shared\ai\blackboard_vehicle;
21 #insert scripts\shared\ai\utility.gsh;
23 #using scripts\shared\turret_shared;
25 #define AMWS_MAX_TIME_AT_SAME_POSITION 1.0
26 #define AMWS_CHANGE_POSITION_TOATTACK_TARGET_DELAY 0.5
28 #define AMWS_MOVE_DIST_MIN 80
29 #define AMWS_MOVE_DIST_MAX 500
30 #define AMWS_AWAY_FROM_CHARACTER 200
32 #define AMWS_EVADE_DIST_MIN 120
33 #define AMWS_EVADE_DIST_MAX 360
34 #define AMWS_EVADE_HALF_HEIGHT ( AMWS_MOVE_DIST_MAX * 0.5 )
35 #define AMWS_EVADE_POINT_SPACING_FACTOR ( 1.5 )
36 #define AMWS_ACCELERATION VAL( self.settings.default_move_acceleration, 10.0 )
38 #define AMWS_ENEMY_TOO_CLOSE_DIST ( 0.8 * 0.5 * ( self.settings.engagementDistMin + self.settings.engagementDistMax ) )
40 #define AMWS_REPATH_RANGE 100
42 #define WEAPON_REGULAR "amws_gun_turret"
43 #define WEAPON_STATIONARY "amws_gun_turret_stationary"
45 #using_animtree( "generic" );
58 self UseAnimTree( #animtree );
60 Target_Set(
self, ( 0, 0, 0 ) );
65 self.health =
self.healthdefault;
69 self EnableAimAssist();
70 self SetNearGoalNotifyDist( 40 );
73 self.fovcosinebusy = 0.574;
75 self.vehAirCraftCollisionEnabled =
true;
77 assert( isdefined(
self.scriptbundlesettings ) );
80 self.goalRadius = 999999;
81 self.goalHeight = 512;
82 self SetGoal(
self.origin,
false,
self.goalRadius,
self.goalHeight );
85 self.delete_on_death =
false;
92 self ASMRequestSubstate(
"locomotion@movement" );
94 self.variant =
"light_weight";
95 if( IsSubStr(
self.vehicleType,
"pamws" ) )
97 self.variant =
"armored";
103 if( IsDefined( level.vehicle_initializer_cb ) )
105 [[level.vehicle_initializer_cb]]( self );
147 self endon(
"death" );
150 if ( !isdefined( death_type ) )
152 params.death_type =
"gibbed";
153 death_type = params.death_type;
156 if ( death_type ===
"suicide_crash" )
166 self endon(
"death" );
168 goaldir = AnglesToForward(
self.angles );
169 goalDist = RandomFloatRange( 300, 400 );
170 goalpos =
self.origin + goaldir * goalDist;
172 self SetMaxAccelerationScale( 50 /
self GetDefaultAcceleration() );
173 self SetSpeed(
self.settings.surgespeedmultiplier *
self.settings.defaultMoveSpeed );
174 self SetVehGoalPos( goalpos,
false );
177 self SetMaxSpeedScale( 0.1 );
178 self SetSpeed( 0.1 );
181 self.death_type =
"gibbed";
189 self endon(
"change_state" );
190 self endon(
"death" );
192 driver =
self GetSeatOccupant( 0 );
194 if ( isPlayer( driver ) )
198 driver endon(
"disconnect" );
202 if (
self.cobra ===
false )
218 if ( isdefined(
self.settings.cobra_fx_1 ) && isdefined(
self.settings.cobra_tag_1 ) )
220 PlayFxOnTag(
self.settings.cobra_fx_1,
self,
self.settings.cobra_tag_1 );
222 self ASMRequestSubstate(
"cobra@stationary" );
232 self notify(
"disable_lens_flare" );
233 self ASMRequestSubstate(
"locomotion@movement" );
242 self endon (
"death");
243 self endon (
"change_state");
245 angles =
self GetTagAngles(
"tag_turret" );
246 self SetTurretTargetRelativeAngles( ( 45, angles[1] -
self.angles[1], 0 ), 0 );
247 angles =
self GetTagAngles(
"tag_gunner_turret1" );
248 self SetTurretTargetRelativeAngles( ( 45, angles[1] -
self.angles[1], 0 ), 1 );
258 self endon(
"change_state" );
259 self endon(
"death" );
262 self SetMaxAccelerationScale( 50 /
self GetDefaultAcceleration() );
269 self SetMaxSpeedScale( 0.1 );
270 self SetSpeed( 0.1 );
288 self endon(
"death" );
289 self endon(
"change_state" );
291 self notify(
"stop_rocket_firing_thread" );
301 transformWhenEnemyClose = ( RandomInt( 100 ) < 25 );
302 losePatientTime = 3 + RandomFloat( 2 );
313 evade_now = ( ( (
self.settings.evade_enemies_locked_on_me === true ) &&
self.locked_on ) || ( (
self.settings.evade_enemies_locking_on_me === true ) &&
self.locking_on ) );
320 if( isdefined(
self.enemy ) )
322 distSqr = DistanceSquared(
self.enemy.origin,
self.origin );
334 if ( !
self VehSeenRecently(
self.enemy, losePatientTime ) )
340 if (
self VehCanSee(
self.enemy ) )
342 if( distSqr <
SQR(
self.settings.engagementDistMax * 3 ) )
344 self SetTurretTargetEnt(
self.enemy, (0,0,-5) );
345 self SetGunnerTargetEnt(
self.enemy, (0,0,-5), 0 );
353 weapon =
self SeatGetWeapon( 1 );
354 if ( weapon.name==
"none" )
371 self notify(
"stop_rocket_firing_thread" );
413 return VAL(
self.settings.ai_uses_minigun,
true );
418 weapon =
self SeatGetWeapon( 1 );
419 if ( weapon.name==
"none" )
422 self endon(
"death" );
423 self endon(
"change_state" );
425 self SetOnTargetAngle( 7 );
426 self SetOnTargetAngle( 7, 0 );
433 if (
self.avoid_shooting_owner ===
true && isdefined(
self.owner ) )
442 if( isdefined(
self.enemy ) &&
self VehCanSee(
self.enemy ) && DistanceSquared(
self.enemy.origin,
self.origin ) <
SQR(
self.settings.engagementDistMax * 3 ) )
444 self SetGunnerTargetEnt(
self.enemy, (0,0,0), 0 );
448 self SetTurretSpinning(
true );
453 if ( !
self.gunner1ontarget )
458 if (
self.gunner1ontarget )
460 if( isdefined(
self.enemy ) &&
self VehCanSee(
self.enemy ) )
462 self vehicle_ai::fire_for_time( RandomFloatRange(
self.settings.burstFireDurationMin,
self.settings.burstFireDurationMax ), 1,
self.enemy );
467 self SetTurretSpinning(
false );
470 if( isdefined(
self.enemy ) && IsAI(
self.enemy ) )
472 wait( RandomFloatRange(
self.settings.burstFireAIDelayMin,
self.settings.burstFireAIDelayMax ) );
476 wait( RandomFloatRange(
self.settings.burstFireDelayMin,
self.settings.burstFireDelayMax ) );
493 self endon(
"change_state" );
494 self endon(
"death" );
496 lastTimeChangePosition = 0;
497 self.shouldGotoNewPosition =
false;
498 self.lastTimeTargetInSight = 0;
501 self.lock_evading = 0;
505 if (
self.lock_evading == 0 )
507 self SetSpeed(
self.settings.defaultMoveSpeed );
515 if( DistanceSquared(
self.enemy.origin,
self.origin ) <
SQR(
self.settings.engagementDistMax * 2 ) )
527 if (
self.settings.engage_enemies_locked_on_me ===
true &&
self.locked_on )
530 self.shouldGotoNewPosition =
true;
532 else if (
self.settings.engage_enemies_locking_on_me ===
true &&
self.locking_on )
535 self.shouldGotoNewPosition =
true;
540 self.lock_evading = 0;
541 if (
self.settings.evade_enemies_locked_on_me ===
true )
543 self.lock_evading |=
self.locked_on;
545 if (
self.settings.evade_enemies_locking_on_me ===
true )
547 self.lock_evading |=
self.locking_on;
548 self.lock_evading |=
self.locking_on_hacking;
556 else if ( !IsDefined(
self.enemy ) )
558 should_slow_down_at_goal =
true;
560 if (
self.lock_evading )
563 should_slow_down_at_goal =
false;
570 if ( IsDefined(
self.current_pathto_pos ) )
572 if (
self SetVehGoalPos(
self.current_pathto_pos, should_slow_down_at_goal,
true ) )
577 self notify(
"amws_end_interrupt_watch" );
578 self playsound (
"veh_amws_scan");
586 self SetTurretTargetEnt(
self.enemy );
588 if (
self vehCanSee(
self.enemy ) )
590 self.lastTimeTargetInSight = GetTime();
593 if (
self.shouldGotoNewPosition ==
false )
597 self.shouldGotoNewPosition =
true;
601 self.shouldGotoNewPosition =
true;
605 if (
self.shouldGotoNewPosition )
607 should_slow_down_at_goal =
true;
609 if (
self.lock_evading )
612 should_slow_down_at_goal =
false;
620 if ( IsDefined(
self.current_pathto_pos ) )
622 if (
self SetVehGoalPos(
self.current_pathto_pos, should_slow_down_at_goal,
true ) )
627 self notify(
"amws_end_interrupt_watch" );
630 if ( isdefined(
self.enemy ) &&
vehicle_ai::IsCooldownReady(
"rocket", 0.5 ) &&
self VehCanSee(
self.enemy ) &&
self.gib_rocket !==
true )
635 lastTimeChangePosition = GetTime();
636 self.shouldGotoNewPosition =
false;
647 self endon(
"death" );
648 self endon(
"change_state" );
649 self notify(
"stop_rocket_firing_thread" );
650 self endon(
"stop_rocket_firing_thread" );
652 if ( !
self.turretontarget )
657 if ( isdefined(
self.enemy ) &&
self.turretontarget )
671 if (
self.lock_evade_now ===
true )
673 perform_evasion_reaction_wait =
true;
677 locked_on_notify = undefined;
678 locking_on_notify = undefined;
680 reacting_to_locks = (
self.settings.evade_enemies_locked_on_me === true ) || (
self.settings.engage_enemies_locked_on_me ===
true );
681 reacting_to_locking = (
self.settings.evade_enemies_locking_on_me === true ) || (
self.settings.engage_enemies_locking_on_me ===
true );
683 previous_locked_on_to_me =
self.locked_on;
684 previous_locking_on_to_me =
self.locking_on;
686 if ( reacting_to_locks )
688 locked_on_notify =
"missle_lock";
691 if ( reacting_to_locking )
693 locking_on_notify =
"locking on";
698 locked_on_to_me_just_changed = previous_locked_on_to_me !=
self.locked_on &&
self.locked_on;
699 locking_on_to_me_just_changed = previous_locking_on_to_me !=
self.locking_on &&
self.locking_on;
701 perform_evasion_reaction_wait = ( ( reacting_to_locks && locked_on_to_me_just_changed ) || ( reacting_to_locking && locking_on_to_me_just_changed ) );
705 if ( perform_evasion_reaction_wait )
711 wait RandomFloatRange(
VAL(
self.settings.enemy_evasion_reaction_time_min, 0.1 ),
VAL(
self.settings.enemy_evasion_reaction_time_max, 0.2 ) );
716 self endon(
"death" );
717 self endon(
"change_state" );
718 self notify(
"stop_rocket_firing_thread" );
719 self endon(
"stop_rocket_firing_thread" );
721 if ( isdefined( enemy ) )
723 self SetTurretTargetEnt( enemy );
726 if (
self.variant ==
"armored" )
739 if(
self.goalforced )
744 queryMultiplier = 1.5;
747 if ( queryResult.data.size == 0 )
750 queryResult = PositionQuery_Source_Navigation(
self.origin, 36, 120, 240,
self.radius,
self );
753 PositionQuery_Filter_DistanceToGoal( queryResult,
self );
755 PositionQuery_Filter_InClaimedLocation( queryResult,
self );
757 best_point = undefined;
758 best_score = -999999;
760 foreach ( point
in queryResult.data )
762 randomScore = randomFloatRange( 0, 100 );
763 distToOriginScore = point.distToOrigin2D * 0.2;
765 if( point.inClaimedLocation )
770 point.score += randomScore + distToOriginScore;
772 if ( point.score > best_score )
774 best_score = point.score;
779 /#
self.debug_ai_move_to_points_considered = queryResult.data; #/
781 if( !isdefined( best_point ) )
783 /#
self.debug_ai_movement_type =
"wander ( 0 / " + queryResult.data.size +
" )"; #/
784 /#
self.debug_ai_move_to_point = undefined; #/
788 /#
self.debug_ai_movement_type =
"wander - " + queryResult.data.size; #/
789 /#
self.debug_ai_move_to_point = best_point.origin; #/
790 return best_point.origin;
795 assert( isdefined( client_flags ) );
797 self SetSpeed(
self.settings.defaultMoveSpeed *
VAL(
self.settings.lock_evade_speed_boost, 2.0 ) );
801 queryResult = PositionQuery_Source_Navigation(
self.origin,
809 PositionQuery_Filter_InClaimedLocation( queryResult,
self );
813 foreach ( point
in queryResult.data )
815 if( point.inClaimedLocation )
817 ADD_POINT_SCORE( point,
"inClaimedLocation", -500 );
821 remaining_lock_threats_to_evaluate = 3;
823 remaining_flags_to_process = client_flags;
824 for ( i = 0; remaining_flags_to_process && remaining_lock_threats_to_evaluate > 0 && i < level.players.size; i++ )
826 attacker = level.players[ i ];
827 if ( isdefined( attacker ) )
829 client_flag = ( 1 << attacker getEntityNumber() );
830 if ( client_flag & remaining_flags_to_process )
833 PositionQuery_Filter_Directness( queryResult,
self.origin, attacker.origin );
836 foreach ( point
in queryResult.data )
838 abs_directness = Abs( point.directness );
839 if( abs_directness < 0.2 )
841 ADD_POINT_SCORE( point,
"evading_directness", 200 );
843 else if ( abs_directness >
VAL(
self.settings.lock_evade_enemy_line_of_sight_directness, 0.9 ) )
845 ADD_POINT_SCORE( point,
"evading_directness_line_of_sight", -101 );
850 remaining_flags_to_process &= ~client_flag;
851 remaining_lock_threats_to_evaluate--;
857 PositionQuery_Filter_Directness( queryResult,
self.origin,
self.origin + ( AnglesToForward(
self.angles ) *
AMWS_EVADE_DIST_MAX ) );
858 foreach ( point
in queryResult.data )
860 if( point.directness > 0.5 )
862 ADD_POINT_SCORE( point,
"prefer forward motion", 105 );
867 best_point = undefined;
868 best_score = -999999;
870 foreach ( point
in queryResult.data )
872 if ( point.score > best_score )
874 best_score = point.score;
879 self.lock_evade_now =
false;
883 /#
self.debug_ai_move_to_points_considered = queryResult.data; #/
885 if( !isdefined( best_point ) )
887 /#
self.debug_ai_movement_type =
"evasive ( 0 / " + queryResult.data.size +
" )"; #/
888 /#
self.debug_ai_move_to_point = undefined; #/
892 /#
self.debug_ai_movement_type =
"evasive - " + queryResult.data.size; #/
893 /#
self.debug_ai_move_to_point = best_point.origin; #/
894 return best_point.origin;
899 if(
self.goalforced )
905 selfDistToTarget = Distance2D(
self.origin, enemy.origin );
907 goodDist = 0.5 * (
self.settings.engagementDistMin +
self.settings.engagementDistMax );
910 closeDist = 1.2 * goodDist;
911 farDist = 3 * goodDist;
913 queryMultiplier = MapFloat( closeDist, farDist, 1, 3, selfDistToTarget );
914 preferedDirectness = 0;
915 if ( selfDistToTarget > goodDist )
917 preferedDirectness = MapFloat( closeDist, farDist, 0, 1, selfDistToTarget );
921 preferedDirectness = MapFloat( tooCloseDist * 0.4, tooCloseDist, -1, -0.6, selfDistToTarget );
924 preferedDistAwayFromOrigin = 300;
931 PositionQuery_Filter_Directness( queryResult,
self.origin, enemy.origin );
932 PositionQuery_Filter_DistanceToGoal( queryResult,
self );
934 PositionQuery_Filter_InClaimedLocation( queryResult,
self );
937 if ( isdefined(
self.avoidEntities ) && isdefined(
self.avoidEntitiesDistance ) )
943 best_point = undefined;
944 best_score = -999999;
946 foreach ( point
in queryResult.data )
949 diffToPreferedDirectness = abs( point.directness - preferedDirectness );
950 directnessScore = MapFloat( 0, 1, 100, 0, diffToPreferedDirectness );
951 if ( diffToPreferedDirectness > 0.2 )
953 directnessScore -= 200;
955 ADD_POINT_SCORE( point,
"directnessRaw", point.directness );
956 ADD_POINT_SCORE( point,
"directness", directnessScore );
959 ADD_POINT_SCORE( point,
"distToOrigin", MapFloat( 0, preferedDistAwayFromOrigin, 0, 100, point.distToOrigin2D ) );
963 if ( point.targetDist < tooCloseDist )
965 targetDistScore -= 200;
968 if( point.inClaimedLocation )
970 ADD_POINT_SCORE( point,
"inClaimedLocation", -500 );
973 ADD_POINT_SCORE( point,
"distToTarget", targetDistScore );
975 ADD_POINT_SCORE( point,
"random", randomFloatRange( 0, randomness ) );
977 if ( point.score > best_score )
979 best_score = point.score;
986 /#
self.debug_ai_move_to_points_considered = queryResult.data; #/
988 if( !isdefined( best_point ) )
990 /#
self.debug_ai_movement_type =
"tactical ( 0 / " + queryResult.data.size +
" )"; #/
991 /#
self.debug_ai_move_to_point = undefined; #/
996 if (
IS_TRUE( GetDvarInt(
"hkai_debugPositionQuery") ) )
998 recordLine(
self.origin, best_point.origin, (0.3,1,0) );
999 recordLine(
self.origin, enemy.origin, (1,0,0.4) );
1003 /#
self.debug_ai_movement_type =
"tactical - " + queryResult.data.size; #/
1004 /#
self.debug_ai_move_to_point = best_point.origin; #/
1006 return best_point.origin;
1011 self endon(
"death" );
1012 self endon(
"change_state" );
1013 self endon(
"near_goal" );
1014 self endon(
"reached_end_node" );
1015 self endon(
"amws_end_interrupt_watch" );
1019 if (
self.locked_on ||
self.locking_on )
1021 /#
self.debug_ai_move_to_points_considered = []; #/
1022 /#
self.debug_ai_movement_type =
"interrupted"; #/
1023 /#
self.debug_ai_move_to_point = undefined; #/
1025 self ClearVehGoalPos();
1027 self.lock_evade_now =
true;
1030 self notify(
"near_goal" );
1035 self endon(
"death" );
1036 self endon(
"change_state" );
1037 self endon(
"near_goal" );
1038 self endon(
"reached_end_node" );
1039 self endon(
"amws_end_interrupt_watch" );
1045 if( isdefined(
self.current_pathto_pos ) )
1047 if( distance2dSquared(
self.current_pathto_pos,
self.goalpos ) >
SQR(
self.goalradius ) )
1050 self notify(
"near_goal" );
1054 if ( isdefined(
self.enemy ) )
1058 self notify(
"near_goal" );
1064 self notify(
"near_goal" );
1074 if (
self.gibbed !==
true )
1078 self.death_type =
"suicide_crash";
1079 self kill(
self.origin + (0,0,10), attacker );
1083 function drone_callback_damage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal )
1085 iDamage =
vehicle_ai::shared_callback_damage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal );