mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Fix ground-seeking AirLift
This commit is contained in:
parent
d901df4fa0
commit
781d611f17
@ -51,6 +51,7 @@
|
|||||||
* **[Capture Logic]** Release all parking slots when an airbase is captured
|
* **[Capture Logic]** Release all parking slots when an airbase is captured
|
||||||
* **[Modding]** Swedish Military Assets Pack air defence presets are now correctly removed from the faction when the mod is disabled.
|
* **[Modding]** Swedish Military Assets Pack air defence presets are now correctly removed from the faction when the mod is disabled.
|
||||||
* **[Mission Generation]** Naval aircraft not always returning to carrier
|
* **[Mission Generation]** Naval aircraft not always returning to carrier
|
||||||
|
* **[Mission Generation]** AI AirLift aircraft crashing into terrain due to insufficient waypoints
|
||||||
|
|
||||||
# Retribution v1.2.1 (hotfix)
|
# Retribution v1.2.1 (hotfix)
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import random
|
||||||
from collections.abc import Iterator
|
from collections.abc import Iterator
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@ -7,7 +8,7 @@ from typing import Optional
|
|||||||
from typing import TYPE_CHECKING, Type
|
from typing import TYPE_CHECKING, Type
|
||||||
|
|
||||||
from game.theater.missiontarget import MissionTarget
|
from game.theater.missiontarget import MissionTarget
|
||||||
from game.utils import feet
|
from game.utils import feet, Distance
|
||||||
from ._common_ctld import generate_random_ctld_point
|
from ._common_ctld import generate_random_ctld_point
|
||||||
from .ibuilder import IBuilder
|
from .ibuilder import IBuilder
|
||||||
from .planningerror import PlanningError
|
from .planningerror import PlanningError
|
||||||
@ -23,13 +24,17 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class AirliftLayout(StandardLayout):
|
class AirliftLayout(StandardLayout):
|
||||||
|
pickup_ascent: FlightWaypoint | None
|
||||||
|
pickup_descent: FlightWaypoint | None
|
||||||
# There will not be a pickup waypoint when the pickup airfield is the departure
|
# There will not be a pickup waypoint when the pickup airfield is the departure
|
||||||
# airfield for cargo planes, as the cargo is pre-loaded. Helicopters will still pick
|
# airfield for cargo planes, as the cargo is pre-loaded. Helicopters will still pick
|
||||||
# up the cargo near the airfield.
|
# up the cargo near the airfield.
|
||||||
pickup: FlightWaypoint | None
|
pickup: FlightWaypoint | None
|
||||||
# pickup_zone will be used for player flights to create the CTLD stuff
|
# pickup_zone will be used for player flights to create the CTLD stuff
|
||||||
ctld_pickup_zone: FlightWaypoint | None
|
ctld_pickup_zone: FlightWaypoint | None
|
||||||
|
drop_off_ascent: FlightWaypoint | None
|
||||||
nav_to_drop_off: list[FlightWaypoint]
|
nav_to_drop_off: list[FlightWaypoint]
|
||||||
|
drop_off_descent: FlightWaypoint | None
|
||||||
# There will not be a drop-off waypoint when the drop-off airfield and the arrival
|
# There will not be a drop-off waypoint when the drop-off airfield and the arrival
|
||||||
# airfield is the same for a cargo plane, as planes will land to unload and we don't
|
# airfield is the same for a cargo plane, as planes will land to unload and we don't
|
||||||
# want a double landing. Helicopters will still drop their cargo near the airfield
|
# want a double landing. Helicopters will still drop their cargo near the airfield
|
||||||
@ -37,6 +42,8 @@ class AirliftLayout(StandardLayout):
|
|||||||
drop_off: FlightWaypoint | None
|
drop_off: FlightWaypoint | None
|
||||||
# drop_off_zone will be used for player flights to create the CTLD stuff
|
# drop_off_zone will be used for player flights to create the CTLD stuff
|
||||||
ctld_drop_off_zone: FlightWaypoint | None
|
ctld_drop_off_zone: FlightWaypoint | None
|
||||||
|
return_ascent: FlightWaypoint | None
|
||||||
|
return_descent: FlightWaypoint | None
|
||||||
|
|
||||||
def add_waypoint(
|
def add_waypoint(
|
||||||
self, wpt: FlightWaypoint, next_wpt: Optional[FlightWaypoint]
|
self, wpt: FlightWaypoint, next_wpt: Optional[FlightWaypoint]
|
||||||
@ -60,17 +67,29 @@ class AirliftLayout(StandardLayout):
|
|||||||
|
|
||||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||||
yield self.departure
|
yield self.departure
|
||||||
|
if self.pickup_ascent is not None:
|
||||||
|
yield self.pickup_ascent
|
||||||
yield from self.nav_to
|
yield from self.nav_to
|
||||||
|
if self.pickup_descent is not None:
|
||||||
|
yield self.pickup_descent
|
||||||
if self.pickup is not None:
|
if self.pickup is not None:
|
||||||
yield self.pickup
|
yield self.pickup
|
||||||
if self.ctld_pickup_zone is not None:
|
if self.ctld_pickup_zone is not None:
|
||||||
yield self.ctld_pickup_zone
|
yield self.ctld_pickup_zone
|
||||||
|
if self.drop_off_ascent is not None:
|
||||||
|
yield self.drop_off_ascent
|
||||||
yield from self.nav_to_drop_off
|
yield from self.nav_to_drop_off
|
||||||
|
if self.drop_off_descent is not None:
|
||||||
|
yield self.drop_off_descent
|
||||||
if self.drop_off is not None:
|
if self.drop_off is not None:
|
||||||
yield self.drop_off
|
yield self.drop_off
|
||||||
|
if self.return_ascent is not None:
|
||||||
|
yield self.return_ascent
|
||||||
if self.ctld_drop_off_zone is not None:
|
if self.ctld_drop_off_zone is not None:
|
||||||
yield self.ctld_drop_off_zone
|
yield self.ctld_drop_off_zone
|
||||||
yield from self.nav_from
|
yield from self.nav_from
|
||||||
|
if self.return_descent is not None:
|
||||||
|
yield self.return_descent
|
||||||
yield self.arrival
|
yield self.arrival
|
||||||
if self.divert is not None:
|
if self.divert is not None:
|
||||||
yield self.divert
|
yield self.divert
|
||||||
@ -121,15 +140,47 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]):
|
|||||||
|
|
||||||
builder = WaypointBuilder(self.flight)
|
builder = WaypointBuilder(self.flight)
|
||||||
|
|
||||||
|
pickup_ascent = None
|
||||||
|
pickup_descent = None
|
||||||
pickup = None
|
pickup = None
|
||||||
|
drop_off_ascent = None
|
||||||
|
drop_off_descent = None
|
||||||
drop_off = None
|
drop_off = None
|
||||||
pickup_zone = None
|
pickup_zone = None
|
||||||
drop_off_zone = None
|
drop_off_zone = None
|
||||||
|
|
||||||
if cargo.origin != self.flight.departure:
|
if cargo.origin != self.flight.departure:
|
||||||
pickup = builder.cargo_stop(cargo.origin)
|
pickup = builder.cargo_stop(cargo.origin)
|
||||||
|
pickup_ascent = self._create_ascent_or_descent(
|
||||||
|
builder,
|
||||||
|
self.flight.departure.position,
|
||||||
|
cargo.origin.position,
|
||||||
|
altitude,
|
||||||
|
altitude_is_agl,
|
||||||
|
)
|
||||||
|
pickup_descent = self._create_ascent_or_descent(
|
||||||
|
builder,
|
||||||
|
cargo.origin.position,
|
||||||
|
self.flight.departure.position,
|
||||||
|
altitude,
|
||||||
|
altitude_is_agl,
|
||||||
|
)
|
||||||
if cargo.next_stop != self.flight.arrival:
|
if cargo.next_stop != self.flight.arrival:
|
||||||
drop_off = builder.cargo_stop(cargo.next_stop)
|
drop_off = builder.cargo_stop(cargo.next_stop)
|
||||||
|
drop_off_ascent = self._create_ascent_or_descent(
|
||||||
|
builder,
|
||||||
|
cargo.origin.position,
|
||||||
|
cargo.next_stop.position,
|
||||||
|
altitude,
|
||||||
|
altitude_is_agl,
|
||||||
|
)
|
||||||
|
drop_off_descent = self._create_ascent_or_descent(
|
||||||
|
builder,
|
||||||
|
cargo.next_stop.position,
|
||||||
|
cargo.origin.position,
|
||||||
|
altitude,
|
||||||
|
altitude_is_agl,
|
||||||
|
)
|
||||||
|
|
||||||
if self.flight.is_helo:
|
if self.flight.is_helo:
|
||||||
# Create CTLD Zones for Helo flights
|
# Create CTLD Zones for Helo flights
|
||||||
@ -150,25 +201,50 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]):
|
|||||||
altitude_is_agl,
|
altitude_is_agl,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return_ascent = self._create_ascent_or_descent(
|
||||||
|
builder,
|
||||||
|
cargo.next_stop.position
|
||||||
|
if cargo.next_stop != self.flight.arrival
|
||||||
|
else cargo.origin.position,
|
||||||
|
self.flight.arrival.position,
|
||||||
|
altitude,
|
||||||
|
altitude_is_agl,
|
||||||
|
)
|
||||||
|
return_descent = self._create_ascent_or_descent(
|
||||||
|
builder,
|
||||||
|
self.flight.arrival.position,
|
||||||
|
cargo.next_stop.position
|
||||||
|
if cargo.next_stop != self.flight.arrival
|
||||||
|
else cargo.origin.position,
|
||||||
|
altitude,
|
||||||
|
altitude_is_agl,
|
||||||
|
)
|
||||||
|
|
||||||
return AirliftLayout(
|
return AirliftLayout(
|
||||||
departure=builder.takeoff(self.flight.departure),
|
departure=builder.takeoff(self.flight.departure),
|
||||||
|
pickup_ascent=pickup_ascent,
|
||||||
nav_to=nav_to_pickup,
|
nav_to=nav_to_pickup,
|
||||||
|
pickup_descent=pickup_descent,
|
||||||
pickup=pickup,
|
pickup=pickup,
|
||||||
ctld_pickup_zone=pickup_zone,
|
ctld_pickup_zone=pickup_zone,
|
||||||
|
drop_off_ascent=drop_off_ascent,
|
||||||
nav_to_drop_off=builder.nav_path(
|
nav_to_drop_off=builder.nav_path(
|
||||||
cargo.origin.position,
|
cargo.origin.position,
|
||||||
cargo.next_stop.position,
|
cargo.next_stop.position,
|
||||||
altitude,
|
altitude,
|
||||||
altitude_is_agl,
|
altitude_is_agl,
|
||||||
),
|
),
|
||||||
|
drop_off_descent=drop_off_descent,
|
||||||
drop_off=drop_off,
|
drop_off=drop_off,
|
||||||
ctld_drop_off_zone=drop_off_zone,
|
ctld_drop_off_zone=drop_off_zone,
|
||||||
|
return_ascent=return_ascent,
|
||||||
nav_from=builder.nav_path(
|
nav_from=builder.nav_path(
|
||||||
cargo.origin.position,
|
cargo.origin.position,
|
||||||
self.flight.arrival.position,
|
self.flight.arrival.position,
|
||||||
altitude,
|
altitude,
|
||||||
altitude_is_agl,
|
altitude_is_agl,
|
||||||
),
|
),
|
||||||
|
return_descent=return_descent,
|
||||||
arrival=builder.land(self.flight.arrival),
|
arrival=builder.land(self.flight.arrival),
|
||||||
divert=builder.divert(self.flight.divert),
|
divert=builder.divert(self.flight.divert),
|
||||||
bullseye=builder.bullseye(),
|
bullseye=builder.bullseye(),
|
||||||
@ -188,3 +264,18 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]):
|
|||||||
if cargo and cargo.transport and isinstance(cargo.transport.destination, CTLD):
|
if cargo and cargo.transport and isinstance(cargo.transport.destination, CTLD):
|
||||||
return generate_random_ctld_point(cargo.transport.destination)
|
return generate_random_ctld_point(cargo.transport.destination)
|
||||||
raise RuntimeError("Could not generate CTLD dropoff")
|
raise RuntimeError("Could not generate CTLD dropoff")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _create_ascent_or_descent(
|
||||||
|
builder: WaypointBuilder,
|
||||||
|
start: Point,
|
||||||
|
end: Point,
|
||||||
|
alt: Distance,
|
||||||
|
agl: bool,
|
||||||
|
) -> FlightWaypoint:
|
||||||
|
distance = start.distance_to_point(end)
|
||||||
|
rdistance = 1000 if agl else min(distance / 10, 20000)
|
||||||
|
heading = round(start.heading_between_point(end))
|
||||||
|
rheading = random.randint(heading - 30, heading + 30) % 360
|
||||||
|
pos = start.point_from_heading(float(rheading), rdistance)
|
||||||
|
return builder.nav(pos, alt, agl)
|
||||||
|
|||||||
@ -15,7 +15,6 @@ from typing import (
|
|||||||
|
|
||||||
from dcs.mapping import Point, Vector2
|
from dcs.mapping import Point, Vector2
|
||||||
|
|
||||||
from game.ato.flightplans._common_ctld import generate_random_ctld_point
|
|
||||||
from game.ato.flightwaypoint import AltitudeReference, FlightWaypoint
|
from game.ato.flightwaypoint import AltitudeReference, FlightWaypoint
|
||||||
from game.ato.flightwaypointtype import FlightWaypointType
|
from game.ato.flightwaypointtype import FlightWaypointType
|
||||||
from game.theater import (
|
from game.theater import (
|
||||||
@ -25,7 +24,6 @@ from game.theater import (
|
|||||||
TheaterGroundObject,
|
TheaterGroundObject,
|
||||||
TheaterUnit,
|
TheaterUnit,
|
||||||
)
|
)
|
||||||
from game.theater.interfaces.CTLD import CTLD
|
|
||||||
from game.utils import Distance, meters, nautical_miles, feet
|
from game.utils import Distance, meters, nautical_miles, feet
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -635,14 +633,10 @@ class WaypointBuilder:
|
|||||||
"""Creates a cargo stop waypoint.
|
"""Creates a cargo stop waypoint.
|
||||||
This waypoint is used by AirLift as a landing and stopover waypoint
|
This waypoint is used by AirLift as a landing and stopover waypoint
|
||||||
"""
|
"""
|
||||||
if isinstance(control_point, CTLD) and control_point.ctld_zones:
|
|
||||||
pos = generate_random_ctld_point(control_point)
|
|
||||||
else:
|
|
||||||
pos = control_point.position
|
|
||||||
return FlightWaypoint(
|
return FlightWaypoint(
|
||||||
"CARGOSTOP",
|
"CARGOSTOP",
|
||||||
FlightWaypointType.CARGO_STOP,
|
FlightWaypointType.CARGO_STOP,
|
||||||
pos,
|
control_point.position,
|
||||||
meters(0),
|
meters(0),
|
||||||
"RADIO",
|
"RADIO",
|
||||||
description=f"Stop for cargo at {control_point.name}",
|
description=f"Stop for cargo at {control_point.name}",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user