From bcae51cc92895a08abba33820e3dc7ae31381dd7 Mon Sep 17 00:00:00 2001 From: Khopa Date: Sun, 23 Aug 2020 13:43:33 +0200 Subject: [PATCH] Added possibility to repair SAM sites (WIP) --- changelog.md | 17 +++- game/db.py | 93 ++++++++++++++---- game/event/event.py | 3 + gen/aircraft.py | 6 +- gen/flights/radio_generator.py | 4 + gen/sam/sam_group_generator.py | 34 +++++++ qt_ui/widgets/map/QLiberationMap.py | 2 +- qt_ui/widgets/map/QMapGroundObject.py | 12 ++- .../windows/groundobject/QGroundObjectMenu.py | 98 +++++++++++++++++++ .../QGroundObjectReplacementMenu.py | 0 resources/tools/mkrelease.py | 2 +- 11 files changed, 242 insertions(+), 29 deletions(-) create mode 100644 gen/flights/radio_generator.py create mode 100644 qt_ui/windows/groundobject/QGroundObjectMenu.py create mode 100644 qt_ui/windows/groundobject/QGroundObjectReplacementMenu.py diff --git a/changelog.md b/changelog.md index 3f2afbb2..bfee826f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,12 +1,21 @@ # 2.1.1 +## Features/Improvements : + +* **[Units/Factions]** Added F-16C to USA 1990 +* **[Units/Factions]** Added MQ-9 Reaper as CAS unit for USA 2005 +* **[Units/Factions]** Added Mig-21, Mig-23, SA-342L to Syria 2011 + ## Fixed issues : -* **[UI/UX]** Spelling issues -* **[Campaign Generator]** Tarawa was placed on land in Syrian Civil War campaign +* **[UI/UX]** Spelling issues (Thanks to Github contributor steveveepee) +* **[Campaign Generator]** LHA was placed on land in Syrian Civil War campaign * **[Campaign Generator]** Fixed inverted configuration for Syria full map -* **[Units/Factions]** Minor changes to USA 1990 -* **[Units/Factions]** AH-64A now has default payloads. AH-64D has payloads for more mission types. +* **[Campaign Generator]** Syria "Inherent Resolve" campaign, added Incirlik Air Base +* **[Mission Generator]** AH-1W was not used by AI to generate CAS mission by default +* **[Mission Generator]** Fixed F-16C targeting pod not being added to payload +* **[Mission Generator]** AH-64A and AH-64D payloads fix. + # 2.1.0 diff --git a/game/db.py b/game/db.py index 6cae3bae..a4cc3e4a 100644 --- a/game/db.py +++ b/game/db.py @@ -295,23 +295,6 @@ PRICES = { Unarmed.Transport_M818: 3, - AirDefence.AAA_Vulcan_M163: 5, - AirDefence.SAM_Linebacker_M6: 10, - - AirDefence.AAA_ZU_23_Closed: 2, - AirDefence.SPAAA_ZSU_23_4_Shilka: 4, - AirDefence.SAM_SA_9_Strela_1_9P31: 8, - AirDefence.SAM_SA_19_Tunguska_2S6: 15, - AirDefence.SAM_SA_6_Kub_LN_2P25: 22, - AirDefence.SAM_SA_8_Osa_9A33: 12, - AirDefence.SAM_SA_3_S_125_LN_5P73: 20, - AirDefence.SAM_SA_2_LN_SM_90: 15, - AirDefence.SAM_SA_11_Buk_LN_9A310M1: 25, - AirDefence.SAM_Hawk_PCP: 20, - AirDefence.SAM_Patriot_LN_M901: 60, - AirDefence.SAM_SA_10_S_300PS_LN_5P85C: 60, - AirDefence.SAM_Chaparral_M48: 10, - # WW2 Armor.MT_Pz_Kpfw_V_Panther_Ausf_G:24, Armor.MT_Pz_Kpfw_IV_Ausf_H:16, @@ -332,9 +315,6 @@ PRICES = { Armor.LAC_M8_Greyhound: 8, Armor.TD_M10_GMC: 14, Armor.StuG_III_Ausf__G: 12, - AirDefence.AAA_Bofors_40mm: 8, - AirDefence.AAA_8_8cm_Flak_36: 8, - AirDefence.AAA_8_8cm_Flak_18: 12, Artillery.M12_GMC: 10, Artillery.Sturmpanzer_IV_Brummbär: 10, @@ -348,6 +328,79 @@ PRICES = { Dry_cargo_ship_Ivanov: 10, Tanker_Elnya_160: 10, + # Air Defence units + AirDefence.SAM_SA_19_Tunguska_2S6: 30, + AirDefence.SAM_SA_6_Kub_LN_2P25: 20, + AirDefence.SAM_SA_3_S_125_LN_5P73: 6, + AirDefence.SAM_SA_10_S_300PS_LN_5P85C: 22, + AirDefence.SAM_SA_10_S_300PS_LN_5P85D: 22, + AirDefence.SAM_SA_11_Buk_LN_9A310M1: 30, + AirDefence.SAM_SA_8_Osa_9A33: 28, + AirDefence.SAM_SA_15_Tor_9A331: 40, + AirDefence.SAM_SA_13_Strela_10M3_9A35M3: 24, + AirDefence.SAM_SA_9_Strela_1_9P31: 16, + AirDefence.SAM_SA_11_Buk_CC_9S470M1: 25, + AirDefence.SAM_SA_8_Osa_LD_9T217: 22, + AirDefence.SAM_Patriot_AMG_AN_MRC_137: 35, + AirDefence.SAM_Patriot_ECS_AN_MSQ_104: 30, + AirDefence.SPAAA_Gepard: 24, + AirDefence.SAM_Hawk_PCP: 14, + AirDefence.AAA_Vulcan_M163: 12, + AirDefence.SAM_Hawk_LN_M192: 8, + AirDefence.SAM_Chaparral_M48: 16, + AirDefence.SAM_Linebacker_M6: 18, + AirDefence.SAM_Patriot_LN_M901: 15, + AirDefence.SAM_Avenger_M1097: 20, + AirDefence.SAM_Patriot_EPP_III: 15, + AirDefence.SAM_Patriot_ICC: 18, + AirDefence.SAM_Roland_ADS: 12, + AirDefence.SAM_SA_10_S_300PS_CP_54K6: 18, + AirDefence.Stinger_MANPADS: 6, + AirDefence.SAM_Stinger_comm_dsr: 4, + AirDefence.SAM_Stinger_comm: 4, + AirDefence.SPAAA_ZSU_23_4_Shilka: 12, + AirDefence.AAA_ZU_23_Closed: 6, + AirDefence.AAA_ZU_23_Emplacement: 6, + AirDefence.AAA_ZU_23_on_Ural_375: 8, + AirDefence.AAA_ZU_23_Insurgent_Closed: 6, + AirDefence.AAA_ZU_23_Insurgent_on_Ural_375: 8, + AirDefence.AAA_ZU_23_Insurgent: 6, + AirDefence.SAM_SA_18_Igla_MANPADS: 10, + AirDefence.SAM_SA_18_Igla_comm: 8, + AirDefence.SAM_SA_18_Igla_S_MANPADS: 12, + AirDefence.SAM_SA_18_Igla_S_comm: 8, + AirDefence.EWR_1L13: 30, + AirDefence.SAM_SA_6_Kub_STR_9S91: 22, + AirDefence.SAM_SA_10_S_300PS_TR_30N6: 24, + AirDefence.SAM_SA_10_S_300PS_SR_5N66M: 30, + AirDefence.EWR_55G6: 30, + AirDefence.SAM_SA_10_S_300PS_SR_64H6E: 30, + AirDefence.SAM_SA_11_Buk_SR_9S18M1: 28, + AirDefence.CP_9S80M1_Sborka: 10, + AirDefence.SAM_Hawk_TR_AN_MPQ_46: 14, + AirDefence.SAM_Hawk_SR_AN_MPQ_50: 18, + AirDefence.SAM_Patriot_STR_AN_MPQ_53: 22, + AirDefence.SAM_Hawk_CWAR_AN_MPQ_55: 20, + AirDefence.SAM_SR_P_19: 14, + AirDefence.SAM_Roland_EWR: 16, + AirDefence.SAM_SA_3_S_125_TR_SNR: 14, + AirDefence.SAM_SA_2_LN_SM_90: 8, + AirDefence.SAM_SA_2_TR_SNR_75_Fan_Song: 12, + AirDefence.Rapier_FSA_Launcher: 6, + AirDefence.Rapier_FSA_Optical_Tracker: 12, + AirDefence.Rapier_FSA_Blindfire_Tracker: 16, + AirDefence.HQ_7_Self_Propelled_LN: 20, + AirDefence.HQ_7_Self_Propelled_STR: 24, + AirDefence.AAA_8_8cm_Flak_18: 6, + AirDefence.AAA_Flak_38: 6, + AirDefence.AAA_8_8cm_Flak_36: 8, + AirDefence.AAA_8_8cm_Flak_37: 10, + AirDefence.AAA_Flak_Vierling_38:6, + AirDefence.AAA_Kdo_G_40: 8, + AirDefence.Flak_Searchlight_37: 4, + AirDefence.Maschinensatz_33: 10, + AirDefence.AAA_8_8cm_Flak_41: 12, + AirDefence.AAA_Bofors_40mm: 8, # FRENCH PACK MOD frenchpack.AMX_10RCR: 10, diff --git a/game/event/event.py b/game/event/event.py index 6af3d6c9..e064ad94 100644 --- a/game/event/event.py +++ b/game/event/event.py @@ -178,9 +178,12 @@ class Event: for i, ground_object in enumerate(cp.ground_objects): if ground_object.dcs_identifier in ["AA", "CARRIER", "LHA"]: for g in ground_object.groups: + if not hasattr(g, "units_losts"): + g.units_losts = [] for u in g.units: if u.name == destroyed_ground_unit_name: g.units.remove(u) + g.units_losts.append(u) destroyed_units = destroyed_units + 1 info.text = u.type ucount = sum([len(g.units) for g in ground_object.groups]) diff --git a/gen/aircraft.py b/gen/aircraft.py index 70826ea5..118189ff 100644 --- a/gen/aircraft.py +++ b/gen/aircraft.py @@ -516,6 +516,10 @@ class AircraftConflictGenerator: group.points[0].tasks.append(OptRestrictJettison(True)) for point in flight.points: - group.add_waypoint(Point(point.x,point.y), point.alt) + group.add_waypoint(Point(point.x, point.y), point.alt) + + + def setup_radio_preset(self, flight, group): + pass diff --git a/gen/flights/radio_generator.py b/gen/flights/radio_generator.py new file mode 100644 index 00000000..1e647287 --- /dev/null +++ b/gen/flights/radio_generator.py @@ -0,0 +1,4 @@ +from dcs.unitgroup import FlyingGroup + + + diff --git a/gen/sam/sam_group_generator.py b/gen/sam/sam_group_generator.py index c1c4c95d..7979cb16 100644 --- a/gen/sam/sam_group_generator.py +++ b/gen/sam/sam_group_generator.py @@ -65,6 +65,40 @@ SAM_MAP = { AirDefence.HQ_7_Self_Propelled_LN: HQ7Generator } +SAM_PRICES = { + AirDefence.SAM_Hawk_PCP: 35, + AirDefence.AAA_ZU_23_Emplacement: 10, + AirDefence.AAA_ZU_23_Closed: 10, + AirDefence.AAA_ZU_23_on_Ural_375: 10, + AirDefence.AAA_ZU_23_Insurgent_on_Ural_375: 10, + AirDefence.AAA_ZU_23_Insurgent_Closed: 10, + AirDefence.AAA_ZU_23_Insurgent: 10, + AirDefence.SPAAA_ZSU_23_4_Shilka: 10, + AirDefence.AAA_Vulcan_M163: 15, + AirDefence.SAM_Linebacker_M6: 20, + AirDefence.Rapier_FSA_Launcher: 20, + AirDefence.SAM_Avenger_M1097: 22, + AirDefence.SPAAA_Gepard: 24, + AirDefence.SAM_Roland_ADS: 40, + AirDefence.SAM_Patriot_LN_M901: 85, + AirDefence.SAM_Patriot_EPP_III: 85, + AirDefence.SAM_Chaparral_M48: 25, + AirDefence.AAA_Bofors_40mm: 15, + AirDefence.AAA_8_8cm_Flak_36: 15, + AirDefence.SAM_SA_2_LN_SM_90: 30, + AirDefence.SAM_SA_3_S_125_LN_5P73: 35, + AirDefence.SAM_SA_6_Kub_LN_2P25: 45, + AirDefence.SAM_SA_8_Osa_9A33: 30, + AirDefence.SAM_SA_9_Strela_1_9P31: 25, + AirDefence.SAM_SA_10_S_300PS_LN_5P85C: 80, + AirDefence.SAM_SA_10_S_300PS_CP_54K6: 80, + AirDefence.SAM_SA_11_Buk_LN_9A310M1: 60, + AirDefence.SAM_SA_13_Strela_10M3_9A35M3: 30, + AirDefence.SAM_SA_15_Tor_9A331: 40, + AirDefence.SAM_SA_19_Tunguska_2S6: 35, + AirDefence.HQ_7_Self_Propelled_LN: 35 +} + def generate_anti_air_group(game, parent_cp, ground_object, faction:str): """ This generate a SAM group diff --git a/qt_ui/widgets/map/QLiberationMap.py b/qt_ui/widgets/map/QLiberationMap.py index 2e763071..5311fac4 100644 --- a/qt_ui/widgets/map/QLiberationMap.py +++ b/qt_ui/widgets/map/QLiberationMap.py @@ -143,7 +143,7 @@ class QLiberationMap(QGraphicsView): go_pos = self._transform_point(ground_object.position) if not ground_object.airbase_group: buildings = self.game.theater.find_ground_objects_by_obj_name(ground_object.obj_name) - scene.addItem(QMapGroundObject(self, go_pos[0], go_pos[1], 12, 12, cp, ground_object, buildings)) + scene.addItem(QMapGroundObject(self, go_pos[0], go_pos[1], 12, 12, cp, ground_object, self.game, buildings)) if ground_object.category == "aa" and self.get_display_rule("sam"): max_range = 0 diff --git a/qt_ui/widgets/map/QMapGroundObject.py b/qt_ui/widgets/map/QMapGroundObject.py index dbbf4d79..d2950f3a 100644 --- a/qt_ui/widgets/map/QMapGroundObject.py +++ b/qt_ui/widgets/map/QMapGroundObject.py @@ -3,17 +3,19 @@ from PySide2.QtGui import QPainter from PySide2.QtWidgets import QGraphicsRectItem, QGraphicsItem, QGraphicsSceneHoverEvent, QGraphicsSceneMouseEvent import qt_ui.uiconstants as CONST -from game import db +from game import db, Game +from qt_ui.windows.groundobject.QGroundObjectMenu import QGroundObjectMenu from theater import TheaterGroundObject, ControlPoint class QMapGroundObject(QGraphicsRectItem): - def __init__(self, parent, x: float, y: float, w: float, h: float, cp: ControlPoint, model: TheaterGroundObject, buildings=[]): + def __init__(self, parent, x: float, y: float, w: float, h: float, cp: ControlPoint, model: TheaterGroundObject, game:Game, buildings=[]): super(QMapGroundObject, self).__init__(x, y, w, h) self.model = model self.cp = cp self.parent = parent + self.game = game self.setAcceptHoverEvents(True) self.setZValue(2) self.buildings = buildings @@ -39,6 +41,8 @@ class QMapGroundObject(QGraphicsRectItem): tooltip = tooltip + str(building.dcs_identifier) + "\n" self.setToolTip(tooltip[:-1]) + def mousePressEvent(self, event:QGraphicsSceneMouseEvent): + self.openEditionMenu() def paint(self, painter, option, widget=None): #super(QMapControlPoint, self).paint(painter, option, widget) @@ -72,3 +76,7 @@ class QMapGroundObject(QGraphicsRectItem): def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent): self.update() + def openEditionMenu(self): + self.editionMenu = QGroundObjectMenu(self.window(), self.model, self.cp, self.game) + self.editionMenu.show() + diff --git a/qt_ui/windows/groundobject/QGroundObjectMenu.py b/qt_ui/windows/groundobject/QGroundObjectMenu.py new file mode 100644 index 00000000..ad8d0ff7 --- /dev/null +++ b/qt_ui/windows/groundobject/QGroundObjectMenu.py @@ -0,0 +1,98 @@ +import logging + +from PySide2.QtGui import QCloseEvent +from PySide2.QtWidgets import QHBoxLayout, QWidget, QDialog, QGridLayout, QLabel, QGroupBox, QVBoxLayout, QPushButton +from dcs import Point + +from game import Game +from game.db import PRICES, unit_type_of +from qt_ui.widgets.QBudgetBox import QBudgetBox +from qt_ui.windows.GameUpdateSignal import GameUpdateSignal +from theater import ControlPoint, TheaterGroundObject + + +class QGroundObjectMenu(QDialog): + + def __init__(self, parent, ground_object: TheaterGroundObject, cp: ControlPoint, game: Game): + super(QGroundObjectMenu, self).__init__(parent) + self.setMinimumWidth(350) + self.ground_object = ground_object + self.cp = cp + self.game = game + self.setWindowTitle("Location " + self.ground_object.obj_name) + self.intelBox = QGroupBox("Units :") + self.intelLayout = QGridLayout() + self.init_ui() + + def init_ui(self): + + self.mainLayout = QVBoxLayout() + self.budget = QBudgetBox(self.game) + self.budget.setGame(self.game) + + self.doLayout() + + self.mainLayout.addWidget(self.intelBox) + self.setLayout(self.mainLayout) + + def doLayout(self): + self.intelBox = QGroupBox("Units :") + self.intelLayout = QGridLayout() + i = 0 + for g in self.ground_object.groups: + if not hasattr(g, "units_losts"): + g.units_losts = [] + for u in g.units: + self.intelLayout.addWidget(QLabel("Unit #" + str(u.id) + " - " + str(u.type) + ""), i, 0) + i = i + 1 + + for u in g.units_losts: + + utype = unit_type_of(u) + if utype in PRICES: + price = PRICES[utype] + else: + price = 6 + + self.intelLayout.addWidget(QLabel("Unit #" + str(u.id) + " - " + str(u.type) + " [DEAD]"), i, 0) + repair = QPushButton("Repair [" + str(price) + "M]") + repair.setProperty("style", "btn-primary") + repair.clicked.connect(lambda u=u, g=g, p=price: self.repair_unit(g, u, p)) + self.intelLayout.addWidget(repair, i, 1) + i = i + 1 + self.intelBox.setLayout(self.intelLayout) + + def do_refresh_layout(self): + try: + for i in range(self.mainLayout.count()): + self.mainLayout.removeItem(self.mainLayout.itemAt(i)); + self.doLayout() + self.mainLayout.addWidget(self.intelBox) + except Exception as e: + print(e) + + def repair_unit(self, group, unit, price): + + print(group) + print(unit.type) + [print(u.id) for u in group.units] + + if self.game.budget > price: + self.game.budget -= price + group.units_losts = [u for u in group.units_losts if u.id != unit.id] + group.units.append(unit) + GameUpdateSignal.get_instance().updateGame(self.game) + + # Remove destroyed units in the vicinity + destroyed_units = self.game.get_destroyed_units() + for d in destroyed_units: + p = Point(d["x"], d["z"]) + if p.distance_to_point(unit.position) < 15: + destroyed_units.remove(d) + logging.info("Removed destroyed units " + str(d)) + logging.info("Repaired unit : " + str(unit.id) + " " + str(unit.type)) + + self.do_refresh_layout() + + def closeEvent(self, closeEvent: QCloseEvent): + GameUpdateSignal.get_instance().updateGame(self.game) diff --git a/qt_ui/windows/groundobject/QGroundObjectReplacementMenu.py b/qt_ui/windows/groundobject/QGroundObjectReplacementMenu.py new file mode 100644 index 00000000..e69de29b diff --git a/resources/tools/mkrelease.py b/resources/tools/mkrelease.py index f7dea204..a76029b9 100644 --- a/resources/tools/mkrelease.py +++ b/resources/tools/mkrelease.py @@ -45,7 +45,7 @@ def _mk_archieve(): shutil.rmtree("./dist") except FileNotFoundError: pass - os.system("pyinstaller.exe pyinstaller.spec") + os.system("pyinstaller.exe --clean pyinstaller.spec") #archieve = ZipFile(path, "w") #archieve.writestr("dcs_liberation.bat", "cd dist\\dcs_liberation\r\nliberation_main \"%UserProfile%\\Saved Games\" \"{}\"".format(VERSION)) #_zip_dir(archieve, "./dist/dcs_liberation")