Merge pull request #437 from walterroach/operation_refactor

Operation refactor
This commit is contained in:
walterroach 2020-11-23 22:36:46 -06:00 committed by GitHub
commit 17fe977b06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 192 additions and 254 deletions

14
game/event/airwar.py Normal file
View File

@ -0,0 +1,14 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from .event import Event
if TYPE_CHECKING:
from game.theater import ConflictTheater
class AirWarEvent(Event):
"""Event handler for the air battle"""
def __str__(self):
return "AirWar"

View File

@ -15,10 +15,11 @@ from game.theater import ControlPoint
from gen import AirTaskingOrder
from gen.ground_forces.combat_stance import CombatStance
from ..unitmap import UnitMap
from game.operation.operation import Operation
if TYPE_CHECKING:
from ..game import Game
from game.operation.operation import Operation
DIFFICULTY_LOG_BASE = 1.1
EVENT_DEPARTURE_MAX_DISTANCE = 340000
@ -32,21 +33,16 @@ STRONG_DEFEAT_INFLUENCE = 0.5
class Event:
silent = False
informational = False
is_awacs_enabled = False
ca_slots = 0
game = None # type: Game
location = None # type: Point
from_cp = None # type: ControlPoint
to_cp = None # type: ControlPoint
operation = None # type: Operation
difficulty = 1 # type: int
BONUS_BASE = 5
def __init__(self, game, from_cp: ControlPoint, target_cp: ControlPoint, location: Point, attacker_name: str, defender_name: str):
self.game = game
self.departure_cp: Optional[ControlPoint] = None
self.from_cp = from_cp
self.to_cp = target_cp
self.location = location
@ -57,46 +53,17 @@ class Event:
def is_player_attacking(self) -> bool:
return self.attacker_name == self.game.player_name
@property
def enemy_cp(self) -> Optional[ControlPoint]:
if self.attacker_name == self.game.player_name:
return self.to_cp
else:
return self.departure_cp
@property
def tasks(self) -> List[Type[Task]]:
return []
@property
def global_cp_available(self) -> bool:
return False
def is_departure_available_from(self, cp: ControlPoint) -> bool:
if not cp.captured:
return False
if self.location.distance_to_point(cp.position) > EVENT_DEPARTURE_MAX_DISTANCE:
return False
if cp.is_global and not self.global_cp_available:
return False
return True
def bonus(self) -> int:
return int(math.log(self.to_cp.importance + 1, DIFFICULTY_LOG_BASE) * self.BONUS_BASE)
def is_successful(self, debriefing: Debriefing) -> bool:
return self.operation.is_successful(debriefing)
def generate(self) -> UnitMap:
self.operation.is_awacs_enabled = self.is_awacs_enabled
self.operation.ca_slots = self.ca_slots
self.operation.prepare(self.game)
unit_map = self.operation.generate()
self.operation.current_mission.save(
Operation.prepare(self.game)
unit_map = Operation.generate()
Operation.current_mission.save(
persistency.mission_path_for("liberation_nextturn.miz"))
return unit_map

View File

@ -1,42 +1,11 @@
from typing import List, Type
from dcs.task import CAP, CAS, Task
from game.operation.operation import Operation
from ..debriefing import Debriefing
from .event import Event
class FrontlineAttackEvent(Event):
@property
def tasks(self) -> List[Type[Task]]:
if self.is_player_attacking:
return [CAS, CAP]
else:
return [CAP]
@property
def global_cp_available(self) -> bool:
return True
"""
An event centered on a FrontLine Conflict.
Currently the same as its parent, but here for legacy compatibility as well as to allow for
future unique Event handling
"""
def __str__(self):
return "Frontline attack"
def is_successful(self, debriefing: Debriefing):
attackers_success = True
if self.from_cp.captured:
return attackers_success
else:
return not attackers_success
def commit(self, debriefing: Debriefing):
super(FrontlineAttackEvent, self).commit(debriefing)
def skip(self):
if self.to_cp.captured:
self.to_cp.base.affect_strength(-0.1)
def player_attacking(self):
assert self.departure_cp is not None
self.operation = Operation(departure_cp=self.departure_cp,)

View File

@ -182,8 +182,7 @@ class Game:
def finish_event(self, event: Event, debriefing: Debriefing):
logging.info("Finishing event {}".format(event))
event.commit(debriefing)
if event.is_successful(debriefing):
self.budget += event.bonus()
self.budget += event.bonus()
if event in self.events:
self.events.remove(event)
@ -194,7 +193,7 @@ class Game:
if isinstance(event, Event):
return event and event.attacker_name and event.attacker_name == self.player_name
else:
return event and event.name and event.name == self.player_name
raise RuntimeError(f"{event} was passed when an Event type was expected")
def on_load(self) -> None:
LuaPluginManager.load_settings(self.settings)

View File

@ -14,9 +14,7 @@ from dcs.lua.parse import loads
from dcs.mapping import Point
from dcs.translation import String
from dcs.triggers import TriggerStart
from dcs.unittype import UnitType
from game.plugins import LuaPluginManager
from game.theater import ControlPoint
from gen import Conflict, FlightType, VisualGenerator
from gen.aircraft import AIRCRAFT_DATA, AircraftConflictGenerator, FlightData
from gen.airfields import AIRFIELD_DATA
@ -41,9 +39,7 @@ if TYPE_CHECKING:
class Operation:
attackers_starting_position = None # type: db.StartingPosition
defenders_starting_position = None # type: db.StartingPosition
"""Static class for managing the final Mission generation"""
current_mission = None # type: Mission
airgen = None # type: AircraftConflictGenerator
triggersgen = None # type: TriggersGenerator
@ -58,16 +54,13 @@ class Operation:
environment_settings = None
trigger_radius = TRIGGER_RADIUS_MEDIUM
is_quick = None
is_awacs_enabled = False
ca_slots = 0
player_awacs_enabled = True
# TODO: #436 Generate Air Support for red
enemy_awacs_enabled = True
ca_slots = 1
unit_map: UnitMap
def __init__(self,
departure_cp: ControlPoint,
):
self.departure_cp = departure_cp
self.plugin_scripts: List[str] = []
self.jtacs: List[JtacInfo] = []
jtacs: List[JtacInfo] = []
plugin_scripts: List[str] = []
@classmethod
def prepare(cls, game: Game):
@ -93,12 +86,6 @@ class Operation:
frontline.position
)
def units_of(self, country_name: str) -> List[UnitType]:
return []
def is_successful(self, debriefing: Debriefing) -> bool:
return True
@classmethod
def _set_mission(cls, mission: Mission) -> None:
cls.current_mission = mission
@ -115,22 +102,25 @@ class Operation:
cls.current_mission.coalition["red"].add_country(
country_dict[db.country_id_from_name(e_country)]())
def inject_lua_trigger(self, contents: str, comment: str) -> None:
@classmethod
def inject_lua_trigger(cls, contents: str, comment: str) -> None:
trigger = TriggerStart(comment=comment)
trigger.add_action(DoScript(String(contents)))
self.current_mission.triggerrules.triggers.append(trigger)
cls.current_mission.triggerrules.triggers.append(trigger)
def bypass_plugin_script(self, mnemonic: str) -> None:
self.plugin_scripts.append(mnemonic)
@classmethod
def bypass_plugin_script(cls, mnemonic: str) -> None:
cls.plugin_scripts.append(mnemonic)
def inject_plugin_script(self, plugin_mnemonic: str, script: str,
@classmethod
def inject_plugin_script(cls, plugin_mnemonic: str, script: str,
script_mnemonic: str) -> None:
if script_mnemonic in self.plugin_scripts:
if script_mnemonic in cls.plugin_scripts:
logging.debug(
f"Skipping already loaded {script} for {plugin_mnemonic}"
)
else:
self.plugin_scripts.append(script_mnemonic)
cls.plugin_scripts.append(script_mnemonic)
plugin_path = Path("./resources/plugins", plugin_mnemonic)
@ -143,13 +133,14 @@ class Operation:
trigger = TriggerStart(comment=f"Load {script_mnemonic}")
filename = script_path.resolve()
fileref = self.current_mission.map_resource.add_resource_file(
fileref = cls.current_mission.map_resource.add_resource_file(
filename)
trigger.add_action(DoScriptFile(fileref))
self.current_mission.triggerrules.triggers.append(trigger)
cls.current_mission.triggerrules.triggers.append(trigger)
@classmethod
def notify_info_generators(
self,
cls,
groundobjectgen: GroundObjectsGenerator,
airsupportgen: AirSupportConflictGenerator,
jtacs: List[JtacInfo],
@ -158,8 +149,8 @@ class Operation:
"""Generates subscribed MissionInfoGenerator objects (currently kneeboards and briefings)
"""
gens: List[MissionInfoGenerator] = [
KneeboardGenerator(self.current_mission, self.game),
BriefingGenerator(self.current_mission, self.game)
KneeboardGenerator(cls.current_mission, cls.game),
BriefingGenerator(cls.current_mission, cls.game)
]
for gen in gens:
for dynamic_runway in groundobjectgen.runways.values():
@ -168,7 +159,7 @@ class Operation:
for tanker in airsupportgen.air_support.tankers:
gen.add_tanker(tanker)
if self.is_awacs_enabled:
if cls.player_awacs_enabled:
for awacs in airsupportgen.air_support.awacs:
gen.add_awacs(awacs)
@ -189,15 +180,17 @@ class Operation:
cls._create_tacan_registry(unique_map_frequencies)
cls._create_radio_registry(unique_map_frequencies)
def assign_channels_to_flights(self, flights: List[FlightData],
@classmethod
def assign_channels_to_flights(cls, flights: List[FlightData],
air_support: AirSupport) -> None:
"""Assigns preset radio channels for client flights."""
for flight in flights:
if not flight.client_units:
continue
self.assign_channels_to_flight(flight, air_support)
cls.assign_channels_to_flight(flight, air_support)
def assign_channels_to_flight(self, flight: FlightData,
@staticmethod
def assign_channels_to_flight(flight: FlightData,
air_support: AirSupport) -> None:
"""Assigns preset radio channels for a client flight."""
airframe = flight.aircraft_type
@ -235,12 +228,11 @@ class Operation:
def _create_radio_registry(cls, unique_map_frequencies: Set[RadioFrequency]) -> None:
cls.radio_registry = RadioRegistry()
for data in AIRFIELD_DATA.values():
if data.theater == cls.game.theater.terrain.name:
if data.atc:
unique_map_frequencies.add(data.atc.hf)
unique_map_frequencies.add(data.atc.vhf_fm)
unique_map_frequencies.add(data.atc.vhf_am)
unique_map_frequencies.add(data.atc.uhf)
if data.theater == cls.game.theater.terrain.name and data.atc:
unique_map_frequencies.add(data.atc.hf)
unique_map_frequencies.add(data.atc.vhf_fm)
unique_map_frequencies.add(data.atc.vhf_am)
unique_map_frequencies.add(data.atc.uhf)
# No need to reserve ILS or TACAN because those are in the
# beacon list.
@ -254,19 +246,20 @@ class Operation:
)
cls.groundobjectgen.generate()
def _generate_destroyed_units(self) -> None:
@classmethod
def _generate_destroyed_units(cls) -> None:
"""Add destroyed units to the Mission"""
for d in self.game.get_destroyed_units():
for d in cls.game.get_destroyed_units():
try:
utype = db.unit_type_from_name(d["type"])
except KeyError:
continue
pos = Point(d["x"], d["z"])
if utype is not None and not self.game.position_culled(pos) and self.game.settings.perf_destroyed_units:
self.current_mission.static_group(
country=self.current_mission.country(
self.game.player_country),
if utype is not None and not cls.game.position_culled(pos) and cls.game.settings.perf_destroyed_units:
cls.current_mission.static_group(
country=cls.current_mission.country(
cls.game.player_country),
name="",
_type=utype,
hidden=True,
@ -274,63 +267,64 @@ class Operation:
heading=d["orientation"],
dead=True,
)
def generate(self) -> UnitMap:
@classmethod
def generate(cls) -> UnitMap:
"""Build the final Mission to be exported"""
self.create_unit_map()
self.create_radio_registries()
cls.create_unit_map()
cls.create_radio_registries()
# Set mission time and weather conditions.
EnvironmentGenerator(self.current_mission,
self.game.conditions).generate()
self._generate_ground_units()
self._generate_destroyed_units()
self._generate_air_units()
self.assign_channels_to_flights(self.airgen.flights,
self.airsupportgen.air_support)
self._generate_ground_conflicts()
EnvironmentGenerator(cls.current_mission,
cls.game.conditions).generate()
cls._generate_ground_units()
cls._generate_destroyed_units()
cls._generate_air_units()
cls.assign_channels_to_flights(cls.airgen.flights,
cls.airsupportgen.air_support)
cls._generate_ground_conflicts()
# TODO: This is silly, once Bulls position is defined without Conflict this should be removed.
default_conflict = [i for i in self.conflicts()][0]
default_conflict = [i for i in cls.conflicts()][0]
# Triggers
triggersgen = TriggersGenerator(self.current_mission, default_conflict,
self.game)
triggersgen = TriggersGenerator(cls.current_mission, default_conflict,
cls.game)
triggersgen.generate()
# Setup combined arms parameters
self.current_mission.groundControl.pilot_can_control_vehicles = self.ca_slots > 0
if self.game.player_country in [country.name for country in self.current_mission.coalition["blue"].countries.values()]:
self.current_mission.groundControl.blue_tactical_commander = self.ca_slots
cls.current_mission.groundControl.pilot_can_control_vehicles = cls.ca_slots > 0
if cls.game.player_country in [country.name for country in cls.current_mission.coalition["blue"].countries.values()]:
cls.current_mission.groundControl.blue_tactical_commander = cls.ca_slots
else:
self.current_mission.groundControl.red_tactical_commander = self.ca_slots
cls.current_mission.groundControl.red_tactical_commander = cls.ca_slots
# Options
forcedoptionsgen = ForcedOptionsGenerator(
self.current_mission, self.game)
cls.current_mission, cls.game)
forcedoptionsgen.generate()
# Generate Visuals Smoke Effects
visualgen = VisualGenerator(self.current_mission, self.game)
if self.game.settings.perf_smoke_gen:
visualgen = VisualGenerator(cls.current_mission, cls.game)
if cls.game.settings.perf_smoke_gen:
visualgen.generate()
self.generate_lua(self.airgen, self.airsupportgen, self.jtacs)
cls.generate_lua(cls.airgen, cls.airsupportgen, cls.jtacs)
# Inject Plugins Lua Scripts and data
for plugin in LuaPluginManager.plugins():
if plugin.enabled:
plugin.inject_scripts(self)
plugin.inject_configuration(self)
plugin.inject_scripts(cls)
plugin.inject_configuration(cls)
self.assign_channels_to_flights(self.airgen.flights,
self.airsupportgen.air_support)
self.notify_info_generators(
self.groundobjectgen,
self.airsupportgen,
self.jtacs,
self.airgen
cls.assign_channels_to_flights(cls.airgen.flights,
cls.airsupportgen.air_support)
cls.notify_info_generators(
cls.groundobjectgen,
cls.airsupportgen,
cls.jtacs,
cls.airgen
)
return self.unit_map
return cls.unit_map
@classmethod
def _generate_air_units(cls) -> None:
@ -343,7 +337,7 @@ class Operation:
cls.airsupportgen = AirSupportConflictGenerator(
cls.current_mission, default_conflict, cls.game, cls.radio_registry,
cls.tacan_registry)
cls.airsupportgen.generate(cls.is_awacs_enabled)
cls.airsupportgen.generate()
# Generate Aircraft Activity on the map
cls.airgen = AircraftConflictGenerator(
@ -364,33 +358,35 @@ class Operation:
cls.current_mission.country(cls.game.player_country),
cls.current_mission.country(cls.game.enemy_country))
def _generate_ground_conflicts(self) -> None:
@classmethod
def _generate_ground_conflicts(cls) -> None:
"""For each frontline in the Operation, generate the ground conflicts and JTACs"""
for front_line in self.game.theater.conflicts(True):
for front_line in cls.game.theater.conflicts(True):
player_cp = front_line.control_point_a
enemy_cp = front_line.control_point_b
conflict = Conflict.frontline_cas_conflict(
self.game.player_name,
self.game.enemy_name,
self.current_mission.country(self.game.player_country),
self.current_mission.country(self.game.enemy_country),
cls.game.player_name,
cls.game.enemy_name,
cls.current_mission.country(cls.game.player_country),
cls.current_mission.country(cls.game.enemy_country),
player_cp,
enemy_cp,
self.game.theater
cls.game.theater
)
# Generate frontline ops
player_gp = self.game.ground_planners[player_cp.id].units_per_cp[enemy_cp.id]
enemy_gp = self.game.ground_planners[enemy_cp.id].units_per_cp[player_cp.id]
player_gp = cls.game.ground_planners[player_cp.id].units_per_cp[enemy_cp.id]
enemy_gp = cls.game.ground_planners[enemy_cp.id].units_per_cp[player_cp.id]
ground_conflict_gen = GroundConflictGenerator(
self.current_mission,
conflict, self.game,
cls.current_mission,
conflict, cls.game,
player_gp, enemy_gp,
player_cp.stances[enemy_cp.id]
)
ground_conflict_gen.generate()
self.jtacs.extend(ground_conflict_gen.jtacs)
cls.jtacs.extend(ground_conflict_gen.jtacs)
def generate_lua(self, airgen: AircraftConflictGenerator,
@classmethod
def generate_lua(cls, airgen: AircraftConflictGenerator,
airsupportgen: AirSupportConflictGenerator,
jtacs: List[JtacInfo]) -> None:
# TODO: Refactor this
@ -411,7 +407,7 @@ class Operation:
"tacan": str(tanker.tacan.number) + tanker.tacan.band.name
}
if self.is_awacs_enabled:
if airsupportgen.air_support.awacs:
for awacs in airsupportgen.air_support.awacs:
luaData["AWACs"][awacs.callsign] = {
"dcsGroupName": awacs.dcsGroupName,

View File

@ -5,7 +5,7 @@ import logging
import textwrap
from dataclasses import dataclass
from pathlib import Path
from typing import List, Optional, TYPE_CHECKING
from typing import List, Optional, TYPE_CHECKING, Type
from game.settings import Settings
@ -22,7 +22,7 @@ class LuaPluginWorkOrder:
self.mnemonic = mnemonic
self.disable = disable
def work(self, operation: Operation) -> None:
def work(self, operation: Type[Operation]) -> None:
if self.disable:
operation.bypass_plugin_script(self.mnemonic)
else:
@ -144,11 +144,11 @@ class LuaPlugin(PluginSettings):
for option in self.definition.options:
option.set_settings(self.settings)
def inject_scripts(self, operation: Operation) -> None:
def inject_scripts(self, operation: Type[Operation]) -> None:
for work_order in self.definition.work_orders:
work_order.work(operation)
def inject_configuration(self, operation: Operation) -> None:
def inject_configuration(self, operation: Type[Operation]) -> None:
# inject the plugin options
if self.options:
option_decls = []

View File

@ -7,7 +7,7 @@ from dataclasses import dataclass
from functools import cached_property
from itertools import tee
from pathlib import Path
from typing import Any, Dict, Iterator, List, Optional, Tuple, Union
from typing import Any, Dict, Iterator, List, Optional, Tuple, Union, cast
from dcs import Mission
from dcs.countries import (
@ -470,6 +470,43 @@ class ConflictTheater:
closest = control_point
closest_distance = distance
return closest
def closest_opposing_control_points(self) -> Tuple[ControlPoint, ControlPoint]:
"""
Returns a tuple of the two nearest opposing ControlPoints in theater.
(player_cp, enemy_cp)
"""
all_cp_min_distances = {}
for idx, control_point in enumerate(self.controlpoints):
distances = {}
closest_distance = None
for i, cp in enumerate(self.controlpoints):
if i != idx and cp.captured is not control_point.captured:
dist = cp.position.distance_to_point(control_point.position)
if not closest_distance:
closest_distance = dist
distances[cp.id] = dist
if dist < closest_distance:
distances[cp.id] = dist
closest_cp_id = min(distances, key=distances.get)
all_cp_min_distances[(control_point.id, closest_cp_id)] = distances[closest_cp_id]
closest_opposing_cps = [
self.find_control_point_by_id(i)
for i
in min(all_cp_min_distances, key=all_cp_min_distances.get)
] # type: List[ControlPoint]
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:
for i in self.controlpoints:
if i.id == id:
return i
raise RuntimeError(f"Cannot find ControlPoint with ID {id}")
def add_json_cp(self, theater, p: dict) -> ControlPoint:
cp: ControlPoint

View File

@ -1,3 +1,4 @@
import logging
from dataclasses import dataclass, field
from typing import List, Type
@ -67,7 +68,7 @@ class AirSupportConflictGenerator:
def support_tasks(cls) -> List[Type[MainTask]]:
return [Refueling, AWACS]
def generate(self, is_awacs_enabled):
def generate(self):
player_cp = self.conflict.from_cp if self.conflict.from_cp.captured else self.conflict.to_cp
fallback_tanker_number = 0
@ -120,26 +121,26 @@ class AirSupportConflictGenerator:
self.air_support.tankers.append(TankerInfo(str(tanker_group.name), callsign, variant, freq, tacan))
if is_awacs_enabled:
try:
freq = self.radio_registry.alloc_uhf()
awacs_unit = db.find_unittype(AWACS, self.conflict.attackers_side)[0]
awacs_flight = self.mission.awacs_flight(
country=self.mission.country(self.game.player_country),
name=namegen.next_awacs_name(self.mission.country(self.game.player_country)),
plane_type=awacs_unit,
altitude=AWACS_ALT,
airport=None,
position=self.conflict.position.random_point_within(AWACS_DISTANCE, AWACS_DISTANCE),
frequency=freq.mhz,
start_type=StartType.Warm,
)
awacs_flight.set_frequency(freq.mhz)
awacs_unit = db.find_unittype(AWACS, self.conflict.attackers_side)[0]
if awacs_unit:
freq = self.radio_registry.alloc_uhf()
awacs_flight = self.mission.awacs_flight(
country=self.mission.country(self.game.player_country),
name=namegen.next_awacs_name(self.mission.country(self.game.player_country)),
plane_type=awacs_unit,
altitude=AWACS_ALT,
airport=None,
position=self.conflict.position.random_point_within(AWACS_DISTANCE, AWACS_DISTANCE),
frequency=freq.mhz,
start_type=StartType.Warm,
)
awacs_flight.set_frequency(freq.mhz)
awacs_flight.points[0].tasks.append(SetInvisibleCommand(True))
awacs_flight.points[0].tasks.append(SetImmortalCommand(True))
awacs_flight.points[0].tasks.append(SetInvisibleCommand(True))
awacs_flight.points[0].tasks.append(SetImmortalCommand(True))
self.air_support.awacs.append(AwacsInfo(
str(awacs_flight.name), callsign_for_support_unit(awacs_flight), freq))
except:
print("No AWACS for faction")
self.air_support.awacs.append(AwacsInfo(
str(awacs_flight.name), callsign_for_support_unit(awacs_flight), freq))
else:
logging.warning("No AWACS for faction")

View File

@ -8,37 +8,8 @@ from dcs.mapping import Point
from game.theater.conflicttheater import ConflictTheater, FrontLine
from game.theater.controlpoint import ControlPoint
AIR_DISTANCE = 40000
CAPTURE_AIR_ATTACKERS_DISTANCE = 25000
CAPTURE_AIR_DEFENDERS_DISTANCE = 60000
STRIKE_AIR_ATTACKERS_DISTANCE = 45000
STRIKE_AIR_DEFENDERS_DISTANCE = 25000
CAP_CAS_DISTANCE = 10000, 120000
GROUND_INTERCEPT_SPREAD = 5000
GROUND_DISTANCE_FACTOR = 1.4
GROUND_DISTANCE = 2000
GROUND_ATTACK_DISTANCE = 25000, 13000
TRANSPORT_FRONTLINE_DIST = 1800
INTERCEPT_ATTACKERS_HEADING = -45, 45
INTERCEPT_DEFENDERS_HEADING = -10, 10
INTERCEPT_CONFLICT_DISTANCE = 50000
INTERCEPT_ATTACKERS_DISTANCE = 100000
INTERCEPT_MAX_DISTANCE = 160000
INTERCEPT_MIN_DISTANCE = 100000
NAVAL_INTERCEPT_DISTANCE_FACTOR = 1
NAVAL_INTERCEPT_DISTANCE_MAX = 40000
NAVAL_INTERCEPT_STEP = 5000
FRONTLINE_LENGTH = 80000
FRONTLINE_MIN_CP_DISTANCE = 5000
FRONTLINE_DISTANCE_STRENGTH_FACTOR = 0.7
def _opposite_heading(h):
@ -98,10 +69,6 @@ class Conflict:
def opposite_heading(self) -> int:
return _heading_sum(self.heading, 180)
@property
def to_size(self):
return self.to_cp.size * GROUND_DISTANCE_FACTOR
def find_ground_position(self, at: Point, heading: int, max_distance: int = 40000) -> Point:
return Conflict._find_ground_position(at, max_distance, heading, self.theater)

View File

@ -10,7 +10,7 @@ from PySide2.QtWidgets import (
import qt_ui.uiconstants as CONST
from game import Game
from game.event import CAP, CAS, FrontlineAttackEvent
from game.event.airwar import AirWarEvent
from gen.ato import Package
from gen.flights.traveltime import TotEstimator
from qt_ui.models import GameModel
@ -214,26 +214,14 @@ class QTopPanel(QFrame):
if negative_starts:
if not self.confirm_negative_start_time(negative_starts):
return
# TODO: Refactor this nonsense.
game_event = None
for event in self.game.events:
if isinstance(event,
FrontlineAttackEvent) and event.is_player_attacking:
game_event = event
# TODO: Why does this exist?
if game_event is None:
game_event = FrontlineAttackEvent(
self.game,
self.game.theater.controlpoints[0],
self.game.theater.controlpoints[1],
self.game.theater.controlpoints[1].position,
self.game.player_name,
self.game.enemy_name)
game_event.is_awacs_enabled = True
game_event.ca_slots = 1
game_event.departure_cp = self.game.theater.controlpoints[0]
game_event.player_attacking()
closest_cps = self.game.theater.closest_opposing_control_points()
game_event = AirWarEvent(
self.game,
closest_cps[0],
closest_cps[1],
self.game.theater.controlpoints[0].position,
self.game.player_name,
self.game.enemy_name)
unit_map = self.game.initiate_event(game_event)
waiting = QWaitingForMissionResultWindow(game_event, self.game,