From 6f4b7e0f1a0a411461c571e56bcaf01aebab224c Mon Sep 17 00:00:00 2001 From: Khopa Date: Mon, 3 Aug 2020 19:43:12 +0200 Subject: [PATCH] WIP --- changelog.md | 3 +- game/data/cap_capabilities_db.py | 33 ++++++++++ game/game.py | 16 +++++ game/operation/operation.py | 9 --- gen/armor.py | 66 +++++++++++++++---- gen/groundobjectsgen.py | 6 -- qt_ui/main.py | 3 +- qt_ui/windows/basemenu/QBaseMenu2.py | 7 +- qt_ui/windows/mission/QMissionPlanning.py | 4 ++ .../windows/mission/flight/QFlightCreator.py | 8 ++- 10 files changed, 122 insertions(+), 33 deletions(-) create mode 100644 game/data/cap_capabilities_db.py diff --git a/changelog.md b/changelog.md index 03925eab..36a84a4a 100644 --- a/changelog.md +++ b/changelog.md @@ -25,12 +25,13 @@ * **[Mission Generator]** Fixed A-20G spawning with no payload * **[Mission Generator]** Fixed Su-33 spawning too heavy to take off from carrier * **[Mission Generator]** Base defense units were not controllable with Combined Arms +* **[Mission Script/Performance]** Mission lua script will not listen to weapons fired event anymore and register every fired weapons. This should improve performance especially in WW2 scenarios or when rocket artillery is firing. * **[Campaign Generator]** Carrier name will now not appear for faction who do not have carriers * **[Units/Factions]** Remove JF-17 from USA 2005 faction * **[Units/Factions]** Removed Oliver Hazard Perry from cold war factions (too powerful sam system) * **[Bug]** On the persian gulf full map campaign, the two carriers were sharing the same id, this was causing a lot of bugs -* **[Performance]** Tuned the culling setting so that you cannot run into situation where no AI flights are generated +* **[Performance]** Tuned the culling setting so that you cannot run into situation where no friendly or enemy AI flights are generated * **[Other]** Application doesn't gracefully exit. * **[Other]** Other minor fixes diff --git a/game/data/cap_capabilities_db.py b/game/data/cap_capabilities_db.py new file mode 100644 index 00000000..eb367238 --- /dev/null +++ b/game/data/cap_capabilities_db.py @@ -0,0 +1,33 @@ +from dcs.planes import * +from pydcs_extensions.a4ec.a4ec import A_4E_C + +""" +This list contains the aircraft that do not use the guns as the last resort weapons, but as a main weapon +They'll RTB when they don't have gun ammo left +""" +GUNFIGHTERS = [ + + # Cold War + MiG_15bis, + MiG_19P, + MiG_21Bis, + F_86F_Sabre, + A_4E_C, + F_5E_3, + + # Trainers + C_101CC, + L_39ZA, + + # WW2 + P_51D_30_NA, + P_51D, + P_47D_30, + SpitfireLFMkIXCW, + SpitfireLFMkIX, + Bf_109K_4, + FW_190D9, + FW_190A8, + I_16, + +] \ No newline at end of file diff --git a/game/game.py b/game/game.py index ef99be2d..df42fe2e 100644 --- a/game/game.py +++ b/game/game.py @@ -76,6 +76,22 @@ class Game: self.__frontlineData = [] self.__destroyed_units = [] + self.sanitize_sides() + + + def sanitize_sides(self): + """ + Make sure the opposing factions are using different countries + :return: + """ + if self.player_country == self.enemy_country: + if self.player_country == "USA": + self.enemy_country = "USAF Aggressors" + elif self.player_country == "Russia": + self.enemy_country = "USSR" + else: + self.enemy_country = "Russia" + @property def player_faction(self): return db.FACTIONS[self.player_name] diff --git a/game/operation/operation.py b/game/operation/operation.py index fad744ae..8fddbb63 100644 --- a/game/operation/operation.py +++ b/game/operation/operation.py @@ -91,15 +91,6 @@ class Operation: p_country = self.game.player_country e_country = self.game.enemy_country - if self.game.player_country == self.game.enemy_country: - if self.game.player_country != "USAF Aggresors": - e_country = "USAF Aggresors" - else: - if self.game.player_country != "Russia": - e_country = "Russia" - else: - e_country = "USA" - 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)]()) diff --git a/gen/armor.py b/gen/armor.py index 6cb78a64..43348ca7 100644 --- a/gen/armor.py +++ b/gen/armor.py @@ -1,5 +1,5 @@ from dcs.action import AITaskPush, AITaskSet -from dcs.condition import TimeAfter, UnitDamaged, Or +from dcs.condition import TimeAfter, UnitDamaged, Or, GroupLifeLess from dcs.task import * from dcs.triggers import TriggerOnce, Event @@ -93,13 +93,11 @@ class GroundConflictGenerator: self.gen_infantry_group_for_group(g, False, self.mission.country(self.game.enemy_country), self.conflict.heading - 90) - # Plan combat actions for groups self.plan_action_for_groups(self.player_stance, player_groups, enemy_groups, self.conflict.heading + 90, self.conflict.from_cp, self.conflict.to_cp) self.plan_action_for_groups(self.enemy_stance, enemy_groups, player_groups, self.conflict.heading - 90, self.conflict.to_cp, self.conflict.from_cp) - def gen_infantry_group_for_group(self, group, is_player, side:Country, forward_heading): # Disable infantry unit gen if disabled @@ -161,8 +159,7 @@ class GroundConflictGenerator: dcs_group.add_trigger_action(hold_task) # Artillery strike random start - artillery_trigger = TriggerOnce(Event.NoEvent, - "ArtilleryFireTask #" + str(dcs_group.id)) + artillery_trigger = TriggerOnce(Event.NoEvent, "ArtilleryFireTask #" + str(dcs_group.id)) artillery_trigger.add_condition(TimeAfter(seconds=random.randint(1, 45)* 60)) fire_task = FireAtPoint(target, len(group.units) * 10, 100) @@ -179,7 +176,7 @@ class GroundConflictGenerator: # Hold position dcs_group.points[0].tasks.append(Hold()) - retreat = self.find_retreat_point(dcs_group, forward_heading) + retreat = self.find_retreat_point(dcs_group, forward_heading, (int)(RETREAT_DISTANCE/8)) dcs_group.add_waypoint(dcs_group.position.point_from_heading(forward_heading, 1), PointAction.OffRoad) dcs_group.points[1].tasks.append(Hold()) dcs_group.add_waypoint(retreat, PointAction.OffRoad) @@ -190,12 +187,15 @@ class GroundConflictGenerator: if i < len(dcs_group.units) - 1: artillery_fallback.add_condition(Or()) + hold_2 = Hold() + hold_2.number = 3 + dcs_group.add_trigger_action(hold_2) retreat_task = GoToWaypoint(toIndex=3) - retreat_task.number = 3 + retreat_task.number = 4 dcs_group.add_trigger_action(retreat_task) - artillery_fallback.add_action(AITaskSet(dcs_group.id, len(dcs_group.tasks))) + artillery_fallback.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks))) self.mission.triggerrules.triggers.append(artillery_fallback) for u in dcs_group.units: @@ -232,12 +232,16 @@ class GroundConflictGenerator: i = 1 for target in targets: rand_offset = Point(random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK), random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK)) - dcs_group.add_waypoint(target.points[0].position+rand_offset,PointAction.OffRoad) + dcs_group.add_waypoint(target.points[0].position+rand_offset, PointAction.OffRoad) dcs_group.points[i].tasks.append(AttackGroup(target.id)) i = i + 1 if to_cp.position.distance_to_point(dcs_group.points[0].position) <= AGGRESIVE_MOVE_DISTANCE: attack_point = to_cp.position.random_point_within(500, 0) dcs_group.add_waypoint(attack_point) + + if stance != CombatStance.RETREAT: + self.add_morale_trigger(dcs_group, forward_heading) + elif group.role in [CombatGroupRole.APC, CombatGroupRole.ATGM]: if stance in [CombatStance.AGGRESIVE, CombatStance.BREAKTHROUGH, CombatStance.ELIMINATION]: @@ -248,6 +252,9 @@ class GroundConflictGenerator: attack_point = self.find_offensive_point(dcs_group, forward_heading, AGGRESIVE_MOVE_DISTANCE) dcs_group.add_waypoint(attack_point, PointAction.OnRoad) + if stance != CombatStance.RETREAT: + self.add_morale_trigger(dcs_group, forward_heading) + if stance == CombatStance.RETREAT: # In retreat mode, the units will fall back # If the ally base is close enough, the units will even regroup there @@ -260,14 +267,51 @@ class GroundConflictGenerator: dcs_group.add_waypoint(reposition_point, PointAction.OffRoad) - def find_retreat_point(self, dcs_group, frontline_heading): + def add_morale_trigger(self, dcs_group, forward_heading): + """ + This add a trigger to manage units fleeing whenever their group is hit hard, or being engaged by CAS + """ + + if len(dcs_group.units) == 1: + return + + # Units should hold position on last waypoint + dcs_group.points[len(dcs_group.points) - 1].tasks.append(Hold()) + + # Force unit heading + for unit in dcs_group.units: + unit.heading = forward_heading + dcs_group.manualHeading = True + + # We add a new retreat waypoint + 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.enabled = False + dcs_group.add_trigger_action(Hold()) + dcs_group.add_trigger_action(fallback) + + # Create trigger + fallback = TriggerOnce(Event.NoEvent, "Morale manager #" + str(dcs_group.id)) + + # Usually more than 50% casualties = RETREAT + fallback.add_condition(GroupLifeLess(dcs_group.id, random.randint(51, 76))) + + # Do retreat to the configured retreat waypoint + fallback.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks))) + + self.mission.triggerrules.triggers.append(fallback) + + + def find_retreat_point(self, dcs_group, frontline_heading, distance=RETREAT_DISTANCE): """ Find a point to retreat to :param dcs_group: DCS mission group we are searching a retreat point for :param frontline_heading: Heading of the frontline :return: dcs.mapping.Point object with the desired position """ - return dcs_group.points[0].position.point_from_heading(frontline_heading-180, RETREAT_DISTANCE) + return dcs_group.points[0].position.point_from_heading(frontline_heading-180, distance) def find_offensive_point(self, dcs_group, frontline_heading, distance): """ diff --git a/gen/groundobjectsgen.py b/gen/groundobjectsgen.py index ff90e58e..458e5ae9 100644 --- a/gen/groundobjectsgen.py +++ b/gen/groundobjectsgen.py @@ -45,12 +45,6 @@ class GroundObjectsGenerator: def generate(self): - cp = None # type: ControlPoint - if self.conflict.attackers_country.name == self.game.player_country: - cp = self.conflict.to_cp - else: - cp = self.conflict.from_cp - for cp in self.game.theater.controlpoints: if cp.captured: diff --git a/qt_ui/main.py b/qt_ui/main.py index e6760d51..6feba8f8 100644 --- a/qt_ui/main.py +++ b/qt_ui/main.py @@ -21,10 +21,11 @@ from userdata import liberation_install, persistency, liberation_theme if __name__ == "__main__": + os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" # Potential fix for 4K screens app = QApplication(sys.argv) # init the theme and load the stylesheet based on the theme index - liberation_theme.init(); + liberation_theme.init() css = "" with open("./resources/stylesheets/"+liberation_theme.get_theme_css_file()) as stylesheet: app.setStyleSheet(stylesheet.read()) diff --git a/qt_ui/windows/basemenu/QBaseMenu2.py b/qt_ui/windows/basemenu/QBaseMenu2.py index 065b3631..d74a72b8 100644 --- a/qt_ui/windows/basemenu/QBaseMenu2.py +++ b/qt_ui/windows/basemenu/QBaseMenu2.py @@ -35,8 +35,8 @@ class QBaseMenu2(QDialog): self.setWindowFlags(Qt.WindowStaysOnTopHint) self.setMinimumSize(300, 200) - self.setMinimumWidth(680) - self.setMaximumWidth(680) + self.setMinimumWidth(800) + self.setMaximumWidth(800) self.setModal(True) self.initUi() @@ -50,7 +50,7 @@ class QBaseMenu2(QDialog): header = QLabel(self) header.setGeometry(0, 0, 655, 106) - pixmap = QPixmap("./resources/ui/airbase.png") + pixmap = QPixmap(self.get_base_image()) header.setPixmap(pixmap) title = QLabel("" + self.cp.name + "") @@ -75,7 +75,6 @@ class QBaseMenu2(QDialog): def closeEvent(self, closeEvent:QCloseEvent): GameUpdateSignal.get_instance().updateGame(self.game) - def get_base_image(self): if self.cp.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP: return "./resources/ui/carrier.png" diff --git a/qt_ui/windows/mission/QMissionPlanning.py b/qt_ui/windows/mission/QMissionPlanning.py index d1e513ce..b64ec969 100644 --- a/qt_ui/windows/mission/QMissionPlanning.py +++ b/qt_ui/windows/mission/QMissionPlanning.py @@ -33,10 +33,12 @@ class QMissionPlanning(QDialog): self.select_airbase = QChooseAirbase(self.game) self.select_airbase.selected_airbase_changed.connect(self.on_departure_cp_changed) self.planned_flight_view = QPlannedFlightsView(None) + self.available_aircraft_at_selected_location = {} if self.captured_cp[0].id in self.game.planners.keys(): self.planner = self.game.planners[self.captured_cp[0].id] self.planned_flight_view.set_flight_planner(self.planner) self.selected_cp = self.captured_cp[0] + self.available_aircraft_at_selected_location = self.planner.get_available_aircraft() self.planned_flight_view.selectionModel().setCurrentIndex(self.planned_flight_view.indexAt(QPoint(1, 1)), QItemSelectionModel.Rows) self.planned_flight_view.selectionModel().selectionChanged.connect(self.on_flight_selection_change) @@ -82,8 +84,10 @@ class QMissionPlanning(QDialog): if len(cps) == 1: self.selected_cp = cps[0] self.planner = self.game.planners[cps[0].id] + self.available_aircraft_at_selected_location = self.planner.get_available_aircraft() self.planned_flight_view.set_flight_planner(self.planner) else: + self.available_aircraft_at_selected_location = {} self.planned_flight_view.set_flight_planner(None) def on_flight_selection_change(self): diff --git a/qt_ui/windows/mission/flight/QFlightCreator.py b/qt_ui/windows/mission/flight/QFlightCreator.py index 2ee1c9d7..293ba75f 100644 --- a/qt_ui/windows/mission/flight/QFlightCreator.py +++ b/qt_ui/windows/mission/flight/QFlightCreator.py @@ -40,7 +40,8 @@ class QFlightCreator(QDialog): for aircraft_type in self.planner.get_available_aircraft().keys(): print(aircraft_type) print(aircraft_type.name) - self.select_type_aircraft.addItem(aircraft_type.id, userData=aircraft_type) + if self.available[aircraft_type] > 0: + self.select_type_aircraft.addItem(aircraft_type.id, userData=aircraft_type) self.select_type_aircraft.setCurrentIndex(0) self.select_flight_type = QComboBox() @@ -61,6 +62,11 @@ class QFlightCreator(QDialog): self.select_count_of_aircraft.setMaximum(4) self.select_count_of_aircraft.setValue(2) + aircraft_type = self.select_type_aircraft.currentData() + if aircraft_type is not None: + self.select_count_of_aircraft.setValue(min(self.available[aircraft_type], 2)) + self.select_count_of_aircraft.setMaximum(min(self.available[aircraft_type], 4)) + self.add_button = QPushButton("Add") self.add_button.clicked.connect(self.create_flight)