Cleanup and refine airlift and airassault waypoints

- Drop Off and Pickup now correctly worded
- Helo waypoints now represent LandingZones for pickup and dropoff
This commit is contained in:
RndName 2022-11-01 21:01:48 +01:00
parent a47cb865fb
commit b4b9bbf476
7 changed files with 89 additions and 33 deletions

View File

@ -4,8 +4,7 @@ from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from typing import Iterator, TYPE_CHECKING, Type from typing import Iterator, TYPE_CHECKING, Type
from game.ato.flightplans.airlift import AirliftLayout from game.ato.flightplans.standard import StandardFlightPlan, StandardLayout
from game.ato.flightplans.standard import StandardFlightPlan
from game.theater.controlpoint import ControlPointType from game.theater.controlpoint import ControlPointType
from game.theater.missiontarget import MissionTarget from game.theater.missiontarget import MissionTarget
from game.utils import Distance, feet, meters from game.utils import Distance, feet, meters
@ -17,13 +16,18 @@ if TYPE_CHECKING:
@dataclass(frozen=True) @dataclass(frozen=True)
class AirAssaultLayout(AirliftLayout): class AirAssaultLayout(StandardLayout):
nav_to_pickup: list[FlightWaypoint]
pickup: FlightWaypoint | None
nav_to_drop_off: list[FlightWaypoint]
drop_off: FlightWaypoint
target: FlightWaypoint target: FlightWaypoint
nav_to_home: list[FlightWaypoint]
def iter_waypoints(self) -> Iterator[FlightWaypoint]: def iter_waypoints(self) -> Iterator[FlightWaypoint]:
yield self.departure yield self.departure
yield from self.nav_to_pickup yield from self.nav_to_pickup
if self.pickup: if self.pickup is not None:
yield self.pickup yield self.pickup
yield from self.nav_to_drop_off yield from self.nav_to_drop_off
yield self.drop_off yield self.drop_off
@ -81,19 +85,27 @@ class Builder(IBuilder[AirAssaultFlightPlan, AirAssaultLayout]):
pickup_position = self.flight.departure.position pickup_position = self.flight.departure.position
else: else:
# Create a special pickup zone for Helos from Airbase / FOB # Create a special pickup zone for Helos from Airbase / FOB
pickup = builder.pickup( pickup = builder.cargo_pickup(
MissionTarget( MissionTarget(
"Pickup Zone", "Pickup Zone",
self.flight.departure.position.random_point_within(1200, 600), self.flight.departure.position.random_point_within(1200, 600),
) ),
self.flight.is_helo,
) )
pickup_position = pickup.position pickup_position = pickup.position
assault_area = builder.assault_area(self.package.target) assault_area = builder.assault_area(self.package.target)
heading = self.package.target.position.heading_between_point(pickup_position) heading = self.package.target.position.heading_between_point(pickup_position)
# Once there is a plane which is capable of AirDrop Paratrooper
# we can make use of the AIRDROP Wayppoint type.
# This would also need a special Waypointbuilder.
# Currently AirAssault can only be used by Helos so we just create
# the drop_off Landing Zone
drop_off_zone = MissionTarget( drop_off_zone = MissionTarget(
"Dropoff zone", "Dropoff zone",
self.package.target.position.point_from_heading(heading, 1200), self.package.target.position.point_from_heading(heading, 1200),
) )
drop_off = builder.cargo_dropoff(drop_off_zone, self.flight.is_helo)
return AirAssaultLayout( return AirAssaultLayout(
departure=builder.takeoff(self.flight.departure), departure=builder.takeoff(self.flight.departure),
@ -110,8 +122,7 @@ class Builder(IBuilder[AirAssaultFlightPlan, AirAssaultLayout]):
altitude, altitude,
altitude_is_agl, altitude_is_agl,
), ),
drop_off=builder.drop_off(drop_off_zone), drop_off=drop_off,
refuel=None,
target=assault_area, target=assault_area,
nav_to_home=builder.nav_path( nav_to_home=builder.nav_path(
drop_off_zone.position, drop_off_zone.position,

View File

@ -21,7 +21,7 @@ class AirliftLayout(StandardLayout):
nav_to_pickup: list[FlightWaypoint] nav_to_pickup: list[FlightWaypoint]
pickup: FlightWaypoint | None pickup: FlightWaypoint | None
nav_to_drop_off: list[FlightWaypoint] nav_to_drop_off: list[FlightWaypoint]
drop_off: FlightWaypoint drop_off: FlightWaypoint | None
refuel: FlightWaypoint | None refuel: FlightWaypoint | None
nav_to_home: list[FlightWaypoint] nav_to_home: list[FlightWaypoint]
@ -31,7 +31,8 @@ class AirliftLayout(StandardLayout):
if self.pickup is not None: if self.pickup is not None:
yield self.pickup yield self.pickup
yield from self.nav_to_drop_off yield from self.nav_to_drop_off
yield self.drop_off if self.drop_off is not None:
yield self.drop_off
if self.refuel is not None: if self.refuel is not None:
yield self.refuel yield self.refuel
yield from self.nav_to_home yield from self.nav_to_home
@ -48,7 +49,7 @@ class AirliftFlightPlan(StandardFlightPlan[AirliftLayout]):
@property @property
def tot_waypoint(self) -> FlightWaypoint: def tot_waypoint(self) -> FlightWaypoint:
return self.layout.drop_off return self.layout.drop_off or self.layout.arrival
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None: def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
# TOT planning isn't really useful for transports. They're behind the front # TOT planning isn't really useful for transports. They're behind the front
@ -78,12 +79,13 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]):
pickup = None pickup = None
refuel = None refuel = None
drop_off = None
if self.flight.is_helo: if self.flight.is_helo:
# Create a pickupzone where the cargo will be spawned # Create a pickupzone where the cargo will be spawned
pickup_zone = MissionTarget( pickup_zone = MissionTarget(
"Pickup Zone", cargo.origin.position.random_point_within(1000, 200) "Pickup Zone", cargo.origin.position.random_point_within(1000, 200)
) )
pickup = builder.pickup(pickup_zone) pickup = builder.cargo_pickup(pickup_zone, True)
# If The cargo is at the departure controlpoint, the pickup waypoint should # If The cargo is at the departure controlpoint, the pickup waypoint should
# only be created for client flights # only be created for client flights
pickup.only_for_player = cargo.origin == self.flight.departure pickup.only_for_player = cargo.origin == self.flight.departure
@ -93,15 +95,16 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]):
"Dropoff zone", "Dropoff zone",
cargo.next_stop.position.random_point_within(1000, 200), cargo.next_stop.position.random_point_within(1000, 200),
) )
drop_off = builder.drop_off(drop_off_zone) drop_off = builder.cargo_dropoff(drop_off_zone, True)
# Add an additional stopover point so that the flight can refuel # Add an additional refuel waypoint
refuel = builder.land_refuel(cargo.next_stop) refuel = builder.land_refuel(cargo.next_stop)
else: else:
# Fixed Wing will get stopover points for pickup and dropoff # Fixed Wing will get landing&refuel waypoints for pickup and dropoff
if cargo.origin != self.flight.departure: if cargo.origin != self.flight.departure:
pickup = builder.land_refuel(cargo.origin) pickup = builder.cargo_pickup(cargo.origin, False)
drop_off = builder.land_refuel(cargo.next_stop) if cargo.next_stop != self.flight.arrival:
drop_off = builder.cargo_dropoff(cargo.next_stop, False)
nav_to_pickup = builder.nav_path( nav_to_pickup = builder.nav_path(
self.flight.departure.position, self.flight.departure.position,
@ -114,7 +117,7 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]):
# Normal Landing Waypoint # Normal Landing Waypoint
arrival = builder.land(self.flight.arrival) arrival = builder.land(self.flight.arrival)
else: else:
# The AI Needs another Stopover point to actually fly back to the original # The AI Needs another landing&refuel point to actually fly back to the original
# base. Otherwise the Cargo drop will be the new Landing Waypoint and the # base. Otherwise the Cargo drop will be the new Landing Waypoint and the
# AI will end its mission there instead of flying back. # AI will end its mission there instead of flying back.
# https://forum.dcs.world/topic/211775-landing-to-refuel-and-rearm-the-landingrefuar-waypoint/ # https://forum.dcs.world/topic/211775-landing-to-refuel-and-rearm-the-landingrefuar-waypoint/

