From f63a107c1165728255910c1beb8e27e8f1571bf4 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Mon, 7 Mar 2022 23:23:52 -0800 Subject: [PATCH] Account for time elapsed when leaving combat. --- game/ato/flightstate/flightstate.py | 1 + game/ato/flightstate/incombat.py | 11 +++++++++-- game/sim/aircraftsimulation.py | 2 +- game/sim/combat/aircombat.py | 12 +++++++++--- game/sim/combat/atip.py | 15 +++++++++++++-- game/sim/combat/combatinitiator.py | 2 +- game/sim/combat/defendingsam.py | 12 +++++++++--- game/sim/combat/frozencombat.py | 18 ++++++++++++++---- 8 files changed, 57 insertions(+), 16 deletions(-) diff --git a/game/ato/flightstate/flightstate.py b/game/ato/flightstate/flightstate.py index 47013d5d..f0546a03 100644 --- a/game/ato/flightstate/flightstate.py +++ b/game/ato/flightstate/flightstate.py @@ -19,6 +19,7 @@ class FlightState(ABC): def __init__(self, flight: Flight, settings: Settings) -> None: self.flight = flight self.settings = settings + self.avoid_further_combat = False @property def alive(self) -> bool: diff --git a/game/ato/flightstate/incombat.py b/game/ato/flightstate/incombat.py index ccbf6347..ef394331 100644 --- a/game/ato/flightstate/incombat.py +++ b/game/ato/flightstate/incombat.py @@ -24,9 +24,16 @@ class InCombat(InFlight): self.previous_state = previous_state self.combat = combat - def exit_combat(self) -> None: - # TODO: Account for time passed while frozen. + def exit_combat( + self, + events: GameUpdateEvents, + time: datetime, + elapsed_time: timedelta, + avoid_further_combat: bool = False, + ) -> None: self.flight.set_state(self.previous_state) + self.previous_state.avoid_further_combat = avoid_further_combat + self.previous_state.on_game_tick(events, time, elapsed_time) @property def in_combat(self) -> bool: diff --git a/game/sim/aircraftsimulation.py b/game/sim/aircraftsimulation.py index ee1ced2d..e83e9da2 100644 --- a/game/sim/aircraftsimulation.py +++ b/game/sim/aircraftsimulation.py @@ -49,7 +49,7 @@ class AircraftSimulation: still_active = [] for combat in self.combats: - if combat.on_game_tick(duration, self.results, events): + if combat.on_game_tick(time, duration, self.results, events): events.end_combat(combat) else: still_active.append(combat) diff --git a/game/sim/combat/aircombat.py b/game/sim/combat/aircombat.py index abdb8e48..0fc6e948 100644 --- a/game/sim/combat/aircombat.py +++ b/game/sim/combat/aircombat.py @@ -2,7 +2,7 @@ from __future__ import annotations import logging import random -from datetime import timedelta +from datetime import datetime, timedelta from typing import TYPE_CHECKING from shapely.ops import unary_union @@ -61,7 +61,13 @@ class AirCombat(JoinableCombat): def describe(self) -> str: return f"in air-to-air combat" - def resolve(self, results: SimulationResults, events: GameUpdateEvents) -> None: + def resolve( + self, + results: SimulationResults, + events: GameUpdateEvents, + time: datetime, + elapsed_time: timedelta, + ) -> None: blue = [] red = [] for flight in self.flights: @@ -95,4 +101,4 @@ class AirCombat(JoinableCombat): if random.random() >= 0.5: flight.kill(results, events) else: - flight.state.exit_combat() + flight.state.exit_combat(events, time, elapsed_time) diff --git a/game/sim/combat/atip.py b/game/sim/combat/atip.py index 17b6bd67..61ed55b5 100644 --- a/game/sim/combat/atip.py +++ b/game/sim/combat/atip.py @@ -2,11 +2,12 @@ from __future__ import annotations import logging from collections.abc import Iterator -from datetime import timedelta +from datetime import datetime, timedelta from typing import TYPE_CHECKING from .frozencombat import FrozenCombat from .. import GameUpdateEvents +from ...ato.flightstate import InCombat if TYPE_CHECKING: from game.ato import Flight @@ -27,8 +28,18 @@ class AtIp(FrozenCombat): def iter_flights(self) -> Iterator[Flight]: yield self.flight - def resolve(self, results: SimulationResults, events: GameUpdateEvents) -> None: + def resolve( + self, + results: SimulationResults, + events: GameUpdateEvents, + time: datetime, + elapsed_time: timedelta, + ) -> None: logging.debug( f"{self.flight} attack on {self.flight.package.target} auto-resolved with " "mission failure but no losses" ) + assert isinstance(self.flight.state, InCombat) + self.flight.state.exit_combat( + events, time, elapsed_time, avoid_further_combat=True + ) diff --git a/game/sim/combat/combatinitiator.py b/game/sim/combat/combatinitiator.py index 17c5d2d3..c5d064d9 100644 --- a/game/sim/combat/combatinitiator.py +++ b/game/sim/combat/combatinitiator.py @@ -95,7 +95,7 @@ class CombatInitiator: if not flight.state.in_flight: return None - if flight.state.is_at_ip: + if flight.state.is_at_ip and not flight.state.avoid_further_combat: return AtIp(timedelta(minutes=1), flight) position = flight.state.estimate_position() diff --git a/game/sim/combat/defendingsam.py b/game/sim/combat/defendingsam.py index 9356e376..2dfe98fb 100644 --- a/game/sim/combat/defendingsam.py +++ b/game/sim/combat/defendingsam.py @@ -3,7 +3,7 @@ from __future__ import annotations import logging import random from collections.abc import Iterator -from datetime import timedelta +from datetime import datetime, timedelta from typing import TYPE_CHECKING from game.ato.flightstate import InCombat @@ -37,7 +37,13 @@ class DefendingSam(FrozenCombat): def iter_flights(self) -> Iterator[Flight]: yield self.flight - def resolve(self, results: SimulationResults, events: GameUpdateEvents) -> None: + def resolve( + self, + results: SimulationResults, + events: GameUpdateEvents, + time: datetime, + elapsed_time: timedelta, + ) -> None: assert isinstance(self.flight.state, InCombat) if random.random() >= 0.5: logging.debug(f"Air defense combat auto-resolved with {self.flight} lost") @@ -46,4 +52,4 @@ class DefendingSam(FrozenCombat): logging.debug( f"Air defense combat auto-resolved with {self.flight} surviving" ) - self.flight.state.exit_combat() + self.flight.state.exit_combat(events, time, elapsed_time) diff --git a/game/sim/combat/frozencombat.py b/game/sim/combat/frozencombat.py index 51502dcb..332c107f 100644 --- a/game/sim/combat/frozencombat.py +++ b/game/sim/combat/frozencombat.py @@ -3,7 +3,7 @@ from __future__ import annotations import uuid from abc import ABC, abstractmethod from collections.abc import Iterator -from datetime import timedelta +from datetime import datetime, timedelta from typing import TYPE_CHECKING from game.ato.flightstate import InCombat, InFlight @@ -21,16 +21,26 @@ class FrozenCombat(ABC): self.elapsed_time = timedelta() def on_game_tick( - self, duration: timedelta, results: SimulationResults, events: GameUpdateEvents + self, + time: datetime, + duration: timedelta, + results: SimulationResults, + events: GameUpdateEvents, ) -> bool: self.elapsed_time += duration if self.elapsed_time >= self.freeze_duration: - self.resolve(results, events) + self.resolve(results, events, time, self.elapsed_time) return True return False @abstractmethod - def resolve(self, results: SimulationResults, events: GameUpdateEvents) -> None: + def resolve( + self, + results: SimulationResults, + events: GameUpdateEvents, + time: datetime, + elapsed_time: timedelta, + ) -> None: ... @abstractmethod