Change Operation to a static class

Removed always True "event successful"

Add `AirWarEvent` as the primary game `Event` applied to every miz

Cleanup of `FrontLineAttackEvent`

Change `Operation.is_awacs_enabled` to two bools for each side red/blue
Currently controlled by whether an AWACs is available for the faction
(and only ever true for Blue)
This commit is contained in:
walterroach 2020-11-23 16:46:05 -06:00
parent 63bdbebcaa
commit da17d1e5d1
8 changed files with 182 additions and 206 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):
"""An Event centered on the overall Air War"""
def __str__(self):
return "Frontline attack"

View File

@ -15,10 +15,11 @@ from game.theater import ControlPoint
from gen import AirTaskingOrder from gen import AirTaskingOrder
from gen.ground_forces.combat_stance import CombatStance from gen.ground_forces.combat_stance import CombatStance
from ..unitmap import UnitMap from ..unitmap import UnitMap
from game.operation.operation import Operation
if TYPE_CHECKING: if TYPE_CHECKING:
from ..game import Game from ..game import Game
from game.operation.operation import Operation
DIFFICULTY_LOG_BASE = 1.1 DIFFICULTY_LOG_BASE = 1.1
EVENT_DEPARTURE_MAX_DISTANCE = 340000 EVENT_DEPARTURE_MAX_DISTANCE = 340000
@ -32,21 +33,18 @@ STRONG_DEFEAT_INFLUENCE = 0.5
class Event: class Event:
silent = False silent = False
informational = False informational = False
is_awacs_enabled = False
ca_slots = 0
game = None # type: Game game = None # type: Game
location = None # type: Point location = None # type: Point
from_cp = None # type: ControlPoint from_cp = None # type: ControlPoint
to_cp = None # type: ControlPoint to_cp = None # type: ControlPoint
operation = None # type: Operation operation = Operation
difficulty = 1 # type: int difficulty = 1 # type: int
BONUS_BASE = 5 BONUS_BASE = 5
def __init__(self, game, from_cp: ControlPoint, target_cp: ControlPoint, location: Point, attacker_name: str, defender_name: str): def __init__(self, game, from_cp: ControlPoint, target_cp: ControlPoint, location: Point, attacker_name: str, defender_name: str):
self.game = game self.game = game
self.departure_cp: Optional[ControlPoint] = None
self.from_cp = from_cp self.from_cp = from_cp
self.to_cp = target_cp self.to_cp = target_cp
self.location = location self.location = location
@ -57,41 +55,14 @@ class Event:
def is_player_attacking(self) -> bool: def is_player_attacking(self) -> bool:
return self.attacker_name == self.game.player_name 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 @property
def tasks(self) -> List[Type[Task]]: def tasks(self) -> List[Type[Task]]:
return [] 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: def bonus(self) -> int:
return int(math.log(self.to_cp.importance + 1, DIFFICULTY_LOG_BASE) * self.BONUS_BASE) 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: def generate(self) -> UnitMap:
self.operation.is_awacs_enabled = self.is_awacs_enabled
self.operation.ca_slots = self.ca_slots self.operation.ca_slots = self.ca_slots
self.operation.prepare(self.game) self.operation.prepare(self.game)

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 from .event import Event
class FrontlineAttackEvent(Event): class FrontlineAttackEvent(Event):
"""
@property An event centered on a FrontLine Conflict.
def tasks(self) -> List[Type[Task]]: Currently the same as its parent, but here for legacy compatibility as well as to allow for
if self.is_player_attacking: future unique Event handling
return [CAS, CAP] """
else:
return [CAP]
@property
def global_cp_available(self) -> bool:
return True
def __str__(self): def __str__(self):
return "Frontline attack" 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): def finish_event(self, event: Event, debriefing: Debriefing):
logging.info("Finishing event {}".format(event)) logging.info("Finishing event {}".format(event))
event.commit(debriefing) event.commit(debriefing)
if event.is_successful(debriefing): self.budget += event.bonus()
self.budget += event.bonus()
if event in self.events: if event in self.events:
self.events.remove(event) self.events.remove(event)
@ -194,7 +193,7 @@ class Game:
if isinstance(event, Event): if isinstance(event, Event):
return event and event.attacker_name and event.attacker_name == self.player_name return event and event.attacker_name and event.attacker_name == self.player_name
else: else:
return event and event.name and event.name == self.player_name raise RuntimeError(f"{event} was passed when an expected")
def on_load(self) -> None: def on_load(self) -> None:
LuaPluginManager.load_settings(self.settings) LuaPluginManager.load_settings(self.settings)

View File

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

View File

@ -471,6 +471,40 @@ class ConflictTheater:
closest_distance = distance closest_distance = distance
return closest return closest
def closest_opposing_control_points(self) -> Tuple[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
if dist < closest_distance:
distances[cp.id] = dist
closest_cp = min(distances, key=distances.get)
all_cp_min_distances[(control_point.id, closest_cp)] = distances[closest_cp]
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]
if closest_opposing_cps[0].captured:
return tuple(closest_opposing_cps)
else:
return 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: def add_json_cp(self, theater, p: dict) -> ControlPoint:
cp: ControlPoint cp: ControlPoint
if p["type"] == "airbase": if p["type"] == "airbase":

View File

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

View File

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