Set up bullseye early, create waypoints.

Setting this up as part of the game makes it possible for us to show in
the UI.

https://github.com/dcs-liberation/dcs_liberation/issues/136
This commit is contained in:
Dan Albert 2021-05-20 18:18:13 -07:00
parent 3c8c76f50d
commit a382e74a89
12 changed files with 104 additions and 22 deletions

View File

@ -11,6 +11,7 @@ Saves from 2.5 are not compatible with 3.0.
* **[Campaign AI]** AI now considers Ju-88s for CAS, strike, and DEAD missions.
* **[Campaign AI]** Fix purchase of aircraft by priority (the faction's list was being used as the priority list rather than the game's).
* **[Flight Planner]** AI strike flight plans now include the correct target actions for building groups.
* **[Flight Planner]** Flight plans now include bullseye waypoints.
* **[Kneeboard]** ATC table overflow alleviated by wrapping long airfield names and splitting ATC frequency and channel into separate rows.
* **[UI]** Added new web based map UI. This is mostly functional but many of the old display options are a WIP. Revert to the old map with --old-map.
* **[UI]** Campaigns generated for an older or newer version of the game will now be marked as incompatible. They can still be played, but bugs may be present.

View File

@ -34,6 +34,7 @@ from .procurement import AircraftProcurementRequest, ProcurementAi
from .profiling import logged_duration
from .settings import Settings
from .theater import ConflictTheater
from .theater.bullseye import Bullseye
from .theater.transitnetwork import TransitNetwork, TransitNetworkBuilder
from .threatzones import ThreatZones
from .transfers import PendingTransfers
@ -130,6 +131,9 @@ class Game:
self.blue_ato = AirTaskingOrder()
self.red_ato = AirTaskingOrder()
self.blue_bullseye = Bullseye(Point(0, 0))
self.red_bullseye = Bullseye(Point(0, 0))
self.aircraft_inventory = GlobalAircraftInventory(self.theater.controlpoints)
self.transfers = PendingTransfers(self)
@ -201,6 +205,11 @@ class Game:
return self.player_faction
return self.enemy_faction
def bullseye_for(self, player: bool) -> Bullseye:
if player:
return self.blue_bullseye
return self.red_bullseye
def _roll(self, prob, mult):
if self.settings.version == "dev":
# always generate all events for dev
@ -337,10 +346,17 @@ class Game:
return TurnState.CONTINUE
def set_bullseye(self) -> None:
player_cp, enemy_cp = self.theater.closest_opposing_control_points()
self.blue_bullseye = Bullseye(enemy_cp.position)
self.red_bullseye = Bullseye(player_cp.position)
def initialize_turn(self) -> None:
self.events = []
self._generate_events()
self.set_bullseye()
# Update statistics
self.game_stats.update(self)

View File

@ -108,8 +108,12 @@ class Operation:
@classmethod
def _setup_mission_coalitions(cls):
cls.current_mission.coalition["blue"] = Coalition("blue")
cls.current_mission.coalition["red"] = Coalition("red")
cls.current_mission.coalition["blue"] = Coalition(
"blue", bullseye=cls.game.blue_bullseye.to_pydcs()
)
cls.current_mission.coalition["red"] = Coalition(
"red", bullseye=cls.game.red_bullseye.to_pydcs()
)
p_country = cls.game.player_country
e_country = cls.game.enemy_country

26
game/theater/bullseye.py Normal file
View File

@ -0,0 +1,26 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Dict, TYPE_CHECKING
from dcs import Point
from game.theater import LatLon
if TYPE_CHECKING:
from game.theater import ConflictTheater
@dataclass
class Bullseye:
position: Point
@classmethod
def from_pydcs(cls, bulls: Dict[str, float]) -> Bullseye:
return cls(Point(bulls["x"], bulls["y"]))
def to_pydcs(self) -> Dict[str, float]:
return {"x": self.position.x, "y": self.position.y}
def to_lat_lon(self, theater: ConflictTheater) -> LatLon:
return theater.point_to_ll(self.position)

View File

@ -41,6 +41,7 @@ from dcs.unitgroup import (
)
from dcs.vehicles import AirDefence, Armor, MissilesSS, Unarmed
from .latlon import LatLon
from ..scenery_group import SceneryGroup
from pyproj import CRS, Transformer
from shapely import geometry, ops
@ -582,15 +583,6 @@ class ReferencePoint:
image_coordinates: Point
@dataclass(frozen=True)
class LatLon:
latitude: float
longitude: float
def as_list(self) -> List[float]:
return [self.latitude, self.longitude]
class ConflictTheater:
terrain: Terrain

