Helicopter flights can be planned from FOBs

This commit is contained in:
Khopa 2021-05-21 00:34:51 +02:00
parent 844dc48d65
commit d5990e60c9
10 changed files with 93 additions and 17 deletions

13
game/helipad.py Normal file
View File

@ -0,0 +1,13 @@
from typing import Optional
from dcs.unitgroup import StaticGroup
from game.point_with_heading import PointWithHeading
class Helipad(PointWithHeading):
def __init__(self):
super(Helipad, self).__init__()
self.heading = 0
self.occupied = False
self.static_unit: Optional[StaticGroup] = None

View File

@ -41,6 +41,7 @@ from dcs.unitgroup import (
)
from dcs.vehicles import AirDefence, Armor, MissilesSS, Unarmed
from ..helipad import Helipad
from ..scenery_group import SceneryGroup
from pyproj import CRS, Transformer
from shapely import geometry, ops
@ -549,7 +550,7 @@ class MizCampaignLoader:
for group in self.helipads:
closest, distance = self.objective_info(group)
closest.helipads.append(
PointWithHeading.from_point(group.position, group.units[0].heading)
Helipad.from_point(group.position, group.units[0].heading)
)
for group in self.factories:

View File

@ -10,6 +10,7 @@ from enum import Enum
from functools import total_ordering
from typing import Any, Dict, Iterator, List, Optional, Set, TYPE_CHECKING, Type, Union
from dcs import helicopters
from dcs.mapping import Point
from dcs.ships import (
CVN_74_John_C__Stennis,
@ -39,6 +40,7 @@ from .theatergroundobject import (
VehicleGroupGroundObject,
)
from ..db import PRICES
from ..helipad import Helipad
from ..utils import nautical_miles
from ..weather import Conditions
@ -296,7 +298,7 @@ class ControlPoint(MissionTarget, ABC):
self.connected_objectives: List[TheaterGroundObject] = []
self.base_defenses: List[BaseDefenseGroundObject] = []
self.preset_locations = PresetLocations()
self.helipads: List[PointWithHeading] = []
self.helipads: List[Helipad] = []
# TODO: Should be Airbase specific.
self.size = size
@ -378,6 +380,29 @@ class ControlPoint(MissionTarget, ABC):
return True
return False
@property
def has_helipads(self) -> bool:
"""
Returns true if cp has helipads
"""
return len(self.helipads) > 0
@property
def has_free_helipad(self) -> bool:
"""
Returns true if cp has a free helipad
"""
return False in [h.occupied for h in self.helipads]
def get_free_helipad(self) -> Optional[Helipad]:
"""
Returns the first free additional helipad
"""
for h in self.helipads:
if not h.occupied:
return h
return None
def can_recruit_ground_units(self, game: Game) -> bool:
"""Returns True if this control point is capable of recruiting ground units."""
if not self.can_deploy_ground_units:
@ -1084,10 +1109,13 @@ class Fob(ControlPoint):
@property
def total_aircraft_parking(self) -> int:
return 0
return len(self.helipads)
def can_operate(self, aircraft: FlyingType) -> bool:
return False
if aircraft in helicopters.helicopter_map.values():
return True
else:
return False
@property
def heading(self) -> int:

View File

@ -46,4 +46,4 @@ class MissionTarget:
@property
def strike_targets(self) -> List[Union[MissionTarget, Unit]]:
raise NotImplementedError
return []

View File

@ -1110,18 +1110,43 @@ class AircraftConflictGenerator:
at=self.m.find_group(group_name),
)
else:
if not isinstance(cp, Airfield):
raise RuntimeError(
f"Attempted to spawn at airfield for non-airfield {cp}"
# If the flight is an helicopter flight, then prioritize dedicated helipads
group = None
if flight.unit_type in helicopters.helicopter_map.values():
helipad = cp.get_free_helipad()
if helipad is not None:
group = self._generate_at_group(
name=name,
side=country,
unit_type=flight.unit_type,
count=flight.count,
start_type=flight.start_type,
at=helipad.static_unit,
)
group.points[0].action = PointAction.FromGroundArea
group.points[0].type = "From Ground Area"
helipad.occupied = True
for i in range(flight.count - 1):
helipad = cp.get_free_helipad()
if helipad is not None:
helipad.occupied = True
group.units[1 + i].position = Point(helipad.x, helipad.y)
if group is None:
if not isinstance(cp, Airfield):
raise RuntimeError(
f"Attempted to spawn at airfield for non-airfield {cp}"
)
group = self._generate_at_airport(
name=name,
side=country,
unit_type=flight.unit_type,
count=flight.count,
start_type=flight.start_type,
airport=cp.airport,
)
group = self._generate_at_airport(
name=name,
side=country,
unit_type=flight.unit_type,
count=flight.count,
start_type=flight.start_type,
airport=cp.airport,
)
except Exception as e:
# Generated when there is no place on Runway or on Parking Slots
logging.error(e)

View File

@ -25,7 +25,11 @@ class ClosestAirfields:
@property
def operational_airfields(self) -> Iterator[ControlPoint]:
return (c for c in self.closest_airfields if c.runway_is_operational())
return (
c
for c in self.closest_airfields
if c.runway_is_operational() or c.has_helipads
)
def airfields_within(self, distance: Distance) -> Iterator[ControlPoint]:
"""Iterates over all airfields within the given range of the target.

View File

@ -142,6 +142,8 @@ class FlightWaypoint:
PointAction.FromParkingArea: FlightWaypointType.TAKEOFF,
PointAction.FromParkingAreaHot: FlightWaypointType.TAKEOFF,
PointAction.FromRunway: FlightWaypointType.TAKEOFF,
PointAction.FromGroundArea: FlightWaypointType.TAKEOFF,
PointAction.FromGroundAreaHot: FlightWaypointType.TAKEOFF,
}[point.action]
if waypoint.waypoint_type == FlightWaypointType.NAV:
waypoint.name = "NAV"

View File

@ -1772,4 +1772,5 @@ class FlightPlanBuilder:
for flight in self.package.flights:
if flight.departure == airfield:
return airfield
raise RuntimeError("Could not find any airfield assigned to this package")

View File

@ -587,6 +587,8 @@ class HelipadGenerator:
sp.position = pad.position
sg.add_point(sp)
country.add_static_group(sg)
helipad.static_unit = sg
helipad.occupied = False
class GroundObjectsGenerator: