diff --git a/game/event/event.py b/game/event/event.py index 841093e7..58de2b63 100644 --- a/game/event/event.py +++ b/game/event/event.py @@ -181,7 +181,7 @@ class Event: # -- AA Site groups for i, ground_object in enumerate(cp.ground_objects): - if ground_object.dcs_identifier == "AA": + if ground_object.dcs_identifier in ["AA", "CARRIER"]: for g in ground_object.groups: for u in g.units: if u.name == destroyed_ground_unit_name: diff --git a/game/factions/china_2000.py b/game/factions/china_2000.py index 1e654d15..a3b68ad8 100644 --- a/game/factions/china_2000.py +++ b/game/factions/china_2000.py @@ -44,5 +44,12 @@ China_2000 = { AirDefence.AAA_ZU_23_Closed, AirDefence.Rapier_FSA_Launcher, # Standing as PL-9C Shorad AirDefence.HQ_7_Self_Propelled_LN + ], "aircraft_carrier": [ + CV_1143_5_Admiral_Kuznetsov, + ], "destroyer": [ + Type_052B_Destroyer, + Type_052C_Destroyer + ], "cruiser": [ + Type_054A_Frigate, ] } \ No newline at end of file diff --git a/game/factions/russia_1975.py b/game/factions/russia_1975.py index e002fd90..88533eca 100644 --- a/game/factions/russia_1975.py +++ b/game/factions/russia_1975.py @@ -1,7 +1,7 @@ from dcs.helicopters import Mi_8MT, Mi_24V from dcs.planes import MiG_21Bis, MiG_23MLD, MiG_25PD, MiG_29A, Su_17M4, Su_24M, Su_25, IL_76MD, IL_78M, An_26B, An_30M, \ Yak_40, A_50 -from dcs.ships import CV_1143_5_Admiral_Kuznetsov, Dry_cargo_ship_Ivanov, Bulk_cargo_ship_Yakushev, Tanker_Elnya_160 +from dcs.ships import * from dcs.vehicles import AirDefence, Armor, Unarmed, Infantry Russia_1975 = { @@ -48,8 +48,14 @@ Russia_1975 = { Dry_cargo_ship_Ivanov, Tanker_Elnya_160 ], - "shorad":[ + "shorad": [ AirDefence.AAA_ZU_23_Emplacement, AirDefence.SPAAA_ZSU_23_4_Shilka + ], "aircraft_carrier": [ + CV_1143_5_Admiral_Kuznetsov, + ], "destroyer": [ + FF_1135M_Rezky, + ], "cruiser": [ + CGN_1144_2_Pyotr_Velikiy, ] } \ No newline at end of file diff --git a/game/factions/russia_1990.py b/game/factions/russia_1990.py index 42228528..ca2421cc 100644 --- a/game/factions/russia_1990.py +++ b/game/factions/russia_1990.py @@ -53,5 +53,11 @@ Russia_1990 = { AirDefence.SAM_SA_9_Strela_1_9P31, AirDefence.SAM_SA_13_Strela_10M3_9A35M3, AirDefence.SPAAA_ZSU_23_4_Shilka + ], "aircraft_carrier": [ + CV_1143_5_Admiral_Kuznetsov, + ], "destroyer": [ + FF_1135M_Rezky, + ], "cruiser": [ + FSG_1241_1MP_Molniya, ] } \ No newline at end of file diff --git a/game/factions/russia_2010.py b/game/factions/russia_2010.py index c53ddd90..d6abc1e0 100644 --- a/game/factions/russia_2010.py +++ b/game/factions/russia_2010.py @@ -49,5 +49,11 @@ Russia_2010 = { "shorad":[ AirDefence.SAM_SA_19_Tunguska_2S6, AirDefence.SAM_SA_13_Strela_10M3_9A35M3 + ], "aircraft_carrier": [ + CV_1143_5_Admiral_Kuznetsov, + ], "destroyer": [ + FF_1135M_Rezky, + ], "cruiser": [ + FSG_1241_1MP_Molniya, ] } diff --git a/game/factions/usa_1990.py b/game/factions/usa_1990.py index 0238ebce..c30ac322 100644 --- a/game/factions/usa_1990.py +++ b/game/factions/usa_1990.py @@ -34,11 +34,18 @@ USA_1990 = { AirDefence.SAM_Hawk_PCP, - CVN_74_John_C__Stennis, LHA_1_Tarawa, Armed_speedboat, ], "shorad":[ AirDefence.SAM_Avenger_M1097, + ], "aircraft_carrier": [ + CVN_74_John_C__Stennis, + ], "helicopter_carrier": [ + LHA_1_Tarawa, + ], "destroyer": [ + Oliver_Hazzard_Perry_class, + ], "cruiser": [ + Ticonderoga_class, ] } \ No newline at end of file diff --git a/game/factions/usa_2005.py b/game/factions/usa_2005.py index bbcc8cf7..b07d8256 100644 --- a/game/factions/usa_2005.py +++ b/game/factions/usa_2005.py @@ -38,7 +38,16 @@ USA_2005 = { CVN_74_John_C__Stennis, LHA_1_Tarawa, Armed_speedboat, - ], "shorad":[ + ], "shorad": [ AirDefence.SAM_Avenger_M1097, + ], "aircraft_carrier": [ + CVN_74_John_C__Stennis, + ], "helicopter_carrier": [ + LHA_1_Tarawa, + ], "destroyer": [ + Oliver_Hazzard_Perry_class, + ], "cruiser": [ + Ticonderoga_class, ] -} \ No newline at end of file + +} diff --git a/game/game.py b/game/game.py index 68b7e8ac..423bdc68 100644 --- a/game/game.py +++ b/game/game.py @@ -313,10 +313,10 @@ class Game: # Plan flights for next turn self.planners = {} for cp in self.theater.controlpoints: - planner = FlightPlanner(cp, self) - planner.plan_flights() - self.planners[cp.id] = planner - print(planner) + if cp.has_runway(): + planner = FlightPlanner(cp, self) + planner.plan_flights() + self.planners[cp.id] = planner @property diff --git a/game/operation/operation.py b/game/operation/operation.py index d0fb5aaa..761a5108 100644 --- a/game/operation/operation.py +++ b/game/operation/operation.py @@ -138,6 +138,9 @@ class Operation: # Air Support (Tanker & Awacs) self.airsupportgen.generate(self.is_awacs_enabled) + # Generate carrier groups & ships + + # Generate Activity on the map for cp in self.game.theater.controlpoints: side = cp.captured @@ -164,7 +167,6 @@ class Operation: else: self.current_mission.groundControl.red_tactical_commander = self.ca_slots - # triggers if self.game.is_player_attack(self.conflict.attackers_country): cp = self.conflict.from_cp diff --git a/gen/defenses/armor_group_generator.py b/gen/defenses/armor_group_generator.py index dec5196d..3824f7da 100644 --- a/gen/defenses/armor_group_generator.py +++ b/gen/defenses/armor_group_generator.py @@ -8,11 +8,11 @@ from gen.defenses.armored_group_generator import ArmoredGroupGenerator def generate_armor_group(faction:str, game, ground_object): """ - This generate a SAM group + This generate a group of ground units :param parentCp: The parent control point - :param ground_object: The ground object which will own the sam group + :param ground_object: The ground object which will own the group :param country: Owner country - :return: Nothing, but put the group reference inside the ground object + :return: Generated group """ possible_unit = [u for u in db.FACTIONS[faction]["units"] if u in Armor.__dict__.values()] diff --git a/gen/fleet/carrier_group.py b/gen/fleet/carrier_group.py new file mode 100644 index 00000000..669e6f9b --- /dev/null +++ b/gen/fleet/carrier_group.py @@ -0,0 +1,25 @@ +import random + +from gen.sam.group_generator import GroupGenerator + + +class CarrierGroupGenerator(GroupGenerator): + + def __init__(self, game, ground_object, faction): + super(CarrierGroupGenerator, self).__init__(game, ground_object) + self.faction = faction + + def generate(self): + + # Add carrier + if self.faction["aircraft_carrier"]: + carrier_type = random.choice(self.faction["aircraft_carrier"]) + self.add_unit(carrier_type, "Carrier", self.position.x, self.position.y, self.heading) + + # Add destroyers escort + dd_type = random.choice(self.faction["destroyer"]) + self.add_unit(dd_type, "DD1", self.position.x + 250, self.position.y + 450, self.heading) + self.add_unit(dd_type, "DD2", self.position.x + 250, self.position.y - 450, self.heading) + + self.add_unit(dd_type, "DD3", self.position.x + 450, self.position.y + 850, self.heading) + self.add_unit(dd_type, "DD4", self.position.x + 450, self.position.y - 850, self.heading) \ No newline at end of file diff --git a/gen/fleet/heli_carrier_group.py b/gen/fleet/heli_carrier_group.py new file mode 100644 index 00000000..d9e26ea8 --- /dev/null +++ b/gen/fleet/heli_carrier_group.py @@ -0,0 +1,25 @@ +import random + +from gen.sam.group_generator import GroupGenerator + + +class HelicopterCarrierGroupGenerator(GroupGenerator): + + def __init__(self, game, ground_object, faction): + super(HelicopterCarrierGroupGenerator, self).__init__(game, ground_object) + self.faction = faction + + def generate(self): + + # Add carrier + if self.faction["aircraft_carrier"]: + carrier_type = random.choice(self.faction["helicopter_carrier"]) + self.add_unit(carrier_type, "Carrier", self.position.x, self.position.y, self.heading) + + # Add destroyers escort + dd_type = random.choice(self.faction["destroyer"]) + self.add_unit(dd_type, "DD1", self.position.x + 50, self.position.y + 150, self.heading) + self.add_unit(dd_type, "DD2", self.position.x + 50, self.position.y - 150, self.heading) + + self.add_unit(dd_type, "DD3", self.position.x + 150, self.position.y + 250, self.heading) + self.add_unit(dd_type, "DD4", self.position.x + 150, self.position.y - 250, self.heading) \ No newline at end of file diff --git a/gen/fleet/ship_group_generator.py b/gen/fleet/ship_group_generator.py new file mode 100644 index 00000000..c902d09b --- /dev/null +++ b/gen/fleet/ship_group_generator.py @@ -0,0 +1,20 @@ +import random + +from dcs.vehicles import Armor + +from game import db +from gen.defenses.armored_group_generator import ArmoredGroupGenerator +from gen.fleet.carrier_group import CarrierGroupGenerator + + +def generate_carrier_group(faction:str, game, ground_object): + """ + This generate a ship group + :param parentCp: The parent control point + :param ground_object: The ground object which will own the ship group + :param country: Owner country + :return: Nothing, but put the group reference inside the ground object + """ + generator = CarrierGroupGenerator(game, ground_object, db.FACTIONS[faction]) + generator.generate() + return generator.get_generated_group() diff --git a/gen/groundobjectsgen.py b/gen/groundobjectsgen.py index 7fe82734..0df9cebf 100644 --- a/gen/groundobjectsgen.py +++ b/gen/groundobjectsgen.py @@ -65,15 +65,37 @@ class GroundObjectsGenerator: if ground_object.dcs_identifier == "AA": for g in ground_object.groups: if len(g.units) > 0: - vg = self.m.vehicle_group(side, g.name, unit_type_from_name(g.units[0].type), position=g.position) + + utype = unit_type_from_name(g.units[0].type) + + vg = self.m.vehicle_group(side, g.name, utype, position=g.position, heading=g.units[0].heading) vg.units[0].name = self.m.string(g.units[0].name) - for i,u in enumerate(g.units): + for i, u in enumerate(g.units): if i > 0: vehicle = Vehicle(self.m.next_unit_id(), self.m.string(u.name), u.type) vehicle.position.x = u.position.x vehicle.position.y = u.position.y vehicle.heading = u.heading vg.add_unit(vehicle) + elif ground_object.dcs_identifier == "CARRIER": + for g in ground_object.groups: + if len(g.units) > 0: + + utype = unit_type_from_name(g.units[0].type) + + sg = self.m.ship_group(side, g.name, utype, position=g.position, heading=g.units[0].heading) + + sg.units[0].name = self.m.string(g.units[0].name) + for i, u in enumerate(g.units): + if i > 0: + print(u.type) + print(type(u.type)) + ship = Ship(self.m.next_unit_id(), self.m.string(u.name), unit_type_from_name(u.type)) + ship.position.x = u.position.x + ship.position.y = u.position.y + ship.heading = u.heading + sg.add_unit(ship) + else: if ground_object.dcs_identifier in warehouse_map: static_type = warehouse_map[ground_object.dcs_identifier] diff --git a/qt_ui/widgets/map/QLiberationMap.py b/qt_ui/widgets/map/QLiberationMap.py index c49cc731..3ef903ee 100644 --- a/qt_ui/widgets/map/QLiberationMap.py +++ b/qt_ui/widgets/map/QLiberationMap.py @@ -9,7 +9,7 @@ from dcs import Point import qt_ui.uiconstants as CONST from game import Game, db from game.event import InfantryTransportEvent, StrikeEvent, BaseAttackEvent, UnitsDeliveryEvent, Event, \ - FrontlineAttackEvent, FrontlinePatrolEvent, ConvoyStrikeEvent + FrontlineAttackEvent, FrontlinePatrolEvent, ConvoyStrikeEvent, ControlPointType from gen import Conflict from qt_ui.widgets.map.QLiberationScene import QLiberationScene from qt_ui.widgets.map.QMapControlPoint import QMapControlPoint diff --git a/qt_ui/windows/QBaseMenu.py b/qt_ui/windows/QBaseMenu.py index bedad38a..a907b9cc 100644 --- a/qt_ui/windows/QBaseMenu.py +++ b/qt_ui/windows/QBaseMenu.py @@ -56,7 +56,8 @@ class QBaseMenu(QDialog): title = QLabel("" + self.cp.name + "") title.setAlignment(Qt.AlignLeft | Qt.AlignTop) title.setProperty("style", "base-title") - unitsPower = QLabel("{} / {}".format(self.cp.base.total_planes, self.cp.base.total_armor)) + unitsPower = QLabel("{} / {} / Runway : {}".format(self.cp.base.total_planes, self.cp.base.total_armor, + "Available" if self.cp.has_runway() else "Unavailable")) self.topLayout.addWidget(title) self.topLayout.addWidget(unitsPower) @@ -154,7 +155,9 @@ class QBaseMenu(QDialog): self.rightLayout.addWidget(QPlannedFlightsView(self.game.planners[self.cp.id])) except Exception: traceback.print_exc() - self.rightLayout.addWidget(QAirportInformation(self.cp, self.airport)) + + if self.airport: + self.rightLayout.addWidget(QAirportInformation(self.cp, self.airport)) self.mainLayout.addLayout(self.rightLayout, 1, 2) self.setLayout(self.mainLayout) diff --git a/theater/controlpoint.py b/theater/controlpoint.py index b17e8d53..d5f6db84 100644 --- a/theater/controlpoint.py +++ b/theater/controlpoint.py @@ -1,13 +1,26 @@ import re import typing +from enum import Enum from dcs.mapping import * from dcs.terrain import Airport +from dcs.ships import CVN_74_John_C__Stennis, LHA_1_Tarawa, CV_1143_5_Admiral_Kuznetsov +from game import db from .theatergroundobject import TheaterGroundObject + +class ControlPointType(Enum): + AIRBASE = 0 # An airbase with slot for everything + AIRCRAFT_CARRIER_GROUP = 1 # A group with a Stennis type carrier (F/A-18, F-14 compatible) + LHA_GROUP = 2 # A group with a Tarawa carrier (Helicopters & Harrier) + FARP = 4 # A FARP, with slots for helicopters + FOB = 5 # A FOB (ground units only) + + class ControlPoint: + id = 0 position = None # type: Point name = None # type: str @@ -21,8 +34,10 @@ class ControlPoint: captured = False has_frontline = True frontline_offset = 0.0 + cptype: ControlPointType = None - def __init__(self, id: int, name: str, position: Point, at, radials: typing.Collection[int], size: int, importance: float, has_frontline=True): + def __init__(self, id: int, name: str, position: Point, at, radials: typing.Collection[int], size: int, importance: float, + has_frontline=True, cptype=ControlPointType.AIRBASE): import theater.base self.id = id @@ -31,6 +46,7 @@ class ControlPoint: self.position = position self.at = at self.ground_objects = [] + self.ships = [] self.size = size self.importance = importance @@ -39,16 +55,18 @@ class ControlPoint: self.radials = radials self.connected_points = [] self.base = theater.base.Base() + self.cptype = cptype @classmethod def from_airport(cls, airport: Airport, radials: typing.Collection[int], size: int, importance: float, has_frontline=True): assert airport - return cls(airport.id, airport.name, airport.position, airport, radials, size, importance, has_frontline) + return cls(airport.id, airport.name, airport.position, airport, radials, size, importance, has_frontline, cptype=ControlPointType.AIRBASE) @classmethod def carrier(cls, name: str, at: Point): import theater.conflicttheater - return cls(0, name, at, at, theater.conflicttheater.LAND, theater.conflicttheater.SIZE_SMALL, 1, has_frontline=False) + return cls(0, name, at, at, theater.conflicttheater.LAND, theater.conflicttheater.SIZE_SMALL, 1, + has_frontline=False, cptype=ControlPointType.AIRCRAFT_CARRIER_GROUP) def __str__(self): return self.name @@ -70,6 +88,24 @@ class ControlPoint: def connect(self, to): self.connected_points.append(to) + def has_runway(self): + """ + Check whether this control point can have aircraft taking off or landing. + :return: + """ + if self.cptype in [ControlPointType.AIRCRAFT_CARRIER_GROUP, ControlPointType.LHA_GROUP] : + for g in self.ground_objects: + if g.dcs_identifier == "CARRIER": + for group in g.groups: + for u in group.units: + if db.unit_type_from_name(u.type) in [CVN_74_John_C__Stennis, LHA_1_Tarawa, CV_1143_5_Admiral_Kuznetsov]: + return True + return False + elif self.cptype in [ControlPointType.AIRBASE, ControlPointType.FARP]: + return True + else: + return True + def is_connected(self, to) -> bool: return to in self.connected_points diff --git a/theater/start_generator.py b/theater/start_generator.py index c7d78943..8bd6905f 100644 --- a/theater/start_generator.py +++ b/theater/start_generator.py @@ -5,7 +5,9 @@ import typing import logging from gen.defenses.armor_group_generator import generate_armor_group +from gen.fleet.ship_group_generator import generate_carrier_group from gen.sam.sam_group_generator import generate_anti_air_group, generate_shorad_group +from theater import ControlPointType from theater.base import * from theater.conflicttheater import * @@ -68,12 +70,8 @@ def generate_groundobjects(theater: ConflictTheater, game): else: faction = game.enemy_name - for i in range(random.randint(2,6)): - point = find_location(True, cp.position, theater, 1000, 2800, []) - - if point is None: - print("Couldn't find point for {}".format(cp)) - continue + if cp.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP: + # Create ground object group group_id = group_id + 1 @@ -82,28 +80,52 @@ def generate_groundobjects(theater: ConflictTheater, game): g.object_id = 0 g.cp_id = cp.id g.airbase_group = True - g.dcs_identifier = "AA" + g.dcs_identifier = "CARRIER" g.heading = 0 - g.position = Point(point.x, point.y) - - if i == 0: - group = generate_armor_group(faction, game, g) - elif i == 1 and random.randint(0,1) == 0: - group = generate_anti_air_group(game, cp, g, faction) - elif random.randint(0, 2) == 1: - group = generate_shorad_group(game, cp, g, faction) - else: - group = generate_armor_group(faction, game, g) - + g.position = Point(cp.position.x, cp.position.y) + group = generate_carrier_group(faction, game, g) g.groups = [] if group is not None: g.groups.append(group) cp.ground_objects.append(g) - print("---------------------------") - print("CP Generation : " + cp.name) - for ground_object in cp.ground_objects: - print(ground_object.groups) + else: + for i in range(random.randint(2,6)): + point = find_location(True, cp.position, theater, 1000, 2800, []) + + if point is None: + print("Couldn't find point for {}".format(cp)) + continue + + group_id = group_id + 1 + + g = TheaterGroundObject() + g.group_id = group_id + g.object_id = 0 + g.cp_id = cp.id + g.airbase_group = True + g.dcs_identifier = "AA" + g.heading = 0 + g.position = Point(point.x, point.y) + + if i == 0: + group = generate_armor_group(faction, game, g) + elif i == 1 and random.randint(0,1) == 0: + group = generate_anti_air_group(game, cp, g, faction) + elif random.randint(0, 2) == 1: + group = generate_shorad_group(game, cp, g, faction) + else: + group = generate_armor_group(faction, game, g) + + g.groups = [] + if group is not None: + g.groups.append(group) + cp.ground_objects.append(g) + + print("---------------------------") + print("CP Generation : " + cp.name) + for ground_object in cp.ground_objects: + print(ground_object.groups) def find_location(on_ground, near, theater, min, max, others) -> typing.Optional[Point]: diff --git a/theater/theatergroundobject.py b/theater/theatergroundobject.py index 5b53454c..5e7fb5d6 100644 --- a/theater/theatergroundobject.py +++ b/theater/theatergroundobject.py @@ -31,6 +31,7 @@ ABBREV_NAME = { CATEGORY_MAP = { + "CARRIER": ["CARRIER"], "aa": ["AA"], "power": ["Workshop A", "Electric power box", "Garage small A"], "warehouse": ["Warehouse", "Hangar A"], diff --git a/userdata/debriefing.py b/userdata/debriefing.py index a1ad02d7..58d3ca5c 100644 --- a/userdata/debriefing.py +++ b/userdata/debriefing.py @@ -54,7 +54,8 @@ class Debriefing: type = db.unit_type_from_name(aircraft.split("|")[4]) player_unit = (country == self.player_country_id) aircraft = DebriefingDeadUnitInfo(country, player_unit, type) - self.dead_aircraft.append(aircraft) + if type is not None: + self.dead_aircraft.append(aircraft) except Exception as e: print(e) @@ -64,7 +65,8 @@ class Debriefing: type = db.unit_type_from_name(unit.split("|")[4]) player_unit = (country == self.player_country_id) unit = DebriefingDeadUnitInfo(country, player_unit, type) - self.dead_units.append(unit) + if type is not None: + self.dead_units.append(unit) except Exception as e: print(e)