mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Check for available aircraft as task precondition.
This makes it so that the mission planning effects are applied only if the package can be fulfilled. For example, breakthrough will be used only if all the BAI missions were fulfilled, not if they will *attempt* to be fulfilled.
This commit is contained in:
@@ -15,5 +15,5 @@ class CaptureBase(CompoundTask[TheaterState]):
|
||||
front_line: FrontLine
|
||||
|
||||
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
|
||||
yield [BreakthroughAttack(self.front_line, state.player)]
|
||||
yield [BreakthroughAttack(self.front_line, state.context.coalition.player)]
|
||||
yield [DestroyEnemyGroundUnits(self.front_line)]
|
||||
|
||||
@@ -14,6 +14,6 @@ class DefendBase(CompoundTask[TheaterState]):
|
||||
front_line: FrontLine
|
||||
|
||||
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
|
||||
yield [DefensiveStance(self.front_line, state.player)]
|
||||
yield [RetreatStance(self.front_line, state.player)]
|
||||
yield [DefensiveStance(self.front_line, state.context.coalition.player)]
|
||||
yield [RetreatStance(self.front_line, state.context.coalition.player)]
|
||||
yield [PlanCas(self.front_line)]
|
||||
|
||||
@@ -14,6 +14,6 @@ class DestroyEnemyGroundUnits(CompoundTask[TheaterState]):
|
||||
front_line: FrontLine
|
||||
|
||||
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
|
||||
yield [EliminationAttack(self.front_line, state.player)]
|
||||
yield [AggressiveAttack(self.front_line, state.player)]
|
||||
yield [EliminationAttack(self.front_line, state.context.coalition.player)]
|
||||
yield [AggressiveAttack(self.front_line, state.context.coalition.player)]
|
||||
yield [PlanCas(self.front_line)]
|
||||
|
||||
@@ -7,5 +7,6 @@ from game.htn import CompoundTask, Method
|
||||
|
||||
class ProtectAirSpace(CompoundTask[TheaterState]):
|
||||
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
|
||||
for cp in state.vulnerable_control_points:
|
||||
yield [PlanBarcap(cp, state.barcap_rounds)]
|
||||
for cp, needed in state.barcaps_needed.items():
|
||||
if needed > 0:
|
||||
yield [PlanBarcap(cp)]
|
||||
|
||||
@@ -6,12 +6,11 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from game.commander.tasks.theatercommandertask import TheaterCommanderTask
|
||||
from game.commander.theaterstate import TheaterState
|
||||
from game.profiling import MultiEventTracer
|
||||
from game.theater import FrontLine
|
||||
from gen.ground_forces.combat_stance import CombatStance
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gen.flights.ai_flight_planner import CoalitionMissionPlanner
|
||||
from game.coalition import Coalition
|
||||
|
||||
|
||||
class FrontLineStanceTask(TheaterCommanderTask, ABC):
|
||||
@@ -27,7 +26,10 @@ class FrontLineStanceTask(TheaterCommanderTask, ABC):
|
||||
|
||||
@staticmethod
|
||||
def management_allowed(state: TheaterState) -> bool:
|
||||
return not state.player or state.stance_automation_enabled
|
||||
return (
|
||||
not state.context.coalition.player
|
||||
or state.context.settings.automate_front_line_stance
|
||||
)
|
||||
|
||||
def better_stance_already_set(self, state: TheaterState) -> bool:
|
||||
current_stance = state.front_line_stances[self.front_line]
|
||||
@@ -69,7 +71,5 @@ class FrontLineStanceTask(TheaterCommanderTask, ABC):
|
||||
def apply_effects(self, state: TheaterState) -> None:
|
||||
state.front_line_stances[self.front_line] = self.stance
|
||||
|
||||
def execute(
|
||||
self, mission_planner: CoalitionMissionPlanner, tracer: MultiEventTracer
|
||||
) -> None:
|
||||
def execute(self, coalition: Coalition) -> None:
|
||||
self.friendly_cp.stances[self.enemy_cp.id] = self.stance
|
||||
|
||||
@@ -8,18 +8,19 @@ from enum import unique, IntEnum, auto
|
||||
from typing import TYPE_CHECKING, Optional, Generic, TypeVar, Iterator, Union
|
||||
|
||||
from game.commander.missionproposals import ProposedFlight, EscortType, ProposedMission
|
||||
from game.commander.packagefulfiller import PackageFulfiller
|
||||
from game.commander.tasks.theatercommandertask import TheaterCommanderTask
|
||||
from game.commander.theaterstate import TheaterState
|
||||
from game.data.doctrine import Doctrine
|
||||
from game.profiling import MultiEventTracer
|
||||
from game.settings import AutoAtoBehavior
|
||||
from game.theater import MissionTarget
|
||||
from game.theater.theatergroundobject import IadsGroundObject, NavalGroundObject
|
||||
from game.utils import Distance, meters
|
||||
from gen import Package
|
||||
from gen.flights.flight import FlightType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gen.flights.ai_flight_planner import CoalitionMissionPlanner
|
||||
|
||||
from game.coalition import Coalition
|
||||
|
||||
MissionTargetT = TypeVar("MissionTargetT", bound=MissionTarget)
|
||||
|
||||
@@ -36,18 +37,26 @@ class RangeType(IntEnum):
|
||||
class PackagePlanningTask(TheaterCommanderTask, Generic[MissionTargetT]):
|
||||
target: MissionTargetT
|
||||
flights: list[ProposedFlight] = field(init=False)
|
||||
package: Optional[Package] = field(init=False, default=None)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
self.flights = []
|
||||
self.package = Package(self.target)
|
||||
|
||||
def preconditions_met(self, state: TheaterState) -> bool:
|
||||
return not state.player or state.ato_automation_enabled
|
||||
if (
|
||||
state.context.coalition.player
|
||||
and state.context.settings.auto_ato_behavior is AutoAtoBehavior.Disabled
|
||||
):
|
||||
return False
|
||||
return self.fulfill_mission(state)
|
||||
|
||||
def execute(
|
||||
self, mission_planner: CoalitionMissionPlanner, tracer: MultiEventTracer
|
||||
) -> None:
|
||||
self.propose_flights(mission_planner.doctrine)
|
||||
mission_planner.plan_mission(ProposedMission(self.target, self.flights), tracer)
|
||||
def execute(self, coalition: Coalition) -> None:
|
||||
if self.package is None:
|
||||
raise RuntimeError("Attempted to execute failed package planning task")
|
||||
for flight in self.package.flights:
|
||||
coalition.aircraft_inventory.claim_for_flight(flight)
|
||||
coalition.ato.add_package(self.package)
|
||||
|
||||
@abstractmethod
|
||||
def propose_flights(self, doctrine: Doctrine) -> None:
|
||||
@@ -70,6 +79,19 @@ class PackagePlanningTask(TheaterCommanderTask, Generic[MissionTargetT]):
|
||||
def asap(self) -> bool:
|
||||
return False
|
||||
|
||||
def fulfill_mission(self, state: TheaterState) -> bool:
|
||||
self.propose_flights(state.context.coalition.doctrine)
|
||||
fulfiller = PackageFulfiller(
|
||||
state.context.coalition,
|
||||
state.context.theater,
|
||||
state.available_aircraft,
|
||||
state.context.settings,
|
||||
)
|
||||
self.package = fulfiller.plan_mission(
|
||||
ProposedMission(self.target, self.flights), state.context.tracer
|
||||
)
|
||||
return self.package is not None
|
||||
|
||||
def propose_common_escorts(self, doctrine: Doctrine) -> None:
|
||||
self.propose_flight(
|
||||
FlightType.SEAD_ESCORT,
|
||||
|
||||
@@ -1,46 +1,23 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from game.commander.missionproposals import ProposedMission, ProposedFlight
|
||||
from game.commander.tasks.theatercommandertask import TheaterCommanderTask
|
||||
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
||||
from game.commander.theaterstate import TheaterState
|
||||
from game.profiling import MultiEventTracer
|
||||
from game.data.doctrine import Doctrine
|
||||
from game.theater import ControlPoint
|
||||
from gen.flights.flight import FlightType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gen.flights.ai_flight_planner import CoalitionMissionPlanner
|
||||
|
||||
|
||||
@dataclass
|
||||
class PlanBarcap(TheaterCommanderTask):
|
||||
target: ControlPoint
|
||||
rounds: int
|
||||
|
||||
class PlanBarcap(PackagePlanningTask[ControlPoint]):
|
||||
def preconditions_met(self, state: TheaterState) -> bool:
|
||||
if state.player and not state.ato_automation_enabled:
|
||||
if not super().preconditions_met(state):
|
||||
return False
|
||||
return self.target in state.vulnerable_control_points
|
||||
return state.barcaps_needed[self.target] > 0
|
||||
|
||||
def apply_effects(self, state: TheaterState) -> None:
|
||||
state.vulnerable_control_points.remove(self.target)
|
||||
state.barcaps_needed[self.target] -= 1
|
||||
|
||||
def execute(
|
||||
self, mission_planner: CoalitionMissionPlanner, tracer: MultiEventTracer
|
||||
) -> None:
|
||||
for _ in range(self.rounds):
|
||||
mission_planner.plan_mission(
|
||||
ProposedMission(
|
||||
self.target,
|
||||
[
|
||||
ProposedFlight(
|
||||
FlightType.BARCAP,
|
||||
2,
|
||||
mission_planner.doctrine.mission_ranges.cap,
|
||||
),
|
||||
],
|
||||
),
|
||||
tracer,
|
||||
)
|
||||
def propose_flights(self, doctrine: Doctrine) -> None:
|
||||
self.propose_flight(FlightType.BARCAP, 2, doctrine.mission_ranges.cap)
|
||||
|
||||
@@ -5,16 +5,12 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from game.commander.theaterstate import TheaterState
|
||||
from game.htn import PrimitiveTask
|
||||
from game.profiling import MultiEventTracer
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gen.flights.ai_flight_planner import CoalitionMissionPlanner
|
||||
from game.coalition import Coalition
|
||||
|
||||
|
||||
# TODO: Refactor so that we don't need to call up to the mission planner.
|
||||
class TheaterCommanderTask(PrimitiveTask[TheaterState]):
|
||||
@abstractmethod
|
||||
def execute(
|
||||
self, mission_planner: CoalitionMissionPlanner, tracer: MultiEventTracer
|
||||
) -> None:
|
||||
def execute(self, coalition: Coalition) -> None:
|
||||
...
|
||||
|
||||
Reference in New Issue
Block a user