Move frozen combat handling out of MapModel.

This commit is contained in:
Dan Albert
2022-02-19 13:50:59 -08:00
parent 2168143fea
commit 09457d8aab
17 changed files with 140 additions and 136 deletions

View File

@@ -4,6 +4,7 @@ import uuid
from datetime import datetime, timedelta
from typing import Any, List, Optional, TYPE_CHECKING
from dcs import Point
from dcs.planes import C_101CC, C_101EB, Su_33
from gen.flights.loadouts import Loadout
@@ -125,6 +126,9 @@ class Flight:
def points(self) -> List[FlightWaypoint]:
return self.flight_plan.waypoints[1:]
def position(self) -> Point:
return self.state.estimate_position()
def resize(self, new_size: int) -> None:
self.squadron.claim_inventory(new_size - self.count)
self.roster.resize(new_size)

View File

@@ -0,0 +1,10 @@
from abc import ABC
from dcs import Point
from game.ato.flightstate import FlightState
class AtDeparture(FlightState, ABC):
def estimate_position(self) -> Point:
return self.flight.departure.position

View File

@@ -3,6 +3,8 @@ from __future__ import annotations
from datetime import datetime, timedelta
from typing import TYPE_CHECKING
from dcs import Point
from .flightstate import FlightState
from ..starttype import StartType
@@ -20,6 +22,9 @@ class Completed(FlightState):
def is_waiting_for_start(self) -> bool:
return False
def estimate_position(self) -> Point:
return self.flight.arrival.position
@property
def spawn_type(self) -> StartType:
# TODO: May want to do something different to make these uncontrolled?

View File

@@ -4,6 +4,8 @@ from abc import ABC, abstractmethod
from datetime import datetime, timedelta
from typing import Optional, TYPE_CHECKING
from dcs import Point
from game.ato.starttype import StartType
if TYPE_CHECKING:
@@ -44,6 +46,10 @@ class FlightState(ABC):
def is_waiting_for_start(self) -> bool:
...
@abstractmethod
def estimate_position(self) -> Point:
...
@property
@abstractmethod
def spawn_type(self) -> StartType:

View File

@@ -4,7 +4,7 @@ import logging
from datetime import datetime, timedelta
from typing import TYPE_CHECKING
from .flightstate import FlightState
from .atdeparture import AtDeparture
from .taxi import Taxi
from ..starttype import StartType
@@ -14,7 +14,7 @@ if TYPE_CHECKING:
from game.sim.gameupdateevents import GameUpdateEvents
class StartUp(FlightState):
class StartUp(AtDeparture):
def __init__(self, flight: Flight, settings: Settings, now: datetime) -> None:
super().__init__(flight, settings)
self.completion_time = now + flight.flight_plan.estimate_startup()

View File

@@ -4,7 +4,7 @@ import logging
from datetime import datetime, timedelta
from typing import TYPE_CHECKING
from .flightstate import FlightState
from .atdeparture import AtDeparture
from .navigating import Navigating
from ..starttype import StartType
from ...utils import LBS_TO_KG
@@ -15,7 +15,7 @@ if TYPE_CHECKING:
from game.sim.gameupdateevents import GameUpdateEvents
class Takeoff(FlightState):
class Takeoff(AtDeparture):
def __init__(self, flight: Flight, settings: Settings, now: datetime) -> None:
super().__init__(flight, settings)
# TODO: Not accounted for in FlightPlan, can cause discrepancy without loiter.

View File

@@ -4,7 +4,7 @@ import logging
from datetime import datetime, timedelta
from typing import TYPE_CHECKING
from .flightstate import FlightState
from .atdeparture import AtDeparture
from .takeoff import Takeoff
from ..starttype import StartType
@@ -14,7 +14,7 @@ if TYPE_CHECKING:
from game.sim.gameupdateevents import GameUpdateEvents
class Taxi(FlightState):
class Taxi(AtDeparture):
def __init__(self, flight: Flight, settings: Settings, now: datetime) -> None:
super().__init__(flight, settings)
self.completion_time = now + flight.flight_plan.estimate_ground_ops()

View File

