mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
An HTN (https://en.wikipedia.org/wiki/Hierarchical_task_network) is similar to a decision tree, but it is able to reset to an earlier stage if a subtask fails and tasks are able to account for the changes in world state caused by earlier tasks. Currently this just uses exactly the same strategy as before so we can prove the system, but it should make it simpler to improve on task planning.
74 lines
2.2 KiB
Python
74 lines
2.2 KiB
Python
from __future__ import annotations
|
|
|
|
from abc import abstractmethod
|
|
from dataclasses import dataclass, field
|
|
from typing import TYPE_CHECKING, Optional, Generic, TypeVar
|
|
|
|
from game.commander.missionproposals import ProposedFlight, EscortType, ProposedMission
|
|
from game.commander.theaterstate import TheaterState
|
|
from game.data.doctrine import Doctrine
|
|
from game.htn import PrimitiveTask
|
|
from game.profiling import MultiEventTracer
|
|
from game.theater import MissionTarget
|
|
from game.utils import Distance
|
|
from gen.flights.flight import FlightType
|
|
|
|
if TYPE_CHECKING:
|
|
from gen.flights.ai_flight_planner import CoalitionMissionPlanner
|
|
|
|
|
|
MissionTargetT = TypeVar("MissionTargetT", bound=MissionTarget)
|
|
|
|
|
|
# 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
|
|
@dataclass # type: ignore
|
|
class PackagePlanningTask(PrimitiveTask[TheaterState], Generic[MissionTargetT]):
|
|
target: MissionTargetT
|
|
flights: list[ProposedFlight] = field(init=False)
|
|
|
|
def __post_init__(self) -> None:
|
|
self.flights = []
|
|
|
|
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)
|
|
|
|
@abstractmethod
|
|
def propose_flights(self, doctrine: Doctrine) -> None:
|
|
...
|
|
|
|
def propose_flight(
|
|
self,
|
|
task: FlightType,
|
|
num_aircraft: int,
|
|
max_distance: Optional[Distance],
|
|
escort_type: Optional[EscortType] = None,
|
|
) -> None:
|
|
if max_distance is None:
|
|
max_distance = Distance.inf()
|
|
self.flights.append(
|
|
ProposedFlight(task, num_aircraft, max_distance, escort_type)
|
|
)
|
|
|
|
@property
|
|
def asap(self) -> bool:
|
|
return False
|
|
|
|
def propose_common_escorts(self, doctrine: Doctrine) -> None:
|
|
self.propose_flight(
|
|
FlightType.SEAD_ESCORT,
|
|
2,
|
|
doctrine.mission_ranges.offensive,
|
|
EscortType.Sead,
|
|
)
|
|
|
|
self.propose_flight(
|
|
FlightType.ESCORT,
|
|
2,
|
|
doctrine.mission_ranges.offensive,
|
|
EscortType.AirToAir,
|
|
)
|