diff --git a/game/missiongenerator/aircraft/flightgroupconfigurator.py b/game/missiongenerator/aircraft/flightgroupconfigurator.py index ea29a6f6..d49d45c0 100644 --- a/game/missiongenerator/aircraft/flightgroupconfigurator.py +++ b/game/missiongenerator/aircraft/flightgroupconfigurator.py @@ -173,18 +173,24 @@ class FlightGroupConfigurator: self, unit: FlyingUnit, member: FlightMember, laser_codes: list[Optional[int]] ) -> None: self.set_skill(unit, member) + if (code := member.tgp_laser_code) is not None: laser_codes.append(code.code) else: laser_codes.append(None) - settings = self.flight.coalition.game.settings - if not member.is_player or not settings.plugins.get("ewrj"): + + self.handle_ew_jamming(member, unit) + + def handle_ew_jamming(self, member: FlightMember, unit: FlyingUnit) -> None: + if not member.is_player: return + settings = self.flight.coalition.game.settings + # Check if ecm_required option is enabled jammer_required = settings.plugin_option("ewrj.ecm_required") - if jammer_required: - ecm = WeaponType.JAMMER - if not member.loadout.has_weapon_of_type(ecm): - return + has_jammer = member.loadout.has_weapon_of_type(WeaponType.JAMMER) + if jammer_required and not has_jammer: + return + # Create the original ewrj_menu_trigger for player flight members ewrj_menu_trigger = TriggerStart(comment=f"EWRJ-{unit.name}") ewrj_menu_trigger.add_action(DoScript(String(f'EWJamming("{unit.name}")'))) self.mission.triggerrules.triggers.append(ewrj_menu_trigger) diff --git a/game/missiongenerator/aircraft/waypoints/joinpoint.py b/game/missiongenerator/aircraft/waypoints/joinpoint.py index 034c46be..9e1d9c43 100644 --- a/game/missiongenerator/aircraft/waypoints/joinpoint.py +++ b/game/missiongenerator/aircraft/waypoints/joinpoint.py @@ -9,9 +9,13 @@ from dcs.task import ( OptFormation, Targets, SetUnlimitedFuelCommand, + RunScript, + OptReactOnThreat, ) from game.ato import FlightType +from game.data.doctrine import Doctrine +from game.data.weapons import WeaponType from game.theater import NavalControlPoint from game.utils import nautical_miles, feet from .pydcswaypointbuilder import PydcsWaypointBuilder @@ -54,29 +58,24 @@ class JoinPointBuilder(PydcsWaypointBuilder): max_dist=doctrine.escort_engagement_range.nautical_miles, vertical_spacing=doctrine.escort_spacing.feet, ) - elif self.flight.flight_type == FlightType.SEAD_ESCORT: - if isinstance(self.flight.package.target, NavalControlPoint): - self.configure_escort_tasks( - waypoint, - [ - Targets.All.Naval.id, - Targets.All.GroundUnits.AirDefence.AAA.SAMRelated.id, - ], - max_dist=doctrine.sead_escort_engagement_range.nautical_miles, - vertical_spacing=doctrine.sead_escort_spacing.feet, + + elif self.flight.flight_type in [ + FlightType.SEAD_SWEEP, + FlightType.SEAD, + FlightType.SEAD_ESCORT, + ]: + self.start_defensive_jamming(waypoint) + if self.flight.flight_type == FlightType.SEAD_ESCORT: + self.handle_sead_escort(doctrine, waypoint) + # Let the AI use ECM to preemptively defend themselves. + ecm_option = OptECMUsing( + value=OptECMUsing.Values.UseIfDetectedLockByRadar ) + waypoint.tasks.append(ecm_option) else: - self.configure_escort_tasks( - waypoint, - [Targets.All.GroundUnits.AirDefence.AAA.SAMRelated.id], - max_dist=doctrine.sead_escort_engagement_range.nautical_miles, - vertical_spacing=doctrine.sead_escort_spacing.feet, - ) - - # Let the AI use ECM to preemptively defend themselves. - ecm_option = OptECMUsing(value=OptECMUsing.Values.UseIfDetectedLockByRadar) - waypoint.tasks.append(ecm_option) - + # Let the AI use ECM to defend themselves. + ecm_option = OptECMUsing(value=OptECMUsing.Values.UseIfOnlyLockByRadar) + waypoint.tasks.append(ecm_option) elif not self.flight.flight_type.is_air_to_air: # Capture any non A/A type to avoid issues with SPJs that use the primary radar such as the F/A-18C. # You can bully them with STT to not be able to fire radar guided missiles at you, @@ -86,6 +85,47 @@ class JoinPointBuilder(PydcsWaypointBuilder): ecm_option = OptECMUsing(value=OptECMUsing.Values.UseIfOnlyLockByRadar) waypoint.tasks.append(ecm_option) + def handle_sead_escort(self, doctrine: Doctrine, waypoint: MovingPoint) -> None: + if isinstance(self.flight.package.target, NavalControlPoint): + self.configure_escort_tasks( + waypoint, + [ + Targets.All.Naval.id, + Targets.All.GroundUnits.AirDefence.AAA.SAMRelated.id, + ], + max_dist=doctrine.sead_escort_engagement_range.nautical_miles, + vertical_spacing=doctrine.sead_escort_spacing.feet, + ) + else: + self.configure_escort_tasks( + waypoint, + [Targets.All.GroundUnits.AirDefence.AAA.SAMRelated.id], + max_dist=doctrine.sead_escort_engagement_range.nautical_miles, + vertical_spacing=doctrine.sead_escort_spacing.feet, + ) + + def start_defensive_jamming(self, waypoint: MovingPoint) -> None: + # Start Defensive Jamming + settings = self.flight.coalition.game.settings + ai_jammer = settings.plugin_option("ewrj.ai_jammer_enabled") + if settings.plugins.get("ewrj") and ai_jammer: + ecm_required = settings.plugin_option("ewrj.ecm_required") + has_jammer = False + for unit, member in zip(self.group.units, self.flight.iter_members()): + has_jammer = member.loadout.has_weapon_of_type(WeaponType.JAMMER) + if ecm_required and not has_jammer: + continue + if not member.is_player: + script_content = f'startDjamming("{unit.name}")' + start_jamming_script = RunScript(script_content) + waypoint.tasks.append(start_jamming_script) + has_jammer = True + if has_jammer: + passive_defense = OptReactOnThreat( + OptReactOnThreat.Values.PassiveDefense + ) + waypoint.tasks.append(passive_defense) + def configure_escort_tasks( self, waypoint: MovingPoint, diff --git a/game/missiongenerator/aircraft/waypoints/racetrack.py b/game/missiongenerator/aircraft/waypoints/racetrack.py index b167653c..f3300fc9 100644 --- a/game/missiongenerator/aircraft/waypoints/racetrack.py +++ b/game/missiongenerator/aircraft/waypoints/racetrack.py @@ -9,6 +9,8 @@ from dcs.task import ( Tanker, Targets, SetUnlimitedFuelCommand, + RunScript, + OptReactOnThreat, ) from game.ato import FlightType @@ -38,6 +40,22 @@ class RaceTrackBuilder(PydcsWaypointBuilder): ) return + if self.flight.flight_type == FlightType.AEWC: + # Start Offensive Jamming for all AWACS flights + settings = self.flight.coalition.game.settings + ai_jammer = settings.plugin_option("ewrj.ai_jammer_enabled") + if settings.plugins.get("ewrj") and ai_jammer: + # all units in group are AWACS, no specific checks needed + for unit, member in zip(self.group.units, self.flight.iter_members()): + script_content = f'startEWjamm("{unit.name}")' + start_jamming_script = RunScript(script_content) + waypoint.tasks.append(start_jamming_script) + + passive_defense = OptReactOnThreat( + OptReactOnThreat.Values.PassiveDefense + ) + waypoint.tasks.append(passive_defense) + # NB: It's important that the engage task comes before the orbit task. # Though they're on the same waypoint, if the orbit task comes first it # is their first priority and they will not engage any targets because diff --git a/game/missiongenerator/aircraft/waypoints/racetrackend.py b/game/missiongenerator/aircraft/waypoints/racetrackend.py index ff8402da..04841a35 100644 --- a/game/missiongenerator/aircraft/waypoints/racetrackend.py +++ b/game/missiongenerator/aircraft/waypoints/racetrackend.py @@ -1,8 +1,13 @@ import logging from dcs.point import MovingPoint -from dcs.task import SetUnlimitedFuelCommand +from dcs.task import ( + SetUnlimitedFuelCommand, + RunScript, + OptReactOnThreat, +) +from game.ato import FlightType from game.ato.flightplans.patrolling import PatrollingFlightPlan from .pydcswaypointbuilder import PydcsWaypointBuilder @@ -13,6 +18,21 @@ class RaceTrackEndBuilder(PydcsWaypointBuilder): if self.flight.squadron.coalition.game.settings.ai_unlimited_fuel: waypoint.tasks.insert(0, SetUnlimitedFuelCommand(True)) + # Disable Offensive Jamming at Racetrack End + if self.flight.flight_type == FlightType.AEWC: + # Stop Offensive Jamming for all AWACS flights + settings = self.flight.coalition.game.settings + ai_jammer = settings.plugin_option("ewrj.ai_jammer_enabled") + if settings.plugins.get("ewrj") and ai_jammer: + # all units in group are AWACS, no specific checks needed + for unit, member in zip(self.group.units, self.flight.iter_members()): + script_content = f'stopEWjamming("{unit.name}")' + stop_jamming_script = RunScript(script_content) + waypoint.tasks.append(stop_jamming_script) + + evade_fire = OptReactOnThreat(OptReactOnThreat.Values.EvadeFire) + waypoint.tasks.append(evade_fire) + def build(self) -> MovingPoint: waypoint = super().build() diff --git a/game/missiongenerator/aircraft/waypoints/splitpoint.py b/game/missiongenerator/aircraft/waypoints/splitpoint.py index 0fdd0b2a..3e29ff35 100644 --- a/game/missiongenerator/aircraft/waypoints/splitpoint.py +++ b/game/missiongenerator/aircraft/waypoints/splitpoint.py @@ -2,11 +2,14 @@ from dcs.point import MovingPoint from dcs.task import ( OptECMUsing, OptFormation, - RunScript, SetUnlimitedFuelCommand, SwitchWaypoint, + RunScript, + OptReactOnThreat, ) +from game.ato import FlightType +from game.data.weapons import WeaponType from game.utils import knots from .pydcswaypointbuilder import PydcsWaypointBuilder @@ -41,6 +44,33 @@ class SplitPointBuilder(PydcsWaypointBuilder): f'trigger.action.setUserFlag("split-{id(self.package)}", true)' ) waypoint.tasks.append(script) - elif self.flight.flight_type.is_escort_type: - index = len(self.group.points) - self.group.add_trigger_action(SwitchWaypoint(None, index)) + + elif self.flight.flight_type in [ + FlightType.SEAD_SWEEP, + FlightType.SEAD, + FlightType.SEAD_ESCORT, + ]: + if self.flight.flight_type == FlightType.SEAD_ESCORT: + # Moved previous escort split tasks + if self.flight.flight_type.is_escort_type: + index = len(self.group.points) + self.group.add_trigger_action(SwitchWaypoint(None, index)) + self.stop_defensive_jamming(waypoint) + + def stop_defensive_jamming(self, waypoint: MovingPoint) -> None: + # Stop Defensive Jamming + settings = self.flight.coalition.game.settings + ai_jammer = settings.plugin_option("ewrj.ai_jammer_enabled") + if settings.plugins.get("ewrj") and ai_jammer: + for unit, member in zip(self.group.units, self.flight.iter_members()): + if settings.plugin_option("ewrj.ecm_required"): + ecm = WeaponType.JAMMER + if not member.loadout.has_weapon_of_type(ecm): + continue + if not member.is_player: + script_content = f'stopDjamming("{unit.name}")' + stop_jamming_script = RunScript(script_content) + waypoint.tasks.append(stop_jamming_script) + + evaide_fire = OptReactOnThreat(OptReactOnThreat.Values.EvadeFire) + waypoint.tasks.append(evaide_fire) diff --git a/resources/plugins/ewrj/plugin.json b/resources/plugins/ewrj/plugin.json index 3b072322..b531f2b2 100644 --- a/resources/plugins/ewrj/plugin.json +++ b/resources/plugins/ewrj/plugin.json @@ -1,11 +1,16 @@ { - "nameInUI": "EW Jammer (for player only)", + "nameInUI": "EW Jammer Scipt 2.0", "defaultValue": false, "specificOptions": [ { "nameInUI": "Require ECM pod", "mnemonic": "ecm_required", "defaultValue": true + }, + { + "nameInUI": "Enable AI Jammers", + "mnemonic": "ai_jammer_enabled", + "defaultValue": false } ], "scriptsWorkOrders": [