mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Add cargo ships to the sim, track kills.
Not targetable yet. https://github.com/Khopa/dcs_liberation/issues/826
This commit is contained in:
@@ -22,6 +22,7 @@ from dcs.unittype import FlyingType, UnitType
|
||||
|
||||
from game import db
|
||||
from game.theater import Airfield, ControlPoint
|
||||
from game.transfers import CargoShip
|
||||
from game.unitmap import (
|
||||
AirliftUnit,
|
||||
Building,
|
||||
@@ -70,6 +71,9 @@ class GroundLosses:
|
||||
player_convoy: List[ConvoyUnit] = field(default_factory=list)
|
||||
enemy_convoy: List[ConvoyUnit] = field(default_factory=list)
|
||||
|
||||
player_cargo_ships: List[CargoShip] = field(default_factory=list)
|
||||
enemy_cargo_ships: List[CargoShip] = field(default_factory=list)
|
||||
|
||||
player_airlifts: List[AirliftUnit] = field(default_factory=list)
|
||||
enemy_airlifts: List[AirliftUnit] = field(default_factory=list)
|
||||
|
||||
@@ -138,6 +142,11 @@ class Debriefing:
|
||||
yield from self.ground_losses.player_convoy
|
||||
yield from self.ground_losses.enemy_convoy
|
||||
|
||||
@property
|
||||
def cargo_ship_losses(self) -> Iterator[CargoShip]:
|
||||
yield from self.ground_losses.player_cargo_ships
|
||||
yield from self.ground_losses.enemy_cargo_ships
|
||||
|
||||
@property
|
||||
def airlift_losses(self) -> Iterator[AirliftUnit]:
|
||||
yield from self.ground_losses.player_airlifts
|
||||
@@ -181,6 +190,17 @@ class Debriefing:
|
||||
losses_by_type[loss.unit_type] += 1
|
||||
return losses_by_type
|
||||
|
||||
def cargo_ship_losses_by_type(self, player: bool) -> Dict[Type[UnitType], int]:
|
||||
losses_by_type: Dict[Type[UnitType], int] = defaultdict(int)
|
||||
if player:
|
||||
ships = self.ground_losses.player_cargo_ships
|
||||
else:
|
||||
ships = self.ground_losses.enemy_cargo_ships
|
||||
for ship in ships:
|
||||
for unit_type, count in ship.units.items():
|
||||
losses_by_type[unit_type] += count
|
||||
return losses_by_type
|
||||
|
||||
def airlift_losses_by_type(self, player: bool) -> Dict[Type[UnitType], int]:
|
||||
losses_by_type: Dict[Type[UnitType], int] = defaultdict(int)
|
||||
if player:
|
||||
@@ -237,6 +257,14 @@ class Debriefing:
|
||||
losses.enemy_convoy.append(convoy_unit)
|
||||
continue
|
||||
|
||||
cargo_ship = self.unit_map.cargo_ship(unit_name)
|
||||
if cargo_ship is not None:
|
||||
if cargo_ship.player_owned:
|
||||
losses.player_cargo_ships.append(cargo_ship)
|
||||
else:
|
||||
losses.enemy_cargo_ships.append(cargo_ship)
|
||||
continue
|
||||
|
||||
ground_object_unit = self.unit_map.ground_object_unit(unit_name)
|
||||
if ground_object_unit is not None:
|
||||
if ground_object_unit.ground_object.control_point.captured:
|
||||
|
||||
@@ -169,6 +169,15 @@ class Event:
|
||||
logging.info(f"{unit_type} destroyed in {convoy_name}")
|
||||
convoy.kill_unit(unit_type)
|
||||
|
||||
@staticmethod
|
||||
def commit_cargo_ship_losses(debriefing: Debriefing) -> None:
|
||||
for ship in debriefing.cargo_ship_losses:
|
||||
logging.info(
|
||||
f"All units destroyed in cargo ship from {ship.origin} to "
|
||||
f"{ship.destination}."
|
||||
)
|
||||
ship.kill_all()
|
||||
|
||||
@staticmethod
|
||||
def commit_airlift_losses(debriefing: Debriefing) -> None:
|
||||
for loss in debriefing.airlift_losses:
|
||||
|
||||
@@ -23,7 +23,8 @@ from gen.airsupportgen import AirSupport, AirSupportConflictGenerator
|
||||
from gen.armor import GroundConflictGenerator, JtacInfo
|
||||
from gen.beacons import load_beacons_for_terrain
|
||||
from gen.briefinggen import BriefingGenerator, MissionInfoGenerator
|
||||
from gen.convoys import ConvoyGenerator
|
||||
from gen.cargoshipgen import CargoShipGenerator
|
||||
from gen.convoygen import ConvoyGenerator
|
||||
from gen.environmentgen import EnvironmentGenerator
|
||||
from gen.forcedoptionsgen import ForcedOptionsGenerator
|
||||
from gen.groundobjectsgen import GroundObjectsGenerator
|
||||
@@ -304,7 +305,7 @@ class Operation:
|
||||
# Set mission time and weather conditions.
|
||||
EnvironmentGenerator(cls.current_mission, cls.game.conditions).generate()
|
||||
cls._generate_ground_units()
|
||||
cls._generate_convoys()
|
||||
cls._generate_transports()
|
||||
cls._generate_destroyed_units()
|
||||
cls._generate_air_units()
|
||||
cls.assign_channels_to_flights(
|
||||
@@ -426,9 +427,10 @@ class Operation:
|
||||
cls.jtacs.extend(ground_conflict_gen.jtacs)
|
||||
|
||||
@classmethod
|
||||
def _generate_convoys(cls) -> None:
|
||||
def _generate_transports(cls) -> None:
|
||||
"""Generates convoys for unit transfers by road."""
|
||||
ConvoyGenerator(cls.current_mission, cls.game, cls.unit_map).generate()
|
||||
CargoShipGenerator(cls.current_mission, cls.game, cls.unit_map).generate()
|
||||
|
||||
@classmethod
|
||||
def reset_naming_ids(cls):
|
||||
|
||||
@@ -4,7 +4,7 @@ import logging
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass, field
|
||||
from functools import singledispatchmethod
|
||||
from typing import Dict, Iterator, List, Optional, TYPE_CHECKING, Type
|
||||
from typing import Dict, Generic, Iterator, List, Optional, TYPE_CHECKING, Type, TypeVar
|
||||
|
||||
from dcs.mapping import Point
|
||||
from dcs.unittype import FlyingType, VehicleType
|
||||
@@ -271,6 +271,10 @@ class MultiGroupTransport(MissionTarget, Transport):
|
||||
pass
|
||||
raise KeyError
|
||||
|
||||
def kill_all(self) -> None:
|
||||
for transfer in self.transfers:
|
||||
transfer.kill_all()
|
||||
|
||||
def disband(self) -> None:
|
||||
for transfer in list(self.transfers):
|
||||
self.remove_units(transfer)
|
||||
@@ -298,14 +302,6 @@ class MultiGroupTransport(MissionTarget, Transport):
|
||||
def description(self) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def route_start(self) -> Point:
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def route_end(self) -> Point:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Convoy(MultiGroupTransport):
|
||||
def __init__(self, origin: ControlPoint, destination: ControlPoint) -> None:
|
||||
@@ -345,12 +341,8 @@ class CargoShip(MultiGroupTransport):
|
||||
yield from super().mission_types(for_player)
|
||||
|
||||
@property
|
||||
def route_start(self) -> Point:
|
||||
return self.origin.shipping_lanes[self.destination][0]
|
||||
|
||||
@property
|
||||
def route_end(self) -> Point:
|
||||
return self.destination.shipping_lanes[self.origin][-1]
|
||||
def route(self) -> List[Point]:
|
||||
return self.origin.shipping_lanes[self.destination]
|
||||
|
||||
def description(self) -> str:
|
||||
return f"On a ship to {self.destination}"
|
||||
@@ -359,16 +351,19 @@ class CargoShip(MultiGroupTransport):
|
||||
return None
|
||||
|
||||
|
||||
class TransportMap:
|
||||
TransportType = TypeVar("TransportType", bound=MultiGroupTransport)
|
||||
|
||||
|
||||
class TransportMap(Generic[TransportType]):
|
||||
def __init__(self) -> None:
|
||||
# Dict of origin -> destination -> transport.
|
||||
self.transports: Dict[
|
||||
ControlPoint, Dict[ControlPoint, MultiGroupTransport]
|
||||
ControlPoint, Dict[ControlPoint, TransportType]
|
||||
] = defaultdict(dict)
|
||||
|
||||
def create_transport(
|
||||
self, origin: ControlPoint, destination: ControlPoint
|
||||
) -> MultiGroupTransport:
|
||||
) -> TransportType:
|
||||
raise NotImplementedError
|
||||
|
||||
def transport_exists(self, origin: ControlPoint, destination: ControlPoint) -> bool:
|
||||
@@ -376,27 +371,27 @@ class TransportMap:
|
||||
|
||||
def find_transport(
|
||||
self, origin: ControlPoint, destination: ControlPoint
|
||||
) -> Optional[MultiGroupTransport]:
|
||||
) -> Optional[TransportType]:
|
||||
return self.transports[origin].get(destination)
|
||||
|
||||
def find_or_create_transport(
|
||||
self, origin: ControlPoint, destination: ControlPoint
|
||||
) -> MultiGroupTransport:
|
||||
) -> TransportType:
|
||||
transport = self.find_transport(origin, destination)
|
||||
if transport is None:
|
||||
transport = self.create_transport(origin, destination)
|
||||
self.transports[origin][destination] = transport
|
||||
return transport
|
||||
|
||||
def departing_from(self, origin: ControlPoint) -> Iterator[MultiGroupTransport]:
|
||||
def departing_from(self, origin: ControlPoint) -> Iterator[TransportType]:
|
||||
yield from self.transports[origin].values()
|
||||
|
||||
def travelling_to(self, destination: ControlPoint) -> Iterator[MultiGroupTransport]:
|
||||
def travelling_to(self, destination: ControlPoint) -> Iterator[TransportType]:
|
||||
for destination_dict in self.transports.values():
|
||||
if destination in destination_dict:
|
||||
yield destination_dict[destination]
|
||||
|
||||
def disband_transport(self, transport: MultiGroupTransport) -> None:
|
||||
def disband_transport(self, transport: TransportType) -> None:
|
||||
transport.disband()
|
||||
del self.transports[transport.origin][transport.destination]
|
||||
|
||||
@@ -416,7 +411,7 @@ class TransportMap:
|
||||
next_stop = self.next_stop_for(transfer)
|
||||
self.find_or_create_transport(transfer.position, next_stop).add_units(transfer)
|
||||
|
||||
def remove(self, transport: MultiGroupTransport, transfer: TransferOrder) -> None:
|
||||
def remove(self, transport: TransportType, transfer: TransferOrder) -> None:
|
||||
transport.remove_units(transfer)
|
||||
if not transport.transfers:
|
||||
self.disband_transport(transport)
|
||||
@@ -425,7 +420,7 @@ class TransportMap:
|
||||
for transport in list(self):
|
||||
self.disband_transport(transport)
|
||||
|
||||
def __iter__(self) -> Iterator[MultiGroupTransport]:
|
||||
def __iter__(self) -> Iterator[TransportType]:
|
||||
for destination_dict in self.transports.values():
|
||||
yield from destination_dict.values()
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ from dcs.unittype import VehicleType
|
||||
from game import db
|
||||
from game.theater import Airfield, ControlPoint, TheaterGroundObject
|
||||
from game.theater.theatergroundobject import BuildingGroundObject
|
||||
from game.transfers import MultiGroupTransport, TransferOrder
|
||||
from game.transfers import CargoShip, Convoy, TransferOrder
|
||||
from gen.flights.flight import Flight
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class GroundObjectUnit:
|
||||
@dataclass(frozen=True)
|
||||
class ConvoyUnit:
|
||||
unit_type: Type[VehicleType]
|
||||
convoy: MultiGroupTransport
|
||||
convoy: Convoy
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -51,6 +51,7 @@ class UnitMap:
|
||||
self.ground_object_units: Dict[str, GroundObjectUnit] = {}
|
||||
self.buildings: Dict[str, Building] = {}
|
||||
self.convoys: Dict[str, ConvoyUnit] = {}
|
||||
self.cargo_ships: Dict[str, CargoShip] = {}
|
||||
self.airlifts: Dict[str, AirliftUnit] = {}
|
||||
|
||||
def add_aircraft(self, group: FlyingGroup, flight: Flight) -> None:
|
||||
@@ -130,7 +131,7 @@ class UnitMap:
|
||||
def ground_object_unit(self, name: str) -> Optional[GroundObjectUnit]:
|
||||
return self.ground_object_units.get(name, None)
|
||||
|
||||
def add_convoy_units(self, group: Group, convoy: MultiGroupTransport) -> None:
|
||||
def add_convoy_units(self, group: Group, convoy: Convoy) -> None:
|
||||
for unit in group.units:
|
||||
# The actual name is a String (the pydcs translatable string), which
|
||||
# doesn't define __eq__.
|
||||
@@ -149,6 +150,23 @@ class UnitMap:
|
||||
def convoy_unit(self, name: str) -> Optional[ConvoyUnit]:
|
||||
return self.convoys.get(name, None)
|
||||
|
||||
def add_cargo_ship(self, group: Group, ship: CargoShip) -> None:
|
||||
if len(group.units) > 1:
|
||||
# Cargo ship "groups" are single units. Killing the one ship kills the whole
|
||||
# transfer. If we ever want to add escorts or create multiple cargo ships in
|
||||
# a convoy of ships that logic needs to change.
|
||||
raise ValueError("Expected cargo ship to be a single unit group.")
|
||||
unit = group.units[0]
|
||||
# The actual name is a String (the pydcs translatable string), which
|
||||
# doesn't define __eq__.
|
||||
name = str(unit.name)
|
||||
if name in self.cargo_ships:
|
||||
raise RuntimeError(f"Duplicate cargo ship: {name}")
|
||||
self.cargo_ships[name] = ship
|
||||
|
||||
def cargo_ship(self, name: str) -> Optional[CargoShip]:
|
||||
return self.cargo_ships.get(name, None)
|
||||
|
||||
def add_airlift_units(self, group: FlyingGroup, transfer: TransferOrder) -> None:
|
||||
for transport, cargo_type in zip(group.units, transfer.iter_units()):
|
||||
# The actual name is a String (the pydcs translatable string), which
|
||||
|
||||
Reference in New Issue
Block a user