mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Generate common ingress/egress points.
This still isn't very good because it doesn't work well for anything but the automatically planned package. Instead, should be a part of the Package itself, generated the first time it is needed, and resettable by the user.
This commit is contained in:
parent
07cbaa3e70
commit
56a5864600
19
gen/ato.py
19
gen/ato.py
@ -11,7 +11,7 @@ the single CAP flight.
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
import logging
|
import logging
|
||||||
from typing import Dict, List
|
from typing import Dict, Iterator, List, Optional
|
||||||
|
|
||||||
from .flights.flight import Flight, FlightType
|
from .flights.flight import Flight, FlightType
|
||||||
from theater.missiontarget import MissionTarget
|
from theater.missiontarget import MissionTarget
|
||||||
@ -48,10 +48,9 @@ class Package:
|
|||||||
self.flights.remove(flight)
|
self.flights.remove(flight)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def package_description(self) -> str:
|
def primary_task(self) -> Optional[FlightType]:
|
||||||
"""Generates a package description based on flight composition."""
|
|
||||||
if not self.flights:
|
if not self.flights:
|
||||||
return "No mission"
|
return None
|
||||||
|
|
||||||
flight_counts: Dict[FlightType, int] = defaultdict(lambda: 0)
|
flight_counts: Dict[FlightType, int] = defaultdict(lambda: 0)
|
||||||
for flight in self.flights:
|
for flight in self.flights:
|
||||||
@ -84,13 +83,21 @@ class Package:
|
|||||||
]
|
]
|
||||||
for task in task_priorities:
|
for task in task_priorities:
|
||||||
if flight_counts[task]:
|
if flight_counts[task]:
|
||||||
return task.name
|
return task
|
||||||
|
|
||||||
# If we get here, our task_priorities list above is incomplete. Log the
|
# If we get here, our task_priorities list above is incomplete. Log the
|
||||||
# issue and return the type of *any* flight in the package.
|
# issue and return the type of *any* flight in the package.
|
||||||
some_mission = next(iter(self.flights)).flight_type
|
some_mission = next(iter(self.flights)).flight_type
|
||||||
logging.warning(f"Unhandled mission type: {some_mission}")
|
logging.warning(f"Unhandled mission type: {some_mission}")
|
||||||
return some_mission.name
|
return some_mission
|
||||||
|
|
||||||
|
@property
|
||||||
|
def package_description(self) -> str:
|
||||||
|
"""Generates a package description based on flight composition."""
|
||||||
|
task = self.primary_task
|
||||||
|
if task is None:
|
||||||
|
return "No mission"
|
||||||
|
return task.name
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
def __hash__(self) -> int:
|
||||||
# TODO: Far from perfect. Number packages?
|
# TODO: Far from perfect. Number packages?
|
||||||
|
|||||||
@ -414,8 +414,8 @@ class CoalitionMissionPlanner:
|
|||||||
return
|
return
|
||||||
|
|
||||||
package = builder.build()
|
package = builder.build()
|
||||||
|
builder = FlightPlanBuilder(self.game, self.is_player, package)
|
||||||
for flight in package.flights:
|
for flight in package.flights:
|
||||||
builder = FlightPlanBuilder(self.game, self.is_player)
|
|
||||||
builder.populate_flight_plan(flight, package.target)
|
builder.populate_flight_plan(flight, package.target)
|
||||||
self.ato.add_package(package)
|
self.ato.add_package(package)
|
||||||
|
|
||||||
|
|||||||
@ -11,10 +11,12 @@ import logging
|
|||||||
import random
|
import random
|
||||||
from typing import List, Optional, TYPE_CHECKING
|
from typing import List, Optional, TYPE_CHECKING
|
||||||
|
|
||||||
|
from dcs.mapping import Point
|
||||||
from dcs.unit import Unit
|
from dcs.unit import Unit
|
||||||
|
|
||||||
from game.data.doctrine import Doctrine, MODERN_DOCTRINE
|
from game.data.doctrine import Doctrine, MODERN_DOCTRINE
|
||||||
from game.utils import nm_to_meter
|
from game.utils import nm_to_meter
|
||||||
|
from gen.ato import Package
|
||||||
from theater import ControlPoint, FrontLine, MissionTarget, TheaterGroundObject
|
from theater import ControlPoint, FrontLine, MissionTarget, TheaterGroundObject
|
||||||
from .closestairfields import ObjectiveDistanceCache
|
from .closestairfields import ObjectiveDistanceCache
|
||||||
from .flight import Flight, FlightType, FlightWaypoint, FlightWaypointType
|
from .flight import Flight, FlightType, FlightWaypoint, FlightWaypointType
|
||||||
@ -36,8 +38,10 @@ class InvalidObjectiveLocation(RuntimeError):
|
|||||||
class FlightPlanBuilder:
|
class FlightPlanBuilder:
|
||||||
"""Generates flight plans for flights."""
|
"""Generates flight plans for flights."""
|
||||||
|
|
||||||
def __init__(self, game: Game, is_player: bool) -> None:
|
def __init__(self, game: Game, is_player: bool,
|
||||||
|
package: Optional[Package] = None) -> None:
|
||||||
self.game = game
|
self.game = game
|
||||||
|
self.package = package
|
||||||
self.is_player = is_player
|
self.is_player = is_player
|
||||||
if is_player:
|
if is_player:
|
||||||
faction = self.game.player_faction
|
faction = self.game.player_faction
|
||||||
@ -110,23 +114,9 @@ class FlightPlanBuilder:
|
|||||||
# TODO: Stop clobbering flight type.
|
# TODO: Stop clobbering flight type.
|
||||||
flight.flight_type = FlightType.STRIKE
|
flight.flight_type = FlightType.STRIKE
|
||||||
|
|
||||||
heading = flight.from_cp.position.heading_between_point(
|
|
||||||
location.position
|
|
||||||
)
|
|
||||||
ingress_heading = heading - 180 + 25
|
|
||||||
|
|
||||||
ingress_pos = location.position.point_from_heading(
|
|
||||||
ingress_heading, self.doctrine.ingress_egress_distance
|
|
||||||
)
|
|
||||||
|
|
||||||
egress_heading = heading - 180 - 25
|
|
||||||
egress_pos = location.position.point_from_heading(
|
|
||||||
egress_heading, self.doctrine.ingress_egress_distance
|
|
||||||
)
|
|
||||||
|
|
||||||
builder = WaypointBuilder(self.doctrine)
|
builder = WaypointBuilder(self.doctrine)
|
||||||
builder.ascent(flight.from_cp)
|
builder.ascent(flight.from_cp)
|
||||||
builder.ingress_strike(ingress_pos, location)
|
builder.ingress_strike(self.ingress_point(flight, location), location)
|
||||||
|
|
||||||
if len(location.groups) > 0 and location.dcs_identifier == "AA":
|
if len(location.groups) > 0 and location.dcs_identifier == "AA":
|
||||||
# TODO: Replace with DEAD?
|
# TODO: Replace with DEAD?
|
||||||
@ -153,7 +143,7 @@ class FlightPlanBuilder:
|
|||||||
location
|
location
|
||||||
)
|
)
|
||||||
|
|
||||||
builder.egress(egress_pos, location)
|
builder.egress(self.egress_point(flight, location), location)
|
||||||
builder.rtb(flight.from_cp)
|
builder.rtb(flight.from_cp)
|
||||||
|
|
||||||
flight.points = builder.build()
|
flight.points = builder.build()
|
||||||
@ -267,23 +257,9 @@ class FlightPlanBuilder:
|
|||||||
|
|
||||||
flight.flight_type = random.choice([FlightType.SEAD, FlightType.DEAD])
|
flight.flight_type = random.choice([FlightType.SEAD, FlightType.DEAD])
|
||||||
|
|
||||||
heading = flight.from_cp.position.heading_between_point(
|
|
||||||
location.position
|
|
||||||
)
|
|
||||||
ingress_heading = heading - 180 + 25
|
|
||||||
|
|
||||||
ingress_pos = location.position.point_from_heading(
|
|
||||||
ingress_heading, self.doctrine.ingress_egress_distance
|
|
||||||
)
|
|
||||||
|
|
||||||
egress_heading = heading - 180 - 25
|
|
||||||
egress_pos = location.position.point_from_heading(
|
|
||||||
egress_heading, self.doctrine.ingress_egress_distance
|
|
||||||
)
|
|
||||||
|
|
||||||
builder = WaypointBuilder(self.doctrine)
|
builder = WaypointBuilder(self.doctrine)
|
||||||
builder.ascent(flight.from_cp)
|
builder.ascent(flight.from_cp)
|
||||||
builder.ingress_sead(ingress_pos, location)
|
builder.ingress_sead(self.ingress_point(flight, location), location)
|
||||||
|
|
||||||
# TODO: Unify these.
|
# TODO: Unify these.
|
||||||
# There doesn't seem to be any reason to treat the UI fragged missions
|
# There doesn't seem to be any reason to treat the UI fragged missions
|
||||||
@ -307,7 +283,7 @@ class FlightPlanBuilder:
|
|||||||
else:
|
else:
|
||||||
builder.sead_area(location)
|
builder.sead_area(location)
|
||||||
|
|
||||||
builder.egress(egress_pos, location)
|
builder.egress(self.egress_point(flight, location), location)
|
||||||
builder.rtb(flight.from_cp)
|
builder.rtb(flight.from_cp)
|
||||||
|
|
||||||
flight.points = builder.build()
|
flight.points = builder.build()
|
||||||
@ -319,19 +295,6 @@ class FlightPlanBuilder:
|
|||||||
# Packages should determine some common points like push, ingress,
|
# Packages should determine some common points like push, ingress,
|
||||||
# egress, and split points ahead of time so they can be shared by all
|
# egress, and split points ahead of time so they can be shared by all
|
||||||
# flights.
|
# flights.
|
||||||
heading = flight.from_cp.position.heading_between_point(
|
|
||||||
location.position
|
|
||||||
)
|
|
||||||
ingress_heading = heading - 180 + 25
|
|
||||||
|
|
||||||
ingress_pos = location.position.point_from_heading(
|
|
||||||
ingress_heading, self.doctrine.ingress_egress_distance
|
|
||||||
)
|
|
||||||
|
|
||||||
egress_heading = heading - 180 - 25
|
|
||||||
egress_pos = location.position.point_from_heading(
|
|
||||||
egress_heading, self.doctrine.ingress_egress_distance
|
|
||||||
)
|
|
||||||
|
|
||||||
patrol_alt = random.randint(
|
patrol_alt = random.randint(
|
||||||
self.doctrine.min_patrol_altitude,
|
self.doctrine.min_patrol_altitude,
|
||||||
@ -340,7 +303,8 @@ class FlightPlanBuilder:
|
|||||||
|
|
||||||
builder = WaypointBuilder(self.doctrine)
|
builder = WaypointBuilder(self.doctrine)
|
||||||
builder.ascent(flight.from_cp)
|
builder.ascent(flight.from_cp)
|
||||||
builder.race_track(ingress_pos, egress_pos, patrol_alt)
|
builder.race_track(self.ingress_point(flight, location),
|
||||||
|
self.egress_point(flight, location), patrol_alt)
|
||||||
builder.rtb(flight.from_cp)
|
builder.rtb(flight.from_cp)
|
||||||
|
|
||||||
flight.points = builder.build()
|
flight.points = builder.build()
|
||||||
@ -396,8 +360,7 @@ class FlightPlanBuilder:
|
|||||||
builder.descent(arrival)
|
builder.descent(arrival)
|
||||||
return builder.build()[0]
|
return builder.build()[0]
|
||||||
|
|
||||||
@staticmethod
|
def generate_rtb_waypoint(self, arrival: ControlPoint) -> FlightWaypoint:
|
||||||
def generate_rtb_waypoint(arrival: ControlPoint) -> FlightWaypoint:
|
|
||||||
"""Generate RTB landing point.
|
"""Generate RTB landing point.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -406,3 +369,38 @@ class FlightPlanBuilder:
|
|||||||
builder = WaypointBuilder(self.doctrine)
|
builder = WaypointBuilder(self.doctrine)
|
||||||
builder.land(arrival)
|
builder.land(arrival)
|
||||||
return builder.build()[0]
|
return builder.build()[0]
|
||||||
|
|
||||||
|
def ingress_point(self, flight: Flight, target: MissionTarget) -> Point:
|
||||||
|
heading = self._heading_to_package_airfield(flight, target)
|
||||||
|
return target.position.point_from_heading(
|
||||||
|
heading - 180 + 25, self.doctrine.ingress_egress_distance
|
||||||
|
)
|
||||||
|
|
||||||
|
def egress_point(self, flight: Flight, target: MissionTarget) -> Point:
|
||||||
|
heading = self._heading_to_package_airfield(flight, target)
|
||||||
|
return target.position.point_from_heading(
|
||||||
|
heading - 180 - 25, self.doctrine.ingress_egress_distance
|
||||||
|
)
|
||||||
|
|
||||||
|
def _heading_to_package_airfield(self, flight: Flight,
|
||||||
|
target: MissionTarget) -> int:
|
||||||
|
airfield = self.package_airfield(flight, target)
|
||||||
|
return airfield.position.heading_between_point(target.position)
|
||||||
|
|
||||||
|
# TODO: Set ingress/egress/join/split points in the Package.
|
||||||
|
def package_airfield(self, flight: Flight,
|
||||||
|
target: MissionTarget) -> ControlPoint:
|
||||||
|
# The package airfield is either the flight's airfield (when there is no
|
||||||
|
# package) or the closest airfield to the objective that is the
|
||||||
|
# departure airfield for some flight in the package.
|
||||||
|
if self.package is None:
|
||||||
|
return flight.from_cp
|
||||||
|
|
||||||
|
cache = ObjectiveDistanceCache.get_closest_airfields(target)
|
||||||
|
for airfield in cache.closest_airfields:
|
||||||
|
for flight in self.package.flights:
|
||||||
|
if flight.from_cp == airfield:
|
||||||
|
return airfield
|
||||||
|
raise RuntimeError(
|
||||||
|
"Could not find any airfield assigned to this package"
|
||||||
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user