#using scripts\codescripts\struct; #using scripts\shared\array_shared; #using scripts\shared\clientfield_shared; #using scripts\shared\flag_shared; #using scripts\shared\fx_shared; #using scripts\shared\math_shared; #using scripts\shared\statemachine_shared; #using scripts\shared\system_shared; #using scripts\shared\turret_shared; #using scripts\shared\util_shared; #using scripts\shared\vehicle_death_shared; #using scripts\shared\vehicle_shared; #using scripts\shared\vehicle_ai_shared; #namespace auto_turret; function autoexec __init__sytem__() { system::register("auto_turret",&__init__,undefined,undefined); } function __init__() { vehicle::add_main_callback( "auto_turret", &turret_initialze ); } function turret_initialze() { self.health = self.healthdefault; if ( isdefined( self.scriptbundlesettings ) ) { self.settings = struct::get_script_bundle( "vehiclecustomsettings", self.scriptbundlesettings ); } else { self.settings = struct::get_script_bundle( "vehiclecustomsettings", "artillerysettings" ); } sightfov = self.settings.sightfov; if ( !isdefined( sightfov ) ) { sightfov = 0; } self.fovcosine = Cos( sightfov - 0.1 ); self.fovcosinebusy = Cos( sightfov - 0.1 ); if ( self.settings.disconnectpaths === true ) { self DisconnectPaths(); } if ( self.settings.ignoreme === true ) { self.ignoreme = true; } if ( self.settings.laseron === true ) { self laserOn(); } // only auto aim if it can fire. if more specific situation comes we can separate aim assist into a separate GDT field if ( self.settings.disablefiring !== true ) { self EnableAimAssist(); } else { self.noCybercom = true; } self thread turret::track_lens_flare(); self.overrideVehicleDamage =&turretCallback_VehicleDamage; self.allowFriendlyFireDamageOverride = &turretAllowFriendlyFireDamage; //disable some cybercom abilities if( IsDefined( level.vehicle_initializer_cb ) ) { [[level.vehicle_initializer_cb]]( self ); } self.ignoreFireFly = true; self.ignoreDecoy = true; self vehicle_ai::InitThreatBias(); defaultRole(); } function defaultRole() { self vehicle_ai::init_state_machine_for_role( "default" ); self vehicle_ai::get_state_callbacks( "death" ).update_func = &state_death_update; self vehicle_ai::get_state_callbacks( "combat" ).update_func = &state_combat_update; self vehicle_ai::get_state_callbacks( "combat" ).exit_func = &state_combat_exit; self vehicle_ai::get_state_callbacks( "off" ).enter_func = &state_off_enter; self vehicle_ai::get_state_callbacks( "off" ).exit_func = &state_off_exit; self vehicle_ai::get_state_callbacks( "emped" ).enter_func = &state_emped_enter; self vehicle_ai::get_state_callbacks( "emped" ).update_func = &state_emped_update; self vehicle_ai::get_state_callbacks( "emped" ).exit_func = &state_emped_exit; self vehicle_ai::add_state( "unaware", undefined, &state_unaware_update, undefined ); vehicle_ai::add_interrupt_connection( "unaware", "scripted", "enter_scripted" ); vehicle_ai::add_interrupt_connection( "unaware", "emped", "emped" ); vehicle_ai::add_interrupt_connection( "unaware", "off", "shut_off" ); vehicle_ai::add_interrupt_connection( "unaware", "driving", "enter_vehicle" ); vehicle_ai::add_interrupt_connection( "unaware", "pain", "pain" ); vehicle_ai::add_utility_connection( "unaware", "combat", &should_switch_to_combat ); vehicle_ai::add_utility_connection( "combat", "unaware", &should_switch_to_unaware ); vehicle_ai::StartInitialState( "unaware" ); } // ---------------------------------------------- // State: death // ---------------------------------------------- function state_death_update( params ) { self endon( "death" ); owner = self GetVehicleOwner(); if ( isdefined( owner ) ) { self waittill( "exit_vehicle" ); } self SetTurretSpinning( false ); self turret::toggle_lensflare( false ); params.death_type = "default"; // only do default death for now self thread turret_idle_sound_stop(); self playsound("veh_sentry_turret_dmg_hit"); self.turretRotScale = 2; self rest_turret( params.resting_pitch ); self vehicle_ai::defaultstate_death_update( params ); } // death----------------------------------------- // ---------------------------------------------- // State: unaware // ---------------------------------------------- function should_switch_to_unaware( current_state, to_state, connection ) { if ( !isdefined( self.enemy ) || !self VehSeenRecently( self.enemy, 1.5 ) ) { return 100; } return 0; } function state_unaware_update( params ) { self endon( "death" ); self endon( "change_state" ); turret_left = true; relativeAngle = 0; self thread turret_idle_sound(); self playsound ("mpl_turret_startup"); self ClearTargetEntity(); while( true ) { rotScale = self.settings.scanning_speedscale; if ( !isdefined( rotScale ) ) { rotScale = 0.01; } self.turretRotScale = rotScale; scanning_arc = self.settings.scanning_arc; if ( !isdefined( scanning_arc ) ) { scanning_arc = 0; } limits = self GetTurretLimitsYaw(); scanning_arc = min( scanning_arc, limits[0] - 0.1 ); scanning_arc = min( scanning_arc, limits[1] - 0.1 ); if ( scanning_arc > 179 ) { if ( self.turretontarget ) { relativeAngle += 90; if ( relativeAngle > 180 ) { relativeAngle -= 360; } } scanning_arc = relativeAngle; } else { if ( self.turretontarget ) { turret_left = !turret_left; } if ( !turret_left ) { scanning_arc *= -1; } } scanning_pitch = self.settings.scanning_pitch; if ( !isdefined( scanning_pitch ) ) { scanning_pitch = 0; } self SetTurretTargetRelativeAngles( ( scanning_pitch, scanning_arc, 0 ) ); self vehicle_ai::evaluate_connections(); wait 0.5; } } // unaware--------------------------------------- // ---------------------------------------------- // State: combat // ---------------------------------------------- function should_switch_to_combat( current_state, to_state, connection ) { if ( isdefined( self.enemy ) && isAlive( self.enemy ) && self VehCanSee( self.enemy ) ) { return 100; } return 0; } function state_combat_update( params ) { self endon( "death" ); self endon( "change_state" ); if( isdefined( self.enemy ) ) { sentry_turret_alert_sound(); wait 0.5; } while( 1 ) { if( isdefined( self.enemy ) && self VehCanSee( self.enemy ) ) { self.turretRotScale = 1; if ( isdefined( self.enemy ) && self HasPart( "tag_minigun_spin" ) ) { self SetTurretTargetEnt( self.enemy ); self SetTurretSpinning( true ); wait 0.5; } for( i = 0; i < 3; i++ ) { if( isdefined( self.enemy ) && IsAlive( self.enemy ) && self VehCanSee( self.enemy ) ) { self SetTurretTargetEnt( self.enemy ); wait 0.1; waitTime = RandomFloatRange( 0.4, 1.5 ); if ( self.settings.disablefiring !== true ) { self sentry_turret_fire_for_time( waitTime, self.enemy ); } else { wait waitTime; } } if( isdefined( self.enemy ) && IsPlayer( self.enemy ) ) wait RandomFloatRange( 0.3, 0.6 ); else wait RandomFloatRange( 0.3, 0.6 ) * 2; } self SetTurretSpinning( false ); if( isdefined( self.enemy ) && IsAlive( self.enemy ) && self VehCanSee( self.enemy ) ) { if( IsPlayer( self.enemy ) ) wait RandomFloatRange( 0.5, 1.3 ); else wait RandomFloatRange( 0.5, 1.3 ) * 2; } } self vehicle_ai::evaluate_connections(); wait 0.5; } } function state_combat_exit( params ) { self SetTurretSpinning( false ); } function sentry_turret_fire_for_time( totalFireTime, enemy ) { self endon( "death" ); self endon( "change_state" ); sentry_turret_alert_sound(); wait .1;//giving time for the alert to play weapon = self SeatGetWeapon( 0 ); fireTime = weapon.fireTime; time = 0; is_minigun = false; if ( IsSubStr( weapon.name, "minigun" ) ) { is_minigun = true; self SetTurretSpinning( true ); wait 0.5; } while( time < totalFireTime ) { self FireWeapon(0, enemy); wait fireTime; time += fireTime; } if( is_minigun ) { self SetTurretSpinning( false ); } } // combat---------------------------------------- // ---------------------------------------------- // State: off // ---------------------------------------------- function state_off_enter( params ) { self vehicle_ai::defaultstate_off_enter( params ); self.turretRotScale = 0.5; self rest_turret( params.resting_pitch ); } function state_off_exit( params ) { self vehicle_ai::defaultstate_off_exit( params ); self.turretRotScale = 1.0; self playsound ("mpl_turret_startup"); } function rest_turret( resting_pitch ) { if ( !isdefined( resting_pitch ) ) { resting_pitch = self.settings.resting_pitch; } if ( !isdefined( resting_pitch ) ) { resting_pitch = 0; } angles = self GetTagAngles( "tag_turret" ) - self.angles; self SetTurretTargetRelativeAngles( ( resting_pitch, angles[1], 0 ) ); } // off------------------------------------------- // ---------------------------------------------- // State: emped // ---------------------------------------------- function state_emped_enter( params ) { self vehicle_ai::defaultstate_emped_enter( params ); PlaySoundAtPosition( "veh_sentry_turret_emp_down", self.origin ); self.turretRotScale = 0.5; self rest_turret( params.resting_pitch ); params.laserOn = IsLaserOn( self ); self LaserOff(); self vehicle::lights_off(); if ( !isdefined( self.abnormal_status ) ) { self.abnormal_status = spawnStruct(); } self.abnormal_status.emped = true; self.abnormal_status.attacker = params.notify_param[1]; self.abnormal_status.inflictor = params.notify_param[2]; self vehicle::toggle_emp_fx( true ); } function state_emped_update( params ) { self endon ("death"); self endon ("change_state"); time = params.notify_param[0]; assert( isdefined( time ) ); vehicle_ai::Cooldown( "emped_timer", time ); while( !vehicle_ai::IsCooldownReady( "emped_timer" ) ) { timeLeft = max( vehicle_ai::GetCooldownLeft( "emped_timer" ), 0.5 ); wait timeLeft; } self.abnormal_status.emped = false; self vehicle::toggle_emp_fx( false ); self vehicle_ai::emp_startup_fx(); self rest_turret( 0 ); wait 1; self vehicle_ai::evaluate_connections(); } function state_emped_exit( params ) { self vehicle_ai::defaultstate_emped_exit( params ); self.turretRotScale = 1.0; self playsound ("mpl_turret_startup"); } // emped----------------------------------------- // ---------------------------------------------- // State: scripted // ---------------------------------------------- function state_scripted_update( params ) { self.turretRotScale = 1; } // scripted-------------------------------------- function turretAllowFriendlyFireDamage( eInflictor, eAttacker, sMeansOfDeath, weapon ) { if ( isdefined( eAttacker ) && isdefined( sMeansOfDeath ) && sMeansOfDeath == "MOD_EXPLOSIVE" ) { return true; } return false; } function turretCallback_VehicleDamage( 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 ); //self HidePart( "tag_shield" ); return iDamage; } function sentry_turret_alert_sound() { self playsound ("veh_turret_alert"); } function turret_idle_sound() { if ( !isdefined( self.sndloop_ent ) ) { self.sndloop_ent = Spawn("script_origin", self.origin); self.sndloop_ent linkto (self); self.sndloop_ent PlayLoopSound ("veh_turret_idle"); } } function turret_idle_sound_stop() { self endon( "death" ); if ( isdefined( self.sndloop_ent ) ) { self.sndloop_ent stoploopsound( .5 ); wait( 2 ); self.sndloop_ent delete(); } }