mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
refactor of previous commits
refactor to enum typing and many other fixes fix tests attempt to fix some typescript more typescript fixes more typescript test fixes revert all API changes update to pydcs mypy fixes Use properties to check if player is blue/red/neutral update requirements.txt black -_- bump pydcs and fix mypy add opponent property bump pydcs
This commit is contained in:
parent
362ce66f80
commit
31c80dfd02
@ -264,6 +264,11 @@ class ForceGroup:
|
||||
units = unit_group.generate_units(
|
||||
ground_object, unit_type, unit_count, fixed_pos, fixed_hdg
|
||||
)
|
||||
# If the control point is neutral, the units are dead
|
||||
if ground_object.control_point.captured.is_neutral:
|
||||
for unit in units:
|
||||
if not unit.is_static:
|
||||
unit.alive = False
|
||||
# Get or create the TheaterGroup
|
||||
ground_group = ground_object.group_by_name(group_name)
|
||||
if ground_group is not None:
|
||||
|
||||
@ -35,6 +35,7 @@ if TYPE_CHECKING:
|
||||
from game.sim.gameupdateevents import GameUpdateEvents
|
||||
from game.sim.simulationresults import SimulationResults
|
||||
from game.squadrons import Squadron, Pilot
|
||||
from game.theater.player import Player
|
||||
from game.transfers import TransferOrder
|
||||
from game.data.weapons import WeaponType
|
||||
from .flightmember import FlightMember
|
||||
@ -174,7 +175,7 @@ class Flight(
|
||||
self.roster = FlightMembers.from_roster(self, self.roster)
|
||||
|
||||
@property
|
||||
def blue(self) -> bool:
|
||||
def blue(self) -> Player:
|
||||
return self.squadron.player
|
||||
|
||||
@property
|
||||
|
||||
@ -11,7 +11,7 @@ from ..packagewaypoints import PackageWaypoints
|
||||
if TYPE_CHECKING:
|
||||
from game.coalition import Coalition
|
||||
from game.data.doctrine import Doctrine
|
||||
from game.theater import ConflictTheater
|
||||
from game.theater import ConflictTheater, Player
|
||||
from game.threatzones import ThreatZones
|
||||
from ..flight import Flight
|
||||
from ..package import Package
|
||||
@ -71,7 +71,7 @@ class IBuilder(ABC, Generic[FlightPlanT, LayoutT]):
|
||||
return self.flight.coalition
|
||||
|
||||
@property
|
||||
def is_player(self) -> bool:
|
||||
def is_player(self) -> Player:
|
||||
return self.coalition.player
|
||||
|
||||
@property
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Optional, TYPE_CHECKING, Union
|
||||
|
||||
@ -26,6 +26,7 @@ from game.theater.controlpoint import (
|
||||
Carrier,
|
||||
ControlPoint,
|
||||
ControlPointType,
|
||||
Player,
|
||||
Fob,
|
||||
Lha,
|
||||
OffMapSpawn,
|
||||
@ -123,9 +124,13 @@ class MizCampaignLoader:
|
||||
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
|
||||
)
|
||||
if airport.dynamic_spawn:
|
||||
starting_coalition = Player.NEUTRAL
|
||||
elif airport.is_blue():
|
||||
starting_coalition = Player.BLUE
|
||||
else:
|
||||
starting_coalition = Player.RED
|
||||
cp = Airfield(airport, self.theater, starting_coalition, 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".
|
||||
@ -133,43 +138,44 @@ class MizCampaignLoader:
|
||||
|
||||
return cp
|
||||
|
||||
def country(self, blue: bool) -> Country:
|
||||
country = self.mission.country(
|
||||
self.BLUE_COUNTRY.name if blue else self.RED_COUNTRY.name
|
||||
)
|
||||
def country(self, blue: Player) -> Country:
|
||||
if blue.is_blue:
|
||||
country = self.mission.country(self.BLUE_COUNTRY.name)
|
||||
else:
|
||||
country = self.mission.country(self.RED_COUNTRY.name)
|
||||
# Should be guaranteed because we initialized them.
|
||||
assert country
|
||||
return country
|
||||
|
||||
@property
|
||||
def blue(self) -> Country:
|
||||
return self.country(blue=True)
|
||||
return self.country(blue=Player.BLUE)
|
||||
|
||||
@property
|
||||
def red(self) -> Country:
|
||||
return self.country(blue=False)
|
||||
return self.country(blue=Player.RED)
|
||||
|
||||
def off_map_spawns(self, blue: bool) -> Iterator[PlaneGroup]:
|
||||
def off_map_spawns(self, blue: Player) -> Iterator[PlaneGroup]:
|
||||
for group in self.country(blue).plane_group:
|
||||
if group.units[0].type == self.OFF_MAP_UNIT_TYPE:
|
||||
yield group
|
||||
|
||||
def carriers(self, blue: bool) -> Iterator[ShipGroup]:
|
||||
def carriers(self, blue: Player) -> Iterator[ShipGroup]:
|
||||
for group in self.country(blue).ship_group:
|
||||
if group.units[0].type == self.CV_UNIT_TYPE:
|
||||
yield group
|
||||
|
||||
def lhas(self, blue: bool) -> Iterator[ShipGroup]:
|
||||
def lhas(self, blue: Player) -> Iterator[ShipGroup]:
|
||||
for group in self.country(blue).ship_group:
|
||||
if group.units[0].type == self.LHA_UNIT_TYPE:
|
||||
yield group
|
||||
|
||||
def fobs(self, blue: bool) -> Iterator[VehicleGroup]:
|
||||
def fobs(self, blue: Player) -> Iterator[VehicleGroup]:
|
||||
for group in self.country(blue).vehicle_group:
|
||||
if group.units[0].type == self.FOB_UNIT_TYPE:
|
||||
yield group
|
||||
|
||||
def invisible_fobs(self, blue: bool) -> Iterator[VehicleGroup]:
|
||||
def invisible_fobs(self, blue: Player) -> Iterator[VehicleGroup]:
|
||||
for group in self.country(blue).vehicle_group:
|
||||
if group.units[0].type == self.INVISIBLE_FOB_UNIT_TYPE:
|
||||
yield group
|
||||
@ -290,12 +296,12 @@ class MizCampaignLoader:
|
||||
def control_points(self) -> dict[UUID, ControlPoint]:
|
||||
control_points = {}
|
||||
for airport in self.mission.terrain.airport_list():
|
||||
if airport.is_blue() or airport.is_red():
|
||||
if airport.is_blue() or airport.is_red() or airport.is_neutral():
|
||||
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):
|
||||
for blue in (Player.RED, Player.BLUE):
|
||||
for group in self.off_map_spawns(blue):
|
||||
control_point = OffMapSpawn(
|
||||
str(group.name), group.position, self.theater, starts_blue=blue
|
||||
@ -348,13 +354,13 @@ class MizCampaignLoader:
|
||||
|
||||
@property
|
||||
def front_line_path_groups(self) -> Iterator[VehicleGroup]:
|
||||
for group in self.country(blue=True).vehicle_group:
|
||||
for group in self.country(blue=Player.BLUE).vehicle_group:
|
||||
if group.units[0].type == self.FRONT_LINE_UNIT_TYPE:
|
||||
yield group
|
||||
|
||||
@property
|
||||
def shipping_lane_groups(self) -> Iterator[ShipGroup]:
|
||||
for group in self.country(blue=True).ship_group:
|
||||
for group in self.country(blue=Player.BLUE).ship_group:
|
||||
if group.units[0].type == self.SHIPPING_LANE_UNIT_TYPE:
|
||||
yield group
|
||||
|
||||
@ -378,7 +384,7 @@ class MizCampaignLoader:
|
||||
|
||||
@property
|
||||
def cp_convoy_spawns(self) -> Iterator[VehicleGroup]:
|
||||
for group in self.country(blue=True).vehicle_group:
|
||||
for group in self.country(blue=Player.BLUE).vehicle_group:
|
||||
if group.units[0].type == self.CP_CONVOY_SPAWN_TYPE:
|
||||
yield group
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ from game.procurement import AircraftProcurementRequest, ProcurementAi
|
||||
from game.profiling import MultiEventTracer, logged_duration
|
||||
from game.squadrons import AirWing
|
||||
from game.theater.bullseye import Bullseye
|
||||
from game.theater.player import Player
|
||||
from game.theater.transitnetwork import TransitNetwork, TransitNetworkBuilder
|
||||
from game.threatzones import ThreatZones
|
||||
from game.transfers import PendingTransfers
|
||||
@ -32,7 +33,7 @@ if TYPE_CHECKING:
|
||||
|
||||
class Coalition:
|
||||
def __init__(
|
||||
self, game: Game, faction: Faction, budget: float, player: bool
|
||||
self, game: Game, faction: Faction, budget: float, player: Player
|
||||
) -> None:
|
||||
self.game = game
|
||||
self.player = player
|
||||
@ -68,9 +69,11 @@ class Coalition:
|
||||
|
||||
@property
|
||||
def coalition_id(self) -> int:
|
||||
if self.player:
|
||||
if self.player.is_blue:
|
||||
return 2
|
||||
return 1
|
||||
elif self.player.is_red:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@property
|
||||
def opponent(self) -> Coalition:
|
||||
@ -95,8 +98,9 @@ class Coalition:
|
||||
state = self.__dict__.copy()
|
||||
# Avoid persisting any volatile types that can be deterministically
|
||||
# recomputed on load for the sake of save compatibility.
|
||||
del state["_threat_zone"]
|
||||
del state["_navmesh"]
|
||||
if state["player"] != Player.NEUTRAL:
|
||||
del state["_threat_zone"]
|
||||
del state["_navmesh"]
|
||||
del state["faker"]
|
||||
return state
|
||||
|
||||
@ -203,7 +207,7 @@ class Coalition:
|
||||
squadron.refund_orders()
|
||||
|
||||
def plan_missions(self, now: datetime) -> None:
|
||||
color = "Blue" if self.player else "Red"
|
||||
color = "Blue" if self.player.is_blue else "Red"
|
||||
with MultiEventTracer() as tracer:
|
||||
with tracer.trace(f"{color} mission planning"):
|
||||
with tracer.trace(f"{color} mission identification"):
|
||||
@ -220,7 +224,7 @@ class Coalition:
|
||||
# to ground forces and 1400 to aircraft. After that the budget will be spent
|
||||
# proportionally based on how much is already invested.
|
||||
|
||||
if self.player:
|
||||
if self.player.is_blue:
|
||||
manage_runways = self.game.settings.automate_runway_repair
|
||||
manage_front_line = self.game.settings.automate_front_line_reinforcements
|
||||
manage_aircraft = self.game.settings.automate_aircraft_reinforcements
|
||||
|
||||
@ -16,6 +16,7 @@ from game.theater import (
|
||||
OffMapSpawn,
|
||||
ParkingType,
|
||||
NavalControlPoint,
|
||||
Player,
|
||||
)
|
||||
from game.theater.theatergroundobject import (
|
||||
BuildingGroundObject,
|
||||
@ -35,7 +36,7 @@ MissionTargetType = TypeVar("MissionTargetType", bound=MissionTarget)
|
||||
class ObjectiveFinder:
|
||||
"""Identifies potential objectives for the mission planner."""
|
||||
|
||||
def __init__(self, game: Game, is_player: bool) -> None:
|
||||
def __init__(self, game: Game, is_player: Player) -> None:
|
||||
self.game = game
|
||||
self.is_player = is_player
|
||||
|
||||
@ -154,7 +155,7 @@ class ObjectiveFinder:
|
||||
airfields_in_proximity = self.closest_airfields_to(cp)
|
||||
airbase_threat_range = self.game.settings.airbase_threat_range
|
||||
if (
|
||||
not self.is_player
|
||||
self.is_player.is_red
|
||||
and randint(1, 100)
|
||||
> self.game.settings.opfor_autoplanner_aggressiveness
|
||||
):
|
||||
@ -216,7 +217,7 @@ class ObjectiveFinder:
|
||||
|
||||
def farthest_friendly_control_point(self) -> ControlPoint:
|
||||
"""Finds the friendly control point that is farthest from any threats."""
|
||||
threat_zones = self.game.threat_zone_for(not self.is_player)
|
||||
threat_zones = self.game.threat_zone_for(self.is_player.opponent)
|
||||
|
||||
farthest = None
|
||||
max_distance = meters(0)
|
||||
@ -234,7 +235,7 @@ class ObjectiveFinder:
|
||||
|
||||
def closest_friendly_control_point(self) -> ControlPoint:
|
||||
"""Finds the friendly control point that is closest to any threats."""
|
||||
threat_zones = self.game.threat_zone_for(not self.is_player)
|
||||
threat_zones = self.game.threat_zone_for(self.is_player.opponent)
|
||||
|
||||
closest = None
|
||||
min_distance = meters(math.inf)
|
||||
@ -258,14 +259,14 @@ class ObjectiveFinder:
|
||||
return (
|
||||
c
|
||||
for c in self.game.theater.controlpoints
|
||||
if not c.is_friendly(self.is_player)
|
||||
if not c.is_friendly(self.is_player) and c.captured != Player.NEUTRAL
|
||||
)
|
||||
|
||||
def prioritized_points(self) -> list[ControlPoint]:
|
||||
prioritized = []
|
||||
capturable_later = []
|
||||
isolated = []
|
||||
for cp in self.game.theater.control_points_for(not self.is_player):
|
||||
for cp in self.game.theater.control_points_for(self.is_player.opponent):
|
||||
if cp.is_isolated:
|
||||
isolated.append(cp)
|
||||
continue
|
||||
|
||||
@ -43,7 +43,9 @@ class PackageFulfiller:
|
||||
|
||||
@property
|
||||
def is_player(self) -> bool:
|
||||
return self.coalition.player
|
||||
if self.coalition.player.is_blue:
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def ato(self) -> AirTaskingOrder:
|
||||
|
||||
@ -10,7 +10,7 @@ from game.commander.tasks.compound.reduceenemyfrontlinecapacity import (
|
||||
from game.commander.tasks.primitive.breakthroughattack import BreakthroughAttack
|
||||
from game.commander.theaterstate import TheaterState
|
||||
from game.htn import CompoundTask, Method
|
||||
from game.theater import ControlPoint, FrontLine
|
||||
from game.theater import ControlPoint, FrontLine, Player
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@ -26,12 +26,12 @@ class CaptureBase(CompoundTask[TheaterState]):
|
||||
def enemy_cp(self, state: TheaterState) -> ControlPoint:
|
||||
return self.front_line.control_point_hostile_to(state.context.coalition.player)
|
||||
|
||||
def units_deployable(self, state: TheaterState, player: bool) -> int:
|
||||
def units_deployable(self, state: TheaterState, player: Player) -> int:
|
||||
cp = self.front_line.control_point_friendly_to(player)
|
||||
ammo_depots = list(state.ammo_dumps_at(cp))
|
||||
return cp.deployable_front_line_units_with(len(ammo_depots))
|
||||
|
||||
def unit_cap(self, state: TheaterState, player: bool) -> int:
|
||||
def unit_cap(self, state: TheaterState, player: Player) -> int:
|
||||
cp = self.front_line.control_point_friendly_to(player)
|
||||
ammo_depots = list(state.ammo_dumps_at(cp))
|
||||
return cp.front_line_capacity_with(len(ammo_depots))
|
||||
|
||||
@ -11,10 +11,11 @@ from game.theater import FrontLine
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.coalition import Coalition
|
||||
from game.theater.player import Player
|
||||
|
||||
|
||||
class FrontLineStanceTask(TheaterCommanderTask, ABC):
|
||||
def __init__(self, front_line: FrontLine, player: bool) -> None:
|
||||
def __init__(self, front_line: FrontLine, player: Player) -> None:
|
||||
self.front_line = front_line
|
||||
self.friendly_cp = self.front_line.control_point_friendly_to(player)
|
||||
self.enemy_cp = self.front_line.control_point_hostile_to(player)
|
||||
|
||||
@ -6,7 +6,7 @@ from game.ato.flighttype import FlightType
|
||||
from game.commander.missionproposals import EscortType
|
||||
from game.commander.tasks.packageplanningtask import PackagePlanningTask
|
||||
from game.commander.theaterstate import TheaterState
|
||||
from game.theater import FrontLine
|
||||
from game.theater import FrontLine, Player
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -19,9 +19,8 @@ class PlanCas(PackagePlanningTask[FrontLine]):
|
||||
# An exception is made for turn zero since that's not being truly planned, but
|
||||
# just to determine what missions should be planned on turn 1 (when there *will*
|
||||
# be ground units) and what aircraft should be ordered.
|
||||
enemy_cp = self.target.control_point_friendly_to(
|
||||
player=not state.context.coalition.player
|
||||
)
|
||||
player = state.context.coalition.player.opponent
|
||||
enemy_cp = self.target.control_point_friendly_to(player)
|
||||
if enemy_cp.deployable_front_line_units == 0 and state.context.turn > 0:
|
||||
return False
|
||||
return super().preconditions_met(state)
|
||||
|
||||
@ -67,10 +67,11 @@ from game.profiling import MultiEventTracer
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game import Game
|
||||
from game.theater.player import Player
|
||||
|
||||
|
||||
class TheaterCommander(Planner[TheaterState, TheaterCommanderTask]):
|
||||
def __init__(self, game: Game, player: bool) -> None:
|
||||
def __init__(self, game: Game, player: Player) -> None:
|
||||
super().__init__(
|
||||
PlanNextAction(
|
||||
aircraft_cold_start=game.settings.default_start_type is StartType.COLD
|
||||
|
||||
@ -20,6 +20,7 @@ from game.theater import (
|
||||
ControlPoint,
|
||||
FrontLine,
|
||||
MissionTarget,
|
||||
Player,
|
||||
)
|
||||
from game.theater.theatergroundobject import (
|
||||
BuildingGroundObject,
|
||||
@ -152,7 +153,7 @@ class TheaterState(WorldState["TheaterState"]):
|
||||
|
||||
@classmethod
|
||||
def from_game(
|
||||
cls, game: Game, player: bool, now: datetime, tracer: MultiEventTracer
|
||||
cls, game: Game, player: Player, now: datetime, tracer: MultiEventTracer
|
||||
) -> TheaterState:
|
||||
coalition = game.coalition_for(player)
|
||||
finder = ObjectiveFinder(game, player)
|
||||
@ -213,8 +214,8 @@ class TheaterState(WorldState["TheaterState"]):
|
||||
)
|
||||
),
|
||||
strike_targets=list(finder.strike_targets()),
|
||||
enemy_barcaps=list(game.theater.control_points_for(not player)),
|
||||
threat_zones=game.threat_zone_for(not player),
|
||||
enemy_barcaps=list(game.theater.control_points_for(player.opponent)),
|
||||
threat_zones=game.threat_zone_for(player.opponent),
|
||||
vulnerable_control_points=vulnerable_control_points,
|
||||
control_point_priority_queue=ordered_capturable_points,
|
||||
priority_cp=(
|
||||
|
||||
@ -16,7 +16,7 @@ from uuid import UUID
|
||||
|
||||
from game.dcs.aircrafttype import AircraftType
|
||||
from game.dcs.groundunittype import GroundUnitType
|
||||
from game.theater import Airfield, ControlPoint
|
||||
from game.theater import Airfield, ControlPoint, Player
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game import Game
|
||||
@ -45,9 +45,9 @@ class AirLosses:
|
||||
def losses(self) -> Iterator[FlyingUnit]:
|
||||
return itertools.chain(self.player, self.enemy)
|
||||
|
||||
def by_type(self, player: bool) -> Dict[AircraftType, int]:
|
||||
def by_type(self, player: Player) -> Dict[AircraftType, int]:
|
||||
losses_by_type: Dict[AircraftType, int] = defaultdict(int)
|
||||
losses = self.player if player else self.enemy
|
||||
losses = self.player if player.is_blue else self.enemy
|
||||
for loss in losses:
|
||||
losses_by_type[loss.flight.unit_type] += 1
|
||||
return losses_by_type
|
||||
@ -87,7 +87,7 @@ class GroundLosses:
|
||||
@dataclass(frozen=True)
|
||||
class BaseCaptureEvent:
|
||||
control_point: ControlPoint
|
||||
captured_by_player: bool
|
||||
captured_by_player: Player
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@ -166,7 +166,7 @@ class Debriefing:
|
||||
|
||||
def merge_simulation_results(self, results: SimulationResults) -> None:
|
||||
for air_loss in results.air_losses:
|
||||
if air_loss.flight.squadron.player:
|
||||
if air_loss.flight.squadron.player.is_blue:
|
||||
self.air_losses.player.append(air_loss)
|
||||
else:
|
||||
self.air_losses.enemy.append(air_loss)
|
||||
@ -209,9 +209,9 @@ class Debriefing:
|
||||
def casualty_count(self, control_point: ControlPoint) -> int:
|
||||
return len([x for x in self.front_line_losses if x.origin == control_point])
|
||||
|
||||
def front_line_losses_by_type(self, player: bool) -> dict[GroundUnitType, int]:
|
||||
def front_line_losses_by_type(self, player: Player) -> dict[GroundUnitType, int]:
|
||||
losses_by_type: dict[GroundUnitType, int] = defaultdict(int)
|
||||
if player:
|
||||
if player.is_blue:
|
||||
losses = self.ground_losses.player_front_line
|
||||
else:
|
||||
losses = self.ground_losses.enemy_front_line
|
||||
@ -219,9 +219,9 @@ class Debriefing:
|
||||
losses_by_type[loss.unit_type] += 1
|
||||
return losses_by_type
|
||||
|
||||
def convoy_losses_by_type(self, player: bool) -> dict[GroundUnitType, int]:
|
||||
def convoy_losses_by_type(self, player: Player) -> dict[GroundUnitType, int]:
|
||||
losses_by_type: dict[GroundUnitType, int] = defaultdict(int)
|
||||
if player:
|
||||
if player.is_blue:
|
||||
losses = self.ground_losses.player_convoy
|
||||
else:
|
||||
losses = self.ground_losses.enemy_convoy
|
||||
@ -229,9 +229,9 @@ class Debriefing:
|
||||
losses_by_type[loss.unit_type] += 1
|
||||
return losses_by_type
|
||||
|
||||
def cargo_ship_losses_by_type(self, player: bool) -> dict[GroundUnitType, int]:
|
||||
def cargo_ship_losses_by_type(self, player: Player) -> dict[GroundUnitType, int]:
|
||||
losses_by_type: dict[GroundUnitType, int] = defaultdict(int)
|
||||
if player:
|
||||
if player.is_blue:
|
||||
ships = self.ground_losses.player_cargo_ships
|
||||
else:
|
||||
ships = self.ground_losses.enemy_cargo_ships
|
||||
@ -240,9 +240,9 @@ class Debriefing:
|
||||
losses_by_type[unit_type] += count
|
||||
return losses_by_type
|
||||
|
||||
def airlift_losses_by_type(self, player: bool) -> dict[GroundUnitType, int]:
|
||||
def airlift_losses_by_type(self, player: Player) -> dict[GroundUnitType, int]:
|
||||
losses_by_type: dict[GroundUnitType, int] = defaultdict(int)
|
||||
if player:
|
||||
if player.is_blue:
|
||||
losses = self.ground_losses.player_airlifts
|
||||
else:
|
||||
losses = self.ground_losses.enemy_airlifts
|
||||
@ -251,9 +251,9 @@ class Debriefing:
|
||||
losses_by_type[unit_type] += 1
|
||||
return losses_by_type
|
||||
|
||||
def ground_object_losses_by_type(self, player: bool) -> Dict[str, int]:
|
||||
def ground_object_losses_by_type(self, player: Player) -> Dict[str, int]:
|
||||
losses_by_type: Dict[str, int] = defaultdict(int)
|
||||
if player:
|
||||
if player.is_blue:
|
||||
losses = self.ground_losses.player_ground_objects
|
||||
else:
|
||||
losses = self.ground_losses.enemy_ground_objects
|
||||
@ -261,9 +261,9 @@ class Debriefing:
|
||||
losses_by_type[loss.theater_unit.type.id] += 1
|
||||
return losses_by_type
|
||||
|
||||
def scenery_losses_by_type(self, player: bool) -> Dict[str, int]:
|
||||
def scenery_losses_by_type(self, player: Player) -> Dict[str, int]:
|
||||
losses_by_type: Dict[str, int] = defaultdict(int)
|
||||
if player:
|
||||
if player.is_blue:
|
||||
losses = self.ground_losses.player_scenery
|
||||
else:
|
||||
losses = self.ground_losses.enemy_scenery
|
||||
@ -279,7 +279,7 @@ class Debriefing:
|
||||
if aircraft is None:
|
||||
logging.error(f"Could not find Flight matching {unit_name}")
|
||||
continue
|
||||
if aircraft.flight.departure.captured:
|
||||
if aircraft.flight.departure.captured.is_blue:
|
||||
player_losses.append(aircraft)
|
||||
else:
|
||||
enemy_losses.append(aircraft)
|
||||
@ -290,7 +290,7 @@ class Debriefing:
|
||||
for unit_name in self.state_data.killed_ground_units:
|
||||
front_line_unit = self.unit_map.front_line_unit(unit_name)
|
||||
if front_line_unit is not None:
|
||||
if front_line_unit.origin.captured:
|
||||
if front_line_unit.origin.captured.is_blue:
|
||||
losses.player_front_line.append(front_line_unit)
|
||||
else:
|
||||
losses.enemy_front_line.append(front_line_unit)
|
||||
@ -298,7 +298,7 @@ class Debriefing:
|
||||
|
||||
convoy_unit = self.unit_map.convoy_unit(unit_name)
|
||||
if convoy_unit is not None:
|
||||
if convoy_unit.convoy.player_owned:
|
||||
if convoy_unit.convoy.player_owned.is_blue:
|
||||
losses.player_convoy.append(convoy_unit)
|
||||
else:
|
||||
losses.enemy_convoy.append(convoy_unit)
|
||||
@ -306,7 +306,7 @@ class Debriefing:
|
||||
|
||||
cargo_ship = self.unit_map.cargo_ship(unit_name)
|
||||
if cargo_ship is not None:
|
||||
if cargo_ship.player_owned:
|
||||
if cargo_ship.player_owned.is_blue:
|
||||
losses.player_cargo_ships.append(cargo_ship)
|
||||
else:
|
||||
losses.enemy_cargo_ships.append(cargo_ship)
|
||||
@ -314,7 +314,9 @@ class Debriefing:
|
||||
|
||||
ground_object = self.unit_map.theater_units(unit_name)
|
||||
if ground_object is not None:
|
||||
if ground_object.theater_unit.ground_object.is_friendly(to_player=True):
|
||||
if ground_object.theater_unit.ground_object.is_friendly(
|
||||
to_player=Player.BLUE
|
||||
):
|
||||
losses.player_ground_objects.append(ground_object)
|
||||
else:
|
||||
losses.enemy_ground_objects.append(ground_object)
|
||||
@ -323,7 +325,9 @@ class Debriefing:
|
||||
scenery_object = self.unit_map.scenery_object(unit_name)
|
||||
# Try appending object to the name, because we do this for building statics.
|
||||
if scenery_object is not None:
|
||||
if scenery_object.ground_unit.ground_object.is_friendly(to_player=True):
|
||||
if scenery_object.ground_unit.ground_object.is_friendly(
|
||||
to_player=Player.BLUE
|
||||
):
|
||||
losses.player_scenery.append(scenery_object)
|
||||
else:
|
||||
losses.enemy_scenery.append(scenery_object)
|
||||
@ -331,9 +335,9 @@ class Debriefing:
|
||||
|
||||
airfield = self.unit_map.airfield(unit_name)
|
||||
if airfield is not None:
|
||||
if airfield.captured:
|
||||
if airfield.captured.is_blue:
|
||||
losses.player_airfields.append(airfield)
|
||||
else:
|
||||
elif airfield.captured.is_red:
|
||||
losses.enemy_airfields.append(airfield)
|
||||
continue
|
||||
|
||||
@ -349,7 +353,7 @@ class Debriefing:
|
||||
for unit_name in self.state_data.killed_aircraft:
|
||||
airlift_unit = self.unit_map.airlift_unit(unit_name)
|
||||
if airlift_unit is not None:
|
||||
if airlift_unit.transfer.player:
|
||||
if airlift_unit.transfer.player.is_blue:
|
||||
losses.player_airlifts.append(airlift_unit)
|
||||
else:
|
||||
losses.enemy_airlifts.append(airlift_unit)
|
||||
@ -377,8 +381,10 @@ class Debriefing:
|
||||
# Captured base is not a part of the campaign. This happens when neutral
|
||||
# bases are near the conflict. Nothing to do.
|
||||
continue
|
||||
|
||||
captured_by_player = int(new_owner_id_str) == blue_coalition_id
|
||||
if int(new_owner_id_str) == blue_coalition_id:
|
||||
captured_by_player = Player.BLUE
|
||||
else:
|
||||
captured_by_player = Player.RED
|
||||
if control_point.is_friendly(to_player=captured_by_player):
|
||||
# Base is currently friendly to the new owner. Was captured and
|
||||
# recaptured in the same mission. Nothing to do.
|
||||
|
||||
46
game/game.py
46
game/game.py
@ -6,7 +6,7 @@ import math
|
||||
from collections.abc import Iterator
|
||||
from datetime import date, datetime, time, timedelta
|
||||
from enum import Enum
|
||||
from typing import Any, List, TYPE_CHECKING, Type, Union, cast
|
||||
from typing import Any, List, TYPE_CHECKING, Union, cast
|
||||
from uuid import UUID
|
||||
|
||||
from dcs.countries import Switzerland, USAFAggressors, UnitedNationsPeacekeepers
|
||||
@ -32,7 +32,7 @@ from .infos.information import Information
|
||||
from .lasercodes.lasercoderegistry import LaserCodeRegistry
|
||||
from .profiling import logged_duration
|
||||
from .settings import Settings
|
||||
from .theater import ConflictTheater
|
||||
from .theater import ConflictTheater, Player
|
||||
from .theater.bullseye import Bullseye
|
||||
from .theater.theatergroundobject import (
|
||||
EwrGroundObject,
|
||||
@ -138,8 +138,11 @@ class Game:
|
||||
self.conditions = self.generate_conditions(forced_time=start_time)
|
||||
|
||||
self.sanitize_sides(player_faction, enemy_faction)
|
||||
self.blue = Coalition(self, player_faction, player_budget, player=True)
|
||||
self.red = Coalition(self, enemy_faction, enemy_budget, player=False)
|
||||
self.blue = Coalition(self, player_faction, player_budget, player=Player.BLUE)
|
||||
self.red = Coalition(self, enemy_faction, enemy_budget, player=Player.RED)
|
||||
neutral_faction = player_faction
|
||||
neutral_faction.country = self.neutral_country
|
||||
self.neutral = Coalition(self, neutral_faction, 0, player=Player.NEUTRAL)
|
||||
self.blue.set_opponent(self.red)
|
||||
self.red.set_opponent(self.blue)
|
||||
|
||||
@ -178,10 +181,10 @@ class Game:
|
||||
def point_in_world(self, x: float, y: float) -> Point:
|
||||
return Point(x, y, self.theater.terrain)
|
||||
|
||||
def ato_for(self, player: bool) -> AirTaskingOrder:
|
||||
def ato_for(self, player: Player) -> AirTaskingOrder:
|
||||
return self.coalition_for(player).ato
|
||||
|
||||
def transit_network_for(self, player: bool) -> TransitNetwork:
|
||||
def transit_network_for(self, player: Player) -> TransitNetwork:
|
||||
return self.coalition_for(player).transit_network
|
||||
|
||||
def generate_conditions(self, forced_time: time | None = None) -> Conditions:
|
||||
@ -209,32 +212,35 @@ class Game:
|
||||
else:
|
||||
enemy_faction.country = country_with_name("Russia")
|
||||
|
||||
def faction_for(self, player: bool) -> Faction:
|
||||
def faction_for(self, player: Player) -> Faction:
|
||||
return self.coalition_for(player).faction
|
||||
|
||||
def faker_for(self, player: bool) -> Faker:
|
||||
def faker_for(self, player: Player) -> Faker:
|
||||
return self.coalition_for(player).faker
|
||||
|
||||
def air_wing_for(self, player: bool) -> AirWing:
|
||||
def air_wing_for(self, player: Player) -> AirWing:
|
||||
return self.coalition_for(player).air_wing
|
||||
|
||||
@property
|
||||
def neutral_country(self) -> Type[Country]:
|
||||
def neutral_country(self) -> Country:
|
||||
"""Return the best fitting country that can be used as neutral faction in the generated mission"""
|
||||
countries_in_use = {self.red.faction.country, self.blue.faction.country}
|
||||
if UnitedNationsPeacekeepers not in countries_in_use:
|
||||
return UnitedNationsPeacekeepers
|
||||
return UnitedNationsPeacekeepers()
|
||||
elif Switzerland.name not in countries_in_use:
|
||||
return Switzerland
|
||||
return Switzerland()
|
||||
else:
|
||||
return USAFAggressors
|
||||
return USAFAggressors()
|
||||
|
||||
def coalition_for(self, player: bool) -> Coalition:
|
||||
if player:
|
||||
def coalition_for(self, player: Player) -> Coalition:
|
||||
if player.is_neutral:
|
||||
return self.neutral
|
||||
elif player.is_blue:
|
||||
return self.blue
|
||||
return self.red
|
||||
else:
|
||||
return self.red
|
||||
|
||||
def adjust_budget(self, amount: float, player: bool) -> None:
|
||||
def adjust_budget(self, amount: float, player: Player) -> None:
|
||||
self.coalition_for(player).adjust_budget(amount)
|
||||
|
||||
def on_load(self, game_still_initializing: bool = False) -> None:
|
||||
@ -489,7 +495,7 @@ class Game:
|
||||
self.current_group_id += 1
|
||||
return self.current_group_id
|
||||
|
||||
def compute_transit_network_for(self, player: bool) -> TransitNetwork:
|
||||
def compute_transit_network_for(self, player: Player) -> TransitNetwork:
|
||||
return TransitNetworkBuilder(self.theater, player).build()
|
||||
|
||||
def compute_threat_zones(self, events: GameUpdateEvents) -> None:
|
||||
@ -498,10 +504,10 @@ class Game:
|
||||
self.blue.compute_nav_meshes(events)
|
||||
self.red.compute_nav_meshes(events)
|
||||
|
||||
def threat_zone_for(self, player: bool) -> ThreatZones:
|
||||
def threat_zone_for(self, player: Player) -> ThreatZones:
|
||||
return self.coalition_for(player).threat_zone
|
||||
|
||||
def navmesh_for(self, player: bool) -> NavMesh:
|
||||
def navmesh_for(self, player: Player) -> NavMesh:
|
||||
return self.coalition_for(player).nav_mesh
|
||||
|
||||
def compute_unculled_zones(self, events: GameUpdateEvents) -> None:
|
||||
|
||||
@ -66,7 +66,7 @@ class GroundUnitOrders:
|
||||
bought_units: dict[GroundUnitType, int] = {}
|
||||
units_needing_transfer: dict[GroundUnitType, int] = {}
|
||||
for unit_type, count in self.units.items():
|
||||
allegiance = "Ally" if self.destination.captured else "Enemy"
|
||||
allegiance = "Ally" if self.destination.captured.is_blue else "Enemy"
|
||||
d: dict[GroundUnitType, int]
|
||||
if self.destination != ground_unit_source:
|
||||
source = ground_unit_source
|
||||
|
||||
@ -4,6 +4,7 @@ from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from game.config import REWARDS
|
||||
from game.theater.player import Player
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game import Game
|
||||
@ -22,8 +23,8 @@ class BuildingIncome:
|
||||
|
||||
|
||||
class Income:
|
||||
def __init__(self, game: Game, player: bool) -> None:
|
||||
if player:
|
||||
def __init__(self, game: Game, player: Player) -> None:
|
||||
if player.is_blue:
|
||||
self.multiplier = game.settings.player_income_multiplier
|
||||
else:
|
||||
self.multiplier = game.settings.enemy_income_multiplier
|
||||
|
||||
@ -220,7 +220,7 @@ class AircraftGenerator:
|
||||
):
|
||||
continue
|
||||
|
||||
if control_point.captured:
|
||||
if control_point.captured.is_blue:
|
||||
country = player_country
|
||||
else:
|
||||
country = enemy_country
|
||||
@ -237,12 +237,12 @@ class AircraftGenerator:
|
||||
squadron.location, Fob
|
||||
)
|
||||
if (
|
||||
squadron.coalition.player
|
||||
squadron.coalition.player.is_blue
|
||||
and self.game.settings.perf_disable_untasked_blufor_aircraft
|
||||
):
|
||||
return
|
||||
elif (
|
||||
not squadron.coalition.player
|
||||
not squadron.coalition.player.is_red
|
||||
and self.game.settings.perf_disable_untasked_opfor_aircraft
|
||||
):
|
||||
return
|
||||
@ -274,7 +274,7 @@ class AircraftGenerator:
|
||||
).create_idle_aircraft()
|
||||
if group:
|
||||
if (
|
||||
not squadron.coalition.player
|
||||
squadron.coalition.player.is_red
|
||||
and squadron.aircraft.flyable
|
||||
and (
|
||||
self.game.settings.enable_squadron_pilot_limits
|
||||
|
||||
@ -14,6 +14,7 @@ if TYPE_CHECKING:
|
||||
from game.dcs.aircrafttype import AircraftType
|
||||
from game.radio.radios import RadioFrequency
|
||||
from game.runways import RunwayData
|
||||
from game.theater.player import Player
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@ -42,7 +43,7 @@ class FlightData:
|
||||
size: int
|
||||
|
||||
#: True if this flight belongs to the player's coalition.
|
||||
friendly: bool
|
||||
friendly: Player
|
||||
|
||||
#: Number of seconds after mission start the flight is set to depart.
|
||||
departure_delay: timedelta
|
||||
|
||||
@ -299,7 +299,7 @@ class FlightGroupConfigurator:
|
||||
unit.set_player()
|
||||
|
||||
def skill_level_for(self, unit: FlyingUnit, pilot: Optional[Pilot]) -> Skill:
|
||||
if self.flight.squadron.player:
|
||||
if self.flight.squadron.player.is_blue:
|
||||
base_skill = Skill(self.game.settings.player_skill)
|
||||
else:
|
||||
base_skill = Skill(self.game.settings.enemy_skill)
|
||||
|
||||
@ -17,8 +17,8 @@ from game.radio.radios import RadioFrequency
|
||||
from game.runways import RunwayData
|
||||
from game.theater import ControlPoint, FrontLine
|
||||
from .aircraft.flightdata import FlightData
|
||||
from .missiondata import AwacsInfo, TankerInfo
|
||||
from .flotgenerator import JtacInfo
|
||||
from .missiondata import AwacsInfo, TankerInfo
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game import Game
|
||||
@ -182,7 +182,7 @@ class BriefingGenerator(MissionInfoGenerator):
|
||||
def generate_allied_flights_by_departure(self) -> None:
|
||||
"""Create iterable to display allied flights grouped by departure airfield."""
|
||||
for flight in self.flights:
|
||||
if not flight.client_units and flight.friendly:
|
||||
if not flight.client_units and flight.friendly.is_blue:
|
||||
name = flight.departure.airfield_name
|
||||
if (
|
||||
name in self.allied_flights_by_departure
|
||||
|
||||
@ -18,6 +18,7 @@ from game.utils import kph
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game import Game
|
||||
from game.theater.player import Player
|
||||
|
||||
|
||||
class ConvoyGenerator:
|
||||
@ -94,7 +95,7 @@ class ConvoyGenerator:
|
||||
name: str,
|
||||
position: Point,
|
||||
units: dict[GroundUnitType, int],
|
||||
for_player: bool,
|
||||
for_player: Player,
|
||||
) -> VehicleGroup:
|
||||
unit_types = list(units.items())
|
||||
main_unit_type, main_unit_count = unit_types[0]
|
||||
|
||||
@ -15,6 +15,7 @@ FRONTLINE_COLORS = Rgba(255, 0, 0, 255)
|
||||
WHITE = Rgba(255, 255, 255, 255)
|
||||
CP_RED = Rgba(255, 0, 0, 80)
|
||||
CP_BLUE = Rgba(0, 0, 255, 80)
|
||||
CP_NEUTRAL = Rgba(128, 128, 128, 80)
|
||||
BLUE_PATH_COLOR = Rgba(0, 0, 255, 100)
|
||||
RED_PATH_COLOR = Rgba(255, 0, 0, 100)
|
||||
ACTIVE_PATH_COLOR = Rgba(255, 80, 80, 100)
|
||||
@ -35,10 +36,12 @@ class DrawingsGenerator:
|
||||
Generate cps as circles
|
||||
"""
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if cp.captured:
|
||||
if cp.captured.is_blue:
|
||||
color = CP_BLUE
|
||||
else:
|
||||
elif cp.captured.is_red:
|
||||
color = CP_RED
|
||||
else:
|
||||
color = CP_NEUTRAL
|
||||
shape = self.player_layer.add_circle(
|
||||
cp.position,
|
||||
TRIGGER_RADIUS_CAPTURE,
|
||||
@ -61,9 +64,9 @@ class DrawingsGenerator:
|
||||
continue
|
||||
else:
|
||||
# Determine path color
|
||||
if cp.captured and destination.captured:
|
||||
if cp.captured.is_blue and destination.captured.is_blue:
|
||||
color = BLUE_PATH_COLOR
|
||||
elif not cp.captured and not destination.captured:
|
||||
elif cp.captured.is_red and destination.captured.is_red:
|
||||
color = RED_PATH_COLOR
|
||||
else:
|
||||
color = ACTIVE_PATH_COLOR
|
||||
|
||||
@ -40,7 +40,7 @@ from game.ground_forces.ai_ground_planner import (
|
||||
from game.ground_forces.combat_stance import CombatStance
|
||||
from game.naming import namegen
|
||||
from game.radio.radios import RadioRegistry
|
||||
from game.theater.controlpoint import ControlPoint
|
||||
from game.theater.controlpoint import ControlPoint, Player
|
||||
from game.unitmap import UnitMap
|
||||
from game.utils import Heading
|
||||
from .frontlineconflictdescription import FrontLineConflictDescription
|
||||
@ -101,12 +101,12 @@ class FlotGenerator:
|
||||
|
||||
# Create player groups at random position
|
||||
player_groups = self._generate_groups(
|
||||
self.player_planned_combat_groups, is_player=True
|
||||
self.player_planned_combat_groups, is_player=Player.BLUE
|
||||
)
|
||||
|
||||
# Create enemy groups at random position
|
||||
enemy_groups = self._generate_groups(
|
||||
self.enemy_planned_combat_groups, is_player=False
|
||||
self.enemy_planned_combat_groups, is_player=Player.RED
|
||||
)
|
||||
|
||||
# TODO: Differentiate AirConflict and GroundConflict classes.
|
||||
@ -193,7 +193,7 @@ class FlotGenerator:
|
||||
callsign=callsign,
|
||||
region=frontline,
|
||||
code=str(code),
|
||||
blue=True,
|
||||
blue=Player.BLUE,
|
||||
freq=freq,
|
||||
)
|
||||
)
|
||||
@ -215,7 +215,7 @@ class FlotGenerator:
|
||||
def gen_infantry_group_for_group(
|
||||
self,
|
||||
group: VehicleGroup,
|
||||
is_player: bool,
|
||||
is_player: Player,
|
||||
side: Country,
|
||||
forward_heading: Heading,
|
||||
) -> None:
|
||||
@ -294,7 +294,7 @@ class FlotGenerator:
|
||||
GroundForcePainter(faction, vehicle).apply_livery()
|
||||
vg.hidden_on_mfd = True
|
||||
|
||||
def _earliest_tot_on_flot(self, player: bool) -> timedelta:
|
||||
def _earliest_tot_on_flot(self, player: Player) -> timedelta:
|
||||
tots = [
|
||||
x.time_over_target
|
||||
for x in self.game.ato_for(player).packages
|
||||
@ -413,7 +413,7 @@ class FlotGenerator:
|
||||
"""
|
||||
duration = timedelta()
|
||||
if stance in [CombatStance.DEFENSIVE, CombatStance.AGGRESSIVE]:
|
||||
duration = self._earliest_tot_on_flot(not to_cp.coalition.player)
|
||||
duration = self._earliest_tot_on_flot(to_cp.coalition.player.opponent)
|
||||
self._set_reform_waypoint(dcs_group, forward_heading, duration)
|
||||
if stance == CombatStance.AGGRESSIVE:
|
||||
# Attack nearest enemy if any
|
||||
@ -503,7 +503,7 @@ class FlotGenerator:
|
||||
"""
|
||||
duration = timedelta()
|
||||
if stance in [CombatStance.DEFENSIVE, CombatStance.AGGRESSIVE]:
|
||||
duration = self._earliest_tot_on_flot(not to_cp.coalition.player)
|
||||
duration = self._earliest_tot_on_flot(to_cp.coalition.player.opponent)
|
||||
self._set_reform_waypoint(dcs_group, forward_heading, duration)
|
||||
if stance in [
|
||||
CombatStance.AGGRESSIVE,
|
||||
@ -762,7 +762,7 @@ class FlotGenerator:
|
||||
)
|
||||
|
||||
def _generate_groups(
|
||||
self, groups: list[CombatGroup], is_player: bool
|
||||
self, groups: list[CombatGroup], is_player: Player
|
||||
) -> List[Tuple[VehicleGroup, CombatGroup]]:
|
||||
"""Finds valid positions for planned groups and generates a pydcs group for them"""
|
||||
positioned_groups = []
|
||||
@ -795,7 +795,7 @@ class FlotGenerator:
|
||||
final_position,
|
||||
heading=spawn_heading.opposite,
|
||||
)
|
||||
if is_player:
|
||||
if is_player == Player.BLUE:
|
||||
g.set_skill(Skill(self.game.settings.player_skill))
|
||||
else:
|
||||
g.set_skill(Skill(self.game.settings.enemy_vehicle_skill))
|
||||
@ -813,7 +813,7 @@ class FlotGenerator:
|
||||
|
||||
def _generate_group(
|
||||
self,
|
||||
player: bool,
|
||||
player: Player,
|
||||
side: Country,
|
||||
unit_type: GroundUnitType,
|
||||
count: int,
|
||||
|
||||
@ -10,15 +10,14 @@ from dcs import Mission
|
||||
from dcs.action import DoScript, DoScriptFile
|
||||
from dcs.translation import String
|
||||
from dcs.triggers import TriggerStart
|
||||
from dcs.unit import Skill
|
||||
|
||||
from game.ato import FlightType
|
||||
from game.data.units import UnitClass
|
||||
from game.dcs.aircrafttype import AircraftType
|
||||
from game.plugins import LuaPluginManager
|
||||
from game.theater import TheaterGroundObject
|
||||
from game.theater.iadsnetwork.iadsrole import IadsRole
|
||||
from game.utils import escape_string_for_lua
|
||||
from game.data.units import UnitClass
|
||||
from .missiondata import MissionData
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -144,7 +143,7 @@ class LuaGenerator:
|
||||
|
||||
target_points = lua_data.add_item("TargetPoints")
|
||||
for flight in self.mission_data.flights:
|
||||
if flight.friendly and flight.flight_type in [
|
||||
if flight.friendly.is_blue and flight.flight_type in [
|
||||
FlightType.ANTISHIP,
|
||||
FlightType.DEAD,
|
||||
FlightType.SEAD,
|
||||
@ -178,7 +177,7 @@ class LuaGenerator:
|
||||
for cp in self.game.theater.controlpoints:
|
||||
coalition_object = (
|
||||
lua_data.get_or_create_item("BlueAA")
|
||||
if cp.captured
|
||||
if cp.captured.is_blue
|
||||
else lua_data.get_or_create_item("RedAA")
|
||||
)
|
||||
for ground_object in cp.ground_objects:
|
||||
|
||||
@ -15,6 +15,7 @@ from game.runways import RunwayData
|
||||
if TYPE_CHECKING:
|
||||
from game.radio.radios import RadioFrequency
|
||||
from game.radio.tacan import TacanChannel
|
||||
from game.theater.player import Player
|
||||
from game.utils import Distance
|
||||
from uuid import UUID
|
||||
|
||||
@ -24,7 +25,7 @@ class GroupInfo:
|
||||
group_name: str
|
||||
callsign: str
|
||||
freq: RadioFrequency
|
||||
blue: bool
|
||||
blue: Player
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -85,7 +86,7 @@ class LogisticsInfo:
|
||||
|
||||
pilot_names: list[str]
|
||||
transport: AircraftType
|
||||
blue: bool
|
||||
blue: Player
|
||||
|
||||
logistic_unit: str = field(default_factory=str)
|
||||
pickup_zone: str = field(default_factory=str)
|
||||
|
||||
@ -391,9 +391,12 @@ class MissionGenerator:
|
||||
tmu.theater_unit.position,
|
||||
self.mission.terrain,
|
||||
).dict()
|
||||
warehouse["coalition"] = (
|
||||
"blue" if tmu.theater_unit.ground_object.coalition.player else "red"
|
||||
)
|
||||
if tmu.theater_unit.ground_object.coalition.player.is_neutral:
|
||||
warehouse["coalition"] = "neutral"
|
||||
elif tmu.theater_unit.ground_object.coalition.player.is_blue:
|
||||
warehouse["coalition"] = "blue"
|
||||
else:
|
||||
warehouse["coalition"] = "red"
|
||||
warehouse["dynamicCargo"] = settings.dynamic_cargo
|
||||
if tmu.theater_unit.is_ship or tmu.dcs_unit.category == "Heliports": # type: ignore
|
||||
warehouse["dynamicSpawn"] = settings.dynamic_slots
|
||||
|
||||
@ -13,6 +13,7 @@ from dcs.vehicles import vehicle_map
|
||||
|
||||
from game.dcs.groundunittype import GroundUnitType
|
||||
from game.naming import namegen
|
||||
from game.theater import Player
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game import Game
|
||||
@ -25,12 +26,12 @@ class RebellionGenerator:
|
||||
|
||||
def generate(self) -> None:
|
||||
ownfor_country = self.mission.country(
|
||||
self.game.coalition_for(player=True).faction.country.name
|
||||
self.game.coalition_for(player=Player.BLUE).faction.country.name
|
||||
)
|
||||
for rz in self.game.theater.ownfor_rebel_zones:
|
||||
self._generate_rebel_zone(ownfor_country, rz)
|
||||
opfor_country = self.mission.country(
|
||||
self.game.coalition_for(player=False).faction.country.name
|
||||
self.game.coalition_for(player=Player.RED).faction.country.name
|
||||
)
|
||||
for rz in self.game.theater.opfor_rebel_zones:
|
||||
self._generate_rebel_zone(opfor_country, rz)
|
||||
|
||||
@ -67,6 +67,7 @@ from game.radio.tacan import TacanBand, TacanChannel, TacanRegistry, TacanUsage
|
||||
from game.runways import RunwayData
|
||||
from game.theater import (
|
||||
ControlPoint,
|
||||
Player,
|
||||
TheaterGroundObject,
|
||||
TheaterUnit,
|
||||
NavalControlPoint,
|
||||
@ -408,7 +409,7 @@ class GroundObjectGenerator:
|
||||
# Align the trigger zones to the faction color on the DCS briefing/F10 map.
|
||||
color = (
|
||||
{1: 0.2, 2: 0.7, 3: 1, 4: 0.15}
|
||||
if scenery.ground_object.is_friendly(to_player=True)
|
||||
if scenery.ground_object.is_friendly(to_player=Player.BLUE)
|
||||
else {1: 1, 2: 0.2, 3: 0.2, 4: 0.15}
|
||||
)
|
||||
|
||||
@ -878,7 +879,12 @@ class HelipadGenerator:
|
||||
pad.position,
|
||||
self.m.terrain,
|
||||
).dict()
|
||||
warehouse["coalition"] = "blue" if self.cp.coalition.player else "red"
|
||||
if self.cp.coalition.player.is_neutral:
|
||||
warehouse["coalition"] = "neutral"
|
||||
elif self.cp.coalition.player.is_blue:
|
||||
warehouse["coalition"] = "blue"
|
||||
else:
|
||||
warehouse["coalition"] = "red"
|
||||
# configure dynamic spawn + hot start of DS, plus dynamic cargo?
|
||||
self.m.warehouses.warehouses[pad.id] = warehouse
|
||||
|
||||
@ -1005,7 +1011,12 @@ class GroundSpawnRoadbaseGenerator:
|
||||
pad.position,
|
||||
self.m.terrain,
|
||||
).dict()
|
||||
warehouse["coalition"] = "blue" if self.cp.coalition.player else "red"
|
||||
if self.cp.coalition.player.is_neutral:
|
||||
warehouse["coalition"] = "neutral"
|
||||
elif self.cp.coalition.player.is_blue:
|
||||
warehouse["coalition"] = "blue"
|
||||
else:
|
||||
warehouse["coalition"] = "red"
|
||||
# configure dynamic spawn + hot start of DS, plus dynamic cargo?
|
||||
self.m.warehouses.warehouses[pad.id] = warehouse
|
||||
|
||||
@ -1131,7 +1142,12 @@ class GroundSpawnLargeGenerator:
|
||||
pad.position,
|
||||
self.m.terrain,
|
||||
).dict()
|
||||
warehouse["coalition"] = "blue" if self.cp.coalition.player else "red"
|
||||
if self.cp.coalition.player.is_neutral:
|
||||
warehouse["coalition"] = "neutral"
|
||||
elif self.cp.coalition.player.is_blue:
|
||||
warehouse["coalition"] = "blue"
|
||||
else:
|
||||
warehouse["coalition"] = "red"
|
||||
# configure dynamic spawn + hot start of DS, plus dynamic cargo?
|
||||
self.m.warehouses.warehouses[pad.id] = warehouse
|
||||
|
||||
@ -1275,7 +1291,12 @@ class GroundSpawnGenerator:
|
||||
pad.position,
|
||||
self.m.terrain,
|
||||
).dict()
|
||||
warehouse["coalition"] = "blue" if self.cp.coalition.player else "red"
|
||||
if self.cp.coalition.player.is_neutral:
|
||||
warehouse["coalition"] = "neutral"
|
||||
elif self.cp.coalition.player.is_blue:
|
||||
warehouse["coalition"] = "blue"
|
||||
else:
|
||||
warehouse["coalition"] = "red"
|
||||
# configure dynamic spawn + hot start of DS, plus dynamic cargo?
|
||||
self.m.warehouses.warehouses[pad.id] = warehouse
|
||||
|
||||
|
||||
@ -12,7 +12,6 @@ from dcs.action import (
|
||||
RemoveSceneObjects,
|
||||
RemoveSceneObjectsMask,
|
||||
SceneryDestructionZone,
|
||||
Smoke,
|
||||
)
|
||||
from dcs.condition import (
|
||||
AllOfCoalitionOutsideZone,
|
||||
@ -99,9 +98,12 @@ class TriggerGenerator:
|
||||
raise RuntimeError(
|
||||
f"Could not find {airfield.airport.name} in the mission"
|
||||
)
|
||||
cp_airport.set_coalition(
|
||||
airfield.captured and player_coalition or enemy_coalition
|
||||
)
|
||||
if airfield.captured.is_neutral:
|
||||
cp_airport.set_coalition("neutral")
|
||||
elif airfield.captured.is_blue:
|
||||
cp_airport.set_coalition(player_coalition)
|
||||
elif airfield.captured.is_red:
|
||||
cp_airport.set_coalition(enemy_coalition)
|
||||
|
||||
def _set_skill(self, player_coalition: str, enemy_coalition: str) -> None:
|
||||
"""
|
||||
@ -138,7 +140,7 @@ class TriggerGenerator:
|
||||
zone = self.mission.triggers.add_triggerzone(
|
||||
location, radius=10, hidden=True, name="MARK"
|
||||
)
|
||||
if cp.captured:
|
||||
if cp.captured.is_blue:
|
||||
name = ground_object.obj_name + " [ALLY]"
|
||||
else:
|
||||
name = ground_object.obj_name + " [ENEMY]"
|
||||
@ -186,7 +188,7 @@ class TriggerGenerator:
|
||||
"""
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if isinstance(cp, self.capture_zone_types) and not cp.is_carrier:
|
||||
if cp.captured:
|
||||
if cp.captured.is_blue:
|
||||
attacking_coalition = enemy_coalition
|
||||
attack_coalition_int = 1 # 1 is the Event int for Red
|
||||
defending_coalition = player_coalition
|
||||
@ -242,6 +244,51 @@ class TriggerGenerator:
|
||||
recapture_trigger.add_action(ClearFlag(flag=flag))
|
||||
self.mission.triggerrules.triggers.append(recapture_trigger)
|
||||
|
||||
if cp.captured.is_neutral:
|
||||
red_capture_trigger = TriggerCondition(
|
||||
Event.NoEvent, "Capture Trigger"
|
||||
)
|
||||
red_capture_trigger.add_condition(
|
||||
AllOfCoalitionOutsideZone(
|
||||
attacking_coalition, trigger_zone.id, unit_type="GROUND"
|
||||
)
|
||||
)
|
||||
red_capture_trigger.add_condition(
|
||||
PartOfCoalitionInZone(
|
||||
defending_coalition, trigger_zone.id, unit_type="GROUND"
|
||||
)
|
||||
)
|
||||
red_capture_trigger.add_condition(FlagIsFalse(flag=flag))
|
||||
script_string = String(
|
||||
f'base_capture_events[#base_capture_events + 1] = "{cp.id}||{defend_coalition_int}||{cp.full_name}"'
|
||||
)
|
||||
red_capture_trigger.add_action(DoScript(script_string))
|
||||
red_capture_trigger.add_action(SetFlag(flag=flag))
|
||||
self.mission.triggerrules.triggers.append(red_capture_trigger)
|
||||
|
||||
inverted_recapture_trigger = TriggerCondition(
|
||||
Event.NoEvent, "Capture Trigger"
|
||||
)
|
||||
inverted_recapture_trigger.add_condition(
|
||||
AllOfCoalitionOutsideZone(
|
||||
defending_coalition, trigger_zone.id, unit_type="GROUND"
|
||||
)
|
||||
)
|
||||
inverted_recapture_trigger.add_condition(
|
||||
PartOfCoalitionInZone(
|
||||
attacking_coalition, trigger_zone.id, unit_type="GROUND"
|
||||
)
|
||||
)
|
||||
inverted_recapture_trigger.add_condition(FlagIsTrue(flag=flag))
|
||||
script_string = String(
|
||||
f'base_capture_events[#base_capture_events + 1] = "{cp.id}||{attack_coalition_int}||{cp.full_name}"'
|
||||
)
|
||||
inverted_recapture_trigger.add_action(DoScript(script_string))
|
||||
inverted_recapture_trigger.add_action(ClearFlag(flag=flag))
|
||||
self.mission.triggerrules.triggers.append(
|
||||
inverted_recapture_trigger
|
||||
)
|
||||
|
||||
def generate(self) -> None:
|
||||
player_coalition = "blue"
|
||||
enemy_coalition = "red"
|
||||
|
||||
@ -55,7 +55,7 @@ class GameStats:
|
||||
turn_data = GameTurnMetadata()
|
||||
|
||||
for cp in game.theater.controlpoints:
|
||||
if cp.captured:
|
||||
if cp.captured.is_blue:
|
||||
for squadron in cp.squadrons:
|
||||
turn_data.allied_units.aircraft_count += squadron.owned_aircraft
|
||||
turn_data.allied_units.vehicles_count += sum(cp.base.armor.values())
|
||||
|
||||
@ -35,6 +35,7 @@ from game.runways import RunwayData
|
||||
from game.settings import Settings
|
||||
from game.squadrons import AirWing
|
||||
from game.squadrons import Squadron
|
||||
from game.theater.player import Player
|
||||
from game.theater.controlpoint import (
|
||||
ControlPoint,
|
||||
OffMapSpawn,
|
||||
@ -833,7 +834,7 @@ class PretenseAircraftGenerator:
|
||||
"""
|
||||
self.initialize_pretense_data_structures(cp)
|
||||
|
||||
is_player = True
|
||||
is_player = Player.BLUE
|
||||
if country == cp.coalition.faction.country:
|
||||
offmap_transport_cp = self.find_pretense_cargo_plane_cp(cp)
|
||||
|
||||
@ -868,7 +869,7 @@ class PretenseAircraftGenerator:
|
||||
coalition = (
|
||||
self.game.coalition_for(is_player)
|
||||
if country == self.game.coalition_for(is_player).faction.country
|
||||
else self.game.coalition_for(False)
|
||||
else self.game.coalition_for(Player.RED)
|
||||
)
|
||||
self.generate_pretense_aircraft_for_other_side(cp, coalition, ato)
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ from game.missiongenerator.aircraft.flightgroupspawner import (
|
||||
)
|
||||
from game.missiongenerator.missiondata import MissionData
|
||||
from game.naming import NameGenerator
|
||||
from game.theater import Airfield, ControlPoint, Fob, NavalControlPoint
|
||||
from game.theater import Airfield, ControlPoint, Fob, NavalControlPoint, Player
|
||||
|
||||
|
||||
class PretenseNameGenerator(NameGenerator):
|
||||
@ -87,7 +87,7 @@ class PretenseFlightGroupSpawner(FlightGroupSpawner):
|
||||
|
||||
def insert_into_pretense(self, name: str) -> None:
|
||||
cp = self.flight.departure
|
||||
is_player = True
|
||||
is_player = Player.BLUE
|
||||
cp_side = (
|
||||
2
|
||||
if self.flight.coalition
|
||||
|
||||
@ -1495,13 +1495,13 @@ class PretenseLuaGenerator(LuaGenerator):
|
||||
cp_name.replace("ä", "a")
|
||||
cp_name.replace("ö", "o")
|
||||
cp_name.replace("ø", "o")
|
||||
cp_side = 2 if cp.captured else 1
|
||||
cp_side = 2 if cp.captured.is_blue else 1
|
||||
|
||||
if isinstance(cp, OffMapSpawn):
|
||||
continue
|
||||
elif (
|
||||
cp.is_fleet
|
||||
and cp.captured
|
||||
and cp.captured.is_blue
|
||||
and self.game.settings.pretense_controllable_carrier
|
||||
):
|
||||
# Friendly carrier, generate carrier parameters
|
||||
@ -1591,7 +1591,7 @@ class PretenseLuaGenerator(LuaGenerator):
|
||||
# Also connect carrier and LHA control points to adjacent friendly points
|
||||
if cp.is_fleet and (
|
||||
not self.game.settings.pretense_controllable_carrier
|
||||
or not cp.captured
|
||||
or cp.captured.is_red
|
||||
):
|
||||
num_of_carrier_connections = 0
|
||||
for (
|
||||
@ -1616,7 +1616,7 @@ class PretenseLuaGenerator(LuaGenerator):
|
||||
try:
|
||||
if (
|
||||
cp.is_fleet
|
||||
and cp.captured
|
||||
and cp.captured.is_blue
|
||||
and self.game.settings.pretense_controllable_carrier
|
||||
):
|
||||
break
|
||||
|
||||
@ -28,6 +28,7 @@ from game.missiongenerator.visualsgenerator import VisualsGenerator
|
||||
from game.naming import namegen
|
||||
from game.persistency import pre_pretense_backups_dir
|
||||
from game.pretense.pretenseaircraftgenerator import PretenseAircraftGenerator
|
||||
from game.theater import Player
|
||||
from game.theater.bullseye import Bullseye
|
||||
from game.unitmap import UnitMap
|
||||
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
||||
@ -233,7 +234,7 @@ class PretenseMissionGenerator(MissionGenerator):
|
||||
callsign=callsign,
|
||||
region=frontline,
|
||||
code=str(code),
|
||||
blue=True,
|
||||
blue=Player.BLUE,
|
||||
freq=freq,
|
||||
)
|
||||
)
|
||||
|
||||
@ -49,6 +49,7 @@ from game.theater import (
|
||||
TheaterUnit,
|
||||
NavalControlPoint,
|
||||
PresetLocation,
|
||||
Player,
|
||||
)
|
||||
from game.theater.theatergroundobject import (
|
||||
CarrierGroundObject,
|
||||
@ -248,7 +249,7 @@ class PretenseGroundObjectGenerator(GroundObjectGenerator):
|
||||
"""
|
||||
unit_type = None
|
||||
faction = self.coalition.faction
|
||||
is_player = True
|
||||
is_player = Player.BLUE
|
||||
side = (
|
||||
2
|
||||
if self.country == self.game.coalition_for(is_player).faction.country
|
||||
@ -458,7 +459,7 @@ class PretenseGroundObjectGenerator(GroundObjectGenerator):
|
||||
cp_name_trimmed = PretenseNameGenerator.pretense_trimmed_cp_name(
|
||||
control_point.name
|
||||
)
|
||||
is_player = True
|
||||
is_player = Player.BLUE
|
||||
side = (
|
||||
2
|
||||
if self.country
|
||||
@ -567,7 +568,7 @@ class PretenseGroundObjectGenerator(GroundObjectGenerator):
|
||||
cp_name_trimmed = PretenseNameGenerator.pretense_trimmed_cp_name(
|
||||
control_point.name
|
||||
)
|
||||
is_player = True
|
||||
is_player = Player.BLUE
|
||||
side = (
|
||||
2
|
||||
if self.country == self.game.coalition_for(is_player).faction.country
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import math
|
||||
import random
|
||||
from typing import TYPE_CHECKING, List
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from dcs import Point
|
||||
from dcs.action import (
|
||||
@ -11,10 +9,6 @@ from dcs.action import (
|
||||
DoScript,
|
||||
MarkToAll,
|
||||
SetFlag,
|
||||
RemoveSceneObjects,
|
||||
RemoveSceneObjectsMask,
|
||||
SceneryDestructionZone,
|
||||
Smoke,
|
||||
)
|
||||
from dcs.condition import (
|
||||
AllOfCoalitionOutsideZone,
|
||||
@ -22,7 +16,6 @@ from dcs.condition import (
|
||||
FlagIsTrue,
|
||||
PartOfCoalitionInZone,
|
||||
TimeAfter,
|
||||
TimeSinceFlag,
|
||||
)
|
||||
from dcs.mission import Mission
|
||||
from dcs.task import Option
|
||||
@ -31,12 +24,11 @@ from dcs.terrain.syria.airports import Damascus, Khalkhalah
|
||||
from dcs.translation import String
|
||||
from dcs.triggers import Event, TriggerCondition, TriggerOnce
|
||||
from dcs.unit import Skill
|
||||
from numpy import cross, einsum, arctan2
|
||||
from shapely import MultiPolygon, Point as ShapelyPoint
|
||||
|
||||
from game.naming import ALPHA_MILITARY
|
||||
from game.pretense.pretenseflightgroupspawner import PretenseNameGenerator
|
||||
from game.theater import Airfield
|
||||
from game.theater import Airfield, Player
|
||||
from game.theater.controlpoint import Fob, TRIGGER_RADIUS_CAPTURE, OffMapSpawn
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -157,7 +149,7 @@ class PretenseTriggerGenerator:
|
||||
zone = self.mission.triggers.add_triggerzone(
|
||||
location, radius=10, hidden=True, name="MARK"
|
||||
)
|
||||
if cp.captured:
|
||||
if cp.captured.is_blue:
|
||||
name = ground_object.obj_name + " [ALLY]"
|
||||
else:
|
||||
name = ground_object.obj_name + " [ENEMY]"
|
||||
@ -174,7 +166,7 @@ class PretenseTriggerGenerator:
|
||||
"""
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if isinstance(cp, self.capture_zone_types) and not cp.is_carrier:
|
||||
if cp.captured:
|
||||
if cp.captured.is_blue:
|
||||
attacking_coalition = enemy_coalition
|
||||
attack_coalition_int = 1 # 1 is the Event int for Red
|
||||
defending_coalition = player_coalition
|
||||
@ -243,7 +235,7 @@ class PretenseTriggerGenerator:
|
||||
self.game.settings.pretense_carrier_zones_navmesh == "Blue navmesh"
|
||||
)
|
||||
sea_zones_landmap = self.game.coalition_for(
|
||||
player=False
|
||||
player=Player.RED
|
||||
).nav_mesh.theater.landmap
|
||||
if (
|
||||
self.game.settings.pretense_controllable_carrier
|
||||
@ -251,7 +243,7 @@ class PretenseTriggerGenerator:
|
||||
):
|
||||
navmesh_number = 0
|
||||
for navmesh_poly in self.game.coalition_for(
|
||||
player=use_blue_navmesh
|
||||
player=Player.BLUE if use_blue_navmesh else Player.RED
|
||||
).nav_mesh.polys:
|
||||
navmesh_number += 1
|
||||
if sea_zones_landmap.sea_zones.intersects(navmesh_poly.poly):
|
||||
@ -325,7 +317,7 @@ class PretenseTriggerGenerator:
|
||||
if (
|
||||
cp.is_fleet
|
||||
and self.game.settings.pretense_controllable_carrier
|
||||
and cp.captured
|
||||
and cp.captured.is_blue
|
||||
):
|
||||
# Friendly carrier zones are generated above
|
||||
continue
|
||||
|
||||
@ -8,7 +8,7 @@ from typing import Iterator, List, Optional, TYPE_CHECKING, Tuple
|
||||
from game.config import RUNWAY_REPAIR_COST
|
||||
from game.data.units import UnitClass
|
||||
from game.dcs.groundunittype import GroundUnitType
|
||||
from game.theater import ControlPoint, MissionTarget, ParkingType
|
||||
from game.theater import ControlPoint, MissionTarget, ParkingType, Player
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game import Game
|
||||
@ -34,20 +34,20 @@ class ProcurementAi:
|
||||
def __init__(
|
||||
self,
|
||||
game: Game,
|
||||
for_player: bool,
|
||||
owner: Player,
|
||||
faction: Faction,
|
||||
manage_runways: bool,
|
||||
manage_front_line: bool,
|
||||
manage_aircraft: bool,
|
||||
) -> None:
|
||||
self.game = game
|
||||
self.is_player = for_player
|
||||
self.air_wing = game.air_wing_for(for_player)
|
||||
self.is_player = owner
|
||||
self.air_wing = game.air_wing_for(owner)
|
||||
self.faction = faction
|
||||
self.manage_runways = manage_runways
|
||||
self.manage_front_line = manage_front_line
|
||||
self.manage_aircraft = manage_aircraft
|
||||
self.threat_zones = self.game.threat_zone_for(not self.is_player)
|
||||
self.threat_zones = self.game.threat_zone_for(self.is_player.opponent)
|
||||
|
||||
def calculate_ground_unit_budget_share(self) -> float:
|
||||
armor_investment = 0
|
||||
@ -114,7 +114,7 @@ class ProcurementAi:
|
||||
if control_point.runway_can_be_repaired:
|
||||
control_point.begin_runway_repair()
|
||||
budget -= RUNWAY_REPAIR_COST
|
||||
if self.is_player:
|
||||
if self.is_player.is_blue:
|
||||
self.game.message(
|
||||
"We have begun repairing the runway at " f"{control_point}"
|
||||
)
|
||||
@ -223,7 +223,7 @@ class ProcurementAi:
|
||||
|
||||
@property
|
||||
def owned_points(self) -> List[ControlPoint]:
|
||||
if self.is_player:
|
||||
if self.is_player.is_blue:
|
||||
return self.game.theater.player_points()
|
||||
else:
|
||||
return self.game.theater.enemy_points()
|
||||
|
||||
@ -29,12 +29,16 @@ class ControlPointJs(BaseModel):
|
||||
destination = None
|
||||
if control_point.target_position is not None:
|
||||
destination = control_point.target_position.latlng()
|
||||
if control_point.captured.is_blue:
|
||||
blue = True
|
||||
else:
|
||||
blue = False
|
||||
return ControlPointJs(
|
||||
id=control_point.id,
|
||||
name=control_point.name,
|
||||
blue=control_point.captured,
|
||||
blue=blue,
|
||||
position=control_point.position.latlng(),
|
||||
mobile=control_point.moveable and control_point.captured,
|
||||
mobile=control_point.moveable and control_point.captured.is_blue,
|
||||
destination=destination,
|
||||
sidc=str(control_point.sidc()),
|
||||
)
|
||||
|
||||
@ -6,6 +6,7 @@ from fastapi import APIRouter, Body, Depends, HTTPException, status
|
||||
from starlette.responses import Response
|
||||
|
||||
from game import Game
|
||||
from game.theater.player import Player
|
||||
from .models import ControlPointJs
|
||||
from ..dependencies import GameContext
|
||||
from ..leaflet import LeafletPoint
|
||||
@ -75,7 +76,7 @@ def set_destination(
|
||||
)
|
||||
if not cp.moveable:
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN, detail=f"{cp} is not mobile")
|
||||
if not cp.captured:
|
||||
if not cp.captured.is_blue:
|
||||
raise HTTPException(
|
||||
status.HTTP_403_FORBIDDEN, detail=f"{cp} is not owned by the player"
|
||||
)
|
||||
@ -120,7 +121,7 @@ def cancel_travel(cp_id: UUID, game: Game = Depends(GameContext.require)) -> Non
|
||||
)
|
||||
if not cp.moveable:
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN, detail=f"{cp} is not mobile")
|
||||
if not cp.captured:
|
||||
if not cp.captured.is_blue:
|
||||
raise HTTPException(
|
||||
status.HTTP_403_FORBIDDEN, detail=f"{cp} is not owned by the player"
|
||||
)
|
||||
|
||||
@ -15,6 +15,7 @@ from game.server.mapzones.models import ThreatZonesJs, UnculledZoneJs
|
||||
from game.server.navmesh.models import NavMeshJs
|
||||
from game.server.supplyroutes.models import SupplyRouteJs
|
||||
from game.server.tgos.models import TgoJs
|
||||
from game.theater import Player
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game import Game
|
||||
@ -26,9 +27,9 @@ class GameUpdateEventsJs(BaseModel):
|
||||
new_combats: list[FrozenCombatJs]
|
||||
updated_combats: list[FrozenCombatJs]
|
||||
ended_combats: list[UUID]
|
||||
navmesh_updates: dict[bool, NavMeshJs]
|
||||
navmesh_updates: dict[Player, NavMeshJs]
|
||||
updated_unculled_zones: list[UnculledZoneJs]
|
||||
threat_zones_updated: dict[bool, ThreatZonesJs]
|
||||
threat_zones_updated: dict[Player, ThreatZonesJs]
|
||||
new_flights: list[FlightJs]
|
||||
updated_flights: list[FlightJs]
|
||||
deleted_flights: set[UUID]
|
||||
|
||||
@ -41,9 +41,13 @@ class FlightJs(BaseModel):
|
||||
waypoints = None
|
||||
if with_waypoints:
|
||||
waypoints = waypoints_for_flight(flight)
|
||||
if flight.blue.is_blue:
|
||||
blue = True
|
||||
else:
|
||||
blue = False
|
||||
return FlightJs(
|
||||
id=flight.id,
|
||||
blue=flight.blue,
|
||||
blue=blue,
|
||||
position=position,
|
||||
sidc=str(flight.sidc()),
|
||||
waypoints=waypoints,
|
||||
|
||||
@ -5,6 +5,7 @@ from uuid import UUID
|
||||
from pydantic import BaseModel
|
||||
|
||||
from game.server.leaflet import LeafletPoint
|
||||
from game.theater.player import Player
|
||||
from game.theater.iadsnetwork.iadsnetwork import IadsNetworkNode, IadsNetwork
|
||||
|
||||
|
||||
@ -34,8 +35,16 @@ class IadsConnectionJs(BaseModel):
|
||||
iads_connections = []
|
||||
tgo = network_node.group.ground_object
|
||||
for id, connection in network_node.connections.items():
|
||||
if connection.ground_object.is_friendly(True) != tgo.is_friendly(True):
|
||||
if connection.ground_object.is_friendly(Player.BLUE) != tgo.is_friendly(
|
||||
Player.BLUE
|
||||
):
|
||||
continue # Skip connections which are not from same coalition
|
||||
if tgo.is_friendly(Player.BLUE):
|
||||
blue = True
|
||||
elif tgo.is_friendly(Player.RED):
|
||||
blue = False
|
||||
else:
|
||||
continue # Skip neutral
|
||||
iads_connections.append(
|
||||
IadsConnectionJs(
|
||||
id=id,
|
||||
@ -49,7 +58,7 @@ class IadsConnectionJs(BaseModel):
|
||||
network_node.group.alive_units > 0
|
||||
and connection.alive_units > 0
|
||||
),
|
||||
blue=tgo.is_friendly(True),
|
||||
blue=blue,
|
||||
is_power="power"
|
||||
in [tgo.category, connection.ground_object.category],
|
||||
)
|
||||
|
||||
@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
|
||||
from pydantic import BaseModel
|
||||
|
||||
from game.server.leaflet import LeafletPoint, LeafletPoly, ShapelyUtil
|
||||
from game.theater import ConflictTheater
|
||||
from game.theater import ConflictTheater, Player
|
||||
from game.threatzones import ThreatZones
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -85,9 +85,9 @@ class ThreatZoneContainerJs(BaseModel):
|
||||
def for_game(game: Game) -> ThreatZoneContainerJs:
|
||||
return ThreatZoneContainerJs(
|
||||
blue=ThreatZonesJs.from_zones(
|
||||
game.threat_zone_for(player=True), game.theater
|
||||
game.threat_zone_for(player=Player.BLUE), game.theater
|
||||
),
|
||||
red=ThreatZonesJs.from_zones(
|
||||
game.threat_zone_for(player=False), game.theater
|
||||
game.threat_zone_for(player=Player.RED), game.theater
|
||||
),
|
||||
)
|
||||
|
||||
@ -2,12 +2,13 @@ from fastapi import APIRouter, Depends
|
||||
|
||||
from game import Game
|
||||
from game.server import GameContext
|
||||
from game.theater.player import Player
|
||||
from .models import NavMeshJs
|
||||
|
||||
router: APIRouter = APIRouter(prefix="/navmesh")
|
||||
|
||||
|
||||
@router.get("/", operation_id="get_navmesh", response_model=NavMeshJs)
|
||||
def get(for_player: bool, game: Game = Depends(GameContext.require)) -> NavMeshJs:
|
||||
def get(for_player: Player, game: Game = Depends(GameContext.require)) -> NavMeshJs:
|
||||
mesh = game.coalition_for(for_player).nav_mesh
|
||||
return NavMeshJs.from_navmesh(mesh, game)
|
||||
|
||||
@ -76,6 +76,10 @@ class SupplyRouteJs(BaseModel):
|
||||
def for_link(
|
||||
game: Game, a: ControlPoint, b: ControlPoint, points: list[Point], sea: bool
|
||||
) -> SupplyRouteJs:
|
||||
if a.captured.is_blue:
|
||||
blue = True
|
||||
else:
|
||||
blue = False
|
||||
return SupplyRouteJs(
|
||||
# Although these are not persistent objects in the backend, the frontend
|
||||
# needs unique IDs for anything that it will use in a list. That means that
|
||||
@ -93,7 +97,7 @@ class SupplyRouteJs(BaseModel):
|
||||
points=[p.latlng() for p in points],
|
||||
front_active=not sea and a.front_is_active(b),
|
||||
is_sea=sea,
|
||||
blue=a.captured,
|
||||
blue=blue,
|
||||
active_transports=TransportFinder(game, a, b).describe_active_transports(
|
||||
sea
|
||||
),
|
||||
|
||||
@ -34,12 +34,16 @@ class TgoJs(BaseModel):
|
||||
def for_tgo(tgo: TheaterGroundObject) -> TgoJs:
|
||||
threat_ranges = [group.max_threat_range().meters for group in tgo.groups]
|
||||
detection_ranges = [group.max_detection_range().meters for group in tgo.groups]
|
||||
if tgo.control_point.captured.is_blue:
|
||||
blue = True
|
||||
else:
|
||||
blue = False
|
||||
return TgoJs(
|
||||
id=tgo.id,
|
||||
name=tgo.name,
|
||||
control_point_name=tgo.control_point.name,
|
||||
category=tgo.category,
|
||||
blue=tgo.control_point.captured,
|
||||
blue=blue,
|
||||
position=tgo.position.latlng(),
|
||||
units=[unit.display_name for unit in tgo.units],
|
||||
threat_ranges=threat_ranges,
|
||||
|
||||
@ -46,7 +46,7 @@ class AirCombat(JoinableCombat):
|
||||
blue_flights = []
|
||||
red_flights = []
|
||||
for flight in self.flights:
|
||||
if flight.squadron.player:
|
||||
if flight.squadron.player.is_blue:
|
||||
blue_flights.append(str(flight))
|
||||
else:
|
||||
red_flights.append(str(flight))
|
||||
@ -71,7 +71,7 @@ class AirCombat(JoinableCombat):
|
||||
blue = []
|
||||
red = []
|
||||
for flight in self.flights:
|
||||
if flight.squadron.player:
|
||||
if flight.squadron.player.is_blue:
|
||||
blue.append(flight)
|
||||
else:
|
||||
red.append(flight)
|
||||
|
||||
@ -6,6 +6,7 @@ from collections.abc import Iterator
|
||||
from datetime import timedelta
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
from game.theater.player import Player
|
||||
from .aircombat import AirCombat
|
||||
from .aircraftengagementzones import AircraftEngagementZones
|
||||
from .atip import AtIp
|
||||
@ -31,8 +32,10 @@ class CombatInitiator:
|
||||
def update_active_combats(self) -> None:
|
||||
blue_a2a = AircraftEngagementZones.from_ato(self.game.blue.ato)
|
||||
red_a2a = AircraftEngagementZones.from_ato(self.game.red.ato)
|
||||
blue_sam = SamEngagementZones.from_theater(self.game.theater, player=True)
|
||||
red_sam = SamEngagementZones.from_theater(self.game.theater, player=False)
|
||||
blue_sam = SamEngagementZones.from_theater(
|
||||
self.game.theater, player=Player.BLUE
|
||||
)
|
||||
red_sam = SamEngagementZones.from_theater(self.game.theater, player=Player.RED)
|
||||
|
||||
# Check each vulnerable flight to see if it has initiated combat. If any flight
|
||||
# initiates combat, a single FrozenCombat will be created for all involved
|
||||
@ -46,7 +49,7 @@ class CombatInitiator:
|
||||
if flight.state.in_combat:
|
||||
return
|
||||
|
||||
if flight.squadron.player:
|
||||
if flight.squadron.player.is_blue:
|
||||
a2a = red_a2a
|
||||
own_a2a = blue_a2a
|
||||
sam = red_sam
|
||||
|
||||
@ -9,7 +9,7 @@ from shapely.ops import unary_union
|
||||
from game.utils import dcs_to_shapely_point
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.theater import ConflictTheater, TheaterGroundObject
|
||||
from game.theater import ConflictTheater, TheaterGroundObject, Player
|
||||
from game.threatzones import ThreatPoly
|
||||
|
||||
|
||||
@ -31,7 +31,9 @@ class SamEngagementZones:
|
||||
yield tgo
|
||||
|
||||
@classmethod
|
||||
def from_theater(cls, theater: ConflictTheater, player: bool) -> SamEngagementZones:
|
||||
def from_theater(
|
||||
cls, theater: ConflictTheater, player: Player
|
||||
) -> SamEngagementZones:
|
||||
commit_regions = []
|
||||
individual_zones = []
|
||||
for cp in theater.control_points_for(player):
|
||||
|
||||
@ -12,7 +12,7 @@ if TYPE_CHECKING:
|
||||
from game.ato import Flight, Package
|
||||
from game.navmesh import NavMesh
|
||||
from game.sim.combat import FrozenCombat
|
||||
from game.theater import ControlPoint, FrontLine, TheaterGroundObject
|
||||
from game.theater import ControlPoint, FrontLine, TheaterGroundObject, Player
|
||||
from game.threatzones import ThreatZones
|
||||
from game.theater.iadsnetwork.iadsnetwork import IadsNetworkNode
|
||||
|
||||
@ -24,9 +24,9 @@ class GameUpdateEvents:
|
||||
updated_combats: list[FrozenCombat] = field(default_factory=list)
|
||||
ended_combats: list[FrozenCombat] = field(default_factory=list)
|
||||
updated_flight_positions: list[tuple[Flight, Point]] = field(default_factory=list)
|
||||
navmesh_updates: dict[bool, NavMesh] = field(default_factory=dict)
|
||||
navmesh_updates: dict[Player, NavMesh] = field(default_factory=dict)
|
||||
unculled_zones_updated: list[Point] = field(default_factory=list)
|
||||
threat_zones_updated: dict[bool, ThreatZones] = field(default_factory=dict)
|
||||
threat_zones_updated: dict[Player, ThreatZones] = field(default_factory=dict)
|
||||
new_flights: set[Flight] = field(default_factory=set)
|
||||
updated_flights: set[Flight] = field(default_factory=set)
|
||||
deleted_flights: set[UUID] = field(default_factory=set)
|
||||
@ -70,7 +70,7 @@ class GameUpdateEvents:
|
||||
self.updated_flight_positions.append((flight, new_position))
|
||||
return self
|
||||
|
||||
def update_navmesh(self, player: bool, navmesh: NavMesh) -> GameUpdateEvents:
|
||||
def update_navmesh(self, player: Player, navmesh: NavMesh) -> GameUpdateEvents:
|
||||
self.navmesh_updates[player] = navmesh
|
||||
return self
|
||||
|
||||
@ -78,7 +78,9 @@ class GameUpdateEvents:
|
||||
self.unculled_zones_updated = zones
|
||||
return self
|
||||
|
||||
def update_threat_zones(self, player: bool, zones: ThreatZones) -> GameUpdateEvents:
|
||||
def update_threat_zones(
|
||||
self, player: Player, zones: ThreatZones
|
||||
) -> GameUpdateEvents:
|
||||
self.threat_zones_updated[player] = zones
|
||||
return self
|
||||
|
||||
|
||||
@ -145,7 +145,7 @@ class MissionResultsProcessor:
|
||||
def commit_captures(self, debriefing: Debriefing, events: GameUpdateEvents) -> None:
|
||||
for captured in debriefing.base_captures:
|
||||
try:
|
||||
if captured.captured_by_player:
|
||||
if captured.captured_by_player.is_blue:
|
||||
self.game.message(
|
||||
f"{captured.control_point} captured!",
|
||||
f"We took control of {captured.control_point}.",
|
||||
|
||||
@ -14,12 +14,13 @@ from ..utils import Distance
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.game import Game
|
||||
from game.theater.player import Player
|
||||
from ..ato.flighttype import FlightType
|
||||
from .squadron import Squadron
|
||||
|
||||
|
||||
class AirWing:
|
||||
def __init__(self, player: bool, game: Game, faction: Faction) -> None:
|
||||
def __init__(self, player: Player, game: Game, faction: Faction) -> None:
|
||||
self.player = player
|
||||
self.squadrons: dict[AircraftType, list[Squadron]] = defaultdict(list)
|
||||
self.squadron_defs = SquadronDefLoader(game, faction).load()
|
||||
|
||||
@ -24,7 +24,7 @@ if TYPE_CHECKING:
|
||||
from game import Game
|
||||
from game.coalition import Coalition
|
||||
from game.dcs.aircrafttype import AircraftType
|
||||
from game.theater import ControlPoint, MissionTarget
|
||||
from game.theater import ControlPoint, MissionTarget, Player
|
||||
from .operatingbases import OperatingBases
|
||||
from .squadrondef import SquadronDef
|
||||
|
||||
@ -96,7 +96,7 @@ class Squadron:
|
||||
self._livery_pool: list[str] = []
|
||||
|
||||
@property
|
||||
def player(self) -> bool:
|
||||
def player(self) -> Player:
|
||||
return self.coalition.player
|
||||
|
||||
def assign_to_base(self, base: ControlPoint) -> None:
|
||||
@ -134,7 +134,7 @@ class Squadron:
|
||||
return self.claim_new_pilot_if_allowed()
|
||||
|
||||
# For opfor, so player/AI option is irrelevant.
|
||||
if not self.player:
|
||||
if self.player != Player.BLUE:
|
||||
return self.available_pilots.pop()
|
||||
|
||||
preference = self.settings.auto_ato_behavior
|
||||
|
||||
@ -15,6 +15,7 @@ from .daytimemap import DaytimeMap
|
||||
from .frontline import FrontLine
|
||||
from .iadsnetwork.iadsnetwork import IadsNetwork
|
||||
from .landmap import poly_contains, load_landmap
|
||||
from .player import Player
|
||||
from .seasonalconditions import SeasonalConditions
|
||||
from ..utils import Heading
|
||||
|
||||
@ -170,10 +171,10 @@ class ConflictTheater:
|
||||
return new_point
|
||||
|
||||
def control_points_for(
|
||||
self, player: bool, state_check: bool = False
|
||||
self, player: Player, state_check: bool = False
|
||||
) -> Iterator[ControlPoint]:
|
||||
for point in self.controlpoints:
|
||||
if point.captured == player:
|
||||
if point.captured is player:
|
||||
if not state_check:
|
||||
yield point
|
||||
elif point.is_carrier and point.runway_is_operational():
|
||||
@ -182,14 +183,21 @@ class ConflictTheater:
|
||||
yield point
|
||||
|
||||
def player_points(self, state_check: bool = False) -> List[ControlPoint]:
|
||||
return list(self.control_points_for(player=True, state_check=state_check))
|
||||
return list(
|
||||
self.control_points_for(player=Player.BLUE, state_check=state_check)
|
||||
)
|
||||
|
||||
def conflicts(self) -> Iterator[FrontLine]:
|
||||
for cp in self.player_points():
|
||||
yield from cp.front_lines.values()
|
||||
|
||||
def enemy_points(self, state_check: bool = False) -> List[ControlPoint]:
|
||||
return list(self.control_points_for(player=False, state_check=state_check))
|
||||
return list(self.control_points_for(player=Player.RED, state_check=state_check))
|
||||
|
||||
def neutral_points(self, state_check: bool = False) -> List[ControlPoint]:
|
||||
return list(
|
||||
self.control_points_for(player=Player.NEUTRAL, state_check=state_check)
|
||||
)
|
||||
|
||||
def closest_control_point(
|
||||
self, point: Point, allow_naval: bool = False
|
||||
@ -259,10 +267,12 @@ class ConflictTheater:
|
||||
"""
|
||||
closest_cps = list()
|
||||
distances_to_cp = dict()
|
||||
if cp.captured:
|
||||
if cp.captured.is_blue:
|
||||
control_points = self.player_points()
|
||||
else:
|
||||
elif cp.captured.is_red:
|
||||
control_points = self.enemy_points()
|
||||
elif cp.captured.is_neutral:
|
||||
control_points = self.neutral_points()
|
||||
for other_cp in control_points:
|
||||
if cp == other_cp:
|
||||
continue
|
||||
|
||||
@ -68,6 +68,7 @@ from .base import Base
|
||||
from .frontline import FrontLine
|
||||
from .interfaces.CTLD import CTLD
|
||||
from .missiontarget import MissionTarget
|
||||
from .player import Player
|
||||
from .theatergroundobject import (
|
||||
GenericCarrierGroundObject,
|
||||
TheaterGroundObject,
|
||||
@ -377,7 +378,7 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
position: Point,
|
||||
at: StartingPosition,
|
||||
theater: ConflictTheater,
|
||||
starts_blue: bool,
|
||||
starting_coalition: Player,
|
||||
cptype: ControlPointType = ControlPointType.AIRBASE,
|
||||
is_invisible: bool = False,
|
||||
) -> None:
|
||||
@ -386,8 +387,8 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
self.full_name = name
|
||||
self.at = at
|
||||
self.theater = theater
|
||||
self.starts_blue = starts_blue
|
||||
self.is_invisible = is_invisible
|
||||
self.starting_coalition = starting_coalition
|
||||
self.connected_objectives: List[TheaterGroundObject] = []
|
||||
self.preset_locations = PresetLocations()
|
||||
self.helipads: List[PointWithHeading] = []
|
||||
@ -435,7 +436,7 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
|
||||
def finish_init(self, game: Game) -> None:
|
||||
assert self._coalition is None
|
||||
self._coalition = game.coalition_for(self.starts_blue)
|
||||
self._coalition = game.coalition_for(self.starting_coalition)
|
||||
assert self._front_line_db is None
|
||||
self._front_line_db = game.db.front_lines
|
||||
|
||||
@ -444,7 +445,8 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
# the entire game state when it comes up.
|
||||
from game.sim import GameUpdateEvents
|
||||
|
||||
self._create_missing_front_lines(laser_code_registry, GameUpdateEvents())
|
||||
if self.captured != Player.NEUTRAL:
|
||||
self._create_missing_front_lines(laser_code_registry, GameUpdateEvents())
|
||||
|
||||
@property
|
||||
def front_line_db(self) -> Database[FrontLine]:
|
||||
@ -455,9 +457,11 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
self, laser_code_registry: LaserCodeRegistry, events: GameUpdateEvents
|
||||
) -> None:
|
||||
for connection in self.convoy_routes.keys():
|
||||
if not connection.front_line_active_with(
|
||||
self
|
||||
) and not connection.is_friendly_to(self):
|
||||
if (
|
||||
not connection.front_line_active_with(self)
|
||||
and not connection.is_friendly_to(self)
|
||||
and connection.captured != Player.NEUTRAL
|
||||
):
|
||||
self._create_front_line_with(laser_code_registry, connection, events)
|
||||
|
||||
def _create_front_line_with(
|
||||
@ -491,6 +495,8 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
|
||||
@property
|
||||
def has_frontline(self) -> bool:
|
||||
if self.captured.is_neutral:
|
||||
return False
|
||||
return bool(self.front_lines)
|
||||
|
||||
def front_line_active_with(self, other: ControlPoint) -> bool:
|
||||
@ -500,14 +506,17 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
return self.front_lines[other]
|
||||
|
||||
@property
|
||||
def captured(self) -> bool:
|
||||
def captured(self) -> Player:
|
||||
return self.coalition.player
|
||||
|
||||
@property
|
||||
def standard_identity(self) -> StandardIdentity:
|
||||
return (
|
||||
StandardIdentity.FRIEND if self.captured else StandardIdentity.HOSTILE_FAKER
|
||||
)
|
||||
if self.captured.is_neutral:
|
||||
return StandardIdentity.UNKNOWN
|
||||
elif self.captured.is_blue:
|
||||
return StandardIdentity.FRIEND
|
||||
else:
|
||||
return StandardIdentity.HOSTILE_FAKER
|
||||
|
||||
@property
|
||||
def sidc_status(self) -> Status:
|
||||
@ -819,7 +828,7 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
found.append(g)
|
||||
return found
|
||||
|
||||
def is_friendly(self, to_player: bool) -> bool:
|
||||
def is_friendly(self, to_player: Player) -> bool:
|
||||
return self.captured == to_player
|
||||
|
||||
def is_friendly_to(self, control_point: ControlPoint) -> bool:
|
||||
@ -828,7 +837,9 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
def capture_equipment(self, game: Game) -> None:
|
||||
total = self.base.total_armor_value
|
||||
self.base.armor.clear()
|
||||
game.adjust_budget(total, player=not self.captured)
|
||||
game.adjust_budget(
|
||||
total, player=Player.BLUE if self.captured.is_red else Player.RED
|
||||
)
|
||||
game.message(
|
||||
f"{self.name} is not connected to any friendly points. Ground "
|
||||
f"vehicles have been captured and sold for ${total}M."
|
||||
@ -857,7 +868,9 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
|
||||
def capture_aircraft(self, game: Game, airframe: AircraftType, count: int) -> None:
|
||||
value = airframe.price * count
|
||||
game.adjust_budget(value, player=not self.captured)
|
||||
game.adjust_budget(
|
||||
value, player=Player.BLUE if self.captured.is_red else Player.RED
|
||||
)
|
||||
game.message(
|
||||
f"No valid retreat destination in range of {self.name} for {airframe} "
|
||||
f"{count} aircraft have been captured and sold for ${value}M."
|
||||
@ -960,7 +973,7 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
pass
|
||||
|
||||
# TODO: Should be Airbase specific.
|
||||
def capture(self, game: Game, events: GameUpdateEvents, for_player: bool) -> None:
|
||||
def capture(self, game: Game, events: GameUpdateEvents, for_player: Player) -> None:
|
||||
new_coalition = game.coalition_for(for_player)
|
||||
self.ground_unit_orders.refund_all(self.coalition)
|
||||
self.retreat_ground_units(game)
|
||||
@ -1125,6 +1138,8 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
|
||||
@property
|
||||
def has_active_frontline(self) -> bool:
|
||||
if self.captured.is_neutral:
|
||||
return False
|
||||
return any(not c.is_friendly(self.captured) for c in self.connected_points)
|
||||
|
||||
def front_is_active(self, other: ControlPoint) -> bool:
|
||||
@ -1211,7 +1226,7 @@ class Airfield(ControlPoint, CTLD):
|
||||
self,
|
||||
airport: Airport,
|
||||
theater: ConflictTheater,
|
||||
starts_blue: bool,
|
||||
starting_coalition: Player,
|
||||
ctld_zones: Optional[List[Tuple[Point, float]]] = None,
|
||||
influence_zone: Optional[List[Tuple[Point, float]]] = None,
|
||||
) -> None:
|
||||
@ -1220,7 +1235,7 @@ class Airfield(ControlPoint, CTLD):
|
||||
airport.position,
|
||||
airport,
|
||||
theater,
|
||||
starts_blue,
|
||||
starting_coalition,
|
||||
cptype=ControlPointType.AIRBASE,
|
||||
)
|
||||
self.airport = airport
|
||||
@ -1252,7 +1267,7 @@ class Airfield(ControlPoint, CTLD):
|
||||
return True
|
||||
return self.runway_is_operational()
|
||||
|
||||
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
|
||||
def mission_types(self, for_player: Player) -> Iterator[FlightType]:
|
||||
from game.ato import FlightType
|
||||
|
||||
if not self.is_friendly(for_player):
|
||||
@ -1373,7 +1388,7 @@ class NavalControlPoint(
|
||||
def is_fleet(self) -> bool:
|
||||
return True
|
||||
|
||||
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
|
||||
def mission_types(self, for_player: Player) -> Iterator[FlightType]:
|
||||
from game.ato import FlightType
|
||||
|
||||
if self.is_friendly(for_player):
|
||||
@ -1482,7 +1497,7 @@ class NavalControlPoint(
|
||||
|
||||
class Carrier(NavalControlPoint):
|
||||
def __init__(
|
||||
self, name: str, at: Point, theater: ConflictTheater, starts_blue: bool
|
||||
self, name: str, at: Point, theater: ConflictTheater, starts_blue: Player
|
||||
):
|
||||
super().__init__(
|
||||
name,
|
||||
@ -1497,7 +1512,7 @@ class Carrier(NavalControlPoint):
|
||||
def symbol_set_and_entity(self) -> tuple[SymbolSet, Entity]:
|
||||
return SymbolSet.SEA_SURFACE, SeaSurfaceEntity.CARRIER
|
||||
|
||||
def capture(self, game: Game, events: GameUpdateEvents, for_player: bool) -> None:
|
||||
def capture(self, game: Game, events: GameUpdateEvents, for_player: Player) -> None:
|
||||
raise RuntimeError("Carriers cannot be captured")
|
||||
|
||||
@property
|
||||
@ -1522,7 +1537,7 @@ class EssexCarrier(Carrier):
|
||||
|
||||
class Lha(NavalControlPoint):
|
||||
def __init__(
|
||||
self, name: str, at: Point, theater: ConflictTheater, starts_blue: bool
|
||||
self, name: str, at: Point, theater: ConflictTheater, starts_blue: Player
|
||||
):
|
||||
super().__init__(
|
||||
name, at, at, theater, starts_blue, cptype=ControlPointType.LHA_GROUP
|
||||
@ -1532,7 +1547,7 @@ class Lha(NavalControlPoint):
|
||||
def symbol_set_and_entity(self) -> tuple[SymbolSet, Entity]:
|
||||
return SymbolSet.SEA_SURFACE, SeaSurfaceEntity.AMPHIBIOUS_ASSAULT_SHIP_GENERAL
|
||||
|
||||
def capture(self, game: Game, events: GameUpdateEvents, for_player: bool) -> None:
|
||||
def capture(self, game: Game, events: GameUpdateEvents, for_player: Player) -> None:
|
||||
raise RuntimeError("LHAs cannot be captured")
|
||||
|
||||
@property
|
||||
@ -1555,7 +1570,7 @@ class OffMapSpawn(ControlPoint):
|
||||
return True
|
||||
|
||||
def __init__(
|
||||
self, name: str, position: Point, theater: ConflictTheater, starts_blue: bool
|
||||
self, name: str, position: Point, theater: ConflictTheater, starts_blue: Player
|
||||
):
|
||||
super().__init__(
|
||||
name,
|
||||
@ -1570,10 +1585,10 @@ class OffMapSpawn(ControlPoint):
|
||||
def symbol_set_and_entity(self) -> tuple[SymbolSet, Entity]:
|
||||
return SymbolSet.LAND_INSTALLATIONS, LandInstallationEntity.AIPORT_AIR_BASE
|
||||
|
||||
def capture(self, game: Game, events: GameUpdateEvents, for_player: bool) -> None:
|
||||
def capture(self, game: Game, events: GameUpdateEvents, for_player: Player) -> None:
|
||||
raise RuntimeError("Off map control points cannot be captured")
|
||||
|
||||
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
|
||||
def mission_types(self, for_player: Player) -> Iterator[FlightType]:
|
||||
yield from []
|
||||
|
||||
def total_aircraft_parking(self, parking_type: ParkingType) -> int:
|
||||
@ -1630,7 +1645,7 @@ class Fob(ControlPoint, RadioFrequencyContainer, CTLD):
|
||||
name: str,
|
||||
at: Point,
|
||||
theater: ConflictTheater,
|
||||
starts_blue: bool,
|
||||
starts_blue: Player,
|
||||
ctld_zones: Optional[List[Tuple[Point, float]]] = None,
|
||||
is_invisible: bool = False,
|
||||
influence_zone: Optional[List[Tuple[Point, float]]] = None,
|
||||
@ -1667,7 +1682,7 @@ class Fob(ControlPoint, RadioFrequencyContainer, CTLD):
|
||||
def runway_status(self) -> RunwayStatus:
|
||||
return RunwayStatus()
|
||||
|
||||
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
|
||||
def mission_types(self, for_player: Player) -> Iterator[FlightType]:
|
||||
from game.ato import FlightType
|
||||
|
||||
if not self.is_friendly(for_player):
|
||||
|
||||
@ -8,6 +8,7 @@ from typing import Any, Iterator, List, TYPE_CHECKING, Tuple
|
||||
from dcs.mapping import Point
|
||||
|
||||
from .missiontarget import MissionTarget
|
||||
from .player import Player
|
||||
from ..lasercodes.lasercode import LaserCode
|
||||
from ..utils import Heading, pairwise
|
||||
|
||||
@ -89,19 +90,22 @@ class FrontLine(MissionTarget):
|
||||
def update_position(self) -> None:
|
||||
self.position = self._compute_position()
|
||||
|
||||
def control_point_friendly_to(self, player: bool) -> ControlPoint:
|
||||
if player:
|
||||
def control_point_friendly_to(self, player: Player) -> ControlPoint:
|
||||
if player.is_blue:
|
||||
return self.blue_cp
|
||||
return self.red_cp
|
||||
|
||||
def control_point_hostile_to(self, player: bool) -> ControlPoint:
|
||||
return self.control_point_friendly_to(not player)
|
||||
def control_point_hostile_to(self, player: Player) -> ControlPoint:
|
||||
if player.is_blue:
|
||||
return self.red_cp
|
||||
else:
|
||||
return self.blue_cp
|
||||
|
||||
def is_friendly(self, to_player: bool) -> bool:
|
||||
def is_friendly(self, to_player: Player) -> bool:
|
||||
"""Returns True if the objective is in friendly territory."""
|
||||
return False
|
||||
|
||||
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
|
||||
def mission_types(self, for_player: Player) -> Iterator[FlightType]:
|
||||
from game.ato import FlightType
|
||||
|
||||
yield from [
|
||||
@ -210,6 +214,6 @@ class FrontLine(MissionTarget):
|
||||
raise ValueError(
|
||||
"Cannot sort control points that are friendly to each other"
|
||||
)
|
||||
if a.captured:
|
||||
if a.captured.is_blue:
|
||||
return a, b
|
||||
return b, a
|
||||
|
||||
@ -16,6 +16,7 @@ from game.theater.theatergroundobject import (
|
||||
TheaterGroundObject,
|
||||
)
|
||||
from game.theater.theatergroup import IadsGroundGroup
|
||||
from game.theater.player import Player
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.game import Game
|
||||
@ -31,7 +32,7 @@ class SkynetNode:
|
||||
"""Dataclass for a SkynetNode used in the LUA Data table by the luagenerator"""
|
||||
|
||||
dcs_name: str
|
||||
player: bool
|
||||
player: Player
|
||||
iads_role: IadsRole
|
||||
properties: dict[str, str] = field(default_factory=dict)
|
||||
connections: dict[str, list[str]] = field(default_factory=lambda: defaultdict(list))
|
||||
@ -62,7 +63,7 @@ class SkynetNode:
|
||||
def from_group(cls, group: IadsGroundGroup) -> SkynetNode:
|
||||
node = cls(
|
||||
cls.dcs_name_for_group(group),
|
||||
group.ground_object.is_friendly(True),
|
||||
group.ground_object.coalition.player,
|
||||
group.iads_role,
|
||||
)
|
||||
unit_type = group.units[0].unit_type
|
||||
@ -314,8 +315,8 @@ class IadsNetwork:
|
||||
self._make_advanced_connections_by_range(node)
|
||||
|
||||
def _is_friendly(self, node: IadsNetworkNode, tgo: TheaterGroundObject) -> bool:
|
||||
node_friendly = node.group.ground_object.is_friendly(True)
|
||||
tgo_friendly = tgo.is_friendly(True)
|
||||
node_friendly = node.group.ground_object.is_friendly(Player.BLUE)
|
||||
tgo_friendly = tgo.is_friendly(Player.BLUE)
|
||||
return node_friendly == tgo_friendly
|
||||
|
||||
def _update_network(
|
||||
|
||||
@ -6,7 +6,7 @@ from dcs.mapping import Point
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.ato.flighttype import FlightType
|
||||
from game.theater import TheaterUnit, Coalition
|
||||
from game.theater import TheaterUnit, Coalition, Player
|
||||
|
||||
|
||||
class MissionTarget:
|
||||
@ -24,11 +24,11 @@ class MissionTarget:
|
||||
"""Computes the distance to the given mission target."""
|
||||
return self.position.distance_to_point(other.position)
|
||||
|
||||
def is_friendly(self, to_player: bool) -> bool:
|
||||
def is_friendly(self, to_player: Player) -> bool:
|
||||
"""Returns True if the objective is in friendly territory."""
|
||||
raise NotImplementedError
|
||||
|
||||
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
|
||||
def mission_types(self, for_player: Player) -> Iterator[FlightType]:
|
||||
from game.ato import FlightType
|
||||
|
||||
if self.is_friendly(for_player):
|
||||
|
||||
30
game/theater/player.py
Normal file
30
game/theater/player.py
Normal file
@ -0,0 +1,30 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Player(Enum):
|
||||
NEUTRAL = "Neutral"
|
||||
BLUE = "Blue"
|
||||
RED = "Red"
|
||||
|
||||
@property
|
||||
def is_red(self) -> bool:
|
||||
"""Returns True if the player is Red."""
|
||||
return self == Player.RED
|
||||
|
||||
@property
|
||||
def is_blue(self) -> bool:
|
||||
"""Returns True if the player is Blue."""
|
||||
return self == Player.BLUE
|
||||
|
||||
@property
|
||||
def is_neutral(self) -> bool:
|
||||
"""Returns True if the player is Neutral."""
|
||||
return self == Player.NEUTRAL
|
||||
|
||||
@property
|
||||
def opponent(self) -> "Player":
|
||||
"""Returns the opponent player."""
|
||||
if self.is_blue:
|
||||
return Player.RED
|
||||
else:
|
||||
return Player.BLUE
|
||||
@ -32,6 +32,7 @@ from . import (
|
||||
Fob,
|
||||
OffMapSpawn,
|
||||
)
|
||||
from .player import Player
|
||||
from .theatergroup import (
|
||||
IadsGroundGroup,
|
||||
IadsRole,
|
||||
@ -158,12 +159,12 @@ class GameGenerator:
|
||||
game.settings.version = VERSION
|
||||
return game
|
||||
|
||||
def should_remove_carrier(self, player: bool) -> bool:
|
||||
faction = self.player if player else self.enemy
|
||||
def should_remove_carrier(self, player: Player) -> bool:
|
||||
faction = self.player if player.is_blue else self.enemy
|
||||
return self.generator_settings.no_carrier or not faction.carriers
|
||||
|
||||
def should_remove_lha(self, player: bool) -> bool:
|
||||
faction = self.player if player else self.enemy
|
||||
def should_remove_lha(self, player: Player) -> bool:
|
||||
faction = self.player if player.is_blue else self.enemy
|
||||
return self.generator_settings.no_lha or not [
|
||||
x for x in faction.carriers if x.unit_class == UnitClass.HELICOPTER_CARRIER
|
||||
]
|
||||
@ -174,11 +175,13 @@ class GameGenerator:
|
||||
# Remove carrier and lha, invert situation if needed
|
||||
for cp in self.theater.controlpoints:
|
||||
if self.generator_settings.inverted:
|
||||
cp.starts_blue = cp.captured_invert
|
||||
cp.starting_coalition = (
|
||||
Player.RED if not cp.captured_invert else Player.BLUE
|
||||
)
|
||||
|
||||
if cp.is_carrier and self.should_remove_carrier(cp.starts_blue):
|
||||
if cp.is_carrier and self.should_remove_carrier(cp.starting_coalition):
|
||||
to_remove.append(cp)
|
||||
elif cp.is_lha and self.should_remove_lha(cp.starts_blue):
|
||||
elif cp.is_lha and self.should_remove_lha(cp.starting_coalition):
|
||||
to_remove.append(cp)
|
||||
|
||||
# do remove
|
||||
@ -230,11 +233,13 @@ class ControlPointGroundObjectGenerator:
|
||||
self.control_point.connected_objectives.append(ground_object)
|
||||
|
||||
def generate_navy(self) -> None:
|
||||
if self.control_point.captured.is_neutral:
|
||||
return
|
||||
skip_player_navy = self.generator_settings.no_player_navy
|
||||
if self.control_point.captured and skip_player_navy:
|
||||
if self.control_point.captured.is_blue and skip_player_navy:
|
||||
return
|
||||
skip_enemy_navy = self.generator_settings.no_enemy_navy
|
||||
if not self.control_point.captured and skip_enemy_navy:
|
||||
if self.control_point.captured.is_red and skip_enemy_navy:
|
||||
return
|
||||
for position in self.control_point.preset_locations.ships:
|
||||
unit_group = self.armed_forces.random_group_for_task(GroupTask.NAVY)
|
||||
@ -352,7 +357,7 @@ class CarrierGroundObjectGenerator(GenericCarrierGroundObjectGenerator):
|
||||
self.control_point.name,
|
||||
self.control_point.position,
|
||||
self.game.theater,
|
||||
self.control_point.starts_blue,
|
||||
self.control_point.starting_coalition,
|
||||
)
|
||||
self.control_point.finish_init(self.game)
|
||||
self.game.theater.controlpoints.append(self.control_point)
|
||||
|
||||
@ -21,6 +21,7 @@ from game.sidc import (
|
||||
)
|
||||
from game.theater.presetlocation import PresetLocation
|
||||
from .missiontarget import MissionTarget
|
||||
from .player import Player
|
||||
from ..data.groups import GroupTask
|
||||
from ..utils import Distance, Heading, meters
|
||||
|
||||
@ -98,11 +99,12 @@ class TheaterGroundObject(MissionTarget, SidcDescribable, ABC):
|
||||
|
||||
@property
|
||||
def standard_identity(self) -> StandardIdentity:
|
||||
return (
|
||||
StandardIdentity.FRIEND
|
||||
if self.control_point.captured
|
||||
else StandardIdentity.HOSTILE_FAKER
|
||||
)
|
||||
if self.control_point.captured.is_blue:
|
||||
return StandardIdentity.FRIEND
|
||||
elif self.control_point.captured.is_neutral:
|
||||
return StandardIdentity.UNKNOWN
|
||||
else:
|
||||
return StandardIdentity.HOSTILE_FAKER
|
||||
|
||||
@property
|
||||
def is_dead(self) -> bool:
|
||||
@ -154,10 +156,12 @@ class TheaterGroundObject(MissionTarget, SidcDescribable, ABC):
|
||||
def faction_color(self) -> str:
|
||||
return "BLUE" if self.control_point.captured else "RED"
|
||||
|
||||
def is_friendly(self, to_player: bool) -> bool:
|
||||
def is_friendly(self, to_player: Player) -> bool:
|
||||
if self.control_point.captured.is_neutral:
|
||||
return False
|
||||
return self.control_point.is_friendly(to_player)
|
||||
|
||||
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
|
||||
def mission_types(self, for_player: Player) -> Iterator[FlightType]:
|
||||
from game.ato import FlightType
|
||||
|
||||
if self.is_friendly(for_player):
|
||||
@ -360,7 +364,7 @@ class BuildingGroundObject(TheaterGroundObject):
|
||||
|
||||
|
||||
class NavalGroundObject(TheaterGroundObject, ABC):
|
||||
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
|
||||
def mission_types(self, for_player: Player) -> Iterator[FlightType]:
|
||||
from game.ato import FlightType
|
||||
|
||||
if not self.is_friendly(for_player):
|
||||
@ -466,7 +470,7 @@ class MissileSiteGroundObject(TheaterGroundObject):
|
||||
def should_head_to_conflict(self) -> bool:
|
||||
return True
|
||||
|
||||
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
|
||||
def mission_types(self, for_player: Player) -> Iterator[FlightType]:
|
||||
from game.ato import FlightType
|
||||
|
||||
if not self.is_friendly(for_player):
|
||||
@ -507,7 +511,7 @@ class CoastalSiteGroundObject(TheaterGroundObject):
|
||||
def should_head_to_conflict(self) -> bool:
|
||||
return True
|
||||
|
||||
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
|
||||
def mission_types(self, for_player: Player) -> Iterator[FlightType]:
|
||||
from game.ato import FlightType
|
||||
|
||||
if not self.is_friendly(for_player):
|
||||
@ -534,7 +538,7 @@ class IadsGroundObject(TheaterGroundObject, ABC):
|
||||
task=task,
|
||||
)
|
||||
|
||||
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
|
||||
def mission_types(self, for_player: Player) -> Iterator[FlightType]:
|
||||
from game.ato import FlightType
|
||||
|
||||
if not self.is_friendly(for_player):
|
||||
@ -585,7 +589,7 @@ class SamGroundObject(IadsGroundObject):
|
||||
def symbol_set_and_entity(self) -> tuple[SymbolSet, Entity]:
|
||||
return SymbolSet.LAND_UNIT, LandUnitEntity.AIR_DEFENSE
|
||||
|
||||
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
|
||||
def mission_types(self, for_player: Player) -> Iterator[FlightType]:
|
||||
from game.ato import FlightType
|
||||
|
||||
if not self.is_friendly(for_player):
|
||||
@ -642,7 +646,7 @@ class VehicleGroupGroundObject(TheaterGroundObject):
|
||||
def should_head_to_conflict(self) -> bool:
|
||||
return True
|
||||
|
||||
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
|
||||
def mission_types(self, for_player: Player) -> Iterator[FlightType]:
|
||||
from game.ato import FlightType
|
||||
|
||||
if not self.is_friendly(for_player):
|
||||
@ -697,7 +701,7 @@ class ShipGroundObject(NavalGroundObject):
|
||||
|
||||
|
||||
class IadsBuildingGroundObject(BuildingGroundObject):
|
||||
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
|
||||
def mission_types(self, for_player: Player) -> Iterator[FlightType]:
|
||||
from game.ato import FlightType
|
||||
|
||||
if not self.is_friendly(for_player):
|
||||
|
||||
@ -9,6 +9,7 @@ from typing import Dict, Iterator, List, Optional, Set, Tuple
|
||||
|
||||
from .conflicttheater import ConflictTheater
|
||||
from .controlpoint import ControlPoint
|
||||
from .player import Player
|
||||
|
||||
|
||||
class NoPathError(RuntimeError):
|
||||
@ -152,7 +153,7 @@ class TransitNetwork:
|
||||
|
||||
|
||||
class TransitNetworkBuilder:
|
||||
def __init__(self, theater: ConflictTheater, for_player: bool) -> None:
|
||||
def __init__(self, theater: ConflictTheater, for_player: Player) -> None:
|
||||
self.control_points = list(theater.control_points_for(for_player))
|
||||
self.network = TransitNetwork()
|
||||
self.airports: Set[ControlPoint] = {
|
||||
|
||||
@ -26,6 +26,7 @@ from game.utils import Distance, meters, nautical_miles
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game import Game
|
||||
from game.theater.player import Player
|
||||
|
||||
|
||||
ThreatPoly = Union[MultiPolygon, Polygon]
|
||||
@ -187,7 +188,7 @@ class ThreatZones:
|
||||
return min(cap_threat_range, max_distance)
|
||||
|
||||
@classmethod
|
||||
def for_faction(cls, game: Game, player: bool) -> ThreatZones:
|
||||
def for_faction(cls, game: Game, player: Player) -> ThreatZones:
|
||||
"""Generates the threat zones projected by the given coalition.
|
||||
|
||||
Args:
|
||||
|
||||
@ -50,7 +50,14 @@ from game.dcs.aircrafttype import AircraftType
|
||||
from game.dcs.groundunittype import GroundUnitType
|
||||
from game.naming import namegen
|
||||
from game.procurement import AircraftProcurementRequest
|
||||
from game.theater import ControlPoint, MissionTarget, ParkingType, Carrier, Airfield
|
||||
from game.theater import (
|
||||
ControlPoint,
|
||||
MissionTarget,
|
||||
ParkingType,
|
||||
Carrier,
|
||||
Airfield,
|
||||
Player,
|
||||
)
|
||||
from game.theater.transitnetwork import (
|
||||
TransitConnection,
|
||||
TransitNetwork,
|
||||
@ -92,7 +99,7 @@ class TransferOrder:
|
||||
position: ControlPoint = field(init=False)
|
||||
|
||||
#: True if the transfer order belongs to the player.
|
||||
player: bool = field(init=False)
|
||||
player: Player = field(init=False)
|
||||
|
||||
#: The units being transferred.
|
||||
units: dict[GroundUnitType, int]
|
||||
@ -111,7 +118,7 @@ class TransferOrder:
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
self.position = self.origin
|
||||
self.player = self.origin.is_friendly(to_player=True)
|
||||
self.player = self.origin.captured
|
||||
|
||||
@property
|
||||
def description(self) -> str:
|
||||
@ -239,7 +246,10 @@ class Airlift(Transport):
|
||||
|
||||
@property
|
||||
def player_owned(self) -> bool:
|
||||
return self.transfer.player
|
||||
if self.transfer.player.is_blue:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def find_escape_route(self) -> Optional[ControlPoint]:
|
||||
# TODO: Move units to closest base.
|
||||
@ -384,8 +394,11 @@ class MultiGroupTransport(MissionTarget, Transport):
|
||||
self.origin = origin
|
||||
self.transfers: List[TransferOrder] = []
|
||||
|
||||
def is_friendly(self, to_player: bool) -> bool:
|
||||
return self.origin.captured
|
||||
def is_friendly(self, to_player: Player) -> bool:
|
||||
if self.origin.captured.is_blue:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def add_units(self, transfer: TransferOrder) -> None:
|
||||
self.transfers.append(transfer)
|
||||
@ -432,7 +445,7 @@ class MultiGroupTransport(MissionTarget, Transport):
|
||||
yield unit_type
|
||||
|
||||
@property
|
||||
def player_owned(self) -> bool:
|
||||
def player_owned(self) -> Player:
|
||||
return self.origin.captured
|
||||
|
||||
def find_escape_route(self) -> Optional[ControlPoint]:
|
||||
@ -450,7 +463,7 @@ class Convoy(MultiGroupTransport):
|
||||
def __init__(self, origin: ControlPoint, destination: ControlPoint) -> None:
|
||||
super().__init__(namegen.next_convoy_name(), origin, destination)
|
||||
|
||||
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
|
||||
def mission_types(self, for_player: Player) -> Iterator[FlightType]:
|
||||
if self.is_friendly(for_player):
|
||||
return
|
||||
|
||||
@ -476,7 +489,7 @@ class CargoShip(MultiGroupTransport):
|
||||
def __init__(self, origin: ControlPoint, destination: ControlPoint) -> None:
|
||||
super().__init__(namegen.next_cargo_ship_name(), origin, destination)
|
||||
|
||||
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
|
||||
def mission_types(self, for_player: Player) -> Iterator[FlightType]:
|
||||
if self.is_friendly(for_player):
|
||||
return
|
||||
|
||||
@ -570,7 +583,7 @@ class CargoShipMap(TransportMap[CargoShip]):
|
||||
|
||||
|
||||
class PendingTransfers:
|
||||
def __init__(self, game: Game, player: bool) -> None:
|
||||
def __init__(self, game: Game, player: Player) -> None:
|
||||
self.game = game
|
||||
self.player = player
|
||||
self.convoys = ConvoyMap()
|
||||
|
||||
@ -24,7 +24,7 @@ from game.radio.tacan import TacanChannel
|
||||
from game.server import EventStream
|
||||
from game.sim.gameupdateevents import GameUpdateEvents
|
||||
from game.squadrons.squadron import Pilot, Squadron
|
||||
from game.theater import NavalControlPoint
|
||||
from game.theater import NavalControlPoint, Player
|
||||
from game.theater.missiontarget import MissionTarget
|
||||
from game.transfers import PendingTransfers, TransferOrder
|
||||
from qt_ui.simcontroller import SimController
|
||||
@ -564,8 +564,8 @@ class GameModel:
|
||||
self.allocated_icls: list[int] = list()
|
||||
self.init_comms_registry()
|
||||
|
||||
def ato_model_for(self, player: bool) -> AtoModel:
|
||||
if player:
|
||||
def ato_model_for(self, player: Player) -> AtoModel:
|
||||
if player.is_blue:
|
||||
return self.ato_model
|
||||
return self.red_ato_model
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ from PySide6.QtWidgets import (
|
||||
|
||||
from game import Game
|
||||
from game.income import Income
|
||||
from game.theater import Player
|
||||
from qt_ui.windows.intel import IntelWindow
|
||||
|
||||
|
||||
@ -76,8 +77,8 @@ class QIntelBox(QGroupBox):
|
||||
|
||||
def economic_strength_text(self) -> str:
|
||||
assert self.game is not None
|
||||
own = Income(self.game, player=True).total
|
||||
enemy = Income(self.game, player=False).total
|
||||
own = Income(self.game, player=Player.BLUE).total
|
||||
enemy = Income(self.game, player=Player.RED).total
|
||||
|
||||
if not enemy:
|
||||
return "enemy economy ruined"
|
||||
|
||||
@ -6,6 +6,7 @@ from game.ato.flightwaypointtype import FlightWaypointType
|
||||
from game.missiongenerator.frontlineconflictdescription import (
|
||||
FrontLineConflictDescription,
|
||||
)
|
||||
from game.theater.player import Player
|
||||
from game.theater.controlpoint import ControlPointType
|
||||
from game.utils import Distance
|
||||
from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox
|
||||
@ -93,7 +94,7 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
|
||||
wpt.targets.append(target)
|
||||
wpt.obj_name = tgo.obj_name
|
||||
wpt.waypoint_type = FlightWaypointType.CUSTOM
|
||||
if tgo.is_friendly(to_player=True):
|
||||
if tgo.is_friendly(to_player=Player.BLUE):
|
||||
wpt.description = f"Friendly unit: {target.name}"
|
||||
else:
|
||||
wpt.description = f"Enemy unit: {target.name}"
|
||||
|
||||
@ -790,7 +790,7 @@ class AirWingConfigurationDialog(QDialog):
|
||||
self.tabs = []
|
||||
for coalition in game.coalitions:
|
||||
coalition_tab = AirWingConfigurationTab(coalition, game, aircraft_present)
|
||||
name = "Blue" if coalition.player else "Red"
|
||||
name = "Blue" if coalition.player.is_blue else "Red"
|
||||
self.tab_widget.addTab(coalition_tab, name)
|
||||
self.tabs.append(coalition_tab)
|
||||
|
||||
|
||||
@ -14,13 +14,14 @@ from PySide6.QtWidgets import (
|
||||
)
|
||||
|
||||
from game.debriefing import Debriefing
|
||||
from game.theater import Player
|
||||
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class LossGrid(QGridLayout):
|
||||
def __init__(self, debriefing: Debriefing, player: bool) -> None:
|
||||
def __init__(self, debriefing: Debriefing, player: Player) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.add_loss_rows(
|
||||
@ -57,8 +58,10 @@ class LossGrid(QGridLayout):
|
||||
|
||||
|
||||
class ScrollingCasualtyReportContainer(QGroupBox):
|
||||
def __init__(self, debriefing: Debriefing, player: bool) -> None:
|
||||
country = debriefing.player_country if player else debriefing.enemy_country
|
||||
def __init__(self, debriefing: Debriefing, player: Player) -> None:
|
||||
country = (
|
||||
debriefing.player_country if player.is_blue else debriefing.enemy_country
|
||||
)
|
||||
super().__init__(f"{country}'s lost units:")
|
||||
scroll_content = QWidget()
|
||||
scroll_content.setLayout(LossGrid(debriefing, player))
|
||||
@ -91,10 +94,14 @@ class QDebriefingWindow(QDialog):
|
||||
title = QLabel("<b>Casualty report</b>")
|
||||
layout.addWidget(title)
|
||||
|
||||
player_lost_units = ScrollingCasualtyReportContainer(debriefing, player=True)
|
||||
player_lost_units = ScrollingCasualtyReportContainer(
|
||||
debriefing, player=Player.BLUE
|
||||
)
|
||||
layout.addWidget(player_lost_units)
|
||||
|
||||
enemy_lost_units = ScrollingCasualtyReportContainer(debriefing, player=False)
|
||||
enemy_lost_units = ScrollingCasualtyReportContainer(
|
||||
debriefing, player=Player.RED
|
||||
)
|
||||
layout.addWidget(enemy_lost_units, 1)
|
||||
|
||||
okay = QPushButton("Okay")
|
||||
|
||||
@ -25,7 +25,7 @@ from dcs.unittype import UnitType
|
||||
|
||||
from game import Game
|
||||
from game.dcs.groundunittype import GroundUnitType
|
||||
from game.theater import ControlPoint
|
||||
from game.theater import ControlPoint, Player
|
||||
from game.transfers import TransferOrder
|
||||
from qt_ui.models import GameModel
|
||||
from qt_ui.widgets.QLabeledWidget import QLabeledWidget
|
||||
@ -40,7 +40,7 @@ class TransferDestinationComboBox(QComboBox):
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if (
|
||||
cp != self.origin
|
||||
and cp.is_friendly(to_player=True)
|
||||
and cp.is_friendly(to_player=Player.BLUE)
|
||||
and cp.can_deploy_ground_units
|
||||
):
|
||||
self.addItem(cp.name, cp)
|
||||
|
||||
@ -27,6 +27,7 @@ from game.theater import (
|
||||
FREE_FRONTLINE_UNIT_SUPPLY,
|
||||
NavalControlPoint,
|
||||
ParkingType,
|
||||
Player,
|
||||
)
|
||||
from qt_ui.dialogs import Dialog
|
||||
from qt_ui.models import GameModel
|
||||
@ -85,7 +86,7 @@ class QBaseMenu2(QDialog):
|
||||
self.freq_widget = None
|
||||
self.link4_widget = None
|
||||
|
||||
is_friendly = cp.is_friendly(True)
|
||||
is_friendly = cp.is_friendly(Player.BLUE)
|
||||
if is_friendly and isinstance(cp, RadioFrequencyContainer):
|
||||
self.freq_widget = QFrequencyWidget(cp, self.game_model)
|
||||
cp_settings.addWidget(self.freq_widget, counter // 2, counter % 2)
|
||||
|
||||
@ -19,7 +19,7 @@ from game.config import REWARDS
|
||||
from game.data.building_data import FORTIFICATION_BUILDINGS
|
||||
from game.server import EventStream
|
||||
from game.sim.gameupdateevents import GameUpdateEvents
|
||||
from game.theater import ControlPoint, TheaterGroundObject
|
||||
from game.theater import ControlPoint, TheaterGroundObject, Player
|
||||
from game.theater.theatergroundobject import (
|
||||
BuildingGroundObject,
|
||||
)
|
||||
@ -87,12 +87,12 @@ class QGroundObjectMenu(QDialog):
|
||||
|
||||
if isinstance(self.ground_object, BuildingGroundObject):
|
||||
self.mainLayout.addWidget(self.buildingBox)
|
||||
if self.cp.captured:
|
||||
if self.cp.captured.is_blue:
|
||||
self.mainLayout.addWidget(self.financesBox)
|
||||
else:
|
||||
self.mainLayout.addWidget(self.intelBox)
|
||||
self.mainLayout.addWidget(self.orientationBox)
|
||||
if self.ground_object.is_iads and self.cp.is_friendly(to_player=False):
|
||||
if self.ground_object.is_iads and self.cp.is_friendly(to_player=Player.RED):
|
||||
self.mainLayout.addWidget(self.hiddenBox)
|
||||
|
||||
self.actionLayout = QHBoxLayout()
|
||||
@ -118,8 +118,10 @@ class QGroundObjectMenu(QDialog):
|
||||
|
||||
@property
|
||||
def show_buy_sell_actions(self) -> bool:
|
||||
if self.cp.captured.is_neutral:
|
||||
return False
|
||||
buysell_allowed = self.game.settings.enable_enemy_buy_sell
|
||||
buysell_allowed |= self.cp.captured
|
||||
buysell_allowed |= self.cp.captured.is_blue
|
||||
return buysell_allowed
|
||||
|
||||
def doLayout(self):
|
||||
@ -133,7 +135,7 @@ class QGroundObjectMenu(QDialog):
|
||||
QLabel(f"<b>Unit {str(unit.display_name)}</b>"), i, 0
|
||||
)
|
||||
|
||||
if not unit.alive and unit.repairable and self.cp.captured:
|
||||
if not unit.alive and unit.repairable and self.cp.captured.is_blue:
|
||||
price = unit.unit_type.price if unit.unit_type else 0
|
||||
repair = QPushButton(f"Repair [{price}M]")
|
||||
repair.setProperty("style", "btn-success")
|
||||
|
||||
@ -33,7 +33,11 @@ pluggy==1.5.0
|
||||
pre-commit==4.2.0
|
||||
pydantic==2.11.0b2
|
||||
pydantic-settings==2.8.1
|
||||
<<<<<<< HEAD
|
||||
pydcs @ git+https://github.com/dcs-retribution/pydcs@60cddd944d454f59f5c866a2ec2ae3e91526da25
|
||||
=======
|
||||
pydcs @ git+https://github.com/Druss99/pydcs@efbd67fc87de6efe40316523cdd96d1c91711f43
|
||||
>>>>>>> 9f9f53b26 (refactor of previous commits)
|
||||
pyinstaller==5.13.2
|
||||
pyinstaller-hooks-contrib==2024.0
|
||||
pyparsing==3.2.1
|
||||
|
||||
147
resources/campaigns/scenic_rout_neutral.yaml
Normal file
147
resources/campaigns/scenic_rout_neutral.yaml
Normal file
@ -0,0 +1,147 @@
|
||||
---
|
||||
name: Persian Gulf - Scenic Route - Neutral
|
||||
theater: Persian Gulf
|
||||
authors: Fuzzle
|
||||
description: <p>A lightweight naval campaign involving a US Navy carrier group pushing across the coast of Iran. <strong>This is a purely naval campaign, meaning you will need to use the Air Assault mission type with transports to take the first FOB. Ensure you soften it up enough first!</strong></p><p><strong>Backstory:</strong> Iran has declared war on all US forces in the Gulf resulting in all local allies withdrawing their support for American troops. A lone carrier group must pacify the southern coast of Iran and hold out until backup can arrive lest the US and her interests be ejected from the region permanently.</p>
|
||||
version: "10.7"
|
||||
advanced_iads: true
|
||||
recommended_player_faction: US Navy 2005
|
||||
recommended_enemy_faction: Iran 2015
|
||||
miz: scenic_route_neutral.miz
|
||||
performance: 1
|
||||
recommended_start_date: 2005-04-26
|
||||
recommended_player_money: 1000
|
||||
recommended_enemy_money: 1300
|
||||
recommended_player_income_multiplier: 1.2
|
||||
recommended_enemy_income_multiplier: 0.7
|
||||
squadrons:
|
||||
#BLUFOR CVN
|
||||
Naval-1:
|
||||
- primary: BARCAP
|
||||
secondary: air-to-air
|
||||
aircraft:
|
||||
- VF-143
|
||||
size: 14
|
||||
- primary: SEAD
|
||||
secondary: air-to-ground
|
||||
aircraft:
|
||||
- VFA-113
|
||||
- primary: AEW&C
|
||||
aircraft:
|
||||
- VAW-125
|
||||
size: 2
|
||||
- primary: Refueling
|
||||
aircraft:
|
||||
- VS-35 (Tanker)
|
||||
size: 4
|
||||
- primary: Anti-ship
|
||||
secondary: air-to-ground
|
||||
aircraft:
|
||||
- VS-35
|
||||
size: 8
|
||||
- primary: Transport
|
||||
aircraft:
|
||||
- HSM-40
|
||||
size: 2
|
||||
# BLUFOR LHA
|
||||
Naval-2:
|
||||
- primary: BAI
|
||||
secondary: air-to-ground
|
||||
aircraft:
|
||||
- VMA-223
|
||||
size: 10
|
||||
- primary: Transport
|
||||
secondary: air-to-ground
|
||||
aircraft:
|
||||
- HMLA-169 (UH-1H)
|
||||
size: 4
|
||||
- primary: CAS
|
||||
secondary: air-to-ground
|
||||
aircraft:
|
||||
- HMLA-169 (AH-1W)
|
||||
size: 6
|
||||
# OPFOR CVN
|
||||
Naval-3:
|
||||
- primary: BARCAP
|
||||
secondary: any
|
||||
- primary: Strike
|
||||
secondary: any
|
||||
- primary: BAI
|
||||
secondary: any
|
||||
- primary: Refueling
|
||||
# Kish Intl
|
||||
24:
|
||||
- primary: AEW&C
|
||||
aircraft:
|
||||
- A-50
|
||||
size: 1
|
||||
- primary: BARCAP
|
||||
secondary: any
|
||||
aircraft:
|
||||
- F-14A Tomcat (Block 135-GR Late)
|
||||
- primary: Strike
|
||||
secondary: air-to-ground
|
||||
aircraft:
|
||||
- Su-24MK Fencer-D
|
||||
- primary: BARCAP
|
||||
secondary: any
|
||||
aircraft:
|
||||
- F-4E Phantom II
|
||||
# Havadarya
|
||||
9:
|
||||
- primary: BARCAP
|
||||
secondary: any
|
||||
aircraft:
|
||||
- F-4E Phantom II
|
||||
size: 10
|
||||
- primary: CAS
|
||||
secondary: air-to-ground
|
||||
aircraft:
|
||||
- Su-25 Frogfoot
|
||||
size: 12
|
||||
# Bandar Abbas Intl
|
||||
2:
|
||||
- primary: TARCAP
|
||||
secondary: any
|
||||
aircraft:
|
||||
- F-5E Tiger II
|
||||
- primary: BARCAP
|
||||
secondary: air-to-ground
|
||||
aircraft:
|
||||
- MiG-29A Fulcrum-A
|
||||
- primary: Strike
|
||||
secondary: air-to-ground
|
||||
aircraft:
|
||||
- Su-24MK
|
||||
- primary: BARCAP
|
||||
secondary: air-to-ground
|
||||
aircraft:
|
||||
- F-4E Phantom II
|
||||
# OPFOR First FOB
|
||||
FOB Seerik:
|
||||
- primary: CAS
|
||||
secondary: air-to-ground
|
||||
aircraft:
|
||||
- Mi-24V Hind-E
|
||||
size: 6
|
||||
# OPFOR Second FOB
|
||||
FOB Kohnehshahr:
|
||||
- primary: CAS
|
||||
secondary: air-to-ground
|
||||
aircraft:
|
||||
- Mi-24V Hind-F
|
||||
size: 6
|
||||
# OPFOR Third FOB
|
||||
FOB Khvosh:
|
||||
- primary: CAS
|
||||
secondary: air-to-ground
|
||||
aircraft:
|
||||
- Mi-28N Havoc
|
||||
size: 6
|
||||
# OPFOR Last FOB
|
||||
FOB Charak:
|
||||
- primary: CAS
|
||||
secondary: air-to-ground
|
||||
aircraft:
|
||||
- Mi-28N Havoc
|
||||
size: 6
|
||||
BIN
resources/campaigns/scenic_route_neutral.miz
Normal file
BIN
resources/campaigns/scenic_route_neutral.miz
Normal file
Binary file not shown.
@ -17,6 +17,7 @@ from game.theater.controlpoint import (
|
||||
OffMapSpawn,
|
||||
Fob,
|
||||
ParkingType,
|
||||
Player,
|
||||
)
|
||||
from game.utils import Heading
|
||||
|
||||
@ -30,8 +31,8 @@ def test_mission_types_friendly(mocker: Any) -> None:
|
||||
mocker.patch("game.theater.controlpoint.Airfield.is_friendly", return_value=True)
|
||||
airport = Airport(None, None) # type: ignore
|
||||
airport.name = "test" # required for Airfield.__init__
|
||||
airfield = Airfield(airport, theater=None, starts_blue=True) # type: ignore
|
||||
mission_types = list(airfield.mission_types(for_player=True))
|
||||
airfield = Airfield(airport, theater=None, starts_blue=Player.BLUE) # type: ignore
|
||||
mission_types = list(airfield.mission_types(for_player=Player.BLUE))
|
||||
assert len(mission_types) == 3
|
||||
assert FlightType.AEWC in mission_types
|
||||
assert FlightType.REFUELING in mission_types
|
||||
@ -39,8 +40,8 @@ def test_mission_types_friendly(mocker: Any) -> None:
|
||||
|
||||
# Carrier
|
||||
mocker.patch("game.theater.controlpoint.Carrier.is_friendly", return_value=True)
|
||||
carrier = Carrier(name="test", at=None, theater=None, starts_blue=True) # type: ignore
|
||||
mission_types = list(carrier.mission_types(for_player=True))
|
||||
carrier = Carrier(name="test", at=None, theater=None, starts_blue=Player.BLUE) # type: ignore
|
||||
mission_types = list(carrier.mission_types(for_player=Player.BLUE))
|
||||
assert len(mission_types) == 3
|
||||
assert FlightType.AEWC in mission_types
|
||||
assert FlightType.REFUELING in mission_types
|
||||
@ -48,8 +49,8 @@ def test_mission_types_friendly(mocker: Any) -> None:
|
||||
|
||||
# LHA
|
||||
mocker.patch("game.theater.controlpoint.Lha.is_friendly", return_value=True)
|
||||
lha = Lha(name="test", at=None, theater=None, starts_blue=True) # type: ignore
|
||||
mission_types = list(lha.mission_types(for_player=True))
|
||||
lha = Lha(name="test", at=None, theater=None, starts_blue=Player.BLUE) # type: ignore
|
||||
mission_types = list(lha.mission_types(for_player=Player.BLUE))
|
||||
assert len(mission_types) == 3
|
||||
assert FlightType.AEWC in mission_types
|
||||
assert FlightType.REFUELING in mission_types
|
||||
@ -57,16 +58,16 @@ def test_mission_types_friendly(mocker: Any) -> None:
|
||||
|
||||
# Fob
|
||||
mocker.patch("game.theater.controlpoint.Fob.is_friendly", return_value=True)
|
||||
fob = Fob(name="test", at=None, theater=None, starts_blue=True) # type: ignore
|
||||
mission_types = list(fob.mission_types(for_player=True))
|
||||
fob = Fob(name="test", at=None, theater=None, starts_blue=Player.BLUE) # type: ignore
|
||||
mission_types = list(fob.mission_types(for_player=Player.BLUE))
|
||||
assert len(mission_types) == 2
|
||||
assert FlightType.AEWC in mission_types
|
||||
assert FlightType.BARCAP in mission_types
|
||||
|
||||
# Off map spawn
|
||||
mocker.patch("game.theater.controlpoint.OffMapSpawn.is_friendly", return_value=True)
|
||||
off_map_spawn = OffMapSpawn(name="test", position=None, theater=None, starts_blue=True) # type: ignore
|
||||
mission_types = list(off_map_spawn.mission_types(for_player=True))
|
||||
off_map_spawn = OffMapSpawn(name="test", position=None, theater=None, starts_blue=Player.BLUE) # type: ignore
|
||||
mission_types = list(off_map_spawn.mission_types(for_player=Player.BLUE))
|
||||
assert len(mission_types) == 0
|
||||
|
||||
|
||||
@ -79,8 +80,8 @@ def test_mission_types_enemy(mocker: Any) -> None:
|
||||
mocker.patch("game.theater.controlpoint.Airfield.is_friendly", return_value=False)
|
||||
airport = Airport(None, None) # type: ignore
|
||||
airport.name = "test" # required for Airfield.__init__
|
||||
airfield = Airfield(airport, theater=None, starts_blue=True) # type: ignore
|
||||
mission_types = list(airfield.mission_types(for_player=True))
|
||||
airfield = Airfield(airport, theater=None, starts_blue=Player.BLUE) # type: ignore
|
||||
mission_types = list(airfield.mission_types(for_player=Player.BLUE))
|
||||
assert len(mission_types) == 8
|
||||
assert FlightType.OCA_AIRCRAFT in mission_types
|
||||
assert FlightType.OCA_RUNWAY in mission_types
|
||||
@ -93,8 +94,8 @@ def test_mission_types_enemy(mocker: Any) -> None:
|
||||
|
||||
# Carrier
|
||||
mocker.patch("game.theater.controlpoint.Carrier.is_friendly", return_value=False)
|
||||
carrier = Carrier(name="test", at=None, theater=None, starts_blue=True) # type: ignore
|
||||
mission_types = list(carrier.mission_types(for_player=True))
|
||||
carrier = Carrier(name="test", at=None, theater=None, starts_blue=Player.BLUE) # type: ignore
|
||||
mission_types = list(carrier.mission_types(for_player=Player.BLUE))
|
||||
assert len(mission_types) == 5
|
||||
assert FlightType.ANTISHIP in mission_types
|
||||
assert FlightType.ESCORT in mission_types
|
||||
@ -104,8 +105,8 @@ def test_mission_types_enemy(mocker: Any) -> None:
|
||||
|
||||
# LHA
|
||||
mocker.patch("game.theater.controlpoint.Lha.is_friendly", return_value=False)
|
||||
lha = Lha(name="test", at=None, theater=None, starts_blue=True) # type: ignore
|
||||
mission_types = list(lha.mission_types(for_player=True))
|
||||
lha = Lha(name="test", at=None, theater=None, starts_blue=Player.BLUE) # type: ignore
|
||||
mission_types = list(lha.mission_types(for_player=Player.BLUE))
|
||||
assert len(mission_types) == 5
|
||||
assert FlightType.ANTISHIP in mission_types
|
||||
assert FlightType.ESCORT in mission_types
|
||||
@ -115,8 +116,8 @@ def test_mission_types_enemy(mocker: Any) -> None:
|
||||
|
||||
# Fob
|
||||
mocker.patch("game.theater.controlpoint.Fob.is_friendly", return_value=False)
|
||||
fob = Fob(name="test", at=None, theater=None, starts_blue=True) # type: ignore
|
||||
mission_types = list(fob.mission_types(for_player=True))
|
||||
fob = Fob(name="test", at=None, theater=None, starts_blue=Player.BLUE) # type: ignore
|
||||
mission_types = list(fob.mission_types(for_player=Player.BLUE))
|
||||
assert len(mission_types) == 6
|
||||
assert FlightType.AIR_ASSAULT in mission_types
|
||||
assert FlightType.ESCORT in mission_types
|
||||
@ -129,8 +130,8 @@ def test_mission_types_enemy(mocker: Any) -> None:
|
||||
mocker.patch(
|
||||
"game.theater.controlpoint.OffMapSpawn.is_friendly", return_value=False
|
||||
)
|
||||
off_map_spawn = OffMapSpawn(name="test", position=None, theater=None, starts_blue=True) # type: ignore
|
||||
mission_types = list(off_map_spawn.mission_types(for_player=True))
|
||||
off_map_spawn = OffMapSpawn(name="test", position=None, theater=None, starts_blue=Player.BLUE) # type: ignore
|
||||
mission_types = list(off_map_spawn.mission_types(for_player=Player.BLUE))
|
||||
assert len(mission_types) == 0
|
||||
|
||||
|
||||
@ -144,7 +145,7 @@ def test_control_point_parking(mocker: Any) -> None:
|
||||
airport = Airport(None, None) # type: ignore
|
||||
airport.name = "test" # required for Airfield.__init__
|
||||
point = Point(0, 0, None) # type: ignore
|
||||
control_point = Airfield(airport, theater=None, starts_blue=True) # type: ignore
|
||||
control_point = Airfield(airport, theater=None, starts_blue=Player.BLUE) # type: ignore
|
||||
parking_type_ground_start = ParkingType(
|
||||
fixed_wing=False, fixed_wing_stol=True, rotary_wing=False
|
||||
)
|
||||
|
||||
@ -4,7 +4,7 @@ import pytest
|
||||
from dcs.mapping import Point
|
||||
|
||||
from game.ato.flighttype import FlightType
|
||||
from game.theater.controlpoint import OffMapSpawn
|
||||
from game.theater.controlpoint import OffMapSpawn, Player
|
||||
from game.theater.presetlocation import PresetLocation
|
||||
from game.theater.theatergroundobject import (
|
||||
BuildingGroundObject,
|
||||
@ -34,7 +34,7 @@ def test_mission_types_friendly(mocker: Any) -> None:
|
||||
name="dummy_control_point",
|
||||
position=Point(0, 0, None), # type: ignore
|
||||
theater=None, # type: ignore
|
||||
starts_blue=True,
|
||||
starts_blue=Player.BLUE,
|
||||
)
|
||||
|
||||
# Patch is_friendly as it's difficult to set up a proper ControlPoint
|
||||
@ -56,7 +56,7 @@ def test_mission_types_friendly(mocker: Any) -> None:
|
||||
control_point=dummy_control_point,
|
||||
task=None,
|
||||
)
|
||||
mission_types = list(ground_object.mission_types(for_player=True))
|
||||
mission_types = list(ground_object.mission_types(for_player=Player.BLUE))
|
||||
assert mission_types == [FlightType.BARCAP]
|
||||
|
||||
for ground_object_type in [BuildingGroundObject, IadsBuildingGroundObject]:
|
||||
@ -67,7 +67,7 @@ def test_mission_types_friendly(mocker: Any) -> None:
|
||||
control_point=dummy_control_point,
|
||||
task=None,
|
||||
)
|
||||
mission_types = list(ground_object.mission_types(for_player=True))
|
||||
mission_types = list(ground_object.mission_types(for_player=Player.BLUE))
|
||||
assert mission_types == [FlightType.BARCAP]
|
||||
|
||||
|
||||
@ -84,7 +84,7 @@ def test_mission_types_enemy(mocker: Any) -> None:
|
||||
name="dummy_control_point",
|
||||
position=Point(0, 0, None), # type: ignore
|
||||
theater=None, # type: ignore
|
||||
starts_blue=True,
|
||||
starts_blue=Player.BLUE,
|
||||
)
|
||||
|
||||
# Patch is_friendly as it's difficult to set up a proper ControlPoint
|
||||
@ -99,7 +99,7 @@ def test_mission_types_enemy(mocker: Any) -> None:
|
||||
control_point=dummy_control_point,
|
||||
task=None,
|
||||
)
|
||||
mission_types = list(building.mission_types(for_player=False))
|
||||
mission_types = list(building.mission_types(for_player=Player.RED))
|
||||
assert len(mission_types) == 6
|
||||
assert FlightType.STRIKE in mission_types
|
||||
assert FlightType.REFUELING in mission_types
|
||||
@ -115,7 +115,7 @@ def test_mission_types_enemy(mocker: Any) -> None:
|
||||
control_point=dummy_control_point,
|
||||
task=None,
|
||||
)
|
||||
mission_types = list(iads_building.mission_types(for_player=False))
|
||||
mission_types = list(iads_building.mission_types(for_player=Player.RED))
|
||||
assert len(mission_types) == 7
|
||||
assert FlightType.STRIKE in mission_types
|
||||
assert FlightType.REFUELING in mission_types
|
||||
@ -136,7 +136,7 @@ def test_mission_types_enemy(mocker: Any) -> None:
|
||||
control_point=dummy_control_point,
|
||||
task=None,
|
||||
)
|
||||
mission_types = list(ground_object.mission_types(for_player=False))
|
||||
mission_types = list(ground_object.mission_types(for_player=Player.RED))
|
||||
assert len(mission_types) == 7
|
||||
assert FlightType.ANTISHIP in mission_types
|
||||
assert FlightType.STRIKE in mission_types
|
||||
@ -152,7 +152,7 @@ def test_mission_types_enemy(mocker: Any) -> None:
|
||||
control_point=dummy_control_point,
|
||||
task=None,
|
||||
)
|
||||
mission_types = list(sam.mission_types(for_player=False))
|
||||
mission_types = list(sam.mission_types(for_player=Player.RED))
|
||||
assert len(mission_types) == 8
|
||||
assert FlightType.DEAD in mission_types
|
||||
assert FlightType.SEAD in mission_types
|
||||
@ -168,7 +168,7 @@ def test_mission_types_enemy(mocker: Any) -> None:
|
||||
location=dummy_location,
|
||||
control_point=dummy_control_point,
|
||||
)
|
||||
mission_types = list(ewr.mission_types(for_player=False))
|
||||
mission_types = list(ewr.mission_types(for_player=Player.RED))
|
||||
assert len(mission_types) == 7
|
||||
assert FlightType.DEAD in mission_types
|
||||
assert FlightType.STRIKE in mission_types
|
||||
@ -187,7 +187,7 @@ def test_mission_types_enemy(mocker: Any) -> None:
|
||||
location=dummy_location,
|
||||
control_point=dummy_control_point,
|
||||
)
|
||||
mission_types = list(ground_object.mission_types(for_player=False))
|
||||
mission_types = list(ground_object.mission_types(for_player=Player.RED))
|
||||
assert len(mission_types) == 7
|
||||
assert FlightType.BAI in mission_types
|
||||
assert FlightType.STRIKE in mission_types
|
||||
@ -203,7 +203,7 @@ def test_mission_types_enemy(mocker: Any) -> None:
|
||||
control_point=dummy_control_point,
|
||||
task=None,
|
||||
)
|
||||
mission_types = list(vehicles.mission_types(for_player=False))
|
||||
mission_types = list(vehicles.mission_types(for_player=Player.RED))
|
||||
assert len(mission_types) == 7
|
||||
assert FlightType.BAI in mission_types
|
||||
assert FlightType.STRIKE in mission_types
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user