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:
Dan Albert 2021-05-07 20:56:39 -07:00
parent 12f474ecbe
commit b0c24f6e51
13 changed files with 125 additions and 154 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
) )

View File

@ -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(

View File

@ -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

View File

@ -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,

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -63,26 +63,19 @@ 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 = [ wpt = FlightWaypoint(
ecp FlightWaypointType.CUSTOM,
for ecp in cp.connected_points pos.x,
if ecp.captured != cp.captured pos.y,
] Distance.from_meters(800),
for ecp in enemy_cp: )
pos = Conflict.frontline_position(cp, ecp, self.game.theater)[0] wpt.name = f"Frontline {front_line.name} [CAS]"
wpt = FlightWaypoint( wpt.alt_type = "RADIO"
FlightWaypointType.CUSTOM, wpt.pretty_name = wpt.name
pos.x, wpt.description = "Frontline"
pos.y, i = add_model_item(i, model, wpt.pretty_name, wpt)
Distance.from_meters(800),
)
wpt.name = "Frontline " + cp.name + "/" + ecp.name + " [CAS]"
wpt.alt_type = "RADIO"
wpt.pretty_name = wpt.name
wpt.description = "Frontline"
i = add_model_item(i, model, wpt.pretty_name, wpt)
if self.include_targets: if self.include_targets:
for cp in self.game.theater.controlpoints: for cp in self.game.theater.controlpoints:

View File

@ -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()

View File

@ -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)
frontline = FrontLine(a, b, self.game.theater) if a.captured:
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(