operation refactoring

This commit is contained in:
walterroach 2020-11-21 17:00:22 -06:00
parent 8889e35f9e
commit f6e0dbbb6a
12 changed files with 268 additions and 234 deletions

View File

@ -92,7 +92,7 @@ class Event:
self.operation.is_awacs_enabled = self.is_awacs_enabled
self.operation.ca_slots = self.ca_slots
self.operation.prepare(self.game.theater.terrain, is_quick=False)
self.operation.prepare(self.game)
self.operation.generate()
self.operation.current_mission.save(persistency.mission_path_for("liberation_nextturn.miz"))
self.environment_settings = self.operation.environment_settings

View File

@ -1,11 +1,10 @@
from typing import List, Type
from dcs.task import CAP, CAS, Task
from game.operation.operation import Operation
from game import db
# from game.operation.frontlineattack import FrontlineAttackOperation
from .event import Event
from ..debriefing import Debriefing
from .event import Event
class FrontlineAttackEvent(Event):
@ -38,12 +37,6 @@ class FrontlineAttackEvent(Event):
if self.to_cp.captured:
self.to_cp.base.affect_strength(-0.1)
def player_attacking(self, flights: db.TaskForceDict):
def player_attacking(self):
assert self.departure_cp is not None
op = FrontlineAttackOperation(game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
from_cp=self.from_cp,
departure_cp=self.departure_cp,
to_cp=self.to_cp)
self.operation = op
self.operation = Operation(departure_cp=self.departure_cp,)

View File

