diff --git a/changelog.md b/changelog.md index 69868d60..c6446692 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,7 @@ Saves from 5.x are not compatible with 6.0. * **[Mission Generation]** Added performance option to not cull IADS when culling would effect how mission is played at target area. * **[Mission Generation]** Reworked the ground object generation which now uses a new layout system * **[Mission Generation]** Added information about the modulation (AM/FM) of the assigned frequencies to the kneeboard and assign AM modulation instead of FM for JTAC. +* **[Mission Generation]** Add option to configure the maximum frontline length in settings * **[Factions]** Updated the Faction file structure. Older custom faction files will not work correctly and have to be updated to the new structure. * **[Flight Planning]** Added preset formations for different flight types at hold, join, ingress, and split waypoints. Air to Air flights will tend toward line-abreast and spread-four formations. Air to ground flights will tend towards trail formation. * **[Flight Planning]** Added the ability to plan tankers for recovery on package flights. AI does not plan. diff --git a/game/ato/flightplans/cas.py b/game/ato/flightplans/cas.py index 53d2f780..e44cf571 100644 --- a/game/ato/flightplans/cas.py +++ b/game/ato/flightplans/cas.py @@ -52,9 +52,8 @@ class CasFlightPlan(PatrollingFlightPlan[CasLayout]): @property def engagement_distance(self) -> Distance: - from game.missiongenerator.frontlineconflictdescription import FRONTLINE_LENGTH - - return meters(FRONTLINE_LENGTH) / 2 + max_length = self.flight.coalition.game.settings.max_frontline_length * 1000 + return meters(max_length) / 2 @property def combat_speed_waypoints(self) -> set[FlightWaypoint]: @@ -79,7 +78,7 @@ class Builder(IBuilder[CasFlightPlan, CasLayout]): ) ingress, heading, distance = FrontLineConflictDescription.frontline_vector( - location, self.theater + location, self.theater, self.coalition.game.settings ) center = ingress.point_from_heading(heading.degrees, distance / 2) egress = ingress.point_from_heading(heading.degrees, distance) diff --git a/game/game.py b/game/game.py index d010e162..8b27098a 100644 --- a/game/game.py +++ b/game/game.py @@ -490,7 +490,7 @@ class Game: # By default, use the existing frontline conflict position for front_line in self.theater.conflicts(): position = FrontLineConflictDescription.frontline_position( - front_line, self.theater + front_line, self.theater, self.settings ) zones.append(position[0]) zones.append(front_line.blue_cp.position) diff --git a/game/missiongenerator/drawingsgenerator.py b/game/missiongenerator/drawingsgenerator.py index d5f73876..c2a88424 100644 --- a/game/missiongenerator/drawingsgenerator.py +++ b/game/missiongenerator/drawingsgenerator.py @@ -90,7 +90,7 @@ class DrawingsGenerator: heading, distance, ) = FrontLineConflictDescription.frontline_vector( - front_line, self.game.theater + front_line, self.game.theater, self.game.settings ) end_point = plane_start.point_from_heading(heading.degrees, distance) diff --git a/game/missiongenerator/flotgenerator.py b/game/missiongenerator/flotgenerator.py index 07753dd1..20af789a 100644 --- a/game/missiongenerator/flotgenerator.py +++ b/game/missiongenerator/flotgenerator.py @@ -97,11 +97,11 @@ class FlotGenerator: def generate(self) -> None: position = FrontLineConflictDescription.frontline_position( - self.conflict.front_line, self.game.theater + self.conflict.front_line, self.game.theater, self.game.settings ) frontline_vector = FrontLineConflictDescription.frontline_vector( - self.conflict.front_line, self.game.theater + self.conflict.front_line, self.game.theater, self.game.settings ) # Create player groups at random position diff --git a/game/missiongenerator/frontlineconflictdescription.py b/game/missiongenerator/frontlineconflictdescription.py index d4e0c9bf..4f3faaac 100644 --- a/game/missiongenerator/frontlineconflictdescription.py +++ b/game/missiongenerator/frontlineconflictdescription.py @@ -7,14 +7,12 @@ from dcs.country import Country from dcs.mapping import Point from shapely.geometry import LineString, Point as ShapelyPoint +from game.settings import Settings from game.theater.conflicttheater import ConflictTheater, FrontLine from game.theater.controlpoint import ControlPoint from game.utils import Heading -FRONTLINE_LENGTH = 80000 - - class FrontLineConflictDescription: def __init__( self, @@ -54,12 +52,12 @@ class FrontLineConflictDescription: @classmethod def frontline_position( - cls, frontline: FrontLine, theater: ConflictTheater + cls, frontline: FrontLine, theater: ConflictTheater, settings: Settings ) -> Tuple[Point, Heading]: attack_heading = frontline.attack_heading position = cls.find_ground_position( frontline.position, - FRONTLINE_LENGTH, + settings.max_frontline_length * 1000, attack_heading.right, theater, ) @@ -69,19 +67,25 @@ class FrontLineConflictDescription: @classmethod def frontline_vector( - cls, front_line: FrontLine, theater: ConflictTheater + cls, front_line: FrontLine, theater: ConflictTheater, settings: Settings ) -> Tuple[Point, Heading, int]: """ Returns a vector for a valid frontline location avoiding exclusion zones. """ - center_position, heading = cls.frontline_position(front_line, theater) + center_position, heading = cls.frontline_position(front_line, theater, settings) left_heading = heading.left right_heading = heading.right left_position = cls.extend_ground_position( - center_position, int(FRONTLINE_LENGTH / 2), left_heading, theater + center_position, + int(settings.max_frontline_length * 1000 / 2), + left_heading, + theater, ) right_position = cls.extend_ground_position( - center_position, int(FRONTLINE_LENGTH / 2), right_heading, theater + center_position, + int(settings.max_frontline_length * 1000 / 2), + right_heading, + theater, ) distance = int(left_position.distance_to_point(right_position)) return left_position, right_heading, distance @@ -95,9 +99,12 @@ class FrontLineConflictDescription: defender: Country, front_line: FrontLine, theater: ConflictTheater, + settings: Settings, ) -> FrontLineConflictDescription: assert cls.has_frontline_between(front_line.blue_cp, front_line.red_cp) - position, heading, distance = cls.frontline_vector(front_line, theater) + position, heading, distance = cls.frontline_vector( + front_line, theater, settings + ) conflict = cls( position=position, heading=heading, diff --git a/game/missiongenerator/missiongenerator.py b/game/missiongenerator/missiongenerator.py index 3280c594..2c173423 100644 --- a/game/missiongenerator/missiongenerator.py +++ b/game/missiongenerator/missiongenerator.py @@ -202,6 +202,7 @@ class MissionGenerator: self.mission.country(self.game.red.country_name), front_line, self.game.theater, + self.game.settings, ) # Generate frontline ops player_gp = self.game.ground_planners[player_cp.id].units_per_cp[ diff --git a/game/missiongenerator/visualsgenerator.py b/game/missiongenerator/visualsgenerator.py index ed43a9f8..04245622 100644 --- a/game/missiongenerator/visualsgenerator.py +++ b/game/missiongenerator/visualsgenerator.py @@ -84,7 +84,7 @@ class VisualsGenerator: heading, distance, ) = FrontLineConflictDescription.frontline_vector( - front_line, self.game.theater + front_line, self.game.theater, self.game.settings ) if not plane_start: continue diff --git a/game/settings/settings.py b/game/settings/settings.py index 2dd3934a..3378920a 100644 --- a/game/settings/settings.py +++ b/game/settings/settings.py @@ -394,6 +394,15 @@ class Settings: min=30, max=150, ) + # Mission specific + max_frontline_length: int = bounded_int_option( + "Maximum frontline length (km)", + page=MISSION_GENERATOR_PAGE, + section=GAMEPLAY_SECTION, + default=80, + min=1, + max=100, + ) # Performance perf_smoke_gen: bool = boolean_option(