Fix save issues after aborting mission.

When the mission is aborted the pending mission is still in the event
list, which is part of the game option. That event has a reference to
the operation, which in turn contains all the mission generator
objects. Two of these objects are the radio/TACAN allocators, which
use a generator to track the next free channel. Generators cannot be
picked, so because these are transitively part of the game object the
game cannot be saved.

Aside from the briefing generator, none of those objects are
actually needed outside the generation function itself, so just make
them locals instead.

This probably needs a larger refactor at some point. It doesn't look
like we need so many calls into the operation type (it has an
initialize, a prepare, and a generate, and it doesn't seem to need
anything but the last one). The only reason breifinggen needs to
remain a part of the class is because the briefing title and
description are filled in from the derived class, where title and
description should probably be overridden properties instead. I'm also
not sure if we need to make the event list a part of game at all, and
also don't think that the mission needs to be one of these events.
This commit is contained in:
Dan Albert 2020-10-04 12:48:35 -07:00
parent e27625556c
commit f040804d02

View File

@ -68,26 +68,8 @@ class Operation:
def initialize(self, mission: Mission, conflict: Conflict): def initialize(self, mission: Mission, conflict: Conflict):
self.current_mission = mission self.current_mission = mission
self.conflict = conflict self.conflict = conflict
self.radio_registry = RadioRegistry() self.briefinggen = BriefingGenerator(self.current_mission,
self.tacan_registry = TacanRegistry() self.conflict, self.game)
self.airgen = AircraftConflictGenerator(
mission, conflict, self.game.settings, self.game,
self.radio_registry)
self.airsupportgen = AirSupportConflictGenerator(
mission, conflict, self.game, self.radio_registry,
self.tacan_registry)
self.triggersgen = TriggersGenerator(mission, conflict, self.game)
self.visualgen = VisualGenerator(mission, conflict, self.game)
self.envgen = EnviromentGenerator(mission, conflict, self.game)
self.forcedoptionsgen = ForcedOptionsGenerator(mission, conflict, self.game)
self.groundobjectgen = GroundObjectsGenerator(
mission,
conflict,
self.game,
self.radio_registry,
self.tacan_registry
)
self.briefinggen = BriefingGenerator(mission, conflict, self.game)
def prepare(self, terrain: Terrain, is_quick: bool): def prepare(self, terrain: Terrain, is_quick: bool):
with open("resources/default_options.lua", "r") as f: with open("resources/default_options.lua", "r") as f:
@ -127,6 +109,9 @@ class Operation:
self.defenders_starting_position = self.to_cp.at self.defenders_starting_position = self.to_cp.at
def generate(self): def generate(self):
radio_registry = RadioRegistry()
tacan_registry = TacanRegistry()
# Dedup beacon/radio frequencies, since some maps have some frequencies # Dedup beacon/radio frequencies, since some maps have some frequencies
# used multiple times. # used multiple times.
beacons = load_beacons_for_terrain(self.game.theater.terrain.name) beacons = load_beacons_for_terrain(self.game.theater.terrain.name)
@ -138,7 +123,7 @@ class Operation:
logging.error( logging.error(
f"TACAN beacon has no channel: {beacon.callsign}") f"TACAN beacon has no channel: {beacon.callsign}")
else: else:
self.tacan_registry.reserve(beacon.tacan_channel) tacan_registry.reserve(beacon.tacan_channel)
for airfield, data in AIRFIELD_DATA.items(): for airfield, data in AIRFIELD_DATA.items():
if data.theater == self.game.theater.terrain.name: if data.theater == self.game.theater.terrain.name:
@ -150,16 +135,26 @@ class Operation:
# beacon list. # beacon list.
for frequency in unique_map_frequencies: for frequency in unique_map_frequencies:
self.radio_registry.reserve(frequency) radio_registry.reserve(frequency)
# Generate meteo # Generate meteo
envgen = EnviromentGenerator(self.current_mission, self.conflict,
self.game)
if self.environment_settings is None: if self.environment_settings is None:
self.environment_settings = self.envgen.generate() self.environment_settings = envgen.generate()
else: else:
self.envgen.load(self.environment_settings) envgen.load(self.environment_settings)
# Generate ground object first # Generate ground object first
self.groundobjectgen.generate()
groundobjectgen = GroundObjectsGenerator(
self.current_mission,
self.conflict,
self.game,
radio_registry,
tacan_registry
)
groundobjectgen.generate()
# Generate destroyed units # Generate destroyed units
for d in self.game.get_destroyed_units(): for d in self.game.get_destroyed_units():
@ -180,11 +175,16 @@ class Operation:
dead=True, dead=True,
) )
# Air Support (Tanker & Awacs) # Air Support (Tanker & Awacs)
self.airsupportgen.generate(self.is_awacs_enabled) airsupportgen = AirSupportConflictGenerator(
self.current_mission, self.conflict, self.game, radio_registry,
tacan_registry)
airsupportgen.generate(self.is_awacs_enabled)
# Generate Activity on the map # Generate Activity on the map
airgen = AircraftConflictGenerator(
self.current_mission, self.conflict, self.game.settings, self.game,
radio_registry)
for cp in self.game.theater.controlpoints: for cp in self.game.theater.controlpoints:
side = cp.captured side = cp.captured
if side: if side:
@ -192,11 +192,11 @@ class Operation:
else: else:
country = self.current_mission.country(self.game.enemy_country) country = self.current_mission.country(self.game.enemy_country)
if cp.id in self.game.planners.keys(): if cp.id in self.game.planners.keys():
self.airgen.generate_flights( airgen.generate_flights(
cp, cp,
country, country,
self.game.planners[cp.id], self.game.planners[cp.id],
self.groundobjectgen.runways groundobjectgen.runways
) )
# Generate ground units on frontline everywhere # Generate ground units on frontline everywhere
@ -221,18 +221,20 @@ class Operation:
self.current_mission.groundControl.red_tactical_commander = self.ca_slots self.current_mission.groundControl.red_tactical_commander = self.ca_slots
# Triggers # Triggers
if self.game.is_player_attack(self.conflict.attackers_country): triggersgen = TriggersGenerator(self.current_mission, self.conflict,
cp = self.conflict.from_cp self.game)
else: triggersgen.generate()
cp = self.conflict.to_cp
self.triggersgen.generate()
# Options # Options
self.forcedoptionsgen.generate() forcedoptionsgen = ForcedOptionsGenerator(self.current_mission,
self.conflict, self.game)
forcedoptionsgen.generate()
# Generate Visuals Smoke Effects # Generate Visuals Smoke Effects
visualgen = VisualGenerator(self.current_mission, self.conflict,
self.game)
if self.game.settings.perf_smoke_gen: if self.game.settings.perf_smoke_gen:
self.visualgen.generate() visualgen.generate()
# Inject Plugins Lua Scripts # Inject Plugins Lua Scripts
listOfPluginsScripts = [] listOfPluginsScripts = []
@ -327,19 +329,20 @@ class Operation:
trigger.add_action(DoScript(String(lua))) trigger.add_action(DoScript(String(lua)))
self.current_mission.triggerrules.triggers.append(trigger) self.current_mission.triggerrules.triggers.append(trigger)
self.assign_channels_to_flights() self.assign_channels_to_flights(airgen.flights,
airsupportgen.air_support)
kneeboard_generator = KneeboardGenerator(self.current_mission) kneeboard_generator = KneeboardGenerator(self.current_mission)
for dynamic_runway in self.groundobjectgen.runways.values(): for dynamic_runway in groundobjectgen.runways.values():
self.briefinggen.add_dynamic_runway(dynamic_runway) self.briefinggen.add_dynamic_runway(dynamic_runway)
for tanker in self.airsupportgen.air_support.tankers: for tanker in airsupportgen.air_support.tankers:
self.briefinggen.add_tanker(tanker) self.briefinggen.add_tanker(tanker)
kneeboard_generator.add_tanker(tanker) kneeboard_generator.add_tanker(tanker)
if self.is_awacs_enabled: if self.is_awacs_enabled:
for awacs in self.airsupportgen.air_support.awacs: for awacs in airsupportgen.air_support.awacs:
self.briefinggen.add_awacs(awacs) self.briefinggen.add_awacs(awacs)
kneeboard_generator.add_awacs(awacs) kneeboard_generator.add_awacs(awacs)
@ -347,21 +350,23 @@ class Operation:
self.briefinggen.add_jtac(jtac) self.briefinggen.add_jtac(jtac)
kneeboard_generator.add_jtac(jtac) kneeboard_generator.add_jtac(jtac)
for flight in self.airgen.flights: for flight in airgen.flights:
self.briefinggen.add_flight(flight) self.briefinggen.add_flight(flight)
kneeboard_generator.add_flight(flight) kneeboard_generator.add_flight(flight)
self.briefinggen.generate() self.briefinggen.generate()
kneeboard_generator.generate() kneeboard_generator.generate()
def assign_channels_to_flights(self) -> None: def assign_channels_to_flights(self, flights: List[FlightData],
air_support: AirSupport) -> None:
"""Assigns preset radio channels for client flights.""" """Assigns preset radio channels for client flights."""
for flight in self.airgen.flights: for flight in flights:
if not flight.client_units: if not flight.client_units:
continue continue
self.assign_channels_to_flight(flight) self.assign_channels_to_flight(flight, air_support)
def assign_channels_to_flight(self, flight: FlightData) -> None: def assign_channels_to_flight(self, flight: FlightData,
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
@ -372,4 +377,5 @@ class Operation:
return return
aircraft_data.channel_allocator.assign_channels_for_flight( aircraft_data.channel_allocator.assign_channels_for_flight(
flight, self.airsupportgen.air_support) flight, air_support
)