#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; }