11
game/theater/latlon.py Normal file
View File

@ -0,0 +1,11 @@
from dataclasses import dataclass
from typing import List
@dataclass(frozen=True)
class LatLon:
latitude: float
longitude: float
def as_list(self) -> List[float]:
return [self.latitude, self.longitude]

View File

@ -79,6 +79,7 @@ class FlightWaypointType(Enum):
INGRESS_OCA_AIRCRAFT = 25
PICKUP = 26
DROP_OFF = 27
BULLSEYE = 28
class FlightWaypoint:

View File

@ -426,6 +426,7 @@ class BarCapFlightPlan(PatrollingFlightPlan):
takeoff: FlightWaypoint
land: FlightWaypoint
divert: Optional[FlightWaypoint]
bullseye: FlightWaypoint
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
yield self.takeoff
@ -438,6 +439,7 @@ class BarCapFlightPlan(PatrollingFlightPlan):
yield self.land
if self.divert is not None:
yield self.divert
yield self.bullseye
@dataclass(frozen=True)
@ -446,6 +448,7 @@ class CasFlightPlan(PatrollingFlightPlan):
target: FlightWaypoint
land: FlightWaypoint
divert: Optional[FlightWaypoint]
bullseye: FlightWaypoint
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
yield self.takeoff
@ -459,6 +462,7 @@ class CasFlightPlan(PatrollingFlightPlan):
yield self.land
if self.divert is not None:
yield self.divert
yield self.bullseye
def request_escort_at(self) -> Optional[FlightWaypoint]:
return self.patrol_start
@ -472,6 +476,7 @@ class TarCapFlightPlan(PatrollingFlightPlan):
takeoff: FlightWaypoint
land: FlightWaypoint
divert: Optional[FlightWaypoint]
bullseye: FlightWaypoint
lead_time: timedelta
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
@ -485,6 +490,7 @@ class TarCapFlightPlan(PatrollingFlightPlan):
yield self.land
if self.divert is not None:
yield self.divert
yield self.bullseye
@property
def tot_offset(self) -> timedelta:
@ -523,6 +529,7 @@ class StrikeFlightPlan(FormationFlightPlan):
nav_from: List[FlightWaypoint]
land: FlightWaypoint
divert: Optional[FlightWaypoint]
bullseye: FlightWaypoint
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
yield self.takeoff
@ -537,6 +544,7 @@ class StrikeFlightPlan(FormationFlightPlan):
yield self.land
if self.divert is not None:
yield self.divert
yield self.bullseye
@property
def package_speed_waypoints(self) -> Set[FlightWaypoint]:
@ -641,6 +649,7 @@ class SweepFlightPlan(LoiterFlightPlan):
nav_from: List[FlightWaypoint]
land: FlightWaypoint
divert: Optional[FlightWaypoint]
bullseye: FlightWaypoint
lead_time: timedelta
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
@ -653,6 +662,7 @@ class SweepFlightPlan(LoiterFlightPlan):
yield self.land
if self.divert is not None:
yield self.divert
yield self.bullseye
@property
def tot_waypoint(self) -> Optional[FlightWaypoint]:
@ -704,6 +714,7 @@ class AwacsFlightPlan(LoiterFlightPlan):
nav_from: List[FlightWaypoint]
land: FlightWaypoint
divert: Optional[FlightWaypoint]
bullseye: FlightWaypoint
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
yield self.takeoff
@ -713,6 +724,7 @@ class AwacsFlightPlan(LoiterFlightPlan):
yield self.land
if self.divert is not None:
yield self.divert
yield self.bullseye
@property
def mission_start_time(self) -> Optional[timedelta]:
@ -746,6 +758,7 @@ class AirliftFlightPlan(FlightPlan):
nav_to_home: List[FlightWaypoint]
land: FlightWaypoint
divert: Optional[FlightWaypoint]
bullseye: FlightWaypoint
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
yield self.takeoff
@ -758,6 +771,7 @@ class AirliftFlightPlan(FlightPlan):
yield self.land
if self.divert is not None:
yield self.divert
yield self.bullseye
@property
def tot_waypoint(self) -> Optional[FlightWaypoint]:
@ -1053,6 +1067,7 @@ class FlightPlanBuilder:
),
land=builder.land(flight.arrival),
divert=builder.divert(flight.divert),
bullseye=builder.bullseye(),
hold=start,
hold_duration=timedelta(hours=4),
)
@ -1151,6 +1166,7 @@ class FlightPlanBuilder:
patrol_end=end,
land=builder.land(flight.arrival),
divert=builder.divert(flight.divert),
bullseye=builder.bullseye(),
)
def generate_sweep(self, flight: Flight) -> SweepFlightPlan:
@ -1187,6 +1203,7 @@ class FlightPlanBuilder:
sweep_end=end,
land=builder.land(flight.arrival),
divert=builder.divert(flight.divert),
bullseye=builder.bullseye(),
)
def generate_transport(self, flight: Flight) -> AirliftFlightPlan:
@ -1238,6 +1255,7 @@ class FlightPlanBuilder:
),
land=builder.land(flight.arrival),
divert=builder.divert(flight.divert),
bullseye=builder.bullseye(),
)
def racetrack_for_objective(
@ -1389,6 +1407,7 @@ class FlightPlanBuilder:
patrol_end=end,
land=builder.land(flight.arrival),
divert=builder.divert(flight.divert),
bullseye=builder.bullseye(),
)
def generate_dead(
@ -1517,6 +1536,7 @@ class FlightPlanBuilder:
),
land=builder.land(flight.arrival),
divert=builder.divert(flight.divert),
bullseye=builder.bullseye(),
)
def generate_cas(self, flight: Flight) -> CasFlightPlan:
@ -1562,6 +1582,7 @@ class FlightPlanBuilder:
patrol_end=builder.egress(egress, location),
land=builder.land(flight.arrival),
divert=builder.divert(flight.divert),
bullseye=builder.bullseye(),
)
@staticmethod
@ -1696,6 +1717,7 @@ class FlightPlanBuilder:
),
land=builder.land(flight.arrival),
divert=builder.divert(flight.divert),
bullseye=builder.bullseye(),
)
def _retreating_rendezvous_point(self, attack_transition: Point) -> Point:

View File

@ -50,6 +50,7 @@ class WaypointBuilder:
self.threat_zones = game.threat_zone_for(not player)
self.navmesh = game.navmesh_for(player)
self.targets = targets
self._bullseye = game.bullseye_for(player)
@property
def is_helo(self) -> bool:
@ -145,6 +146,19 @@ class WaypointBuilder:
waypoint.only_for_player = True
return waypoint
def bullseye(self) -> FlightWaypoint:
waypoint = FlightWaypoint(
FlightWaypointType.BULLSEYE,
self._bullseye.position.x,
self._bullseye.position.y,
meters(0),
)
waypoint.pretty_name = "Bullseye"
waypoint.description = "Bullseye"
waypoint.name = "BULLSEYE"
waypoint.only_for_player = True
return waypoint
def hold(self, position: Point) -> FlightWaypoint:
waypoint = FlightWaypoint(
FlightWaypointType.LOITER,

View File

@ -209,16 +209,6 @@ class TriggersGenerator:
player_coalition = "blue"
enemy_coalition = "red"
player_cp, enemy_cp = self.game.theater.closest_opposing_control_points()
self.mission.coalition["blue"].bullseye = {
"x": enemy_cp.position.x,
"y": enemy_cp.position.y,
}
self.mission.coalition["red"].bullseye = {
"x": player_cp.position.x,
"y": player_cp.position.y,
}
self._set_skill(player_coalition, enemy_coalition)
self._set_allegiances(player_coalition, enemy_coalition)
self._gen_markers()

View File

@ -375,6 +375,7 @@ class WaypointJs(QObject):
timingChanged = Signal()
isTakeoffChanged = Signal()
isDivertChanged = Signal()
isBullseyeChanged = Signal()
def __init__(
self,
@ -439,6 +440,10 @@ class WaypointJs(QObject):
def isDivert(self) -> bool:
return self.waypoint.waypoint_type is FlightWaypointType.DIVERT
@Property(bool, notify=isBullseyeChanged)
def isBullseye(self) -> bool:
return self.waypoint.waypoint_type is FlightWaypointType.BULLSEYE
@Slot(list, result=str)
def setPosition(self, position: LeafletLatLon) -> str:
point = self.theater.ll_to_point(LatLon(*position))

View File

@ -570,7 +570,7 @@ class Waypoint {
}
includeInPath() {
return !this.waypoint.isDivert;
return !this.waypoint.isDivert && !this.waypoint.isBullseye;
}
}