2023-04-13 17:30:38 +02:00

1089 lines
38 KiB
Plaintext

#using scripts\codescripts\struct;
#using scripts\shared\math_shared;
#using scripts\shared\statemachine_shared;
#using scripts\shared\system_shared;
#using scripts\shared\array_shared;
#using scripts\shared\util_shared;
#using scripts\shared\weapons\_heatseekingmissile;
#using scripts\shared\vehicle_shared;
#using scripts\shared\vehicle_death_shared;
#using scripts\shared\vehicle_ai_shared;
#using scripts\shared\ai\systems\blackboard;
#using scripts\shared\ai\blackboard_vehicle;
#using scripts\shared\turret_shared;
#using_animtree( "generic" );
#namespace amws;
function autoexec __init__sytem__() { system::register("amws",&__init__,undefined,undefined); }
function __init__()
{
vehicle::add_main_callback( "amws", &amws_initialize );
}
function amws_initialize()
{
self UseAnimTree( #animtree );
Target_Set( self, ( 0, 0, 0 ) );
blackboard::CreateBlackBoardForEntity( self );
self Blackboard::RegisterVehicleBlackBoardAttributes();
self.health = self.healthdefault;
self vehicle::friendly_fire_shield();
self EnableAimAssist();
self SetNearGoalNotifyDist( 40 );
self.fovcosine = 0; // +/-90 degrees = 180
self.fovcosinebusy = 0.574; //+/- 55 degrees = 110 fov
self.vehAirCraftCollisionEnabled = true;
assert( isdefined( self.scriptbundlesettings ) );
self.settings = struct::get_script_bundle( "vehiclecustomsettings", self.scriptbundlesettings );
self.goalRadius = 999999;
self.goalHeight = 512;
self SetGoal( self.origin, false, self.goalRadius, self.goalHeight );
//self.waittime_before_delete = 40;
self.delete_on_death = false;
self.overrideVehicleDamage = &drone_callback_damage;
self thread vehicle_ai::nudge_collision();
self.cobra = false;
self ASMRequestSubstate( "locomotion@movement" );
self.variant = "light_weight";
if( IsSubStr( self.vehicleType, "pamws" ) )
{
self.variant = "armored";
}
self vehicle_ai::Cooldown( "cobra_up", 10 );
//disable some cybercom abilities
if( IsDefined( level.vehicle_initializer_cb ) )
{
[[level.vehicle_initializer_cb]]( self );
}
defaultRole();
}
function defaultRole()
{
self vehicle_ai::init_state_machine_for_role( "default" );
self vehicle_ai::get_state_callbacks( "combat" ).enter_func = &state_combat_enter;
self vehicle_ai::get_state_callbacks( "combat" ).update_func = &state_combat_update;
self vehicle_ai::get_state_callbacks( "driving" ).update_func = &state_driving_update;
self vehicle_ai::get_state_callbacks( "emped" ).update_func = &state_emped_update;
self vehicle_ai::get_state_callbacks( "surge" ).update_func = &state_surge_update;
self vehicle_ai::get_state_callbacks( "surge" ).exit_func = &state_surge_exit;
self vehicle_ai::get_state_callbacks( "death" ).update_func = &state_death_update;
self vehicle_ai::add_state( "stationary",
&state_stationary_enter,
&state_stationary_update,
&state_stationary_exit );
vehicle_ai::add_interrupt_connection( "stationary", "scripted", "enter_scripted" );
vehicle_ai::add_interrupt_connection( "stationary", "emped", "emped" );
vehicle_ai::add_interrupt_connection( "stationary", "off", "shut_off" );
vehicle_ai::add_interrupt_connection( "stationary", "driving", "enter_vehicle" );
vehicle_ai::add_interrupt_connection( "stationary", "pain", "pain" );
vehicle_ai::add_interrupt_connection( "stationary", "surge", "surge" );
vehicle_ai::add_utility_connection( "stationary", "combat" );
vehicle_ai::add_utility_connection( "combat", "stationary" );
self vehicle_ai::StartInitialState( "combat" );
}
// ----------------------------------------------
// State: death
// ----------------------------------------------
function state_death_update( params )
{
self endon( "death" );
death_type = vehicle_ai::get_death_type( params );
if ( !isdefined( death_type ) )
{
params.death_type = "gibbed";
death_type = params.death_type;
}
if ( death_type === "suicide_crash" )
{
self death_suicide_crash( params );
}
self vehicle_ai::defaultstate_death_update( params );
}
function death_suicide_crash( params )
{
self endon( "death" );
goaldir = AnglesToForward( self.angles );
goalDist = RandomFloatRange( 300, 400 );
goalpos = self.origin + goaldir * goalDist;
self SetMaxSpeedScale( 50 * 17.6 / ( self GetMaxSpeed( true ) ) );
self SetMaxAccelerationScale( 50 / self GetDefaultAcceleration() );
self SetSpeed( self.settings.surgespeedmultiplier * self.settings.defaultMoveSpeed );
self SetVehGoalPos( goalpos, false );
self util::waittill_any_timeout( 3.5, "near_goal", "veh_collision" );
self SetMaxSpeedScale( 0.1 );
self SetSpeed( 0.1 );
self vehicle_ai::ClearAllMovement();
self vehicle_ai::ClearAllLookingAndTargeting();
self.death_type = "gibbed";
}
// ----------------------------------------------
// State: driving
// ----------------------------------------------
function state_driving_update( params )
{
self endon( "change_state" );
self endon( "death" );
driver = self GetSeatOccupant( 0 );
if ( isPlayer( driver ) )
{
while ( true )
{
driver endon( "disconnect" );
driver util::waittill_vehicle_move_up_button_pressed();
if ( self.cobra === false )
{
self cobra_raise();
}
else
{
self cobra_retract();
}
}
}
}
// rise up and turn into a turret
function cobra_raise()
{
self.cobra = true;
if ( isdefined( self.settings.cobra_fx_1 ) && isdefined( self.settings.cobra_tag_1 ) )
{
PlayFxOnTag( self.settings.cobra_fx_1, self, self.settings.cobra_tag_1 );
}
self ASMRequestSubstate( "cobra@stationary" );
self vehicle_ai::waittill_asm_complete( "cobra@stationary", 4 );
self laserOn();
}
// retract turret and be mobile again
function cobra_retract()
{
self.cobra = false;
self laserOff();
self notify( "disable_lens_flare" );
self ASMRequestSubstate( "locomotion@movement" );
self vehicle_ai::waittill_asm_complete( "locomotion@movement", 4 );
}
// ----------------------------------------------
// State: emped
// ----------------------------------------------
function state_emped_update( params )
{
self endon ("death");
self endon ("change_state");
angles = self GetTagAngles( "tag_turret" );
self SetTurretTargetRelativeAngles( ( 45, angles[1] - self.angles[1], 0 ), 0 );
angles = self GetTagAngles( "tag_gunner_turret1" );
self SetTurretTargetRelativeAngles( ( 45, angles[1] - self.angles[1], 0 ), 1 );
self vehicle_ai::defaultstate_emped_update( params );
}
// ----------------------------------------------
// State: surge
// ----------------------------------------------
function state_surge_update( params )
{
self endon( "change_state" );
self endon( "death" );
self SetMaxSpeedScale( 50 * 17.6 / ( self GetMaxSpeed( true ) ) );
self SetMaxAccelerationScale( 50 / self GetDefaultAcceleration() );
self vehicle_ai::defaultstate_surge_update( params );
}
function state_surge_exit( params )
{
self SetMaxSpeedScale( 0.1 );
self SetSpeed( 0.1 );
self vehicle_ai::ClearAllMovement();
self vehicle_ai::ClearAllLookingAndTargeting();
}
// ----------------------------------------------
// State: stationary
// ----------------------------------------------
function state_stationary_enter( params )
{
vehicle_ai::ClearAllLookingAndTargeting();
vehicle_ai::ClearAllMovement();
self SetBrake( 1 );
//self SetVehWeapon( GetWeapon( WEAPON_STATIONARY ) );
}
function state_stationary_update( params )
{
self endon( "death" );
self endon( "change_state" );
self notify( "stop_rocket_firing_thread" );
vehicle_ai::ClearAllLookingAndTargeting();
vehicle_ai::ClearAllMovement();
wait 1;
self cobra_raise();
minTime = 6; // don't retract before this
maxTime = 12; // retract after this
transformWhenEnemyClose = ( RandomInt( 100 ) < 25 );
losePatientTime = 3 + RandomFloat( 2 ); // retract if don't see enemy for this long
startTime = GetTime();
vehicle_ai::Cooldown( "rocket", 2 );
evade_now = false;
while ( true )
{
// /# self vehicle_ai::UpdatePersonalThreatBias_Bots( 800, 2.0 ); #/ // give bots more threat (for testing purposes -- DO NOT CHECK IN active)
evade_now = ( ( ( self.settings.evade_enemies_locked_on_me === true ) && self.locked_on ) || ( ( self.settings.evade_enemies_locking_on_me === true ) && self.locking_on ) );
if ( vehicle_ai::TimeSince( startTime ) > maxTime || evade_now )
{
break;
}
if( isdefined( self.enemy ) )
{
distSqr = DistanceSquared( self.enemy.origin, self.origin );
// check get out of stationary state
if ( vehicle_ai::TimeSince( startTime ) > minTime )
{
// enemy is too close, emergency transform
if ( transformWhenEnemyClose && distSqr < ( (200) * (200) ) )
{
break;
}
// haven't seen enemy for too long, transform
if ( !self VehSeenRecently( self.enemy, losePatientTime ) )
{
break;
}
}
if ( self VehCanSee( self.enemy ) )
{
if( distSqr < ( (self.settings.engagementDistMax * 3) * (self.settings.engagementDistMax * 3) ) )
{
self SetTurretTargetEnt( self.enemy, (0,0,-5) );
self SetGunnerTargetEnt( self.enemy, (0,0,-5), 0 );
if ( vehicle_ai::IsCooldownReady( "rocket" ) && self.turretontarget && self.gib_rocket !== true )
{
self thread FireRocketLauncher( self.enemy );
vehicle_ai::Cooldown( "rocket", self.settings.rocketcooldown );
}
weapon = self SeatGetWeapon( 1 );
if ( weapon.name=="none" )
idx = 0;
else
idx = 1;
self vehicle_ai::fire_for_time( 1, idx, self.enemy, 0.5 );
}
else
{
break;
}
}
}
wait 0.1;
}
self notify( "stop_rocket_firing_thread" );
vehicle_ai::ClearAllLookingAndTargeting();
vehicle_ai::ClearAllMovement();
if ( evade_now )
{
self wait_evasion_reaction_time();
}
else
{
self state_stationary_update_wait( 0.5 );
}
self cobra_retract();
self vehicle_ai::evaluate_connections();
}
function state_stationary_update_wait( wait_time ) // self == sentient
{
waittill_weapon_lock_or_timeout( wait_time );
}
function state_stationary_exit( params )
{
//self SetVehWeapon( GetWeapon( WEAPON_REGULAR ) );
vehicle_ai::ClearAllLookingAndTargeting();
vehicle_ai::ClearAllMovement();
self SetBrake( 0 );
self vehicle_ai::Cooldown( "cobra_up", 10 );
}
// ----------------------------------------------
// State: combat
// ----------------------------------------------
function state_combat_enter( params )
{
self thread turretFireUpdate();
}
function is_ai_using_minigun()
{
return (isdefined(self.settings.ai_uses_minigun)?self.settings.ai_uses_minigun:true);
}
function turretFireUpdate()
{
weapon = self SeatGetWeapon( 1 );
if ( weapon.name=="none" )
return;
self endon( "death" );
self endon( "change_state" );
self SetOnTargetAngle( 7 );
self SetOnTargetAngle( 7, 0 );
while( 1 )
{
if ( self.avoid_shooting_owner === true && isdefined( self.owner ) )
{
if ( self vehicle_ai::owner_in_line_of_fire() )
{
wait 0.5;
continue;
}
}
if( isdefined( self.enemy ) && self VehCanSee( self.enemy ) && DistanceSquared( self.enemy.origin, self.origin ) < ( (self.settings.engagementDistMax * 3) * (self.settings.engagementDistMax * 3) ) )
{
self SetGunnerTargetEnt( self.enemy, (0,0,0), 0 );
if ( self is_ai_using_minigun() )
{
self SetTurretSpinning( true );
}
wait 0.05; // allow gunner1ontarget to update before checking
if ( !self.gunner1ontarget )
{
wait 0.5;
}
if ( self.gunner1ontarget )
{
if( isdefined( self.enemy ) && self VehCanSee( self.enemy ) )
{
self vehicle_ai::fire_for_time( RandomFloatRange( self.settings.burstFireDurationMin, self.settings.burstFireDurationMax ), 1, self.enemy );
}
if ( self is_ai_using_minigun() )
{
self SetTurretSpinning( false );
}
if( isdefined( self.enemy ) && IsAI( self.enemy ) )
{
wait( RandomFloatRange( self.settings.burstFireAIDelayMin, self.settings.burstFireAIDelayMax ) );
}
else
{
wait( RandomFloatRange( self.settings.burstFireDelayMin, self.settings.burstFireDelayMax ) );
}
}
else
{
wait 0.5;
}
}
else
{
wait 0.4;
}
}
}
function state_combat_update( params )
{
self endon( "change_state" );
self endon( "death" );
lastTimeChangePosition = 0;
self.shouldGotoNewPosition = false;
self.lastTimeTargetInSight = 0;
heatseekingmissile::InitLockField( self );
self.lock_evading = 0; // for use with self.locked_on and self.locking_on
for( ;; )
{
if ( self.lock_evading == 0 )
{
self SetSpeed( self.settings.defaultMoveSpeed );
self SetAcceleration( (isdefined(self.settings.default_move_acceleration)?self.settings.default_move_acceleration:10.0) );
}
if ( RandomInt( 100 ) < 3 && vehicle_ai::IsCooldownReady( "cobra_up" ) && ( self.lock_evading == 0 ))
{
if ( isdefined( self.enemy ) && DistanceSquared( self.enemy.origin, self.origin ) > ( (200) * (200) ) )
{
if( DistanceSquared( self.enemy.origin, self.origin ) < ( (self.settings.engagementDistMax * 2) * (self.settings.engagementDistMax * 2) ) )
{
self vehicle_ai::evaluate_connections();
}
}
}
// /# self vehicle_ai::UpdatePersonalThreatBias_Bots( 800, 2.0 ); #/ // give bots more threat (for testing purposes -- DO NOT CHECK IN active)
// evaluate lock threats to engage them
if ( self.settings.engage_enemies_locked_on_me === true && self.locked_on )
{
self vehicle_ai::UpdatePersonalThreatBias_AttackerLockedOnToMe( (isdefined(self.settings.enemies_locked_on_me_threat_bias)?self.settings.enemies_locked_on_me_threat_bias:5000), (isdefined(self.settings.enemies_locked_on_me_threat_bias_duration)?self.settings.enemies_locked_on_me_threat_bias_duration:1.0) );
self.shouldGotoNewPosition = true;
}
else if ( self.settings.engage_enemies_locking_on_me === true && self.locking_on )
{
self vehicle_ai::UpdatePersonalThreatBias_AttackerLockingOnToMe( (isdefined(self.settings.enemies_locking_on_me_threat_bias)?self.settings.enemies_locking_on_me_threat_bias:2000), (isdefined(self.settings.enemies_locking_on_me_threat_bias_duration)?self.settings.enemies_locking_on_me_threat_bias_duration:1.0) );
self.shouldGotoNewPosition = true;
}
// evalate lock threats for evading
self.lock_evading = 0;
if ( self.settings.evade_enemies_locked_on_me === true )
{
self.lock_evading |= self.locked_on;
}
if ( self.settings.evade_enemies_locking_on_me === true )
{
self.lock_evading |= self.locking_on;
self.lock_evading |= self.locking_on_hacking;
}
if ( ( isdefined( self.inpain ) && self.inpain ) )
{
wait 0.1;
}
else if ( !IsDefined( self.enemy ) )
{
should_slow_down_at_goal = true;
if ( self.lock_evading )
{
self.current_pathto_pos = GetNextMovePosition_evasive( self.lock_evading );
should_slow_down_at_goal = false;
}
else
{
self.current_pathto_pos = GetNextMovePosition_wander();
}
if ( IsDefined( self.current_pathto_pos ) )
{
if ( self SetVehGoalPos( self.current_pathto_pos, should_slow_down_at_goal, true ) )
{
self thread path_update_interrupt_by_attacker();
self thread path_update_interrupt();
self vehicle_ai::waittill_pathing_done();
self notify( "amws_end_interrupt_watch" );
self playsound ("veh_amws_scan");
}
}
self state_combat_update_wait( 0.5 );
}
else
{
self SetTurretTargetEnt( self.enemy );
if ( self vehCanSee( self.enemy ) )
{
self.lastTimeTargetInSight = GetTime();
}
if ( self.shouldGotoNewPosition == false )
{
if ( GetTime() > lastTimeChangePosition + 1000 * 1.0 )
{
self.shouldGotoNewPosition = true;
}
else if ( GetTime() > self.lastTimeTargetInSight + 1000 * 0.5 )
{
self.shouldGotoNewPosition = true;
}
}
if ( self.shouldGotoNewPosition )
{
should_slow_down_at_goal = true;
if ( self.lock_evading )
{
self.current_pathto_pos = GetNextMovePosition_evasive( self.lock_evading );
should_slow_down_at_goal = false;
}
else
{
self.current_pathto_pos = GetNextMovePosition_tactical( self.enemy );
}
if ( IsDefined( self.current_pathto_pos ) )
{
if ( self SetVehGoalPos( self.current_pathto_pos, should_slow_down_at_goal, true ) )
{
self thread path_update_interrupt_by_attacker();
self thread path_update_interrupt();
self vehicle_ai::waittill_pathing_done();
self notify( "amws_end_interrupt_watch" );
}
if ( isdefined( self.enemy ) && vehicle_ai::IsCooldownReady( "rocket", 0.5 ) && self VehCanSee( self.enemy ) && self.gib_rocket !== true )
{
self thread aim_and_fire_rocket_launcher( 0.4 ); // call as thread to prevent blocking movement while aiming rockets
}
lastTimeChangePosition = GetTime();
self.shouldGotoNewPosition = false;
}
}
self state_combat_update_wait( 0.5 );
}
}
}
function aim_and_fire_rocket_launcher( aim_time ) // self == amws
{
self endon( "death" );
self endon( "change_state" );
self notify( "stop_rocket_firing_thread" );
self endon( "stop_rocket_firing_thread" );
if ( !self.turretontarget )
{
wait aim_time;
}
if ( isdefined( self.enemy ) && self.turretontarget )
{
vehicle_ai::Cooldown( "rocket", self.settings.rocketcooldown );
self thread FireRocketLauncher( self.enemy );
}
}
function state_combat_update_wait( wait_time ) // self == sentient
{
self waittill_weapon_lock_or_timeout( wait_time );
}
function waittill_weapon_lock_or_timeout( wait_time ) // self == sentient
{
if ( self.lock_evade_now === true )
{
perform_evasion_reaction_wait = true;
}
else
{
locked_on_notify = undefined;
locking_on_notify = undefined;
reacting_to_locks = ( self.settings.evade_enemies_locked_on_me === true ) || ( self.settings.engage_enemies_locked_on_me === true );
reacting_to_locking = ( self.settings.evade_enemies_locking_on_me === true ) || ( self.settings.engage_enemies_locking_on_me === true );
previous_locked_on_to_me = self.locked_on;
previous_locking_on_to_me = self.locking_on;
if ( reacting_to_locks )
{
locked_on_notify = "missle_lock";
}
if ( reacting_to_locking )
{
locking_on_notify = "locking on";
}
self util::waittill_any_timeout( wait_time, "damage", locking_on_notify, locked_on_notify );
locked_on_to_me_just_changed = previous_locked_on_to_me != self.locked_on && self.locked_on;
locking_on_to_me_just_changed = previous_locking_on_to_me != self.locking_on && self.locking_on;
perform_evasion_reaction_wait = ( ( reacting_to_locks && locked_on_to_me_just_changed ) || ( reacting_to_locking && locking_on_to_me_just_changed ) );
}
// perform evasion reaction time if need be
if ( perform_evasion_reaction_wait )
self wait_evasion_reaction_time();
}
function wait_evasion_reaction_time() // self == vehicle with setting
{
wait RandomFloatRange( (isdefined(self.settings.enemy_evasion_reaction_time_min)?self.settings.enemy_evasion_reaction_time_min:0.1), (isdefined(self.settings.enemy_evasion_reaction_time_max)?self.settings.enemy_evasion_reaction_time_max:0.2) );
}
function FireRocketLauncher( enemy )
{
self endon( "death" );
self endon( "change_state" );
self notify( "stop_rocket_firing_thread" );
self endon( "stop_rocket_firing_thread" );
if ( isdefined( enemy ) )
{
self SetTurretTargetEnt( enemy );
self util::waittill_any_timeout( 1, "turret_on_target" );
if ( self.variant == "armored" )
{
vehicle_ai::fire_for_rounds( 1, 0, enemy );
}
else
{
vehicle_ai::fire_for_rounds( 2, 0, enemy );
}
}
}
function GetNextMovePosition_wander() // no self.enemy
{
if( self.goalforced )
{
return self.goalpos;
}
queryMultiplier = 1.5;
queryResult = PositionQuery_Source_Navigation( self.origin, 80, 500 * queryMultiplier, 0.5 * 500, 3 * self.radius * queryMultiplier, self, self.radius * queryMultiplier );
if ( queryResult.data.size == 0 )
{
// try to move a little bit away since we couldn't find any points in the first position query
queryResult = PositionQuery_Source_Navigation( self.origin, 36, 120, 240, self.radius, self );
}
PositionQuery_Filter_DistanceToGoal( queryResult, self );
vehicle_ai::PositionQuery_Filter_OutOfGoalAnchor( queryResult );
PositionQuery_Filter_InClaimedLocation( queryResult, self );
best_point = undefined;
best_score = -999999;
foreach ( point in queryResult.data )
{
randomScore = randomFloatRange( 0, 100 );
distToOriginScore = point.distToOrigin2D * 0.2;
if( point.inClaimedLocation )
{
point.score -= 500;
}
point.score += randomScore + distToOriginScore;
if ( point.score > best_score )
{
best_score = point.score;
best_point = point;
}
}
/# self.debug_ai_move_to_points_considered = queryResult.data; #/
if( !isdefined( best_point ) )
{
/# self.debug_ai_movement_type = "wander ( 0 / " + queryResult.data.size + " )"; #/
/# self.debug_ai_move_to_point = undefined; #/
return undefined;
}
/# self.debug_ai_movement_type = "wander - " + queryResult.data.size; #/
/# self.debug_ai_move_to_point = best_point.origin; #/
return best_point.origin;
}
function GetNextMovePosition_evasive( client_flags )
{
assert( isdefined( client_flags ) );
self SetSpeed( self.settings.defaultMoveSpeed * (isdefined(self.settings.lock_evade_speed_boost)?self.settings.lock_evade_speed_boost:2.0) );
self SetAcceleration( (isdefined(self.settings.default_move_acceleration)?self.settings.default_move_acceleration:10.0) * (isdefined(self.settings.lock_evade_acceleration_boost)?self.settings.lock_evade_acceleration_boost:2.0) );
// query points to where to evade
queryResult = PositionQuery_Source_Navigation( self.origin,
(isdefined(self.settings.lock_evade_dist_min)?self.settings.lock_evade_dist_min:120),
(isdefined(self.settings.lock_evade_dist_max)?self.settings.lock_evade_dist_max:360),
math::clamp( (isdefined(self.settings.lock_evade_dist_half_height)?self.settings.lock_evade_dist_half_height:( 500 * 0.5 )), 0.1, 99000 ),
(isdefined(self.settings.lock_evade_point_spacing_factor)?self.settings.lock_evade_point_spacing_factor:( 1.5 )) * self.radius,
self );
// initial filter
PositionQuery_Filter_InClaimedLocation( queryResult, self );
//note: goalpos related filters intentionally left out as the amws is trying to evade getting killed by a rocket
// process claimed location score
foreach ( point in queryResult.data )
{
if( point.inClaimedLocation )
{
/# if ( !isdefined( point._scoreDebug ) ) { point._scoreDebug = []; } point._scoreDebug[ "inClaimedLocation" ] = -500; #/ point.score += -500;;
}
}
remaining_lock_threats_to_evaluate = 3; // used as iteration cap
remaining_flags_to_process = client_flags;
for ( i = 0; remaining_flags_to_process && remaining_lock_threats_to_evaluate > 0 && i < level.players.size; i++ )
{
attacker = level.players[ i ];
if ( isdefined( attacker ) )
{
client_flag = ( 1 << attacker getEntityNumber() );
if ( client_flag & remaining_flags_to_process )
{
// filter directness relative to lock threat
PositionQuery_Filter_Directness( queryResult, self.origin, attacker.origin );
// update score based on directness
foreach ( point in queryResult.data )
{
abs_directness = Abs( point.directness );
if( abs_directness < 0.2 )
{
/# if ( !isdefined( point._scoreDebug ) ) { point._scoreDebug = []; } point._scoreDebug[ "evading_directness" ] = 200; #/ point.score += 200;;
}
else if ( abs_directness > (isdefined(self.settings.lock_evade_enemy_line_of_sight_directness)?self.settings.lock_evade_enemy_line_of_sight_directness:0.9) )
{
/# if ( !isdefined( point._scoreDebug ) ) { point._scoreDebug = []; } point._scoreDebug[ "evading_directness_line_of_sight" ] = -101; #/ point.score += -101;;
}
}
// update for next threat
remaining_flags_to_process &= ~client_flag;
remaining_lock_threats_to_evaluate--;
}
}
}
// give a point "ahead" of the amws more points
PositionQuery_Filter_Directness( queryResult, self.origin, self.origin + ( AnglesToForward( self.angles ) * 360 ) );
foreach ( point in queryResult.data )
{
if( point.directness > 0.5 )
{
/# if ( !isdefined( point._scoreDebug ) ) { point._scoreDebug = []; } point._scoreDebug[ "prefer forward motion" ] = 105; #/ point.score += 105;;
}
}
// score points
best_point = undefined;
best_score = -999999;
foreach ( point in queryResult.data )
{
if ( point.score > best_score )
{
best_score = point.score;
best_point = point;
}
}
self.lock_evade_now = false;
self vehicle_ai::PositionQuery_DebugScores( queryResult );
/# self.debug_ai_move_to_points_considered = queryResult.data; #/
if( !isdefined( best_point ) )
{
/# self.debug_ai_movement_type = "evasive ( 0 / " + queryResult.data.size + " )"; #/
/# self.debug_ai_move_to_point = undefined; #/
return undefined;
}
/# self.debug_ai_movement_type = "evasive - " + queryResult.data.size; #/
/# self.debug_ai_move_to_point = best_point.origin; #/
return best_point.origin;
}
function GetNextMovePosition_tactical( enemy )
{
if( self.goalforced )
{
return self.goalpos;
}
// distance based multipliers
selfDistToTarget = Distance2D( self.origin, enemy.origin );
goodDist = 0.5 * ( self.settings.engagementDistMin + self.settings.engagementDistMax );
tooCloseDist = ( 0.8 * 0.5 * ( self.settings.engagementDistMin + self.settings.engagementDistMax ) );
closeDist = 1.2 * goodDist;
farDist = 3 * goodDist;
queryMultiplier = MapFloat( closeDist, farDist, 1, 3, selfDistToTarget );
preferedDirectness = 0;
if ( selfDistToTarget > goodDist )
{
preferedDirectness = MapFloat( closeDist, farDist, 0, 1, selfDistToTarget );
}
else
{
preferedDirectness = MapFloat( tooCloseDist * 0.4, tooCloseDist, -1, -0.6, selfDistToTarget );
}
preferedDistAwayFromOrigin = 300;
randomness = 30;
// query
queryResult = PositionQuery_Source_Navigation( self.origin, 80, 500 * queryMultiplier, 0.5 * 500, 2 * self.radius * queryMultiplier, self, 1 * self.radius * queryMultiplier );
// filter
PositionQuery_Filter_Directness( queryResult, self.origin, enemy.origin );
PositionQuery_Filter_DistanceToGoal( queryResult, self );
vehicle_ai::PositionQuery_Filter_OutOfGoalAnchor( queryResult );
PositionQuery_Filter_InClaimedLocation( queryResult, self );
vehicle_ai::PositionQuery_Filter_EngagementDist( queryResult, enemy, self.settings.engagementDistMin, self.settings.engagementDistMax );
if ( isdefined( self.avoidEntities ) && isdefined( self.avoidEntitiesDistance ) )
{
vehicle_ai::PositionQuery_Filter_DistAwayFromTarget( queryResult, self.avoidEntities, self.avoidEntitiesDistance, -500 );
}
// score points
best_point = undefined;
best_score = -999999;
foreach ( point in queryResult.data )
{
// directness
diffToPreferedDirectness = abs( point.directness - preferedDirectness );
directnessScore = MapFloat( 0, 1, 100, 0, diffToPreferedDirectness );
if ( diffToPreferedDirectness > 0.2 )
{
directnessScore -= 200;
}
/# if ( !isdefined( point._scoreDebug ) ) { point._scoreDebug = []; } point._scoreDebug[ "directnessRaw" ] = point.directness; #/ point.score += point.directness;;
/# if ( !isdefined( point._scoreDebug ) ) { point._scoreDebug = []; } point._scoreDebug[ "directness" ] = directnessScore; #/ point.score += directnessScore;;
// distance from origin
/# if ( !isdefined( point._scoreDebug ) ) { point._scoreDebug = []; } point._scoreDebug[ "distToOrigin" ] = MapFloat( 0, preferedDistAwayFromOrigin, 0, 100, point.distToOrigin2D ); #/ point.score += MapFloat( 0, preferedDistAwayFromOrigin, 0, 100, point.distToOrigin2D );;
// distance to target
targetDistScore = 0;
if ( point.targetDist < tooCloseDist )
{
targetDistScore -= 200;
}
if( point.inClaimedLocation )
{
/# if ( !isdefined( point._scoreDebug ) ) { point._scoreDebug = []; } point._scoreDebug[ "inClaimedLocation" ] = -500; #/ point.score += -500;;
}
/# if ( !isdefined( point._scoreDebug ) ) { point._scoreDebug = []; } point._scoreDebug[ "distToTarget" ] = targetDistScore; #/ point.score += targetDistScore;;
/# if ( !isdefined( point._scoreDebug ) ) { point._scoreDebug = []; } point._scoreDebug[ "random" ] = randomFloatRange( 0, randomness ); #/ point.score += randomFloatRange( 0, randomness );;
if ( point.score > best_score )
{
best_score = point.score;
best_point = point;
}
}
self vehicle_ai::PositionQuery_DebugScores( queryResult );
/# self.debug_ai_move_to_points_considered = queryResult.data; #/
if( !isdefined( best_point ) )
{
/# self.debug_ai_movement_type = "tactical ( 0 / " + queryResult.data.size + " )"; #/
/# self.debug_ai_move_to_point = undefined; #/
return undefined;
}
/#
if ( ( isdefined( GetDvarInt("hkai_debugPositionQuery") ) && GetDvarInt("hkai_debugPositionQuery") ) )
{
recordLine( self.origin, best_point.origin, (0.3,1,0) );
recordLine( self.origin, enemy.origin, (1,0,0.4) );
}
#/
/# self.debug_ai_movement_type = "tactical - " + queryResult.data.size; #/
/# self.debug_ai_move_to_point = best_point.origin; #/
return best_point.origin;
}
function path_update_interrupt_by_attacker() //self == vehicle
{
self endon( "death" );
self endon( "change_state" );
self endon( "near_goal" );
self endon( "reached_end_node" );
self endon( "amws_end_interrupt_watch" );
self util::waittill_any( "locking on", "missile_lock", "damage" );
if ( self.locked_on || self.locking_on )
{
/# self.debug_ai_move_to_points_considered = []; #/
/# self.debug_ai_movement_type = "interrupted"; #/
/# self.debug_ai_move_to_point = undefined; #/
self ClearVehGoalPos(); // do this to prevent the "stopping" of the vehicle
self.lock_evade_now = true;
}
self notify( "near_goal" );
}
function path_update_interrupt()
{
self endon( "death" );
self endon( "change_state" );
self endon( "near_goal" );
self endon( "reached_end_node" );
self endon( "amws_end_interrupt_watch" );
wait 1;
while( 1 )
{
if( isdefined( self.current_pathto_pos ) )
{
if( distance2dSquared( self.current_pathto_pos, self.goalpos ) > ( (self.goalradius) * (self.goalradius) ) )
{
wait 0.2;
self notify( "near_goal" );
}
}
if ( isdefined( self.enemy ) )
{
if( self VehCanSee( self.enemy ) && distance2dSquared( self.origin, self.enemy.origin ) < ( (( 0.8 * 0.5 * ( self.settings.engagementDistMin + self.settings.engagementDistMax ) )) * (( 0.8 * 0.5 * ( self.settings.engagementDistMin + self.settings.engagementDistMax ) )) ) )
{
self notify( "near_goal" );
}
if ( vehicle_ai::IsCooldownReady( "rocket" ) && vehicle_ai::IsCooldownReady( "rocket_launcher_check" ) )
{
vehicle_ai::Cooldown( "rocket_launcher_check", 2.5 );
self notify( "near_goal" );
}
}
wait 0.2;
}
}
function gib( attacker )
{
if ( self.gibbed !== true )
{
self vehicle::do_gib_dynents();
self.gibbed = true;
self.death_type = "suicide_crash";
self kill( self.origin + (0,0,10), attacker );
}
}
function drone_callback_damage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal )
{
iDamage = vehicle_ai::shared_callback_damage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal );
return iDamage;
}