1 #using scripts\codescripts\struct;
3 #using scripts\shared\flag_shared;
4 #using scripts\shared\math_shared;
5 #using scripts\shared\array_shared;
6 #using scripts\shared\sound_shared;
7 #using scripts\shared\system_shared;
8 #using scripts\shared\util_shared;
9 #using scripts\shared\statemachine_shared;
10 #using scripts\shared\vehicle_shared;
11 #using scripts\shared\vehicle_death_shared;
12 #using scripts\shared\turret_shared;
13 #using scripts\shared\ai\systems\ai_interface;
15 #insert scripts\shared\shared.gsh;
16 #insert scripts\shared\statemachine.gsh;
17 #insert scripts\shared\archetype_shared\archetype_shared.gsh;
19 #define ANIMTREE "generic"
20 #define STATE_PRIORITY_DEATH 100
21 #define STATE_PRIORITY_INCREASE 50
22 #define STATE_PRIORITY_DECREASE 20
24 #namespace vehicle_ai;
30 #using_animtree( ANIMTREE );
62 aiarray = GetAIArray();
64 foreach( ai
in aiarray )
71 if (
self.ignoreFireFly ===
true && ai.firefly ===
true )
73 self SetPersonalIgnore( ai );
76 if (
self.ignoreDecoy ===
true && ai.isDecoy ===
true )
78 self SetPersonalIgnore( ai );
85 if ( !isdefined( entity ) )
89 if ( isplayer( entity ) && entity.usingvehicle && isdefined( entity.viewlockedentity ) && entity.viewlockedentity.archetype === archetype )
93 if ( isvehicle( entity ) && entity.archetype === archetype )
103 if( isdefined(
self.enemy ) &&
self VehCanSee(
self.enemy ) )
107 else if( isdefined(
self.enemylastseenpos ) )
109 return self.enemylastseenpos;
119 if ( isdefined( target ) )
121 if ( IsVec( target ) )
125 else if (
IS_TRUE( geteye ) && IsSentient( target ) )
127 pos = target GetEye();
129 else if ( IsEntity( target ) )
133 else if ( isdefined( target.origin ) && IsVec( target.origin ) )
146 if ( isdefined( target ) && IsSentient( target ) )
148 offset = target GetEye() - target.origin;
166 self endon(
"death" );
167 self endon(
"change_state" );
168 self notify(
"fire_stop" );
169 self endon(
"fire_stop" );
171 if ( !isdefined( turretIdx ) )
176 weapon =
self SeatGetWeapon( turretIdx );
178 assert( isdefined( weapon ) && weapon.name!=
"none" && weapon.fireTime>0 );
180 firetime = weapon.firetime * intervalScale;
182 fireCount = int( floor( totalFireTime / fireTime ) ) + 1;
199 self endon(
"death" );
200 self endon(
"fire_stop" );
201 self endon(
"change_state" );
203 if ( !isdefined( turretIdx ) )
208 weapon =
self SeatGetWeapon( turretIdx );
210 assert( isdefined( weapon ) && weapon.name!=
"none" && weapon.fireTime>0 );
217 self endon(
"death" );
218 self endon(
"fire_stop" );
219 self endon(
"change_state" );
221 if( isdefined( target ) && IsSentient( target ) )
223 target endon(
"death" );
226 assert( isdefined( turretIdx ) );
231 if ( ( isdefined( target ) && !IsPlayer( target ) && isAI( target ) ) || isdefined(
self.fire_half_blanks ) )
238 while ( counter < fireCount )
246 if ( isdefined( target ) && !isVec( target ) && isdefined( target.attackerAccuracy ) && target.attackerAccuracy == 0 )
250 else if ( aiFireChance > 1 )
252 self FireTurret( turretIdx, counter % aiFireChance );
266 if ( !isdefined(
self.owner ) )
269 dist_squared_to_owner = DistanceSquared(
self.owner.origin,
self.origin );
270 line_of_fire_dot = ( ( dist_squared_to_owner < 96 * 96 ) ? 0.8660 : 0.9848 );
271 gun_angles =
self GetTagAngles(
VAL(
self.avoid_shooting_owner_ref_tag ,
"tag_flash" ) );
272 gun_forward = AnglesToForward( gun_angles );
273 dot = VectorDot( gun_forward, VectorNormalize(
self.owner.origin -
self.origin ) );
274 return ( dot > line_of_fire_dot );
279 if ( isEntity( target ) )
281 if ( turretIdx == 0 )
283 self SetTurretTargetEnt( target, offset );
287 self SetGunnerTargetEnt( target, offset, turretIdx - 1 );
290 else if ( isVec( target ) )
292 origin = target + offset;
294 if ( turretIdx == 0 )
296 self SetTurretTargetVec( target );
300 self SetGunnerTargetVec( target, turretIdx - 1 );
305 AssertMsg(
"Turret target must be an entity or a vector." );
311 self FireWeapon( turretIdx, undefined, undefined,
self );
316 self endon(
"death" );
318 self waittill(
"weapon_fired", proj );
320 if( !isdefined( proj ) )
325 proj endon(
"death" );
329 while( isdefined( target ) )
331 if( proj GetVelocity()[2] < -150 && DistanceSquared( proj.origin, target.origin ) <
SQR(1200) )
333 proj Missile_SetTarget( undefined );
342 self endon(
"change_state" );
344 self util::waittill_any_ex( maxtime,
"near_goal",
"force_goal",
"reached_end_node",
"goal",
"pathfind_failed",
"change_state" );
349 self endon(
"change_state" );
352 succeeded = (
result ===
"pathfind_succeeded" );
358 self endon(
"death" );
359 self notify(
"end_asm_terminated_thread" );
360 self endon(
"end_asm_terminated_thread" );
361 self waittill(
"asm_terminated" );
362 self notify(
"asm_complete",
"__terminated__" );
367 self endon(
"death" );
368 self notify(
"end_asm_timeout_thread" );
369 self endon(
"end_asm_timeout_thread" );
371 self notify(
"asm_complete",
"__timeout__" );
376 self endon(
"death" );
381 substate = undefined;
382 while ( !isdefined( substate ) || ( substate != substate_to_wait && substate !=
"__terminated__" && substate !=
"__timeout__" ) )
384 self waittill(
"asm_complete", substate );
386 self notify(
"end_asm_terminated_thread" );
387 self notify(
"end_asm_timeout_thread" );
393 if ( damageType ==
"MOD_EXPLOSIVE" || damageType ==
"MOD_GRENADE_SPLASH" || damageType ==
"MOD_PROJECTILE_SPLASH" )
395 self SetVehVelocity(
self.velocity + VectorNormalize( hitDirection ) * 300 );
396 ang_vel =
self GetAngularVelocity();
397 ang_vel += ( RandomFloatRange( -300, 300 ), RandomFloatRange( -300, 300 ), RandomFloatRange( -300, 300 ) );
398 self SetAngularVelocity( ang_vel );
402 ang_vel =
self GetAngularVelocity();
403 yaw_vel = RandomFloatRange( -320, 320 );
406 ang_vel += ( RandomFloatRange( -150, 150 ), yaw_vel, RandomFloatRange( -150, 150 ) );
407 self SetAngularVelocity( ang_vel );
413 self endon(
"crash_done" );
414 self endon(
"death" );
418 self waittill(
"veh_predictedcollision", velocity, normal );
419 if ( normal[2] >= 0.6 )
421 self notify(
"veh_collision", velocity, normal );
429 tilted = ( normal[2] < 0.6 );
430 fx_origin =
self.origin - normal * ( tilted ? 28 : 10 );
432 self PlaySound(
"veh_wasp_wall_imp" );
439 self endon(
"crash_done" );
440 self endon(
"power_off_done" );
441 self endon(
"death" );
442 self notify(
"end_nudge_collision" );
443 self endon(
"end_nudge_collision" );
445 if (
self.notsolid ===
true )
452 self waittill(
"veh_collision", velocity, normal );
453 ang_vel =
self GetAngularVelocity() * 0.5;
454 self SetAngularVelocity( ang_vel );
459 if ( IsAlive(
self ) && ( normal[2] < 0.6 || !empedOrOff ) )
461 self SetVehVelocity(
self.velocity + normal * 90 );
464 else if ( empedOrOff )
466 if ( isdefined(
self.bounced ) )
468 self playsound(
"veh_wasp_wall_imp" );
469 self SetVehVelocity( ( 0, 0, 0 ) );
470 self SetAngularVelocity( ( 0, 0, 0 ) );
472 pitch =
self.angles[0];
474 self.angles = ( pitch,
self.angles[1],
self.angles[2] );
476 self.bounced = undefined;
477 self notify(
"landed" );
483 self SetVehVelocity(
self.velocity + normal * 30 );
489 impact_vel = abs( VectorDot( velocity, normal ) );
491 if( normal[2] < 0.6 && impact_vel < 100 )
493 self SetVehVelocity(
self.velocity + normal * 90 );
498 self playsound(
"veh_wasp_ground_death" );
500 self notify(
"crash_done" );
509 self endon(
"death" );
510 self endon(
"change_state" );
511 self endon(
"landed" );
515 velocity =
self.velocity;
516 self.angles = (
self.angles[0] * 0.85,
self.angles[1],
self.angles[2] * 0.85 );
517 ang_vel =
self GetAngularVelocity() * 0.85;
518 self SetAngularVelocity( ang_vel );
519 self SetVehVelocity( velocity + (0,0,-60) );
528 self endon(
"death" );
535 self endon(
"death" );
536 self notify(
"end_immolating_thread" );
537 self endon(
"end_immolating_thread" );
539 damagePerSecond =
self.settings.burn_damagepersecond;
540 if ( !isdefined( damagePerSecond ) || damagePerSecond <= 0 )
545 secondsPerOneDamage = 1.0 / Float( damagePerSecond );
547 if ( !isdefined(
self.abnormal_status ) )
549 self.abnormal_status = spawnStruct();
552 if (
self.abnormal_status.burning !==
true )
557 self.abnormal_status.burning =
true;
558 self.abnormal_status.attacker = attacker;
559 self.abnormal_status.inflictor = inflictor;
561 lastingTime =
self.settings.burn_lastingtime;
562 if ( !isdefined( lastingTime ) )
564 lastingTime = 999999;
568 interval = max( secondsPerOneDamage, 0.5 );
573 previousTime = GetTime();
577 self DoDamage( damageInt,
self.origin, attacker,
self,
"none",
"MOD_BURNED" );
581 self.abnormal_status.burning =
false;
594 self endon(
"death" );
597 self._iffoverride_oldTeam =
self.team;
600 if(isDefined(
self.iff_override_cb))
601 self [[
self.iff_override_cb]](
true);
603 if(isDefined(
self.settings) && !
IS_TRUE(
self.settings.iffshouldrevertteam ) )
608 timeout = (isDefined(
self.settings)?
self.settings.ifftimetillrevert:time);
612 if (msg ==
"timeout" )
614 self notify (
"iff_override_reverted");
617 self playsound (
"gdt_iff_deactivate");
620 if(isDefined(
self.iff_override_cb))
621 self [[
self.iff_override_cb]](
false);
627 self endon(
"death" );
629 old_ignoreme =
self.ignoreme;
630 self.ignoreme =
true;
638 self SetTeam( team );
645 self.ignoreme = old_ignoreme;
650 self endon(
"death" );
657 while ( GetTime() <
startTime + time * 1000 )
670 self notify(
"shut_off" );
675 self notify(
"start_up" );
699 self ClearTargetEntity();
700 self ClearGunnerTarget(0);
701 self ClearGunnerTarget(1);
702 self ClearGunnerTarget(2);
703 self ClearGunnerTarget(3);
704 self ClearLookAtEnt();
709 if( !IsAirborne(
self ) )
713 self ClearVehGoalPos();
714 self PathVariableOffsetClear();
715 self PathFixedOffsetClear();
717 if ( zeroOutSpeed ===
true )
719 self notify(
"landed" );
720 self SetVehVelocity( ( 0, 0, 0 ) );
721 self SetPhysAcceleration( ( 0, 0, 0 ) );
722 self SetAngularVelocity( ( 0, 0, 0 ) );
726 function shared_callback_damage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal )
728 if (
should_emp(
self, weapon, sMeansOfDeath, eInflictor, eAttacker ) )
730 minEmpDownTime = 0.8 *
self.settings.empdowntime;
731 maxEmpDownTime = 1.2 *
self.settings.empdowntime;
732 self notify (
"emped", RandomFloatRange( minEmpDownTime, maxEmpDownTime ), eAttacker, eInflictor );
735 if (
should_burn(
self, weapon, sMeansOfDeath, eInflictor, eAttacker ) )
740 if ( !isdefined(
self.damageLevel ) )
742 self.damageLevel = 0;
743 self.newDamageLevel =
self.damageLevel;
747 if ( newDamageLevel >
self.damageLevel )
749 self.newDamageLevel = newDamageLevel;
752 if (
self.newDamageLevel >
self.damageLevel )
754 self.damageLevel =
self.newDamageLevel;
755 if (
self.pain_when_damagelevel_change ===
true )
757 self notify(
"pain" );
765 function should_emp( vehicle, weapon, meansOfDeath, eInflictor, eAttacker )
767 if( !isdefined( vehicle ) || meansOfDeath ===
"MOD_IMPACT" || vehicle.disableElectroDamage ===
true )
772 if ( !( ( isdefined( weapon ) && weapon.isEmp ) || meansOfDeath ===
"MOD_ELECTROCUTED" ) )
777 causer = ( isdefined( eAttacker ) ? eAttacker : eInflictor );
778 if ( !isdefined( causer ) )
783 if ( IsAI( causer ) && IsVehicle( causer ) )
788 if ( level.teamBased )
790 return ( vehicle.team != causer.team );
794 if ( isdefined( vehicle.owner ) )
796 return ( vehicle.owner != causer );
799 return ( vehicle != causer );
803 function should_burn( vehicle, weapon, meansOfDeath, eInflictor, eAttacker )
805 if ( level.disableVehicleBurnDamage ===
true || vehicle.disableBurnDamage ===
true )
810 if( !isdefined( vehicle ) )
815 if ( meansOfDeath !==
"MOD_BURNED" )
820 if ( vehicle === eInflictor )
825 causer = ( isdefined( eAttacker ) ? eAttacker : eInflictor );
826 if ( !isdefined( causer ) )
831 if ( IsAI( causer ) && IsVehicle( causer ) )
836 if ( level.teamBased )
838 return ( vehicle.team != causer.team );
842 if ( isdefined( vehicle.owner ) )
844 return ( vehicle.owner != causer );
847 return ( vehicle != causer );
870 params = SpawnStruct();
871 params.isInitialState =
true;
874 if ( isdefined(
self.script_startstate ) )
876 self set_state(
self.script_startstate, params );
888 params = spawnStruct();
889 params.no_clear_movement = no_clear_movement;
891 self._no_death_state = disable_death_state;
896 if ( isAlive(
self ) &&
is_instate(
"scripted" ) )
898 if ( isdefined( statename ) )
912 self.current_role = rolename;
929 rolename =
"default";
930 if ( isdefined(
self.current_role ) )
932 rolename =
self.current_role;
935 if ( IsDefined(
self.state_machines[ rolename ] ) )
937 return self.state_machines[ rolename ].states[ statename ];
946 if ( !isdefined( rolename ) )
948 rolename =
"default";
951 if ( IsDefined(
self.state_machines[ rolename ] ) )
953 return self.state_machines[ rolename ].states[ statename ];
962 if ( IsDefined(
self.current_role ) && IsDefined(
self.state_machines[
self.current_role ].current_state ) )
964 return self.state_machines[
self.current_role ].current_state.name;
973 if ( IsDefined(
self.current_role ) && IsDefined(
self.state_machines[
self.current_role ].previous_state ) )
975 return self.state_machines[
self.current_role ].previous_state.name;
984 if ( IsDefined(
self.current_role ) && IsDefined(
self.state_machines[
self.current_role ].next_state ) )
986 return self.state_machines[
self.current_role ].next_state.name;
996 if ( IsDefined(
self.current_role ) && IsDefined(
self.state_machines[
self.current_role ].current_state ) )
998 return self.state_machines[
self.current_role ].current_state.name === statename;
1007 if ( IsDefined(
self.current_role ) )
1009 statemachine =
self.state_machines[
self.current_role ];
1011 if ( IsDefined( statemachine ) )
1036 if ( !isdefined( rolename ) )
1038 rolename =
"default";
1042 statemachine.isRole =
true;
1044 if ( !IsDefined(
self.current_role ) )
1137 return statemachine;
1144 if( !IsDefined( level.level_specific_add_state_callbacks ) )
1146 level.level_specific_add_state_callbacks = [];
1149 level.level_specific_add_state_callbacks[level.level_specific_add_state_callbacks.size] = func;
1154 if( IsDefined( level.level_specific_add_state_callbacks ) )
1156 for( i = 0; i < level.level_specific_add_state_callbacks.size; i++ )
1158 self [[ level.level_specific_add_state_callbacks[i] ]]();
1169 if (
IS_TRUE(
self._no_death_state ) )
1174 death_info = SpawnStruct();
1175 death_info.inflictor = eInflictor;
1176 death_info.attacker = eAttacker;
1177 death_info.damage = iDamage;
1178 death_info.meansOfDeath = sMeansOfDeath;
1179 death_info.weapon = weapon;
1180 death_info.dir = vDir;
1181 death_info.hitLoc = sHitLoc;
1182 death_info.timeOffset = psOffsetTime;
1189 state_machines =
self.state_machines;
1191 self waittill(
"free_vehicle");
1194 foreach(stateMachine
in state_machines)
1205 self DisableAimAssist();
1211 self CancelAIMove();
1214 self.takedamage = 0;
1220 if ( isdefined(
self.settings.burn_death_fx_1 ) && isdefined(
self.settings.burn_death_tag_1 ) )
1222 PlayFxOnTag(
self.settings.burn_death_fx_1,
self,
self.settings.burn_death_tag_1 );
1225 if ( isdefined(
self.settings.burn_death_sound_1 ) )
1227 self PlaySound(
self.settings.burn_death_sound_1 );
1233 if ( isdefined(
self.settings.emp_death_fx_1 ) && isdefined(
self.settings.emp_death_tag_1 ) )
1235 PlayFxOnTag(
self.settings.emp_death_fx_1,
self,
self.settings.emp_death_tag_1 );
1238 if ( isdefined(
self.settings.emp_death_sound_1 ) )
1240 self PlaySound(
self.settings.emp_death_sound_1 );
1246 self endon(
"death" );
1248 if ( !isdefined(
self ) ||
self.abandoned ===
true ||
self.damage_on_death ===
false ||
self.radiusdamageradius <= 0 )
1253 position =
self.origin + ( 0,0,15 );
1254 radius =
self.radiusdamageradius * radiusScale;
1255 damageMax =
self.radiusdamagemax;
1256 damageMin =
self.radiusdamagemin;
1260 if ( isdefined(
self ) )
1262 self RadiusDamage( position, radius, damageMax, damageMin, undefined, meansOfDamage );
1268 self endon(
"death" );
1270 self.skipFriendlyFireCheck =
true;
1279 self endon(
"death" );
1281 self.skipFriendlyFireCheck =
true;
1290 self endon(
"death" );
1300 self endon(
"death" );
1305 if( isdefined( level.disable_thermal ) )
1307 [[level.disable_thermal]]();
1310 waittime =
VAL(
self.waittime_before_delete, 0 );
1312 owner =
self GetVehicleOwner();
1313 if ( isDefined( owner ) &&
self isRemoteControl() )
1316 waittime = max( waittime, 4 );
1327 death_type =
"default";
1331 death_type =
self.death_type;
1334 if ( !isdefined( death_type ) )
1336 death_type = params.death_type;
1340 if( !isdefined( death_type ) && isdefined(
self.abnormal_status ) &&
self.abnormal_status.burning ===
true )
1342 death_type =
"burning";
1346 if ( !isdefined( death_type ) && ( isdefined(
self.abnormal_status ) &&
self.abnormal_status.emped ===
true ) ||
1347 ( isdefined( params.weapon ) && params.weapon.isEmp ) )
1349 death_type =
"emped";
1357 self endon(
"death" );
1359 if( IsDefined( level.vehicle_destructer_cb ) )
1361 [[level.vehicle_destructer_cb]]( self );
1373 switch( death_type )
1390 if ( params.no_clear_movement !==
true )
1394 if ( HasASM(
self ) )
1396 self ASMRequestSubstate(
"locomotion@movement" );
1406 if ( params.no_clear_movement !==
true )
1433 params.laserOn = IsLaserOn(
self );
1440 if( IsAirborne(
self ) )
1442 self SetRotorSpeed( 0 );
1445 if ( !isdefined(
self.abnormal_status ) )
1447 self.abnormal_status = spawnStruct();
1450 self.abnormal_status.emped =
true;
1451 self.abnormal_status.attacker = params.notify_param[1];
1452 self.abnormal_status.inflictor = params.notify_param[2];
1459 if ( isdefined(
self.settings.emp_startup_fx_1 ) && isdefined(
self.settings.emp_startup_tag_1 ) )
1461 PlayFxOnTag(
self.settings.emp_startup_fx_1,
self,
self.settings.emp_startup_tag_1 );
1467 self endon (
"death");
1468 self endon (
"change_state");
1470 time = params.notify_param[0];
1471 assert( isdefined( time ) );
1480 self.abnormal_status.emped =
false;
1494 if ( params.laserOn ===
true )
1499 if( IsAirborne(
self ) )
1501 self SetPhysAcceleration( ( 0, 0, 0 ) );
1503 self SetRotorSpeed( 1 );
1526 self endon(
"change_state" );
1527 self endon(
"death" );
1529 if ( !isdefined(
self.abnormal_status ) )
1531 self.abnormal_status = spawnStruct();
1534 self.abnormal_status.emped =
true;
1542 targets = GetAITeamArray(
"axis",
"team3" );
1543 ArrayRemoveValue( targets,
self );
1544 closest = ArrayGetClosest(
self.origin, targets );
1546 self SetSpeed(
self.settings.surgespeedmultiplier *
self.settings.defaultMoveSpeed );
1550 while( GetTime() -
startTime <
self.settings.surgetimetolive * 1000 )
1552 if ( !IsDefined( closest ) )
1554 self detonate( params.notify_param[0] );
1559 targetPos = closest.origin + ( 0, 0, 32 );
1561 if ( IsDefined( targetPos ) )
1563 queryResult = PositionQuery_Source_Navigation( targetPos, 0, 64, 35, 5,
self );
1565 foreach ( point
in queryResult.data )
1567 self.current_pathto_pos = point.origin;
1569 foundpath =
self SetVehGoalPos(
self.current_pathto_pos,
false,
true );
1589 if ( pathfailcount > 10 )
1591 self detonate( params.notify_param[0] );
1597 if( IsAlive(
self ) )
1599 self detonate( params.notify_param[0] );
1605 self endon(
"death" );
1606 self endon(
"change_state" );
1607 self endon(
"near_goal" );
1608 self endon(
"reached_end_node" );
1614 if( isdefined(
self.current_pathto_pos ) )
1616 if( distance2dSquared(
self.current_pathto_pos,
self.goalpos ) >
SQR(
self.goalradius ) )
1620 self notify(
"near_goal" );
1629 self endon(
"death" );
1630 self endon(
"change_state" );
1632 wait( 0.25 *
self.settings.surgetimetolive );
1633 self SetTeam( attacker.team );
1638 if ( IsDefined( closest ) && IsAlive( closest ) )
1640 if( distanceSquared( closest.origin,
self.origin ) <
SQR( 80 ) )
1652 self SetTeam( attacker.team );
1653 self RadiusDamage(
self.origin + ( 0, 0, 5 ),
self.settings.surgedamageradius, 1500, 1000, attacker,
"MOD_EXPLOSIVE" );
1654 if( IsAlive(
self ) )
1662 self endon(
"death" );
1663 self endon(
"change_state" );
1686 self DisableAimAssist();
1688 params.laserOn = IsLaserOn(
self );
1695 if( isdefined( level.disable_thermal ) )
1697 [[level.disable_thermal]]();
1700 if( IsAirborne(
self ) )
1702 if ( params.isInitialState !==
true && params.no_falling !==
true )
1704 self SetPhysAcceleration( ( 0, 0, -300 ) );
1707 self SetRotorSpeed( 0 );
1716 self EnableAimAssist();
1717 if( IsAirborne(
self ) )
1719 self SetPhysAcceleration( ( 0, 0, 0 ) );
1721 self SetRotorSpeed( 1 );
1723 if ( params.laserOn ===
true )
1728 if( isdefined( level.enable_thermal ) )
1732 [[level.enable_thermal]]();
1744 params.driver =
self GetSeatOccupant( 0 );
1745 assert ( isdefined(params.driver) );
1747 self DisableAimAssist();
1749 if ( level.playersDrivingVehiclesBecomeInvulnerable )
1751 params.driver EnableInvulnerability();
1752 params.driver.ignoreme =
true;
1755 self.turretRotScale = 1;
1756 self.team = params.driver.team;
1757 if ( HasASM(
self ) )
1759 self ASMRequestSubstate(
"locomotion@movement" );
1762 self SetHeliHeightCap(
true );
1766 self CancelAIMove();
1768 if( isdefined( params.driver ) && !isdefined(
self.customDamageMonitor ) )
1776 self EnableAimAssist();
1777 if( isdefined( params.driver ) )
1779 params.driver DisableInvulnerability();
1780 params.driver.ignoreme =
false;
1782 self.turretRotScale = 1;
1784 self SetHeliHeightCap(
false );
1789 if( isdefined( params.driver ) )
1813 #define POINTS_MAX_DIST 2000
1817 sightCheckOrigin = position + (0,0,sight_check_height);
1818 return sighttracepassed( sightCheckOrigin, enemy.origin + (0,0,30),
false,
self );
1823 if(
self.goalforced )
1825 goalpos = GetClosestPointOnNavMesh(
self.goalpos,
self.radius * 2,
self.radius );
1831 PixBeginEvent(
"vehicle_ai_shared::FindNewPosition" );
1832 queryResult = PositionQuery_Source_Navigation(
self.origin, 0,
POINTS_MAX_DIST, 300, point_spacing,
self, point_spacing * 2 );
1836 PositionQuery_filter_Random( queryResult, 0, 50 );
1837 PositionQuery_Filter_DistanceToGoal( queryResult,
self );
1840 origin =
self.goalpos;
1842 best_point = undefined;
1843 best_score = -999999;
1845 if ( isdefined(
self.enemy ) )
1847 PositionQuery_Filter_Sight( queryResult,
self.enemy.origin,
self GetEye() -
self.origin,
self, 0,
self.enemy );
1853 if( isdefined( side_turret_enemy ) && side_turret_enemy !=
self.enemy )
1855 PositionQuery_Filter_Sight( queryResult, side_turret_enemy.origin, (0,0,sight_check_height),
self, 20,
self,
"sight2" );
1862 if( isdefined( side_turret_enemy ) && side_turret_enemy !=
self.enemy )
1864 PositionQuery_Filter_Sight( queryResult, side_turret_enemy.origin, (0,0,sight_check_height),
self, 20,
self,
"sight3" );
1868 foreach ( point
in queryResult.data )
1870 ADD_POINT_SCORE( point,
"engagementDist", -point.distAwayFromEngagementArea );
1872 if( distance2dSquared(
self.origin, point.origin ) < 170 * 170 )
1874 ADD_POINT_SCORE( point,
"tooCloseToSelf", -170 );
1877 if( isdefined( point.sight ) && point.sight )
1879 ADD_POINT_SCORE( point,
"sight", 250 );
1881 if( isdefined( point.sight2 ) && point.sight2 )
1883 ADD_POINT_SCORE( point,
"sight2", 150 );
1885 if( isdefined( point.sight3 ) && point.sight3 )
1887 ADD_POINT_SCORE( point,
"sight3", 150 );
1890 if ( point.score > best_score )
1892 best_score = point.score;
1899 foreach ( point
in queryResult.data )
1901 if( distance2dSquared(
self.origin, point.origin ) < 170 * 170 )
1903 ADD_POINT_SCORE( point,
"tooCloseToSelf", -100 );
1906 if( point.score > best_score )
1908 best_score = point.score;
1916 if( isdefined( best_point ) )
1918 origin = best_point.origin;
1921 return origin + (0,0,10);
1932 return ( GetTime() - startTimeInMilliseconds ) * 0.001;
1937 if( !isdefined(
self._cooldown ) )
1939 self._cooldown = [];
1947 self._cooldown[
name ] = GetTime() + time_seconds * 1000;
1954 if ( !isdefined(
self._cooldown[
name ] ) )
1956 self._cooldown[
name ] = GetTime() - 1;
1958 return self._cooldown[
name ];
1972 if ( !isdefined( timeForward_seconds ) )
1974 timeForward_seconds = 0;
1977 cooldownReadyTime =
self._cooldown[
name ];
1978 return !isdefined( cooldownReadyTime ) || GetTime() + timeForward_seconds * 1000 > cooldownReadyTime;
1985 self._cooldown[
name ] = GetTime() - 1;
1997 if( isdefined(
self._cooldown ) )
1999 foreach ( str_name, cooldown
in self._cooldown )
2001 self._cooldown[ str_name ] = GetTime() - 1;
2012 if ( !
IS_TRUE( GetDvarInt(
"hkai_debugPositionQuery") ) )
2017 foreach( point
in queryResult.data )
2027 if ( !isdefined(
self._scoreDebug ) )
2032 if ( !
IS_TRUE( GetDvarInt(
"hkai_debugPositionQuery") ) )
2041 if (
self.score >= 0 )
2046 RecordStar(
self.origin, color );
2047 record3DText(
"" +
self.score +
":",
self.origin - (0,0,step * count), color );
2048 foreach(
name, score
in self._scoreDebug )
2051 record3DText(
name +
" " + score,
self.origin - (0,0,step * count), color );
2059 if ( !isdefined( left ) )
2063 else if ( !isdefined( right ) )
2068 return left < right;
2085 return _cmp_val( left.score, right.score, descending );
2090 foreach( point
in queryResult.data )
2092 score = RandomFloatRange( min, max );
2093 ADD_POINT_SCORE( point,
"random", score );
2099 sorted = array::merge_sort( queryResult.data, &
_sort_by_score, descending );
2101 queryResult.data = sorted;
2106 foreach( point
in queryResult.data )
2108 if ( point.distToGoal > tolerance )
2110 score = -10000 - point.distToGoal * 10;
2111 ADD_POINT_SCORE( point,
"outOfGoalAnchor", score );
2118 if( !isdefined( enemy ) )
2121 engagementDistance = ( engagementDistanceMin + engagementDistanceMax ) * 0.5;
2122 half_engagement_width = Abs( engagementDistanceMax - engagementDistance );
2126 vec_enemy_to_self = VectorNormalize(
FLAT_ORIGIN(
self.origin ) - enemy_origin );
2128 foreach( point
in queryResult.data )
2130 point.distAwayFromEngagementArea = 0;
2132 vec_enemy_to_point =
FLAT_ORIGIN( point.origin ) - enemy_origin;
2135 dist_in_front_of_enemy = VectorDot( vec_enemy_to_point, vec_enemy_to_self );
2137 if( abs(dist_in_front_of_enemy) < engagementDistanceMin )
2139 dist_in_front_of_enemy = -engagementDistanceMin;
2142 dist_away_from_sweet_line = Abs( dist_in_front_of_enemy - engagementDistance );
2144 if( dist_away_from_sweet_line > half_engagement_width )
2146 point.distAwayFromEngagementArea = dist_away_from_sweet_line - half_engagement_width;
2149 too_far_dist = engagementDistanceMax * 1.1;
2150 too_far_dist_sq =
SQR( too_far_dist );
2152 dist_from_enemy_sq = distance2dSquared( point.origin, enemy_origin );
2154 if( dist_from_enemy_sq > too_far_dist_sq )
2156 ratioSq = dist_from_enemy_sq / too_far_dist_sq;
2157 dist = ratioSq * too_far_dist;
2158 dist_outside = dist - too_far_dist;
2160 if( dist_outside > point.distAwayFromEngagementArea )
2162 point.distAwayFromEngagementArea = dist_outside;
2170 if ( !isdefined( targetArray ) || !isArray( targetArray ) )
2175 foreach( point
in queryResult.data )
2178 foreach( target
in targetArray )
2181 if ( IsVec( target ) )
2185 else if ( IsSentient( target ) && IsAlive( target ) )
2187 origin = target.origin;
2189 else if ( IsEntity( target ) )
2191 origin = target.origin;
2194 if ( isdefined( origin ) && distance2dSquared( point.origin, origin ) <
SQR( distance ) )
2203 ADD_POINT_SCORE( point,
"TooCloseToOthers", tooClosePenalty );
2210 if( !isdefined( enemy ) )
2215 engagementHeight = 0.5 * (
self.settings.engagementHeightMin +
self.settings.engagementHeightMax );
2216 half_height = Abs( engagementHeightMax - engagementHeight );
2218 targetHeight = enemy.origin[2] + engagementHeight;
2219 distFromEngagementHeight = Abs( origin[2] - targetHeight );
2221 if ( distFromEngagementHeight > half_height )
2223 result = distFromEngagementHeight - half_height;
2231 if( !isdefined( enemy ) )
2234 engagementHeight = 0.5 * ( engagementHeightMin + engagementHeightMax );
2235 half_height = Abs( engagementHeightMax - engagementHeight );
2237 foreach( point
in queryResult.data )
2239 point.distEngagementHeight = 0;
2241 targetHeight = enemy.origin[2] + engagementHeight;
2242 distFromEngagementHeight = Abs( point.origin[2] - targetHeight );
2244 if ( distFromEngagementHeight > half_height )
2246 point.distEngagementHeight = distFromEngagementHeight - half_height;
2253 for( i = 0; i < queryResult.data.size; i++ )
2255 point = queryResult.data[i];
2256 if ( point.distToGoal > tolerance )
2258 ArrayRemoveIndex( queryResult.data, i );
2276 assert( isdefined( client_flags ) );
2278 remaining_flags_to_process = client_flags;
2279 for ( i = 0; remaining_flags_to_process && i < level.players.size; i++ )
2281 attacker = level.players[ i ];
2282 if ( isdefined( attacker ) )
2284 client_flag = ( 1 << attacker getEntityNumber() );
2285 if ( client_flag & remaining_flags_to_process )
2287 self SetPersonalThreatBias( attacker, Int( threat_bias ), bias_duration );
2289 if ( get_perfect_info )
2290 self GetPerfectInfo( attacker, update_last_seen );
2292 remaining_flags_to_process &= ~client_flag;
2301 foreach( player
in level.players )
2305 self SetPersonalThreatBias( player, Int( threat_bias ), bias_duration );
2314 self endon(
"death" );
2318 self waittill(
"ccom_lock_being_targeted", hijackingPlayer );
2320 self GetPerfectInfo( hijackingPlayer,
true );
2322 if( isPlayer( hijackingPlayer ) )
2324 self SetPersonalThreatBias( hijackingPlayer, 1500, 4.0 );