mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Refactor front line code to make sides explicit.
A was intended to be the blue point and B was intended to be the red point. Make this a part of the name so that's clear, and clean up related code to keep that reliable.
This commit is contained in:
parent
12f474ecbe
commit
b0c24f6e51
14
game/game.py
14
game/game.py
@ -220,11 +220,11 @@ class Game:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _generate_events(self):
|
def _generate_events(self):
|
||||||
for front_line in self.theater.conflicts(True):
|
for front_line in self.theater.conflicts():
|
||||||
self._generate_player_event(
|
self._generate_player_event(
|
||||||
FrontlineAttackEvent,
|
FrontlineAttackEvent,
|
||||||
front_line.control_point_a,
|
front_line.blue_cp,
|
||||||
front_line.control_point_b,
|
front_line.red_cp,
|
||||||
)
|
)
|
||||||
|
|
||||||
def adjust_budget(self, amount: float, player: bool) -> None:
|
def adjust_budget(self, amount: float, player: bool) -> None:
|
||||||
@ -459,12 +459,10 @@ class Game:
|
|||||||
|
|
||||||
# By default, use the existing frontline conflict position
|
# By default, use the existing frontline conflict position
|
||||||
for front_line in self.theater.conflicts():
|
for front_line in self.theater.conflicts():
|
||||||
position = Conflict.frontline_position(
|
position = Conflict.frontline_position(front_line, self.theater)
|
||||||
front_line.control_point_a, front_line.control_point_b, self.theater
|
|
||||||
)
|
|
||||||
zones.append(position[0])
|
zones.append(position[0])
|
||||||
zones.append(front_line.control_point_a.position)
|
zones.append(front_line.blue_cp.position)
|
||||||
zones.append(front_line.control_point_b.position)
|
zones.append(front_line.red_cp.position)
|
||||||
|
|
||||||
for cp in self.theater.controlpoints:
|
for cp in self.theater.controlpoints:
|
||||||
# Don't cull missile sites - their range is long enough to make them
|
# Don't cull missile sites - their range is long enough to make them
|
||||||
|
|||||||
@ -34,7 +34,7 @@ from gen.radios import RadioFrequency, RadioRegistry
|
|||||||
from gen.tacan import TacanRegistry
|
from gen.tacan import TacanRegistry
|
||||||
from gen.triggergen import TRIGGER_RADIUS_MEDIUM, TriggersGenerator
|
from gen.triggergen import TRIGGER_RADIUS_MEDIUM, TriggersGenerator
|
||||||
from .. import db
|
from .. import db
|
||||||
from ..theater import Airfield
|
from ..theater import Airfield, FrontLine
|
||||||
from ..unitmap import UnitMap
|
from ..unitmap import UnitMap
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -76,8 +76,7 @@ class Operation:
|
|||||||
for frontline in cls.game.theater.conflicts():
|
for frontline in cls.game.theater.conflicts():
|
||||||
yield Conflict(
|
yield Conflict(
|
||||||
cls.game.theater,
|
cls.game.theater,
|
||||||
frontline.control_point_a,
|
frontline,
|
||||||
frontline.control_point_b,
|
|
||||||
cls.game.player_name,
|
cls.game.player_name,
|
||||||
cls.game.enemy_name,
|
cls.game.enemy_name,
|
||||||
cls.game.player_country,
|
cls.game.player_country,
|
||||||
@ -95,8 +94,7 @@ class Operation:
|
|||||||
)
|
)
|
||||||
return Conflict(
|
return Conflict(
|
||||||
cls.game.theater,
|
cls.game.theater,
|
||||||
player_cp,
|
FrontLine(player_cp, enemy_cp, cls.game.theater),
|
||||||
enemy_cp,
|
|
||||||
cls.game.player_name,
|
cls.game.player_name,
|
||||||
cls.game.enemy_name,
|
cls.game.enemy_name,
|
||||||
cls.game.player_country,
|
cls.game.player_country,
|
||||||
@ -399,16 +397,15 @@ class Operation:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def _generate_ground_conflicts(cls) -> None:
|
def _generate_ground_conflicts(cls) -> None:
|
||||||
"""For each frontline in the Operation, generate the ground conflicts and JTACs"""
|
"""For each frontline in the Operation, generate the ground conflicts and JTACs"""
|
||||||
for front_line in cls.game.theater.conflicts(True):
|
for front_line in cls.game.theater.conflicts():
|
||||||
player_cp = front_line.control_point_a
|
player_cp = front_line.blue_cp
|
||||||
enemy_cp = front_line.control_point_b
|
enemy_cp = front_line.red_cp
|
||||||
conflict = Conflict.frontline_cas_conflict(
|
conflict = Conflict.frontline_cas_conflict(
|
||||||
cls.game.player_name,
|
cls.game.player_name,
|
||||||
cls.game.enemy_name,
|
cls.game.enemy_name,
|
||||||
cls.current_mission.country(cls.game.player_country),
|
cls.current_mission.country(cls.game.player_country),
|
||||||
cls.current_mission.country(cls.game.enemy_country),
|
cls.current_mission.country(cls.game.enemy_country),
|
||||||
player_cp,
|
front_line,
|
||||||
enemy_cp,
|
|
||||||
cls.game.theater,
|
cls.game.theater,
|
||||||
)
|
)
|
||||||
# Generate frontline ops
|
# Generate frontline ops
|
||||||
|
|||||||
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||||||
import itertools
|
import itertools
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import math
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@ -601,12 +602,12 @@ class ConflictTheater:
|
|||||||
def player_points(self) -> List[ControlPoint]:
|
def player_points(self) -> List[ControlPoint]:
|
||||||
return list(self.control_points_for(player=True))
|
return list(self.control_points_for(player=True))
|
||||||
|
|
||||||
def conflicts(self, from_player=True) -> Iterator[FrontLine]:
|
def conflicts(self) -> Iterator[FrontLine]:
|
||||||
for cp in [x for x in self.controlpoints if x.captured == from_player]:
|
for player_cp in [x for x in self.controlpoints if x.captured]:
|
||||||
for connected_point in [
|
for enemy_cp in [
|
||||||
x for x in cp.connected_points if x.captured != from_player
|
x for x in player_cp.connected_points if not x.is_friendly_to(player_cp)
|
||||||
]:
|
]:
|
||||||
yield FrontLine(cp, connected_point, self)
|
yield FrontLine(player_cp, enemy_cp, self)
|
||||||
|
|
||||||
def enemy_points(self) -> List[ControlPoint]:
|
def enemy_points(self) -> List[ControlPoint]:
|
||||||
return list(self.control_points_for(player=False))
|
return list(self.control_points_for(player=False))
|
||||||
@ -646,36 +647,26 @@ class ConflictTheater:
|
|||||||
Returns a tuple of the two nearest opposing ControlPoints in theater.
|
Returns a tuple of the two nearest opposing ControlPoints in theater.
|
||||||
(player_cp, enemy_cp)
|
(player_cp, enemy_cp)
|
||||||
"""
|
"""
|
||||||
all_cp_min_distances = {}
|
seen = set()
|
||||||
for idx, control_point in enumerate(self.controlpoints):
|
min_distance = math.inf
|
||||||
distances = {}
|
closest_blue = None
|
||||||
closest_distance = None
|
closest_red = None
|
||||||
for i, cp in enumerate(self.controlpoints):
|
for blue_cp in self.player_points():
|
||||||
if i != idx and cp.captured is not control_point.captured:
|
for red_cp in self.enemy_points():
|
||||||
dist = cp.position.distance_to_point(control_point.position)
|
if (blue_cp, red_cp) in seen:
|
||||||
if not closest_distance:
|
continue
|
||||||
closest_distance = dist
|
seen.add((blue_cp, red_cp))
|
||||||
distances[cp.id] = dist
|
seen.add((red_cp, blue_cp))
|
||||||
if dist < closest_distance:
|
|
||||||
distances[cp.id] = dist
|
|
||||||
closest_cp_id = min(distances, key=distances.get) # type: ignore
|
|
||||||
|
|
||||||
all_cp_min_distances[(control_point.id, closest_cp_id)] = distances[
|
dist = red_cp.position.distance_to_point(blue_cp.position)
|
||||||
closest_cp_id
|
if dist < min_distance:
|
||||||
]
|
closest_red = red_cp
|
||||||
closest_opposing_cps = [
|
closest_blue = blue_cp
|
||||||
self.find_control_point_by_id(i)
|
min_distance = dist
|
||||||
for i in min(
|
|
||||||
all_cp_min_distances, key=all_cp_min_distances.get
|
assert closest_blue is not None
|
||||||
) # type: ignore
|
assert closest_red is not None
|
||||||
] # type: List[ControlPoint]
|
return closest_blue, closest_red
|
||||||
assert len(closest_opposing_cps) == 2
|
|
||||||
if closest_opposing_cps[0].captured:
|
|
||||||
return cast(Tuple[ControlPoint, ControlPoint], tuple(closest_opposing_cps))
|
|
||||||
else:
|
|
||||||
return cast(
|
|
||||||
Tuple[ControlPoint, ControlPoint], tuple(reversed(closest_opposing_cps))
|
|
||||||
)
|
|
||||||
|
|
||||||
def find_control_point_by_id(self, id: int) -> ControlPoint:
|
def find_control_point_by_id(self, id: int) -> ControlPoint:
|
||||||
for i in self.controlpoints:
|
for i in self.controlpoints:
|
||||||
@ -923,16 +914,16 @@ class FrontLine(MissionTarget):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
control_point_a: ControlPoint,
|
blue_point: ControlPoint,
|
||||||
control_point_b: ControlPoint,
|
red_point: ControlPoint,
|
||||||
theater: ConflictTheater,
|
theater: ConflictTheater,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.control_point_a = control_point_a
|
self.blue_cp = blue_point
|
||||||
self.control_point_b = control_point_b
|
self.red_cp = red_point
|
||||||
self.segments: List[FrontLineSegment] = []
|
self.segments: List[FrontLineSegment] = []
|
||||||
self.theater = theater
|
self.theater = theater
|
||||||
self._build_segments()
|
self._build_segments()
|
||||||
self.name = f"Front line {control_point_a}/{control_point_b}"
|
self.name = f"Front line {blue_point}/{red_point}"
|
||||||
|
|
||||||
def is_friendly(self, to_player: bool) -> bool:
|
def is_friendly(self, to_player: bool) -> bool:
|
||||||
"""Returns True if the objective is in friendly territory."""
|
"""Returns True if the objective is in friendly territory."""
|
||||||
@ -964,7 +955,7 @@ class FrontLine(MissionTarget):
|
|||||||
@property
|
@property
|
||||||
def control_points(self) -> Tuple[ControlPoint, ControlPoint]:
|
def control_points(self) -> Tuple[ControlPoint, ControlPoint]:
|
||||||
"""Returns a tuple of the two control points."""
|
"""Returns a tuple of the two control points."""
|
||||||
return self.control_point_a, self.control_point_b
|
return self.blue_cp, self.red_cp
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def attack_distance(self):
|
def attack_distance(self):
|
||||||
@ -998,7 +989,7 @@ class FrontLine(MissionTarget):
|
|||||||
Returns a point {distance} away from control_point_a along the frontline segments.
|
Returns a point {distance} away from control_point_a along the frontline segments.
|
||||||
"""
|
"""
|
||||||
if distance < self.segments[0].attack_distance:
|
if distance < self.segments[0].attack_distance:
|
||||||
return self.control_point_a.position.point_from_heading(
|
return self.blue_cp.position.point_from_heading(
|
||||||
self.segments[0].attack_heading, distance
|
self.segments[0].attack_heading, distance
|
||||||
)
|
)
|
||||||
remaining_dist = distance
|
remaining_dist = distance
|
||||||
@ -1016,14 +1007,12 @@ class FrontLine(MissionTarget):
|
|||||||
The distance from point "a" where the conflict should occur
|
The distance from point "a" where the conflict should occur
|
||||||
according to the current strength of each control point
|
according to the current strength of each control point
|
||||||
"""
|
"""
|
||||||
total_strength = (
|
total_strength = self.blue_cp.base.strength + self.red_cp.base.strength
|
||||||
self.control_point_a.base.strength + self.control_point_b.base.strength
|
if self.blue_cp.base.strength == 0:
|
||||||
)
|
|
||||||
if self.control_point_a.base.strength == 0:
|
|
||||||
return self._adjust_for_min_dist(0)
|
return self._adjust_for_min_dist(0)
|
||||||
if self.control_point_b.base.strength == 0:
|
if self.red_cp.base.strength == 0:
|
||||||
return self._adjust_for_min_dist(self.attack_distance)
|
return self._adjust_for_min_dist(self.attack_distance)
|
||||||
strength_pct = self.control_point_a.base.strength / total_strength
|
strength_pct = self.blue_cp.base.strength / total_strength
|
||||||
return self._adjust_for_min_dist(strength_pct * self.attack_distance)
|
return self._adjust_for_min_dist(strength_pct * self.attack_distance)
|
||||||
|
|
||||||
def _adjust_for_min_dist(self, distance: Numeric) -> Numeric:
|
def _adjust_for_min_dist(self, distance: Numeric) -> Numeric:
|
||||||
@ -1044,11 +1033,9 @@ class FrontLine(MissionTarget):
|
|||||||
def _build_segments(self) -> None:
|
def _build_segments(self) -> None:
|
||||||
"""Create line segments for the frontline"""
|
"""Create line segments for the frontline"""
|
||||||
control_point_ids = "|".join(
|
control_point_ids = "|".join(
|
||||||
[str(self.control_point_a.id), str(self.control_point_b.id)]
|
[str(self.blue_cp.id), str(self.red_cp.id)]
|
||||||
) # from_cp.id|to_cp.id
|
) # from_cp.id|to_cp.id
|
||||||
reversed_cp_ids = "|".join(
|
reversed_cp_ids = "|".join([str(self.red_cp.id), str(self.blue_cp.id)])
|
||||||
[str(self.control_point_b.id), str(self.control_point_a.id)]
|
|
||||||
)
|
|
||||||
complex_frontlines = self.theater.frontline_data
|
complex_frontlines = self.theater.frontline_data
|
||||||
if (complex_frontlines) and (
|
if (complex_frontlines) and (
|
||||||
(control_point_ids in complex_frontlines)
|
(control_point_ids in complex_frontlines)
|
||||||
@ -1071,9 +1058,7 @@ class FrontLine(MissionTarget):
|
|||||||
# If no complex frontline has been configured, fall back to the old straight line method.
|
# If no complex frontline has been configured, fall back to the old straight line method.
|
||||||
else:
|
else:
|
||||||
self.segments.append(
|
self.segments.append(
|
||||||
FrontLineSegment(
|
FrontLineSegment(self.blue_cp.position, self.red_cp.position)
|
||||||
self.control_point_a.position, self.control_point_b.position
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@ -92,9 +92,9 @@ class AirSupportConflictGenerator:
|
|||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
player_cp = (
|
player_cp = (
|
||||||
self.conflict.from_cp
|
self.conflict.blue_cp
|
||||||
if self.conflict.from_cp.captured
|
if self.conflict.blue_cp.captured
|
||||||
else self.conflict.to_cp
|
else self.conflict.red_cp
|
||||||
)
|
)
|
||||||
|
|
||||||
fallback_tanker_number = 0
|
fallback_tanker_number = 0
|
||||||
@ -107,8 +107,8 @@ class AirSupportConflictGenerator:
|
|||||||
freq = self.radio_registry.alloc_uhf()
|
freq = self.radio_registry.alloc_uhf()
|
||||||
tacan = self.tacan_registry.alloc_for_band(TacanBand.Y)
|
tacan = self.tacan_registry.alloc_for_band(TacanBand.Y)
|
||||||
tanker_heading = (
|
tanker_heading = (
|
||||||
self.conflict.to_cp.position.heading_between_point(
|
self.conflict.red_cp.position.heading_between_point(
|
||||||
self.conflict.from_cp.position
|
self.conflict.blue_cp.position
|
||||||
)
|
)
|
||||||
+ TANKER_HEADING_OFFSET * i
|
+ TANKER_HEADING_OFFSET * i
|
||||||
)
|
)
|
||||||
|
|||||||
24
gen/armor.py
24
gen/armor.py
@ -134,10 +134,10 @@ class GroundConflictGenerator:
|
|||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
position = Conflict.frontline_position(
|
position = Conflict.frontline_position(
|
||||||
self.conflict.from_cp, self.conflict.to_cp, self.game.theater
|
self.conflict.front_line, self.game.theater
|
||||||
)
|
)
|
||||||
frontline_vector = Conflict.frontline_vector(
|
frontline_vector = Conflict.frontline_vector(
|
||||||
self.conflict.from_cp, self.conflict.to_cp, self.game.theater
|
self.conflict.front_line, self.game.theater
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create player groups at random position
|
# Create player groups at random position
|
||||||
@ -156,21 +156,21 @@ class GroundConflictGenerator:
|
|||||||
player_groups,
|
player_groups,
|
||||||
enemy_groups,
|
enemy_groups,
|
||||||
self.conflict.heading + 90,
|
self.conflict.heading + 90,
|
||||||
self.conflict.from_cp,
|
self.conflict.blue_cp,
|
||||||
self.conflict.to_cp,
|
self.conflict.red_cp,
|
||||||
)
|
)
|
||||||
self.plan_action_for_groups(
|
self.plan_action_for_groups(
|
||||||
self.enemy_stance,
|
self.enemy_stance,
|
||||||
enemy_groups,
|
enemy_groups,
|
||||||
player_groups,
|
player_groups,
|
||||||
self.conflict.heading - 90,
|
self.conflict.heading - 90,
|
||||||
self.conflict.to_cp,
|
self.conflict.red_cp,
|
||||||
self.conflict.from_cp,
|
self.conflict.blue_cp,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add JTAC
|
# Add JTAC
|
||||||
if self.game.player_faction.has_jtac:
|
if self.game.player_faction.has_jtac:
|
||||||
n = "JTAC" + str(self.conflict.from_cp.id) + str(self.conflict.to_cp.id)
|
n = "JTAC" + str(self.conflict.blue_cp.id) + str(self.conflict.red_cp.id)
|
||||||
code = 1688 - len(self.jtacs)
|
code = 1688 - len(self.jtacs)
|
||||||
|
|
||||||
utype = MQ_9_Reaper
|
utype = MQ_9_Reaper
|
||||||
@ -191,7 +191,7 @@ class GroundConflictGenerator:
|
|||||||
OrbitAction(5000, 300, OrbitAction.OrbitPattern.Circle)
|
OrbitAction(5000, 300, OrbitAction.OrbitPattern.Circle)
|
||||||
)
|
)
|
||||||
frontline = (
|
frontline = (
|
||||||
f"Frontline {self.conflict.from_cp.name}/{self.conflict.to_cp.name}"
|
f"Frontline {self.conflict.blue_cp.name}/{self.conflict.red_cp.name}"
|
||||||
)
|
)
|
||||||
# Note: Will need to change if we ever add ground based JTAC.
|
# Note: Will need to change if we ever add ground based JTAC.
|
||||||
callsign = callsign_for_support_unit(jtac)
|
callsign = callsign_for_support_unit(jtac)
|
||||||
@ -213,9 +213,9 @@ class GroundConflictGenerator:
|
|||||||
logging.warning("Could not find infantry position")
|
logging.warning("Could not find infantry position")
|
||||||
return
|
return
|
||||||
if side == self.conflict.attackers_country:
|
if side == self.conflict.attackers_country:
|
||||||
cp = self.conflict.from_cp
|
cp = self.conflict.blue_cp
|
||||||
else:
|
else:
|
||||||
cp = self.conflict.to_cp
|
cp = self.conflict.red_cp
|
||||||
|
|
||||||
if is_player:
|
if is_player:
|
||||||
faction = self.game.player_name
|
faction = self.game.player_name
|
||||||
@ -782,9 +782,9 @@ class GroundConflictGenerator:
|
|||||||
) -> VehicleGroup:
|
) -> VehicleGroup:
|
||||||
|
|
||||||
if side == self.conflict.attackers_country:
|
if side == self.conflict.attackers_country:
|
||||||
cp = self.conflict.from_cp
|
cp = self.conflict.blue_cp
|
||||||
else:
|
else:
|
||||||
cp = self.conflict.to_cp
|
cp = self.conflict.red_cp
|
||||||
|
|
||||||
logging.info("armorgen: {} for {}".format(unit, side.id))
|
logging.info("armorgen: {} for {}".format(unit, side.id))
|
||||||
group = self.mission.vehicle_group(
|
group = self.mission.vehicle_group(
|
||||||
|
|||||||
@ -36,8 +36,8 @@ class CommInfo:
|
|||||||
class FrontLineInfo:
|
class FrontLineInfo:
|
||||||
def __init__(self, front_line: FrontLine):
|
def __init__(self, front_line: FrontLine):
|
||||||
self.front_line: FrontLine = front_line
|
self.front_line: FrontLine = front_line
|
||||||
self.player_base: ControlPoint = front_line.control_point_a
|
self.player_base: ControlPoint = front_line.blue_cp
|
||||||
self.enemy_base: ControlPoint = front_line.control_point_b
|
self.enemy_base: ControlPoint = front_line.red_cp
|
||||||
self.player_zero: bool = self.player_base.base.total_armor == 0
|
self.player_zero: bool = self.player_base.base.total_armor == 0
|
||||||
self.enemy_zero: bool = self.enemy_base.base.total_armor == 0
|
self.enemy_zero: bool = self.enemy_base.base.total_armor == 0
|
||||||
self.advantage: bool = (
|
self.advantage: bool = (
|
||||||
@ -164,7 +164,7 @@ class BriefingGenerator(MissionInfoGenerator):
|
|||||||
|
|
||||||
def _generate_frontline_info(self) -> None:
|
def _generate_frontline_info(self) -> None:
|
||||||
"""Build FrontLineInfo objects from FrontLine type and append to briefing."""
|
"""Build FrontLineInfo objects from FrontLine type and append to briefing."""
|
||||||
for front_line in self.game.theater.conflicts(from_player=True):
|
for front_line in self.game.theater.conflicts():
|
||||||
self.add_frontline(FrontLineInfo(front_line))
|
self.add_frontline(FrontLineInfo(front_line))
|
||||||
|
|
||||||
# TODO: This should determine if runway is friendly through a method more robust than the existing string match
|
# TODO: This should determine if runway is friendly through a method more robust than the existing string match
|
||||||
|
|||||||
@ -17,8 +17,7 @@ class Conflict:
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
theater: ConflictTheater,
|
theater: ConflictTheater,
|
||||||
from_cp: ControlPoint,
|
front_line: FrontLine,
|
||||||
to_cp: ControlPoint,
|
|
||||||
attackers_side: str,
|
attackers_side: str,
|
||||||
defenders_side: str,
|
defenders_side: str,
|
||||||
attackers_country: Country,
|
attackers_country: Country,
|
||||||
@ -33,22 +32,28 @@ class Conflict:
|
|||||||
self.attackers_country = attackers_country
|
self.attackers_country = attackers_country
|
||||||
self.defenders_country = defenders_country
|
self.defenders_country = defenders_country
|
||||||
|
|
||||||
self.from_cp = from_cp
|
self.front_line = front_line
|
||||||
self.to_cp = to_cp
|
|
||||||
self.theater = theater
|
self.theater = theater
|
||||||
self.position = position
|
self.position = position
|
||||||
self.heading = heading
|
self.heading = heading
|
||||||
self.size = size
|
self.size = size
|
||||||
|
|
||||||
|
@property
|
||||||
|
def blue_cp(self) -> ControlPoint:
|
||||||
|
return self.front_line.blue_cp
|
||||||
|
|
||||||
|
@property
|
||||||
|
def red_cp(self) -> ControlPoint:
|
||||||
|
return self.front_line.red_cp
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def has_frontline_between(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> bool:
|
def has_frontline_between(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> bool:
|
||||||
return from_cp.has_frontline and to_cp.has_frontline
|
return from_cp.has_frontline and to_cp.has_frontline
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def frontline_position(
|
def frontline_position(
|
||||||
cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater
|
cls, frontline: FrontLine, theater: ConflictTheater
|
||||||
) -> Tuple[Point, int]:
|
) -> Tuple[Point, int]:
|
||||||
frontline = FrontLine(from_cp, to_cp, theater)
|
|
||||||
attack_heading = frontline.attack_heading
|
attack_heading = frontline.attack_heading
|
||||||
position = cls.find_ground_position(
|
position = cls.find_ground_position(
|
||||||
frontline.position,
|
frontline.position,
|
||||||
@ -60,12 +65,12 @@ class Conflict:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def frontline_vector(
|
def frontline_vector(
|
||||||
cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater
|
cls, front_line: FrontLine, theater: ConflictTheater
|
||||||
) -> Tuple[Point, int, int]:
|
) -> Tuple[Point, int, int]:
|
||||||
"""
|
"""
|
||||||
Returns a vector for a valid frontline location avoiding exclusion zones.
|
Returns a vector for a valid frontline location avoiding exclusion zones.
|
||||||
"""
|
"""
|
||||||
center_position, heading = cls.frontline_position(from_cp, to_cp, theater)
|
center_position, heading = cls.frontline_position(front_line, theater)
|
||||||
left_heading = heading_sum(heading, -90)
|
left_heading = heading_sum(heading, -90)
|
||||||
right_heading = heading_sum(heading, 90)
|
right_heading = heading_sum(heading, 90)
|
||||||
left_position = cls.extend_ground_position(
|
left_position = cls.extend_ground_position(
|
||||||
@ -84,18 +89,16 @@ class Conflict:
|
|||||||
defender_name: str,
|
defender_name: str,
|
||||||
attacker: Country,
|
attacker: Country,
|
||||||
defender: Country,
|
defender: Country,
|
||||||
from_cp: ControlPoint,
|
front_line: FrontLine,
|
||||||
to_cp: ControlPoint,
|
|
||||||
theater: ConflictTheater,
|
theater: ConflictTheater,
|
||||||
):
|
):
|
||||||
assert cls.has_frontline_between(from_cp, to_cp)
|
assert cls.has_frontline_between(front_line.blue_cp, front_line.red_cp)
|
||||||
position, heading, distance = cls.frontline_vector(from_cp, to_cp, theater)
|
position, heading, distance = cls.frontline_vector(front_line, theater)
|
||||||
conflict = cls(
|
conflict = cls(
|
||||||
position=position,
|
position=position,
|
||||||
heading=heading,
|
heading=heading,
|
||||||
theater=theater,
|
theater=theater,
|
||||||
from_cp=from_cp,
|
front_line=front_line,
|
||||||
to_cp=to_cp,
|
|
||||||
attackers_side=attacker_name,
|
attackers_side=attacker_name,
|
||||||
defenders_side=defender_name,
|
defenders_side=defender_name,
|
||||||
attackers_country=attacker,
|
attackers_country=attacker,
|
||||||
|
|||||||
@ -409,13 +409,7 @@ class ObjectiveFinder:
|
|||||||
|
|
||||||
def front_lines(self) -> Iterator[FrontLine]:
|
def front_lines(self) -> Iterator[FrontLine]:
|
||||||
"""Iterates over all active front lines in the theater."""
|
"""Iterates over all active front lines in the theater."""
|
||||||
for cp in self.friendly_control_points():
|
yield from self.game.theater.conflicts()
|
||||||
for connected in cp.connected_points:
|
|
||||||
if connected.is_friendly(self.is_player):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if Conflict.has_frontline_between(cp, connected):
|
|
||||||
yield FrontLine(cp, connected, self.game.theater)
|
|
||||||
|
|
||||||
def vulnerable_control_points(self) -> Iterator[ControlPoint]:
|
def vulnerable_control_points(self) -> Iterator[ControlPoint]:
|
||||||
"""Iterates over friendly CPs that are vulnerable to enemy CPs.
|
"""Iterates over friendly CPs that are vulnerable to enemy CPs.
|
||||||
@ -447,19 +441,19 @@ class ObjectiveFinder:
|
|||||||
|
|
||||||
def convoys(self) -> Iterator[Convoy]:
|
def convoys(self) -> Iterator[Convoy]:
|
||||||
for front_line in self.front_lines():
|
for front_line in self.front_lines():
|
||||||
if front_line.control_point_a.is_friendly(self.is_player):
|
if front_line.blue_cp.is_friendly(self.is_player):
|
||||||
enemy_cp = front_line.control_point_a
|
enemy_cp = front_line.blue_cp
|
||||||
else:
|
else:
|
||||||
enemy_cp = front_line.control_point_b
|
enemy_cp = front_line.red_cp
|
||||||
|
|
||||||
yield from self.game.transfers.convoys.travelling_to(enemy_cp)
|
yield from self.game.transfers.convoys.travelling_to(enemy_cp)
|
||||||
|
|
||||||
def cargo_ships(self) -> Iterator[CargoShip]:
|
def cargo_ships(self) -> Iterator[CargoShip]:
|
||||||
for front_line in self.front_lines():
|
for front_line in self.front_lines():
|
||||||
if front_line.control_point_a.is_friendly(self.is_player):
|
if front_line.blue_cp.is_friendly(self.is_player):
|
||||||
enemy_cp = front_line.control_point_a
|
enemy_cp = front_line.blue_cp
|
||||||
else:
|
else:
|
||||||
enemy_cp = front_line.control_point_b
|
enemy_cp = front_line.red_cp
|
||||||
|
|
||||||
yield from self.game.transfers.cargo_ships.travelling_to(enemy_cp)
|
yield from self.game.transfers.cargo_ships.travelling_to(enemy_cp)
|
||||||
|
|
||||||
|
|||||||
@ -1320,11 +1320,9 @@ class FlightPlanBuilder:
|
|||||||
def racetrack_for_frontline(
|
def racetrack_for_frontline(
|
||||||
self, origin: Point, front_line: FrontLine
|
self, origin: Point, front_line: FrontLine
|
||||||
) -> Tuple[Point, Point]:
|
) -> Tuple[Point, Point]:
|
||||||
ally_cp, enemy_cp = front_line.control_points
|
|
||||||
|
|
||||||
# Find targets waypoints
|
# Find targets waypoints
|
||||||
ingress, heading, distance = Conflict.frontline_vector(
|
ingress, heading, distance = Conflict.frontline_vector(
|
||||||
ally_cp, enemy_cp, self.game.theater
|
front_line, self.game.theater
|
||||||
)
|
)
|
||||||
center = ingress.point_from_heading(heading, distance / 2)
|
center = ingress.point_from_heading(heading, distance / 2)
|
||||||
orbit_center = center.point_from_heading(
|
orbit_center = center.point_from_heading(
|
||||||
@ -1533,7 +1531,7 @@ class FlightPlanBuilder:
|
|||||||
raise InvalidObjectiveLocation(flight.flight_type, location)
|
raise InvalidObjectiveLocation(flight.flight_type, location)
|
||||||
|
|
||||||
ingress, heading, distance = Conflict.frontline_vector(
|
ingress, heading, distance = Conflict.frontline_vector(
|
||||||
location.control_points[0], location.control_points[1], self.game.theater
|
location, self.game.theater
|
||||||
)
|
)
|
||||||
center = ingress.point_from_heading(heading, distance / 2)
|
center = ingress.point_from_heading(heading, distance / 2)
|
||||||
egress = ingress.point_from_heading(heading, distance)
|
egress = ingress.point_from_heading(heading, distance)
|
||||||
|
|||||||
@ -98,13 +98,13 @@ class VisualGenerator:
|
|||||||
|
|
||||||
def _generate_frontline_smokes(self):
|
def _generate_frontline_smokes(self):
|
||||||
for front_line in self.game.theater.conflicts():
|
for front_line in self.game.theater.conflicts():
|
||||||
from_cp = front_line.control_point_a
|
from_cp = front_line.blue_cp
|
||||||
to_cp = front_line.control_point_b
|
to_cp = front_line.red_cp
|
||||||
if from_cp.is_global or to_cp.is_global:
|
if from_cp.is_global or to_cp.is_global:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
plane_start, heading, distance = Conflict.frontline_vector(
|
plane_start, heading, distance = Conflict.frontline_vector(
|
||||||
from_cp, to_cp, self.game.theater
|
front_line, self.game.theater
|
||||||
)
|
)
|
||||||
if not plane_start:
|
if not plane_start:
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -63,22 +63,15 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
|
|||||||
return i + 1
|
return i + 1
|
||||||
|
|
||||||
if self.include_frontlines:
|
if self.include_frontlines:
|
||||||
for cp in self.game.theater.controlpoints:
|
for front_line in self.game.theater.conflicts():
|
||||||
if cp.captured:
|
pos = Conflict.frontline_position(front_line, self.game.theater)[0]
|
||||||
enemy_cp = [
|
|
||||||
ecp
|
|
||||||
for ecp in cp.connected_points
|
|
||||||
if ecp.captured != cp.captured
|
|
||||||
]
|
|
||||||
for ecp in enemy_cp:
|
|
||||||
pos = Conflict.frontline_position(cp, ecp, self.game.theater)[0]
|
|
||||||
wpt = FlightWaypoint(
|
wpt = FlightWaypoint(
|
||||||
FlightWaypointType.CUSTOM,
|
FlightWaypointType.CUSTOM,
|
||||||
pos.x,
|
pos.x,
|
||||||
pos.y,
|
pos.y,
|
||||||
Distance.from_meters(800),
|
Distance.from_meters(800),
|
||||||
)
|
)
|
||||||
wpt.name = "Frontline " + cp.name + "/" + ecp.name + " [CAS]"
|
wpt.name = f"Frontline {front_line.name} [CAS]"
|
||||||
wpt.alt_type = "RADIO"
|
wpt.alt_type = "RADIO"
|
||||||
wpt.pretty_name = wpt.name
|
wpt.pretty_name = wpt.name
|
||||||
wpt.description = "Frontline"
|
wpt.description = "Frontline"
|
||||||
|
|||||||
@ -101,16 +101,16 @@ class QFrontLine(QGraphicsLineItem):
|
|||||||
Dialog.open_new_package_dialog(self.mission_target)
|
Dialog.open_new_package_dialog(self.mission_target)
|
||||||
|
|
||||||
def cheat_forward(self) -> None:
|
def cheat_forward(self) -> None:
|
||||||
self.mission_target.control_point_a.base.affect_strength(0.1)
|
self.mission_target.blue_cp.base.affect_strength(0.1)
|
||||||
self.mission_target.control_point_b.base.affect_strength(-0.1)
|
self.mission_target.red_cp.base.affect_strength(-0.1)
|
||||||
# Clear the ATO to replan missions affected by the front line.
|
# Clear the ATO to replan missions affected by the front line.
|
||||||
self.game_model.game.reset_ato()
|
self.game_model.game.reset_ato()
|
||||||
self.game_model.game.initialize_turn()
|
self.game_model.game.initialize_turn()
|
||||||
GameUpdateSignal.get_instance().updateGame(self.game_model.game)
|
GameUpdateSignal.get_instance().updateGame(self.game_model.game)
|
||||||
|
|
||||||
def cheat_backward(self) -> None:
|
def cheat_backward(self) -> None:
|
||||||
self.mission_target.control_point_a.base.affect_strength(-0.1)
|
self.mission_target.blue_cp.base.affect_strength(-0.1)
|
||||||
self.mission_target.control_point_b.base.affect_strength(0.1)
|
self.mission_target.red_cp.base.affect_strength(0.1)
|
||||||
# Clear the ATO to replan missions affected by the front line.
|
# Clear the ATO to replan missions affected by the front line.
|
||||||
self.game_model.game.reset_ato()
|
self.game_model.game.reset_ato()
|
||||||
self.game_model.game.initialize_turn()
|
self.game_model.game.initialize_turn()
|
||||||
|
|||||||
@ -865,8 +865,8 @@ class QLiberationMap(QGraphicsView):
|
|||||||
a[1],
|
a[1],
|
||||||
b[0],
|
b[0],
|
||||||
b[1],
|
b[1],
|
||||||
frontline.control_point_a,
|
frontline.blue_cp,
|
||||||
frontline.control_point_b,
|
frontline.red_cp,
|
||||||
convoys,
|
convoys,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -914,7 +914,10 @@ class QLiberationMap(QGraphicsView):
|
|||||||
if convoy is not None:
|
if convoy is not None:
|
||||||
convoys.append(convoy)
|
convoys.append(convoy)
|
||||||
|
|
||||||
|
if a.captured:
|
||||||
frontline = FrontLine(a, b, self.game.theater)
|
frontline = FrontLine(a, b, self.game.theater)
|
||||||
|
else:
|
||||||
|
frontline = FrontLine(b, a, self.game.theater)
|
||||||
if a.front_is_active(b):
|
if a.front_is_active(b):
|
||||||
if DisplayOptions.actual_frontline_pos:
|
if DisplayOptions.actual_frontline_pos:
|
||||||
self.draw_actual_frontline(scene, frontline, convoys)
|
self.draw_actual_frontline(scene, frontline, convoys)
|
||||||
@ -947,7 +950,7 @@ class QLiberationMap(QGraphicsView):
|
|||||||
) -> None:
|
) -> None:
|
||||||
self.draw_bezier_frontline(scene, frontline, convoys)
|
self.draw_bezier_frontline(scene, frontline, convoys)
|
||||||
vector = Conflict.frontline_vector(
|
vector = Conflict.frontline_vector(
|
||||||
frontline.control_point_a, frontline.control_point_b, self.game.theater
|
frontline.blue_cp, frontline.red_cp, self.game.theater
|
||||||
)
|
)
|
||||||
left_pos = self._transform_point(vector[0])
|
left_pos = self._transform_point(vector[0])
|
||||||
right_pos = self._transform_point(
|
right_pos = self._transform_point(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user