Designated CTLD zones for ControlPoints (AB/FOB/FARP)

Resolves #46
This commit is contained in:
Raffson 2023-01-20 23:00:13 +01:00
parent 0d57f710aa
commit 531625ff08
No known key found for this signature in database
GPG Key ID: B0402B2C9B764D99
9 changed files with 104 additions and 26 deletions

View File

@ -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

View 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")

View File

@ -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)

View File

@ -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")

View File

@ -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}",

View File

@ -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]

View File

@ -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]:

View 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

View File

@ -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)