mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
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
105 lines
3.0 KiB
Python
105 lines
3.0 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
import random
|
|
from datetime import datetime, timedelta
|
|
from typing import TYPE_CHECKING
|
|
|
|
from shapely.ops import unary_union
|
|
|
|
from game.ato.flightstate import InCombat, InFlight
|
|
from game.utils import dcs_to_shapely_point
|
|
from .joinablecombat import JoinableCombat
|
|
from .. import GameUpdateEvents
|
|
|
|
if TYPE_CHECKING:
|
|
from game.ato import Flight
|
|
from ..simulationresults import SimulationResults
|
|
|
|
|
|
class AirCombat(JoinableCombat):
|
|
def __init__(self, freeze_duration: timedelta, flights: list[Flight]) -> None:
|
|
super().__init__(freeze_duration, flights)
|
|
footprints = []
|
|
for flight in self.flights:
|
|
if (region := flight.state.a2a_commit_region()) is not None:
|
|
footprints.append(region)
|
|
self.footprint = unary_union(footprints)
|
|
|
|
def joinable_by(self, flight: Flight) -> bool:
|
|
if not flight.state.will_join_air_combat:
|
|
return False
|
|
|
|
if not isinstance(flight.state, InFlight):
|
|
raise NotImplementedError(
|
|
f"Only InFlight flights are expected to join air combat. {flight} is "
|
|
"not InFlight"
|
|
)
|
|
|
|
if self.footprint.intersects(
|
|
dcs_to_shapely_point(flight.state.estimate_position())
|
|
):
|
|
return True
|
|
return False
|
|
|
|
def __str__(self) -> str:
|
|
blue_flights = []
|
|
red_flights = []
|
|
for flight in self.flights:
|
|
if flight.squadron.player.is_blue:
|
|
blue_flights.append(str(flight))
|
|
else:
|
|
red_flights.append(str(flight))
|
|
|
|
blue = ", ".join(blue_flights)
|
|
red = ", ".join(red_flights)
|
|
return f"air combat {blue} vs {red}"
|
|
|
|
def because(self) -> str:
|
|
return f"of {self}"
|
|
|
|
def describe(self) -> str:
|
|
return f"in air-to-air combat"
|
|
|
|
def resolve(
|
|
self,
|
|
results: SimulationResults,
|
|
events: GameUpdateEvents,
|
|
time: datetime,
|
|
elapsed_time: timedelta,
|
|
) -> None:
|
|
blue = []
|
|
red = []
|
|
for flight in self.flights:
|
|
if flight.squadron.player.is_blue:
|
|
blue.append(flight)
|
|
else:
|
|
red.append(flight)
|
|
if len(blue) > len(red):
|
|
winner = blue
|
|
loser = red
|
|
elif len(blue) < len(red):
|
|
winner = red
|
|
loser = blue
|
|
elif random.random() >= 0.5:
|
|
winner = blue
|
|
loser = red
|
|
else:
|
|
winner = red
|
|
loser = blue
|
|
|
|
if winner == blue:
|
|
logging.debug(f"{self} auto-resolved as blue victory")
|
|
else:
|
|
logging.debug(f"{self} auto-resolved as red victory")
|
|
|
|
for flight in loser:
|
|
flight.kill(results, events)
|
|
|
|
for flight in winner:
|
|
assert isinstance(flight.state, InCombat)
|
|
if random.random() >= 0.5:
|
|
flight.kill(results, events)
|
|
else:
|
|
flight.state.exit_combat(events, time, elapsed_time)
|