Rename frontline vector to bounds, add a class.

This isn't actually the data that callers usually want. Most of the
callers just want the bounds. The heading and length are trivially
computed from that. Add a class to contain the result so it's easier to
refactor.
This commit is contained in:
Dan Albert 2022-09-11 14:12:07 -07:00
parent e53a487948
commit b9b1f51957
6 changed files with 62 additions and 58 deletions

View File

@ -78,11 +78,14 @@ class Builder(IBuilder[CasFlightPlan, CasLayout]):
FrontLineConflictDescription,
)
ingress, heading, distance = FrontLineConflictDescription.frontline_vector(
location, self.theater
bounds = FrontLineConflictDescription.frontline_bounds(location, self.theater)
ingress = bounds.left_position
center = ingress.point_from_heading(
bounds.heading_from_left_to_right.degrees, bounds.length / 2
)
egress = ingress.point_from_heading(
bounds.heading_from_left_to_right.degrees, bounds.length
)
center = ingress.point_from_heading(heading.degrees, distance / 2)
egress = ingress.point_from_heading(heading.degrees, distance)
ingress_distance = ingress.distance_to_point(self.flight.departure.position)
egress_distance = egress.distance_to_point(self.flight.departure.position)

View File

@ -15,14 +15,14 @@ from dcs.task import (
)
from dcs.unittype import UnitType
from game.ato.ai_flight_planner_db import AEWC_CAPABLE
from game.callsigns import callsign_for_support_unit
from game.naming import namegen
from game.radio.radios import RadioRegistry
from game.radio.tacan import TacanBand, TacanRegistry, TacanUsage
from game.utils import Heading
from game.ato.ai_flight_planner_db import AEWC_CAPABLE
from .missiondata import MissionData, AwacsInfo, TankerInfo
from .frontlineconflictdescription import FrontLineConflictDescription
from .missiondata import AwacsInfo, MissionData, TankerInfo
if TYPE_CHECKING:
from game import Game

View File

@ -85,18 +85,16 @@ class DrawingsGenerator:
Generate a frontline "line" for each active frontline
"""
for front_line in self.game.theater.conflicts():
(
plane_start,
heading,
distance,
) = FrontLineConflictDescription.frontline_vector(
bounds = FrontLineConflictDescription.frontline_bounds(
front_line, self.game.theater
)
end_point = plane_start.point_from_heading(heading.degrees, distance)
end_point = bounds.left_position.point_from_heading(
bounds.heading_from_left_to_right.degrees, bounds.length
)
shape = self.player_layer.add_line_segment(
plane_start,
end_point - plane_start,
bounds.left_position,
end_point - bounds.left_position,
line_thickness=16,
color=FRONTLINE_COLORS,
line_style=LineStyle.Triangle,

View File

@ -43,9 +43,9 @@ from game.radio.radios import RadioRegistry
from game.theater.controlpoint import ControlPoint
from game.unitmap import UnitMap
from game.utils import Heading
from .missiondata import MissionData, JtacInfo
from .frontlineconflictdescription import FrontLineConflictDescription
from .lasercoderegistry import LaserCodeRegistry
from .missiondata import JtacInfo, MissionData
if TYPE_CHECKING:
from game import Game
@ -100,18 +100,14 @@ class FlotGenerator:
self.conflict.front_line, self.game.theater
)
frontline_vector = FrontLineConflictDescription.frontline_vector(
self.conflict.front_line, self.game.theater
)
# Create player groups at random position
player_groups = self._generate_groups(
self.player_planned_combat_groups, frontline_vector, True
self.player_planned_combat_groups, is_player=True
)
# Create enemy groups at random position
enemy_groups = self._generate_groups(
self.enemy_planned_combat_groups, frontline_vector, False
self.enemy_planned_combat_groups, is_player=False
)
# TODO: Differentiate AirConflict and GroundConflict classes.
@ -698,33 +694,33 @@ class FlotGenerator:
return rg
def get_valid_position_for_group(
self,
conflict_position: Point,
combat_width: int,
distance_from_frontline: int,
heading: Heading,
spawn_heading: Heading,
) -> Optional[Point]:
shifted = conflict_position.point_from_heading(
heading.degrees, random.randint(0, combat_width)
self, distance_from_frontline: int, spawn_heading: Heading
) -> Point | None:
assert self.conflict.heading is not None
assert self.conflict.size is not None
shifted = self.conflict.position.point_from_heading(
self.conflict.heading.degrees,
random.randint(0, self.conflict.size),
)
desired_point = shifted.point_from_heading(
spawn_heading.degrees, distance_from_frontline
)
return FrontLineConflictDescription.find_ground_position(
desired_point, combat_width, heading, self.conflict.theater
desired_point,
self.conflict.size,
self.conflict.heading,
self.conflict.theater,
)
def _generate_groups(
self,
groups: list[CombatGroup],
frontline_vector: Tuple[Point, Heading, int],
is_player: bool,
self, groups: list[CombatGroup], 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 = heading.left if is_player else heading.right
assert self.conflict.heading is not None
spawn_heading = (
self.conflict.heading.left if is_player else self.conflict.heading.right
)
country = self.game.coalition_for(is_player).country_name
for group in groups:
if group.role == CombatGroupRole.ARTILLERY:
@ -738,7 +734,7 @@ class FlotGenerator:
)
final_position = self.get_valid_position_for_group(
position, combat_width, distance_from_frontline, heading, spawn_heading
distance_from_frontline, spawn_heading
)
if final_position is not None:

View File

@ -1,7 +1,8 @@
from __future__ import annotations
import logging
from typing import Tuple, Optional
from dataclasses import dataclass
from typing import Optional, Tuple
from dcs.country import Country
from dcs.mapping import Point
@ -11,10 +12,16 @@ from game.theater.conflicttheater import ConflictTheater, FrontLine
from game.theater.controlpoint import ControlPoint
from game.utils import Heading
FRONTLINE_LENGTH = 80000
@dataclass(frozen=True)
class FrontLineBounds:
left_position: Point
heading_from_left_to_right: Heading
length: int
class FrontLineConflictDescription:
def __init__(
self,
@ -28,7 +35,6 @@ class FrontLineConflictDescription:
heading: Optional[Heading] = None,
size: Optional[int] = None,
):
self.attackers_side = attackers_side
self.defenders_side = defenders_side
self.attackers_country = attackers_country
@ -68,9 +74,9 @@ class FrontLineConflictDescription:
return position, attack_heading.opposite
@classmethod
def frontline_vector(
def frontline_bounds(
cls, front_line: FrontLine, theater: ConflictTheater
) -> Tuple[Point, Heading, int]:
) -> FrontLineBounds:
"""
Returns a vector for a valid frontline location avoiding exclusion zones.
"""
@ -84,7 +90,7 @@ class FrontLineConflictDescription:
center_position, int(FRONTLINE_LENGTH / 2), right_heading, theater
)
distance = int(left_position.distance_to_point(right_position))
return left_position, right_heading, distance
return FrontLineBounds(left_position, right_heading, distance)
@classmethod
def frontline_cas_conflict(
@ -97,17 +103,20 @@ class FrontLineConflictDescription:
theater: ConflictTheater,
) -> FrontLineConflictDescription:
assert cls.has_frontline_between(front_line.blue_cp, front_line.red_cp)
position, heading, distance = cls.frontline_vector(front_line, theater)
# TODO: Break apart the front-line and air conflict descriptions.
# We're wastefully not caching the front-line bounds here because air conflicts
# can't compute bounds, only a position.
bounds = cls.frontline_bounds(front_line, theater)
conflict = cls(
position=position,
heading=heading,
position=bounds.left_position,
heading=bounds.heading_from_left_to_right,
theater=theater,
front_line=front_line,
attackers_side=attacker_name,
defenders_side=defender_name,
attackers_country=attacker,
defenders_country=defender,
size=distance,
size=bounds.length,
)
return conflict

View File

@ -1,7 +1,7 @@
from __future__ import annotations
import random
from typing import TYPE_CHECKING, Any
from typing import Any, TYPE_CHECKING
from dcs.mission import Mission
from dcs.unit import Static
@ -79,18 +79,16 @@ class VisualsGenerator:
if from_cp.is_global or to_cp.is_global:
continue
(
plane_start,
heading,
distance,
) = FrontLineConflictDescription.frontline_vector(
bounds = FrontLineConflictDescription.frontline_bounds(
front_line, self.game.theater
)
if not plane_start:
continue
for offset in range(0, distance, self.game.settings.perf_smoke_spacing):
position = plane_start.point_from_heading(heading.degrees, offset)
for offset in range(
0, bounds.length, self.game.settings.perf_smoke_spacing
):
position = bounds.left_position.point_from_heading(
bounds.heading_from_left_to_right.degrees, offset
)
for k, v in FRONT_SMOKE_TYPE_CHANCES.items():
if random.randint(0, 100) <= k: