Merge remote-tracking branch 'remotes/dcs-retribution/dcs-retribution/dev' into pretense-generator

This commit is contained in:
MetalStormGhost
2024-06-09 20:25:55 +03:00
91 changed files with 1369 additions and 187 deletions

View File

@@ -1,8 +1,9 @@
from __future__ import annotations
from copy import deepcopy
from dataclasses import dataclass, field
from datetime import datetime
from typing import Literal, TYPE_CHECKING
from typing import Literal, TYPE_CHECKING, Any, Dict, Optional
from dcs import Point
@@ -56,3 +57,17 @@ class FlightWaypoint:
def __hash__(self) -> int:
return hash(id(self))
def __deepcopy__(self, memo: Optional[Dict[int, Any]] = None) -> FlightWaypoint:
obj = FlightWaypoint(self.name, self.waypoint_type, self.position)
for attr in dir(self):
if attr == "control_point":
obj.control_point = self.control_point
elif attr == "targets":
obj.targets = self.targets
elif "__" in attr or attr not in obj.__dataclass_fields__:
continue
else:
attr_copy = deepcopy(getattr(self, attr))
setattr(obj, attr, attr_copy)
return obj

View File

@@ -235,6 +235,7 @@ class Package(RadioFrequencyContainer):
clone.time_over_target = deepcopy(package.time_over_target)
for f in package.flights:
cf = Flight.clone_flight(f)
cf.flight_plan.layout = deepcopy(f.flight_plan.layout)
cf.package = clone
clone.add_flight(cf)
return clone

View File

@@ -74,6 +74,8 @@ class MizCampaignLoader:
AirDefence.Hawk_ln.id,
AirDefence.S_75M_Volhov.id,
AirDefence.x_5p73_s_125_ln.id,
AirDefence.NASAMS_LN_B.id,
AirDefence.NASAMS_LN_C.id,
}
SHORT_RANGE_SAM_UNIT_TYPES = {

View File

@@ -78,7 +78,11 @@ class PackageBuilder:
)
# If this is a client flight, set the start_type again to match the configured default
# https://github.com/dcs-liberation/dcs_liberation/issues/1567
if flight.roster is not None and flight.roster.player_count > 0:
if (
squadron.location.required_aircraft_start_type is None
and flight.roster is not None
and flight.roster.player_count > 0
):
flight.start_type = (
squadron.coalition.game.settings.default_start_type_client
)

View File

@@ -133,6 +133,21 @@ class PackageFulfiller:
threats[EscortType.Sead] = True
return threats
def can_plan_escort(self, type: EscortType) -> bool:
if type == EscortType.AirToAir:
return self.air_wing_can_plan(FlightType.ESCORT)
elif type == EscortType.Sead:
for task in [
FlightType.SEAD,
FlightType.SEAD_ESCORT,
FlightType.SEAD_SWEEP,
]:
if self.air_wing_can_plan(task):
return True
elif type == EscortType.Refuel:
return self.air_wing_can_plan(FlightType.REFUELING)
return False
def plan_mission(
self,
mission: ProposedMission,
@@ -207,7 +222,9 @@ class PackageFulfiller:
# This list was generated from the not None set, so this should be
# impossible.
assert escort.escort_type is not None
if needed_escorts[escort.escort_type]:
if needed_escorts[escort.escort_type] and self.can_plan_escort(
escort.escort_type
):
with tracer.trace("Flight planning"):
self.plan_flight(
mission, escort, builder, missing_types, purchase_multiplier

View File

@@ -40,6 +40,7 @@ from game.radio.channels import (
ViperChannelNamer,
WarthogChannelNamer,
PhantomChannelNamer,
KiowaChannelNamer,
)
from game.utils import (
Distance,
@@ -116,6 +117,7 @@ class RadioConfig:
"a10c-legacy": LegacyWarthogChannelNamer,
"a10c-ii": WarthogChannelNamer,
"phantom": PhantomChannelNamer,
"kiowa": KiowaChannelNamer,
}[config.get("namer", "default")]

View File

@@ -119,7 +119,7 @@ class AircraftGenerator:
if not package.flights:
continue
for flight in package.flights:
if flight.alive:
if flight.alive and not isinstance(flight.state, Completed):
if not flight.squadron.location.runway_is_operational():
logging.warning(
f"Runway not operational, skipping flight: {flight.flight_type}"

View File

@@ -93,7 +93,6 @@ class MigrationUnpickler(pickle.Unpickler):
except AttributeError:
alternate = name.split('.')[:-1] + [name.split('.')[-1][0].lower() + name.split('.')[-1][1:]]
name = '.'.join(alternate)
print(name)
return super().find_class(module, name)
# fmt: on

View File

@@ -421,3 +421,16 @@ class PhantomChannelNamer(ChannelNamer):
@classmethod
def name(cls) -> str:
return "phantom"
class KiowaChannelNamer(ChannelNamer):
"""Channel namer for OH58D Kiowa Warrior"""
@staticmethod
def channel_name(radio_id: int, channel_id: int) -> str:
radio_name = ["UHF AM", "VHF AM", "VHF FM1", "VHF FM2"][radio_id - 1]
return f"{radio_name} Ch {channel_id}"
@classmethod
def name(cls) -> str:
return "kiowa"

View File

@@ -661,10 +661,16 @@ class PendingTransfers:
def _cancel_transport_air(
self, transport: Airlift, _transfer: TransferOrder
) -> None:
from game.sim import GameUpdateEvents
from game.server import EventStream
flight = transport.flight
flight.package.remove_flight(flight)
events = GameUpdateEvents().delete_flight(flight)
if not flight.package.flights:
self.game.ato_for(self.player).remove_package(flight.package)
events = events.delete_flights_in_package(flight.package)
EventStream().put_nowait(events)
@cancel_transport.register
def _cancel_transport_convoy(