mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
parent
0d57f710aa
commit
531625ff08
@ -19,6 +19,7 @@
|
||||
* **[UI]** Configurable ICLS for capable Carriers & LHAs.
|
||||
* **[UI]** Configurable LINK4 for Carriers.
|
||||
* **[Kneeboard]** Show package information in Support page
|
||||
* **[Campaign Design]** Ability to define designated CTLD zones for Control Points (Airbases & FOBs/FARPs)
|
||||
|
||||
## Fixes
|
||||
* **[UI]** Removed deprecated options
|
||||
|
||||
20
game/ato/flightplans/_common_ctld.py
Normal file
20
game/ato/flightplans/_common_ctld.py
Normal file
@ -0,0 +1,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
from typing import Union, TYPE_CHECKING, Tuple
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from dcs import Point
|
||||
|
||||
from game.theater import ControlPoint
|
||||
from game.theater.interfaces.CTLD import CTLD
|
||||
|
||||
|
||||
def generate_random_ctld_point(cp: Union[ControlPoint, CTLD]) -> Point:
|
||||
if isinstance(cp, CTLD) and cp.ctld_zones:
|
||||
zone: Tuple[Point, float] = random.choice(cp.ctld_zones)
|
||||
pos, radius = zone
|
||||
return pos.random_point_within(radius)
|
||||
elif isinstance(cp, CTLD) and isinstance(cp, ControlPoint):
|
||||
return cp.position.random_point_within(2000, 200)
|
||||
raise RuntimeError("Could not generate CTLD point")
|
||||
@ -4,6 +4,7 @@ from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
from typing import Iterator, TYPE_CHECKING, Type
|
||||
|
||||
from ._common_ctld import generate_random_ctld_point
|
||||
from game.ato.flightplans.standard import StandardFlightPlan, StandardLayout
|
||||
from game.theater.controlpoint import ControlPointType
|
||||
from game.theater.missiontarget import MissionTarget
|
||||
@ -13,8 +14,10 @@ from .planningerror import PlanningError
|
||||
from .uizonedisplay import UiZone, UiZoneDisplay
|
||||
from .waypointbuilder import WaypointBuilder
|
||||
from ..flightwaypoint import FlightWaypointType
|
||||
from ...theater.interfaces.CTLD import CTLD
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from dcs import Point
|
||||
from ..flightwaypoint import FlightWaypoint
|
||||
|
||||
|
||||
@ -104,16 +107,10 @@ class Builder(IBuilder[AirAssaultFlightPlan, AirAssaultLayout]):
|
||||
pickup = None
|
||||
pickup_position = self.flight.departure.position
|
||||
else:
|
||||
# TODO The calculation of the Pickup LZ is currently randomized. This
|
||||
# leads to the problem that we can not gurantee that the LZ is clear of
|
||||
# obstacles. This has to be improved in the future so that the Mission can
|
||||
# be autoplanned. In the current state the User has to check the created
|
||||
# Waypoints for the Pickup and Dropoff LZs are free of obstacles.
|
||||
# Create a special pickup zone for Helos from Airbase / FOB
|
||||
pickup = builder.pickup_zone(
|
||||
MissionTarget(
|
||||
"Pickup Zone",
|
||||
self.flight.departure.position.random_point_within(1200, 600),
|
||||
self._generate_ctld_pickup(),
|
||||
)
|
||||
)
|
||||
pickup_position = pickup.position
|
||||
@ -123,7 +120,7 @@ class Builder(IBuilder[AirAssaultFlightPlan, AirAssaultLayout]):
|
||||
assault_area.only_for_player = False
|
||||
assault_area.alt = feet(1000)
|
||||
|
||||
# TODO we can not gurantee a safe LZ for DropOff. See comment above.
|
||||
# TODO: define CTLD dropoff zones in campaign miz?
|
||||
drop_off_zone = MissionTarget(
|
||||
"Dropoff zone",
|
||||
self.package.target.position.point_from_heading(heading, 1200),
|
||||
@ -159,3 +156,7 @@ class Builder(IBuilder[AirAssaultFlightPlan, AirAssaultLayout]):
|
||||
|
||||
def build(self) -> AirAssaultFlightPlan:
|
||||
return AirAssaultFlightPlan(self.flight, self.layout())
|
||||
|
||||
def _generate_ctld_pickup(self) -> Point:
|
||||
assert isinstance(self.flight.departure, CTLD)
|
||||
return generate_random_ctld_point(self.flight.departure)
|
||||
|
||||
@ -5,15 +5,18 @@ from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
from typing import TYPE_CHECKING, Type
|
||||
|
||||
from ._common_ctld import generate_random_ctld_point
|
||||
from game.theater.missiontarget import MissionTarget
|
||||
from game.utils import feet
|
||||
from .ibuilder import IBuilder
|
||||
from .planningerror import PlanningError
|
||||
from .standard import StandardFlightPlan, StandardLayout
|
||||
from .waypointbuilder import WaypointBuilder
|
||||
from ...theater.interfaces.CTLD import CTLD
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..flightwaypoint import FlightWaypoint
|
||||
from dcs import Point
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@ -106,15 +109,10 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]):
|
||||
if self.flight.is_helo:
|
||||
# Create CTLD Zones for Helo flights
|
||||
pickup_zone = builder.pickup_zone(
|
||||
MissionTarget(
|
||||
"Pickup Zone", cargo.origin.position.random_point_within(1000, 200)
|
||||
)
|
||||
MissionTarget("Pickup Zone", self._generate_ctld_pickup())
|
||||
)
|
||||
drop_off_zone = builder.dropoff_zone(
|
||||
MissionTarget(
|
||||
"Dropoff zone",
|
||||
cargo.next_stop.position.random_point_within(1000, 200),
|
||||
)
|
||||
MissionTarget("Dropoff zone", self._generate_ctld_dropoff())
|
||||
)
|
||||
# Show the zone waypoints only to the player
|
||||
pickup_zone.only_for_player = True
|
||||
@ -153,3 +151,15 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]):
|
||||
|
||||
def build(self) -> AirliftFlightPlan:
|
||||
return AirliftFlightPlan(self.flight, self.layout())
|
||||
|
||||
def _generate_ctld_pickup(self) -> Point:
|
||||
cargo = self.flight.cargo
|
||||
if cargo and cargo.origin and isinstance(cargo.origin, CTLD):
|
||||
return generate_random_ctld_point(cargo.origin)
|
||||
raise RuntimeError("Could not generate CTLD pickup")
|
||||
|
||||
def _generate_ctld_dropoff(self) -> Point:
|
||||
cargo = self.flight.cargo
|
||||
if cargo and cargo.transport and isinstance(cargo.transport.destination, CTLD):
|
||||
return generate_random_ctld_point(cargo.transport.destination)
|
||||
raise RuntimeError("Could not generate CTLD dropoff")
|
||||
|
||||
@ -14,6 +14,7 @@ from typing import (
|
||||
|
||||
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.flightwaypointtype import FlightWaypointType
|
||||
from game.theater import (
|
||||
@ -23,6 +24,7 @@ from game.theater import (
|
||||
TheaterGroundObject,
|
||||
TheaterUnit,
|
||||
)
|
||||
from game.theater.interfaces.CTLD import CTLD
|
||||
from game.utils import Distance, meters, nautical_miles, feet
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -557,10 +559,14 @@ class WaypointBuilder:
|
||||
"""Creates a cargo stop 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(
|
||||
"CARGOSTOP",
|
||||
FlightWaypointType.CARGO_STOP,
|
||||
control_point.position,
|
||||
pos,
|
||||
meters(0),
|
||||
"RADIO",
|
||||
description=f"Stop for cargo at {control_point.name}",
|
||||
|
||||
@ -32,6 +32,7 @@ from game.utils import Distance, meters
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.theater.conflicttheater import ConflictTheater
|
||||
from dcs import Point
|
||||
|
||||
|
||||
class MizCampaignLoader:
|
||||
@ -107,8 +108,12 @@ class MizCampaignLoader:
|
||||
if self.mission.country(self.RED_COUNTRY.name) is None:
|
||||
self.mission.coalition["red"].add_country(self.RED_COUNTRY)
|
||||
|
||||
def control_point_from_airport(self, airport: Airport) -> ControlPoint:
|
||||
cp = Airfield(airport, self.theater, starts_blue=airport.is_blue())
|
||||
def control_point_from_airport(
|
||||
self, airport: Airport, ctld_zones: List[Tuple[Point, float]]
|
||||
) -> ControlPoint:
|
||||
cp = Airfield(
|
||||
airport, self.theater, starts_blue=airport.is_blue(), ctld_zones=ctld_zones
|
||||
)
|
||||
|
||||
# Use the unlimited aircraft option to determine if an airfield should
|
||||
# be owned by the player when the campaign is "inverted".
|
||||
@ -245,7 +250,8 @@ class MizCampaignLoader:
|
||||
control_points = {}
|
||||
for airport in self.mission.terrain.airport_list():
|
||||
if airport.is_blue() or airport.is_red():
|
||||
control_point = self.control_point_from_airport(airport)
|
||||
ctld_zones = self.get_ctld_zones(airport.name)
|
||||
control_point = self.control_point_from_airport(airport, ctld_zones)
|
||||
control_points[control_point.id] = control_point
|
||||
|
||||
for blue in (False, True):
|
||||
@ -268,8 +274,13 @@ class MizCampaignLoader:
|
||||
control_point.captured_invert = ship.late_activation
|
||||
control_points[control_point.id] = control_point
|
||||
for fob in self.fobs(blue):
|
||||
ctld_zones = self.get_ctld_zones(fob.name)
|
||||
control_point = Fob(
|
||||
str(fob.name), fob.position, self.theater, starts_blue=blue
|
||||
str(fob.name),
|
||||
fob.position,
|
||||
self.theater,
|
||||
starts_blue=blue,
|
||||
ctld_zones=ctld_zones,
|
||||
)
|
||||
control_point.captured_invert = fob.late_activation
|
||||
control_points[control_point.id] = control_point
|
||||
@ -460,3 +471,9 @@ class MizCampaignLoader:
|
||||
self.add_preset_locations()
|
||||
self.add_supply_routes()
|
||||
self.add_shipping_lanes()
|
||||
|
||||
def get_ctld_zones(self, prefix: str) -> List[Tuple[Point, float]]:
|
||||
zones = [t for t in self.mission.triggers.zones() if prefix + " CTLD" in t.name]
|
||||
for z in zones:
|
||||
self.mission.triggers.zones().remove(z)
|
||||
return [(z.position, z.radius) for z in zones]
|
||||
|
||||
@ -41,6 +41,7 @@ from dcs.ships import (
|
||||
Hms_invincible,
|
||||
)
|
||||
from dcs.terrain.terrain import Airport, ParkingSlot
|
||||
from dcs.triggers import TriggerZone
|
||||
from dcs.unitgroup import ShipGroup, StaticGroup
|
||||
from dcs.unittype import ShipType
|
||||
|
||||
@ -62,6 +63,7 @@ from game.theater.presetlocation import PresetLocation
|
||||
from game.utils import Distance, Heading, meters
|
||||
from .base import Base
|
||||
from .frontline import FrontLine
|
||||
from .interfaces.CTLD import CTLD
|
||||
from .missiontarget import MissionTarget
|
||||
from .theatergroundobject import (
|
||||
GenericCarrierGroundObject,
|
||||
@ -1047,9 +1049,13 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
...
|
||||
|
||||
|
||||
class Airfield(ControlPoint):
|
||||
class Airfield(ControlPoint, CTLD):
|
||||
def __init__(
|
||||
self, airport: Airport, theater: ConflictTheater, starts_blue: bool
|
||||
self,
|
||||
airport: Airport,
|
||||
theater: ConflictTheater,
|
||||
starts_blue: bool,
|
||||
ctld_zones: Optional[List[Tuple[Point, float]]] = None,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
airport.name,
|
||||
@ -1061,6 +1067,7 @@ class Airfield(ControlPoint):
|
||||
)
|
||||
self.airport = airport
|
||||
self._runway_status = RunwayStatus()
|
||||
self.ctld_zones = ctld_zones
|
||||
|
||||
@property
|
||||
def dcs_airport(self) -> Airport:
|
||||
@ -1399,14 +1406,20 @@ class OffMapSpawn(ControlPoint):
|
||||
return ControlPointStatus.Functional
|
||||
|
||||
|
||||
class Fob(ControlPoint, RadioFrequencyContainer):
|
||||
class Fob(ControlPoint, RadioFrequencyContainer, CTLD):
|
||||
def __init__(
|
||||
self, name: str, at: Point, theater: ConflictTheater, starts_blue: bool
|
||||
):
|
||||
self,
|
||||
name: str,
|
||||
at: Point,
|
||||
theater: ConflictTheater,
|
||||
starts_blue: bool,
|
||||
ctld_zones: Optional[List[Tuple[Point, float]]] = None,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
name, at, at, theater, starts_blue, cptype=ControlPointType.FOB
|
||||
)
|
||||
self.name = name
|
||||
self.ctld_zones = ctld_zones
|
||||
|
||||
@property
|
||||
def symbol_set_and_entity(self) -> tuple[SymbolSet, Entity]:
|
||||
|
||||
7
game/theater/interfaces/CTLD.py
Normal file
7
game/theater/interfaces/CTLD.py
Normal file
@ -0,0 +1,7 @@
|
||||
from typing import Optional, List, Tuple
|
||||
|
||||
from dcs import Point
|
||||
|
||||
|
||||
class CTLD:
|
||||
ctld_zones: Optional[List[Tuple[Point, float]]] = None
|
||||
@ -173,5 +173,8 @@ VERSION = _build_version_string()
|
||||
#: * Campaign designers can now define more settings:
|
||||
#: `max_frontline_length: 25` (in km)
|
||||
#: `culling_exclusion_radius: 35` (in km)
|
||||
#:
|
||||
#: Version 10.6
|
||||
#: * Designated CTLD zones for ControlPoints (Airbases & FOBs/FARPs)
|
||||
|
||||
CAMPAIGN_FORMAT_VERSION = (10, 5)
|
||||
CAMPAIGN_FORMAT_VERSION = (10, 6)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user