Merge branch 'dev' into pr/204

This commit is contained in:
Raffson
2024-07-28 15:57:38 +02:00
100 changed files with 1342 additions and 220 deletions

View File

@@ -57,6 +57,8 @@ class AircraftBehavior:
self.configure_refueling(group, flight)
elif self.task in [FlightType.CAS, FlightType.BAI]:
self.configure_cas(group, flight)
elif self.task == FlightType.ARMED_RECON:
self.configure_armed_recon(group, flight)
elif self.task == FlightType.DEAD:
self.configure_dead(group, flight)
elif self.task in [FlightType.SEAD, FlightType.SEAD_SWEEP]:
@@ -183,6 +185,17 @@ class AircraftBehavior:
restrict_jettison=True,
)
def configure_armed_recon(self, group: FlyingGroup[Any], flight: Flight) -> None:
self.configure_task(flight, group, CAS, [AFAC, AntishipStrike])
self.configure_behavior(
flight,
group,
react_on_threat=OptReactOnThreat.Values.EvadeFire,
roe=OptROE.Values.OpenFire,
rtb_winchester=OptRTBOnOutOfAmmo.Values.All,
restrict_jettison=True,
)
def configure_dead(self, group: FlyingGroup[Any], flight: Flight) -> None:
# Only CAS and SEAD are capable of the Attack Group task. SEAD is arguably more
# appropriate but it has an extremely limited list of capable aircraft, whereas
@@ -379,23 +392,35 @@ class AircraftBehavior:
if preferred_task in flight.unit_type.dcs_unit_type.tasks:
group.task = preferred_task.name
elif fallback_tasks:
return
if fallback_tasks:
for task in fallback_tasks:
if task in flight.unit_type.dcs_unit_type.tasks:
group.task = task.name
return
elif flight.unit_type.dcs_unit_type.task_default and preferred_task == Nothing:
if flight.unit_type.dcs_unit_type.task_default and preferred_task == Nothing:
group.task = flight.unit_type.dcs_unit_type.task_default.name
logging.warning(
f"{ac_type} is not capable of 'Nothing', using default task '{group.task}'"
)
else:
fallback_part = (
f" nor any of the following fall-back tasks: {[task.name for task in fallback_tasks]}"
if fallback_tasks
else ""
return
if flight.roster.members and flight.roster.members[0].is_player:
group.task = (
flight.unit_type.dcs_unit_type.task_default.name
if flight.unit_type.dcs_unit_type.task_default
else group.task # even if this is incompatible, if it's a client we don't really care...
)
raise RuntimeError(
f"{ac_type} is neither capable of {preferred_task.name}"
f"{fallback_part}. Can't generate {flight.flight_type} flight."
logging.warning(
f"Client override: {ac_type} is not capable of '{preferred_task}', using default task '{group.task}'"
)
return
fallback_part = (
f" nor any of the following fall-back tasks: {[task.name for task in fallback_tasks]}"
if fallback_tasks
else ""
)
raise RuntimeError(
f"{ac_type} is neither capable of {preferred_task.name}"
f"{fallback_part}. Can't generate {flight.flight_type} flight."
)

View File

@@ -497,21 +497,20 @@ class FlightGroupSpawner:
) -> Optional[FlyingGroup[Any]]:
is_airbase = False
is_roadbase = False
ground_spawn = None
try:
if is_large:
if len(self.ground_spawns_large[cp]) > 0:
ground_spawn = self.ground_spawns_large[cp].pop()
is_airbase = True
else:
if len(self.ground_spawns_roadbase[cp]) > 0:
ground_spawn = self.ground_spawns_roadbase[cp].pop()
is_roadbase = True
if len(self.ground_spawns[cp]) > 0:
ground_spawn = self.ground_spawns[cp].pop()
is_airbase = True
except IndexError as ex:
logging.warning("Not enough ground spawn slots available at " + str(ex))
if not is_large and len(self.ground_spawns_roadbase[cp]) > 0:
ground_spawn = self.ground_spawns_roadbase[cp].pop()
is_roadbase = True
elif not is_large and len(self.ground_spawns[cp]) > 0:
ground_spawn = self.ground_spawns[cp].pop()
is_airbase = True
elif len(self.ground_spawns_large[cp]) > 0:
ground_spawn = self.ground_spawns_large[cp].pop()
is_airbase = True
if ground_spawn is None:
logging.warning("Not enough ground spawn slots available at " + cp.name)
return None
group = self._generate_at_group(name, ground_spawn[0])
@@ -581,14 +580,12 @@ class FlightGroupSpawner:
for i in range(self.flight.count - 1):
try:
terrain = cp.coalition.game.theater.terrain
if is_large:
if len(self.ground_spawns_large[cp]) > 0:
ground_spawn = self.ground_spawns_large[cp].pop()
else:
if len(self.ground_spawns_roadbase[cp]) > 0:
ground_spawn = self.ground_spawns_roadbase[cp].pop()
else:
ground_spawn = self.ground_spawns[cp].pop()
if not is_large and len(self.ground_spawns_roadbase[cp]) > 0:
ground_spawn = self.ground_spawns_roadbase[cp].pop()
elif not is_large and len(self.ground_spawns[cp]) > 0:
ground_spawn = self.ground_spawns[cp].pop()
elif len(self.ground_spawns_large[cp]) > 0:
ground_spawn = self.ground_spawns_large[cp].pop()
group.units[1 + i].position = Point(
ground_spawn[0].x, ground_spawn[0].y, terrain=terrain
)

View File

@@ -0,0 +1,35 @@
from dcs.point import MovingPoint
from dcs.task import (
OptECMUsing,
ControlledTask,
Targets,
EngageTargetsInZone,
)
from game.utils import nautical_miles
from .pydcswaypointbuilder import PydcsWaypointBuilder
class ArmedReconIngressBuilder(PydcsWaypointBuilder):
def add_tasks(self, waypoint: MovingPoint) -> None:
self.register_special_ingress_points()
# Preemptively use ECM to better avoid getting swatted.
ecm_option = OptECMUsing(value=OptECMUsing.Values.UseIfDetectedLockByRadar)
waypoint.tasks.append(ecm_option)
waypoint.add_task(
ControlledTask(
EngageTargetsInZone(
position=self.flight.flight_plan.tot_waypoint.position,
radius=int(
nautical_miles(
self.flight.coalition.game.settings.armed_recon_engagement_range_distance
).meters
),
targets=[
Targets.All.GroundUnits,
Targets.All.Air.Helicopters,
],
)
)
)

View File

@@ -17,7 +17,7 @@ class HoldPointBuilder(PydcsWaypointBuilder):
loiter = ControlledTask(
OrbitAction(
altitude=waypoint.alt,
speed=speed.meters_per_second,
speed=speed.kph,
pattern=OrbitAction.OrbitPattern.Circle,
)
)

View File

@@ -8,7 +8,6 @@ from dcs.task import (
OptECMUsing,
OptFormation,
Targets,
OptROE,
SetUnlimitedFuelCommand,
)
@@ -94,12 +93,6 @@ class JoinPointBuilder(PydcsWaypointBuilder):
max_dist: float = 30.0,
vertical_spacing: float = 2000.0,
) -> None:
if self.flight.is_helo:
# Make helicopters a bit more aggressive
waypoint.tasks.append(OptROE(value=OptROE.Values.OpenFireWeaponFree))
else:
waypoint.tasks.append(OptROE(value=OptROE.Values.OpenFire))
rx = (random.random() + 0.1) * 333
ry = feet(vertical_spacing).meters
rz = (random.random() + 0.1) * 166 * random.choice([-1, 1])

