mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Merge branch 'theater-refactor' into develop
This commit is contained in:
commit
ee113d080e
@ -87,8 +87,8 @@ class Debriefing:
|
|||||||
|
|
||||||
for i, ground_object in enumerate(cp.ground_objects):
|
for i, ground_object in enumerate(cp.ground_objects):
|
||||||
logging.info(unit)
|
logging.info(unit)
|
||||||
logging.info(ground_object.string_identifier)
|
logging.info(ground_object.group_name)
|
||||||
if ground_object.matches_string_identifier(unit):
|
if ground_object.is_same_group(unit):
|
||||||
unit = DebriefingDeadUnitInfo(country, player_unit, ground_object.dcs_identifier)
|
unit = DebriefingDeadUnitInfo(country, player_unit, ground_object.dcs_identifier)
|
||||||
self.dead_buildings.append(unit)
|
self.dead_buildings.append(unit)
|
||||||
elif ground_object.dcs_identifier in ["AA", "CARRIER", "LHA"]:
|
elif ground_object.dcs_identifier in ["AA", "CARRIER", "LHA"]:
|
||||||
|
|||||||
@ -145,8 +145,8 @@ class Event:
|
|||||||
if ground_object.is_dead:
|
if ground_object.is_dead:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if ground_object.matches_string_identifier(destroyed_ground_unit_name):
|
if ground_object.is_same_group(destroyed_ground_unit_name):
|
||||||
logging.info("cp {} killing ground object {}".format(cp, ground_object.string_identifier))
|
logging.info("cp {} killing ground object {}".format(cp, ground_object.group_name))
|
||||||
cp.ground_objects[i].is_dead = True
|
cp.ground_objects[i].is_dead = True
|
||||||
|
|
||||||
info = Information("Building destroyed",
|
info = Information("Building destroyed",
|
||||||
|
|||||||
@ -1298,7 +1298,7 @@ class DeadIngressBuilder(PydcsWaypointBuilder):
|
|||||||
|
|
||||||
target_group = self.package.target
|
target_group = self.package.target
|
||||||
if isinstance(target_group, TheaterGroundObject):
|
if isinstance(target_group, TheaterGroundObject):
|
||||||
tgroup = self.mission.find_group(target_group.group_identifier, search="match") # Match search is used due to TheaterGroundObject.name not matching
|
tgroup = self.mission.find_group(target_group.group_name, search="match") # Match search is used due to TheaterGroundObject.name not matching
|
||||||
if tgroup is not None: # the Mission group name because of SkyNet prefixes.
|
if tgroup is not None: # the Mission group name because of SkyNet prefixes.
|
||||||
task = AttackGroup(tgroup.id)
|
task = AttackGroup(tgroup.id)
|
||||||
task.params["expend"] = "All"
|
task.params["expend"] = "All"
|
||||||
@ -1309,7 +1309,7 @@ class DeadIngressBuilder(PydcsWaypointBuilder):
|
|||||||
task.params["groupAttack"] = True
|
task.params["groupAttack"] = True
|
||||||
waypoint.tasks.append(task)
|
waypoint.tasks.append(task)
|
||||||
else:
|
else:
|
||||||
logging.error(f"Could not find group for DEAD mission {target_group.group_identifier}")
|
logging.error(f"Could not find group for DEAD mission {target_group.group_name}")
|
||||||
|
|
||||||
for i, t in enumerate(self.waypoint.targets):
|
for i, t in enumerate(self.waypoint.targets):
|
||||||
if self.group.units[0].unit_type == JF_17 and i < 4:
|
if self.group.units[0].unit_type == JF_17 and i < 4:
|
||||||
@ -1327,7 +1327,7 @@ class SeadIngressBuilder(PydcsWaypointBuilder):
|
|||||||
|
|
||||||
target_group = self.package.target
|
target_group = self.package.target
|
||||||
if isinstance(target_group, TheaterGroundObject):
|
if isinstance(target_group, TheaterGroundObject):
|
||||||
tgroup = self.mission.find_group(target_group.group_identifier, search="match") # Match search is used due to TheaterGroundObject.name not matching
|
tgroup = self.mission.find_group(target_group.group_name, search="match") # Match search is used due to TheaterGroundObject.name not matching
|
||||||
if tgroup is not None: # the Mission group name because of SkyNet prefixes.
|
if tgroup is not None: # the Mission group name because of SkyNet prefixes.
|
||||||
waypoint.add_task(EngageTargetsInZone(
|
waypoint.add_task(EngageTargetsInZone(
|
||||||
position=tgroup.position,
|
position=tgroup.position,
|
||||||
@ -1337,7 +1337,7 @@ class SeadIngressBuilder(PydcsWaypointBuilder):
|
|||||||
])
|
])
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logging.error(f"Could not find group for DEAD mission {target_group.group_identifier}")
|
logging.error(f"Could not find group for DEAD mission {target_group.group_name}")
|
||||||
|
|
||||||
for i, t in enumerate(self.waypoint.targets):
|
for i, t in enumerate(self.waypoint.targets):
|
||||||
if self.group.units[0].unit_type == JF_17 and i < 4:
|
if self.group.units[0].unit_type == JF_17 and i < 4:
|
||||||
|
|||||||
@ -135,7 +135,7 @@ class BuildingSiteGenerator(GenericGroundObjectGenerator):
|
|||||||
if not self.ground_object.is_dead:
|
if not self.ground_object.is_dead:
|
||||||
self.m.vehicle_group(
|
self.m.vehicle_group(
|
||||||
country=self.country,
|
country=self.country,
|
||||||
name=self.ground_object.string_identifier,
|
name=self.ground_object.group_name,
|
||||||
_type=unit_type,
|
_type=unit_type,
|
||||||
position=self.ground_object.position,
|
position=self.ground_object.position,
|
||||||
heading=self.ground_object.heading,
|
heading=self.ground_object.heading,
|
||||||
@ -144,7 +144,7 @@ class BuildingSiteGenerator(GenericGroundObjectGenerator):
|
|||||||
def generate_static(self, static_type: StaticType) -> None:
|
def generate_static(self, static_type: StaticType) -> None:
|
||||||
self.m.static_group(
|
self.m.static_group(
|
||||||
country=self.country,
|
country=self.country,
|
||||||
name=self.ground_object.string_identifier,
|
name=self.ground_object.group_name,
|
||||||
_type=static_type,
|
_type=static_type,
|
||||||
position=self.ground_object.position,
|
position=self.ground_object.position,
|
||||||
heading=self.ground_object.heading,
|
heading=self.ground_object.heading,
|
||||||
|
|||||||
@ -1,19 +1,15 @@
|
|||||||
import random
|
from abc import ABC
|
||||||
|
|
||||||
from dcs.vehicles import AirDefence
|
from game import Game
|
||||||
from game import db
|
|
||||||
from gen.sam.group_generator import GroupGenerator
|
from gen.sam.group_generator import GroupGenerator
|
||||||
|
from theater.theatergroundobject import SamGroundObject
|
||||||
|
|
||||||
|
|
||||||
class GenericSamGroupGenerator(GroupGenerator):
|
class GenericSamGroupGenerator(GroupGenerator, ABC):
|
||||||
"""
|
"""
|
||||||
This is the base for all SAM group generators
|
This is the base for all SAM group generators
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@property
|
def __init__(self, game: Game, ground_object: SamGroundObject) -> None:
|
||||||
def groupNamePrefix(self) -> str:
|
ground_object.skynet_capable = True
|
||||||
# prefix the SAM site for use with the Skynet IADS plugin
|
super().__init__(game, ground_object)
|
||||||
if self.faction == self.game.player_name: # this is the player faction
|
|
||||||
return "BLUE SAM "
|
|
||||||
else:
|
|
||||||
return "RED SAM "
|
|
||||||
|
|||||||
@ -22,31 +22,26 @@ if TYPE_CHECKING:
|
|||||||
# types rather than pydcs groups.
|
# types rather than pydcs groups.
|
||||||
class GroupGenerator:
|
class GroupGenerator:
|
||||||
|
|
||||||
def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Optional[Faction] = None): # faction is not mandatory because some subclasses do not use it
|
def __init__(self, game: Game, ground_object: TheaterGroundObject) -> None:
|
||||||
self.game = game
|
self.game = game
|
||||||
self.go = ground_object
|
self.go = ground_object
|
||||||
self.position = ground_object.position
|
self.position = ground_object.position
|
||||||
self.heading = random.randint(0, 359)
|
self.heading = random.randint(0, 359)
|
||||||
self.faction = faction
|
self.vg = unitgroup.VehicleGroup(self.game.next_group_id(),
|
||||||
self.vg = unitgroup.VehicleGroup(self.game.next_group_id(), self.groupNamePrefix + self.go.group_identifier)
|
self.go.group_name)
|
||||||
wp = self.vg.add_waypoint(self.position, PointAction.OffRoad, 0)
|
wp = self.vg.add_waypoint(self.position, PointAction.OffRoad, 0)
|
||||||
wp.ETA_locked = True
|
wp.ETA_locked = True
|
||||||
|
|
||||||
@property
|
|
||||||
def groupNamePrefix(self) -> str:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def get_generated_group(self) -> unitgroup.VehicleGroup:
|
def get_generated_group(self) -> unitgroup.VehicleGroup:
|
||||||
return self.vg
|
return self.vg
|
||||||
|
|
||||||
def add_unit(self, unit_type: VehicleType, name: str, pos_x: float, pos_y: float, heading: int):
|
def add_unit(self, unit_type: VehicleType, name: str, pos_x: float,
|
||||||
nn = "cgroup|" + str(self.go.cp_id) + '|' + str(self.go.group_id) + '|' + str(self.go.group_identifier) + "|" + name
|
pos_y: float, heading: int) -> Vehicle:
|
||||||
|
|
||||||
unit = Vehicle(self.game.next_unit_id(),
|
unit = Vehicle(self.game.next_unit_id(),
|
||||||
nn, unit_type.id)
|
f"{self.go.group_name}|{name}", unit_type.id)
|
||||||
unit.position.x = pos_x
|
unit.position.x = pos_x
|
||||||
unit.position.y = pos_y
|
unit.position.y = pos_y
|
||||||
unit.heading = heading
|
unit.heading = heading
|
||||||
@ -88,6 +83,7 @@ class GroupGenerator:
|
|||||||
current_offset += outer_offset
|
current_offset += outer_offset
|
||||||
return positions
|
return positions
|
||||||
|
|
||||||
|
|
||||||
class ShipGroupGenerator(GroupGenerator):
|
class ShipGroupGenerator(GroupGenerator):
|
||||||
"""Abstract class for other ship generator classes"""
|
"""Abstract class for other ship generator classes"""
|
||||||
def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction):
|
def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction):
|
||||||
@ -96,15 +92,14 @@ class ShipGroupGenerator(GroupGenerator):
|
|||||||
self.position = ground_object.position
|
self.position = ground_object.position
|
||||||
self.heading = random.randint(0, 359)
|
self.heading = random.randint(0, 359)
|
||||||
self.faction = faction
|
self.faction = faction
|
||||||
self.vg = unitgroup.ShipGroup(self.game.next_group_id(), self.groupNamePrefix + self.go.group_identifier)
|
self.vg = unitgroup.ShipGroup(self.game.next_group_id(),
|
||||||
|
self.go.group_name)
|
||||||
wp = self.vg.add_waypoint(self.position, 0)
|
wp = self.vg.add_waypoint(self.position, 0)
|
||||||
wp.ETA_locked = True
|
wp.ETA_locked = True
|
||||||
|
|
||||||
def add_unit(self, unit_type, name, pos_x, pos_y, heading):
|
def add_unit(self, unit_type, name, pos_x, pos_y, heading) -> Ship:
|
||||||
nn = "cgroup|" + str(self.go.cp_id) + '|' + str(self.go.group_id) + '|' + str(self.go.group_identifier) + "|" + name
|
|
||||||
|
|
||||||
unit = Ship(self.game.next_unit_id(),
|
unit = Ship(self.game.next_unit_id(),
|
||||||
nn, unit_type)
|
f"{self.go.group_name}|{name}", unit_type)
|
||||||
unit.position.x = pos_x
|
unit.position.x = pos_x
|
||||||
unit.position.y = pos_y
|
unit.position.y = pos_y
|
||||||
unit.heading = heading
|
unit.heading = heading
|
||||||
|
|||||||
@ -38,6 +38,7 @@ from gen.sam.sam_zu23_ural import ZU23UralGenerator
|
|||||||
from gen.sam.sam_zu23_ural_insurgent import ZU23UralInsurgentGenerator
|
from gen.sam.sam_zu23_ural_insurgent import ZU23UralInsurgentGenerator
|
||||||
from gen.sam.freya_ewr import FreyaGenerator
|
from gen.sam.freya_ewr import FreyaGenerator
|
||||||
from theater import TheaterGroundObject
|
from theater import TheaterGroundObject
|
||||||
|
from theater.theatergroundobject import SamGroundObject
|
||||||
|
|
||||||
SAM_MAP = {
|
SAM_MAP = {
|
||||||
"HawkGenerator": HawkGenerator,
|
"HawkGenerator": HawkGenerator,
|
||||||
@ -129,13 +130,13 @@ def generate_anti_air_group(game: Game, ground_object: TheaterGroundObject,
|
|||||||
possible_sams_generators = get_faction_possible_sams_generator(faction)
|
possible_sams_generators = get_faction_possible_sams_generator(faction)
|
||||||
if len(possible_sams_generators) > 0:
|
if len(possible_sams_generators) > 0:
|
||||||
sam_generator_class = random.choice(possible_sams_generators)
|
sam_generator_class = random.choice(possible_sams_generators)
|
||||||
generator = sam_generator_class(game, ground_object, db.FACTIONS[faction])
|
generator = sam_generator_class(game, ground_object)
|
||||||
generator.generate()
|
generator.generate()
|
||||||
return generator.get_generated_group()
|
return generator.get_generated_group()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def generate_shorad_group(game: Game, ground_object: TheaterGroundObject,
|
def generate_shorad_group(game: Game, ground_object: SamGroundObject,
|
||||||
faction_name: str) -> Optional[VehicleGroup]:
|
faction_name: str) -> Optional[VehicleGroup]:
|
||||||
faction = db.FACTIONS[faction_name]
|
faction = db.FACTIONS[faction_name]
|
||||||
|
|
||||||
|
|||||||
@ -95,7 +95,7 @@ class QFlightTypeComboBox(QComboBox):
|
|||||||
yield from self.ENEMY_AIRBASE_MISSIONS
|
yield from self.ENEMY_AIRBASE_MISSIONS
|
||||||
elif isinstance(self.target, TheaterGroundObject):
|
elif isinstance(self.target, TheaterGroundObject):
|
||||||
# TODO: Filter more based on the category.
|
# TODO: Filter more based on the category.
|
||||||
friendly = self.target.parent_control_point(self.theater).captured
|
friendly = self.target.control_point.captured
|
||||||
if friendly:
|
if friendly:
|
||||||
yield from self.FRIENDLY_GROUND_OBJECT_MISSIONS
|
yield from self.FRIENDLY_GROUND_OBJECT_MISSIONS
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from PySide2.QtGui import QStandardItem, QStandardItemModel
|
from PySide2.QtGui import QStandardItem, QStandardItemModel
|
||||||
|
|
||||||
from game import Game
|
from game import Game
|
||||||
from gen import Conflict, FlightWaypointType
|
from gen import BuildingGroundObject, Conflict, FlightWaypointType
|
||||||
from gen.flights.flight import FlightWaypoint
|
from gen.flights.flight import FlightWaypoint
|
||||||
from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox
|
from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox
|
||||||
from theater import ControlPointType
|
from theater import ControlPointType
|
||||||
@ -71,7 +71,7 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
|
|||||||
for cp in self.game.theater.controlpoints:
|
for cp in self.game.theater.controlpoints:
|
||||||
if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured):
|
if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured):
|
||||||
for ground_object in cp.ground_objects:
|
for ground_object in cp.ground_objects:
|
||||||
if not ground_object.is_dead and not ground_object.dcs_identifier == "AA":
|
if not ground_object.is_dead and not isinstance(ground_object, BuildingGroundObject):
|
||||||
wpt = FlightWaypoint(
|
wpt = FlightWaypoint(
|
||||||
FlightWaypointType.CUSTOM,
|
FlightWaypointType.CUSTOM,
|
||||||
ground_object.position.x,
|
ground_object.position.x,
|
||||||
|
|||||||
@ -121,8 +121,8 @@ class QLiberationMap(QGraphicsView):
|
|||||||
|
|
||||||
def setGame(self, game: Optional[Game]):
|
def setGame(self, game: Optional[Game]):
|
||||||
self.game = game
|
self.game = game
|
||||||
logging.debug("Reloading Map Canvas")
|
|
||||||
if self.game is not None:
|
if self.game is not None:
|
||||||
|
logging.debug("Reloading Map Canvas")
|
||||||
self.reload_scene()
|
self.reload_scene()
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import traceback
|
||||||
import webbrowser
|
import webbrowser
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
@ -16,7 +17,7 @@ from PySide2.QtWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
import qt_ui.uiconstants as CONST
|
import qt_ui.uiconstants as CONST
|
||||||
from game import Game, persistency, VERSION
|
from game import Game, VERSION, persistency
|
||||||
from qt_ui.dialogs import Dialog
|
from qt_ui.dialogs import Dialog
|
||||||
from qt_ui.displayoptions import DisplayGroup, DisplayOptions, DisplayRule
|
from qt_ui.displayoptions import DisplayGroup, DisplayOptions, DisplayRule
|
||||||
from qt_ui.models import GameModel
|
from qt_ui.models import GameModel
|
||||||
@ -40,10 +41,9 @@ class QLiberationWindow(QMainWindow):
|
|||||||
self.game: Optional[Game] = None
|
self.game: Optional[Game] = None
|
||||||
self.game_model = GameModel()
|
self.game_model = GameModel()
|
||||||
Dialog.set_game(self.game_model)
|
Dialog.set_game(self.game_model)
|
||||||
self.ato_panel = None
|
self.ato_panel = QAirTaskingOrderPanel(self.game_model)
|
||||||
self.info_panel = None
|
self.info_panel = QInfoPanel(self.game)
|
||||||
self.liberation_map = None
|
self.liberation_map = QLiberationMap(self.game_model)
|
||||||
self.setGame(persistency.restore_game())
|
|
||||||
|
|
||||||
self.setGeometry(300, 100, 270, 100)
|
self.setGeometry(300, 100, 270, 100)
|
||||||
self.setWindowTitle(f"DCS Liberation - v{VERSION}")
|
self.setWindowTitle(f"DCS Liberation - v{VERSION}")
|
||||||
@ -55,17 +55,14 @@ class QLiberationWindow(QMainWindow):
|
|||||||
self.initMenuBar()
|
self.initMenuBar()
|
||||||
self.initToolbar()
|
self.initToolbar()
|
||||||
self.connectSignals()
|
self.connectSignals()
|
||||||
self.onGameGenerated(self.game)
|
|
||||||
|
|
||||||
screen = QDesktopWidget().screenGeometry()
|
screen = QDesktopWidget().screenGeometry()
|
||||||
self.setGeometry(0, 0, screen.width(), screen.height())
|
self.setGeometry(0, 0, screen.width(), screen.height())
|
||||||
self.setWindowState(Qt.WindowMaximized)
|
self.setWindowState(Qt.WindowMaximized)
|
||||||
|
|
||||||
def initUi(self):
|
self.onGameGenerated(persistency.restore_game())
|
||||||
self.ato_panel = QAirTaskingOrderPanel(self.game_model)
|
|
||||||
self.liberation_map = QLiberationMap(self.game_model)
|
|
||||||
self.info_panel = QInfoPanel(self.game)
|
|
||||||
|
|
||||||
|
def initUi(self):
|
||||||
hbox = QSplitter(Qt.Horizontal)
|
hbox = QSplitter(Qt.Horizontal)
|
||||||
vbox = QSplitter(Qt.Vertical)
|
vbox = QSplitter(Qt.Vertical)
|
||||||
hbox.addWidget(self.ato_panel)
|
hbox.addWidget(self.ato_panel)
|
||||||
@ -193,8 +190,7 @@ class QLiberationWindow(QMainWindow):
|
|||||||
filter="*.liberation")
|
filter="*.liberation")
|
||||||
if file is not None:
|
if file is not None:
|
||||||
game = persistency.load_game(file[0])
|
game = persistency.load_game(file[0])
|
||||||
self.setGame(game)
|
GameUpdateSignal.get_instance().updateGame(game)
|
||||||
GameUpdateSignal.get_instance().updateGame(self.game)
|
|
||||||
|
|
||||||
def saveGame(self):
|
def saveGame(self):
|
||||||
logging.info("Saving game")
|
logging.info("Saving game")
|
||||||
@ -217,14 +213,27 @@ class QLiberationWindow(QMainWindow):
|
|||||||
GameUpdateSignal.get_instance().updateGame(self.game)
|
GameUpdateSignal.get_instance().updateGame(self.game)
|
||||||
|
|
||||||
def setGame(self, game: Optional[Game]):
|
def setGame(self, game: Optional[Game]):
|
||||||
if game is not None:
|
try:
|
||||||
game.on_load()
|
if game is not None:
|
||||||
self.game = game
|
game.on_load()
|
||||||
if self.info_panel is not None:
|
self.game = game
|
||||||
self.info_panel.setGame(game)
|
if self.info_panel is not None:
|
||||||
self.game_model.set(self.game)
|
self.info_panel.setGame(game)
|
||||||
if self.liberation_map is not None:
|
self.game_model.set(self.game)
|
||||||
self.liberation_map.setGame(game)
|
if self.liberation_map is not None:
|
||||||
|
self.liberation_map.setGame(game)
|
||||||
|
except AttributeError:
|
||||||
|
logging.exception("Incompatible save game")
|
||||||
|
QMessageBox.critical(
|
||||||
|
self,
|
||||||
|
"Could not load save game",
|
||||||
|
"The save game you have loaded is incompatible with this "
|
||||||
|
"version of DCS Liberation.\n"
|
||||||
|
"\n"
|
||||||
|
f"{traceback.format_exc()}",
|
||||||
|
QMessageBox.Ok
|
||||||
|
)
|
||||||
|
GameUpdateSignal.get_instance().updateGame(None)
|
||||||
|
|
||||||
def showAboutDialog(self):
|
def showAboutDialog(self):
|
||||||
text = "<h3>DCS Liberation " + VERSION + "</h3>" + \
|
text = "<h3>DCS Liberation " + VERSION + "</h3>" + \
|
||||||
|
|||||||
@ -72,10 +72,10 @@ if dcsLiberation and SkynetIADS then
|
|||||||
end
|
end
|
||||||
|
|
||||||
--add EW units to the IADS:
|
--add EW units to the IADS:
|
||||||
iads:addEarlyWarningRadarsByPrefix(coalitionPrefix .. " EW")
|
iads:addEarlyWarningRadarsByPrefix(coalitionPrefix .. "|EWR|")
|
||||||
|
|
||||||
--add SAM groups to the IADS:
|
--add SAM groups to the IADS:
|
||||||
iads:addSAMSitesByPrefix(coalitionPrefix .. " SAM")
|
iads:addSAMSitesByPrefix(coalitionPrefix .. "|SAM|")
|
||||||
|
|
||||||
-- specific configurations, for each SAM type
|
-- specific configurations, for each SAM type
|
||||||
if actAsEwr then
|
if actAsEwr then
|
||||||
|
|||||||
@ -19,6 +19,14 @@ grey text -------------------- #B7C0C6
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Makes all message box text selectable.
|
||||||
|
* https://stackoverflow.com/a/32595502/632035
|
||||||
|
*/
|
||||||
|
QMessageBox {
|
||||||
|
messagebox-text-interaction-flags: 5;
|
||||||
|
}
|
||||||
|
|
||||||
/*QMenuBar*/
|
/*QMenuBar*/
|
||||||
QMenuBar {
|
QMenuBar {
|
||||||
spacing: 2px; /* spacing between menu bar items */
|
spacing: 2px; /* spacing between menu bar items */
|
||||||
|
|||||||
@ -1,3 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
windows basis styles
|
windows basis styles
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Makes all message box text selectable.
|
||||||
|
* https://stackoverflow.com/a/32595502/632035
|
||||||
|
*/
|
||||||
|
QMessageBox {
|
||||||
|
messagebox-text-interaction-flags: 5;
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import itertools
|
||||||
import re
|
import re
|
||||||
from typing import Dict, List, TYPE_CHECKING
|
from typing import Dict, List, TYPE_CHECKING
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
@ -17,7 +18,7 @@ from game import db
|
|||||||
from gen.ground_forces.combat_stance import CombatStance
|
from gen.ground_forces.combat_stance import CombatStance
|
||||||
from .base import Base
|
from .base import Base
|
||||||
from .missiontarget import MissionTarget
|
from .missiontarget import MissionTarget
|
||||||
from .theatergroundobject import TheaterGroundObject
|
from .theatergroundobject import SamGroundObject, TheaterGroundObject
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from game import Game
|
from game import Game
|
||||||
@ -50,7 +51,8 @@ class ControlPoint(MissionTarget):
|
|||||||
self.id = id
|
self.id = id
|
||||||
self.full_name = name
|
self.full_name = name
|
||||||
self.at = at
|
self.at = at
|
||||||
self.ground_objects: List[TheaterGroundObject] = []
|
self.connected_objectives: List[TheaterGroundObject] = []
|
||||||
|
self.base_defenses: List[SamGroundObject] = []
|
||||||
|
|
||||||
self.size = size
|
self.size = size
|
||||||
self.importance = importance
|
self.importance = importance
|
||||||
@ -64,6 +66,11 @@ class ControlPoint(MissionTarget):
|
|||||||
self.stances: Dict[int, CombatStance] = {}
|
self.stances: Dict[int, CombatStance] = {}
|
||||||
self.airport = None
|
self.airport = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ground_objects(self) -> List[TheaterGroundObject]:
|
||||||
|
return list(
|
||||||
|
itertools.chain(self.connected_objectives, self.base_defenses))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_airport(cls, airport: Airport, radials: List[int], size: int, importance: float, has_frontline=True):
|
def from_airport(cls, airport: Airport, radials: List[int], size: int, importance: float, has_frontline=True):
|
||||||
assert airport
|
assert airport
|
||||||
@ -225,9 +232,6 @@ class ControlPoint(MissionTarget):
|
|||||||
self.base.armor = {}
|
self.base.armor = {}
|
||||||
|
|
||||||
# Handle cyclic dependency.
|
# Handle cyclic dependency.
|
||||||
from .start_generator import generate_airbase_defense_group
|
from .start_generator import BaseDefenseGenerator
|
||||||
for idx, ground_object in enumerate(self.ground_objects):
|
self.base_defenses = []
|
||||||
ground_object.groups = []
|
BaseDefenseGenerator(game, self, faction_name).generate()
|
||||||
if ground_object.airbase_group and faction_name != "":
|
|
||||||
generate_airbase_defense_group(idx, ground_object,
|
|
||||||
faction_name, game)
|
|
||||||
|
|||||||
@ -160,11 +160,9 @@ class GameGenerator:
|
|||||||
|
|
||||||
|
|
||||||
class ControlPointGroundObjectGenerator:
|
class ControlPointGroundObjectGenerator:
|
||||||
def __init__(self, game: Game, control_point: ControlPoint,
|
def __init__(self, game: Game, control_point: ControlPoint) -> None:
|
||||||
templates: GroundObjectTemplates) -> None:
|
|
||||||
self.game = game
|
self.game = game
|
||||||
self.control_point = control_point
|
self.control_point = control_point
|
||||||
self.templates = templates
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def faction_name(self) -> str:
|
def faction_name(self) -> str:
|
||||||
@ -178,8 +176,7 @@ class ControlPointGroundObjectGenerator:
|
|||||||
return db.FACTIONS[self.faction_name]
|
return db.FACTIONS[self.faction_name]
|
||||||
|
|
||||||
def generate(self) -> bool:
|
def generate(self) -> bool:
|
||||||
self.control_point.ground_objects = []
|
self.control_point.connected_objectives = []
|
||||||
self.generate_ground_points()
|
|
||||||
if self.faction.navy_generators:
|
if self.faction.navy_generators:
|
||||||
# Even airbases can generate navies if they are close enough to the
|
# Even airbases can generate navies if they are close enough to the
|
||||||
# water. This is not controlled by the control point definition, but
|
# water. This is not controlled by the control point definition, but
|
||||||
@ -187,85 +184,8 @@ class ControlPointGroundObjectGenerator:
|
|||||||
# for the ship.
|
# for the ship.
|
||||||
self.generate_navy()
|
self.generate_navy()
|
||||||
|
|
||||||
if self.faction.missiles:
|
|
||||||
# TODO: Presumably only for airbases?
|
|
||||||
self.generate_missile_sites()
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def generate_ground_points(self) -> None:
|
|
||||||
"""Generate ground objects and AA sites for the control point."""
|
|
||||||
|
|
||||||
if self.control_point.is_global:
|
|
||||||
return
|
|
||||||
|
|
||||||
# TODO: Should probably perform this check later.
|
|
||||||
# Just because we don't have factories for the faction doesn't mean we
|
|
||||||
# shouldn't generate AA.
|
|
||||||
available_categories = self.faction.building_set
|
|
||||||
if not available_categories:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Always generate at least one AA point.
|
|
||||||
self.generate_aa_site()
|
|
||||||
|
|
||||||
# And between 2 and 7 other objectives.
|
|
||||||
amount = random.randrange(2, 7)
|
|
||||||
for i in range(amount):
|
|
||||||
# 1 in 4 additional objectives are AA.
|
|
||||||
if random.randint(0, 3) == 0:
|
|
||||||
self.generate_aa_site()
|
|
||||||
else:
|
|
||||||
category = random.choice(available_categories)
|
|
||||||
self.generate_ground_point(category)
|
|
||||||
|
|
||||||
def generate_ground_point(self, category: str) -> None:
|
|
||||||
obj_name = namegen.random_objective_name()
|
|
||||||
template = random.choice(list(self.templates[category].values()))
|
|
||||||
point = find_location(category != "oil",
|
|
||||||
self.control_point.position,
|
|
||||||
self.game.theater, 10000, 40000,
|
|
||||||
self.control_point.ground_objects)
|
|
||||||
|
|
||||||
if point is None:
|
|
||||||
logging.error(
|
|
||||||
f"Could not find point for {obj_name} at {self.control_point}")
|
|
||||||
return
|
|
||||||
|
|
||||||
object_id = 0
|
|
||||||
group_id = self.game.next_group_id()
|
|
||||||
|
|
||||||
# TODO: Create only one TGO per objective, each with multiple units.
|
|
||||||
for unit in template:
|
|
||||||
object_id += 1
|
|
||||||
|
|
||||||
template_point = Point(unit["offset"].x, unit["offset"].y)
|
|
||||||
g = BuildingGroundObject(
|
|
||||||
obj_name, category, group_id, object_id, point + template_point,
|
|
||||||
unit["heading"], self.control_point, unit["type"])
|
|
||||||
|
|
||||||
self.control_point.ground_objects.append(g)
|
|
||||||
|
|
||||||
def generate_aa_site(self) -> None:
|
|
||||||
obj_name = namegen.random_objective_name()
|
|
||||||
position = find_location(True, self.control_point.position,
|
|
||||||
self.game.theater, 10000, 40000,
|
|
||||||
self.control_point.ground_objects)
|
|
||||||
|
|
||||||
if position is None:
|
|
||||||
logging.error(
|
|
||||||
f"Could not find point for {obj_name} at {self.control_point}")
|
|
||||||
return
|
|
||||||
|
|
||||||
group_id = self.game.next_group_id()
|
|
||||||
|
|
||||||
g = SamGroundObject(namegen.random_objective_name(), group_id,
|
|
||||||
position, self.control_point, for_airbase=False)
|
|
||||||
group = generate_anti_air_group(self.game, g, self.faction_name)
|
|
||||||
if group is not None:
|
|
||||||
g.groups = [group]
|
|
||||||
self.control_point.ground_objects.append(g)
|
|
||||||
|
|
||||||
def generate_navy(self) -> None:
|
def generate_navy(self) -> None:
|
||||||
skip_player_navy = self.game.settings.do_not_generate_player_navy
|
skip_player_navy = self.game.settings.do_not_generate_player_navy
|
||||||
if self.control_point.captured and skip_player_navy:
|
if self.control_point.captured and skip_player_navy:
|
||||||
@ -295,30 +215,7 @@ class ControlPointGroundObjectGenerator:
|
|||||||
g.groups = []
|
g.groups = []
|
||||||
if group is not None:
|
if group is not None:
|
||||||
g.groups.append(group)
|
g.groups.append(group)
|
||||||
self.control_point.ground_objects.append(g)
|
self.control_point.connected_objectives.append(g)
|
||||||
|
|
||||||
def generate_missile_sites(self) -> None:
|
|
||||||
for i in range(self.faction.missiles_group_count):
|
|
||||||
self.generate_missile_site()
|
|
||||||
|
|
||||||
def generate_missile_site(self) -> None:
|
|
||||||
point = find_location(True, self.control_point.position,
|
|
||||||
self.game.theater, 2500, 40000, [], False)
|
|
||||||
if point is None:
|
|
||||||
logging.info(
|
|
||||||
f"Could not find point for {self.control_point} missile site")
|
|
||||||
return
|
|
||||||
|
|
||||||
group_id = self.game.next_group_id()
|
|
||||||
|
|
||||||
g = MissileSiteGroundObject(namegen.random_objective_name(), group_id,
|
|
||||||
point, self.control_point)
|
|
||||||
group = generate_missile_group(self.game, g, self.faction_name)
|
|
||||||
g.groups = []
|
|
||||||
if group is not None:
|
|
||||||
g.groups.append(group)
|
|
||||||
self.control_point.ground_objects.append(g)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class CarrierGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
class CarrierGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
||||||
@ -341,7 +238,7 @@ class CarrierGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
|||||||
g.groups = []
|
g.groups = []
|
||||||
if group is not None:
|
if group is not None:
|
||||||
g.groups.append(group)
|
g.groups.append(group)
|
||||||
self.control_point.ground_objects.append(g)
|
self.control_point.connected_objectives.append(g)
|
||||||
self.control_point.name = random.choice(carrier_names)
|
self.control_point.name = random.choice(carrier_names)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -366,21 +263,23 @@ class LhaGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
|||||||
g.groups = []
|
g.groups = []
|
||||||
if group is not None:
|
if group is not None:
|
||||||
g.groups.append(group)
|
g.groups.append(group)
|
||||||
self.control_point.ground_objects.append(g)
|
self.control_point.connected_objectives.append(g)
|
||||||
self.control_point.name = random.choice(lha_names)
|
self.control_point.name = random.choice(lha_names)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
class BaseDefenseGenerator:
|
||||||
def generate(self) -> bool:
|
def __init__(self, game: Game, control_point: ControlPoint,
|
||||||
if not super().generate():
|
faction_name: str) -> None:
|
||||||
return False
|
self.game = game
|
||||||
|
self.control_point = control_point
|
||||||
|
self.faction_name = faction_name
|
||||||
|
|
||||||
|
def generate(self) -> None:
|
||||||
for i in range(random.randint(3, 6)):
|
for i in range(random.randint(3, 6)):
|
||||||
self.generate_sam(i)
|
self.generate_base_defense(i)
|
||||||
return True
|
|
||||||
|
|
||||||
def generate_sam(self, index: int) -> None:
|
def generate_base_defense(self, index: int) -> None:
|
||||||
position = find_location(True, self.control_point.position,
|
position = find_location(True, self.control_point.position,
|
||||||
self.game.theater, 400, 3200, [], True)
|
self.game.theater, 400, 3200, [], True)
|
||||||
|
|
||||||
@ -403,8 +302,122 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
|||||||
g = SamGroundObject(namegen.random_objective_name(), group_id,
|
g = SamGroundObject(namegen.random_objective_name(), group_id,
|
||||||
position, self.control_point, for_airbase=True)
|
position, self.control_point, for_airbase=True)
|
||||||
|
|
||||||
generate_airbase_defense_group(index, g, self.faction_name, self.game)
|
generate_airbase_defense_group(index, g, self.faction_name,
|
||||||
self.control_point.ground_objects.append(g)
|
self.game)
|
||||||
|
self.control_point.base_defenses.append(g)
|
||||||
|
|
||||||
|
|
||||||
|
class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
||||||
|
def __init__(self, game: Game, control_point: ControlPoint,
|
||||||
|
templates: GroundObjectTemplates) -> None:
|
||||||
|
super().__init__(game, control_point)
|
||||||
|
self.templates = templates
|
||||||
|
|
||||||
|
def generate(self) -> bool:
|
||||||
|
if not super().generate():
|
||||||
|
return False
|
||||||
|
|
||||||
|
BaseDefenseGenerator(self.game, self.control_point,
|
||||||
|
self.faction_name).generate()
|
||||||
|
self.generate_ground_points()
|
||||||
|
|
||||||
|
if self.faction.missiles:
|
||||||
|
self.generate_missile_sites()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def generate_ground_points(self) -> None:
|
||||||
|
"""Generate ground objects and AA sites for the control point."""
|
||||||
|
if self.control_point.is_global:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Always generate at least one AA point.
|
||||||
|
self.generate_aa_site()
|
||||||
|
|
||||||
|
# And between 2 and 7 other objectives.
|
||||||
|
amount = random.randrange(2, 7)
|
||||||
|
for i in range(amount):
|
||||||
|
# 1 in 4 additional objectives are AA.
|
||||||
|
if random.randint(0, 3) == 0:
|
||||||
|
self.generate_aa_site()
|
||||||
|
else:
|
||||||
|
self.generate_ground_point()
|
||||||
|
|
||||||
|
def generate_ground_point(self) -> None:
|
||||||
|
try:
|
||||||
|
category = random.choice(self.faction.building_set)
|
||||||
|
except IndexError:
|
||||||
|
logging.exception("Faction has no buildings defined")
|
||||||
|
return
|
||||||
|
|
||||||
|
obj_name = namegen.random_objective_name()
|
||||||
|
template = random.choice(list(self.templates[category].values()))
|
||||||
|
point = find_location(category != "oil",
|
||||||
|
self.control_point.position,
|
||||||
|
self.game.theater, 10000, 40000,
|
||||||
|
self.control_point.ground_objects)
|
||||||
|
|
||||||
|
if point is None:
|
||||||
|
logging.error(
|
||||||
|
f"Could not find point for {obj_name} at {self.control_point}")
|
||||||
|
return
|
||||||
|
|
||||||
|
object_id = 0
|
||||||
|
group_id = self.game.next_group_id()
|
||||||
|
|
||||||
|
# TODO: Create only one TGO per objective, each with multiple units.
|
||||||
|
for unit in template:
|
||||||
|
object_id += 1
|
||||||
|
|
||||||
|
template_point = Point(unit["offset"].x, unit["offset"].y)
|
||||||
|
g = BuildingGroundObject(
|
||||||
|
obj_name, category, group_id, object_id, point + template_point,
|
||||||
|
unit["heading"], self.control_point, unit["type"])
|
||||||
|
|
||||||
|
self.control_point.connected_objectives.append(g)
|
||||||
|
|
||||||
|
def generate_aa_site(self) -> None:
|
||||||
|
obj_name = namegen.random_objective_name()
|
||||||
|
position = find_location(True, self.control_point.position,
|
||||||
|
self.game.theater, 10000, 40000,
|
||||||
|
self.control_point.ground_objects)
|
||||||
|
|
||||||
|
if position is None:
|
||||||
|
logging.error(
|
||||||
|
f"Could not find point for {obj_name} at {self.control_point}")
|
||||||
|
return
|
||||||
|
|
||||||
|
group_id = self.game.next_group_id()
|
||||||
|
|
||||||
|
g = SamGroundObject(namegen.random_objective_name(), group_id,
|
||||||
|
position, self.control_point, for_airbase=False)
|
||||||
|
group = generate_anti_air_group(self.game, g, self.faction_name)
|
||||||
|
if group is not None:
|
||||||
|
g.groups = [group]
|
||||||
|
self.control_point.connected_objectives.append(g)
|
||||||
|
|
||||||
|
def generate_missile_sites(self) -> None:
|
||||||
|
for i in range(self.faction.missiles_group_count):
|
||||||
|
self.generate_missile_site()
|
||||||
|
|
||||||
|
def generate_missile_site(self) -> None:
|
||||||
|
point = find_location(True, self.control_point.position,
|
||||||
|
self.game.theater, 2500, 40000, [], False)
|
||||||
|
if point is None:
|
||||||
|
logging.info(
|
||||||
|
f"Could not find point for {self.control_point} missile site")
|
||||||
|
return
|
||||||
|
|
||||||
|
group_id = self.game.next_group_id()
|
||||||
|
|
||||||
|
g = MissileSiteGroundObject(namegen.random_objective_name(), group_id,
|
||||||
|
point, self.control_point)
|
||||||
|
group = generate_missile_group(self.game, g, self.faction_name)
|
||||||
|
g.groups = []
|
||||||
|
if group is not None:
|
||||||
|
g.groups.append(group)
|
||||||
|
self.control_point.connected_objectives.append(g)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
class GroundObjectGenerator:
|
class GroundObjectGenerator:
|
||||||
@ -424,11 +437,9 @@ class GroundObjectGenerator:
|
|||||||
def generate_for_control_point(self, control_point: ControlPoint) -> bool:
|
def generate_for_control_point(self, control_point: ControlPoint) -> bool:
|
||||||
generator: ControlPointGroundObjectGenerator
|
generator: ControlPointGroundObjectGenerator
|
||||||
if control_point.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP:
|
if control_point.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP:
|
||||||
generator = CarrierGroundObjectGenerator(self.game, control_point,
|
generator = CarrierGroundObjectGenerator(self.game, control_point)
|
||||||
self.templates)
|
|
||||||
elif control_point.cptype == ControlPointType.LHA_GROUP:
|
elif control_point.cptype == ControlPointType.LHA_GROUP:
|
||||||
generator = LhaGroundObjectGenerator(self.game, control_point,
|
generator = LhaGroundObjectGenerator(self.game, control_point)
|
||||||
self.templates)
|
|
||||||
else:
|
else:
|
||||||
generator = AirbaseGroundObjectGenerator(self.game, control_point,
|
generator = AirbaseGroundObjectGenerator(self.game, control_point,
|
||||||
self.templates)
|
self.templates)
|
||||||
@ -436,8 +447,8 @@ class GroundObjectGenerator:
|
|||||||
|
|
||||||
|
|
||||||
def generate_airbase_defense_group(airbase_defense_group_id: int,
|
def generate_airbase_defense_group(airbase_defense_group_id: int,
|
||||||
ground_obj: TheaterGroundObject,
|
ground_obj: SamGroundObject, faction: str,
|
||||||
faction: str, game: Game) -> None:
|
game: Game) -> None:
|
||||||
if airbase_defense_group_id == 0:
|
if airbase_defense_group_id == 0:
|
||||||
group = generate_armor_group(faction, game, ground_obj)
|
group = generate_armor_group(faction, game, ground_obj)
|
||||||
elif airbase_defense_group_id == 1 and random.randint(0, 1) == 0:
|
elif airbase_defense_group_id == 1 and random.randint(0, 1) == 0:
|
||||||
|
|||||||
@ -8,7 +8,6 @@ from dcs.unit import Unit
|
|||||||
from dcs.unitgroup import Group
|
from dcs.unitgroup import Group
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .conflicttheater import ConflictTheater
|
|
||||||
from .controlpoint import ControlPoint
|
from .controlpoint import ControlPoint
|
||||||
from .missiontarget import MissionTarget
|
from .missiontarget import MissionTarget
|
||||||
|
|
||||||
@ -72,29 +71,21 @@ CATEGORY_MAP = {
|
|||||||
|
|
||||||
class TheaterGroundObject(MissionTarget):
|
class TheaterGroundObject(MissionTarget):
|
||||||
|
|
||||||
def __init__(self, name: str, category: str, group_id: int, object_id: int,
|
def __init__(self, name: str, category: str, group_id: int, position: Point,
|
||||||
position: Point, heading: int, cp_id: int, dcs_identifier: str,
|
heading: int, control_point: ControlPoint, dcs_identifier: str,
|
||||||
airbase_group: bool, sea_object: bool) -> None:
|
airbase_group: bool, sea_object: bool) -> None:
|
||||||
super().__init__(name, position)
|
super().__init__(name, position)
|
||||||
self.category = category
|
self.category = category
|
||||||
self.group_id = group_id
|
self.group_id = group_id
|
||||||
self.object_id = object_id
|
|
||||||
self.heading = heading
|
self.heading = heading
|
||||||
self.cp_id = cp_id
|
self.control_point = control_point
|
||||||
self.dcs_identifier = dcs_identifier
|
self.dcs_identifier = dcs_identifier
|
||||||
self.airbase_group = airbase_group
|
self.airbase_group = airbase_group
|
||||||
self.sea_object = sea_object
|
self.sea_object = sea_object
|
||||||
self.is_dead = False
|
self.is_dead = False
|
||||||
|
# TODO: There is never more than one group.
|
||||||
self.groups: List[Group] = []
|
self.groups: List[Group] = []
|
||||||
|
|
||||||
@property
|
|
||||||
def string_identifier(self):
|
|
||||||
return "{}|{}|{}|{}".format(self.category, self.cp_id, self.group_id, self.object_id)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def group_identifier(self) -> str:
|
|
||||||
return "{}|{}".format(self.category, self.group_id)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def units(self) -> List[Unit]:
|
def units(self) -> List[Unit]:
|
||||||
"""
|
"""
|
||||||
@ -103,26 +94,20 @@ class TheaterGroundObject(MissionTarget):
|
|||||||
return list(itertools.chain.from_iterable([g.units for g in self.groups]))
|
return list(itertools.chain.from_iterable([g.units for g in self.groups]))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name_abbrev(self) -> str:
|
def group_name(self) -> str:
|
||||||
return ABBREV_NAME[self.category]
|
"""The name of the unit group."""
|
||||||
|
return f"{self.category}|{self.group_id}"
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return NAME_BY_CATEGORY[self.category]
|
return NAME_BY_CATEGORY[self.category]
|
||||||
|
|
||||||
def matches_string_identifier(self, identifier):
|
def is_same_group(self, identifier: str) -> bool:
|
||||||
return self.string_identifier == identifier
|
return self.group_id == identifier
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def obj_name(self) -> str:
|
def obj_name(self) -> str:
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def parent_control_point(self, theater: ConflictTheater) -> ControlPoint:
|
|
||||||
"""Searches the theater for the parent control point."""
|
|
||||||
for cp in theater.controlpoints:
|
|
||||||
if cp.id == self.cp_id:
|
|
||||||
return cp
|
|
||||||
raise RuntimeError("Could not find matching control point in theater")
|
|
||||||
|
|
||||||
|
|
||||||
class BuildingGroundObject(TheaterGroundObject):
|
class BuildingGroundObject(TheaterGroundObject):
|
||||||
def __init__(self, name: str, category: str, group_id: int, object_id: int,
|
def __init__(self, name: str, category: str, group_id: int, object_id: int,
|
||||||
@ -132,14 +117,19 @@ class BuildingGroundObject(TheaterGroundObject):
|
|||||||
name=name,
|
name=name,
|
||||||
category=category,
|
category=category,
|
||||||
group_id=group_id,
|
group_id=group_id,
|
||||||
object_id=object_id,
|
|
||||||
position=position,
|
position=position,
|
||||||
heading=heading,
|
heading=heading,
|
||||||
cp_id=control_point.id,
|
control_point=control_point,
|
||||||
dcs_identifier=dcs_identifier,
|
dcs_identifier=dcs_identifier,
|
||||||
airbase_group=False,
|
airbase_group=False,
|
||||||
sea_object=False
|
sea_object=False
|
||||||
)
|
)
|
||||||
|
self.object_id = object_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def group_name(self) -> str:
|
||||||
|
"""The name of the unit group."""
|
||||||
|
return f"{self.category}|{self.group_id}|{self.object_id}"
|
||||||
|
|
||||||
|
|
||||||
class GenericCarrierGroundObject(TheaterGroundObject):
|
class GenericCarrierGroundObject(TheaterGroundObject):
|
||||||
@ -154,10 +144,9 @@ class CarrierGroundObject(GenericCarrierGroundObject):
|
|||||||
name=name,
|
name=name,
|
||||||
category="CARRIER",
|
category="CARRIER",
|
||||||
group_id=group_id,
|
group_id=group_id,
|
||||||
object_id=0,
|
|
||||||
position=control_point.position,
|
position=control_point.position,
|
||||||
heading=0,
|
heading=0,
|
||||||
cp_id=control_point.id,
|
control_point=control_point,
|
||||||
dcs_identifier="CARRIER",
|
dcs_identifier="CARRIER",
|
||||||
airbase_group=True,
|
airbase_group=True,
|
||||||
sea_object=True
|
sea_object=True
|
||||||
@ -172,10 +161,9 @@ class LhaGroundObject(GenericCarrierGroundObject):
|
|||||||
name=name,
|
name=name,
|
||||||
category="LHA",
|
category="LHA",
|
||||||
group_id=group_id,
|
group_id=group_id,
|
||||||
object_id=0,
|
|
||||||
position=control_point.position,
|
position=control_point.position,
|
||||||
heading=0,
|
heading=0,
|
||||||
cp_id=control_point.id,
|
control_point=control_point,
|
||||||
dcs_identifier="LHA",
|
dcs_identifier="LHA",
|
||||||
airbase_group=True,
|
airbase_group=True,
|
||||||
sea_object=True
|
sea_object=True
|
||||||
@ -189,16 +177,18 @@ class MissileSiteGroundObject(TheaterGroundObject):
|
|||||||
name=name,
|
name=name,
|
||||||
category="aa",
|
category="aa",
|
||||||
group_id=group_id,
|
group_id=group_id,
|
||||||
object_id=0,
|
|
||||||
position=position,
|
position=position,
|
||||||
heading=0,
|
heading=0,
|
||||||
cp_id=control_point.id,
|
control_point=control_point,
|
||||||
dcs_identifier="AA",
|
dcs_identifier="AA",
|
||||||
airbase_group=False,
|
airbase_group=False,
|
||||||
sea_object=False
|
sea_object=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Differentiate types.
|
||||||
|
# This type gets used both for AA sites (SAM, AAA, or SHORAD) but also for the
|
||||||
|
# armor garrisons at airbases. These should each be split into their own types.
|
||||||
class SamGroundObject(TheaterGroundObject):
|
class SamGroundObject(TheaterGroundObject):
|
||||||
def __init__(self, name: str, group_id: int, position: Point,
|
def __init__(self, name: str, group_id: int, position: Point,
|
||||||
control_point: ControlPoint, for_airbase: bool) -> None:
|
control_point: ControlPoint, for_airbase: bool) -> None:
|
||||||
@ -206,14 +196,26 @@ class SamGroundObject(TheaterGroundObject):
|
|||||||
name=name,
|
name=name,
|
||||||
category="aa",
|
category="aa",
|
||||||
group_id=group_id,
|
group_id=group_id,
|
||||||
object_id=0,
|
|
||||||
position=position,
|
position=position,
|
||||||
heading=0,
|
heading=0,
|
||||||
cp_id=control_point.id,
|
control_point=control_point,
|
||||||
dcs_identifier="AA",
|
dcs_identifier="AA",
|
||||||
airbase_group=for_airbase,
|
airbase_group=for_airbase,
|
||||||
sea_object=False
|
sea_object=False
|
||||||
)
|
)
|
||||||
|
# Set by the SAM unit generator if the generated group is compatible
|
||||||
|
# with Skynet.
|
||||||
|
self.skynet_capable = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def group_name(self) -> str:
|
||||||
|
if self.skynet_capable:
|
||||||
|
# Prefix the group names of SAM sites with the side color so Skynet
|
||||||
|
# can find them.
|
||||||
|
color = "BLUE" if self.control_point.captured else "RED"
|
||||||
|
return f"{color}|SAM|{self.group_id}"
|
||||||
|
else:
|
||||||
|
return super().group_name
|
||||||
|
|
||||||
|
|
||||||
class ShipGroundObject(TheaterGroundObject):
|
class ShipGroundObject(TheaterGroundObject):
|
||||||
@ -223,10 +225,9 @@ class ShipGroundObject(TheaterGroundObject):
|
|||||||
name=name,
|
name=name,
|
||||||
category="aa",
|
category="aa",
|
||||||
group_id=group_id,
|
group_id=group_id,
|
||||||
object_id=0,
|
|
||||||
position=position,
|
position=position,
|
||||||
heading=0,
|
heading=0,
|
||||||
cp_id=control_point.id,
|
control_point=control_point,
|
||||||
dcs_identifier="AA",
|
dcs_identifier="AA",
|
||||||
airbase_group=False,
|
airbase_group=False,
|
||||||
sea_object=True
|
sea_object=True
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user