mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Attack detecting radars with low priority.
IADS that are in detection range (but not attack range) of missions will be targeted at very low priority. These will typically only be planned when no other targets are in range.
This commit is contained in:
parent
78514b6c2e
commit
c0cc5657a7
@ -14,7 +14,6 @@ from game.theater import (
|
|||||||
Airfield,
|
Airfield,
|
||||||
)
|
)
|
||||||
from game.theater.theatergroundobject import (
|
from game.theater.theatergroundobject import (
|
||||||
VehicleGroupGroundObject,
|
|
||||||
NavalGroundObject,
|
NavalGroundObject,
|
||||||
BuildingGroundObject,
|
BuildingGroundObject,
|
||||||
IadsGroundObject,
|
IadsGroundObject,
|
||||||
@ -50,18 +49,6 @@ class ObjectiveFinder:
|
|||||||
if isinstance(ground_object, IadsGroundObject):
|
if isinstance(ground_object, IadsGroundObject):
|
||||||
yield ground_object
|
yield ground_object
|
||||||
|
|
||||||
def enemy_vehicle_groups(self) -> Iterator[VehicleGroupGroundObject]:
|
|
||||||
"""Iterates over all enemy vehicle groups."""
|
|
||||||
for cp in self.enemy_control_points():
|
|
||||||
for ground_object in cp.ground_objects:
|
|
||||||
if not isinstance(ground_object, VehicleGroupGroundObject):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if ground_object.is_dead:
|
|
||||||
continue
|
|
||||||
|
|
||||||
yield ground_object
|
|
||||||
|
|
||||||
def enemy_ships(self) -> Iterator[NavalGroundObject]:
|
def enemy_ships(self) -> Iterator[NavalGroundObject]:
|
||||||
for cp in self.enemy_control_points():
|
for cp in self.enemy_control_points():
|
||||||
for ground_object in cp.ground_objects:
|
for ground_object in cp.ground_objects:
|
||||||
|
|||||||
@ -1,16 +1,24 @@
|
|||||||
from collections import Iterator
|
from collections import Iterator
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
from game.commander.tasks.primitive.antiship import PlanAntiShip
|
from game.commander.tasks.primitive.antiship import PlanAntiShip
|
||||||
from game.commander.tasks.primitive.dead import PlanDead
|
from game.commander.tasks.primitive.dead import PlanDead
|
||||||
from game.commander.theaterstate import TheaterState
|
from game.commander.theaterstate import TheaterState
|
||||||
from game.htn import CompoundTask, Method
|
from game.htn import CompoundTask, Method
|
||||||
from game.theater.theatergroundobject import IadsGroundObject
|
from game.theater.theatergroundobject import IadsGroundObject, NavalGroundObject
|
||||||
|
|
||||||
|
|
||||||
class DegradeIads(CompoundTask[TheaterState]):
|
class DegradeIads(CompoundTask[TheaterState]):
|
||||||
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
|
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
|
||||||
for air_defense in state.threatening_air_defenses:
|
for air_defense in state.threatening_air_defenses:
|
||||||
if isinstance(air_defense, IadsGroundObject):
|
yield [self.plan_against(air_defense)]
|
||||||
yield [PlanDead(air_defense)]
|
for detector in state.detecting_air_defenses:
|
||||||
else:
|
yield [self.plan_against(detector)]
|
||||||
yield [PlanAntiShip(air_defense)]
|
|
||||||
|
@staticmethod
|
||||||
|
def plan_against(
|
||||||
|
target: Union[IadsGroundObject, NavalGroundObject]
|
||||||
|
) -> Union[PlanDead, PlanAntiShip]:
|
||||||
|
if isinstance(target, IadsGroundObject):
|
||||||
|
return PlanDead(target)
|
||||||
|
return PlanAntiShip(target)
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import itertools
|
|||||||
import operator
|
import operator
|
||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
from enum import unique, IntEnum, auto
|
||||||
from typing import TYPE_CHECKING, Optional, Generic, TypeVar, Iterator, Union
|
from typing import TYPE_CHECKING, Optional, Generic, TypeVar, Iterator, Union
|
||||||
|
|
||||||
from game.commander.missionproposals import ProposedFlight, EscortType, ProposedMission
|
from game.commander.missionproposals import ProposedFlight, EscortType, ProposedMission
|
||||||
@ -23,6 +24,12 @@ if TYPE_CHECKING:
|
|||||||
MissionTargetT = TypeVar("MissionTargetT", bound=MissionTarget)
|
MissionTargetT = TypeVar("MissionTargetT", bound=MissionTarget)
|
||||||
|
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class RangeType(IntEnum):
|
||||||
|
Detection = auto()
|
||||||
|
Threat = auto()
|
||||||
|
|
||||||
|
|
||||||
# TODO: Refactor so that we don't need to call up to the mission planner.
|
# TODO: Refactor so that we don't need to call up to the mission planner.
|
||||||
# Bypass type checker due to https://github.com/python/mypy/issues/5374
|
# Bypass type checker due to https://github.com/python/mypy/issues/5374
|
||||||
@dataclass # type: ignore
|
@dataclass # type: ignore
|
||||||
@ -75,8 +82,8 @@ class PackagePlanningTask(TheaterCommanderTask, Generic[MissionTargetT]):
|
|||||||
EscortType.AirToAir,
|
EscortType.AirToAir,
|
||||||
)
|
)
|
||||||
|
|
||||||
def iter_iads_threats(
|
def iter_iads_ranges(
|
||||||
self, state: TheaterState
|
self, state: TheaterState, range_type: RangeType
|
||||||
) -> Iterator[Union[IadsGroundObject, NavalGroundObject]]:
|
) -> Iterator[Union[IadsGroundObject, NavalGroundObject]]:
|
||||||
target_ranges: list[
|
target_ranges: list[
|
||||||
tuple[Union[IadsGroundObject, NavalGroundObject], Distance]
|
tuple[Union[IadsGroundObject, NavalGroundObject], Distance]
|
||||||
@ -86,15 +93,21 @@ class PackagePlanningTask(TheaterCommanderTask, Generic[MissionTargetT]):
|
|||||||
] = itertools.chain(state.enemy_air_defenses, state.enemy_ships)
|
] = itertools.chain(state.enemy_air_defenses, state.enemy_ships)
|
||||||
for target in all_iads:
|
for target in all_iads:
|
||||||
distance = meters(target.distance_to(self.target))
|
distance = meters(target.distance_to(self.target))
|
||||||
threat_range = target.max_threat_range()
|
if range_type is RangeType.Detection:
|
||||||
if not threat_range:
|
target_range = target.max_detection_range()
|
||||||
|
elif range_type is RangeType.Threat:
|
||||||
|
target_range = target.max_threat_range()
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unknown RangeType: {range_type}")
|
||||||
|
if not target_range:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# IADS out of range of our target area will have a positive
|
# IADS out of range of our target area will have a positive
|
||||||
# distance_to_threat and should be pruned. The rest have a decreasing
|
# distance_to_threat and should be pruned. The rest have a decreasing
|
||||||
# distance_to_threat as overlap increases. The most negative distance has
|
# distance_to_threat as overlap increases. The most negative distance has
|
||||||
# the greatest coverage of the target and should be treated as the highest
|
# the greatest coverage of the target and should be treated as the highest
|
||||||
# priority threat.
|
# priority threat.
|
||||||
distance_to_threat = distance - threat_range
|
distance_to_threat = distance - target_range
|
||||||
if distance_to_threat > meters(0):
|
if distance_to_threat > meters(0):
|
||||||
continue
|
continue
|
||||||
target_ranges.append((target, distance_to_threat))
|
target_ranges.append((target, distance_to_threat))
|
||||||
@ -104,11 +117,27 @@ class PackagePlanningTask(TheaterCommanderTask, Generic[MissionTargetT]):
|
|||||||
for target, _range in target_ranges:
|
for target, _range in target_ranges:
|
||||||
yield target
|
yield target
|
||||||
|
|
||||||
|
def iter_detecting_iads(
|
||||||
|
self, state: TheaterState
|
||||||
|
) -> Iterator[Union[IadsGroundObject, NavalGroundObject]]:
|
||||||
|
return self.iter_iads_ranges(state, RangeType.Detection)
|
||||||
|
|
||||||
|
def iter_iads_threats(
|
||||||
|
self, state: TheaterState
|
||||||
|
) -> Iterator[Union[IadsGroundObject, NavalGroundObject]]:
|
||||||
|
return self.iter_iads_ranges(state, RangeType.Threat)
|
||||||
|
|
||||||
def target_area_preconditions_met(
|
def target_area_preconditions_met(
|
||||||
self, state: TheaterState, ignore_iads: bool = False
|
self, state: TheaterState, ignore_iads: bool = False
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Checks if the target area has been cleared of threats."""
|
"""Checks if the target area has been cleared of threats."""
|
||||||
threatened = False
|
threatened = False
|
||||||
|
|
||||||
|
# Non-blocking, but analyzed so we can pick detectors worth eliminating.
|
||||||
|
for detector in self.iter_detecting_iads(state):
|
||||||
|
if detector not in state.detecting_air_defenses:
|
||||||
|
state.detecting_air_defenses.append(detector)
|
||||||
|
|
||||||
if not ignore_iads:
|
if not ignore_iads:
|
||||||
for iads_threat in self.iter_iads_threats(state):
|
for iads_threat in self.iter_iads_threats(state):
|
||||||
threatened = True
|
threatened = True
|
||||||
|
|||||||
@ -13,7 +13,10 @@ from gen.flights.flight import FlightType
|
|||||||
@dataclass
|
@dataclass
|
||||||
class PlanDead(PackagePlanningTask[IadsGroundObject]):
|
class PlanDead(PackagePlanningTask[IadsGroundObject]):
|
||||||
def preconditions_met(self, state: TheaterState) -> bool:
|
def preconditions_met(self, state: TheaterState) -> bool:
|
||||||
if self.target not in state.threatening_air_defenses:
|
if (
|
||||||
|
self.target not in state.threatening_air_defenses
|
||||||
|
and self.target not in state.detecting_air_defenses
|
||||||
|
):
|
||||||
return False
|
return False
|
||||||
return self.target_area_preconditions_met(state, ignore_iads=True)
|
return self.target_area_preconditions_met(state, ignore_iads=True)
|
||||||
|
|
||||||
|
|||||||
@ -30,6 +30,7 @@ class TheaterState(WorldState["TheaterState"]):
|
|||||||
refueling_targets: list[MissionTarget]
|
refueling_targets: list[MissionTarget]
|
||||||
enemy_air_defenses: list[IadsGroundObject]
|
enemy_air_defenses: list[IadsGroundObject]
|
||||||
threatening_air_defenses: list[Union[IadsGroundObject, NavalGroundObject]]
|
threatening_air_defenses: list[Union[IadsGroundObject, NavalGroundObject]]
|
||||||
|
detecting_air_defenses: list[Union[IadsGroundObject, NavalGroundObject]]
|
||||||
enemy_convoys: list[Convoy]
|
enemy_convoys: list[Convoy]
|
||||||
enemy_shipping: list[CargoShip]
|
enemy_shipping: list[CargoShip]
|
||||||
enemy_ships: list[NavalGroundObject]
|
enemy_ships: list[NavalGroundObject]
|
||||||
@ -49,12 +50,18 @@ class TheaterState(WorldState["TheaterState"]):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def eliminate_air_defense(self, target: IadsGroundObject) -> None:
|
def eliminate_air_defense(self, target: IadsGroundObject) -> None:
|
||||||
self.threatening_air_defenses.remove(target)
|
if target in self.threatening_air_defenses:
|
||||||
|
self.threatening_air_defenses.remove(target)
|
||||||
|
if target in self.detecting_air_defenses:
|
||||||
|
self.detecting_air_defenses.remove(target)
|
||||||
self.enemy_air_defenses.remove(target)
|
self.enemy_air_defenses.remove(target)
|
||||||
self._rebuild_threat_zones()
|
self._rebuild_threat_zones()
|
||||||
|
|
||||||
def eliminate_ship(self, target: NavalGroundObject) -> None:
|
def eliminate_ship(self, target: NavalGroundObject) -> None:
|
||||||
self.threatening_air_defenses.remove(target)
|
if target in self.threatening_air_defenses:
|
||||||
|
self.threatening_air_defenses.remove(target)
|
||||||
|
if target in self.detecting_air_defenses:
|
||||||
|
self.detecting_air_defenses.remove(target)
|
||||||
self.enemy_ships.remove(target)
|
self.enemy_ships.remove(target)
|
||||||
self._rebuild_threat_zones()
|
self._rebuild_threat_zones()
|
||||||
|
|
||||||
@ -83,6 +90,7 @@ class TheaterState(WorldState["TheaterState"]):
|
|||||||
# would add the IADS that prevented it from being planned to the list of
|
# would add the IADS that prevented it from being planned to the list of
|
||||||
# IADS threats so that DegradeIads will consider it a threat later.
|
# IADS threats so that DegradeIads will consider it a threat later.
|
||||||
threatening_air_defenses=self.threatening_air_defenses,
|
threatening_air_defenses=self.threatening_air_defenses,
|
||||||
|
detecting_air_defenses=self.detecting_air_defenses,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -95,6 +103,7 @@ class TheaterState(WorldState["TheaterState"]):
|
|||||||
refueling_targets=[finder.closest_friendly_control_point()],
|
refueling_targets=[finder.closest_friendly_control_point()],
|
||||||
enemy_air_defenses=list(finder.enemy_air_defenses()),
|
enemy_air_defenses=list(finder.enemy_air_defenses()),
|
||||||
threatening_air_defenses=[],
|
threatening_air_defenses=[],
|
||||||
|
detecting_air_defenses=[],
|
||||||
enemy_convoys=list(finder.convoys()),
|
enemy_convoys=list(finder.convoys()),
|
||||||
enemy_shipping=list(finder.cargo_ships()),
|
enemy_shipping=list(finder.cargo_ships()),
|
||||||
enemy_ships=list(finder.enemy_ships()),
|
enemy_ships=list(finder.enemy_ships()),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user