mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
* Addresses #478, adding a heading class to represent headings and angles Removed some unused code * Fixing bad merge * Formatting * Fixing type issues and other merge resolution misses
This commit is contained in:
@@ -81,7 +81,7 @@ from game.theater.missiontarget import MissionTarget
|
||||
from game.theater.theatergroundobject import TheaterGroundObject
|
||||
from game.transfers import MultiGroupTransport
|
||||
from game.unitmap import UnitMap
|
||||
from game.utils import Distance, meters, nautical_miles, pairwise
|
||||
from game.utils import Distance, Heading, meters, nautical_miles, pairwise
|
||||
from gen.ato import AirTaskingOrder, Package
|
||||
from gen.callsigns import create_group_callsign_from_unit
|
||||
from gen.flights.flight import (
|
||||
|
||||
@@ -17,6 +17,9 @@ from dcs.task import (
|
||||
)
|
||||
from dcs.unittype import UnitType
|
||||
|
||||
from game.utils import Heading
|
||||
from .flights.ai_flight_planner_db import AEWC_CAPABLE
|
||||
from .naming import namegen
|
||||
from .callsigns import callsign_for_support_unit
|
||||
from .conflictgen import Conflict
|
||||
from .flights.ai_flight_planner_db import AEWC_CAPABLE
|
||||
@@ -122,14 +125,14 @@ class AirSupportConflictGenerator:
|
||||
alt, airspeed = self._get_tanker_params(tanker_unit_type.dcs_unit_type)
|
||||
freq = self.radio_registry.alloc_uhf()
|
||||
tacan = self.tacan_registry.alloc_for_band(TacanBand.Y)
|
||||
tanker_heading = (
|
||||
tanker_heading = Heading.from_degrees(
|
||||
self.conflict.red_cp.position.heading_between_point(
|
||||
self.conflict.blue_cp.position
|
||||
)
|
||||
+ TANKER_HEADING_OFFSET * i
|
||||
)
|
||||
tanker_position = player_cp.position.point_from_heading(
|
||||
tanker_heading, TANKER_DISTANCE
|
||||
tanker_heading.degrees, TANKER_DISTANCE
|
||||
)
|
||||
tanker_group = self.mission.refuel_flight(
|
||||
country=country,
|
||||
|
||||
84
gen/armor.py
84
gen/armor.py
@@ -32,7 +32,7 @@ from game.dcs.aircrafttype import AircraftType
|
||||
from game.dcs.groundunittype import GroundUnitType
|
||||
from game.theater.controlpoint import ControlPoint
|
||||
from game.unitmap import UnitMap
|
||||
from game.utils import heading_sum, opposite_heading
|
||||
from game.utils import Heading
|
||||
from gen.ground_forces.ai_ground_planner import (
|
||||
DISTANCE_FROM_FRONTLINE,
|
||||
CombatGroup,
|
||||
@@ -130,7 +130,7 @@ class GroundConflictGenerator:
|
||||
self.player_stance,
|
||||
player_groups,
|
||||
enemy_groups,
|
||||
self.conflict.heading + 90,
|
||||
self.conflict.heading.right,
|
||||
self.conflict.blue_cp,
|
||||
self.conflict.red_cp,
|
||||
)
|
||||
@@ -138,7 +138,7 @@ class GroundConflictGenerator:
|
||||
self.enemy_stance,
|
||||
enemy_groups,
|
||||
player_groups,
|
||||
self.conflict.heading - 90,
|
||||
self.conflict.heading.left,
|
||||
self.conflict.red_cp,
|
||||
self.conflict.blue_cp,
|
||||
)
|
||||
@@ -182,7 +182,11 @@ class GroundConflictGenerator:
|
||||
)
|
||||
|
||||
def gen_infantry_group_for_group(
|
||||
self, group: VehicleGroup, is_player: bool, side: Country, forward_heading: int
|
||||
self,
|
||||
group: VehicleGroup,
|
||||
is_player: bool,
|
||||
side: Country,
|
||||
forward_heading: Heading,
|
||||
) -> None:
|
||||
|
||||
infantry_position = self.conflict.find_ground_position(
|
||||
@@ -217,7 +221,7 @@ class GroundConflictGenerator:
|
||||
u.dcs_unit_type,
|
||||
position=infantry_position,
|
||||
group_size=1,
|
||||
heading=forward_heading,
|
||||
heading=forward_heading.degrees,
|
||||
move_formation=PointAction.OffRoad,
|
||||
)
|
||||
return
|
||||
@@ -244,7 +248,7 @@ class GroundConflictGenerator:
|
||||
units[0].dcs_unit_type,
|
||||
position=infantry_position,
|
||||
group_size=1,
|
||||
heading=forward_heading,
|
||||
heading=forward_heading.degrees,
|
||||
move_formation=PointAction.OffRoad,
|
||||
)
|
||||
|
||||
@@ -256,17 +260,19 @@ class GroundConflictGenerator:
|
||||
unit.dcs_unit_type,
|
||||
position=position,
|
||||
group_size=1,
|
||||
heading=forward_heading,
|
||||
heading=forward_heading.degrees,
|
||||
move_formation=PointAction.OffRoad,
|
||||
)
|
||||
|
||||
def _set_reform_waypoint(
|
||||
self, dcs_group: VehicleGroup, forward_heading: int
|
||||
self, dcs_group: VehicleGroup, forward_heading: Heading
|
||||
) -> None:
|
||||
"""Setting a waypoint close to the spawn position allows the group to reform gracefully
|
||||
rather than spin
|
||||
"""
|
||||
reform_point = dcs_group.position.point_from_heading(forward_heading, 50)
|
||||
reform_point = dcs_group.position.point_from_heading(
|
||||
forward_heading.degrees, 50
|
||||
)
|
||||
dcs_group.add_waypoint(reform_point)
|
||||
|
||||
def _plan_artillery_action(
|
||||
@@ -274,7 +280,7 @@ class GroundConflictGenerator:
|
||||
stance: CombatStance,
|
||||
gen_group: CombatGroup,
|
||||
dcs_group: VehicleGroup,
|
||||
forward_heading: int,
|
||||
forward_heading: Heading,
|
||||
target: Point,
|
||||
) -> bool:
|
||||
"""
|
||||
@@ -308,7 +314,7 @@ class GroundConflictGenerator:
|
||||
dcs_group, forward_heading, (int)(RETREAT_DISTANCE / 3)
|
||||
)
|
||||
dcs_group.add_waypoint(
|
||||
dcs_group.position.point_from_heading(forward_heading, 1),
|
||||
dcs_group.position.point_from_heading(forward_heading.degrees, 1),
|
||||
PointAction.OffRoad,
|
||||
)
|
||||
dcs_group.points[2].tasks.append(Hold())
|
||||
@@ -336,7 +342,7 @@ class GroundConflictGenerator:
|
||||
self.mission.triggerrules.triggers.append(artillery_fallback)
|
||||
|
||||
for u in dcs_group.units:
|
||||
u.heading = forward_heading + random.randint(-5, 5)
|
||||
u.heading = (forward_heading + Heading.random(-5, 5)).degrees
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -345,7 +351,7 @@ class GroundConflictGenerator:
|
||||
stance: CombatStance,
|
||||
enemy_groups: List[Tuple[VehicleGroup, CombatGroup]],
|
||||
dcs_group: VehicleGroup,
|
||||
forward_heading: int,
|
||||
forward_heading: Heading,
|
||||
to_cp: ControlPoint,
|
||||
) -> bool:
|
||||
"""
|
||||
@@ -378,9 +384,7 @@ class GroundConflictGenerator:
|
||||
else:
|
||||
# We use an offset heading here because DCS doesn't always
|
||||
# force vehicles to move if there's no heading change.
|
||||
offset_heading = forward_heading - 2
|
||||
if offset_heading < 0:
|
||||
offset_heading = 358
|
||||
offset_heading = forward_heading - Heading.from_degrees(2)
|
||||
attack_point = self.find_offensive_point(
|
||||
dcs_group, offset_heading, AGGRESIVE_MOVE_DISTANCE
|
||||
)
|
||||
@@ -398,9 +402,7 @@ class GroundConflictGenerator:
|
||||
else:
|
||||
# We use an offset heading here because DCS doesn't always
|
||||
# force vehicles to move if there's no heading change.
|
||||
offset_heading = forward_heading - 1
|
||||
if offset_heading < 0:
|
||||
offset_heading = 359
|
||||
offset_heading = forward_heading - Heading.from_degrees(1)
|
||||
attack_point = self.find_offensive_point(
|
||||
dcs_group, offset_heading, BREAKTHROUGH_OFFENSIVE_DISTANCE
|
||||
)
|
||||
@@ -436,7 +438,7 @@ class GroundConflictGenerator:
|
||||
self,
|
||||
stance: CombatStance,
|
||||
dcs_group: VehicleGroup,
|
||||
forward_heading: int,
|
||||
forward_heading: Heading,
|
||||
to_cp: ControlPoint,
|
||||
) -> bool:
|
||||
"""
|
||||
@@ -473,7 +475,7 @@ class GroundConflictGenerator:
|
||||
stance: CombatStance,
|
||||
ally_groups: List[Tuple[VehicleGroup, CombatGroup]],
|
||||
enemy_groups: List[Tuple[VehicleGroup, CombatGroup]],
|
||||
forward_heading: int,
|
||||
forward_heading: Heading,
|
||||
from_cp: ControlPoint,
|
||||
to_cp: ControlPoint,
|
||||
) -> None:
|
||||
@@ -514,12 +516,14 @@ class GroundConflictGenerator:
|
||||
else:
|
||||
retreat_point = self.find_retreat_point(dcs_group, forward_heading)
|
||||
reposition_point = retreat_point.point_from_heading(
|
||||
forward_heading, 10
|
||||
forward_heading.degrees, 10
|
||||
) # Another point to make the unit face the enemy
|
||||
dcs_group.add_waypoint(retreat_point, PointAction.OffRoad)
|
||||
dcs_group.add_waypoint(reposition_point, PointAction.OffRoad)
|
||||
|
||||
def add_morale_trigger(self, dcs_group: VehicleGroup, forward_heading: int) -> None:
|
||||
def add_morale_trigger(
|
||||
self, dcs_group: VehicleGroup, forward_heading: Heading
|
||||
) -> None:
|
||||
"""
|
||||
This add a trigger to manage units fleeing whenever their group is hit hard, or being engaged by CAS
|
||||
"""
|
||||
@@ -532,7 +536,7 @@ class GroundConflictGenerator:
|
||||
|
||||
# Force unit heading
|
||||
for unit in dcs_group.units:
|
||||
unit.heading = forward_heading
|
||||
unit.heading = forward_heading.degrees
|
||||
dcs_group.manualHeading = True
|
||||
|
||||
# We add a new retreat waypoint
|
||||
@@ -563,7 +567,7 @@ class GroundConflictGenerator:
|
||||
def find_retreat_point(
|
||||
self,
|
||||
dcs_group: VehicleGroup,
|
||||
frontline_heading: int,
|
||||
frontline_heading: Heading,
|
||||
distance: int = RETREAT_DISTANCE,
|
||||
) -> Point:
|
||||
"""
|
||||
@@ -573,14 +577,14 @@ class GroundConflictGenerator:
|
||||
:return: dcs.mapping.Point object with the desired position
|
||||
"""
|
||||
desired_point = dcs_group.points[0].position.point_from_heading(
|
||||
heading_sum(frontline_heading, +180), distance
|
||||
frontline_heading.opposite.degrees, distance
|
||||
)
|
||||
if self.conflict.theater.is_on_land(desired_point):
|
||||
return desired_point
|
||||
return self.conflict.theater.nearest_land_pos(desired_point)
|
||||
|
||||
def find_offensive_point(
|
||||
self, dcs_group: VehicleGroup, frontline_heading: int, distance: int
|
||||
self, dcs_group: VehicleGroup, frontline_heading: Heading, distance: int
|
||||
) -> Point:
|
||||
"""
|
||||
Find a point to attack
|
||||
@@ -590,7 +594,7 @@ class GroundConflictGenerator:
|
||||
:return: dcs.mapping.Point object with the desired position
|
||||
"""
|
||||
desired_point = dcs_group.points[0].position.point_from_heading(
|
||||
frontline_heading, distance
|
||||
frontline_heading.degrees, distance
|
||||
)
|
||||
if self.conflict.theater.is_on_land(desired_point):
|
||||
return desired_point
|
||||
@@ -688,14 +692,14 @@ class GroundConflictGenerator:
|
||||
conflict_position: Point,
|
||||
combat_width: int,
|
||||
distance_from_frontline: int,
|
||||
heading: int,
|
||||
spawn_heading: int,
|
||||
heading: Heading,
|
||||
spawn_heading: Heading,
|
||||
) -> Optional[Point]:
|
||||
shifted = conflict_position.point_from_heading(
|
||||
heading, random.randint(0, combat_width)
|
||||
heading.degrees, random.randint(0, combat_width)
|
||||
)
|
||||
desired_point = shifted.point_from_heading(
|
||||
spawn_heading, distance_from_frontline
|
||||
spawn_heading.degrees, distance_from_frontline
|
||||
)
|
||||
return Conflict.find_ground_position(
|
||||
desired_point, combat_width, heading, self.conflict.theater
|
||||
@@ -704,17 +708,13 @@ class GroundConflictGenerator:
|
||||
def _generate_groups(
|
||||
self,
|
||||
groups: list[CombatGroup],
|
||||
frontline_vector: Tuple[Point, int, int],
|
||||
frontline_vector: Tuple[Point, Heading, int],
|
||||
is_player: bool,
|
||||
) -> List[Tuple[VehicleGroup, CombatGroup]]:
|
||||
"""Finds valid positions for planned groups and generates a pydcs group for them"""
|
||||
positioned_groups = []
|
||||
position, heading, combat_width = frontline_vector
|
||||
spawn_heading = (
|
||||
int(heading_sum(heading, -90))
|
||||
if is_player
|
||||
else int(heading_sum(heading, 90))
|
||||
)
|
||||
spawn_heading = heading.left if is_player else heading.right
|
||||
country = self.game.coalition_for(is_player).country_name
|
||||
for group in groups:
|
||||
if group.role == CombatGroupRole.ARTILLERY:
|
||||
@@ -737,7 +737,7 @@ class GroundConflictGenerator:
|
||||
group.unit_type,
|
||||
group.size,
|
||||
final_position,
|
||||
heading=opposite_heading(spawn_heading),
|
||||
heading=spawn_heading.opposite,
|
||||
)
|
||||
if is_player:
|
||||
g.set_skill(Skill(self.game.settings.player_skill))
|
||||
@@ -750,7 +750,7 @@ class GroundConflictGenerator:
|
||||
g,
|
||||
is_player,
|
||||
self.mission.country(country),
|
||||
opposite_heading(spawn_heading),
|
||||
spawn_heading.opposite,
|
||||
)
|
||||
else:
|
||||
logging.warning(f"Unable to get valid position for {group}")
|
||||
@@ -764,7 +764,7 @@ class GroundConflictGenerator:
|
||||
count: int,
|
||||
at: Point,
|
||||
move_formation: PointAction = PointAction.OffRoad,
|
||||
heading: int = 0,
|
||||
heading: Heading = Heading.from_degrees(0),
|
||||
) -> VehicleGroup:
|
||||
|
||||
if side == self.conflict.attackers_country:
|
||||
@@ -778,7 +778,7 @@ class GroundConflictGenerator:
|
||||
unit_type.dcs_unit_type,
|
||||
position=at,
|
||||
group_size=count,
|
||||
heading=heading,
|
||||
heading=heading.degrees,
|
||||
move_formation=move_formation,
|
||||
)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ from dcs.vehicles import MissilesSS, Unarmed, AirDefence
|
||||
from game import Game
|
||||
from game.factions.faction import Faction
|
||||
from game.theater.theatergroundobject import CoastalSiteGroundObject
|
||||
from game.utils import Heading
|
||||
from gen.sam.group_generator import VehicleGroupGenerator
|
||||
|
||||
|
||||
@@ -59,5 +60,5 @@ class SilkwormGenerator(VehicleGroupGenerator[CoastalSiteGroundObject]):
|
||||
"STRELA#0",
|
||||
self.position.x + 200,
|
||||
self.position.y + 15,
|
||||
90,
|
||||
Heading.from_degrees(90),
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ from shapely.geometry import LineString, Point as ShapelyPoint
|
||||
|
||||
from game.theater.conflicttheater import ConflictTheater, FrontLine
|
||||
from game.theater.controlpoint import ControlPoint
|
||||
from game.utils import heading_sum, opposite_heading
|
||||
from game.utils import Heading
|
||||
|
||||
|
||||
FRONTLINE_LENGTH = 80000
|
||||
@@ -25,7 +25,7 @@ class Conflict:
|
||||
attackers_country: Country,
|
||||
defenders_country: Country,
|
||||
position: Point,
|
||||
heading: Optional[int] = None,
|
||||
heading: Optional[Heading] = None,
|
||||
size: Optional[int] = None,
|
||||
):
|
||||
|
||||
@@ -55,28 +55,28 @@ class Conflict:
|
||||
@classmethod
|
||||
def frontline_position(
|
||||
cls, frontline: FrontLine, theater: ConflictTheater
|
||||
) -> Tuple[Point, int]:
|
||||
attack_heading = int(frontline.attack_heading)
|
||||
) -> Tuple[Point, Heading]:
|
||||
attack_heading = frontline.attack_heading
|
||||
position = cls.find_ground_position(
|
||||
frontline.position,
|
||||
FRONTLINE_LENGTH,
|
||||
heading_sum(attack_heading, 90),
|
||||
attack_heading.right,
|
||||
theater,
|
||||
)
|
||||
if position is None:
|
||||
raise RuntimeError("Could not find front line position")
|
||||
return position, opposite_heading(attack_heading)
|
||||
return position, attack_heading.opposite
|
||||
|
||||
@classmethod
|
||||
def frontline_vector(
|
||||
cls, front_line: FrontLine, theater: ConflictTheater
|
||||
) -> Tuple[Point, int, int]:
|
||||
) -> Tuple[Point, Heading, int]:
|
||||
"""
|
||||
Returns a vector for a valid frontline location avoiding exclusion zones.
|
||||
"""
|
||||
center_position, heading = cls.frontline_position(front_line, theater)
|
||||
left_heading = heading_sum(heading, -90)
|
||||
right_heading = heading_sum(heading, 90)
|
||||
left_heading = heading.left
|
||||
right_heading = heading.right
|
||||
left_position = cls.extend_ground_position(
|
||||
center_position, int(FRONTLINE_LENGTH / 2), left_heading, theater
|
||||
)
|
||||
@@ -113,10 +113,14 @@ class Conflict:
|
||||
|
||||
@classmethod
|
||||
def extend_ground_position(
|
||||
cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater
|
||||
cls,
|
||||
initial: Point,
|
||||
max_distance: int,
|
||||
heading: Heading,
|
||||
theater: ConflictTheater,
|
||||
) -> Point:
|
||||
"""Finds the first intersection with an exclusion zone in one heading from an initial point up to max_distance"""
|
||||
extended = initial.point_from_heading(heading, max_distance)
|
||||
extended = initial.point_from_heading(heading.degrees, max_distance)
|
||||
if theater.landmap is None:
|
||||
# TODO: Why is this possible?
|
||||
return extended
|
||||
@@ -133,14 +137,14 @@ class Conflict:
|
||||
return extended
|
||||
|
||||
# Otherwise extend the front line only up to the intersection.
|
||||
return initial.point_from_heading(heading, p0.distance(intersection))
|
||||
return initial.point_from_heading(heading.degrees, p0.distance(intersection))
|
||||
|
||||
@classmethod
|
||||
def find_ground_position(
|
||||
cls,
|
||||
initial: Point,
|
||||
max_distance: int,
|
||||
heading: int,
|
||||
heading: Heading,
|
||||
theater: ConflictTheater,
|
||||
coerce: bool = True,
|
||||
) -> Optional[Point]:
|
||||
@@ -153,10 +157,10 @@ class Conflict:
|
||||
if theater.is_on_land(pos):
|
||||
return pos
|
||||
for distance in range(0, int(max_distance), 100):
|
||||
pos = initial.point_from_heading(heading, distance)
|
||||
pos = initial.point_from_heading(heading.degrees, distance)
|
||||
if theater.is_on_land(pos):
|
||||
return pos
|
||||
pos = initial.point_from_heading(opposite_heading(heading), distance)
|
||||
pos = initial.point_from_heading(heading.opposite.degrees, distance)
|
||||
if theater.is_on_land(pos):
|
||||
return pos
|
||||
if coerce:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import random
|
||||
|
||||
from gen.sam.group_generator import ShipGroupGenerator
|
||||
from game.utils import Heading
|
||||
|
||||
from dcs.ships import USS_Arleigh_Burke_IIa, TICONDEROG
|
||||
|
||||
@@ -54,7 +55,7 @@ class CarrierGroupGenerator(ShipGroupGenerator):
|
||||
)
|
||||
|
||||
# Add Ticonderoga escort
|
||||
if self.heading >= 180:
|
||||
if self.heading >= Heading.from_degrees(180):
|
||||
self.add_unit(
|
||||
TICONDEROG,
|
||||
"USS Hué City",
|
||||
|
||||
@@ -37,8 +37,10 @@ from game.theater.theatergroundobject import (
|
||||
NavalGroundObject,
|
||||
BuildingGroundObject,
|
||||
)
|
||||
|
||||
from game.threatzones import ThreatZones
|
||||
from game.utils import Distance, Speed, feet, meters, nautical_miles, knots
|
||||
from game.utils import Distance, Heading, Speed, feet, meters, nautical_miles, knots
|
||||
|
||||
from .closestairfields import ObjectiveDistanceCache
|
||||
from .flight import Flight, FlightType, FlightWaypoint, FlightWaypointType
|
||||
from .traveltime import GroundSpeed, TravelTime
|
||||
@@ -1151,10 +1153,11 @@ class FlightPlanBuilder:
|
||||
"""
|
||||
assert self.package.waypoints is not None
|
||||
target = self.package.target.position
|
||||
|
||||
heading = self.package.waypoints.join.heading_between_point(target)
|
||||
heading = Heading.from_degrees(
|
||||
self.package.waypoints.join.heading_between_point(target)
|
||||
)
|
||||
start_pos = target.point_from_heading(
|
||||
heading, -self.doctrine.sweep_distance.meters
|
||||
heading.degrees, -self.doctrine.sweep_distance.meters
|
||||
)
|
||||
|
||||
builder = WaypointBuilder(flight, self.coalition)
|
||||
@@ -1249,7 +1252,9 @@ class FlightPlanBuilder:
|
||||
else:
|
||||
raise PlanningError("Could not find any enemy airfields")
|
||||
|
||||
heading = location.position.heading_between_point(closest_airfield.position)
|
||||
heading = Heading.from_degrees(
|
||||
location.position.heading_between_point(closest_airfield.position)
|
||||
)
|
||||
|
||||
position = ShapelyPoint(
|
||||
self.package.target.position.x, self.package.target.position.y
|
||||
@@ -1285,20 +1290,20 @@ class FlightPlanBuilder:
|
||||
)
|
||||
|
||||
end = location.position.point_from_heading(
|
||||
heading,
|
||||
heading.degrees,
|
||||
random.randint(int(min_cap_distance.meters), int(max_cap_distance.meters)),
|
||||
)
|
||||
diameter = random.randint(
|
||||
int(self.doctrine.cap_min_track_length.meters),
|
||||
int(self.doctrine.cap_max_track_length.meters),
|
||||
)
|
||||
start = end.point_from_heading(heading - 180, diameter)
|
||||
start = end.point_from_heading(heading.opposite.degrees, diameter)
|
||||
return start, end
|
||||
|
||||
def aewc_orbit(self, location: MissionTarget) -> Point:
|
||||
closest_boundary = self.threat_zones.closest_boundary(location.position)
|
||||
heading_to_threat_boundary = location.position.heading_between_point(
|
||||
closest_boundary
|
||||
heading_to_threat_boundary = Heading.from_degrees(
|
||||
location.position.heading_between_point(closest_boundary)
|
||||
)
|
||||
distance_to_threat = meters(
|
||||
location.position.distance_to_point(closest_boundary)
|
||||
@@ -1312,7 +1317,7 @@ class FlightPlanBuilder:
|
||||
orbit_distance = distance_to_threat - threat_buffer
|
||||
|
||||
return location.position.point_from_heading(
|
||||
orbit_heading, orbit_distance.meters
|
||||
orbit_heading.degrees, orbit_distance.meters
|
||||
)
|
||||
|
||||
def racetrack_for_frontline(
|
||||
@@ -1320,9 +1325,9 @@ class FlightPlanBuilder:
|
||||
) -> Tuple[Point, Point]:
|
||||
# Find targets waypoints
|
||||
ingress, heading, distance = Conflict.frontline_vector(front_line, self.theater)
|
||||
center = ingress.point_from_heading(heading, distance / 2)
|
||||
center = ingress.point_from_heading(heading.degrees, distance / 2)
|
||||
orbit_center = center.point_from_heading(
|
||||
heading - 90,
|
||||
heading.left.degrees,
|
||||
random.randint(
|
||||
int(nautical_miles(6).meters), int(nautical_miles(15).meters)
|
||||
),
|
||||
@@ -1335,8 +1340,8 @@ class FlightPlanBuilder:
|
||||
combat_width = 35000
|
||||
|
||||
radius = combat_width * 1.25
|
||||
start = orbit_center.point_from_heading(heading, radius)
|
||||
end = orbit_center.point_from_heading(heading + 180, radius)
|
||||
start = orbit_center.point_from_heading(heading.degrees, radius)
|
||||
end = orbit_center.point_from_heading(heading.opposite.degrees, radius)
|
||||
|
||||
if end.distance_to_point(origin) < start.distance_to_point(origin):
|
||||
start, end = end, start
|
||||
@@ -1530,8 +1535,8 @@ class FlightPlanBuilder:
|
||||
raise InvalidObjectiveLocation(flight.flight_type, location)
|
||||
|
||||
ingress, heading, distance = Conflict.frontline_vector(location, self.theater)
|
||||
center = ingress.point_from_heading(heading, distance / 2)
|
||||
egress = ingress.point_from_heading(heading, distance)
|
||||
center = ingress.point_from_heading(heading.degrees, distance / 2)
|
||||
egress = ingress.point_from_heading(heading.degrees, distance)
|
||||
|
||||
ingress_distance = ingress.distance_to_point(flight.departure.position)
|
||||
egress_distance = egress.distance_to_point(flight.departure.position)
|
||||
@@ -1566,8 +1571,8 @@ class FlightPlanBuilder:
|
||||
location = self.package.target
|
||||
|
||||
closest_boundary = self.threat_zones.closest_boundary(location.position)
|
||||
heading_to_threat_boundary = location.position.heading_between_point(
|
||||
closest_boundary
|
||||
heading_to_threat_boundary = Heading.from_degrees(
|
||||
location.position.heading_between_point(closest_boundary)
|
||||
)
|
||||
distance_to_threat = meters(
|
||||
location.position.distance_to_point(closest_boundary)
|
||||
@@ -1582,16 +1587,16 @@ class FlightPlanBuilder:
|
||||
orbit_distance = distance_to_threat - threat_buffer
|
||||
|
||||
racetrack_center = location.position.point_from_heading(
|
||||
orbit_heading, orbit_distance.meters
|
||||
orbit_heading.degrees, orbit_distance.meters
|
||||
)
|
||||
|
||||
racetrack_half_distance = Distance.from_nautical_miles(20).meters
|
||||
|
||||
racetrack_start = racetrack_center.point_from_heading(
|
||||
orbit_heading + 90, racetrack_half_distance
|
||||
orbit_heading.right.degrees, racetrack_half_distance
|
||||
)
|
||||
racetrack_end = racetrack_center.point_from_heading(
|
||||
orbit_heading - 90, racetrack_half_distance
|
||||
orbit_heading.left.degrees, racetrack_half_distance
|
||||
)
|
||||
|
||||
builder = WaypointBuilder(flight, self.coalition)
|
||||
|
||||
@@ -55,7 +55,7 @@ from game.theater.theatergroundobject import (
|
||||
SceneryGroundObject,
|
||||
)
|
||||
from game.unitmap import UnitMap
|
||||
from game.utils import feet, knots, mps
|
||||
from game.utils import Heading, feet, knots, mps
|
||||
from .radios import RadioFrequency, RadioRegistry
|
||||
from .runways import RunwayData
|
||||
from .tacan import TacanBand, TacanChannel, TacanRegistry
|
||||
@@ -166,7 +166,7 @@ class MissileSiteGenerator(GenericGroundObjectGenerator[MissileSiteGroundObject]
|
||||
if targets:
|
||||
target = random.choice(targets)
|
||||
real_target = target.point_from_heading(
|
||||
random.randint(0, 360), random.randint(0, 2500)
|
||||
Heading.random().degrees, random.randint(0, 2500)
|
||||
)
|
||||
vg.points[0].add_task(FireAtPoint(real_target))
|
||||
logging.info("Set up fire task for missile group.")
|
||||
@@ -246,7 +246,7 @@ class BuildingSiteGenerator(GenericGroundObjectGenerator[BuildingGroundObject]):
|
||||
name=self.ground_object.group_name,
|
||||
_type=unit_type,
|
||||
position=self.ground_object.position,
|
||||
heading=self.ground_object.heading,
|
||||
heading=self.ground_object.heading.degrees,
|
||||
)
|
||||
self._register_fortification(group)
|
||||
|
||||
@@ -256,7 +256,7 @@ class BuildingSiteGenerator(GenericGroundObjectGenerator[BuildingGroundObject]):
|
||||
name=self.ground_object.group_name,
|
||||
_type=static_type,
|
||||
position=self.ground_object.position,
|
||||
heading=self.ground_object.heading,
|
||||
heading=self.ground_object.heading.degrees,
|
||||
dead=self.ground_object.is_dead,
|
||||
)
|
||||
self._register_building(group)
|
||||
@@ -387,7 +387,9 @@ class GenericCarrierGenerator(GenericGroundObjectGenerator[GenericCarrierGroundO
|
||||
# time as the recovery window.
|
||||
brc = self.steam_into_wind(ship_group)
|
||||
self.activate_beacons(ship_group, tacan, tacan_callsign, icls)
|
||||
self.add_runway_data(brc or 0, atc, tacan, tacan_callsign, icls)
|
||||
self.add_runway_data(
|
||||
brc or Heading.from_degrees(0), atc, tacan, tacan_callsign, icls
|
||||
)
|
||||
self._register_unit_group(group, ship_group)
|
||||
|
||||
def get_carrier_type(self, group: ShipGroup) -> Type[ShipType]:
|
||||
@@ -422,14 +424,14 @@ class GenericCarrierGenerator(GenericGroundObjectGenerator[GenericCarrierGroundO
|
||||
ship.set_frequency(atc_channel.hertz)
|
||||
return ship
|
||||
|
||||
def steam_into_wind(self, group: ShipGroup) -> Optional[int]:
|
||||
wind = self.game.conditions.weather.wind.at_0m
|
||||
brc = wind.direction + 180
|
||||
def steam_into_wind(self, group: ShipGroup) -> Optional[Heading]:
|
||||
wind = self.game.conditions.weather.wind.at_0m.direction
|
||||
brc = Heading.from_degrees(wind.direction).opposite
|
||||
# Aim for 25kts over the deck.
|
||||
carrier_speed = knots(25) - mps(wind.speed)
|
||||
for attempt in range(5):
|
||||
point = group.points[0].position.point_from_heading(
|
||||
brc, 100000 - attempt * 20000
|
||||
brc.degrees, 100000 - attempt * 20000
|
||||
)
|
||||
if self.game.theater.is_in_sea(point):
|
||||
group.points[0].speed = carrier_speed.meters_per_second
|
||||
@@ -459,7 +461,7 @@ class GenericCarrierGenerator(GenericGroundObjectGenerator[GenericCarrierGroundO
|
||||
|
||||
def add_runway_data(
|
||||
self,
|
||||
brc: int,
|
||||
brc: Heading,
|
||||
atc: RadioFrequency,
|
||||
tacan: TacanChannel,
|
||||
callsign: str,
|
||||
@@ -593,7 +595,7 @@ class HelipadGenerator:
|
||||
logging.info("Generating helipad : " + name)
|
||||
pad = SingleHeliPad(name=(name + "_unit"))
|
||||
pad.position = Point(helipad.x, helipad.y)
|
||||
pad.heading = helipad.heading
|
||||
pad.heading = helipad.heading.degrees
|
||||
# pad.heliport_frequency = self.radio_registry.alloc_uhf() TODO : alloc radio & callsign
|
||||
sg = unitgroup.StaticGroup(self.m.next_group_id(), name)
|
||||
sg.add_unit(pad)
|
||||
|
||||
@@ -5,6 +5,7 @@ from dcs.vehicles import Unarmed, MissilesSS, AirDefence
|
||||
from game import Game
|
||||
from game.factions.faction import Faction
|
||||
from game.theater.theatergroundobject import MissileSiteGroundObject
|
||||
from game.utils import Heading
|
||||
from gen.sam.group_generator import VehicleGroupGenerator
|
||||
|
||||
|
||||
@@ -63,5 +64,5 @@ class ScudGenerator(VehicleGroupGenerator[MissileSiteGroundObject]):
|
||||
"STRELA#0",
|
||||
self.position.x + 200,
|
||||
self.position.y + 15,
|
||||
90,
|
||||
Heading.from_degrees(90),
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ from dcs.vehicles import Unarmed, MissilesSS, AirDefence
|
||||
from game import Game
|
||||
from game.factions.faction import Faction
|
||||
from game.theater.theatergroundobject import MissileSiteGroundObject
|
||||
from game.utils import Heading
|
||||
from gen.sam.group_generator import VehicleGroupGenerator
|
||||
|
||||
|
||||
@@ -65,5 +66,5 @@ class V1GroupGenerator(VehicleGroupGenerator[MissileSiteGroundObject]):
|
||||
"Blitz#0",
|
||||
self.position.x + 200,
|
||||
self.position.y + 15,
|
||||
90,
|
||||
Heading.from_degrees(90),
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ from typing import Iterator, Optional
|
||||
from dcs.terrain.terrain import Airport
|
||||
|
||||
from game.weather import Conditions
|
||||
from game.utils import Heading
|
||||
from .airfields import AIRFIELD_DATA
|
||||
from .radios import RadioFrequency
|
||||
from .tacan import TacanChannel
|
||||
@@ -16,7 +17,7 @@ from .tacan import TacanChannel
|
||||
@dataclass(frozen=True)
|
||||
class RunwayData:
|
||||
airfield_name: str
|
||||
runway_heading: int
|
||||
runway_heading: Heading
|
||||
runway_name: str
|
||||
atc: Optional[RadioFrequency] = None
|
||||
tacan: Optional[TacanChannel] = None
|
||||
@@ -26,7 +27,7 @@ class RunwayData:
|
||||
|
||||
@classmethod
|
||||
def for_airfield(
|
||||
cls, airport: Airport, runway_heading: int, runway_name: str
|
||||
cls, airport: Airport, runway_heading: Heading, runway_name: str
|
||||
) -> RunwayData:
|
||||
"""Creates RunwayData for the given runway of an airfield.
|
||||
|
||||
@@ -66,12 +67,14 @@ class RunwayData:
|
||||
runway_number = runway.heading // 10
|
||||
runway_side = ["", "L", "R"][runway.leftright]
|
||||
runway_name = f"{runway_number:02}{runway_side}"
|
||||
yield cls.for_airfield(airport, runway.heading, runway_name)
|
||||
yield cls.for_airfield(
|
||||
airport, Heading.from_degrees(runway.heading), runway_name
|
||||
)
|
||||
|
||||
# pydcs only exposes one runway per physical runway, so to expose
|
||||
# both sides of the runway we need to generate the other.
|
||||
heading = (runway.heading + 180) % 360
|
||||
runway_number = heading // 10
|
||||
heading = Heading.from_degrees(runway.heading).opposite
|
||||
runway_number = heading.degrees // 10
|
||||
runway_side = ["", "R", "L"][runway.leftright]
|
||||
runway_name = f"{runway_number:02}{runway_side}"
|
||||
yield cls.for_airfield(airport, heading, runway_name)
|
||||
@@ -81,10 +84,10 @@ class RunwayAssigner:
|
||||
def __init__(self, conditions: Conditions):
|
||||
self.conditions = conditions
|
||||
|
||||
def angle_off_headwind(self, runway: RunwayData) -> int:
|
||||
wind = self.conditions.weather.wind.at_0m.direction
|
||||
ideal_heading = (wind + 180) % 360
|
||||
return abs(runway.runway_heading - ideal_heading)
|
||||
def angle_off_headwind(self, runway: RunwayData) -> Heading:
|
||||
wind = Heading.from_degrees(self.conditions.weather.wind.at_0m.direction)
|
||||
ideal_heading = wind.opposite
|
||||
return runway.runway_heading.angle_between(ideal_heading)
|
||||
|
||||
def get_preferred_runway(self, airport: Airport) -> RunwayData:
|
||||
"""Returns the preferred runway for the given airport.
|
||||
|
||||
@@ -6,6 +6,7 @@ from gen.sam.airdefensegroupgenerator import (
|
||||
AirDefenseRange,
|
||||
AirDefenseGroupGenerator,
|
||||
)
|
||||
from game.utils import Heading
|
||||
|
||||
GFLAK = [
|
||||
AirDefence.Flak38,
|
||||
@@ -88,7 +89,7 @@ class FlakGenerator(AirDefenseGroupGenerator):
|
||||
"BLITZ#" + str(index),
|
||||
self.position.x + 125 + 15 * i + random.randint(1, 5),
|
||||
self.position.y + 15 * j + random.randint(1, 5),
|
||||
75,
|
||||
Heading.from_degrees(75),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -6,6 +6,7 @@ from gen.sam.airdefensegroupgenerator import (
|
||||
AirDefenseRange,
|
||||
AirDefenseGroupGenerator,
|
||||
)
|
||||
from game.utils import Heading
|
||||
|
||||
|
||||
class AllyWW2FlakGenerator(AirDefenseGroupGenerator):
|
||||
@@ -53,28 +54,28 @@ class AllyWW2FlakGenerator(AirDefenseGroupGenerator):
|
||||
"CMD#1",
|
||||
self.position.x,
|
||||
self.position.y - 20,
|
||||
random.randint(0, 360),
|
||||
Heading.random(),
|
||||
)
|
||||
self.add_unit(
|
||||
Unarmed.M30_CC,
|
||||
"LOG#1",
|
||||
self.position.x,
|
||||
self.position.y + 20,
|
||||
random.randint(0, 360),
|
||||
Heading.random(),
|
||||
)
|
||||
self.add_unit(
|
||||
Unarmed.M4_Tractor,
|
||||
"LOG#2",
|
||||
self.position.x + 20,
|
||||
self.position.y,
|
||||
random.randint(0, 360),
|
||||
Heading.random(),
|
||||
)
|
||||
self.add_unit(
|
||||
Unarmed.Bedford_MWD,
|
||||
"LOG#3",
|
||||
self.position.x - 20,
|
||||
self.position.y,
|
||||
random.randint(0, 360),
|
||||
Heading.random(),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -41,7 +41,7 @@ class EarlyColdWarFlakGenerator(AirDefenseGroupGenerator):
|
||||
"SHO#1",
|
||||
self.position.x - 40,
|
||||
self.position.y - 40,
|
||||
self.heading + 180,
|
||||
self.heading.opposite,
|
||||
),
|
||||
self.add_unit(
|
||||
AirDefence.S_60_Type59_Artillery,
|
||||
@@ -57,7 +57,7 @@ class EarlyColdWarFlakGenerator(AirDefenseGroupGenerator):
|
||||
"SHO#3",
|
||||
self.position.x - 80,
|
||||
self.position.y - 40,
|
||||
self.heading + 180,
|
||||
self.heading.opposite,
|
||||
),
|
||||
self.add_unit(
|
||||
AirDefence.ZU_23_Emplacement_Closed,
|
||||
@@ -113,7 +113,7 @@ class ColdWarFlakGenerator(AirDefenseGroupGenerator):
|
||||
"SHO#1",
|
||||
self.position.x - 40,
|
||||
self.position.y - 40,
|
||||
self.heading + 180,
|
||||
self.heading.opposite,
|
||||
),
|
||||
self.add_unit(
|
||||
AirDefence.S_60_Type59_Artillery,
|
||||
@@ -129,7 +129,7 @@ class ColdWarFlakGenerator(AirDefenseGroupGenerator):
|
||||
"SHO#3",
|
||||
self.position.x - 80,
|
||||
self.position.y - 40,
|
||||
self.heading + 180,
|
||||
self.heading.opposite,
|
||||
),
|
||||
self.add_unit(
|
||||
AirDefence.ZU_23_Emplacement_Closed,
|
||||
|
||||
@@ -4,6 +4,7 @@ from gen.sam.airdefensegroupgenerator import (
|
||||
AirDefenseRange,
|
||||
AirDefenseGroupGenerator,
|
||||
)
|
||||
from game.utils import Heading
|
||||
|
||||
|
||||
class FreyaGenerator(AirDefenseGroupGenerator):
|
||||
@@ -101,7 +102,7 @@ class FreyaGenerator(AirDefenseGroupGenerator):
|
||||
"Inf#3",
|
||||
self.position.x + 20,
|
||||
self.position.y - 24,
|
||||
self.heading + 45,
|
||||
self.heading + Heading.from_degrees(45),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -16,6 +16,7 @@ from dcs.unittype import VehicleType, UnitType, ShipType
|
||||
from game.dcs.groundunittype import GroundUnitType
|
||||
from game.factions.faction import Faction
|
||||
from game.theater.theatergroundobject import TheaterGroundObject, NavalGroundObject
|
||||
from game.utils import Heading
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.game import Game
|
||||
@@ -37,7 +38,7 @@ class GroupGenerator(Generic[GroupT, UnitT, UnitTypeT, TgoT]):
|
||||
self.game = game
|
||||
self.go = ground_object
|
||||
self.position = ground_object.position
|
||||
self.heading = random.randint(0, 359)
|
||||
self.heading: Heading = Heading.random()
|
||||
self.price = 0
|
||||
self.vg: GroupT = group
|
||||
|
||||
@@ -53,7 +54,7 @@ class GroupGenerator(Generic[GroupT, UnitT, UnitTypeT, TgoT]):
|
||||
name: str,
|
||||
pos_x: float,
|
||||
pos_y: float,
|
||||
heading: int,
|
||||
heading: Heading,
|
||||
) -> UnitT:
|
||||
return self.add_unit_to_group(
|
||||
self.vg, unit_type, name, Point(pos_x, pos_y), heading
|
||||
@@ -65,7 +66,7 @@ class GroupGenerator(Generic[GroupT, UnitT, UnitTypeT, TgoT]):
|
||||
unit_type: UnitTypeT,
|
||||
name: str,
|
||||
position: Point,
|
||||
heading: int,
|
||||
heading: Heading,
|
||||
) -> UnitT:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -91,11 +92,11 @@ class VehicleGroupGenerator(
|
||||
unit_type: Type[VehicleType],
|
||||
name: str,
|
||||
position: Point,
|
||||
heading: int,
|
||||
heading: Heading,
|
||||
) -> Vehicle:
|
||||
unit = Vehicle(self.game.next_unit_id(), f"{group.name}|{name}", unit_type.id)
|
||||
unit.position = position
|
||||
unit.heading = heading
|
||||
unit.heading = heading.degrees
|
||||
group.add_unit(unit)
|
||||
|
||||
# get price of unit to calculate the real price of the whole group
|
||||
@@ -109,7 +110,7 @@ class VehicleGroupGenerator(
|
||||
|
||||
def get_circular_position(
|
||||
self, num_units: int, launcher_distance: int, coverage: int = 90
|
||||
) -> Iterable[tuple[float, float, int]]:
|
||||
) -> Iterable[tuple[float, float, Heading]]:
|
||||
"""
|
||||
Given a position on the map, array a group of units in a circle a uniform distance from the unit
|
||||
:param num_units:
|
||||
@@ -131,9 +132,9 @@ class VehicleGroupGenerator(
|
||||
positions = []
|
||||
|
||||
if num_units % 2 == 0:
|
||||
current_offset = self.heading - ((coverage / (num_units - 1)) / 2)
|
||||
current_offset = self.heading.degrees - ((coverage / (num_units - 1)) / 2)
|
||||
else:
|
||||
current_offset = self.heading
|
||||
current_offset = self.heading.degrees
|
||||
current_offset -= outer_offset * (math.ceil(num_units / 2) - 1)
|
||||
for _ in range(1, num_units + 1):
|
||||
x: float = self.position.x + launcher_distance * math.cos(
|
||||
@@ -142,8 +143,7 @@ class VehicleGroupGenerator(
|
||||
y: float = self.position.y + launcher_distance * math.sin(
|
||||
math.radians(current_offset)
|
||||
)
|
||||
heading = current_offset
|
||||
positions.append((x, y, int(heading)))
|
||||
positions.append((x, y, Heading.from_degrees(current_offset)))
|
||||
current_offset += outer_offset
|
||||
return positions
|
||||
|
||||
@@ -172,10 +172,10 @@ class ShipGroupGenerator(
|
||||
unit_type: Type[ShipType],
|
||||
name: str,
|
||||
position: Point,
|
||||
heading: int,
|
||||
heading: Heading,
|
||||
) -> Ship:
|
||||
unit = Ship(self.game.next_unit_id(), f"{self.go.group_name}|{name}", unit_type)
|
||||
unit.position = position
|
||||
unit.heading = heading
|
||||
unit.heading = heading.degrees
|
||||
group.add_unit(unit)
|
||||
return unit
|
||||
|
||||
@@ -86,7 +86,7 @@ class VisualGenerator:
|
||||
continue
|
||||
|
||||
for offset in range(0, distance, self.game.settings.perf_smoke_spacing):
|
||||
position = plane_start.point_from_heading(heading, offset)
|
||||
position = plane_start.point_from_heading(heading.degrees, offset)
|
||||
|
||||
for k, v in FRONT_SMOKE_TYPE_CHANCES.items():
|
||||
if random.randint(0, 100) <= k:
|
||||
|
||||
Reference in New Issue
Block a user