View File

@@ -3,7 +3,7 @@ import logging
from dcs.point import MovingPoint
from dcs.task import EngageTargetsInZone, Targets
from game.theater import Airfield
from game.theater import Airfield, Fob
from game.utils import nautical_miles
from .pydcswaypointbuilder import PydcsWaypointBuilder
@@ -12,7 +12,7 @@ class OcaAircraftIngressBuilder(PydcsWaypointBuilder):
def add_tasks(self, waypoint: MovingPoint) -> None:
target = self.package.target
self.register_special_ingress_points()
if not isinstance(target, Airfield):
if not (isinstance(target, Airfield) or isinstance(target, Fob)):
logging.error(
"Unexpected target type for OCA Strike mission: %s",
target.__class__.__name__,

View File

@@ -2,9 +2,8 @@ from dcs.point import MovingPoint
from dcs.task import (
OptECMUsing,
ControlledTask,
EngageTargets,
Targets,
OptROE,
EngageTargetsInZone,
)
from game.utils import nautical_miles
@@ -14,16 +13,15 @@ from .pydcswaypointbuilder import PydcsWaypointBuilder
class SeadSweepIngressBuilder(PydcsWaypointBuilder):
def add_tasks(self, waypoint: MovingPoint) -> None:
self.register_special_ingress_points()
waypoint.tasks.append(OptROE(value=OptROE.Values.OpenFireWeaponFree))
# Preemptively use ECM to better avoid getting swatted.
ecm_option = OptECMUsing(value=OptECMUsing.Values.UseIfDetectedLockByRadar)
waypoint.tasks.append(ecm_option)
waypoint.add_task(
ControlledTask(
EngageTargets(
# TODO: From doctrine.
max_distance=int(
EngageTargetsInZone(
position=self.flight.flight_plan.tot_waypoint.position,
radius=int(
nautical_miles(
self.flight.coalition.game.settings.sead_sweep_engagement_range_distance
).meters

View File

@@ -22,6 +22,7 @@ from game.settings import Settings
from game.utils import pairwise
from .airassaultingress import AirAssaultIngressBuilder
from .antishipingress import AntiShipIngressBuilder
from .armedreconingress import ArmedReconIngressBuilder
from .baiingress import BaiIngressBuilder
from .casingress import CasIngressBuilder
from .deadingress import DeadIngressBuilder
@@ -136,6 +137,7 @@ class WaypointGenerator:
FlightWaypointType.DROPOFF_ZONE: LandingZoneBuilder,
FlightWaypointType.INGRESS_AIR_ASSAULT: AirAssaultIngressBuilder,
FlightWaypointType.INGRESS_ANTI_SHIP: AntiShipIngressBuilder,
FlightWaypointType.INGRESS_ARMED_RECON: ArmedReconIngressBuilder,
FlightWaypointType.INGRESS_BAI: BaiIngressBuilder,
FlightWaypointType.INGRESS_CAS: CasIngressBuilder,
FlightWaypointType.INGRESS_DEAD: DeadIngressBuilder,
@@ -203,7 +205,10 @@ class WaypointGenerator:
not self.flight.state.in_flight
and self.flight.state.spawn_type is not StartType.RUNWAY
and self.flight.departure.is_fleet
and not self.flight.client_count
and not (
self.flight.client_count
and self.flight.coalition.game.settings.player_flights_sixpack
)
):
# https://github.com/dcs-liberation/dcs_liberation/issues/1309
# Without a delay, AI aircraft will be spawned on the sixpack, which other