2 #using scripts\shared\math_shared;
3 #using scripts\shared\statemachine_shared;
4 #using scripts\shared\system_shared;
5 #using scripts\shared\array_shared;
6 #using scripts\shared\util_shared;
8 #using scripts\shared\vehicle_shared;
9 #using scripts\shared\vehicle_death_shared;
10 #using scripts\shared\vehicle_ai_shared;
11 #using scripts\shared\clientfield_shared;
13 #using scripts\shared\ai\systems\blackboard;
14 #using scripts\shared\ai\blackboard_vehicle;
15 #using scripts\shared\ai\systems\ai_interface;
16 #using scripts\shared\animation_shared;
17 #using scripts\codescripts\struct;
19 #insert scripts\shared\shared.gsh;
20 #insert scripts\shared\statemachine.gsh;
21 #insert scripts\shared\archetype_shared\archetype_shared.gsh;
22 #insert scripts\shared\ai\systems\blackboard.gsh;
24 #insert scripts\shared\ai\utility.gsh;
25 #insert scripts\shared\version.gsh;
26 #insert scripts\shared\vehicles\_sentinel_drone.gsh;
28 #using_animtree( "generic" );
30 #namespace sentinel_drone;
61 level._sentinel_Enemy_Detected_Taunts = [];
62 ARRAY_ADD(level._sentinel_Enemy_Detected_Taunts,
"vox_valk_valkyrie_detected_0");
63 ARRAY_ADD(level._sentinel_Enemy_Detected_Taunts,
"vox_valk_valkyrie_detected_1");
64 ARRAY_ADD(level._sentinel_Enemy_Detected_Taunts,
"vox_valk_valkyrie_detected_2");
65 ARRAY_ADD(level._sentinel_Enemy_Detected_Taunts,
"vox_valk_valkyrie_detected_3");
66 ARRAY_ADD(level._sentinel_Enemy_Detected_Taunts,
"vox_valk_valkyrie_detected_4");
68 level._sentinel_System_Critical_Taunts = [];
69 ARRAY_ADD(level._sentinel_System_Critical_Taunts,
"vox_valk_valkyrie_health_low_0");
70 ARRAY_ADD(level._sentinel_System_Critical_Taunts,
"vox_valk_valkyrie_health_low_1");
71 ARRAY_ADD(level._sentinel_System_Critical_Taunts,
"vox_valk_valkyrie_health_low_2");
72 ARRAY_ADD(level._sentinel_System_Critical_Taunts,
"vox_valk_valkyrie_health_low_3");
73 ARRAY_ADD(level._sentinel_System_Critical_Taunts,
"vox_valk_valkyrie_health_low_4");
82 self UseAnimTree( #animtree );
84 Target_Set(
self, ( 0, 0, 0 ) );
86 self.health =
self.healthdefault;
88 if( !isDefined( level.sentinelDroneMaxHealth ) )
90 level.sentinelDroneMaxHealth =
self.health;
93 self.maxHealth = level.sentinelDroneMaxHealth;
95 if( !isDefined( level.sentinelDroneHealthArmLeft ))
99 if( !isDefined( level.sentinelDroneHealthArmRight ))
103 if( !isDefined( level.sentinelDroneHealthArmTop ))
107 if( !isDefined( level.sentinelDroneHealthFace ))
111 if( !isDefined( level.sentinelDroneHealthCamera ))
115 if( !isDefined( level.sentinelDroneHealthCore ))
120 self.sentinelDroneHealthArms = [];
125 self.sentinelDroneHealthFace = level.sentinelDroneHealthFace;
126 self.sentinelDroneHealthCamera = level.sentinelDroneHealthCamera;
127 self.sentinelDroneHealthCore = level.sentinelDroneHealthCore;
134 if(!isdefined(level.sentinel_drone_target_id))
136 level.sentinel_drone_target_id = 0;
141 if(level.sentinel_drone_target_id == 0)
143 level.sentinel_drone_target_id = 1;
146 self.drone_target_id = level.sentinel_drone_target_id;
156 self EnableAimAssist();
159 self SetVehicleAvoidance(
true );
161 self SetDrawInfrared(
true );
163 self SetHoverParams( 0, 0, 10 );
167 self.fovcosinebusy = 0;
169 self.vehAirCraftCollisionEnabled =
true;
171 assert( isdefined(
self.scriptbundlesettings ) );
174 self.goalRadius = 999999;
175 self.goalHeight = 4000;
176 self SetGoal(
self.origin,
false,
self.goalRadius,
self.goalHeight );
178 self.nextJukeTime = 0;
179 self.nextRollTime = 0;
183 SetDvar(
"Sentinel_Move_Speed", 25);
184 SetDvar(
"Sentinel_Evade_Speed", 40);
186 self.should_buff_zombies =
false;
187 self.disable_flame_fx =
true;
188 self.no_widows_wine =
true;
190 self.targetPlayerTime = GetTime() + 1000 + RandomInt(1000);
194 self.pers[
"team"] =
self.team;
199 if( !isdefined(level.a_sentinel_drones))
201 level.a_sentinel_drones = [];
206 if ( isdefined( level.func_custom_sentinel_drone_cleanup_check ) )
208 self.func_custom_cleanup_check = level.func_custom_sentinel_drone_cleanup_check;
229 self endon(
"death" );
231 if(!isdefined(
self.target_initialized))
236 self.beam_fire_target
clientfield::set(
"sentinel_drone_beam_set_target_id",
self.drone_target_id);
239 self clientfield::set(
"sentinel_drone_beam_set_source_to_target",
self.drone_target_id);
243 self.target_initialized =
true;
264 if( !isdefined( target ) )
269 if( !IsAlive( target ) )
274 if( IsPlayer( target ) && target.sessionstate ==
"spectator" )
279 if( IsPlayer( target ) && target.sessionstate ==
"intermission" )
289 if( target IsNoTarget() )
294 if(
IS_TRUE(target.is_elemental_zombie) )
299 if( isdefined(level.is_valid_player_for_sentinel_drone) )
301 if( ![[level.is_valid_player_for_sentinel_drone]](target) )
307 if(
IS_TRUE(
self.should_buff_zombies) && IsPlayer(target) )
320 if (isdefined(
self.sentinel_GetNearestZombie))
322 ai_zombie = [[
self.sentinel_GetNearestZombie]](
self.origin, b_ignore_elemental, b_outside_playable_area, radius );
331 sentinel_drone_targets = GetPlayers();
332 least_hunted = sentinel_drone_targets[0];
334 search_distance_sq =
SQR(2000);
336 for( i = 0; i < sentinel_drone_targets.size; i++ )
338 if ( !isdefined( sentinel_drone_targets[i].hunted_by_sentinel ) )
340 sentinel_drone_targets[i].hunted_by_sentinel = 0;
350 least_hunted = sentinel_drone_targets[i];
355 dist_to_target_sq = Distance2DSquared(
self.origin, sentinel_drone_targets[i].origin);
356 dist_to_least_hunted_sq = Distance2DSquared(
self.origin, least_hunted.origin);
358 if( dist_to_least_hunted_sq >= search_distance_sq && dist_to_target_sq < search_distance_sq )
360 least_hunted = sentinel_drone_targets[i];
364 if( sentinel_drone_targets[i].hunted_by_sentinel < least_hunted.hunted_by_sentinel )
366 least_hunted = sentinel_drone_targets[i];
383 if( isdefined(
self.sentinel_droneEnemy ) )
385 if( !isdefined(
self.sentinel_droneEnemy.hunted_by_sentinel ) )
387 self.sentinel_droneEnemy.hunted_by_sentinel = 0;
390 if(
self.sentinel_droneEnemy.hunted_by_sentinel > 0 )
392 self.sentinel_droneEnemy.hunted_by_sentinel--;
398 self.sentinel_droneEnemy = undefined;
399 self ClearLookAtEnt();
400 self ClearTurretTarget(0);
404 self.sentinel_droneEnemy = enemy;
407 if(isdefined(
self.skip_first_taunt))
416 self.skip_first_taunt =
true;
419 if( !isdefined(
self.sentinel_droneEnemy.hunted_by_sentinel ) )
421 self.sentinel_droneEnemy.hunted_by_sentinel = 0;
423 self.sentinel_droneEnemy.hunted_by_sentinel++;
424 self SetLookAtEnt(
self.sentinel_droneEnemy );
425 self SetTurretTargetEnt(
self.sentinel_droneEnemy );
430 self endon(
"change_state" );
431 self endon(
"death" );
448 if(
IS_TRUE(
self.should_buff_zombies) )
452 if( !isdefined(target) )
475 self endon(
"change_state" );
476 self endon(
"death" );
478 self.lastTimeTargetInSight = 0;
479 self.nextJukeTime= 0;
483 if ( IsDefined(
self.owner ) && IsDefined(
self.owner.enemy ) )
485 self.sentinel_droneEnemy =
self.owner.enemy;
494 if(
IS_TRUE(
self.playing_intro_anim))
498 else if(
IS_TRUE(
self.is_charging_at_player) )
502 else if ( !isdefined(
self.forced_pos) &&
IS_TRUE(
self.shouldRoll ) )
509 else if ( !IsDefined(
self.sentinel_droneEnemy ) )
516 if(
self.arms_count > 0)
538 self.playing_intro_anim =
true;
539 self ASMRequestSubstate(
"intro@default" );
544 self.playing_intro_anim =
false;
555 self endon(
"change_state" );
556 self endon(
"death" );
558 roll_dir = AnglesToRight(
self.angles );
559 roll_dir = VectorNormalize( roll_dir );
561 juke_initial_pause = GetDvarFloat(
"sentinel_drone_juke_initial_pause_dvar", 0.2);
562 juke_speed = GetDvarInt(
"sentinel_drone_juke_speed_dvar", 300);
563 juke_offset = GetDvarInt(
"sentinel_drone_juke_offset_dvar", 20);
564 juke_distance = GetDvarInt(
"sentinel_drone_juke_distance_dvar", 100);
565 juke_distance_max = GetDvarInt(
"sentinel_drone_juke_distance_max_dvar", 250);
566 juke_min_anim_rate = GetDvarFloat(
"sentinel_drone_juke_min_anim_rate_dvar", 0.9);
572 roll_point =
self.origin + VectorScale( roll_dir, juke_distance_max);
573 roll_asm_state =
"dodge_right@attack";
577 roll_dir = VectorScale(roll_dir, -1);
578 roll_point =
self.origin - VectorScale( roll_dir, -juke_distance_max);
579 roll_asm_state =
"dodge_left@attack";
584 if (isdefined(
trace[
"position"]) )
586 if( !IsPointInNavVolume(
trace[
"position"],
"navvolume_small" ) )
588 trace[
"position"] =
self GetClosestPointOnNavVolume(
trace[
"position"], 100 );
591 if( isdefined(
trace[
"position"]) )
593 if(
trace[
"fraction"] == 1)
595 roll_distance = juke_distance_max - juke_offset;
599 roll_distance = juke_distance_max *
trace[
"fraction"] - juke_offset;
602 if(roll_distance >= juke_distance )
604 roll_anim_rate = juke_distance / roll_distance;
606 if(roll_anim_rate < juke_min_anim_rate)
608 roll_anim_rate = juke_min_anim_rate;
611 roll_speed = (roll_distance / juke_distance) * juke_speed;
617 self.shouldRoll =
false;
626 self ASMRequestSubstate( roll_asm_state );
627 self ASMSetAnimationRate( roll_anim_rate );
629 wait juke_initial_pause;
631 self SetSpeed(roll_speed);
632 self SetVehVelocity( VectorScale(roll_dir, roll_speed) );
633 self SetVehGoalPos(
trace[
"position"],
true,
false );
647 self ASMSetAnimationRate( 1 );
666 self endon(
"change_state" );
667 self endon(
"death" );
669 self notify(
"abort_navigation" );
670 self notify(
"near_goal" );
674 if(GetDvarInt(
"sentinel_NavigationStandStill_new", 0) > 0)
676 self ClearVehGoalPos();
677 self SetVehVelocity( ( 0, 0, 0 ) );
678 self.vehAirCraftCollisionEnabled =
true;
681 if( GetDvarInt(
"debug_sentinel_drone_traces") > 0 )
683 RecordSphere(
self.origin, 30,
ORANGE);
690 if(GetDvarInt(
"sentinel_ClearVehGoalPos", 1) == 1)
692 self ClearVehGoalPos();
695 if(GetDvarInt(
"sentinel_PathVariableOffsetClear", 1) == 1)
697 self PathVariableOffsetClear();
700 if(GetDvarInt(
"sentinel_PathFixedOffsetClear", 1) == 1)
702 self PathFixedOffsetClear();
705 if(GetDvarInt(
"sentinel_ClearSpeed", 1) == 1)
708 self SetVehVelocity( ( 0, 0, 0 ) );
709 self SetPhysAcceleration( ( 0, 0, 0 ) );
710 self SetAngularVelocity( ( 0, 0, 0 ) );
713 self.vehAirCraftCollisionEnabled =
true;
716 if( GetDvarInt(
"debug_sentinel_drone_traces") > 0 )
718 RecordSphere(
self.origin, 30,
ORANGE);
725 if( GetTime() >
self.nextJukeTime )
730 if( isdefined(
self.sentinel_droneEnemy) )
732 if(isdefined(
self.lastJukeTime))
734 if( (GetTime() -
self.lastJukeTime) > 3000 )
736 speed =
self GetSpeed();
754 self.nextJukeTime = 0;
759 self endon(
"change_state" );
760 self endon(
"death" );
761 self endon(
"abort_navigation" );
763 self notify(
"sentinel_NavigateTheWorld" );
765 self endon(
"sentinel_NavigateTheWorld" );
767 lastTimeChangePosition = 0;
768 self.shouldGotoNewPosition =
false;
769 self.last_failsafe_count = 0;
771 Sentinel_Move_Speed = GetDvarInt(
"Sentinel_Move_Speed", 25);
772 Sentinel_Evade_Speed = GetDvarInt(
"Sentinel_Evade_Speed", 40);
774 self SetSpeed( Sentinel_Move_Speed );
777 self ASMRequestSubstate(
"locomotion@movement" );
779 self.current_pathto_pos = undefined;
780 self.next_near_player_check = 0;
782 b_use_path_finding =
true;
786 current_pathto_pos = undefined;
787 b_in_tactical_position =
false;
789 if(
IS_TRUE(
self.playing_intro_anim))
793 else if(
self.goalforced )
796 returnData[
"origin"] =
self GetClosestPointOnNavVolume(
self.goalpos, 100 );
797 returnData[
"centerOnNav"] = IsPointInNavVolume(
self.origin,
"navvolume_small" );
798 current_pathto_pos = returnData[
"origin"];
800 else if( isdefined(
self.forced_pos) )
803 returnData[
"origin"] =
self GetClosestPointOnNavVolume(
self.forced_pos, 100 );
804 returnData[
"centerOnNav"] = IsPointInNavVolume(
self.origin,
"navvolume_small" );
805 current_pathto_pos = returnData[
"origin"];
811 self.evading_player =
false;
812 self SetSpeed( Sentinel_Evade_Speed );
816 self SetSpeed( Sentinel_Move_Speed );
820 current_pathto_pos = returnData[
"origin" ];
822 self.lastJukeTime = GetTime();
823 self.nextJukeTime = GetTime() + 1000 + RandomInt(4000);
825 b_in_tactical_position =
true;
829 self.evading_player =
true;
830 self.next_near_player_check = GetTime() + 1000;
831 self.nextJukeTime = 0;
832 self notify(
"near_goal" );
835 is_on_nav_volume = IsPointInNavVolume(
self.origin,
"navvolume_small");
838 if( GetDvarInt(
"sentinel_DebugFX_NoMove", 0) == 1 )
840 current_pathto_pos = undefined;
841 is_on_nav_volume =
true;
845 if ( isdefined( current_pathto_pos ) )
847 if( isdefined(
self.stuckTime ) &&
IS_TRUE( is_on_nav_volume ) )
849 self.stuckTime = undefined;
853 if( GetDvarInt(
"debug_sentinel_drone_traces") > 0 )
855 RecordSphere(current_pathto_pos, 8,
BLUE);
860 if( GetDvarInt(
"debug_sentinel_drone_paths") > 0 )
862 if(!IsPointInNavVolume(current_pathto_pos,
"navvolume_small") )
864 recordLine( current_pathto_pos, level.players[0].origin + (0, 0, 48),
WHITE );
865 RecordSphere(current_pathto_pos, 10,
WHITE);
866 PrintTopRightln(
"Target Fail ID: " +
self GetEntityNumber(),
WHITE);
871 recordLine(
self.origin, level.players[0].origin + (0, 0, 48),
GREEN );
872 RecordSphere(
self.origin, 10,
GREEN);
873 PrintTopRightln(
"Me Fail ID: " +
self GetEntityNumber(),
GREEN);
878 if (
self SetVehGoalPos( current_pathto_pos,
true, b_use_path_finding ) )
880 b_use_path_finding =
true;
882 self.b_in_tactical_position = b_in_tactical_position;
886 current_pathto_pos = undefined;
888 else if(
IS_TRUE(is_on_nav_volume) )
891 if( GetDvarInt(
"debug_sentinel_drone_paths") > 0 )
893 PrintTopRightln(
"FAILED TO FIND PATH ID: " +
self GetEntityNumber(),
RED);
895 recordLine( current_pathto_pos, level.players[0].origin + (0, 0, 48),
RED );
896 RecordSphere(current_pathto_pos, 10,
RED);
898 recordLine(
self.origin, level.players[0].origin + (0, 0, 48), (1, 0.2, 0.2) );
899 RecordSphere(
self.origin, 10,
RED);
904 self.last_failsafe_time = undefined;
910 if(!isdefined(
self.last_failsafe_time))
912 self.last_failsafe_time = GetTime();
915 if( (GetTime() -
self.last_failsafe_time) >= 3000)
917 self.last_failsafe_count = 0;
921 self.last_failsafe_count++;
924 self.last_failsafe_time = GetTime();
926 if(
self.last_failsafe_count > 25 )
928 new_sentinel_pos =
self GetClosestPointOnNavVolume(
self.origin, 120 );
930 if(isdefined(new_sentinel_pos))
932 dvar_sentinel_getback_to_volume_epsilon = GetDvarInt(
"dvar_sentinel_getback_to_volume_epsilon", 5);
933 if( Distance(
self.origin, new_sentinel_pos) < dvar_sentinel_getback_to_volume_epsilon )
935 self.origin = new_sentinel_pos;
938 if( GetDvarInt(
"debug_sentinel_drone_traces") > 0 )
940 RecordSphere(new_sentinel_pos, 8,
RED);
946 self.vehAirCraftCollisionEnabled =
false;
949 if( GetDvarInt(
"debug_sentinel_drone_traces") > 0 )
951 RecordSphere(new_sentinel_pos, 8,
RED);
955 if(
self SetVehGoalPos( new_sentinel_pos,
true,
false ) )
959 current_pathto_pos = undefined;
962 self.vehAirCraftCollisionEnabled =
true;
965 else if(
self.last_failsafe_count > 100 )
975 if( !isdefined(
self.stuckTime ) )
977 self.stuckTime = GetTime();
980 if( GetTime() -
self.stuckTime > 15000 )
992 self endon(
"change_state" );
993 self endon(
"death" );
996 if(isdefined(
self.sentinel_droneEnemy))
998 selfDistToTarget = Distance2D(
self.origin,
self.sentinel_droneEnemy.origin );
1002 selfDistToTarget = 0;
1007 closeDist = 1.2 * goodDist;
1008 farDist = 3 * goodDist;
1010 queryMultiplier = MapFloat( closeDist, farDist, 1, 3, selfDistToTarget );
1016 SENTINEL_DRONE_TOO_CLOSE_TO_SELF_DIST_EX = GetDvarInt(
"SENTINEL_DRONE_TOO_CLOSE_TO_SELF_DIST_EX", 70);
1017 SENTINEL_DRONE_MOVE_DIST_MAX_EX = GetDvarInt(
"SENTINEL_DRONE_MOVE_DIST_MAX_EX", 600);
1018 SENTINEL_DRONE_MOVE_SPACING = GetDvarInt(
"SENTINEL_DRONE_MOVE_SPACING", 25);
1019 SENTINEL_DRONE_RADIUS_EX = GetDvarInt(
"SENTINEL_DRONE_RADIUS_EX", 35);
1020 SENTINEL_DRONE_HIGHT_EX = GetDvarInt(
"SENTINEL_DRONE_HIGHT_EX",
int(preferedHeightRange));
1022 spacing_multiplier = 1.5;
1023 query_min_dist =
self.settings.engagementDistMin;
1024 query_max_dist = SENTINEL_DRONE_MOVE_DIST_MAX_EX;
1027 if(!
IS_TRUE(b_do_not_chase_enemy) && isdefined(
self.sentinel_droneEnemy) && (GetTime() >
self.targetPlayerTime) )
1029 charge_at_position =
self.sentinel_droneEnemy.origin + (0, 0, 48);
1031 if(!IsPointInNavVolume( charge_at_position,
"navvolume_small" ))
1033 closest_point_on_nav_volume = GetDvarInt(
"closest_point_on_nav_volume", 120);
1034 charge_at_position =
self GetClosestPointOnNavVolume( charge_at_position, closest_point_on_nav_volume );
1037 if(!isdefined(charge_at_position))
1039 queryResult = PositionQuery_Source_Navigation(
self.origin, SENTINEL_DRONE_TOO_CLOSE_TO_SELF_DIST_EX, SENTINEL_DRONE_MOVE_DIST_MAX_EX * queryMultiplier, SENTINEL_DRONE_HIGHT_EX * queryMultiplier, SENTINEL_DRONE_MOVE_SPACING,
"navvolume_small", SENTINEL_DRONE_MOVE_SPACING * spacing_multiplier );
1045 spacing_multiplier = 1;
1046 SENTINEL_DRONE_MOVE_SPACING = 15;
1047 query_min_dist =
self.settings.engagementDistMin * GetDvarFloat(
"sentinel_query_min_dist", 0.2);
1048 query_max_dist = query_max_dist * 0.5;
1052 spacing_multiplier = 1;
1053 SENTINEL_DRONE_MOVE_SPACING = 15;
1054 query_min_dist =
self.settings.engagementDistMin * GetDvarFloat(
"sentinel_query_min_dist", 0.5);
1057 queryResult = PositionQuery_Source_Navigation( charge_at_position, query_min_dist, query_max_dist * queryMultiplier, SENTINEL_DRONE_HIGHT_EX * queryMultiplier, SENTINEL_DRONE_MOVE_SPACING,
"navvolume_small", SENTINEL_DRONE_MOVE_SPACING * spacing_multiplier );
1062 queryResult = PositionQuery_Source_Navigation(
self.origin, SENTINEL_DRONE_TOO_CLOSE_TO_SELF_DIST_EX, SENTINEL_DRONE_MOVE_DIST_MAX_EX * queryMultiplier, SENTINEL_DRONE_HIGHT_EX * queryMultiplier, SENTINEL_DRONE_MOVE_SPACING,
"navvolume_small", SENTINEL_DRONE_MOVE_SPACING * spacing_multiplier );
1066 PositionQuery_Filter_DistanceToGoal( queryResult,
self );
1069 if(isdefined(
self.sentinel_droneEnemy))
1071 if( RandomInt(100) > 15)
1077 enemy_origin =
self.sentinel_droneEnemy.origin + (0, 0, 48);
1082 enemy_origin =
self.origin;
1086 best_point = undefined;
1087 best_score = undefined;
1089 foreach ( point
in queryResult.data )
1093 ADD_POINT_SCORE( point,
"insideEngagementDistance", 25 );
1096 ADD_POINT_SCORE( point,
"random", randomFloatRange( 0, randomness ) );
1098 if(isdefined(point.distAwayFromEngagementArea))
1100 ADD_POINT_SCORE( point,
"engagementDist", -point.distAwayFromEngagementArea );
1103 is_near_another_sentinel = Sentinel_IsNearAnotherSentinel( point.origin, 200 );
1104 <br/>ToDo
move to variable
1106 if(
IS_TRUE(is_near_another_sentinel))
1108 ADD_POINT_SCORE( point,
"NearAnotherSentinel", -200 );
1109 <br/>ToDo
move to variable
1112 is_overlap_another_sentinel = Sentinel_IsNearAnotherSentinel( point.origin, 100 );
1113 <br/>ToDo
move to variable
1115 if(
IS_TRUE(is_overlap_another_sentinel))
1117 ADD_POINT_SCORE( point,
"OverlapAnotherSentinel", -2000 );
1118 <br/>ToDo
move to variable
1122 <br/>ToDo
move to variable
1124 if(
IS_TRUE(is_near_another_player))
1126 ADD_POINT_SCORE( point,
"NearAnotherPlayer", -200 );
1127 <br/>ToDo
move to variable
1131 distFromPreferredHeight = abs( point.origin[2] - goalHeight );
1132 if ( distFromPreferredHeight > preferedHeightRange )
1134 heightScore = (distFromPreferredHeight - preferedHeightRange) * 3;
1135 ADD_POINT_SCORE( point,
"height", -heightScore );
1138 if(!isdefined(best_score))
1140 best_score = point.score;
1143 if( isdefined(
self.sentinel_droneEnemy) )
1145 best_point.visibile = int(BulletTracePassed(point.origin, enemy_origin,
false,
self,
self.sentinel_droneEnemy));
1149 best_point.visibile = int(BulletTracePassed(point.origin, enemy_origin,
false,
self));
1154 if ( point.score > best_score )
1156 if( isdefined(
self.sentinel_droneEnemy) )
1158 point.visibile = int(BulletTracePassed(point.origin, enemy_origin,
false,
self,
self.sentinel_droneEnemy));
1162 point.visibile = int(BulletTracePassed(point.origin, enemy_origin,
false,
self));
1165 if(point.visibile >= best_point.visibile)
1167 best_score = point.score;
1176 if( isdefined( best_point) )
1178 if( best_point.score < -1000)
1180 best_point = undefined;
1187 if (
IS_TRUE( GetDvarInt(
"hkai_debugPositionQuery") ) )
1189 if(isdefined(best_point) )
1191 recordLine(
self.origin, best_point.origin, (0.3,1,0) );
1194 if(isdefined(
self.sentinel_droneEnemy))
1196 recordLine(
self.origin,
self.sentinel_droneEnemy.origin, (1,0,0.4) );
1201 returnData[
"origin" ] = ( ( IsDefined( best_point ) ) ? best_point.origin : undefined );
1202 returnData[
"centerOnNav" ] = queryResult.centerOnNav;
1208 self endon(
"change_state" );
1209 self endon(
"death" );
1213 max_charge_time = GetTime() +
time_out;
1216 if( !isdefined(charge_at_position) )
1220 charge_at_position =
self.sentinel_droneEnemy.origin + (0, 0, 48);
1224 sentinel_dir = AnglesToForward(
self.angles);
1225 charge_at_position =
self.origin + sentinel_dir * Length(
self.sentinel_droneEnemy.origin -
self.origin);
1226 charge_at_position = ( charge_at_position[0], charge_at_position[1],
self.sentinel_droneEnemy.origin[2] );
1230 charge_at_dir = VectorNormalize(charge_at_position -
self.origin);
1233 self ClearLookAtEnt();
1234 self SetVehGoalPos( charge_at_position,
true,
false );
1235 self SetLookAtOrigin( charge_at_position );
1239 velocity =
self GetVelocity() * 0.1;
1240 velocityMag = Length(velocity);
1247 predicted_pos =
self.origin + velocity;
1253 if(
trace[
"fraction"] < 1)
1262 if( isdefined(max_charge_time) && (GetTime() > max_charge_time) )
1274 self endon(
"death" );
1275 self endon(
"change_state" );
1276 self endon(
"near_goal" );
1277 self endon(
"reached_end_node" );
1279 self notify(
"sentinel_PathUpdateInterrupt" );
1280 self endon(
"sentinel_PathUpdateInterrupt" );
1282 skip_sentinel_PathUpdateInterrupt = GetDvarInt(
"skip_sentinel_PathUpdateInterrupt", 1);
1284 if(skip_sentinel_PathUpdateInterrupt == 1)
1293 if( isdefined(
self.current_pathto_pos ) )
1295 if( distance2dSquared(
self.origin,
self.goalpos ) <
SQR(
self.goalradius ) )
1298 if( GetDvarInt(
"debug_sentinel_drone_traces") > 0 )
1300 RecordSphere(
self.origin, 30,
RED);
1305 self notify(
"near_goal" );
1315 self endon(
"death" );
1316 self endon(
"change_state" );
1322 self playrumbleonentity(
"damage_heavy");
1335 result.can_see_enemy =
false;
1336 enemy_moved =
false;
1337 b_still_enemy_in_pos_check =
false;
1339 origin_point = sentinel_origin;
1341 if(!isdefined(prev_enemy_position) )
1343 target_point =
self.sentinel_droneEnemy.origin + (0,0,48);
1345 if( IsPlayer(
self.sentinel_droneEnemy) )
1347 enemy_stance =
self.sentinel_droneEnemy GetStance();
1349 if( enemy_stance ==
"prone" )
1351 target_point =
self.sentinel_droneEnemy.origin + (0,0,2);
1357 b_still_enemy_in_pos_check =
true;
1358 target_point = prev_enemy_position;
1361 forward_vect = AnglesToForward(
self.angles );
1362 vect_to_enemy = target_point - origin_point;
1364 if( VectorDot(forward_vect, vect_to_enemy) <= 0 )
1366 if(!b_still_enemy_in_pos_check)
1379 right_vect = AnglesToRight(
self.angles );
1380 vect_to_enemy_2d = (vect_to_enemy[0], vect_to_enemy[1], 0);
1382 projected_distance = VectorDot(vect_to_enemy_2d, right_vect);
1384 if(abs(projected_distance) > 50)
1386 if(!b_still_enemy_in_pos_check)
1398 if(b_still_enemy_in_pos_check)
1400 beam_to_enemy_length = Distance(target_point, origin_point);
1401 beam_to_enemy_dir = target_point - origin_point;
1402 beam_to_enemy_dir = VectorNormalize(beam_to_enemy_dir);
1409 if( GetDvarInt(
"debug_sentinel_drone_traces") > 0 )
1411 recordLine( origin_point, target_point,
GREEN );
1412 RecordSphere(target_point, 8);
1430 if(
IS_TRUE(
self.playing_intro_anim))
1435 if(
self.arms_count <= 0)
1440 if(!
IS_TRUE(
self.target_initialized))
1446 if( isdefined(
self.sentinel_droneEnemy ) && (!isdefined(
self.nextFireTime) || (GetTime() >
self.nextFireTime) ) )
1450 !Sentinel_IsNearAnotherSentinel(
self.origin, 100 ) )
1456 self.nextFireTime = GetTime() + 2500 + RandomInt(2500);
1461 if(!isdefined(
self.sentinel_droneEnemy ))
1466 enemy_pos =
self.sentinel_droneEnemy.origin;
1468 if(RandomInt(100) < 70 )
1469 <br/>ToDo
move to variable
1471 b_succession =
true;
1475 self.beam_start_position =
self.origin;
1479 fire_state_name =
"fire_succession@attack";
1484 fire_state_name =
"fire@attack";
1488 self ASMRequestSubstate( fire_state_name );
1494 beam_dir =
result.hit_position -
self.origin;
1496 self.beam_fire_target.origin =
result.hit_position;
1497 self.beam_fire_target.angles = VectorToAngles( -beam_dir );
1500 if( GetDvarInt(
"debug_sentinel_drone_traces") > 0 )
1502 recordLine(
self.origin,
result.hit_position, (0.9, 0.7, 0.6) );
1503 RecordSphere(
result.hit_position, 8, (0.9, 0.7, 0.6));
1507 self clearlookatent();
1508 self.angles = VectorToAngles(beam_dir);
1510 self SetLookAtEnt(
self.beam_fire_target );
1511 self setTurretTargetEnt(
self.beam_fire_target );
1514 self waittill(
"fire_beam" );
1538 if( isdefined(
self.sentinel_droneEnemy) )
1540 self SetLookAtEnt(
self.sentinel_droneEnemy );
1541 self setTurretTargetEnt(
self.sentinel_droneEnemy );
1545 self ASMRequestSubstate(
"locomotion@movement" );
1549 if( RandomInt(100) < 40 )
1555 if(RandomInt(100) < 30)
1557 self.nextFireTime = GetTime() + 2500 + RandomInt(2500);
1572 self endon(
"change_state" );
1573 self endon(
"disconnect" );
1574 self endon(
"death" );
1575 self endon(
"death_state_activated");
1577 self.lastTimeFired = GetTime();
1580 beam_dir = target_position -
self.origin;
1582 self.beam_fire_target.origin = target_position;
1583 self.beam_fire_target.angles = VectorToAngles( -beam_dir );
1586 self.angles = VectorToAngles(beam_dir);
1588 self setTurretTargetEnt(
self.beam_fire_target );
1602 self.is_firing_beam =
true;
1613 self.is_firing_beam =
false;
1618 self endon(
"change_state" );
1619 self endon(
"disconnect" );
1620 self endon(
"death" );
1621 self endon(
"death_state_activated");
1623 for(i = 1; i <= 3; i++)
1625 if(
self.sentinelDroneHealthArms[i] <= 0 )
1641 start_beam_time = GetTime() + 2000;
1642 beam_damage_update = 0.1;
1646 while(GetTime() < start_beam_time ||
IS_TRUE(
self.sentinel_DebugFX_PlayAll))
1650 wait beam_damage_update;
1653 for(i = 1; i <= 3; i++)
1655 if(
self.sentinelDroneHealthArms[i] <= 0 )
1666 self endon(
"change_state" );
1667 self endon(
"disconnect" );
1668 self endon(
"death" );
1669 self endon(
"death_state_activated");
1679 arms_notifies[0] =
"attack_quick_left";
1680 arms_notifies[1] =
"attack_quick_right";
1681 arms_notifies[2] =
"attack_quick_top";
1683 for(i = 0; i < 3; i++)
1685 if(
self.sentinelDroneHealthArms[arms_order[i]] <= 0 )
1706 trace_entity =
trace[
"entity"];
1708 if( IsPlayer(trace_entity) )
1712 else if( isdefined(trace_entity) && isdefined(trace_entity.archetype) && trace_entity.archetype ==
ARCHETYPE_ZOMBIE)
1723 self endon(
"change_state" );
1724 self endon(
"disconnect" );
1725 self endon(
"death" );
1726 self endon(
"death_state_activated");
1730 Sentinel_KillMySelf();
1737 if(!isdefined(
self))
1742 self endon(
"change_state" );
1743 self endon(
"disconnect" );
1744 self endon(
"death" );
1745 self endon(
"death_state_activated");
1749 charge_at_position =
self.sentinel_droneEnemy.origin + (0, 0, 48);
1753 self.is_charging_at_player =
true;
1758 self ASMRequestSubstate(
"suicide_intro@death" );
1762 if(
self.sentinelDroneHealthCamera <= 0 )
1764 b_charge_at_player =
false;
1768 charge_at_position = undefined;
1769 b_charge_at_player =
true;
1772 self ASMRequestSubstate(
"suicide_charge@death" );
1774 self SetSpeed( 60 );
1775 <br/>ToDo:
move to GDT
1779 detonation_distance_sq = 100 * 100;
1780 <br/>ToDo:
move to GDT
1782 while(isdefined(
self) && isdefined(
self.sentinel_droneEnemy))
1784 distance_sq = DistanceSquared(
self.sentinel_droneEnemy.origin + (0, 0, 48),
self.origin);
1786 if( distance_sq <= detonation_distance_sq)
1788 sentinel_killmyself();
1801 if(!isdefined(part_name))
1806 return IsSubStr(part_name,
"tag_arm_left");
1811 if(!isdefined(part_name))
1816 return IsSubStr(part_name,
"tag_arm_right");
1821 if(!isdefined(part_name))
1826 return IsSubStr(part_name,
"tag_arm_top");
1831 if(!isdefined(part_name))
1849 if(!isdefined(part_name))
1854 if( part_name ==
"tag_camera_dead" ||
1855 part_name ==
"tag_flash" ||
1856 part_name ==
"tag_laser" ||
1857 part_name ==
"tag_turret")
1867 if(!isdefined(part_name))
1890 if(
self.arms_count == 0)
1900 if(
self.sentinelDroneHealthArms[arm] <= 0)
1905 self.sentinelDroneHealthArms[arm] =
self.sentinelDroneHealthArms[arm] -
damage;
1907 if(
self.sentinelDroneHealthArms[arm] <= 0 )
1911 if ( IsPlayer( eAttacker ) )
1913 if ( !isdefined(
self.e_arms_attacker ) &&
self.arms_count == 2 )
1915 self.e_arms_attacker = eAttacker;
1916 self.b_same_arms_attacker =
true;
1920 if (
self.e_arms_attacker !== eAttacker )
1922 self.b_same_arms_attacker =
false;
1945 if(
self.arms_count == 0 && !
IS_TRUE(
self.disable_charge_when_no_arms) )
1949 if ( IsPlayer( eAttacker ) )
1951 level notify(
"all_sentinel_arms_destroyed",
self.b_same_arms_attacker, eAttacker );
1959 self.disable_charge_when_no_arms =
IS_TRUE(b_disable_charge);
1992 if(
self.sentinelDroneHealthFace <= 0)
2002 self.sentinelDroneHealthFace =
self.sentinelDroneHealthFace -
damage;
2004 if(
self.sentinelDroneHealthFace <= 0 )
2019 if(
self.sentinelDroneHealthFace > 0)
2024 if(
self.sentinelDroneHealthCore <= 0)
2034 self.sentinelDroneHealthCore =
self.sentinelDroneHealthCore -
damage;
2036 if(
self.sentinelDroneHealthCore <= 0 )
2050 if(
self.sentinelDroneHealthCamera <= 0)
2060 self.sentinelDroneHealthCamera =
self.sentinelDroneHealthCamera -
damage;
2062 if(
self.sentinelDroneHealthCamera <= 0 )
2075 if ( IsPlayer( eAttacker ) )
2077 level notify(
"sentinel_camera_destroyed", eAttacker );
2082 function sentinel_CallbackDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal )
2096 if( isdefined(eAttacker) && isdefined(eAttacker.team) && isdefined(level.zombie_vars) && isdefined(level.zombie_vars[eAttacker.team]) &&
2097 IS_TRUE( level.zombie_vars[eAttacker.team][
"zombie_insta_kill"] ) )
2099 iDamage = iDamage * 4;
2100 <br/>ToDo
move to variable
2103 if(
self.sentinelDroneHealthFace <= 0 &&
IsCore(partName) )
2105 iDamage = iDamage * 2;
2106 <br/>ToDo
move to variable
2109 if(GetTime() >
self.nextRollTime)
2113 self.shouldRoll =
true;
2117 self.nextRollTime = GetTime() + RandomInt( 3000 );
2129 function sentinel_drone_CallbackRadiusDamage( eInflictor, eAttacker, iDamage, fInnerDamage, fOuterDamage, iDFlags, sMeansOfDeath, weapon, vPoint, fRadius, fConeAngleCos, vConeDir, psOffsetTime )
2142 if(GetTime() >
self.nextRollTime)
2146 self.shouldRoll =
true;
2150 self.nextRollTime = GetTime() + 3000 + RandomInt( 4000 );
2164 self endon(
"death" );
2169 self ASMRequestSubstate(
"normal@death" );
2187 players = GetPlayers();
2189 for( i = 0; i < players.size; i++ )
2196 min_distance_sq = min_distance * min_distance;
2198 distance_sq = DistanceSquared(
self.origin, players[i].origin + (0, 0, 48) );
2200 if(distance_sq < min_distance_sq)
2217 self endon(
"disconnect");
2218 self endon(
"death" );
2220 self.origin = explosion_origin;
2239 self.forced_pos = position;
2243 self.shouldRoll =
false;
2244 self.forced_pos = undefined;
2250 if(!isdefined(
self.v_compact_mode))
2252 v_compact_mode = GetEnt(
"sentinel_compact",
"targetname" );
2255 if(isdefined(v_compact_mode))
2257 if (
self.sentinel_droneEnemy IsTouching( v_compact_mode ) )
2268 if(!isdefined(
self.sentinel_droneEnemy))
2273 if(!isdefined(
self.v_narrow_volume))
2275 self.v_narrow_volume = GetEnt(
"sentinel_narrow_nav",
"targetname" );
2278 if(isdefined(
self.v_narrow_volume) && isdefined(
self.sentinel_droneEnemy) )
2280 if (
self.sentinel_droneEnemy IsTouching(
self.v_narrow_volume ) )
2294 self.in_compact_mode =
true;
2299 self.in_compact_mode =
false;
2306 self endon(
"disconnect");
2307 self endon(
"death" );
2320 self DoDamage(
self.health + 100,
self.origin );
2328 return self.settings.engagementDistMax * 0.3;
2330 else if(
IS_TRUE(
self.in_compact_mode))
2332 return self.settings.engagementDistMax * 0.85;
2335 return self.settings.engagementDistMax;
2342 return self.settings.engagementDistMin * 0.2;
2344 else if(
IS_TRUE(
self.in_compact_mode))
2346 return self.settings.engagementDistMin * 0.5;
2349 return self.settings.engagementDistMin;
2356 return self.settings.engagementHeightMax * 0.8;
2359 return self.settings.engagementHeightMax;
2364 if(!isdefined(
self.sentinel_droneEnemy))
2366 return self.settings.engagementHeightMin * 3;
2369 return self.settings.engagementHeightMin;
2380 if(
IS_TRUE(b_accept_negative_height) )
2396 if(
trace[
"fraction" ] < 1)
2402 trace = BulletTrace( start,
end, !
IS_TRUE(ignore_characters),
self,
false,
false,
self,
true );
2409 self endon(
"disconnect" );
2410 self endon(
"death" );
2414 self thread [[
self.sentinel_ElectrifyZombie]]( origin, zombie, radius );
2420 for(i = 1; i <= 3; i++)
2428 self notify(
"proximityGrenadeDamageStart" );
2429 self endon(
"proximityGrenadeDamageStart" );
2430 self endon(
"disconnect" );
2431 self endon(
"death" );
2432 eAttacker endon(
"disconnect" );
2434 self DoDamage(
damage, eAttacker.origin, eAttacker, eAttacker );
2438 self playrumbleonentity(
"damage_heavy");
2442 self playrumbleonentity(
"proximity_grenade");
2451 self shellshock(
"electrocution_sentinel_drone", 0.5 );
2455 self shellshock(
"electrocution_sentinel_drone", 1 );
2464 if(!isdefined(level.a_sentinel_drones))
2469 for(i = 0; i < level.a_sentinel_drones.size; i++)
2471 if( level.a_sentinel_drones[i] ==
self)
2473 level.a_sentinel_drones[i] = undefined;
2483 if(!isdefined(level.a_sentinel_drones))
2488 for(i = 0; i < level.a_sentinel_drones.size; i++)
2490 if(!isdefined(level.a_sentinel_drones[i]))
2495 if(level.a_sentinel_drones[i] ==
self)
2498 min_distance_sq = min_distance * min_distance;
2500 distance_sq = DistanceSquared( level.a_sentinel_drones[i].origin, point );
2502 if(distance_sq < min_distance_sq)
2513 players = GetPlayers();
2515 for( i = 0; i < players.size; i++ )
2523 min_distance_sq = min_distance * min_distance;
2525 distance_sq = DistanceSquared( origin, players[i].origin + (0, 0, 48) );
2527 if(distance_sq < min_distance_sq)
2538 if( isdefined(level._lastplayed_drone_taunt) && (GetTime() - level._lastplayed_drone_taunt) < 6000 )
2543 taunt = RandomInt(taunt_Arr.size);
2545 level._lastplayed_drone_taunt = GetTime();
2546 self PlaySound( taunt_Arr[taunt] );
2556 self endon(
"death");
2560 radius = GetDvarInt(
"drone_radius2", 35);
2561 Sphere(
self.origin, radius,
GREEN, 0.5);
2570 self endon(
"death");
2574 if(GetDvarInt(
"sentinel_DebugFX_PlayAll", 0) == 1)
2576 self.sentinel_DebugFX_PlayAll =
true;
2578 forward_vector = AnglesToForward(
self.angles);
2584 else if(
IS_TRUE(
self.sentinel_DebugFX_PlayAll))
2586 self.sentinel_DebugFX_PlayAll =
false;
2591 if(GetDvarInt(
"sentinel_DebugFX_BeamCharge", 0) == 1)
2593 self.sentinel_DebugFX_BeamCharge =
true;
2595 else if(
IS_TRUE(
self.sentinel_DebugFX_BeamCharge))
2597 self.sentinel_DebugFX_BeamCharge =
false;
2602 if(GetDvarInt(
"sentinel_DebugFX_NoArms", 0) == 1)
2604 if( !
IS_TRUE(
self.sentinel_DebugFX_NoArms) )
2606 self.sentinel_DebugFX_NoArms =
true;
2614 if(GetDvarInt(
"sentinel_DebugFX_NoFace", 0) == 1)
2616 if( !
IS_TRUE(
self.sentinel_DebugFX_NoFace) )
2618 self.sentinel_DebugFX_NoFace =
true;
2629 self endon(
"death" );
2631 while(isdefined(
self))
2633 if(GetDvarInt(
"sentinel_debug_buff_zombies", 0) == 1)
2635 self.debug_should_buff_zombies =
true;
2636 self.should_buff_zombies =
true;
2638 else if( isdefined(
self.debug_should_buff_zombies) )
2640 self.debug_should_buff_zombies = undefined;
2641 self.should_buff_zombies =
false;
2644 if(GetDvarInt(
"sentinel_debug_compact", 0) == 1)
2646 self.debug_sentinel_debug_compact =
true;
2649 else if( isdefined(
self.debug_sentinel_debug_compact) )
2651 self.debug_sentinel_debug_compact = undefined;