View File

@ -526,40 +526,65 @@ class WaypointBuilder:
) )
@staticmethod @staticmethod
def pickup(pick_up: MissionTarget) -> FlightWaypoint: def cargo_pickup(pick_up: MissionTarget, is_helo: bool) -> FlightWaypoint:
"""Creates a cargo pickup waypoint. """Creates a cargo pickup waypoint.
Args: Args:
control_point: Pick up location. control_point: Pick up location.
""" """
control_point = pick_up if isinstance(pick_up, ControlPoint) else None control_point = pick_up if isinstance(pick_up, ControlPoint) else None
if is_helo:
return FlightWaypoint(
"PICKUP",
FlightWaypointType.PICKUP_ZONE,
pick_up.position,
meters(0),
"RADIO",
description=f"Pick up cargo from {pick_up.name}",
pretty_name="Pick-up zone",
control_point=control_point,
)
return FlightWaypoint( return FlightWaypoint(
"PICKUP", "PICKUP",
FlightWaypointType.PICKUP, FlightWaypointType.LAND_REFUEL,
pick_up.position, pick_up.position,
meters(0), meters(0),
"RADIO", "RADIO",
description=f"Pick up cargo from {pick_up.name}", description=f"Pick up cargo from {pick_up.name}",
pretty_name="Pick up location", pretty_name="Cargo pick-up",
control_point=control_point, control_point=control_point,
) )
@staticmethod @staticmethod
def drop_off(drop_off: MissionTarget) -> FlightWaypoint: def cargo_dropoff(drop_off: MissionTarget, is_helo: bool) -> FlightWaypoint:
"""Creates a cargo drop-off waypoint. """Creates a cargo drop-off waypoint.
This waypoint is used by AirLift and AirAssault to drop cargo or troops
at the given location
Args: Args:
control_point: Drop-off location. control_point: Drop-off location.
is_helo: Differentiate behaviour between plane and helo
""" """
control_point = drop_off if isinstance(drop_off, ControlPoint) else None control_point = drop_off if isinstance(drop_off, ControlPoint) else None
if is_helo:
return FlightWaypoint(
"DROPOFF",
FlightWaypointType.DROPOFF_ZONE,
drop_off.position,
meters(0),
"RADIO",
description=f"Drop off cargo at {drop_off.name}",
pretty_name="Drop-off zone",
control_point=control_point,
)
return FlightWaypoint( return FlightWaypoint(
"DROP OFF", "DROPOFF",
FlightWaypointType.DROP_OFF, FlightWaypointType.LAND_REFUEL,
drop_off.position, drop_off.position,
meters(0), meters(0),
"RADIO", "RADIO",
description=f"Drop off cargo at {drop_off.name}", description=f"Drop off cargo at {drop_off.name}",
pretty_name="Drop off location", pretty_name="Cargo drop-off",
control_point=control_point, control_point=control_point,
) )

