mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
This PR simplifies fast forward settings and introduces the ability to skip combat instead of resolving.
114 lines
3.4 KiB
Python
114 lines
3.4 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.settings.settings import CombatResolutionMethod
|
|
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:
|
|
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,
|
|
resolution_method: CombatResolutionMethod,
|
|
) -> None:
|
|
|
|
if resolution_method is CombatResolutionMethod.SKIP:
|
|
for flight in self.flights:
|
|
assert isinstance(flight.state, InCombat)
|
|
flight.state.exit_combat(events, time, elapsed_time)
|
|
return
|
|
|
|
blue = []
|
|
red = []
|
|
for flight in self.flights:
|
|
if flight.squadron.player:
|
|
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)
|