@@ -3,6 +3,8 @@ from __future__ import annotations
from datetime import datetime, timedelta
from typing import TYPE_CHECKING
from dcs import Point
from gen.flights.traveltime import TotEstimator
from .flightstate import FlightState
from ..starttype import StartType
@@ -21,6 +23,9 @@ class Uninitialized(FlightState):
def is_waiting_for_start(self) -> bool:
raise RuntimeError("Attempted to simulate flight that is not fully initialized")
def estimate_position(self) -> Point:
raise RuntimeError("Attempted to simulate flight that is not fully initialized")
@property
def spawn_type(self) -> StartType:
raise RuntimeError("Attempted to simulate flight that is not fully initialized")

View File

@@ -4,6 +4,7 @@ from datetime import datetime, timedelta
from typing import TYPE_CHECKING
from game.ato.starttype import StartType
from .atdeparture import AtDeparture
from .flightstate import FlightState
from .navigating import Navigating
from .startup import StartUp
@@ -16,7 +17,7 @@ if TYPE_CHECKING:
from game.sim.gameupdateevents import GameUpdateEvents
class WaitingForStart(FlightState):
class WaitingForStart(AtDeparture):
def __init__(
self,
flight: Flight,

View File

View File

@@ -0,0 +1,49 @@
from __future__ import annotations
from uuid import UUID
from pydantic import BaseModel
from game.server.leaflet import LeafletLatLon, LeafletPoly, ShapelyUtil
from game.sim.combat import FrozenCombat
from game.sim.combat.aircombat import AirCombat
from game.sim.combat.atip import AtIp
from game.sim.combat.defendingsam import DefendingSam
from game.theater import ConflictTheater
class FrozenCombatJs(BaseModel):
id: UUID
flight_position: LeafletLatLon | None
target_positions: list[LeafletLatLon] | None
footprint: list[LeafletPoly] | None
@staticmethod
def for_combat(combat: FrozenCombat, theater: ConflictTheater) -> FrozenCombatJs:
if isinstance(combat, AirCombat):
return FrozenCombatJs(
id=combat.id,
flight_position=None,
target_positions=None,
footprint=ShapelyUtil.polys_to_leaflet(combat.footprint, theater),
)
if isinstance(combat, AtIp):
return FrozenCombatJs(
id=combat.id,
flight_position=theater.point_to_ll(combat.flight.position()).as_list(),
target_positions=[
theater.point_to_ll(combat.flight.package.target.position).as_list()
],
footprint=None,
)
if isinstance(combat, DefendingSam):
return FrozenCombatJs(
id=combat.id,
flight_position=theater.point_to_ll(combat.flight.position()).as_list(),
target_positions=[
theater.point_to_ll(sam.position).as_list()
for sam in combat.air_defenses
],
footprint=None,
)
raise NotImplementedError(f"Unhandled FrozenCombat type: {combat.__class__}")

View File

@@ -5,6 +5,7 @@ from uuid import UUID
from pydantic import BaseModel
from game.server.combat.models import FrozenCombatJs
from game.server.leaflet import LeafletLatLon
if TYPE_CHECKING:
@@ -14,6 +15,8 @@ if TYPE_CHECKING:
class GameUpdateEventsJs(BaseModel):
updated_flights: dict[UUID, LeafletLatLon]
new_combats: list[FrozenCombatJs] = []
updated_combats: list[FrozenCombatJs] = []
@classmethod
def from_events(cls, events: GameUpdateEvents, game: Game) -> GameUpdateEventsJs:
@@ -21,5 +24,12 @@ class GameUpdateEventsJs(BaseModel):
updated_flights={
f[0].id: game.theater.point_to_ll(f[1]).as_list()
for f in events.updated_flights
}
},
new_combats=[
FrozenCombatJs.for_combat(c, game.theater) for c in events.new_combats
],
updated_combats=[
FrozenCombatJs.for_combat(c, game.theater)
for c in events.updated_combats
],
)

View File

@@ -1,5 +1,6 @@
from __future__ import annotations
import uuid
from abc import ABC, abstractmethod
from collections.abc import Iterator
from typing import TYPE_CHECKING
@@ -11,6 +12,9 @@ if TYPE_CHECKING:
class FrozenCombat(ABC):
def __init__(self) -> None:
self.id = uuid.uuid4()
@abstractmethod
def because(self) -> str:
...