View File

@ -43,8 +43,8 @@ class FlightWaypointType(IntEnum):
DIVERT = 23 DIVERT = 23
INGRESS_OCA_RUNWAY = 24 INGRESS_OCA_RUNWAY = 24
INGRESS_OCA_AIRCRAFT = 25 INGRESS_OCA_AIRCRAFT = 25
PICKUP = 26 PICKUP_ZONE = 26 # Pickup Zone for cargo or troops
DROP_OFF = 27 DROPOFF_ZONE = 27 # Dropoff Zone for cargo or troops
BULLSEYE = 28 BULLSEYE = 28
REFUEL = 29 # Should look for nearby tanker to refuel from. REFUEL = 29 # Should look for nearby tanker to refuel from.
CARGO_STOP = 30 # Stopover landing point using the LandingReFuAr waypoint type CARGO_STOP = 30 # Stopover landing point using the LandingReFuAr waypoint type

View File

@ -0,0 +1,17 @@
from dcs.point import MovingPoint
from dcs.task import Land
from .pydcswaypointbuilder import PydcsWaypointBuilder
class LandingZoneBuilder(PydcsWaypointBuilder):
def build(self) -> MovingPoint:
waypoint = super().build()
# Create a landing task, currently only for Helos!
# Calculate a landing point with a small buffer to prevent AI from landing
# directly at the static ammo depot and exploding
landing_point = waypoint.position.random_point_within(15, 5)
# Use Land Task with 30s duration for helos
waypoint.add_task(Land(landing_point, duration=30))
return waypoint

