4 #using scripts\codescripts\struct;
6 #using scripts\shared\gameskill_shared;
7 #using scripts\shared\math_shared;
8 #using scripts\shared\statemachine_shared;
9 #using scripts\shared\system_shared;
10 #using scripts\shared\array_shared;
11 #using scripts\shared\util_shared;
12 #using scripts\shared\clientfield_shared;
14 #using scripts\shared\ai_shared;
15 #using scripts\shared\vehicle_shared;
16 #using scripts\shared\vehicle_death_shared;
17 #using scripts\shared\vehicle_ai_shared;
19 #using scripts\shared\ai\systems\blackboard;
20 #using scripts\shared\ai\blackboard_vehicle;
22 #using scripts\shared\turret_shared;
23 #using scripts\shared\weapons\_spike_charge_siegebot;
25 #using scripts\mp\vehicles\_siegebot;
26 #using scripts\shared\callbacks_shared;
27 #using scripts\shared\laststand_shared;
29 #using scripts\shared\util_shared;
31 #insert scripts\shared\shared.gsh;
32 #insert scripts\shared\version.gsh;
33 #insert scripts\shared\statemachine.gsh;
34 #insert scripts\shared\archetype_shared\archetype_shared.gsh;
36 #insert scripts\shared\ai\utility.gsh;
38 #using scripts\mp\killstreaks\_killstreaks;
39 #using scripts\mp\killstreaks\_killstreak_bundles;
44 #define WEAPON_GUN_NORMAL "siegebot_gun_turret"
45 #define WEAPON_GUN_SPRAY "siegebot_gun_turret_spray"
46 #define WEAPON_JAVELIN "siegebot_javelin_turret"
48 #define JUMP_COOLDOWN 11
49 #define IGNORE_COOLDOWN 12
51 #define DEBUG_ON false
53 #define SIEGEBOT_THEIA_BUNDLE "siegebot_theia"
58 #namespace siegebot_theia;
62 #using_animtree( "generic" );
74 self useanimtree( #animtree );
79 self.health =
self.healthdefault;
83 self EnableAimAssist();
84 self SetNearGoalNotifyDist(
self.radius * 1.2 );
86 Target_Set(
self, ( 0, 0, 150 ) );
89 self.fovcosinebusy = 0;
90 self.maxsightdistsqrd =
SQR( 10000 );
92 assert( isdefined(
self.scriptbundlesettings ) );
96 self.goalRadius = 9999999;
97 self.goalHeight = 5000;
98 self SetGoal(
self.origin,
false,
self.goalRadius,
self.goalHeight );
106 if( !SessionModeIsMultiplayerGame() )
109 self SetGunnerTurretOnTargetRange( 0,
self.settings.gunner_turret_on_target_range );
115 self.damageLevel = 0;
116 self.newDamageLevel =
self.damageLevel;
121 if ( isdefined(
self.combat_goal_volume ) )
123 self SetGoal(
self.combat_goal_volume );
126 if ( !isdefined(
self.height ) )
128 self.height =
self.radius;
131 self.noCybercom =
true;
132 self.ignoreFireFly =
true;
133 self.ignoreDecoy =
true;
138 self.heatlh =
self.maxhealth;
218 self endon(
"death" );
219 self endon(
"nodeath_thread" );
221 self SetTurretSpinning(
false );
225 self SetTurretTargetRelativeAngles( (0,0,0) );
230 self playsound(
"veh_quadtank_sparks");
235 if ( isdefined(
self.jump ) )
237 self.jump.linkEnt Delete();
240 if ( isdefined(
self.fakeTargetEnt ) )
242 self.fakeTargetEnt Delete();
245 if ( isdefined(
self.spikeFakeTargets ) )
247 foreach( target
in self.spikeFakeTargets )
260 self._enablePain = enabled;
266 return isdefined( state ) && state !=
"pain" &&
self._enablePain;
281 self endon(
"death" );
283 if ( 1 <=
self.damagelevel &&
self.damagelevel <= 4 )
285 asmState =
"damage_" +
self.damageLevel +
"@pain";
289 asmState =
"normal@pain";
292 self ASMRequestSubstate( asmState );
309 prepare_death_threshold =
self.healthdefault * 0.1;
310 if (
self.health < prepare_death_threshold )
320 self endon (
"death" );
321 self endon(
"change_state" );
332 self SetVehGoalPos(
self.death_goal_point,
false,
true );
338 self ClearVehGoalPos();
341 self notify(
"end_attack_thread" );
342 self notify(
"end_movement_thread" );
343 self.jump.highground_history =
self.jump.highgrounds[0];
349 self.disable_side_step =
true;
369 if ( isdefined(
self.jump ) )
372 self.jump.linkEnt Delete();
376 self.jump = spawnstruct();
377 self.jump.linkEnt =
Spawn(
"script_origin",
self.origin );
378 self.jump.in_air =
false;
381 self.arena_center =
struct::get(
"arena_center" ).origin;
382 self.death_goal_point =
struct::get(
"death_goal_point" ).origin;
383 self.combat_goal_volume = GetEnt(
"theia_combat_region",
"targetname" );
385 foreach( point
in self.jump.highgrounds )
388 if ( DistanceSquared( point.origin, (-24566.2, 23972.5, -20000) ) <
SQR( 100 ) )
390 point.origin += (20, -20, -100);
393 else if ( DistanceSquared( point.origin, (-27291.2, 25825.6, -20072) ) <
SQR( 100 ) )
395 point.origin += (0, 35, 0);
399 assert(
self.jump.highgrounds.size > 0 );
400 assert(
self.jump.groundpoints.size > 0 );
401 assert( isdefined(
self.arena_center ) );
413 if ( isdefined( target ) )
415 self.jump.highground_history = target;
424 goal =
self.jump.highground_history.origin;
426 trace = PhysicsTrace( goal + ( 0, 0, 200 ), goal - ( 0, 0, 10000 ), ( -10, -10, -10 ), ( 10, 10, 10 ),
self,
PHYSICS_TRACE_MASK_VEHICLE );
429 /#debugstar( goal, 60000, (0,1,0) ); #/
430 /#debugstar(
trace[
"position" ], 60000, (0,1,0) ); #/
431 /#line(goal,
trace[
"position" ], (0,1,0), 1,
false, 60000 ); #/
433 if (
trace[
"fraction" ] < 1 )
435 goal =
trace[
"position" ];
438 self.jump.highground_history = goal;
439 self.jump.goal = goal;
441 params.scaleForward = 70;
442 params.gravityForce = (0, 0, -5);
443 params.upByHeight = 10;
444 params.landingState =
"land_turn@jump";
460 if ( isdefined( target ) )
462 self.jump.lowground_history = target;
471 goal =
self.jump.lowground_history;
473 trace = PhysicsTrace( goal + ( 0, 0, 500 ), goal - ( 0, 0, 10000 ), ( -10, -10, -10 ), ( 10, 10, 10 ),
self,
PHYSICS_TRACE_MASK_VEHICLE );
476 /#debugstar( goal, 60000, (0,1,0) ); #/
477 /#debugstar(
trace[
"position" ], 60000, (0,1,0) ); #/
478 /#line(goal,
trace[
"position" ], (0,1,0), 1,
false, 60000 ); #/
480 if (
trace[
"fraction" ] < 1 )
482 goal =
trace[
"position" ];
485 self.jump.lowground_history = goal;
486 self.jump.goal = goal;
488 params.scaleForward = 70;
489 params.gravityForce = (0, 0, -5);
490 params.upByHeight = -5;
491 params.landingState =
"land@jump";
507 if ( isdefined( target ) )
509 self.jump.lowground_history = target;
529 self endon(
"change_state" );
530 self endon(
"death" );
532 goal =
self.jump.goal;
536 self.jump.linkEnt.origin =
self.origin;
537 self.jump.linkEnt.angles =
self.angles;
541 self LinkTo(
self.jump.linkEnt );
543 self.jump.in_air =
true;
547 /#debugstar( goal, 60000, (0,1,0) ); #/
548 /#debugstar( goal + (0,0,100), 60000, (0,1,0) ); #/
549 /#line(goal, goal + (0,0,100), (0,1,0), 1,
false, 60000 ); #/
553 totalDistance = Distance2D(goal,
self.jump.linkEnt.origin);
554 forward =
FLAT_ORIGIN( ((goal -
self.jump.linkEnt.origin) / totalDistance) );
555 upByDistance = MapFloat( 500, 2000, 46, 52, totalDistance );
556 antiGravityByDistance = MapFloat( 500, 2000, 0, 0.5, totalDistance );
558 initVelocityUp = (0,0,1) * ( upByDistance + params.upByHeight );
559 initVelocityForward = forward * params.scaleForward * MapFloat( 500, 2000, 0.8, 1, totalDistance );
560 velocity = initVelocityUp + initVelocityForward;
563 self ASMRequestSubstate(
"inair@jump" );
564 self waittill(
"engine_startup" );
566 self waittill(
"leave_ground" );
569 jumpStart = GetTime();
572 distanceToGoal = Distance2D(
self.jump.linkEnt.origin, goal);
574 antiGravityScaleUp = MapFloat( 0, 0.5, 0.6, 0, abs( 0.5 - distanceToGoal / totalDistance ) );
575 antiGravityScale = MapFloat( (
self.radius * 1.0), (
self.radius * 3), 0, 1, distanceToGoal );
576 antiGravity = antiGravityScale * antiGravityScaleUp * (-params.gravityForce) + (0,0,antiGravityByDistance);
577 if (
DEBUG_ON ) /#line(
self.jump.linkEnt.origin,
self.jump.linkEnt.origin + antiGravity, (0,1,0), 1,
false, 60000 ); #/
579 velocityForwardScale = MapFloat( (
self.radius * 1), (
self.radius * 4), 0.2, 1, distanceToGoal );
580 velocityForward = initVelocityForward * velocityForwardScale;
581 if (
DEBUG_ON ) /#line(
self.jump.linkEnt.origin,
self.jump.linkEnt.origin + velocityForward, (0,1,0), 1,
false, 60000 ); #/
583 oldVerticleSpeed = velocity[2];
584 velocity = (0,0, velocity[2]);
585 velocity += velocityForward + params.gravityForce + antiGravity;
587 if ( oldVerticleSpeed > 0 && velocity[2] <= 0 )
589 self ASMRequestSubstate(
"fall@jump" );
592 if ( ( velocity[2] <= 0 &&
self.jump.linkEnt.origin[2] + velocity[2] <= goal[2] ) ||
vehicle_ai::TimeSince( jumpStart ) > 10 )
597 heightThreshold = goal[2] + 110;
598 oldHeight =
self.jump.linkEnt.origin[2];
599 self.jump.linkEnt.origin += velocity;
601 if (
self.jump.linkEnt.origin[2] < heightThreshold && ( oldHeight > heightThreshold || ( oldVerticleSpeed > 0 && velocity[2] < 0 ) ) )
603 self notify(
"start_landing" );
604 self ASMRequestSubstate( params.landingState );
607 if (
DEBUG_ON ) /#debugstar(
self.jump.linkEnt.origin, 60000, (1,0,0) ); #/
612 self.jump.linkEnt.origin =
FLAT_ORIGIN(
self.jump.linkEnt.origin ) + ( 0, 0, goal[2] );
613 self notify(
"land_crush" );
616 foreach( player
in level.players )
618 player._takedamage_old = player.takedamage;
619 player.takedamage =
false;
621 self RadiusDamage(
self.origin + ( 0,0,15 ),
self.radiusdamageradius,
self.radiusdamagemax,
self.radiusdamagemin,
self,
"MOD_EXPLOSIVE" );
623 foreach( player
in level.players )
625 player.takedamage = player._takedamage_old;
626 player._takedamage_old = undefined;
628 if ( Distance2DSquared(
self.origin, player.origin ) <
SQR( 200 ) )
630 direction =
FLAT_ORIGIN( ( player.origin -
self.origin ) );
631 if ( Abs( direction[0] ) < 0.01 && Abs( direction[1] ) < 0.01 )
633 direction = ( RandomFloatRange( 1, 2 ), RandomFloatRange( 1, 2 ), 0 );
635 direction = VectorNormalize( direction );
637 player SetVelocity( player GetVelocity() + direction * strength );
639 if ( player.health > 80 )
641 player DoDamage( player.health - 70,
self.origin,
self );
645 player DoDamage( 20,
self.origin,
self );
662 self.jump.in_air =
false;
664 self notify (
"jump_finished" );
681 self SetTurretTargetRelativeAngles( (0,0,0), 0 );
682 self SetTurretTargetRelativeAngles( (0,0,0), 1 );
683 self SetTurretTargetRelativeAngles( (0,0,0), 2 );
684 self SetTurretTargetRelativeAngles( (0,0,0), 3 );
685 self SetTurretTargetRelativeAngles( (0,0,0), 4 );
690 self endon(
"change_state" );
691 self endon(
"death" );
694 currentHighGround = undefined;
695 foreach( highGround
in self.jump.highgrounds )
697 if ( distance2DSquared( highGround.origin,
self.origin ) <
SQR(
self.radius * 6 ) )
699 currentHighGround = highGround;
704 if ( !isdefined( currentHighGround ) )
710 forward = anglesToForward( currentHighGround.angles );
714 while ( !isdefined(
self.enemy) )
721 javelinChance =
self.damageLevel * 0.15;
722 if ( randomFloat( 1.0 ) < javelinChance )
726 level notify(
"theia_finished_platform_attack" );
733 level notify(
"theia_finished_platform_attack" );
736 if ( RandomFloat( 1 ) > 0.4 &&
self.disable_side_step !==
true )
745 level notify(
"theia_finished_platform_attack" );
755 right_dir = AnglesToRight(
self.angles );
756 start =
self.origin + (0,0,10);
758 traceDir = right_dir;
759 jukeState =
"juke_r@movement";
760 oppositeJukeState =
"juke_l@movement";
764 traceDir = -traceDir;
765 jukeState =
"juke_l@movement";
766 oppositeJukeState =
"juke_r@movement";
769 trace = PhysicsTrace( start, start + traceDir * step_size, 0.8 * ( -
self.radius, -
self.radius, 0 ), 0.8 * (
self.radius,
self.radius,
self.height ),
self,
PHYSICS_TRACE_MASK_VEHICLE );
773 /#line(start, start + traceDir * step_size, (1,0,0), 1,
false, 100 ); #/
776 if (
trace[
"fraction"] < 1 )
778 traceDir = -traceDir;
779 trace = PhysicsTrace( start, start + traceDir * step_size, 0.8 * ( -
self.radius, -
self.radius, 0 ), 0.8 * (
self.radius,
self.radius,
self.height ),
self,
PHYSICS_TRACE_MASK_VEHICLE );
780 jukeState = oppositeJukeState;
783 /#line(start, start + traceDir * step_size, (1,0,0), 1,
false, 100 ); #/
787 if (
trace[
"fraction"] >= 1 )
789 self ASMRequestSubstate( jukeState );
801 self SetTurretTargetRelativeAngles( (0,0,0), 0 );
802 self SetTurretTargetRelativeAngles( (0,0,0), 1 );
803 self SetTurretTargetRelativeAngles( (0,0,0), 2 );
804 self SetTurretTargetRelativeAngles( (0,0,0), 3 );
805 self SetTurretTargetRelativeAngles( (0,0,0), 4 );
814 self endon(
"death" );
815 self endon(
"change_state" );
841 foreach( player
in level.players )
843 player._takedamage_old = player.takedamage;
844 player.takedamage =
false;
846 self RadiusDamage( origin + ( 0,0,10 ),
self.radius, 200, 200,
self,
"MOD_EXPLOSIVE" );
848 foreach( player
in level.players )
850 player.takedamage = player._takedamage_old;
851 player._takedamage_old = undefined;
853 if ( Distance2DSquared( origin, player.origin ) <
SQR(
self.radius ) )
855 player DoDamage( 15, origin,
self );
862 self endon(
"death" );
863 self endon(
"change_state" );
864 self notify(
"stop_left_footstep_damage" );
865 self endon(
"stop_left_footstep_damage" );
869 self waittill(
"footstep_left_large_theia" );
876 self endon(
"death" );
877 self endon(
"change_state" );
878 self notify(
"stop_right_footstep_damage" );
879 self endon(
"stop_right_footstep_damage" );
883 self waittill(
"footstep_right_large_theia" );
890 /# Record3DText(
"range: [" + distanceLimitMin +
"," + distanceLimitMax +
"]",
self.origin, (1,0.5,0),
"Script",
self ); #/
894 foreach( point
in pointsArray )
896 distanceToTarget = Distance2D( point.origin,
self.origin );
897 if ( distanceToTarget < distanceLimitMin || distanceLimitMax < distanceToTarget )
899 /# RecordStar( point.origin, (1,0.5,0) ); #/
900 /# Record3DText(
"out of range: " + distanceToTarget, point.origin, (1,0.5,0),
"Script",
self ); #/
904 score = Abs( distanceToTarget - idealDist );
907 score = randomFloat( 200 );
910 if ( isdefined(
self.jump.highground_history ) && Distance2DSquared( point.origin,
self.jump.highground_history ) <
SQR( 50 ) )
915 /# RecordStar( point.origin, (1,0.5,0) ); #/
916 /# Record3DText(
"dist: " + distanceToTarget +
" score: " + score, point.origin, (1,0.5,0),
"Script",
self ); #/
918 if ( score < bestScore )
935 self notify(
"end_attack_thread" );
936 self notify(
"end_movement_thread" );
937 self ClearTurretTarget();
938 self SetTurretSpinning(
false );
943 if ( isPlayer( player ) )
945 if ( player.usingvehicle && isdefined( player.viewlockedentity ) && isVehicle( player.viewlockedentity ) )
947 return player.viewlockedentity;
956 targets = level.players;
959 foreach ( player
in level.players )
962 if ( isdefined( vehicle ) )
968 targets = ArrayCombine( targets, vehicles,
false,
false );
974 index = player GetEntityNumber();
976 if ( !isdefined(
self.player_threat ) )
978 self.player_threat = [];
980 for( i = 0; i < 4; i++ )
982 self.player_threat[
self.player_threat.size] = SpawnStruct();
986 if ( !isdefined(
self.player_threat[index].
damage ) ||
987 !isdefined(
self.player_threat[index].tempBoost ) ||
988 !isdefined(
self.player_threat[index].tempBoostTimeout ) )
1001 foreach( player
in level.players )
1010 index = player GetEntityNumber();
1013 minDamage =
self.player_threat[index].damage;
1014 if ( !isdefined( minDamage ) )
1016 minDamage = 1000000;
1019 if (
self.player_threat.size > 0 )
1021 foreach( threat
in self.player_threat )
1023 if ( isdefined( threat.damage ) )
1025 minDamage = min( minDamage, threat.damage );
1034 self.player_threat[index].damage = minDamage;
1035 self.player_threat[index].tempBoost = 0;
1036 self.player_threat[index].tempBoostTimeout = 0;
1042 index = player GetEntityNumber();
1043 self.player_threat[index].damage +=
damage;
1049 index = player GetEntityNumber();
1051 if (
self.player_threat[index].tempBoostTimeout <= GetTime() )
1053 self.player_threat[index].tempBoost = 0;
1056 self.player_threat[index].tempBoost += boost;
1057 self.player_threat[index].tempBoostTimeout = GetTime() + timeSeconds * 1000;
1068 timeIgnoreOnSpawn = 7;
1069 currentTime = GetTime();
1070 if ( isdefined( player._spawn_time ) && ( player._spawn_time + timeIgnoreOnSpawn * 1000 > currentTime ) )
1075 index = player GetEntityNumber();
1077 if ( !isdefined(
self.player_threat ) || !isdefined(
self.player_threat[ index ] ) )
1082 threat =
self.player_threat[index].damage;
1084 if (
self.player_threat[index].tempBoostTimeout > GetTime() )
1086 threat +=
self.player_threat[index].tempBoost;
1089 if (
self.main_target === player )
1094 if(
self VehSeenRecently( player, 3 ) )
1099 if ( player.health < 50 )
1104 distanceSqr = Distance2DSquared(
self.origin, player.origin );
1105 if ( distanceSqr <
SQR( 800 ) )
1109 else if ( distanceSqr <
SQR( 1500 ) )
1120 best_threat = -1000000;
1121 self.main_target = undefined;
1122 foreach( player
in level.players )
1125 if ( isdefined( threat ) && threat > best_threat )
1127 best_threat = threat;
1128 self.main_target = player;
1135 if ( !isdefined( target ) )
1137 self SetTurretTargetRelativeAngles( (0,0,0), 3 );
1138 self SetTurretTargetRelativeAngles( (0,0,0), 4 );
1149 self endon(
"death" );
1150 point1 =
self.origin;
1151 point2 = target.origin;
1157 /#line(point1, point2, color, 1,
false, 3 ); #/
1165 self endon(
"death" );
1168 for( i = 0; i < 3 && i <
self.spikeFakeTargets.size; i++ )
1170 spike =
self.spikeFakeTargets[ i ];
1178 self endon(
"death" );
1179 self endon(
"change_state" );
1180 self endon(
"end_attack_thread" );
1181 self notify(
"end_attack_thread_gun" );
1182 self endon(
"end_attack_thread_gun" );
1187 if( !isdefined( enemy ) )
1189 self SetTurretTargetRelativeAngles( (0,0,0) );
1194 if( !enemy.allowdeath && !IsPlayer(enemy) )
1196 self SetPersonalThreatBias( enemy, -2000, 8.0 );
1201 distSq = DistanceSquared( enemy.origin,
self.origin );
1202 if (
self VehCanSee( enemy ) && ( IsPlayer( enemy ) || (
SQR( 200 ) < distSq && distSq <
SQR( 2000 ) ) ) )
1204 self SetPersonalThreatBias( enemy, 1000, 1.0 );
1208 self SetPersonalThreatBias( enemy, -1000, 1.0 );
1215 gun_on_target = GetTime();
1216 self SetTurretSpinning(
true );
1222 if( !isdefined( enemy ) )
1224 self SetTurretSpinning(
false );
1228 attack_start = GetTime();
1229 while ( isdefined( enemy ) && enemy ===
self.enemy &&
self VehSeenRecently( enemy, 1.0 ) &&
vehicle_ai::TimeSince( attack_start ) < 5 )
1233 if ( isdefined( enemy ) && isPlayer( enemy ) )
1235 wait( 0.6 + RandomFloat( 0.2 ) );
1240 self SetTurretSpinning(
false );
1248 self endon(
"death" );
1249 self endon(
"change_state" );
1250 self endon(
"end_attack_thread" );
1251 self notify(
"end_attack_thread_rocket" );
1252 self endon(
"end_attack_thread_rocket" );
1257 if( !isdefined( enemy ) )
1275 primaryEnemy = enemy;
1277 targets = GetAITeamArray(
"allies" );
1278 targets = ArrayCombine( targets, level.players,
false,
false );
1280 dirToPrimaryEnemy = VectorNormalize(
FLAT_ORIGIN( (primaryEnemy.origin -
self.origin) ) );
1282 bestCloseScore = 0.0;
1283 bestTarget = undefined;
1284 foreach( target
in targets )
1286 if ( target IsNoTarget() || target == primaryEnemy )
1291 dirToTarget = VectorNormalize(
FLAT_ORIGIN( (target.origin -
self.origin) ) );
1292 angleDot = VectorDot( dirToTarget, dirToPrimaryEnemy );
1293 if ( angleDot < 0.2 )
1298 distanceSelfToTargetSqr = Distance2DSquared( target.origin,
self.origin );
1299 if ( distanceSelfToTargetSqr <
SQR( 400 ) || distanceSelfToTargetSqr >
SQR( 1200 ) )
1306 closeTargetScore += 1 - angleDot;
1308 if ( isPlayer( target ) )
1310 closeTargetScore += 0.5;
1313 distancePrimaryEnemyToTargetSqr = Distance2DSquared( target.origin, primaryEnemy.origin );
1314 if ( distancePrimaryEnemyToTargetSqr <
SQR( 200 ) )
1316 closeTargetScore -= 0.3;
1319 if ( bestCloseScore <= closeTargetScore )
1321 bestCloseScore = closeTargetScore;
1322 bestTarget = target;
1328 if ( isAlive( enemy ) )
1335 turretOrigin =
self GetTagOrigin(
"tag_gunner_flash2" );
1336 distToEnemy = Distance2D(
self.origin, enemy.origin );
1337 shootHeight =
math::clamp( distToEnemy * 0.35, 100, 350 );
1338 points = GeneratePointsAroundCenter( enemy.origin + (0,0,shootHeight), 300, 80, 50 );
1339 pinDelay = Mapfloat( 300, 700, 0.1, 1.0, distToEnemy );
1342 spike =
self.spikeFakeTargets[ 0 ];
1343 spike.origin = points[ 0 ];
1344 self SetGunnerTargetEnt( spike, (0,0,0), 1 );
1346 rocket_on_target = GetTime();
1354 for( i = 0; i < 3 && i <
self.spikeFakeTargets.size && i < points.size; i++ )
1356 spike =
self.spikeFakeTargets[ i ];
1357 spike.origin = points[ i ];
1358 self SetGunnerTargetEnt( spike, (0,0,0), 1 );
1359 self FireWeapon( 2, enemy );
1364 /#debugstar( spike.origin, 200, (1,0,0) ); #/
1365 /#Circle( spike.origin, 150, (1,0,0),
false,
true, 200 ); #/
1372 self SetTurretTargetRelativeAngles( (0,0,0), 2 );
1384 self.rocketaim = is_aiming;
1390 if (
self.rocketaim ===
true )
1392 locomotion =
"locomotion@movement";
1396 locomotion =
"locomotion_rocketup@movement";
1399 self ASMRequestSubstate( locomotion );
1406 ai_array = GetAITeamArray(
"allies" );
1407 ai_array = array::randomize( ai_array );
1408 foreach( ai
in ai_array )
1410 awayFromPlayer =
true;
1411 foreach( player
in level.players )
1413 if (
is_valid_target( player ) && Distance2DSquared( ai.origin, player.origin ) <
SQR( minDist ) )
1415 awayFromPlayer =
false;
1420 if ( !awayFromPlayer )
1431 self endon(
"death" );
1432 self endon(
"change_state" );
1433 self notify(
"end_movement_thread" );
1434 self endon(
"end_movement_thread" );
1440 enemy =
self.main_target;
1447 foreach( player
in level.players )
1449 self SetPersonalThreatBias( player, -1000, 2.0 );
1454 if ( !isdefined(enemy) )
1460 if ( !isdefined(enemy) )
1467 self.current_enemy_pos = enemy.origin;
1469 self SetSpeed(
self.settings.defaultMoveSpeed );
1471 foundpath =
self SetVehGoalPos(
self.current_pathto_pos,
false,
true );
1474 self SetLookAtEnt( enemy );
1479 self notify(
"end_path_interrupt" );
1480 self CancelAIMove();
1481 self ClearVehGoalPos();
1491 self endon(
"death" );
1492 self endon(
"change_state" );
1493 self endon(
"end_movement_thread" );
1494 self notify(
"end_path_interrupt" );
1495 self endon(
"end_path_interrupt" );
1499 if ( isdefined(
self.current_enemy_pos ) && isdefined(
self.main_target ) )
1501 if ( Distance2DSquared(
self.current_enemy_pos,
self.main_target.origin ) >
SQR( 200 ) )
1503 self notify(
"near_goal" );
1512 if(
self.goalforced )
1514 return self.goalpos;
1519 queryOrigin =
self.origin;
1521 if ( isdefined( enemy ) &&
self CanPath(
self.origin, enemy.origin ) )
1523 queryOrigin = enemy.origin;
1526 queryResult = PositionQuery_Source_Navigation( queryOrigin, 0,
self.settings.engagementDistMax + 200, halfHeight, spacing,
self );
1528 if ( isdefined( enemy ) )
1530 PositionQuery_Filter_Sight( queryResult, enemy.origin,
self GetEye() -
self.origin,
self, 0, enemy );
1533 PositionQuery_Filter_DistanceToGoal( queryResult,
self );
1536 forward = AnglesToForward(
self.angles );
1537 if ( isdefined( enemy ) )
1539 enemyDir = VectorNormalize( enemy.origin -
self.origin );
1540 forward = VectorNormalize( forward + 5 * enemyDir );
1543 foreach ( point
in queryResult.data )
1545 if( Distance2DSquared(
self.origin, point.origin ) <
SQR( 300 ) )
1547 ADD_POINT_SCORE( point,
"tooCloseToSelf", -700 );
1550 if( isdefined( enemy ) )
1552 ADD_POINT_SCORE( point,
"engagementDist", -point.distAwayFromEngagementArea );
1554 if ( !point.visibility )
1556 ADD_POINT_SCORE( point,
"visibility", -600 );
1560 pointDirection = VectorNormalize( point.origin -
self.origin );
1561 factor = VectorDot( pointDirection, forward );
1564 ADD_POINT_SCORE( point,
"directionDiff", 600 );
1566 else if ( factor > 0 )
1568 ADD_POINT_SCORE( point,
"directionDiff", 0 );
1570 else if ( factor > -0.5 )
1572 ADD_POINT_SCORE( point,
"directionDiff", -600 );
1576 ADD_POINT_SCORE( point,
"directionDiff", -1200 );
1583 if( queryResult.data.size == 0 )
1586 return queryResult.data[0].origin;
1592 distanceSqrToLeft = distance2DSquared( left.origin, point );
1593 distanceSqrToRight = distance2DSquared( right.origin, point );
1594 return distanceSqrToLeft > distanceSqrToRight;
1599 foreach( highGround
in self.jump.highgrounds )
1601 if ( Distance2DSquared( point, highGround.origin ) <
SQR( minDistance ) )
1611 function get_jumpon_target( distanceLimitMin, distanceLimitMax, idealDist, includingAI, minAngleDiffCos, mustJump )
1613 targets = level.players;
1615 if ( includingAI ===
true )
1617 targets = ArrayCombine( targets, GetAITeamArray(
"allies" ),
false,
false );
1621 angles = ( 0,
self.angles[1], 0 );
1623 forward = AnglesToForward( angles );
1625 bestTarget = undefined;
1626 bestScore = 1000000;
1628 minDistAwayFromHighGround = 300;
1629 maxDistAwayFromArenaCenter = 1800;
1631 /# RecordStar(
self.origin, (1,0.5,0) ); #/
1632 /# Record3DText(
"JUMP TO GROUND",
self.origin, (1,0.5,0),
"Script",
self ); #/
1634 foreach( target
in targets )
1636 if ( !
is_valid_target( target ) || !target.allowdeath || IsAirBorne( target ) )
1641 if ( Distance2DSquared(
self.arena_center, target.origin ) >
SQR( maxDistAwayFromArenaCenter ) )
1643 /# RecordStar( target.origin, (0,0.5,1) ); #/
1644 /# Record3DText(
"too far from center: " + distance2d(
self.arena_center, target.origin ), target.origin, (0,0.5,1),
"Script",
self ); #/
1650 /# RecordStar( target.origin, (0,0.5,1) ); #/
1651 /# Record3DText(
"too close to platform", target.origin, (0,0.5,1),
"Script",
self ); #/
1655 distanceToTarget = Distance2D( target.origin,
self.origin );
1656 if ( distanceToTarget < distanceLimitMin || distanceLimitMax < distanceToTarget )
1658 /# RecordStar( target.origin, (1,0.5,0) ); #/
1659 /# Record3DText(
"out of range: " + distanceToTarget, target.origin, (1,0.5,0),
"Script",
self ); #/
1663 vectorToTarget =
FLAT_ORIGIN( ( target.origin -
self.origin ) );
1664 vectorToTarget = vectorToTarget / distanceToTarget;
1665 if ( isdefined( minAngleDiffCos ) && VectorDot( forward, vectorToTarget ) < minAngleDiffCos )
1670 score = Abs( distanceToTarget - idealDist );
1673 score = randomFloat( 200 );
1676 /# RecordStar( target.origin, (1,0.5,0) ); #/
1677 /# Record3DText(
"dist: " + distanceToTarget +
" score: " + score, target.origin, (1,0.5,0),
"Script",
self ); #/
1679 if ( isPlayer( target ) && !isVehicle( target ) )
1689 queryResult = PositionQuery_Source_Navigation( target.origin, minRadius, maxRadius, 500,
self.radius * 0.5,
self.radius * 1.1 );
1690 if ( queryResult.data.size > 0 )
1692 element = queryResult.data[0];
1693 if ( score < bestScore )
1696 bestTarget = element;
1701 if ( isdefined( bestTarget ) )
1703 return bestTarget.origin;
1706 if ( mustJump ===
false )
1712 queryResult = PositionQuery_Source_Navigation(
self.arena_center, 100, 1300, 500,
self.radius,
self.radius * 1.1 );
1714 assert ( queryResult.data.size > 0 );
1715 pointList = array::randomize( queryResult.data );
1716 foreach ( point
in pointList )
1718 distanceToTargetSqr = Distance2DSquared( point.origin,
self.origin );
1719 if (
SQR( distanceLimitMin ) < distanceToTargetSqr && distanceToTargetSqr <
SQR( distanceLimitMax ) && !
too_close_to_high_ground( point.origin, minDistAwayFromHighGround ) )
1721 return point.origin;
1725 return self.arena_center;
1730 self notify(
"end_movement_thread" );
1731 self notify(
"near_goal" );
1732 self CancelAIMove();
1733 self ClearVehGoalPos();
1734 self ClearTurretTarget();
1735 self ClearLookAtEnt();
1741 if ( !isdefined( targetAngleDiff ) )
1743 targetAngleDiff = 30;
1747 v_to_enemy = VectorNormalize( v_to_enemy );
1748 goalAngles = VectortoAngles( v_to_enemy );
1750 angleDiff = AbsAngleClamp180(
self.angles[1] - goalAngles[1] );
1751 if ( angleDiff <= targetAngleDiff )
1756 self SetLookAtOrigin( position );
1757 self SetTurretTargetVec( position );
1760 angleAdjustingStart = GetTime();
1763 if (
DEBUG_ON ) /#line(
self.origin, position, (1,0,1), 1,
false, 5 ); #/
1764 angleDiff = AbsAngleClamp180(
self.angles[1] - goalAngles[1] );
1768 self ClearVehGoalPos();
1769 self ClearLookAtEnt();
1770 self ClearTurretTarget();
1771 self CancelAIMove();
1774 function theia_callback_damage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal )
1777 if( !IsPlayer(eAttacker) )
1783 iDamage =
self killstreaks::OnDamagePerWeapon(
SIEGEBOT_THEIA_BUNDLE, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon,
self.maxhealth, undefined,
self.maxhealth * 0.4, undefined, 0, undefined,
true, 1.0 );
1788 if ( newDamageLevel >
self.damageLevel )
1790 self.newDamageLevel = newDamageLevel;
1793 if (
self.newDamageLevel >
self.damageLevel &&
pain_canenter() )
1795 self.damageLevel =
self.newDamageLevel;
1796 self notify(
"pain" );
1799 if (
self.damageLevel >= 2 )
1813 if( level.players.size < 1 )
1818 enemy = array::random( level.players );
1820 if ( !isdefined( enemy ) )
1826 forward = AnglesToForward(
self.angles );
1827 shootpos =
self.origin + forward * 200 + (0,0,500);
1831 self ASMRequestSubstate(
"javelin@stationary" );
1832 self waittill(
"fire_javelin" );
1833 level notify(
"theia_preparing_javelin_attack", enemy );
1835 current_weapon =
self SeatGetWeapon( 0 );
1838 self SetVehWeapon( weapon );
1840 self FireWeapon( 0, enemy );
1844 self SetVehWeapon( current_weapon );
1847 shootpos =
self.origin + forward * 500;
1848 self SetTurretTargetVec( shootpos );
1851 self ClearTurretTarget();
1853 if( isdefined( enemy ) && !
self VehCanSee( enemy ) )
1855 forward = AnglesToForward(
self.angles );
1857 aimpos =
self.origin + forward * 1000;
1858 self SetTurretTargetVec( aimpos );
1860 self ClearTurretTarget();
1867 self endon(
"entityshutdown" );
1868 self endon (
"death");
1870 self waittill(
"weapon_fired", projectile );
1873 alias =
"prj_javelin_incoming";
1877 if(!isdefined( projectile ) )
1880 while(isdefined(projectile) && isdefined( projectile.origin ))
1882 if ( isdefined(
self.enemy ) && isdefined(
self.enemy.origin ))
1884 projectileDistance = DistanceSquared( projectile.origin,
self.enemy.origin);
1886 if( projectileDistance <= distance * distance )
1888 projectile playsound (alias);
1904 if( !isdefined(
self.spikeFakeTargets ) ||
self.spikeFakeTargets.size < 1 )
1906 self.spikeFakeTargets = [];
1907 for ( i = 0; i < count; i++ )
1909 newFakeTarget =
Spawn(
"script_origin",
self.origin );
1910 ARRAY_ADD(
self.spikeFakeTargets, newFakeTarget );
1914 if( !isdefined(
self.fakeTargetEnt ) )
1916 self.fakeTargetEnt =
Spawn(
"script_origin",
self.origin );
1923 trace = BulletTrace(
self.origin,
self.origin + (0,0,-800),
false,
self );
1925 if(
trace[
"fraction"] < 1.0 )
1927 self.origin =
trace[
"position"] + (0,0,-20);
1931 self.origin =
self.origin + (0,0,-500);
1937 self endon(
"death");
1941 spikeTargets = array::randomize(
self.spikeFakeTargets );
1942 foreach ( target
in spikeTargets )
1945 wait randomFloatRange(0.05, 0.1);
1950 foreach ( spike
in spikeTargets )
1952 /#debugstar( spike.origin, 200, (1,0,0) ); #/
1953 /#Circle( spike.origin, 150, (1,0,0),
false,
true, 200 ); #/
1961 if ( target IsNoTarget() )
1965 else if ( !target.allowdeath )
1969 else if ( IsAirBorne( target ) )
1986 foreach ( otherTarget
in targetList )
1988 closeEnough = ( Distance2DSquared( target.origin, otherTarget.origin ) <
SQR( radius ) );
1991 closeTargetScore = closeTargetScore +
spike_score( otherTarget );
1995 return closeTargetScore;
2000 spikeCoverRadius = 600;
2005 forward = AnglesToForward(
self.angles );
2006 self SetTurretTargetVec(
self.origin + forward * 1000 );
2009 forward = AnglesToForward(
self.angles );
2011 targets = GetAITeamArray(
"allies" );
2012 targets = ArrayCombine( targets, level.players,
false,
false );
2014 bestCloseScore = 0.0;
2015 bestTarget = undefined;
2016 foreach( target
in targets )
2018 if ( target IsNoTarget() || IsAirBorne( target ) )
2023 distanceSelfToTargetSqr = Distance2DSquared( target.origin,
self.origin );
2024 if ( distanceSelfToTargetSqr <
SQR( 500 ) || distanceSelfToTargetSqr >
SQR( 2100 ) )
2029 dirToTarget =
FLAT_ORIGIN( (target.origin -
self.origin) );
2030 if ( VectorDot( dirToTarget, forward ) < 0.1 )
2037 if ( bestCloseScore <= closeTargetScore )
2039 bestCloseScore = closeTargetScore;
2040 bestTarget = target;
2044 if ( !isdefined( bestTarget ) )
2046 bestTarget = array::random( GeneratePointsAroundCenter(
self.arena_center, 2000, 200 ) );
2050 bestTarget = bestTarget.origin;
2055 /#debugstar( bestTarget, 200, (1,0,0) ); #/
2056 /#Circle( bestTarget, spikeCoverRadius, (1,0,0),
false,
true, 200 ); #/
2060 level notify(
"theia_preparing_spike_attack", bestTarget );
2062 targetOrigin =
FLAT_ORIGIN( bestTarget ) + (0,0,
self.origin[2]);
2063 targetPoints = GeneratePointsAroundCenter( targetOrigin, 1200, 120 );
2065 numOfSpikeAssigned = 0;
2066 for( i = 0; i <
self.spikeFakeTargets.size && i < targetPoints.size; i++ )
2068 spike =
self.spikeFakeTargets[ i ];
2069 spike.origin = targetPoints[i];
2070 numOfSpikeAssigned++;
2073 self ASMRequestSubstate(
"arm_rocket@stationary" );
2074 self waittill(
"fire_spikes" );
2076 for ( i = 0; i < numOfSpikeAssigned; i++ )
2078 spike =
self.spikeFakeTargets[ i ];
2079 self SetGunnerTargetEnt( spike, (0,0,0), 1 );
2080 self FireWeapon( 2 );
2086 self ClearGunnerTarget( 1 );
2087 self ClearTurretTarget();
2098 offset = (0, 0, 10);
2100 self.fakeTargetEnt Unlink();
2102 if ( DistanceSquared(
self.fakeTargetEnt.origin, enemy.origin ) >
SQR( 20 ) )
2104 self.fakeTargetEnt.origin = point;
2108 timeStart = GetTime();
2110 while( GetTime() < timeStart + timeToHit * 1000 )
2112 self.fakeTargetEnt.origin = LerpVector( point, enemy.origin + offset, ( GetTime() - timeStart ) / ( timeToHit * 1000 ) );
2113 if (
DEBUG_ON ) /#debugstar(
self.fakeTargetEnt.origin, 100, (0,1,0) ); #/
2118 self.fakeTargetEnt.origin = enemy.origin + offset;
2120 self.fakeTargetEnt LinkTo( enemy );
2125 if (
IS_TRUE( target.ignoreme ) || ( target.health <= 0 ) )
2133 else if ( IsSentient( target ) && ( target IsNoTarget() || !IsAlive( target ) ) )
2148 targets = GetAITeamArray(
"allies" );
2149 targets = ArrayCombine( targets, level.players,
false,
false );
2152 foreach( target
in targets )
2168 self.turretrotscale = 0.4;
2170 self ClearTurretTarget();
2171 self ClearGunnerTarget( 1 );
2172 self SetTurretTargetRelativeAngles( (0,0,0), 0 );
2173 self SetTurretTargetRelativeAngles( (0,0,0), 1 );
2174 self ASMRequestSubstate(
"sweep@gun" );
2175 self waittill(
"barrelspin_start" );
2177 self SetTurretSpinning(
true );
2179 self waittill(
"barrelspin_loop" );
2182 vectorFromEnemy = VectorNormalize(
FLAT_ORIGIN( (
self.origin - enemy.origin) ) );
2183 position = enemy.origin + vectorFromEnemy * 500;
2184 stopTime = GetTime() + duration * 1000;
2191 v_gunner_barrel1 =
self GetTagOrigin(
"tag_gunner_flash1" );
2192 v_bullet_trace_end = enemy.origin + ( 0, 0, 30 );
2193 trace = BulletTrace( v_gunner_barrel1, v_bullet_trace_end,
true, enemy );
2194 if(
trace[
"fraction"] == 1 )
2196 self GetPerfectInfo( enemy,
true );
2198 else if ( !IsPlayer(enemy) )
2200 self SetPersonalThreatBias( enemy, -2000, 3.0 );
2203 if( !enemy.allowdeath && !IsPlayer(enemy) )
2205 self SetPersonalThreatBias( enemy, -900, 8.0 );
2210 if ( IsPlayer( enemy ) )
2212 vectorFromEnemy = VectorNormalize(
FLAT_ORIGIN( (
self.origin - enemy.origin) ) );
2223 self SetTurretSpinning(
false );
2224 self notify(
"fire_stop" );
2228 self waittill(
"barrelspin_end" );
2231 self.turretrotscale = 1.0;