@ -1,7 +1,9 @@
from __future__ import annotations
import logging
import os
from pathlib import Path
from typing import List, Optional, Set
from typing import TYPE_CHECKING, Iterable, List, Optional, Set
from dcs import Mission
from dcs.action import DoScript, DoScriptFile
@ -9,11 +11,9 @@ from dcs.coalition import Coalition
from dcs.countries import country_dict
from dcs.lua.parse import loads
from dcs.mapping import Point
from dcs.terrain.terrain import Terrain
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
@ -30,18 +30,19 @@ from gen.kneeboard import KneeboardGenerator
from gen.radios import RadioFrequency, RadioRegistry
from gen.tacan import TacanRegistry
from gen.triggergen import TRIGGER_RADIUS_MEDIUM, TriggersGenerator
from .. import db
from ..debriefing import Debriefing
if TYPE_CHECKING:
from game import Game
class Operation:
attackers_starting_position = None # type: db.StartingPosition
defenders_starting_position = None # type: db.StartingPosition
current_mission = None # type: Mission
regular_mission = None # type: Mission
quick_mission = None # type: Mission
conflict = None # type: Conflict
airgen = None # type: AircraftConflictGenerator
triggersgen = None # type: TriggersGenerator
airsupportgen = None # type: AirSupportConflictGenerator
@ -51,7 +52,7 @@ class Operation:
forcedoptionsgen = None # type: ForcedOptionsGenerator
radio_registry: Optional[RadioRegistry] = None
tacan_registry: Optional[TacanRegistry] = None
game = None # type: Game
environment_settings = None
trigger_radius = TRIGGER_RADIUS_MEDIUM
is_quick = None
@ -59,23 +60,35 @@ class Operation:
ca_slots = 0
def __init__(self,
game,
attacker_name: str,
defender_name: str,
from_cp: ControlPoint,
departure_cp: ControlPoint,
to_cp: ControlPoint):
self.game = game
self.attacker_name = attacker_name
self.attacker_country = db.FACTIONS[attacker_name].country
self.defender_name = defender_name
self.defender_country = db.FACTIONS[defender_name].country
print(self.defender_country, self.attacker_country)
self.from_cp = from_cp
):
self.departure_cp = departure_cp
self.to_cp = to_cp
self.is_quick = False
self.plugin_scripts: List[str] = []
self.jtacs: List[JtacInfo] = []
@classmethod
def prepare(cls, game: Game):
with open("resources/default_options.lua", "r") as f:
options_dict = loads(f.read())["options"]
cls._set_mission(Mission(game.theater.terrain))
cls.game = game
cls._setup_mission_coalitions()
cls.current_mission.options.load_from_dict(options_dict)
@classmethod
def conflicts(cls) -> Iterable[Conflict]:
assert cls.game
for frontline in cls.game.theater.conflicts():
yield Conflict(
cls.game.theater,
frontline.control_point_a,
frontline.control_point_b,
cls.game.player_name,
cls.game.enemy_name,
cls.game.player_country,
cls.game.enemy_country,
frontline.position
)
def units_of(self, country_name: str) -> List[UnitType]:
return []
@ -87,51 +100,21 @@ class Operation:
def is_player_attack(self) -> bool:
return self.from_cp.captured
def initialize(self, mission: Mission, conflict: Conflict):
self.current_mission = mission
self.conflict = conflict
# self.briefinggen = BriefingGenerator(self.current_mission, self.game) Is it safe to remove this, or does it also break save compat?
@classmethod
def _set_mission(cls, mission: Mission) -> None:
cls.current_mission = mission
def prepare(self, terrain: Terrain, is_quick: bool):
with open("resources/default_options.lua", "r") as f:
options_dict = loads(f.read())["options"]
@classmethod
def _setup_mission_coalitions(cls):
cls.current_mission.coalition["blue"] = Coalition("blue")
cls.current_mission.coalition["red"] = Coalition("red")
self.current_mission = Mission(terrain)
print(self.game.player_country)
print(country_dict[db.country_id_from_name(self.game.player_country)])
print(country_dict[db.country_id_from_name(self.game.player_country)]())
# Setup coalition :
self.current_mission.coalition["blue"] = Coalition("blue")
self.current_mission.coalition["red"] = Coalition("red")
p_country = self.game.player_country
e_country = self.game.enemy_country
self.current_mission.coalition["blue"].add_country(country_dict[db.country_id_from_name(p_country)]())
self.current_mission.coalition["red"].add_country(country_dict[db.country_id_from_name(e_country)]())
print([c for c in self.current_mission.coalition["blue"].countries.keys()])
print([c for c in self.current_mission.coalition["red"].countries.keys()])
if is_quick:
self.quick_mission = self.current_mission
else:
self.regular_mission = self.current_mission
self.current_mission.options.load_from_dict(options_dict)
self.is_quick = is_quick
if is_quick:
self.attackers_starting_position = None
self.defenders_starting_position = None
else:
self.attackers_starting_position = self.departure_cp.at
# TODO: Is this possible?
if self.to_cp is not None:
self.defenders_starting_position = self.to_cp.at
else:
self.defenders_starting_position = None
p_country = cls.game.player_country
e_country = cls.game.enemy_country
cls.current_mission.coalition["blue"].add_country(
country_dict[db.country_id_from_name(p_country)]())
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:
trigger = TriggerStart(comment=comment)
@ -161,7 +144,8 @@ class Operation:
trigger = TriggerStart(comment=f"Load {script_mnemonic}")
filename = script_path.resolve()
fileref = self.current_mission.map_resource.add_resource_file(filename)
fileref = self.current_mission.map_resource.add_resource_file(
filename)
trigger.add_action(DoScriptFile(fileref))
self.current_mission.triggerrules.triggers.append(trigger)
@ -171,13 +155,13 @@ class Operation:
airsupportgen: AirSupportConflictGenerator,
jtacs: List[JtacInfo],
airgen: AircraftConflictGenerator,
):
):
"""Generates subscribed MissionInfoGenerator objects (currently kneeboards and briefings)
"""
gens: List[MissionInfoGenerator] = [
KneeboardGenerator(self.current_mission, self.game),
BriefingGenerator(self.current_mission, self.game)
]
]
for gen in gens:
for dynamic_runway in groundobjectgen.runways.values():
gen.add_dynamic_runway(dynamic_runway)
@ -195,15 +179,46 @@ class Operation:
for flight in airgen.flights:
gen.add_flight(flight)
gen.generate()
@classmethod
def create_radio_registries(cls) -> None:
unique_map_frequencies = set() # type: Set[RadioFrequency]
cls._create_tacan_registry(unique_map_frequencies)
cls._create_radio_registry(unique_map_frequencies)
def assign_channels_to_flights(self, 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)
def generate(self):
radio_registry = RadioRegistry()
tacan_registry = TacanRegistry()
def assign_channels_to_flight(self, flight: FlightData,
air_support: AirSupport) -> None:
"""Assigns preset radio channels for a client flight."""
airframe = flight.aircraft_type
try:
aircraft_data = AIRCRAFT_DATA[airframe.id]
except KeyError:
logging.warning(f"No aircraft data for {airframe.id}")
return
if aircraft_data.channel_allocator is not None:
aircraft_data.channel_allocator.assign_channels_for_flight(
flight, air_support
)
@classmethod
def _create_tacan_registry(cls, unique_map_frequencies: Set[RadioFrequency]) -> None:
"""
Dedup beacon/radio frequencies, since some maps have some frequencies
used multiple times.
"""
cls.tacan_registry = TacanRegistry()
beacons = load_beacons_for_terrain(cls.game.theater.terrain.name)
# Dedup beacon/radio frequencies, since some maps have some frequencies
# used multiple times.
beacons = load_beacons_for_terrain(self.game.theater.terrain.name)
unique_map_frequencies: Set[RadioFrequency] = set()
for beacon in beacons:
unique_map_frequencies.add(beacon.frequency)
if beacon.is_tacan:
@ -211,36 +226,35 @@ class Operation:
logging.error(
f"TACAN beacon has no channel: {beacon.callsign}")
else:
tacan_registry.reserve(beacon.tacan_channel)
cls.tacan_registry.reserve(beacon.tacan_channel)
for airfield, data in AIRFIELD_DATA.items():
if data.theater == self.game.theater.terrain.name:
@classmethod
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:
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.
unique_map_frequencies: Set[RadioFrequency] = set()
for frequency in unique_map_frequencies:
radio_registry.reserve(frequency)
cls.radio_registry.reserve(frequency)
# Set mission time and weather conditions.
EnvironmentGenerator(self.current_mission,
self.game.conditions).generate()
# Generate ground object first
groundobjectgen = GroundObjectsGenerator(
self.current_mission,
self.conflict,
self.game,
radio_registry,
tacan_registry
@classmethod
def _generate_ground_units(cls):
cls.groundobjectgen = GroundObjectsGenerator(
cls.current_mission,
cls.game,
cls.radio_registry,
cls.tacan_registry
)
groundobjectgen.generate()
cls.groundobjectgen.generate()
# Generate destroyed units
def _generate_destroyed_units(self) -> None:
"""Add destroyed units to the Mission"""
for d in self.game.get_destroyed_units():
try:
utype = db.unit_type_from_name(d["type"])
@ -250,7 +264,8 @@ class Operation:
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),
country=self.current_mission.country(
self.game.player_country),
name="",
_type=utype,
hidden=True,
@ -258,44 +273,25 @@ class Operation:
heading=d["orientation"],
dead=True,
)
def generate(self):
self.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()
# Air Support (Tanker & Awacs)
airsupportgen = AirSupportConflictGenerator(
self.current_mission, self.conflict, self.game, radio_registry,
tacan_registry)
airsupportgen.generate(self.is_awacs_enabled)
# Generate Activity on the map
airgen = AircraftConflictGenerator(
self.current_mission, self.conflict, self.game.settings, self.game,
radio_registry)
airgen.generate_flights(
self.current_mission.country(self.game.player_country),
self.game.blue_ato,
groundobjectgen.runways
)
airgen.generate_flights(
self.current_mission.country(self.game.enemy_country),
self.game.red_ato,
groundobjectgen.runways
)
# Generate ground units on frontline everywhere
jtacs: List[JtacInfo] = []
for front_line in self.game.theater.conflicts(True):
player_cp = front_line.control_point_a
enemy_cp = front_line.control_point_b
conflict = Conflict.frontline_cas_conflict(self.attacker_name, self.defender_name,
self.current_mission.country(self.attacker_country),
self.current_mission.country(self.defender_country),
player_cp, enemy_cp, self.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]
groundConflictGen = GroundConflictGenerator(self.current_mission, conflict, self.game, player_gp, enemy_gp, player_cp.stances[enemy_cp.id])
groundConflictGen.generate()
jtacs.extend(groundConflictGen.jtacs)
# TODO: This is silly, once Bulls position is defined without Conflict this should be removed.
default_conflict = [i for i in self.conflicts()][0]
# Triggers
triggersgen = TriggersGenerator(self.current_mission, default_conflict,
self.game)
triggersgen.generate()
# Setup combined arms parameters
self.current_mission.groundControl.pilot_can_control_vehicles = self.ca_slots > 0
@ -304,34 +300,32 @@ class Operation:
else:
self.current_mission.groundControl.red_tactical_commander = self.ca_slots
# Triggers
triggersgen = TriggersGenerator(self.current_mission, self.conflict,
self.game)
triggersgen.generate()
# Options
forcedoptionsgen = ForcedOptionsGenerator(self.current_mission,
self.conflict, self.game)
forcedoptionsgen = ForcedOptionsGenerator(self.current_mission, self.game)
forcedoptionsgen.generate()
# Generate Visuals Smoke Effects
visualgen = VisualGenerator(self.current_mission, self.conflict,
self.game)
visualgen = VisualGenerator(self.current_mission, self.game)
if self.game.settings.perf_smoke_gen:
visualgen.generate()
luaData = {}
luaData["AircraftCarriers"] = {}
luaData["Tankers"] = {}
luaData["AWACs"] = {}
luaData["JTACs"] = {}
luaData["TargetPoints"] = {}
self.notify_info_generators(
self.groundobjectgen,
self.airsupportgen,
self.jtacs,
self.airgen
)
self.assign_channels_to_flights(airgen.flights,
airsupportgen.air_support)
luaData = {
"AircraftCarriers": {},
"Tankers": {},
"AWACs": {},
"JTACs": {},
"TargetPoints": {},
}
for tanker in airsupportgen.air_support.tankers:
luaData["Tankers"][tanker.callsign] = {
for tanker in self.airsupportgen.air_support.tankers:
luaData["Tankers"][tanker.callsign] = {
"dcsGroupName": tanker.dcsGroupName,
"callsign": tanker.callsign,
"variant": tanker.variant,
@ -340,15 +334,15 @@ class Operation:
}
if self.is_awacs_enabled:
for awacs in airsupportgen.air_support.awacs:
luaData["AWACs"][awacs.callsign] = {
for awacs in self.airsupportgen.air_support.awacs:
luaData["AWACs"][awacs.callsign] = {
"dcsGroupName": awacs.dcsGroupName,
"callsign": awacs.callsign,
"radio": awacs.freq.mhz
}
for jtac in jtacs:
luaData["JTACs"][jtac.callsign] = {
for jtac in self.jtacs:
luaData["JTACs"][jtac.callsign] = {
"dcsGroupName": jtac.dcsGroupName,
"callsign": jtac.callsign,
"zone": jtac.region,
@ -356,7 +350,7 @@ class Operation:
"laserCode": jtac.code
}
for flight in airgen.flights:
for flight in self.airgen.flights:
if flight.friendly and flight.flight_type in [FlightType.ANTISHIP, FlightType.DEAD, FlightType.SEAD, FlightType.STRIKE]:
flightType = flight.flight_type.name
flightTarget = flight.package.target
@ -365,14 +359,15 @@ class Operation:
flightTargetType = None
if hasattr(flightTarget, 'obj_name'):
flightTargetName = flightTarget.obj_name
flightTargetType = flightType + f" TGT ({flightTarget.category})"
flightTargetType = flightType + \
f" TGT ({flightTarget.category})"
elif hasattr(flightTarget, 'name'):
flightTargetName = flightTarget.name
flightTargetType = flightType + " TGT (Airbase)"
luaData["TargetPoints"][flightTargetName] = {
luaData["TargetPoints"][flightTargetName] = {
"name": flightTargetName,
"type": flightTargetType,
"position": { "x": flightTarget.position.x, "y": flightTarget.position.y}
"position": {"x": flightTarget.position.x, "y": flightTarget.position.y}
}
# set a LUA table with data from Liberation that we want to set
@ -398,7 +393,7 @@ dcsLiberation.Tankers = {
"""
for key in luaData["Tankers"]:
data = luaData["Tankers"][key]
dcsGroupName= data["dcsGroupName"]
dcsGroupName = data["dcsGroupName"]
callsign = data["callsign"]
variant = data["variant"]
tacan = data["tacan"]
@ -415,7 +410,7 @@ dcsLiberation.AWACs = {
"""
for key in luaData["AWACs"]:
data = luaData["AWACs"][key]
dcsGroupName= data["dcsGroupName"]
dcsGroupName = data["dcsGroupName"]
callsign = data["callsign"]
radio = data["radio"]
lua += f" {{dcsGroupName='{dcsGroupName}', callsign='{callsign}', radio='{radio}' }}, \n"
@ -430,7 +425,7 @@ dcsLiberation.JTACs = {
"""
for key in luaData["JTACs"]:
data = luaData["JTACs"][key]
dcsGroupName= data["dcsGroupName"]
dcsGroupName = data["dcsGroupName"]
callsign = data["callsign"]
zone = data["zone"]
laserCode = data["laserCode"]
@ -467,7 +462,6 @@ dcsLiberation.TargetPoints = {
"""
trigger = TriggerStart(comment="Set DCS Liberation data")
trigger.add_action(DoScript(String(lua)))
self.current_mission.triggerrules.triggers.append(trigger)
@ -478,30 +472,57 @@ dcsLiberation.TargetPoints = {
plugin.inject_scripts(self)
plugin.inject_configuration(self)
self.assign_channels_to_flights(airgen.flights,
airsupportgen.air_support)
self.notify_info_generators(groundobjectgen, airsupportgen, jtacs, airgen)
@classmethod
def _generate_air_units(cls) -> None:
"""Generate the air units for the Operation"""
# TODO: this is silly, once AirSupportConflictGenerator doesn't require Conflict this can be removed.
default_conflict = [i for i in cls.conflicts()][0]
def assign_channels_to_flights(self, 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)
# Air Support (Tanker & Awacs)
cls.airsupportgen = AirSupportConflictGenerator(
cls.current_mission, default_conflict, cls.game, cls.radio_registry,
cls.tacan_registry)
cls.airsupportgen.generate(cls.is_awacs_enabled)
def assign_channels_to_flight(self, flight: FlightData,
air_support: AirSupport) -> None:
"""Assigns preset radio channels for a client flight."""
airframe = flight.aircraft_type
# Generate Aircraft Activity on the map
cls.airgen = AircraftConflictGenerator(
cls.current_mission, cls.game.settings, cls.game,
cls.radio_registry)
try:
aircraft_data = AIRCRAFT_DATA[airframe.id]
except KeyError:
logging.warning(f"No aircraft data for {airframe.id}")
return
cls.airgen.generate_flights(
cls.current_mission.country(cls.game.player_country),
cls.game.blue_ato,
cls.groundobjectgen.runways
)
cls.airgen.generate_flights(
cls.current_mission.country(cls.game.enemy_country),
cls.game.red_ato,
cls.groundobjectgen.runways
)
if aircraft_data.channel_allocator is not None:
aircraft_data.channel_allocator.assign_channels_for_flight(
flight, air_support
def _generate_ground_conflicts(self) -> None:
"""For each frontline in the Operation, generate the ground conflicts and JTACs"""
self.jtacs: List[JtacInfo] = []
for front_line in self.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),
player_cp,
enemy_cp,
self.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]
ground_conflict_gen = GroundConflictGenerator(
self.current_mission,
conflict, self.game,
player_gp, enemy_gp,
player_cp.stances[enemy_cp.id]
)
ground_conflict_gen.generate()
self.jtacs.extend(ground_conflict_gen.jtacs)

View File

@ -160,6 +160,9 @@ class ControlPoint(MissionTarget):
self.stances: Dict[int, CombatStance] = {}
self.airport = None
self.pending_unit_deliveries: Optional[UnitsDeliveryEvent] = None
def __repr__(self):
return f"<{__class__}: {self.name}>"
@property
def ground_objects(self) -> List[TheaterGroundObject]:

View File

@ -647,12 +647,11 @@ AIRCRAFT_DATA["P-47D-30"] = AIRCRAFT_DATA["P-51D"]
class AircraftConflictGenerator:
def __init__(self, mission: Mission, conflict: Conflict, settings: Settings,
def __init__(self, mission: Mission, settings: Settings,
game: Game, radio_registry: RadioRegistry):
self.m = mission
self.game = game
self.settings = settings
self.conflict = conflict
self.radio_registry = radio_registry
self.flights: List[FlightData] = []

View File

@ -75,10 +75,33 @@ class GroundConflictGenerator:
self.enemy_planned_combat_groups = enemy_planned_combat_groups
self.player_planned_combat_groups = player_planned_combat_groups
self.player_stance = CombatStance(player_stance)
self.enemy_stance = random.choice([CombatStance.AGGRESSIVE, CombatStance.AGGRESSIVE, CombatStance.AGGRESSIVE, CombatStance.ELIMINATION, CombatStance.BREAKTHROUGH]) if len(enemy_planned_combat_groups) > len(player_planned_combat_groups) else random.choice([CombatStance.DEFENSIVE, CombatStance.DEFENSIVE, CombatStance.DEFENSIVE, CombatStance.AMBUSH, CombatStance.AGGRESSIVE])
self.enemy_stance = self._enemy_stance()
self.game = game
self.jtacs: List[JtacInfo] = []
def _enemy_stance(self):
"""Picks the enemy stance according to the number of planned groups on the frontline for each side"""
if len(self.enemy_planned_combat_groups) > len(self.player_planned_combat_groups):
return random.choice(
[
CombatStance.AGGRESSIVE,
CombatStance.AGGRESSIVE,
CombatStance.AGGRESSIVE,
CombatStance.ELIMINATION,
CombatStance.BREAKTHROUGH
]
)
else:
return random.choice(
[
CombatStance.DEFENSIVE,
CombatStance.DEFENSIVE,
CombatStance.DEFENSIVE,
CombatStance.AMBUSH,
CombatStance.AGGRESSIVE
]
)
def _group_point(self, point) -> Point:
distance = random.randint(
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[0]),
@ -266,7 +289,7 @@ class GroundConflictGenerator:
hold_2.number = 3
dcs_group.add_trigger_action(hold_2)
retreat_task = GoToWaypoint(toIndex=3)
retreat_task = GoToWaypoint(to_index=3)
retreat_task.number = 4
dcs_group.add_trigger_action(retreat_task)
@ -362,7 +385,7 @@ class GroundConflictGenerator:
dcs_group.add_waypoint(self.find_retreat_point(dcs_group, forward_heading, (int)(RETREAT_DISTANCE / 8)), PointAction.OffRoad)
# Fallback task
fallback = ControlledTask(GoToWaypoint(toIndex=len(dcs_group.points)))
fallback = ControlledTask(GoToWaypoint(to_index=len(dcs_group.points)))
fallback.enabled = False
dcs_group.add_trigger_action(Hold())
dcs_group.add_trigger_action(fallback)

View File

@ -157,6 +157,24 @@ class Conflict:
return left_position, _heading_sum(heading, 90), int(right_position.distance_to_point(left_position))
@classmethod
def frontline_cas_conflict(cls, attacker_name: str, defender_name: str, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
assert cls.has_frontline_between(from_cp, to_cp)
position, heading, distance = cls.frontline_vector(from_cp, to_cp, theater)
return cls(
position=position,
heading=heading,
distance=distance,
theater=theater,
from_cp=from_cp,
to_cp=to_cp,
attackers_side=attacker_name,
defenders_side=defender_name,
attackers_country=attacker,
defenders_country=defender,
)
@classmethod
def _extend_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point:
pos = initial

View File

@ -1,5 +1,7 @@
from __future__ import annotations
import logging
import typing
from typing import TYPE_CHECKING
from enum import IntEnum
from dcs.mission import Mission
@ -7,6 +9,8 @@ from dcs.forcedoptions import ForcedOptions
from .conflictgen import *
if TYPE_CHECKING:
from game.game import Game
class Labels(IntEnum):
Off = 0
@ -16,9 +20,8 @@ class Labels(IntEnum):
class ForcedOptionsGenerator:
def __init__(self, mission: Mission, conflict: Conflict, game):
def __init__(self, mission: Mission, game: Game):
self.mission = mission
self.conflict = conflict
self.game = game
def _set_options_view(self):

View File

@ -353,40 +353,16 @@ class GroundObjectsGenerator:
locations for spawning ground objects, determining their types, and creating
the appropriate generators.
"""
FARP_CAPACITY = 4
def __init__(self, mission: Mission, conflict: Conflict, game: Game,
def __init__(self, mission: Mission, game: Game,
radio_registry: RadioRegistry, tacan_registry: TacanRegistry):
self.m = mission
self.conflict = conflict
self.game = game
self.radio_registry = radio_registry
self.tacan_registry = tacan_registry
self.icls_alloc = iter(range(1, 21))
self.runways: Dict[str, RunwayData] = {}
def generate_farps(self, number_of_units=1) -> Iterator[StaticGroup]:
if self.conflict.is_vector:
center = self.conflict.center
heading = self.conflict.heading - 90
else:
center, heading = self.conflict.frontline_position(self.conflict.from_cp, self.conflict.to_cp, self.game.theater)
heading -= 90
initial_position = center.point_from_heading(heading, FARP_FRONTLINE_DISTANCE)
position = self.conflict.find_ground_position(initial_position, heading)
if not position:
position = initial_position
for i, _ in enumerate(range(0, number_of_units, self.FARP_CAPACITY)):
position = position.point_from_heading(0, i * 275)
yield self.m.farp(
country=self.m.country(self.game.player_country),
name="FARP",
position=position,
)
def generate(self):
for cp in self.game.theater.controlpoints:
if cp.captured:

View File

@ -32,7 +32,7 @@ class Silence(Option):
class TriggersGenerator:
def __init__(self, mission: Mission, conflict: Conflict, game):
self.mission = mission
self.conflict = conflict
self.conflict = conflict # TODO: Move conflict out of this class. Only needed for bullseye position
self.game = game
def _set_allegiances(self, player_coalition: str, enemy_coalition: str):

View File

@ -92,9 +92,8 @@ def turn_heading(heading, fac):
class VisualGenerator:
def __init__(self, mission: Mission, conflict: Conflict, game: Game):
def __init__(self, mission: Mission, game: Game):
self.mission = mission
self.conflict = conflict
self.game = game
def _generate_frontline_smokes(self):

View File

@ -233,8 +233,7 @@ class QTopPanel(QFrame):
game_event.is_awacs_enabled = True
game_event.ca_slots = 1
game_event.departure_cp = self.game.theater.controlpoints[0]
# game_event.player_attacking({CAS: {}, CAP: {}})
game_event.depart_from = self.game.theater.controlpoints[0]
game_event.player_attacking()
self.game.initiate_event(game_event)
waiting = QWaitingForMissionResultWindow(game_event, self.game)