View File

@ -21,7 +21,7 @@ from game.missiongenerator.missiondata import MissionData
from game.settings import Settings from game.settings import Settings
from game.utils import pairwise from game.utils import pairwise
from .baiingress import BaiIngressBuilder from .baiingress import BaiIngressBuilder
from .cargostop import CargoStopBuilder from .landingzone import LandingZoneBuilder
from .casingress import CasIngressBuilder from .casingress import CasIngressBuilder
from .deadingress import DeadIngressBuilder from .deadingress import DeadIngressBuilder
from .default import DefaultWaypointBuilder from .default import DefaultWaypointBuilder
@ -118,7 +118,6 @@ class WaypointGenerator:
def builder_for_waypoint(self, waypoint: FlightWaypoint) -> PydcsWaypointBuilder: def builder_for_waypoint(self, waypoint: FlightWaypoint) -> PydcsWaypointBuilder:
builders = { builders = {
FlightWaypointType.DROP_OFF: CargoStopBuilder,
FlightWaypointType.INGRESS_BAI: BaiIngressBuilder, FlightWaypointType.INGRESS_BAI: BaiIngressBuilder,
FlightWaypointType.INGRESS_CAS: CasIngressBuilder, FlightWaypointType.INGRESS_CAS: CasIngressBuilder,
FlightWaypointType.INGRESS_DEAD: DeadIngressBuilder, FlightWaypointType.INGRESS_DEAD: DeadIngressBuilder,
@ -133,7 +132,8 @@ class WaypointGenerator:
FlightWaypointType.LOITER: HoldPointBuilder, FlightWaypointType.LOITER: HoldPointBuilder,
FlightWaypointType.PATROL: RaceTrackEndBuilder, FlightWaypointType.PATROL: RaceTrackEndBuilder,
FlightWaypointType.PATROL_TRACK: RaceTrackBuilder, FlightWaypointType.PATROL_TRACK: RaceTrackBuilder,
FlightWaypointType.PICKUP: CargoStopBuilder, FlightWaypointType.PICKUP_ZONE: LandingZoneBuilder,
FlightWaypointType.DROPOFF_ZONE: LandingZoneBuilder,
FlightWaypointType.REFUEL: RefuelPointBuilder, FlightWaypointType.REFUEL: RefuelPointBuilder,
FlightWaypointType.CARGO_STOP: CargoStopBuilder, FlightWaypointType.CARGO_STOP: CargoStopBuilder,
} }

View File

@ -56,8 +56,8 @@ class LogisticsGenerator:
if ( if (
waypoint.waypoint_type waypoint.waypoint_type
not in [ not in [
FlightWaypointType.PICKUP, FlightWaypointType.PICKUP_ZONE,
FlightWaypointType.DROP_OFF, FlightWaypointType.DROPOFF_ZONE,
] ]
or waypoint.only_for_player or waypoint.only_for_player
and not self.flight.client_count and not self.flight.client_count
@ -68,7 +68,7 @@ class LogisticsGenerator:
self.mission.triggers.add_triggerzone( self.mission.triggers.add_triggerzone(
waypoint.position, ZONE_RADIUS, False, zone_name waypoint.position, ZONE_RADIUS, False, zone_name
) )
if waypoint.waypoint_type == FlightWaypointType.PICKUP: if waypoint.waypoint_type == FlightWaypointType.PICKUP_ZONE:
pickup_point = waypoint.position pickup_point = waypoint.position
logistics_info.pickup_zone = zone_name logistics_info.pickup_zone = zone_name
else: else: