mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Update pydcs, adapt to new Point APIs.
This is briefly moving us over to my fork of pydcs while we wait for https://github.com/pydcs/dcs/pull/206 to be merged. The adaptation is invasive enough that I don't want it lingering for long.
This commit is contained in:
parent
ff12b37431
commit
9e2e4ffa74
@ -701,8 +701,7 @@ class StrikeFlightPlan(FormationFlightPlan):
|
||||
return FlightWaypoint(
|
||||
"TARGET AREA",
|
||||
FlightWaypointType.TARGET_GROUP_LOC,
|
||||
self.package.target.position.x,
|
||||
self.package.target.position.y,
|
||||
self.package.target.position,
|
||||
meters(0),
|
||||
"RADIO",
|
||||
)
|
||||
@ -906,8 +905,7 @@ class PackageRefuelingFlightPlan(RefuelingFlightPlan):
|
||||
return FlightWaypoint(
|
||||
"TARGET AREA",
|
||||
FlightWaypointType.TARGET_GROUP_LOC,
|
||||
self.package.target.position.x,
|
||||
self.package.target.position.y,
|
||||
self.package.target.position,
|
||||
meters(0),
|
||||
"RADIO",
|
||||
)
|
||||
@ -924,13 +922,13 @@ class PackageRefuelingFlightPlan(RefuelingFlightPlan):
|
||||
# Cheat in a FlightWaypoint for the split point.
|
||||
split: Point = self.package.waypoints.split
|
||||
split_waypoint: FlightWaypoint = FlightWaypoint(
|
||||
"SPLIT", FlightWaypointType.SPLIT, split.x, split.y, altitude
|
||||
"SPLIT", FlightWaypointType.SPLIT, split, altitude
|
||||
)
|
||||
|
||||
# Cheat in a FlightWaypoint for the refuel point.
|
||||
refuel: Point = self.package.waypoints.refuel
|
||||
refuel_waypoint: FlightWaypoint = FlightWaypoint(
|
||||
"REFUEL", FlightWaypointType.REFUEL, refuel.x, refuel.y, altitude
|
||||
"REFUEL", FlightWaypointType.REFUEL, refuel, altitude
|
||||
)
|
||||
|
||||
delay_target_to_split: timedelta = self.travel_time_between_waypoints(
|
||||
|
||||
@ -31,12 +31,9 @@ class Navigating(InFlight):
|
||||
)
|
||||
|
||||
def estimate_position(self) -> Point:
|
||||
x0 = self.current_waypoint.position.x
|
||||
y0 = self.current_waypoint.position.y
|
||||
x1 = self.next_waypoint.position.x
|
||||
y1 = self.next_waypoint.position.y
|
||||
progress = self.progress()
|
||||
return Point(lerp(x0, x1, progress), lerp(y0, y1, progress))
|
||||
return self.current_waypoint.position.lerp(
|
||||
self.next_waypoint.position, self.progress()
|
||||
)
|
||||
|
||||
def estimate_altitude(self) -> tuple[Distance, str]:
|
||||
return (
|
||||
|
||||
@ -22,8 +22,7 @@ AltitudeReference = Literal["BARO", "RADIO"]
|
||||
class FlightWaypoint:
|
||||
name: str
|
||||
waypoint_type: FlightWaypointType
|
||||
x: float
|
||||
y: float
|
||||
position: Point
|
||||
alt: Distance = meters(0)
|
||||
alt_type: AltitudeReference = "BARO"
|
||||
control_point: ControlPoint | None = None
|
||||
@ -50,8 +49,12 @@ class FlightWaypoint:
|
||||
departure_time: timedelta | None = None
|
||||
|
||||
@property
|
||||
def position(self) -> Point:
|
||||
return Point(self.x, self.y)
|
||||
def x(self) -> float:
|
||||
return self.position.x
|
||||
|
||||
@property
|
||||
def y(self) -> float:
|
||||
return self.position.y
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(id(self))
|
||||
|
||||
@ -12,7 +12,7 @@ from typing import (
|
||||
Union,
|
||||
)
|
||||
|
||||
from dcs.mapping import Point
|
||||
from dcs.mapping import Point, Vector2
|
||||
|
||||
from game.theater import (
|
||||
ControlPoint,
|
||||
@ -71,8 +71,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"NAV",
|
||||
FlightWaypointType.NAV,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
meters(500) if self.is_helo else self.doctrine.rendezvous_altitude,
|
||||
description="Enter theater",
|
||||
pretty_name="Enter theater",
|
||||
@ -81,8 +80,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"TAKEOFF",
|
||||
FlightWaypointType.TAKEOFF,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
meters(0),
|
||||
alt_type="RADIO",
|
||||
description="Takeoff",
|
||||
@ -100,8 +98,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"NAV",
|
||||
FlightWaypointType.NAV,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
meters(500) if self.is_helo else self.doctrine.rendezvous_altitude,
|
||||
description="Exit theater",
|
||||
pretty_name="Exit theater",
|
||||
@ -110,8 +107,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"LANDING",
|
||||
FlightWaypointType.LANDING_POINT,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
meters(0),
|
||||
alt_type="RADIO",
|
||||
description="Land",
|
||||
@ -143,8 +139,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"DIVERT",
|
||||
FlightWaypointType.DIVERT,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
altitude,
|
||||
alt_type=altitude_type,
|
||||
description="Divert",
|
||||
@ -157,8 +152,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"BULLSEYE",
|
||||
FlightWaypointType.BULLSEYE,
|
||||
self._bullseye.position.x,
|
||||
self._bullseye.position.y,
|
||||
self._bullseye.position,
|
||||
meters(0),
|
||||
description="Bullseye",
|
||||
pretty_name="Bullseye",
|
||||
@ -173,8 +167,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"HOLD",
|
||||
FlightWaypointType.LOITER,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
meters(500) if self.is_helo else self.doctrine.rendezvous_altitude,
|
||||
alt_type,
|
||||
description="Wait until push time",
|
||||
@ -189,8 +182,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"JOIN",
|
||||
FlightWaypointType.JOIN,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
meters(80) if self.is_helo else self.doctrine.ingress_altitude,
|
||||
alt_type,
|
||||
description="Rendezvous with package",
|
||||
@ -205,8 +197,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"REFUEL",
|
||||
FlightWaypointType.REFUEL,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
meters(80) if self.is_helo else self.doctrine.ingress_altitude,
|
||||
alt_type,
|
||||
description="Refuel from tanker",
|
||||
@ -221,8 +212,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"SPLIT",
|
||||
FlightWaypointType.SPLIT,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
meters(80) if self.is_helo else self.doctrine.ingress_altitude,
|
||||
alt_type,
|
||||
description="Depart from package",
|
||||
@ -242,8 +232,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"INGRESS",
|
||||
ingress_type,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
meters(60) if self.is_helo else self.doctrine.ingress_altitude,
|
||||
alt_type,
|
||||
description=f"INGRESS on {objective.name}",
|
||||
@ -259,8 +248,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"EGRESS",
|
||||
FlightWaypointType.EGRESS,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
meters(60) if self.is_helo else self.doctrine.ingress_altitude,
|
||||
alt_type,
|
||||
description=f"EGRESS from {target.name}",
|
||||
@ -284,8 +272,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
target.name,
|
||||
FlightWaypointType.TARGET_POINT,
|
||||
target.target.position.x,
|
||||
target.target.position.y,
|
||||
target.target.position,
|
||||
meters(0),
|
||||
"RADIO",
|
||||
description=description,
|
||||
@ -315,8 +302,7 @@ class WaypointBuilder:
|
||||
waypoint = FlightWaypoint(
|
||||
name,
|
||||
FlightWaypointType.TARGET_GROUP_LOC,
|
||||
location.position.x,
|
||||
location.position.y,
|
||||
location.position,
|
||||
meters(0),
|
||||
"RADIO",
|
||||
description=name,
|
||||
@ -340,8 +326,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"CAS",
|
||||
FlightWaypointType.CAS,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
meters(60) if self.is_helo else meters(1000),
|
||||
"RADIO",
|
||||
description="Provide CAS",
|
||||
@ -359,8 +344,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"RACETRACK START",
|
||||
FlightWaypointType.PATROL_TRACK,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
altitude,
|
||||
description="Orbit between this point and the next point",
|
||||
pretty_name="Race-track start",
|
||||
@ -377,8 +361,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"RACETRACK END",
|
||||
FlightWaypointType.PATROL,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
altitude,
|
||||
description="Orbit between this point and the previous point",
|
||||
pretty_name="Race-track end",
|
||||
@ -411,8 +394,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"ORBIT",
|
||||
FlightWaypointType.LOITER,
|
||||
start.x,
|
||||
start.y,
|
||||
start,
|
||||
altitude,
|
||||
description="Anchor and hold at this point",
|
||||
pretty_name="Orbit",
|
||||
@ -429,8 +411,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"SWEEP START",
|
||||
FlightWaypointType.INGRESS_SWEEP,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
altitude,
|
||||
description="Proceed to the target and engage enemy aircraft",
|
||||
pretty_name="Sweep start",
|
||||
@ -447,8 +428,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"SWEEP END",
|
||||
FlightWaypointType.EGRESS,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
altitude,
|
||||
description="End of sweep",
|
||||
pretty_name="Sweep end",
|
||||
@ -491,8 +471,7 @@ class WaypointBuilder:
|
||||
return ingress_wp, FlightWaypoint(
|
||||
"TARGET",
|
||||
FlightWaypointType.TARGET_GROUP_LOC,
|
||||
target.position.x,
|
||||
target.position.y,
|
||||
target.position,
|
||||
meters(60) if self.is_helo else self.doctrine.ingress_altitude,
|
||||
alt_type,
|
||||
description="Escort the package",
|
||||
@ -509,8 +488,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"PICKUP",
|
||||
FlightWaypointType.PICKUP,
|
||||
control_point.position.x,
|
||||
control_point.position.y,
|
||||
control_point.position,
|
||||
meters(0),
|
||||
"RADIO",
|
||||
description=f"Pick up cargo from {control_point}",
|
||||
@ -527,8 +505,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"DROP OFF",
|
||||
FlightWaypointType.PICKUP,
|
||||
control_point.position.x,
|
||||
control_point.position.y,
|
||||
control_point.position,
|
||||
meters(0),
|
||||
"RADIO",
|
||||
description=f"Drop off cargo at {control_point}",
|
||||
@ -554,8 +531,7 @@ class WaypointBuilder:
|
||||
return FlightWaypoint(
|
||||
"NAV",
|
||||
FlightWaypointType.NAV,
|
||||
position.x,
|
||||
position.y,
|
||||
position,
|
||||
altitude,
|
||||
alt_type,
|
||||
description="NAV",
|
||||
@ -617,4 +593,4 @@ class WaypointBuilder:
|
||||
deviation = nautical_miles(1)
|
||||
x_adj = random.randint(int(-deviation.meters), int(deviation.meters))
|
||||
y_adj = random.randint(int(-deviation.meters), int(deviation.meters))
|
||||
return Point(point.x + x_adj, point.y + y_adj)
|
||||
return point + Vector2(x_adj, y_adj)
|
||||
|
||||
@ -1,31 +1,30 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Optional
|
||||
from typing import Any, Optional, TYPE_CHECKING
|
||||
|
||||
from dcs import Point
|
||||
from faker import Faker
|
||||
|
||||
from game.campaignloader import CampaignAirWingConfig
|
||||
from game.armedforces.armedforces import ArmedForces
|
||||
from game.ato.airtaaskingorder import AirTaskingOrder
|
||||
from game.campaignloader.defaultsquadronassigner import DefaultSquadronAssigner
|
||||
from game.commander import TheaterCommander
|
||||
from game.commander.missionscheduler import MissionScheduler
|
||||
from game.armedforces.armedforces import ArmedForces
|
||||
from game.income import Income
|
||||
from game.navmesh import NavMesh
|
||||
from game.orderedset import OrderedSet
|
||||
from game.profiling import logged_duration, MultiEventTracer
|
||||
from game.procurement import AircraftProcurementRequest, ProcurementAi
|
||||
from game.profiling import MultiEventTracer, logged_duration
|
||||
from game.squadrons import AirWing
|
||||
from game.theater.bullseye import Bullseye
|
||||
from game.theater.transitnetwork import TransitNetwork, TransitNetworkBuilder
|
||||
from game.threatzones import ThreatZones
|
||||
from game.transfers import PendingTransfers
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game import Game
|
||||
from game.data.doctrine import Doctrine
|
||||
from game.factions.faction import Faction
|
||||
from game.procurement import AircraftProcurementRequest, ProcurementAi
|
||||
from game.theater.bullseye import Bullseye
|
||||
from game.theater.transitnetwork import TransitNetwork, TransitNetworkBuilder
|
||||
from game.ato.airtaaskingorder import AirTaskingOrder
|
||||
from game.campaignloader import CampaignAirWingConfig
|
||||
from game.data.doctrine import Doctrine
|
||||
from game.factions.faction import Faction
|
||||
|
||||
|
||||
class Coalition:
|
||||
@ -39,7 +38,7 @@ class Coalition:
|
||||
self.ato = AirTaskingOrder()
|
||||
self.transit_network = TransitNetwork()
|
||||
self.procurement_requests: OrderedSet[AircraftProcurementRequest] = OrderedSet()
|
||||
self.bullseye = Bullseye(Point(0, 0))
|
||||
self.bullseye = Bullseye(self.game.point_in_world(0, 0))
|
||||
self.faker = Faker(self.faction.locales)
|
||||
self.air_wing = AirWing(player, game, self.faction)
|
||||
self.armed_forces = ArmedForces(self.faction)
|
||||
|
||||
@ -64,6 +64,7 @@ class TheaterState(WorldState["TheaterState"]):
|
||||
def _rebuild_threat_zones(self) -> None:
|
||||
"""Recreates the theater's threat zones based on the current planned state."""
|
||||
self.threat_zones = ThreatZones.for_threats(
|
||||
self.context.theater,
|
||||
self.context.coalition.opponent.doctrine,
|
||||
barcap_locations=self.enemy_barcaps,
|
||||
air_defenses=itertools.chain(self.enemy_air_defenses, self.enemy_ships),
|
||||
|
||||
@ -4,7 +4,7 @@ from typing import TYPE_CHECKING
|
||||
|
||||
import shapely.ops
|
||||
from dcs import Point
|
||||
from shapely.geometry import Point as ShapelyPoint, Polygon, MultiPolygon
|
||||
from shapely.geometry import MultiPolygon, Point as ShapelyPoint, Polygon
|
||||
|
||||
from game.utils import nautical_miles
|
||||
|
||||
@ -29,6 +29,7 @@ class HoldZoneGeometry:
|
||||
coalition: Coalition,
|
||||
theater: ConflictTheater,
|
||||
) -> None:
|
||||
self._target = target
|
||||
# Hold points are placed one of two ways. Either approach guarantees:
|
||||
#
|
||||
# * Safe hold point.
|
||||
@ -105,4 +106,4 @@ class HoldZoneGeometry:
|
||||
hold, _ = shapely.ops.nearest_points(self.permissible_zones, self.home)
|
||||
else:
|
||||
hold, _ = shapely.ops.nearest_points(self.preferred_lines, self.join)
|
||||
return Point(hold.x, hold.y)
|
||||
return self._target.new_in_same_map(hold.x, hold.y)
|
||||
|
||||
@ -4,9 +4,9 @@ from typing import TYPE_CHECKING
|
||||
|
||||
import shapely.ops
|
||||
from dcs import Point
|
||||
from shapely.geometry import Point as ShapelyPoint, MultiPolygon
|
||||
from shapely.geometry import MultiPolygon, Point as ShapelyPoint
|
||||
|
||||
from game.utils import nautical_miles, meters
|
||||
from game.utils import meters, nautical_miles
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.coalition import Coalition
|
||||
@ -25,6 +25,7 @@ class IpZoneGeometry:
|
||||
home: Point,
|
||||
coalition: Coalition,
|
||||
) -> None:
|
||||
self._target = target
|
||||
self.threat_zone = coalition.opponent.threat_zone.all
|
||||
self.home = ShapelyPoint(home.x, home.y)
|
||||
|
||||
@ -115,4 +116,4 @@ class IpZoneGeometry:
|
||||
ip = self._unsafe_ip()
|
||||
else:
|
||||
ip = self._safe_ip()
|
||||
return Point(ip.x, ip.y)
|
||||
return self._target.new_in_same_map(ip.x, ip.y)
|
||||
|
||||
@ -5,10 +5,10 @@ from typing import TYPE_CHECKING
|
||||
import shapely.ops
|
||||
from dcs import Point
|
||||
from shapely.geometry import (
|
||||
MultiLineString,
|
||||
MultiPolygon,
|
||||
Point as ShapelyPoint,
|
||||
Polygon,
|
||||
MultiPolygon,
|
||||
MultiLineString,
|
||||
)
|
||||
|
||||
from game.utils import nautical_miles
|
||||
@ -27,6 +27,7 @@ class JoinZoneGeometry:
|
||||
def __init__(
|
||||
self, target: Point, home: Point, ip: Point, coalition: Coalition
|
||||
) -> None:
|
||||
self._target = target
|
||||
# Normal join placement is based on the path from home to the IP. If no path is
|
||||
# found it means that the target is on a direct path. In that case we instead
|
||||
# want to enforce that the join point is:
|
||||
@ -100,4 +101,4 @@ class JoinZoneGeometry:
|
||||
join, _ = shapely.ops.nearest_points(self.permissible_zones, self.ip)
|
||||
else:
|
||||
join, _ = shapely.ops.nearest_points(self.preferred_lines, self.home)
|
||||
return Point(join.x, join.y)
|
||||
return self._target.new_in_same_map(join.x, join.y)
|
||||
|
||||
15
game/game.py
15
game/game.py
@ -143,6 +143,9 @@ class Game:
|
||||
yield self.blue
|
||||
yield self.red
|
||||
|
||||
def point_in_world(self, x: float, y: float) -> Point:
|
||||
return Point(x, y, self.theater.terrain)
|
||||
|
||||
def ato_for(self, player: bool) -> AirTaskingOrder:
|
||||
return self.coalition_for(player).ato
|
||||
|
||||
@ -180,9 +183,6 @@ class Game:
|
||||
def country_for(self, player: bool) -> str:
|
||||
return self.coalition_for(player).country_name
|
||||
|
||||
def bullseye_for(self, player: bool) -> Bullseye:
|
||||
return self.coalition_for(player).bullseye
|
||||
|
||||
@property
|
||||
def neutral_country(self) -> Type[Country]:
|
||||
"""Return the best fitting country that can be used as neutral faction in the generated mission"""
|
||||
@ -447,10 +447,7 @@ class Game:
|
||||
d = cp.position.distance_to_point(cp2.position)
|
||||
if d < min_distance:
|
||||
min_distance = d
|
||||
cpoint = Point(
|
||||
(cp.position.x + cp2.position.x) / 2,
|
||||
(cp.position.y + cp2.position.y) / 2,
|
||||
)
|
||||
cpoint = cp.position.midpoint(cp2.position)
|
||||
zones.append(cp.position)
|
||||
zones.append(cp2.position)
|
||||
break
|
||||
@ -473,7 +470,9 @@ class Game:
|
||||
self.__culling_zones = zones
|
||||
|
||||
def add_destroyed_units(self, data: dict[str, Union[float, str]]) -> None:
|
||||
pos = Point(cast(float, data["x"]), cast(float, data["z"]))
|
||||
pos = Point(
|
||||
cast(float, data["x"]), cast(float, data["z"]), self.theater.terrain
|
||||
)
|
||||
if self.theater.is_on_land(pos):
|
||||
self.__destroyed_units.append(data)
|
||||
|
||||
|
||||
@ -3,8 +3,9 @@ import logging
|
||||
import random
|
||||
from typing import Any, Union
|
||||
|
||||
from dcs import Mission, Point
|
||||
from dcs import Mission
|
||||
from dcs.country import Country
|
||||
from dcs.mapping import Vector2
|
||||
from dcs.mission import StartType as DcsStartType
|
||||
from dcs.planes import F_14A, Su_33
|
||||
from dcs.point import PointAction
|
||||
@ -139,7 +140,7 @@ class FlightGroupSpawner:
|
||||
)
|
||||
speed = self.flight.state.estimate_speed()
|
||||
pos = self.flight.state.estimate_position()
|
||||
pos += Point(random.randint(100, 1000), random.randint(100, 1000))
|
||||
pos += Vector2(random.randint(100, 1000), random.randint(100, 1000))
|
||||
alt, alt_type = self.flight.state.estimate_altitude()
|
||||
|
||||
# We don't know where the ground is, so just make sure that any aircraft
|
||||
@ -197,7 +198,7 @@ class FlightGroupSpawner:
|
||||
alt = WARM_START_ALTITUDE
|
||||
|
||||
speed = GroundSpeed.for_flight(self.flight, alt)
|
||||
pos = Point(at.x + random.randint(100, 1000), at.y + random.randint(100, 1000))
|
||||
pos = at + Vector2(random.randint(100, 1000), random.randint(100, 1000))
|
||||
|
||||
group = self.mission.flight_group(
|
||||
country=self.country,
|
||||
|
||||
@ -3,10 +3,9 @@ from __future__ import annotations
|
||||
from datetime import timedelta
|
||||
from typing import Any, Iterable, Union
|
||||
|
||||
from dcs import Mission, Point
|
||||
from dcs import Mission
|
||||
from dcs.planes import AJS37, F_14B, JF_17
|
||||
from dcs.point import MovingPoint, PointAction
|
||||
from dcs.unit import Unit
|
||||
from dcs.unitgroup import FlyingGroup
|
||||
|
||||
from game.ato import Flight, FlightWaypoint
|
||||
@ -41,7 +40,7 @@ class PydcsWaypointBuilder:
|
||||
|
||||
def build(self) -> MovingPoint:
|
||||
waypoint = self.group.add_waypoint(
|
||||
Point(self.waypoint.x, self.waypoint.y),
|
||||
self.waypoint.position,
|
||||
self.waypoint.alt.meters,
|
||||
name=self.waypoint.name,
|
||||
)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
from dcs import Point
|
||||
import copy
|
||||
|
||||
from dcs.planes import B_17G, B_52H, Tu_22M3
|
||||
from dcs.point import MovingPoint
|
||||
from dcs.task import Bombing, OptFormation, WeaponType
|
||||
@ -20,12 +21,10 @@ class StrikeIngressBuilder(PydcsWaypointBuilder):
|
||||
if not targets:
|
||||
return
|
||||
|
||||
center = Point(0, 0)
|
||||
for target in targets:
|
||||
center.x += target.position.x
|
||||
center.y += target.position.y
|
||||
center.x /= len(targets)
|
||||
center.y /= len(targets)
|
||||
center = copy.copy(targets[0].position)
|
||||
for target in targets[1:]:
|
||||
center += target.position
|
||||
center /= len(targets)
|
||||
bombing = Bombing(center, weapon_type=WeaponType.Bombs)
|
||||
bombing.params["expend"] = "All"
|
||||
bombing.params["attackQtyLimit"] = False
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
from datetime import datetime
|
||||
|
||||
from dcs import Point
|
||||
from dcs.drawing import LineStyle, Rgba
|
||||
from dcs.drawing.drawings import StandardLayer
|
||||
from dcs.mission import Mission
|
||||
|
||||
from game import Game, VERSION
|
||||
from game import Game
|
||||
from game.missiongenerator.frontlineconflictdescription import (
|
||||
FrontLineConflictDescription,
|
||||
)
|
||||
@ -73,7 +71,7 @@ class DrawingsGenerator:
|
||||
# Add shape to layer
|
||||
shape = self.player_layer.add_line_segments(
|
||||
cp.position,
|
||||
[Point(0, 0)]
|
||||
[Point(0, 0, self.game.theater.terrain)]
|
||||
+ [p - cp.position for p in convoy_route]
|
||||
+ [destination.position - cp.position],
|
||||
line_thickness=6,
|
||||
|
||||
@ -9,7 +9,7 @@ from dcs import Mission
|
||||
from dcs.action import AITaskPush
|
||||
from dcs.condition import GroupLifeLess, Or, TimeAfter, UnitDamaged
|
||||
from dcs.country import Country
|
||||
from dcs.mapping import Point
|
||||
from dcs.mapping import Point, Vector2
|
||||
from dcs.point import PointAction
|
||||
from dcs.task import (
|
||||
AFAC,
|
||||
@ -372,7 +372,7 @@ class FlotGenerator:
|
||||
# Then move forward OR Attack enemy base if it is not too far away
|
||||
target = self.find_nearest_enemy_group(dcs_group, enemy_groups)
|
||||
if target is not None:
|
||||
rand_offset = Point(
|
||||
rand_offset = Vector2(
|
||||
random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK),
|
||||
random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK),
|
||||
)
|
||||
@ -419,7 +419,7 @@ class FlotGenerator:
|
||||
# In elimination mode, the units focus on destroying as much enemy groups as possible
|
||||
targets = self.find_n_nearest_enemy_groups(dcs_group, enemy_groups, 3)
|
||||
for i, target in enumerate(targets, start=1):
|
||||
rand_offset = Point(
|
||||
rand_offset = Vector2(
|
||||
random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK),
|
||||
random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK),
|
||||
)
|
||||
|
||||
@ -766,7 +766,7 @@ class KneeboardGenerator(MissionInfoGenerator):
|
||||
pages: List[KneeboardPage] = [
|
||||
BriefingPage(
|
||||
flight,
|
||||
self.game.bullseye_for(flight.friendly),
|
||||
self.game.coalition_for(flight.friendly).bullseye,
|
||||
self.game.theater,
|
||||
self.game.conditions.weather,
|
||||
zoned_time,
|
||||
|
||||
@ -129,7 +129,7 @@ class MissionGenerator:
|
||||
"red", bullseye=self.game.red.bullseye.to_pydcs()
|
||||
)
|
||||
self.mission.coalition["neutrals"] = Coalition(
|
||||
"neutrals", bullseye=Bullseye(Point(0, 0)).to_pydcs()
|
||||
"neutrals", bullseye=Bullseye(Point(0, 0, self.mission.terrain)).to_pydcs()
|
||||
)
|
||||
|
||||
p_country = self.game.blue.country_name
|
||||
@ -295,7 +295,7 @@ class MissionGenerator:
|
||||
logging.warning(f"Destroyed unit has no type: {d}")
|
||||
continue
|
||||
|
||||
pos = Point(cast(float, d["x"]), cast(float, d["z"]))
|
||||
pos = Point(cast(float, d["x"]), cast(float, d["z"]), self.mission.terrain)
|
||||
if utype is not None and not self.game.position_culled(pos):
|
||||
self.mission.static_group(
|
||||
country=self.mission.country(self.game.blue.country_name),
|
||||
|
||||
@ -537,8 +537,7 @@ class HelipadGenerator:
|
||||
country = self.m.country(self.game.coalition_for(self.cp.captured).country_name)
|
||||
name = self.cp.name + "_helipad"
|
||||
sg = unitgroup.StaticGroup(self.m.next_group_id(), name)
|
||||
sp = StaticPoint()
|
||||
sp.position = self.cp.position
|
||||
sp = StaticPoint(self.cp.position)
|
||||
sg.add_point(sp)
|
||||
|
||||
for i, helipad in enumerate(self.cp.helipads):
|
||||
|
||||
@ -46,9 +46,8 @@ class NavPoint:
|
||||
point: ShapelyPoint
|
||||
poly: NavMeshPoly
|
||||
|
||||
@property
|
||||
def world_point(self) -> Point:
|
||||
return Point(self.point.x, self.point.y)
|
||||
def world_point(self, theater: ConflictTheater) -> Point:
|
||||
return Point(self.point.x, self.point.y, theater.terrain)
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.poly.ident)
|
||||
@ -90,8 +89,9 @@ class NavFrontier:
|
||||
|
||||
|
||||
class NavMesh:
|
||||
def __init__(self, polys: List[NavMeshPoly]) -> None:
|
||||
def __init__(self, polys: List[NavMeshPoly], theater: ConflictTheater) -> None:
|
||||
self.polys = polys
|
||||
self.theater = theater
|
||||
|
||||
def localize(self, point: Point) -> Optional[NavMeshPoly]:
|
||||
# This is a naive implementation but it's O(n). Runs at about 10k
|
||||
@ -117,8 +117,8 @@ class NavMesh:
|
||||
def travel_heuristic(self, a: NavPoint, b: NavPoint) -> float:
|
||||
return self.travel_cost(a, b)
|
||||
|
||||
@staticmethod
|
||||
def reconstruct_path(
|
||||
self,
|
||||
came_from: Dict[NavPoint, Optional[NavPoint]],
|
||||
origin: NavPoint,
|
||||
destination: NavPoint,
|
||||
@ -126,14 +126,14 @@ class NavMesh:
|
||||
current = destination
|
||||
path: List[Point] = []
|
||||
while current != origin:
|
||||
path.append(current.world_point)
|
||||
path.append(current.world_point(self.theater))
|
||||
previous = came_from[current]
|
||||
if previous is None:
|
||||
raise NavMeshError(
|
||||
f"Could not reconstruct path to {destination} from {origin}"
|
||||
)
|
||||
current = previous
|
||||
path.append(origin.world_point)
|
||||
path.append(origin.world_point(self.theater))
|
||||
path.reverse()
|
||||
return path
|
||||
|
||||
@ -270,4 +270,4 @@ class NavMesh:
|
||||
# Triangulate the safe-region to build the navmesh.
|
||||
navpolys = cls.create_navpolys(triangulate(bounds), threat_zones)
|
||||
cls.associate_neighbors(navpolys)
|
||||
return cls(navpolys)
|
||||
return NavMesh(navpolys, theater)
|
||||
|
||||
@ -3,21 +3,19 @@ from __future__ import annotations
|
||||
import math
|
||||
|
||||
from dcs import Point
|
||||
from dcs.terrain import Terrain
|
||||
|
||||
from game.utils import Heading
|
||||
|
||||
|
||||
class PointWithHeading(Point):
|
||||
def __init__(self) -> None:
|
||||
super(PointWithHeading, self).__init__(0, 0)
|
||||
self.heading: Heading = Heading.from_degrees(0)
|
||||
def __init__(self, x: float, y: float, heading: Heading, terrain: Terrain) -> None:
|
||||
super().__init__(x, y, terrain)
|
||||
self.heading: Heading = heading
|
||||
|
||||
@staticmethod
|
||||
def from_point(point: Point, heading: Heading) -> PointWithHeading:
|
||||
p = PointWithHeading()
|
||||
p.x = point.x
|
||||
p.y = point.y
|
||||
p.heading = heading
|
||||
return p
|
||||
return PointWithHeading(point.x, point.y, heading, point._terrain)
|
||||
|
||||
def rotate(self, origin: Point, heading: Heading) -> None:
|
||||
"""Rotates the Point by a given angle clockwise around the origin"""
|
||||
|
||||
@ -17,7 +17,8 @@ class ShapelyUtil:
|
||||
if poly.is_empty:
|
||||
return []
|
||||
return [
|
||||
theater.point_to_ll(Point(x, y)).as_list() for x, y in poly.exterior.coords
|
||||
Point(x, y, theater.terrain).latlng().as_list()
|
||||
for x, y in poly.exterior.coords
|
||||
]
|
||||
|
||||
@classmethod
|
||||
@ -34,7 +35,7 @@ class ShapelyUtil:
|
||||
def line_to_leaflet(
|
||||
line: LineString, theater: ConflictTheater
|
||||
) -> list[LeafletLatLon]:
|
||||
return [theater.point_to_ll(Point(x, y)).as_list() for x, y in line.coords]
|
||||
return [Point(x, y, theater.terrain).latlng().as_list() for x, y in line.coords]
|
||||
|
||||
@classmethod
|
||||
def lines_to_leaflet(
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
from datetime import timedelta
|
||||
from uuid import UUID
|
||||
|
||||
from dcs.mapping import LatLng
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
|
||||
from game import Game
|
||||
@ -8,7 +9,6 @@ from game.ato.flightwaypoint import FlightWaypoint
|
||||
from game.ato.flightwaypointtype import FlightWaypointType
|
||||
from game.server import GameContext
|
||||
from game.server.waypoints.models import FlightWaypointJs
|
||||
from game.theater import LatLon
|
||||
from game.utils import meters
|
||||
|
||||
router: APIRouter = APIRouter(prefix="/waypoints")
|
||||
@ -23,8 +23,7 @@ def all_waypoints_for_flight(
|
||||
FlightWaypoint(
|
||||
"TAKEOFF",
|
||||
FlightWaypointType.TAKEOFF,
|
||||
flight.departure.position.x,
|
||||
flight.departure.position.y,
|
||||
flight.departure.position,
|
||||
meters(0),
|
||||
"RADIO",
|
||||
),
|
||||
@ -40,7 +39,7 @@ def all_waypoints_for_flight(
|
||||
def set_position(
|
||||
flight_id: UUID,
|
||||
waypoint_idx: int,
|
||||
position: LatLon,
|
||||
position: LatLng,
|
||||
game: Game = Depends(GameContext.get),
|
||||
) -> None:
|
||||
flight = game.db.flights.get(flight_id)
|
||||
@ -48,9 +47,7 @@ def set_position(
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
waypoint = flight.flight_plan.waypoints[waypoint_idx - 1]
|
||||
point = game.theater.ll_to_point(position)
|
||||
waypoint.x = point.x
|
||||
waypoint.y = point.y
|
||||
waypoint.position = game.theater.ll_to_point(position)
|
||||
package_model = (
|
||||
GameContext.get_model()
|
||||
.ato_model_for(flight.blue)
|
||||
|
||||
@ -5,10 +5,10 @@ from typing import Dict, TYPE_CHECKING
|
||||
|
||||
from dcs import Point
|
||||
|
||||
from game.theater import LatLon
|
||||
from .latlon import LatLon
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.theater import ConflictTheater
|
||||
from .conflicttheater import ConflictTheater
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@ -6,7 +6,7 @@ from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Iterator, List, Optional, TYPE_CHECKING, Tuple
|
||||
|
||||
from dcs.mapping import Point
|
||||
from dcs.mapping import LatLng, Point
|
||||
from dcs.terrain import (
|
||||
caucasus,
|
||||
marianaislands,
|
||||
@ -147,8 +147,8 @@ class ConflictTheater:
|
||||
min_distance = distance
|
||||
nearest_point = pt
|
||||
assert isinstance(nearest_point, geometry.Point)
|
||||
point = Point(point.x, point.y)
|
||||
nearest_point = Point(nearest_point.x, nearest_point.y)
|
||||
point = Point(point.x, point.y, self.terrain)
|
||||
nearest_point = Point(nearest_point.x, nearest_point.y, self.terrain)
|
||||
new_point = point.point_from_heading(
|
||||
point.heading_between_point(nearest_point),
|
||||
point.distance_to_point(nearest_point) + extend_dist,
|
||||
@ -261,9 +261,8 @@ class ConflictTheater:
|
||||
lat, lon = self.point_to_ll_transformer.transform(point.x, point.y)
|
||||
return LatLon(lat, lon)
|
||||
|
||||
def ll_to_point(self, ll: LatLon) -> Point:
|
||||
x, y = self.ll_to_point_transformer.transform(ll.lat, ll.lng)
|
||||
return Point(x, y)
|
||||
def ll_to_point(self, ll: LatLng) -> Point:
|
||||
return Point.from_latlng(ll, self.terrain)
|
||||
|
||||
def heading_to_conflict_from(self, position: Point) -> Optional[Heading]:
|
||||
# Heading for a Group to the enemy.
|
||||
@ -281,9 +280,8 @@ class ConflictTheater:
|
||||
]
|
||||
last = len(sorted_conflicts) - 1
|
||||
|
||||
conflict_center = Point(
|
||||
(sorted_conflicts[0].position.x + sorted_conflicts[last].position.x) / 2,
|
||||
(sorted_conflicts[0].position.y + sorted_conflicts[last].position.y) / 2,
|
||||
conflict_center = sorted_conflicts[0].position.midpoint(
|
||||
sorted_conflicts[last].position
|
||||
)
|
||||
|
||||
return Heading.from_degrees(position.heading_between_point(conflict_center))
|
||||
|
||||
@ -7,8 +7,8 @@ from dataclasses import dataclass, field
|
||||
from enum import Enum, auto
|
||||
from typing import Dict, Iterator, List, Optional, Set, Tuple
|
||||
|
||||
from game.theater import ConflictTheater
|
||||
from game.theater.controlpoint import ControlPoint
|
||||
from .conflicttheater import ConflictTheater
|
||||
from .controlpoint import ControlPoint
|
||||
|
||||
|
||||
class NoPathError(RuntimeError):
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import singledispatchmethod
|
||||
from typing import Optional, TYPE_CHECKING, Union, Iterable
|
||||
from typing import Iterable, Optional, TYPE_CHECKING, Union
|
||||
|
||||
from dcs.mapping import Point as DcsPoint
|
||||
from shapely.geometry import (
|
||||
@ -13,11 +13,16 @@ from shapely.geometry import (
|
||||
from shapely.geometry.base import BaseGeometry
|
||||
from shapely.ops import nearest_points, unary_union
|
||||
|
||||
from game.data.doctrine import Doctrine
|
||||
from game.theater import ControlPoint, MissionTarget, TheaterGroundObject
|
||||
from game.utils import Distance, meters, nautical_miles
|
||||
from game.ato.closestairfields import ObjectiveDistanceCache
|
||||
from game.ato import Flight, FlightWaypoint
|
||||
from game.ato.closestairfields import ObjectiveDistanceCache
|
||||
from game.data.doctrine import Doctrine
|
||||
from game.theater import (
|
||||
ConflictTheater,
|
||||
ControlPoint,
|
||||
MissionTarget,
|
||||
TheaterGroundObject,
|
||||
)
|
||||
from game.utils import Distance, meters, nautical_miles
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game import Game
|
||||
@ -29,10 +34,12 @@ ThreatPoly = Union[MultiPolygon, Polygon]
|
||||
class ThreatZones:
|
||||
def __init__(
|
||||
self,
|
||||
theater: ConflictTheater,
|
||||
airbases: ThreatPoly,
|
||||
air_defenses: ThreatPoly,
|
||||
radar_sam_threats: ThreatPoly,
|
||||
) -> None:
|
||||
self.theater = theater
|
||||
self.airbases = airbases
|
||||
self.air_defenses = air_defenses
|
||||
self.radar_sam_threats = radar_sam_threats
|
||||
@ -42,7 +49,7 @@ class ThreatZones:
|
||||
boundary, _ = nearest_points(
|
||||
self.all.boundary, self.dcs_to_shapely_point(point)
|
||||
)
|
||||
return DcsPoint(boundary.x, boundary.y)
|
||||
return DcsPoint(boundary.x, boundary.y, self.theater.terrain)
|
||||
|
||||
def distance_to_threat(self, point: DcsPoint) -> Distance:
|
||||
boundary = self.closest_boundary(point)
|
||||
@ -200,12 +207,13 @@ class ThreatZones:
|
||||
air_defenses.extend(control_point.ground_objects)
|
||||
|
||||
return cls.for_threats(
|
||||
game.faction_for(player).doctrine, air_threats, air_defenses
|
||||
game.theater, game.faction_for(player).doctrine, air_threats, air_defenses
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def for_threats(
|
||||
cls,
|
||||
theater: ConflictTheater,
|
||||
doctrine: Doctrine,
|
||||
barcap_locations: Iterable[ControlPoint],
|
||||
air_defenses: Iterable[TheaterGroundObject],
|
||||
@ -213,6 +221,7 @@ class ThreatZones:
|
||||
"""Generates the threat zones projected by the given locations.
|
||||
|
||||
Args:
|
||||
theater: The theater the threat zones are in.
|
||||
doctrine: The doctrine of the owning coalition.
|
||||
barcap_locations: The locations that will be considered for BARCAP planning.
|
||||
air_defenses: TGOs that may have air defenses.
|
||||
@ -245,7 +254,8 @@ class ThreatZones:
|
||||
threat_zone = point.buffer(threat_range.meters)
|
||||
radar_sam_threats.append(threat_zone)
|
||||
|
||||
return cls(
|
||||
return ThreatZones(
|
||||
theater,
|
||||
airbases=unary_union(air_threats),
|
||||
air_defenses=unary_union(air_defense_threats),
|
||||
radar_sam_threats=unary_union(radar_sam_threats),
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
from PySide2.QtGui import QStandardItem, QStandardItemModel
|
||||
|
||||
from game import Game
|
||||
from game.theater.controlpoint import ControlPointType
|
||||
from game.theater.theatergroundobject import IadsGroundObject, BuildingGroundObject
|
||||
from game.utils import Distance
|
||||
from game.ato.flightwaypoint import FlightWaypoint
|
||||
from game.ato.flightwaypointtype import FlightWaypointType
|
||||
from game.missiongenerator.frontlineconflictdescription import (
|
||||
FrontLineConflictDescription,
|
||||
)
|
||||
from game.ato.flightwaypointtype import FlightWaypointType
|
||||
from game.ato.flightwaypoint import FlightWaypoint
|
||||
from game.theater.controlpoint import ControlPointType
|
||||
from game.theater.theatergroundobject import BuildingGroundObject, IadsGroundObject
|
||||
from game.utils import Distance
|
||||
from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox
|
||||
|
||||
|
||||
@ -72,10 +72,7 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
|
||||
front_line, self.game.theater
|
||||
)[0]
|
||||
wpt = FlightWaypoint(
|
||||
FlightWaypointType.CUSTOM,
|
||||
pos.x,
|
||||
pos.y,
|
||||
Distance.from_meters(800),
|
||||
FlightWaypointType.CUSTOM, pos, Distance.from_meters(800)
|
||||
)
|
||||
wpt.name = f"Frontline {front_line.name} [CAS]"
|
||||
wpt.alt_type = "RADIO"
|
||||
@ -94,8 +91,7 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
|
||||
):
|
||||
wpt = FlightWaypoint(
|
||||
FlightWaypointType.CUSTOM,
|
||||
ground_object.position.x,
|
||||
ground_object.position.y,
|
||||
ground_object.position,
|
||||
Distance.from_meters(0),
|
||||
)
|
||||
wpt.alt_type = "RADIO"
|
||||
@ -122,8 +118,7 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
|
||||
for j, u in enumerate(g.units):
|
||||
wpt = FlightWaypoint(
|
||||
FlightWaypointType.CUSTOM,
|
||||
u.position.x,
|
||||
u.position.y,
|
||||
u.position,
|
||||
Distance.from_meters(0),
|
||||
)
|
||||
wpt.alt_type = "RADIO"
|
||||
@ -151,10 +146,7 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
|
||||
self.include_friendly and cp.captured
|
||||
):
|
||||
wpt = FlightWaypoint(
|
||||
FlightWaypointType.CUSTOM,
|
||||
cp.position.x,
|
||||
cp.position.y,
|
||||
Distance.from_meters(0),
|
||||
FlightWaypointType.CUSTOM, cp.position, Distance.from_meters(0)
|
||||
)
|
||||
wpt.alt_type = "RADIO"
|
||||
wpt.name = cp.name
|
||||
|
||||
@ -4,9 +4,10 @@ from typing import Optional
|
||||
|
||||
from PySide2.QtCore import Property, QObject, Signal, Slot
|
||||
from dcs import Point
|
||||
from dcs.mapping import LatLng
|
||||
|
||||
from game.server.leaflet import LeafletLatLon
|
||||
from game.theater import ConflictTheater, ControlPoint, ControlPointStatus, LatLon
|
||||
from game.theater import ConflictTheater, ControlPoint, ControlPointStatus
|
||||
from game.utils import meters, nautical_miles
|
||||
from qt_ui.dialogs import Dialog
|
||||
from qt_ui.models import GameModel
|
||||
@ -83,7 +84,7 @@ class ControlPointJs(QObject):
|
||||
|
||||
@Slot(list, result=bool)
|
||||
def destinationInRange(self, destination: LeafletLatLon) -> bool:
|
||||
return self.destination_in_range(self.theater.ll_to_point(LatLon(*destination)))
|
||||
return self.destination_in_range(self.theater.ll_to_point(LatLng(*destination)))
|
||||
|
||||
@Slot(list, result=str)
|
||||
def setDestination(self, destination: LeafletLatLon) -> str:
|
||||
@ -92,7 +93,7 @@ class ControlPointJs(QObject):
|
||||
if not self.control_point.captured:
|
||||
return f"{self.control_point} is not owned by player"
|
||||
|
||||
point = self.theater.ll_to_point(LatLon(*destination))
|
||||
point = self.theater.ll_to_point(LatLng(*destination))
|
||||
if not self.destination_in_range(point):
|
||||
return (
|
||||
f"Cannot move {self.control_point} more than "
|
||||
|
||||
@ -32,7 +32,7 @@ pluggy==1.0.0
|
||||
pre-commit==2.17.0
|
||||
py==1.11.0
|
||||
pydantic==1.9.0
|
||||
-e git+https://github.com/pydcs/dcs@63863a88e0a43cb0a310dbab3ce2c7800a099dbb#egg=pydcs
|
||||
-e git+https://github.com/DanAlbert/dcs@e652979adefca9f4358244cf9e42985ef58e9343#egg=pydcs
|
||||
pyinstaller==4.9
|
||||
pyinstaller-hooks-contrib==2022.1
|
||||
pyparsing==3.0.7
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user