diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..9d0da6a6 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,39 @@ +name: Build + +on: [push, pull_request] + +jobs: + + build: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: true + + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + + - name: Install environment + run: | + py -m venv ./venv + + - name: Install dependencies + run: | + ./venv/scripts/activate + pip install -r requirements.txt + # For some reason the shiboken2.abi3.dll is not found properly, so I copy it instead + Copy-Item .\venv\Lib\site-packages\shiboken2\shiboken2.abi3.dll .\venv\Lib\site-packages\PySide2\ -Force + + - name: Build binaries + run: | + ./venv/scripts/activate + $env:PYTHONPATH=".;./pydcs" + pyinstaller pyinstaller.spec + + - uses: actions/upload-artifact@v2 + with: + name: dcs_liberation + path: dist/ diff --git a/.gitignore b/.gitignore index 68c604e4..8c7d79f3 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ logs/liberation.log qt_ui/logs/liberation.log *.psd +resources/scripts/plugins/* diff --git a/.vscode/launch.json b/.vscode/launch.json index 646c8768..fde0564f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,6 +14,17 @@ "PYTHONPATH": ".;./pydcs" }, "preLaunchTask": "Prepare Environment" + }, + { + "name": "Python: Make Release", + "type": "python", + "request": "launch", + "program": "resources\\tools\\mkrelease.py", + "console": "integratedTerminal", + "env": { + "PYTHONPATH": ".;./pydcs" + }, + "preLaunchTask": "Prepare Environment" } ] } \ No newline at end of file diff --git a/changelog.md b/changelog.md index 69a72793..0f60b72f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,22 +1,60 @@ +# 2.1.3 + +## Features/Improvements : +* **[Units/Factions]** Added A-10C_2 to USA 2005 and Bluefor modern factions +* **[UI]** Limit number of aircraft that can be bought to the number of available parking slots. +* **[Mission Generator]** Use inline loading of the JSON.lua library, and save to either %LIBERATION_EXPORT_DIR%, or to DCS working directory + +## Changes : +* **[Units/Factions]** Bluefor generic factions will now use the new "Combined Joint Task Forces Blue" country in the generated mission instead of "USA" + +## Fixes : +* **[UI]** Fixed icon for Viggen +* **[UI]** Added icons for some ground units +* **[Misc]** Fixed issue with Chinese characters in pydcs preventing generating the mission. (Take Off button not working) (thanks to spark135246) +* **[Misc]** Fixed an error causing with ATC frequency preventing generating the mission. (Take Off button not working) (thanks to danalbert) + +# 2.1.2 + +## Fixes : +* **[Mission Generator]** Fix mission generation issues with radio frequencies (Thanks to contributors davidp57 and danalbert) +* **[Mission Generator]** AI should now properly plan flights for Tornados + # 2.1.1 ## Features/Improvements : * **[Other]** Added an installer option (thanks to contributor parithon) -* **[Cheat Menu]** Added possibility to replace destroyed SAM and base defenses units for the player (Click on a SAM site to fix it) -* **[Cheat Menu]** Added recon images for buildings on strike targets, click on a Strike target to get detailled informations +* **[Kneeboards]** Generate mission kneeboards for player flights. Kneeboards include + airfield/carrier information (ATC frequencies, ILS, TACAN, and runway + assignments), assigned radio channels, waypoint lists, and AWACS/JTAC/tanker + information. (Thanks to contributor danalbert) +* **[Radios]** Allocate separate intra-flight channels for most aircraft to reduce global + chatter. (Thanks to contributor danalbert) +* **[Radios]** Configure radio channel presets for most aircraft. Currently supported are: + * AJS37 + * AV-8B + * F-14B + * F-16C + * F/A-18C + * JF-17 + * M-2000C (Thanks to contributor danalbert) +* **[Base Menu]** Added possibility to repair destroyed SAM and base defenses units for the player (Click on a SAM site to fix it) +* **[Base Menu]** Added possibility to buy/sell/replace SAM units +* **[Map]** Added recon images for buildings on strike targets, click on a Strike target to get detailled informations * **[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 * **[Cheat Menu]** Added buttons to remove money ## Fixed issues : -* **[UI/UX]** Spelling issues (Thanks to Github contributor steveveepee) +* **[UI/UX]** Spelling issues (Thanks to contributor steveveepee) * **[Campaign Generator]** LHA was placed on land in Syrian Civil War campaign * **[Campaign Generator]** Fixed inverted configuration for Syria full map * **[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. +* **[Units/Factions]** China will use KJ-2000 as awacs instead of A-50 # 2.1.0 diff --git a/game/db.py b/game/db.py index d1a1cd31..003bbcc4 100644 --- a/game/db.py +++ b/game/db.py @@ -192,6 +192,7 @@ PRICES = { A_10A: 16, A_10C: 22, + A_10C_2: 24, # heli Ka_50: 13, @@ -223,13 +224,16 @@ PRICES = { KC130: 25, A_50: 50, + KJ_2000: 50, E_3A: 50, C_130: 25, # WW2 P_51D_30_NA: 18, P_51D: 16, - P_47D_30: 18, + P_47D_30: 17, + P_47D_30bl1: 16, + P_47D_40: 18, B_17G: 30, # Drones @@ -495,6 +499,7 @@ UNIT_BY_TASK = { AJS37, A_10A, A_10C, + A_10C_2, Su_17M4, Su_25, Su_25T, @@ -519,6 +524,8 @@ UNIT_BY_TASK = { MiG_27K, A_20G, P_47D_30, + P_47D_30bl1, + P_47D_40, Ju_88A4, B_17G, MB_339PAN, @@ -542,7 +549,7 @@ UNIT_BY_TASK = { KC130, S_3B_Tanker, ], - AWACS: [E_3A, A_50, ], + AWACS: [E_3A, A_50, KJ_2000], PinpointStrike: [ Armor.APC_MTLB, Armor.APC_MTLB, @@ -952,6 +959,7 @@ PLANE_PAYLOAD_OVERRIDES = { }, A_10A: COMMON_OVERRIDE, A_10C: COMMON_OVERRIDE, + A_10C_2: COMMON_OVERRIDE, AV8BNA: COMMON_OVERRIDE, C_101CC: COMMON_OVERRIDE, F_5E_3: COMMON_OVERRIDE, @@ -993,6 +1001,8 @@ PLANE_PAYLOAD_OVERRIDES = { Su_17M4: COMMON_OVERRIDE, F_4E: COMMON_OVERRIDE, P_47D_30:COMMON_OVERRIDE, + P_47D_30bl1:COMMON_OVERRIDE, + P_47D_40:COMMON_OVERRIDE, B_17G: COMMON_OVERRIDE, P_51D: COMMON_OVERRIDE, P_51D_30_NA: COMMON_OVERRIDE, diff --git a/game/event/event.py b/game/event/event.py index e064ad94..8146deb3 100644 --- a/game/event/event.py +++ b/game/event/event.py @@ -267,7 +267,7 @@ class Event: ratio = (1.0 + enemy_casualties) / (1.0 + ally_casualties) - player_aggresive = cp.stances[enemy_cp.id] in [CombatStance.AGGRESIVE, CombatStance.ELIMINATION, CombatStance.BREAKTHROUGH] + player_aggresive = cp.stances[enemy_cp.id] in [CombatStance.AGGRESSIVE, CombatStance.ELIMINATION, CombatStance.BREAKTHROUGH] if ally_units_alive == 0: player_won = False diff --git a/game/factions/bluefor_coldwar.py b/game/factions/bluefor_coldwar.py index 70bf9467..5db15d73 100644 --- a/game/factions/bluefor_coldwar.py +++ b/game/factions/bluefor_coldwar.py @@ -4,7 +4,7 @@ from dcs.ships import * from dcs.vehicles import * BLUEFOR_COLDWAR = { - "country": "USA", + "country": "Combined Joint Task Forces Blue", "side": "blue", "units": [ diff --git a/game/factions/bluefor_coldwar_a4.py b/game/factions/bluefor_coldwar_a4.py index 5bf366f5..74983134 100644 --- a/game/factions/bluefor_coldwar_a4.py +++ b/game/factions/bluefor_coldwar_a4.py @@ -6,7 +6,7 @@ from dcs.vehicles import * from pydcs_extensions.a4ec.a4ec import A_4E_C BLUEFOR_COLDWAR_A4 = { - "country": "USA", + "country": "Combined Joint Task Forces Blue", "side": "blue", "units": [ diff --git a/game/factions/bluefor_modern.py b/game/factions/bluefor_modern.py index ca64e2f5..8db0ad89 100644 --- a/game/factions/bluefor_modern.py +++ b/game/factions/bluefor_modern.py @@ -4,7 +4,7 @@ from dcs.ships import * from dcs.vehicles import * BLUEFOR_MODERN = { - "country": "USA", + "country": "Combined Joint Task Forces Blue", "side": "blue", "units": [ @@ -20,6 +20,7 @@ BLUEFOR_MODERN = { Su_25T, A_10A, A_10C, + A_10C_2, AV8BNA, AJS37, diff --git a/game/factions/china_2010.py b/game/factions/china_2010.py index 148fdbd0..0d98d1b9 100644 --- a/game/factions/china_2010.py +++ b/game/factions/china_2010.py @@ -20,7 +20,7 @@ China_2010 = { An_30M, Yak_40, - A_50, + KJ_2000, Mi_8MT, Mi_28N, diff --git a/game/factions/usa_2005.py b/game/factions/usa_2005.py index ddeccd0b..d6b63a58 100644 --- a/game/factions/usa_2005.py +++ b/game/factions/usa_2005.py @@ -13,6 +13,7 @@ USA_2005 = { FA_18C_hornet, F_16C_50, A_10C, + A_10C_2, AV8BNA, MQ_9_Reaper, diff --git a/game/operation/operation.py b/game/operation/operation.py index f66eb124..4bee75b0 100644 --- a/game/operation/operation.py +++ b/game/operation/operation.py @@ -234,39 +234,97 @@ class Operation: if self.game.settings.perf_smoke_gen: self.visualgen.generate() - # Inject Lua Scripts - load_mist = TriggerStart(comment="Load Mist Lua Framework") - with open("./resources/scripts/mist_4_3_74.lua") as f: - load_mist.add_action(DoScript(String(f.read()))) - self.current_mission.triggerrules.triggers.append(load_mist) + # Inject Plugins Lua Scripts + listOfPluginsScripts = [] + try: + with open("./resources/scripts/plugins/__plugins.lst", "r") as a_file: + for line in a_file: + name = line.strip() + if not name.startswith( '#' ): + trigger = TriggerStart(comment="Load " + name) + listOfPluginsScripts.append(name) + fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/plugins/" + name) + trigger.add_action(DoScriptFile(fileref)) + self.current_mission.triggerrules.triggers.append(trigger) + except Exception as e: + print(e) - # Load Ciribob's JTACAutoLase script - load_autolase = TriggerStart(comment="Load JTAC script") - with open("./resources/scripts/JTACAutoLase.lua") as f: + # Inject Mist Script if not done already in the plugins + if not "mist.lua" in listOfPluginsScripts and not "mist_4_3_74.lua" in listOfPluginsScripts: # don't load the script twice + trigger = TriggerStart(comment="Load Mist Lua framework") + fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/mist_4_3_74.lua") + trigger.add_action(DoScriptFile(fileref)) + self.current_mission.triggerrules.triggers.append(trigger) - script = f.read() - script = script + "\n" + # Inject JSON library if not done already in the plugins + if not "json.lua" in listOfPluginsScripts : # don't load the script twice + trigger = TriggerStart(comment="Load JSON Lua library") + fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/json.lua") + trigger.add_action(DoScriptFile(fileref)) + self.current_mission.triggerrules.triggers.append(trigger) - smoke = "true" - if hasattr(self.game.settings, "jtac_smoke_on"): - if not self.game.settings.jtac_smoke_on: - smoke = "false" + # Inject Ciribob's JTACAutoLase if not done already in the plugins + if not "JTACAutoLase.lua" in listOfPluginsScripts : # don't load the script twice + trigger = TriggerStart(comment="Load JTACAutoLase.lua script") + fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/JTACAutoLase.lua") + trigger.add_action(DoScriptFile(fileref)) + self.current_mission.triggerrules.triggers.append(trigger) - for jtac in jtacs: - script += f"\nJTACAutoLase('{jtac.unit_name}', {jtac.code}, {smoke}, 'vehicle')\n" + # set a LUA table with data from Liberation that we want to set + # at the moment it contains Liberation's install path, and an overridable definition for the JTACAutoLase function + # later, we'll add data about the units and points having been generated, in order to facilitate the configuration of the plugin lua scripts + state_location = "[[" + os.path.abspath("state.json") + "]]" + lua = """ + -- setting configuration table + env.info("DCSLiberation|: setting configuration table") + + -- all data in this table is overridable. + dcsLiberation = {} + + -- the base location for state.json; if non-existent, it'll be replaced with LIBERATION_EXPORT_DIR, TEMP, or DCS working directory + dcsLiberation.installPath=""" + state_location + """ + + -- you can override dcsLiberation.JTACAutoLase to make it use your own function ; it will be called with these parameters : ({jtac.unit_name}, {jtac.code}, {smoke}, 'vehicle') for all JTACs + if ctld then + dcsLiberation.JTACAutoLase=ctld.JTACAutoLase + elseif JTACAutoLase then + dcsLiberation.JTACAutoLase=JTACAutoLase + end + + -- later, we'll add more data to the table + --dcsLiberation.POIs = {} + --dcsLiberation.BASEs = {} + --dcsLiberation.JTACs = {} + """ - load_autolase.add_action(DoScript(String(script))) - self.current_mission.triggerrules.triggers.append(load_autolase) + trigger = TriggerStart(comment="Set DCS Liberation data") + trigger.add_action(DoScript(String(lua))) + self.current_mission.triggerrules.triggers.append(trigger) - load_dcs_libe = TriggerStart(comment="Load DCS Liberation Script") - with open("./resources/scripts/dcs_liberation.lua") as f: - script = f.read() - json_location = "[["+os.path.abspath("resources\\scripts\\json.lua")+"]]" - state_location = "[[" + os.path.abspath("state.json") + "]]" - script = script.replace("{{json_file_abs_location}}", json_location) - script = script.replace("{{debriefing_file_location}}", state_location) - load_dcs_libe.add_action(DoScript(String(script))) - self.current_mission.triggerrules.triggers.append(load_dcs_libe) + # Inject DCS-Liberation script if not done already in the plugins + if not "dcs_liberation.lua" in listOfPluginsScripts : # don't load the script twice + trigger = TriggerStart(comment="Load DCS Liberation script") + fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/dcs_liberation.lua") + trigger.add_action(DoScriptFile(fileref)) + self.current_mission.triggerrules.triggers.append(trigger) + + # add a configuration for JTACAutoLase and start lasing for all JTACs + smoke = "true" + if hasattr(self.game.settings, "jtac_smoke_on"): + if not self.game.settings.jtac_smoke_on: + smoke = "false" + + lua = """ + -- setting and starting JTACs + env.info("DCSLiberation|: setting and starting JTACs") + """ + + for jtac in jtacs: + lua += f"if dcsLiberation.JTACAutoLase then dcsLiberation.JTACAutoLase('{jtac.unit_name}', {jtac.code}, {smoke}, 'vehicle') end\n" + + trigger = TriggerStart(comment="Start JTACs") + trigger.add_action(DoScript(String(lua))) + self.current_mission.triggerrules.triggers.append(trigger) self.assign_channels_to_flights() diff --git a/gen/aircraft.py b/gen/aircraft.py index 4911c916..ec5dec26 100644 --- a/gen/aircraft.py +++ b/gen/aircraft.py @@ -279,13 +279,15 @@ class CommonRadioChannelAllocator(RadioChannelAllocator): last_channel = flight.num_radio_channels(radio_id) channel_alloc = iter(range(first_channel, last_channel + 1)) - flight.assign_channel(radio_id, next(channel_alloc), flight.departure.atc) + if flight.departure.atc is not None: + flight.assign_channel(radio_id, next(channel_alloc), + flight.departure.atc) # TODO: If there ever are multiple AWACS, limit to mission relevant. for awacs in air_support.awacs: flight.assign_channel(radio_id, next(channel_alloc), awacs.freq) - if flight.arrival != flight.departure: + if flight.arrival != flight.departure and flight.arrival.atc is not None: flight.assign_channel(radio_id, next(channel_alloc), flight.arrival.atc) @@ -295,7 +297,7 @@ class CommonRadioChannelAllocator(RadioChannelAllocator): flight.assign_channel( radio_id, next(channel_alloc), tanker.freq) - if flight.divert is not None: + if flight.divert is not None and flight.divert.atc is not None: flight.assign_channel(radio_id, next(channel_alloc), flight.divert.atc) except StopIteration: @@ -374,7 +376,7 @@ class AircraftData: AIRCRAFT_DATA: Dict[str, AircraftData] = { "A-10C": AircraftData( inter_flight_radio=get_radio("AN/ARC-164"), - intra_flight_radio=get_radio("AN/ARC-186(V) AM"), + intra_flight_radio=get_radio("AN/ARC-164"), # VHF for intraflight is not accepted anymore by DCS (see https://forums.eagle.ru/showthread.php?p=4499738) channel_allocator=WarthogRadioChannelAllocator() ), diff --git a/gen/airfields.py b/gen/airfields.py index 36f126b3..b7e08712 100644 --- a/gen/airfields.py +++ b/gen/airfields.py @@ -1527,7 +1527,10 @@ class RunwayData: ils: Optional[RadioFrequency] = None try: airfield = AIRFIELD_DATA[airport.name] - atc = airfield.atc.uhf + if airfield.atc is not None: + atc = airfield.atc.uhf + else: + atc = None tacan = airfield.tacan tacan_callsign = airfield.tacan_callsign ils = airfield.ils_freq(runway) diff --git a/gen/airsupportgen.py b/gen/airsupportgen.py index da0689e9..791c80b6 100644 --- a/gen/airsupportgen.py +++ b/gen/airsupportgen.py @@ -65,7 +65,7 @@ class AirSupportConflictGenerator: tanker_position = player_cp.position.point_from_heading(tanker_heading, TANKER_DISTANCE) tanker_group = self.mission.refuel_flight( country=self.mission.country(self.game.player_country), - name=namegen.next_tanker_name(self.mission.country(self.game.player_country)), + name=namegen.next_tanker_name(self.mission.country(self.game.player_country), tanker_unit_type), airport=None, plane_type=tanker_unit_type, position=tanker_position, diff --git a/gen/armor.py b/gen/armor.py index e3b6fcef..5042149f 100644 --- a/gen/armor.py +++ b/gen/armor.py @@ -43,7 +43,7 @@ class GroundConflictGenerator: self.enemy_planned_combat_groups = enemy_planned_combat_groups self.player_planned_combat_groups = player_planned_combat_groups self.player_stance = CombatStance(player_stance) - self.enemy_stance = random.choice([CombatStance.AGGRESIVE, CombatStance.AGGRESIVE, CombatStance.AGGRESIVE, CombatStance.ELIMINATION, CombatStance.BREAKTHROUGH]) if len(enemy_planned_combat_groups) > len(player_planned_combat_groups) else random.choice([CombatStance.DEFENSIVE, CombatStance.DEFENSIVE, CombatStance.DEFENSIVE, CombatStance.AMBUSH, CombatStance.AGGRESIVE]) + self.enemy_stance = random.choice([CombatStance.AGGRESSIVE, CombatStance.AGGRESSIVE, CombatStance.AGGRESSIVE, CombatStance.ELIMINATION, CombatStance.BREAKTHROUGH]) if len(enemy_planned_combat_groups) > len(player_planned_combat_groups) else random.choice([CombatStance.DEFENSIVE, CombatStance.DEFENSIVE, CombatStance.DEFENSIVE, CombatStance.AMBUSH, CombatStance.AGGRESSIVE]) self.game = game self.jtacs: List[JtacInfo] = [] @@ -239,7 +239,7 @@ class GroundConflictGenerator: u.heading = forward_heading + random.randint(-5,5) elif group.role in [CombatGroupRole.TANK, CombatGroupRole.IFV]: - if stance == CombatStance.AGGRESIVE: + if stance == CombatStance.AGGRESSIVE: # Attack nearest enemy if any # Then move forward OR Attack enemy base if it is not too far away target = self.find_nearest_enemy_group(dcs_group, enemy_groups) @@ -280,7 +280,7 @@ class GroundConflictGenerator: elif group.role in [CombatGroupRole.APC, CombatGroupRole.ATGM]: - if stance in [CombatStance.AGGRESIVE, CombatStance.BREAKTHROUGH, CombatStance.ELIMINATION]: + if stance in [CombatStance.AGGRESSIVE, CombatStance.BREAKTHROUGH, CombatStance.ELIMINATION]: # APC & ATGM will never move too much forward, but will follow along any offensive 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) diff --git a/gen/briefinggen.py b/gen/briefinggen.py index 0db4ac5e..10e07001 100644 --- a/gen/briefinggen.py +++ b/gen/briefinggen.py @@ -205,7 +205,7 @@ class BriefingGenerator(MissionInfoGenerator): self.description += "We do not have a single vehicle available to hold our position, the situation is critical, and we will lose ground inevitably.\n" elif enemy_base.base.total_armor == 0: self.description += "The enemy forces have been crushed, we will be able to make significant progress toward " + enemy_base.name + ". \n" - if stance == CombatStance.AGGRESIVE: + if stance == CombatStance.AGGRESSIVE: if has_numerical_superiority: self.description += "On this location, our ground forces will try to make progress against the enemy" self.description += ". As the enemy is outnumbered, our forces should have no issue making progress.\n" diff --git a/gen/defenses/armor_group_generator.py b/gen/defenses/armor_group_generator.py index 3824f7da..7b772e31 100644 --- a/gen/defenses/armor_group_generator.py +++ b/gen/defenses/armor_group_generator.py @@ -3,22 +3,38 @@ import random from dcs.vehicles import Armor from game import db -from gen.defenses.armored_group_generator import ArmoredGroupGenerator +from gen.defenses.armored_group_generator import ArmoredGroupGenerator, FixedSizeArmorGroupGenerator def generate_armor_group(faction:str, game, ground_object): """ This generate a group of ground units - :param parentCp: The parent control point - :param ground_object: The ground object which will own the group - :param country: Owner country :return: Generated group """ possible_unit = [u for u in db.FACTIONS[faction]["units"] if u in Armor.__dict__.values()] if len(possible_unit) > 0: unit_type = random.choice(possible_unit) - generator = ArmoredGroupGenerator(game, ground_object, unit_type) - generator.generate() - return generator.get_generated_group() + return generate_armor_group_of_type(game, ground_object, unit_type) return None + + +def generate_armor_group_of_type(game, ground_object, unit_type): + """ + This generate a group of ground units of given type + :return: Generated group + """ + generator = ArmoredGroupGenerator(game, ground_object, unit_type) + generator.generate() + return generator.get_generated_group() + + +def generate_armor_group_of_type_and_size(game, ground_object, unit_type, size: int): + """ + This generate a group of ground units of given type and size + :return: Generated group + """ + generator = FixedSizeArmorGroupGenerator(game, ground_object, unit_type, size) + generator.generate() + return generator.get_generated_group() + diff --git a/gen/defenses/armored_group_generator.py b/gen/defenses/armored_group_generator.py index f678af81..3b81a1dd 100644 --- a/gen/defenses/armored_group_generator.py +++ b/gen/defenses/armored_group_generator.py @@ -25,3 +25,20 @@ class ArmoredGroupGenerator(GroupGenerator): self.position.y + spacing * j, self.heading) +class FixedSizeArmorGroupGenerator(GroupGenerator): + + def __init__(self, game, ground_object, unit_type, size): + super(FixedSizeArmorGroupGenerator, self).__init__(game, ground_object) + self.unit_type = unit_type + self.size = size + + def generate(self): + spacing = random.randint(20, 70) + + index = 0 + for i in range(self.size): + index = index + 1 + self.add_unit(self.unit_type, "Armor#" + str(index), + self.position.x + spacing * i, + self.position.y, self.heading) + diff --git a/gen/flights/ai_flight_planner_db.py b/gen/flights/ai_flight_planner_db.py index dd924f64..e1393a1a 100644 --- a/gen/flights/ai_flight_planner_db.py +++ b/gen/flights/ai_flight_planner_db.py @@ -27,6 +27,7 @@ INTERCEPT_CAPABLE = [ # Used for CAP, Escort, and intercept if there is not a specialised aircraft available CAP_CAPABLE = [ + MiG_15bis, MiG_19P, MiG_21Bis, @@ -62,6 +63,8 @@ CAP_CAPABLE = [ P_51D_30_NA, P_51D, P_47D_30, + P_47D_30bl1, + P_47D_40, SpitfireLFMkIXCW, SpitfireLFMkIX, @@ -96,6 +99,7 @@ CAS_CAPABLE = [ A_10A, A_10C, + A_10C_2, AV8BNA, F_86F_Sabre, @@ -106,6 +110,9 @@ CAS_CAPABLE = [ F_16C_50, FA_18C_hornet, + Tornado_IDS, + Tornado_GR4, + C_101CC, MB_339PAN, L_39ZA, @@ -119,7 +126,6 @@ CAS_CAPABLE = [ AH_64D, AH_1W, - UH_1H, Mi_8MT, @@ -130,6 +136,8 @@ CAS_CAPABLE = [ P_51D_30_NA, P_51D, P_47D_30, + P_47D_30bl1, + P_47D_40, A_20G, SpitfireLFMkIXCW, @@ -164,6 +172,9 @@ SEAD_CAPABLE = [ Su_34, MiG_27K, + Tornado_IDS, + Tornado_GR4, + A_4E_C, Rafale_A_S ] @@ -187,6 +198,7 @@ STRIKE_CAPABLE = [ A_10A, A_10C, + A_10C_2, AV8BNA, F_86F_Sabre, @@ -197,6 +209,9 @@ STRIKE_CAPABLE = [ F_16C_50, FA_18C_hornet, + Tornado_IDS, + Tornado_GR4, + C_101CC, L_39ZA, AJS37, @@ -204,6 +219,8 @@ STRIKE_CAPABLE = [ P_51D_30_NA, P_51D, P_47D_30, + P_47D_30bl1, + P_47D_40, A_20G, B_17G, @@ -229,8 +246,12 @@ ANTISHIP_CAPABLE = [ F_16A, F_16C_50, A_10C, + A_10C_2, A_10A, + Tornado_IDS, + Tornado_GR4, + Ju_88A4, Rafale_A_S ] diff --git a/gen/ground_forces/ai_ground_planner.py b/gen/ground_forces/ai_ground_planner.py index bdc3a18d..877c9831 100644 --- a/gen/ground_forces/ai_ground_planner.py +++ b/gen/ground_forces/ai_ground_planner.py @@ -197,7 +197,7 @@ DISTANCE_FROM_FRONTLINE = { GROUP_SIZES_BY_COMBAT_STANCE = { CombatStance.DEFENSIVE: [2, 4, 6], - CombatStance.AGGRESIVE: [2, 4, 6], + CombatStance.AGGRESSIVE: [2, 4, 6], CombatStance.RETREAT: [2, 4, 6, 8], CombatStance.BREAKTHROUGH: [4, 6, 6, 8], CombatStance.ELIMINATION: [2, 4, 4, 4, 6], diff --git a/gen/ground_forces/combat_stance.py b/gen/ground_forces/combat_stance.py index d2247f73..604ec508 100644 --- a/gen/ground_forces/combat_stance.py +++ b/gen/ground_forces/combat_stance.py @@ -3,7 +3,7 @@ from enum import Enum class CombatStance(Enum): DEFENSIVE = 0 # Unit will adopt defensive stance with medium group of units - AGGRESIVE = 1 # Unit will attempt to make progress with medium sized group of units + AGGRESSIVE = 1 # Unit will attempt to make progress with medium sized group of units RETREAT = 2 # Unit will retreat BREAKTHROUGH = 3 # Unit will attempt a breakthrough, rushing forward very aggresively with big group of armored units, and even less armored units will move aggresively ELIMINATION = 4 # Unit will progress aggresively toward anemy units, attempting to eliminate the ennemy force diff --git a/gen/naming.py b/gen/naming.py index 0b543e0c..40da3a6b 100644 --- a/gen/naming.py +++ b/gen/naming.py @@ -61,9 +61,9 @@ class NameGenerator: self.number += 1 return "awacs|{}|{}|0|".format(country.id, self.number) - def next_tanker_name(self, country): + def next_tanker_name(self, country, unit_type): self.number += 1 - return "tanker|{}|{}|0|".format(country.id, self.number) + return "tanker|{}|{}|0|{}".format(country.id, self.number, db.unit_type_name(unit_type)) def next_carrier_name(self, country): self.number += 1 diff --git a/gen/radios.py b/gen/radios.py index bf4f1447..c0adb20c 100644 --- a/gen/radios.py +++ b/gen/radios.py @@ -124,7 +124,7 @@ RADIOS: List[Radio] = [ # 4 preset channels (A/B/C/D) Radio("SCR522", MHz(100), MHz(156), step=kHz(25)), - Radio("R&S M3AR VHF", MHz(108), MHz(174), step=MHz(1)), + Radio("R&S M3AR VHF", MHz(120), MHz(174), step=MHz(1)), Radio("R&S M3AR UHF", MHz(225), MHz(400), step=MHz(1)), ] diff --git a/pydcs b/pydcs index f46781b8..ceea62a8 160000 --- a/pydcs +++ b/pydcs @@ -1 +1 @@ -Subproject commit f46781b854102a9f06948c8fb81a40331b78459e +Subproject commit ceea62a8e0731c21b3e1a3e90682aa0affc168f1 diff --git a/qt_ui/uiconstants.py b/qt_ui/uiconstants.py index c0d52631..ada6c94c 100644 --- a/qt_ui/uiconstants.py +++ b/qt_ui/uiconstants.py @@ -8,7 +8,7 @@ from game.event import UnitsDeliveryEvent, FrontlineAttackEvent from theater.theatergroundobject import CATEGORY_MAP from userdata.liberation_theme import get_theme_icons -VERSION_STRING = "2.1.0" +VERSION_STRING = "2.1.3" URLS : Dict[str, str] = { "Manual": "https://github.com/khopa/dcs_liberation/wiki", @@ -82,6 +82,7 @@ def load_icons(): ICONS["New"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/new.png") ICONS["Open"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/open.png") ICONS["Save"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/save.png") + ICONS["Hangar"] = QPixmap("./resources/ui/misc/hangar.png") ICONS["Terrain_Caucasus"] = QPixmap("./resources/ui/terrain_caucasus.gif") ICONS["Terrain_Persian_Gulf"] = QPixmap("./resources/ui/terrain_pg.gif") @@ -138,6 +139,7 @@ def load_aircraft_icons(): AIRCRAFT_ICONS[aircraft[:-7]] = QPixmap(os.path.join("./resources/ui/units/aircrafts/", aircraft)) AIRCRAFT_ICONS["F-16C_50"] = AIRCRAFT_ICONS["F-16C"] AIRCRAFT_ICONS["FA-18C_hornet"] = AIRCRAFT_ICONS["FA-18C"] + AIRCRAFT_ICONS["A-10C_2"] = AIRCRAFT_ICONS["A-10C"] def load_vehicle_icons(): diff --git a/qt_ui/windows/QLiberationWindow.py b/qt_ui/windows/QLiberationWindow.py index f9daed62..9543ab66 100644 --- a/qt_ui/windows/QLiberationWindow.py +++ b/qt_ui/windows/QLiberationWindow.py @@ -243,7 +243,7 @@ class QLiberationWindow(QMainWindow): "

Authors

" + \ "

DCS Liberation was originally developed by shdwp, DCS Liberation 2.0 is a partial rewrite based on this work by Khopa." \ "

Contributors

" + \ - "shdwp, Khopa, Wrycu, calvinmorrow, JohanAberg, Deus, root0fall, Captain Cody" + \ + "shdwp, Khopa, ColonelPanic, Wrycu, calvinmorrow, JohanAberg, Deus, root0fall, Captain Cody, steveveepee, pedromagueija, parithon, bwRavencl, davidp57" + \ "

Special Thanks :

" \ "rp- for the pydcs framework
"\ "Grimes (mrSkortch) & Speed for the MIST framework
"\ diff --git a/qt_ui/windows/basemenu/QRecruitBehaviour.py b/qt_ui/windows/basemenu/QRecruitBehaviour.py index a349e4bc..b41ac68a 100644 --- a/qt_ui/windows/basemenu/QRecruitBehaviour.py +++ b/qt_ui/windows/basemenu/QRecruitBehaviour.py @@ -6,18 +6,28 @@ from PySide2.QtWidgets import ( QSizePolicy, QSpacerItem, ) +import logging from dcs.unittype import UnitType from theater import db + class QRecruitBehaviour: + game = None + cp = None + deliveryEvent = None + existing_units_labels = None + bought_amount_labels = None + maximum_units = -1 + recruitable_types = [] BUDGET_FORMAT = "Available Budget: ${}M" def __init__(self) -> None: self.deliveryEvent = None self.bought_amount_labels = {} self.existing_units_labels = {} + self.recruitable_types = [] self.update_available_budget() @property @@ -75,7 +85,6 @@ class QRecruitBehaviour: sell.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) sell.clicked.connect(lambda: self.sell(unit_type)) - existLayout.addWidget(unitName) existLayout.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum)) existLayout.addWidget(existing_units) @@ -112,10 +121,19 @@ class QRecruitBehaviour: def buy(self, unit_type): + if self.maximum_units > 0: + if self.total_units + 1 > self.maximum_units: + logging.info("Not enough space left !") + # TODO : display modal warning + return + price = db.PRICES[unit_type] if self.budget >= price: self.deliveryEvent.deliver({unit_type: 1}) self.budget -= price + else: + # TODO : display modal warning + logging.info("Not enough money !") self._update_count_label(unit_type) self.update_available_budget() @@ -133,3 +151,34 @@ class QRecruitBehaviour: self._update_count_label(unit_type) self.update_available_budget() + + @property + def total_units(self): + + total = 0 + for unit_type in self.recruitables_types: + total += self.cp.base.total_units(unit_type) + print(unit_type, total, self.cp.base.total_units(unit_type)) + print("--------------------------------") + + if self.deliveryEvent: + for unit_bought in self.deliveryEvent.units: + if db.unit_task(unit_bought) in self.recruitables_types: + total += self.deliveryEvent.units[unit_bought] + print(unit_bought, total, self.deliveryEvent.units[unit_bought]) + + print("=============================") + + return total + + def set_maximum_units(self, maximum_units): + """ + Set the maximum number of units that can be bought + """ + self.maximum_units = maximum_units + + def set_recruitable_types(self, recruitables_types): + """ + Set the maximum number of units that can be bought + """ + self.recruitables_types = recruitables_types \ No newline at end of file diff --git a/qt_ui/windows/basemenu/airfield/QAircraftRecruitmentMenu.py b/qt_ui/windows/basemenu/airfield/QAircraftRecruitmentMenu.py index 73f23617..9ce843d7 100644 --- a/qt_ui/windows/basemenu/airfield/QAircraftRecruitmentMenu.py +++ b/qt_ui/windows/basemenu/airfield/QAircraftRecruitmentMenu.py @@ -31,6 +31,15 @@ class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour): if not self.deliveryEvent: self.deliveryEvent = self.game_model.game.units_delivery_event(self.cp) + # Determine maximum number of aircrafts that can be bought + self.set_maximum_units(self.cp.available_aircraft_slots) + self.set_recruitable_types([CAP, CAS]) + + self.bought_amount_labels = {} + self.existing_units_labels = {} + + self.hangar_status = QHangarStatus(self.total_units, self.cp.available_aircraft_slots) + self.init_ui() def init_ui(self): @@ -66,5 +75,32 @@ class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour): scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) scroll.setWidgetResizable(True) scroll.setWidget(scroll_content) + main_layout.addLayout(self.hangar_status) main_layout.addWidget(scroll) self.setLayout(main_layout) + + def buy(self, unit_type): + super().buy(unit_type) + self.hangar_status.update_label(self.total_units, self.cp.available_aircraft_slots) + + def sell(self, unit_type): + super().sell(unit_type) + self.hangar_status.update_label(self.total_units, self.cp.available_aircraft_slots) + + +class QHangarStatus(QHBoxLayout): + + def __init__(self, current_amount: int, max_amount: int): + super(QHangarStatus, self).__init__() + self.icon = QLabel() + self.icon.setPixmap(ICONS["Hangar"]) + self.text = QLabel("") + + self.update_label(current_amount, max_amount) + self.addWidget(self.icon, Qt.AlignLeft) + self.addWidget(self.text, Qt.AlignLeft) + self.addStretch(50) + self.setAlignment(Qt.AlignLeft) + + def update_label(self, current_amount: int, max_amount: int): + self.text.setText("{}/{}".format(current_amount, max_amount)) diff --git a/qt_ui/windows/basemenu/base_defenses/QBaseDefenseGroupInfo.py b/qt_ui/windows/basemenu/base_defenses/QBaseDefenseGroupInfo.py index bafd70ee..370cf65a 100644 --- a/qt_ui/windows/basemenu/base_defenses/QBaseDefenseGroupInfo.py +++ b/qt_ui/windows/basemenu/base_defenses/QBaseDefenseGroupInfo.py @@ -35,12 +35,12 @@ class QBaseDefenseGroupInfo(QGroupBox): def buildLayout(self): unit_dict = {} - for i in range(self.unit_layout.count()): - item = self.unit_layout.itemAt(i) - if item is not None and item.widget() is not None: - self.unit_layout.removeItem(item) - item.widget().setParent(None) - item.widget().deleteLater() + for i in range(self.unit_layout.rowCount()): + for j in range(self.unit_layout.columnCount()): + item = self.unit_layout.itemAtPosition(i, j) + if item is not None and item.widget() is not None: + item.widget().setParent(None) + print("Remove " + str(i) + ", " + str(j)) for g in self.ground_object.groups: for u in g.units: @@ -60,6 +60,11 @@ class QBaseDefenseGroupInfo(QGroupBox): self.unit_layout.addWidget(QLabel(str(v) + " x " + "" + k + ""), i, 1) i = i + 1 + if len(unit_dict.items()) == 0: + self.unit_layout.addWidget(QLabel("/"), 0, 0) + + + self.setLayout(self.main_layout) def onManage(self): diff --git a/qt_ui/windows/groundobject/QGroundObjectMenu.py b/qt_ui/windows/groundobject/QGroundObjectMenu.py index 6d28c357..dcfed0a3 100644 --- a/qt_ui/windows/groundobject/QGroundObjectMenu.py +++ b/qt_ui/windows/groundobject/QGroundObjectMenu.py @@ -1,7 +1,7 @@ import logging from PySide2 import QtCore -from PySide2.QtGui import QCloseEvent, Qt +from PySide2.QtGui import Qt from PySide2.QtWidgets import QHBoxLayout, QDialog, QGridLayout, QLabel, QGroupBox, QVBoxLayout, QPushButton, \ QComboBox, QSpinBox, QMessageBox from dcs import Point @@ -9,7 +9,7 @@ from dcs import Point from game import Game, db from game.data.building_data import FORTIFICATION_BUILDINGS from game.db import PRICES, unit_type_of, PinpointStrike -from gen.defenses.armor_group_generator import generate_armor_group +from gen.defenses.armor_group_generator import generate_armor_group_of_type_and_size from gen.sam.sam_group_generator import get_faction_possible_sams_generator from qt_ui.uiconstants import EVENT_ICONS from qt_ui.widgets.QBudgetBox import QBudgetBox @@ -288,7 +288,9 @@ class QBuyGroupForGroundObjectDialog(QDialog): self.buyArmorButton.setText("Buy [$" + str(db.PRICES[self.buyArmorCombo.itemData(self.buyArmorCombo.currentIndex())] * self.amount.value()) + "M][-$" + str(self.current_group_value) + "M]") def buyArmor(self): + print("Buy Armor ") utype = self.buyArmorCombo.itemData(self.buyArmorCombo.currentIndex()) + print(utype) price = db.PRICES[utype] * self.amount.value() - self.current_group_value if price > self.game.budget: self.error_money() @@ -298,7 +300,7 @@ class QBuyGroupForGroundObjectDialog(QDialog): self.game.budget -= price # Generate Armor - group = generate_armor_group(self.game.player_name, self.game, self.ground_object) + group = generate_armor_group_of_type_and_size(self.game, self.ground_object, utype, int(self.amount.value())) self.ground_object.groups = [group] GameUpdateSignal.get_instance().updateBudget(self.game) diff --git a/resources/customized_payloads/A-10C_2.lua b/resources/customized_payloads/A-10C_2.lua new file mode 100644 index 00000000..bae0f048 --- /dev/null +++ b/resources/customized_payloads/A-10C_2.lua @@ -0,0 +1,219 @@ +local unitPayloads = { + ["name"] = "A-10C II", + ["payloads"] = { + [1] = { + ["name"] = "New Payload", + ["pylons"] = { + [1] = { + ["CLSID"] = "{LAU-131x3 - 7 AGR-20A}", + ["num"] = 8, + }, + [2] = { + ["CLSID"] = "{LAU-131x3 - 7 AGR-20 M282}", + ["num"] = 9, + }, + [3] = { + ["CLSID"] = "{LAU-131x3 - 7 AGR-20A}", + ["num"] = 4, + }, + [4] = { + ["CLSID"] = "{LAU-131x3 - 7 AGR-20 M282}", + ["num"] = 3, + }, + [5] = { + ["CLSID"] = "{LAU-131 - 7 AGR-20A}", + ["num"] = 2, + }, + [6] = { + ["CLSID"] = "{LAU-131 - 7 AGR-20A}", + ["num"] = 10, + }, + }, + ["tasks"] = { + [1] = 31, + }, + }, + [2] = { + ["name"] = "CAP", + ["pylons"] = { + [1] = { + ["CLSID"] = "{DB434044-F5D0-4F1F-9BA9-B73027E18DD3}", + ["num"] = 11, + }, + [2] = { + ["CLSID"] = "{DB434044-F5D0-4F1F-9BA9-B73027E18DD3}", + ["num"] = 1, + }, + }, + ["tasks"] = { + [1] = 31, + }, + }, + [3] = { + ["name"] = "CAS", + ["pylons"] = { + [1] = { + ["CLSID"] = "ALQ_184", + ["num"] = 1, + }, + [2] = { + ["CLSID"] = "{DB434044-F5D0-4F1F-9BA9-B73027E18DD3}", + ["num"] = 11, + }, + [3] = { + ["CLSID"] = "{A111396E-D3E8-4b9c-8AC9-2432489304D5}", + ["num"] = 10, + }, + [4] = { + ["CLSID"] = "{LAU-131 - 7 AGR-20 M282}", + ["num"] = 8, + }, + [5] = { + ["CLSID"] = "{E6A6262A-CA08-4B3D-B030-E1A993B98453}", + ["num"] = 9, + }, + [6] = { + ["CLSID"] = "{E6A6262A-CA08-4B3D-B030-E1A993B98452}", + ["num"] = 3, + }, + [7] = { + ["CLSID"] = "{LAU-131 - 7 AGR-20 M282}", + ["num"] = 4, + }, + [8] = { + ["CLSID"] = "{LAU-131 - 7 AGR-20A}", + ["num"] = 2, + }, + }, + ["tasks"] = { + [1] = 31, + }, + }, + [4] = { + ["name"] = "STRIKE", + ["pylons"] = { + [1] = { + ["CLSID"] = "{DB434044-F5D0-4F1F-9BA9-B73027E18DD3}", + ["num"] = 11, + }, + [2] = { + ["CLSID"] = "ALQ_184", + ["num"] = 1, + }, + [3] = { + ["CLSID"] = "{A111396E-D3E8-4b9c-8AC9-2432489304D5}", + ["num"] = 10, + }, + [4] = { + ["CLSID"] = "{GBU-38}", + ["num"] = 8, + }, + [5] = { + ["CLSID"] = "{GBU-38}", + ["num"] = 7, + }, + [6] = { + ["CLSID"] = "{GBU-38}", + ["num"] = 4, + }, + [7] = { + ["CLSID"] = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B66}", + ["num"] = 3, + }, + [8] = { + ["CLSID"] = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B66}", + ["num"] = 9, + }, + [9] = { + ["CLSID"] = "{GBU-38}", + ["num"] = 5, + }, + }, + ["tasks"] = { + [1] = 31, + }, + }, + [5] = { + ["name"] = "ANTISHIP", + ["pylons"] = { + [1] = { + ["CLSID"] = "{DB434044-F5D0-4F1F-9BA9-B73027E18DD3}", + ["num"] = 11, + }, + [2] = { + ["CLSID"] = "ALQ_184", + ["num"] = 1, + }, + [3] = { + ["CLSID"] = "{A111396E-D3E8-4b9c-8AC9-2432489304D5}", + ["num"] = 10, + }, + [4] = { + ["CLSID"] = "{DAC53A2F-79CA-42FF-A77A-F5649B601308}", + ["num"] = 9, + }, + [5] = { + ["CLSID"] = "{DAC53A2F-79CA-42FF-A77A-F5649B601308}", + ["num"] = 3, + }, + [6] = { + ["CLSID"] = "{LAU-131 - 7 AGR-20 M282}", + ["num"] = 2, + }, + }, + ["tasks"] = { + [1] = 31, + }, + }, + [6] = { + ["name"] = "SEAD", + ["pylons"] = { + [1] = { + ["CLSID"] = "{DB434044-F5D0-4F1F-9BA9-B73027E18DD3}", + ["num"] = 11, + }, + [2] = { + ["CLSID"] = "ALQ_184", + ["num"] = 1, + }, + [3] = { + ["CLSID"] = "{A111396E-D3E8-4b9c-8AC9-2432489304D5}", + ["num"] = 10, + }, + [4] = { + ["CLSID"] = "{DAC53A2F-79CA-42FF-A77A-F5649B601308}", + ["num"] = 9, + }, + [5] = { + ["CLSID"] = "{DAC53A2F-79CA-42FF-A77A-F5649B601308}", + ["num"] = 3, + }, + [6] = { + ["CLSID"] = "{LAU-131 - 7 AGR-20 M282}", + ["num"] = 2, + }, + [7] = { + ["CLSID"] = "{GBU-38}", + ["num"] = 7, + }, + [8] = { + ["CLSID"] = "{GBU-38}", + ["num"] = 5, + }, + [9] = { + ["CLSID"] = "{GBU-38}", + ["num"] = 8, + }, + [10] = { + ["CLSID"] = "{GBU-38}", + ["num"] = 4, + }, + }, + ["tasks"] = { + [1] = 31, + }, + }, + }, + ["unitType"] = "A-10C_2", +} +return unitPayloads diff --git a/resources/customized_payloads/F-16C_50.lua b/resources/customized_payloads/F-16C_50.lua index d48ad19e..285bccf3 100644 --- a/resources/customized_payloads/F-16C_50.lua +++ b/resources/customized_payloads/F-16C_50.lua @@ -2,41 +2,84 @@ local unitPayloads = { ["name"] = "F-16C_50", ["payloads"] = { [1] = { - ["name"] = "ANTISHIP", + ["name"] = "CAS", ["pylons"] = { [1] = { - ["CLSID"] = "LAU3_HE5", - ["num"] = 6, + ["CLSID"] = "{8A0BE8AE-58D4-4572-9263-3144C0D06364}", + ["num"] = 5, }, [2] = { - ["CLSID"] = "LAU3_HE5", + ["CLSID"] = "{DAC53A2F-79CA-42FF-A77A-F5649B601308}", ["num"] = 7, }, [3] = { - ["CLSID"] = "LAU3_HE5", - ["num"] = 4, - }, - [4] = { - ["CLSID"] = "LAU3_HE5", + ["CLSID"] = "{DAC53A2F-79CA-42FF-A77A-F5649B601308}", ["num"] = 3, }, - [5] = { + [4] = { ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}", ["num"] = 2, }, - [6] = { + [5] = { ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}", ["num"] = 1, }, - [7] = { + [6] = { ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}", ["num"] = 8, }, - [8] = { + [7] = { ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}", ["num"] = 9, }, + [8] = { + ["CLSID"] = "{5335D97A-35A5-4643-9D9B-026C75961E52}", + ["num"] = 4, + }, [9] = { + ["CLSID"] = "{5335D97A-35A5-4643-9D9B-026C75961E52}", + ["num"] = 6, + }, + [10] = { + ["CLSID"] = "{A111396E-D3E8-4b9c-8AC9-2432489304D5}", + ["num"] = 11, + }, + }, + ["tasks"] = { + }, + }, + [2] = { + ["name"] = "ANTISHIP", + ["pylons"] = { + [1] = { + ["CLSID"] = "{DAC53A2F-79CA-42FF-A77A-F5649B601308}", + ["num"] = 7, + }, + [2] = { + ["CLSID"] = "{DAC53A2F-79CA-42FF-A77A-F5649B601308}", + ["num"] = 3, + }, + [3] = { + ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}", + ["num"] = 2, + }, + [4] = { + ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}", + ["num"] = 1, + }, + [5] = { + ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}", + ["num"] = 8, + }, + [6] = { + ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}", + ["num"] = 9, + }, + [7] = { + ["CLSID"] = "{A111396E-D3E8-4b9c-8AC9-2432489304D5}", + ["num"] = 11, + }, + [8] = { ["CLSID"] = "{8A0BE8AE-58D4-4572-9263-3144C0D06364}", ["num"] = 5, }, @@ -44,7 +87,7 @@ local unitPayloads = { ["tasks"] = { }, }, - [2] = { + [3] = { ["name"] = "CAP", ["pylons"] = { [1] = { @@ -87,53 +130,6 @@ local unitPayloads = { ["tasks"] = { }, }, - [3] = { - ["name"] = "CAS", - ["pylons"] = { - [1] = { - ["CLSID"] = "{8A0BE8AE-58D4-4572-9263-3144C0D06364}", - ["num"] = 5, - }, - [2] = { - ["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}", - ["num"] = 7, - }, - [3] = { - ["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}", - ["num"] = 3, - }, - [4] = { - ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}", - ["num"] = 2, - }, - [5] = { - ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}", - ["num"] = 1, - }, - [6] = { - ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}", - ["num"] = 8, - }, - [7] = { - ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}", - ["num"] = 9, - }, - [8] = { - ["CLSID"] = "{5335D97A-35A5-4643-9D9B-026C75961E52}", - ["num"] = 4, - }, - [9] = { - ["CLSID"] = "{5335D97A-35A5-4643-9D9B-026C75961E52}", - ["num"] = 6, - }, - [11] = { - ["CLSID"] = "{A111396E-D3E8-4b9c-8AC9-2432489304D5}", - ["num"] = 11, - }, - }, - ["tasks"] = { - }, - }, [4] = { ["name"] = "STRIKE", ["pylons"] = { @@ -142,11 +138,11 @@ local unitPayloads = { ["num"] = 7, }, [2] = { - ["CLSID"] = "{TER_9A_2L*MK-82}", + ["CLSID"] = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}", ["num"] = 4, }, [3] = { - ["CLSID"] = "{TER_9A_2R*MK-82}", + ["CLSID"] = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}", ["num"] = 6, }, [4] = { @@ -173,7 +169,7 @@ local unitPayloads = { ["CLSID"] = "{8A0BE8AE-58D4-4572-9263-3144C0D06364}", ["num"] = 5, }, - [11] = { + [10] = { ["CLSID"] = "{A111396E-D3E8-4b9c-8AC9-2432489304D5}", ["num"] = 11, }, @@ -185,19 +181,19 @@ local unitPayloads = { ["name"] = "SEAD", ["pylons"] = { [1] = { - ["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}", + ["CLSID"] = "{F376DBEE-4CAE-41BA-ADD9-B2910AC95DEC}", ["num"] = 6, }, [2] = { - ["CLSID"] = "{5335D97A-35A5-4643-9D9B-026C75961E52}", + ["CLSID"] = "{B06DD79A-F21E-4EB9-BD9D-AB3844618C93}", ["num"] = 7, }, [3] = { - ["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}", + ["CLSID"] = "{F376DBEE-4CAE-41BA-ADD9-B2910AC95DEC}", ["num"] = 4, }, [4] = { - ["CLSID"] = "{5335D97A-35A5-4643-9D9B-026C75961E52}", + ["CLSID"] = "{B06DD79A-F21E-4EB9-BD9D-AB3844618C93}", ["num"] = 3, }, [5] = { diff --git a/resources/customized_payloads/P-47D-30.lua b/resources/customized_payloads/P-47D-30.lua index df5f21cd..71dc4891 100644 --- a/resources/customized_payloads/P-47D-30.lua +++ b/resources/customized_payloads/P-47D-30.lua @@ -2,26 +2,6 @@ local unitPayloads = { ["name"] = "P-47D-30", ["payloads"] = { [1] = { - ["name"] = "CAS", - ["pylons"] = { - [1] = { - ["CLSID"] = "{AN-M64}", - ["num"] = 3, - }, - [2] = { - ["CLSID"] = "{AN-M64}", - ["num"] = 2, - }, - [3] = { - ["CLSID"] = "{AN-M64}", - ["num"] = 1, - }, - }, - ["tasks"] = { - [1] = 11, - }, - }, - [2] = { ["name"] = "STRIKE", ["pylons"] = { [1] = { @@ -41,14 +21,34 @@ local unitPayloads = { [1] = 11, }, }, - [3] = { - ["name"] = "ANTISHIP", + [2] = { + ["name"] = "ANTISTRIKE", ["pylons"] = { }, ["tasks"] = { [1] = 11, }, }, + [3] = { + ["name"] = "CAS", + ["pylons"] = { + [1] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 3, + }, + [2] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 2, + }, + [3] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 1, + }, + }, + ["tasks"] = { + [1] = 11, + }, + }, [4] = { ["name"] = "CAP", ["pylons"] = { @@ -77,6 +77,25 @@ local unitPayloads = { [1] = 11, }, }, + [6] = { + ["name"] = "ANTISHIP", + ["pylons"] = { + [1] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 3, + }, + [2] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 2, + }, + [3] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 1, + }, + }, + ["tasks"] = { + }, + }, }, ["tasks"] = { }, diff --git a/resources/customized_payloads/P-47D-30bl1.lua b/resources/customized_payloads/P-47D-30bl1.lua new file mode 100644 index 00000000..50c84308 --- /dev/null +++ b/resources/customized_payloads/P-47D-30bl1.lua @@ -0,0 +1,96 @@ +local unitPayloads = { + ["name"] = "P-47D-30bl1", + ["payloads"] = { + [1] = { + ["name"] = "CAP", + ["pylons"] = { + }, + ["tasks"] = { + [1] = 11, + }, + }, + [2] = { + ["name"] = "CAS", + ["pylons"] = { + [1] = { + ["CLSID"] = "{AN_M57}", + ["num"] = 1, + }, + [2] = { + ["CLSID"] = "{AN_M57}", + ["num"] = 2, + }, + [3] = { + ["CLSID"] = "{AN_M57}", + ["num"] = 3, + }, + }, + ["tasks"] = { + [1] = 11, + }, + }, + [3] = { + ["name"] = "STRIKE", + ["pylons"] = { + [1] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 3, + }, + [2] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 2, + }, + [3] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 1, + }, + }, + ["tasks"] = { + [1] = 11, + }, + }, + [4] = { + ["name"] = "SEAD", + ["pylons"] = { + [1] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 3, + }, + [2] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 2, + }, + [3] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 1, + }, + }, + ["tasks"] = { + [1] = 11, + }, + }, + [5] = { + ["name"] = "ANTISHIP", + ["pylons"] = { + [1] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 3, + }, + [2] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 2, + }, + [3] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 1, + }, + }, + ["tasks"] = { + }, + }, + }, + ["tasks"] = { + }, + ["unitType"] = "P-47D-30bl1", +} +return unitPayloads diff --git a/resources/customized_payloads/P-47D-40.lua b/resources/customized_payloads/P-47D-40.lua new file mode 100644 index 00000000..fea43280 --- /dev/null +++ b/resources/customized_payloads/P-47D-40.lua @@ -0,0 +1,88 @@ +local unitPayloads = { + ["name"] = "P-47D-40", + ["payloads"] = { + [1] = { + ["name"] = "CAP", + ["pylons"] = { + }, + ["tasks"] = { + [1] = 11, + }, + }, + [2] = { + ["name"] = "CAS", + ["pylons"] = { + [1] = { + ["CLSID"] = "{P47_5_HVARS_ON_LEFT_WING_RAILS}", + ["num"] = 4, + }, + [2] = { + ["CLSID"] = "{P47_5_HVARS_ON_RIGHT_WING_RAILS}", + ["num"] = 5, + }, + }, + ["tasks"] = { + [1] = 11, + }, + }, + [3] = { + ["name"] = "SEAD", + ["pylons"] = { + [1] = { + ["CLSID"] = "{P47_5_HVARS_ON_LEFT_WING_RAILS}", + ["num"] = 4, + }, + [2] = { + ["CLSID"] = "{P47_5_HVARS_ON_RIGHT_WING_RAILS}", + ["num"] = 5, + }, + }, + ["tasks"] = { + [1] = 11, + }, + }, + [4] = { + ["name"] = "STRIKE", + ["pylons"] = { + [1] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 3, + }, + [2] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 2, + }, + [3] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 1, + }, + }, + ["tasks"] = { + [1] = 11, + }, + }, + [5] = { + ["name"] = "ANTISHIP", + ["pylons"] = { + [1] = { + ["CLSID"] = "{P47_5_HVARS_ON_RIGHT_WING_RAILS}", + ["num"] = 5, + }, + [2] = { + ["CLSID"] = "{P47_5_HVARS_ON_LEFT_WING_RAILS}", + ["num"] = 4, + }, + [3] = { + ["CLSID"] = "{AN-M64}", + ["num"] = 1, + }, + }, + ["tasks"] = { + }, + }, + }, + ["tasks"] = { + }, + ["unitType"] = "P-47D-40", +} +return unitPayloads diff --git a/resources/scripts/dcs_liberation.lua b/resources/scripts/dcs_liberation.lua index 5c594d35..70a5c239 100644 --- a/resources/scripts/dcs_liberation.lua +++ b/resources/scripts/dcs_liberation.lua @@ -1,9 +1,5 @@ -local jsonlib = {{json_file_abs_location}} -json = loadfile(jsonlib)() - logger = mist.Logger:new("DCSLiberation", "info") - -debriefing_file_location = {{debriefing_file_location}} +logger:info("Check that json.lua is loaded : json = "..tostring(json)) killed_aircrafts = {} killed_ground_units = {} @@ -32,12 +28,56 @@ write_state = function() ["mission_ended"] = mission_ended, ["destroyed_objects_positions"] = destroyed_objects_positions, } + if not json then + local message = string.format("Unable to save DCS Liberation state to %s, JSON library is not loaded !",debriefing_file_location) + logger:error(message) + messageAll(message) + end fp:write(json:encode(game_state)) fp:close() -- logger.info("Done writing DCS Liberation state") -- messageAll("Done writing DCS Liberation state.") end +debriefing_file_location = nil +if dcsLiberation then + debriefing_file_location = dcsLiberation.installPath +end +if debriefing_file_location then + logger:info("Using DCS Liberation install folder for state.json") +else + if os then + debriefing_file_location = os.getenv("LIBERATION_EXPORT_DIR") + if debriefing_file_location then debriefing_file_location = debriefing_file_location .. "\\" end + end + if debriefing_file_location then + logger:info("Using LIBERATION_EXPORT_DIR environment variable for state.json") + else + if os then + debriefing_file_location = os.getenv("TEMP") + if debriefing_file_location then debriefing_file_location = debriefing_file_location .. "\\" end + end + if debriefing_file_location then + logger:info("Using TEMP environment variable for state.json") + else + if lfs then + debriefing_file_location = lfs.writedir() + end + if debriefing_file_location then + logger:info("Using DCS working directory for state.json") + end + end + end +end +if debriefing_file_location then + local filename = "state.json" + if not debriefing_file_location:sub(-#filename) == filename then + debriefing_file_location = debriefing_file_location .. filename + end + logger:info(string.format("DCS Liberation state will be written as json to [[%s]]",debriefing_file_location)) +else + logger:error("No usable storage path for state.json") +end write_state_error_handling = function() if pcall(write_state) then diff --git a/resources/scripts/json.lua b/resources/scripts/json.lua index 8b3ddb1f..632c6840 100644 --- a/resources/scripts/json.lua +++ b/resources/scripts/json.lua @@ -968,7 +968,7 @@ function OBJDEF:new(args) return setmetatable(new, OBJDEF) end -return OBJDEF:new() +json = OBJDEF:new() -- -- Version history: diff --git a/resources/scripts/plugins/__plugins.lst.sample b/resources/scripts/plugins/__plugins.lst.sample new file mode 100644 index 00000000..27cbf0c0 --- /dev/null +++ b/resources/scripts/plugins/__plugins.lst.sample @@ -0,0 +1,29 @@ +# this is a list of lua scripts that will be injected in the mission, in the same order +mist.lua +Moose.lua +CTLD.lua +NIOD.lua +WeatherMark.lua +veaf.lua +dcsUnits.lua +# JTACAutoLase is an empty file, only there to disable loading the official script (already included in CTLD) +JTACAutoLase.lua +veafAssets.lua +veafCarrierOperations.lua +veafCarrierOperations2.lua +veafCasMission.lua +veafCombatMission.lua +veafCombatZone.lua +veafGrass.lua +veafInterpreter.lua +veafMarkers.lua +veafMove.lua +veafNamedPoints.lua +veafRadio.lua +veafRemote.lua +veafSecurity.lua +veafShortcuts.lua +veafSpawn.lua +veafTransportMission.lua +veafUnits.lua +missionConfig.lua diff --git a/resources/ui/misc/hangar.png b/resources/ui/misc/hangar.png new file mode 100644 index 00000000..dc98f559 Binary files /dev/null and b/resources/ui/misc/hangar.png differ diff --git a/resources/ui/units/aircrafts/AJS37 Viggen_24.jpg b/resources/ui/units/aircrafts/AJS37_24.jpg similarity index 100% rename from resources/ui/units/aircrafts/AJS37 Viggen_24.jpg rename to resources/ui/units/aircrafts/AJS37_24.jpg diff --git a/resources/ui/units/vehicles/2S6 Tunguska_24.jpg b/resources/ui/units/vehicles/2S6 Tunguska_24.jpg new file mode 100644 index 00000000..94e5ee27 Binary files /dev/null and b/resources/ui/units/vehicles/2S6 Tunguska_24.jpg differ diff --git a/resources/ui/units/vehicles/Bofors 40 mm_24.jpg b/resources/ui/units/vehicles/Bofors 40 mm_24.jpg new file mode 100644 index 00000000..dda787d8 Binary files /dev/null and b/resources/ui/units/vehicles/Bofors 40 mm_24.jpg differ diff --git a/resources/ui/units/vehicles/Gepard_24.jpg b/resources/ui/units/vehicles/Gepard_24.jpg new file mode 100644 index 00000000..f2775a84 Binary files /dev/null and b/resources/ui/units/vehicles/Gepard_24.jpg differ diff --git a/resources/ui/units/vehicles/HAWK ANMPG-46 TR_24.jpg b/resources/ui/units/vehicles/HAWK ANMPG-46 TR_24.jpg new file mode 100644 index 00000000..7c1ae752 Binary files /dev/null and b/resources/ui/units/vehicles/HAWK ANMPG-46 TR_24.jpg differ diff --git a/resources/ui/units/vehicles/HAWK ANMPQ-50 SR_24.jpg b/resources/ui/units/vehicles/HAWK ANMPQ-50 SR_24.jpg new file mode 100644 index 00000000..17408863 Binary files /dev/null and b/resources/ui/units/vehicles/HAWK ANMPQ-50 SR_24.jpg differ diff --git a/resources/ui/units/vehicles/HAWK M192 LN_24.jpg b/resources/ui/units/vehicles/HAWK M192 LN_24.jpg new file mode 100644 index 00000000..08544b41 Binary files /dev/null and b/resources/ui/units/vehicles/HAWK M192 LN_24.jpg differ diff --git a/resources/ui/units/vehicles/HQ-7 Mobile Launcher_24.jpg b/resources/ui/units/vehicles/HQ-7 Mobile Launcher_24.jpg new file mode 100644 index 00000000..7fd18f05 Binary files /dev/null and b/resources/ui/units/vehicles/HQ-7 Mobile Launcher_24.jpg differ diff --git a/resources/ui/units/vehicles/HQ-7 Mobile Radar_24.jpg b/resources/ui/units/vehicles/HQ-7 Mobile Radar_24.jpg new file mode 100644 index 00000000..377709ef Binary files /dev/null and b/resources/ui/units/vehicles/HQ-7 Mobile Radar_24.jpg differ diff --git a/resources/ui/units/vehicles/M-163 Vulcan_24.jpg b/resources/ui/units/vehicles/M-163 Vulcan_24.jpg new file mode 100644 index 00000000..f5489310 Binary files /dev/null and b/resources/ui/units/vehicles/M-163 Vulcan_24.jpg differ diff --git a/resources/ui/units/vehicles/M1043 HMMWV_24.jpg b/resources/ui/units/vehicles/M1043 HMMWV Armament_24.jpg similarity index 100% rename from resources/ui/units/vehicles/M1043 HMMWV_24.jpg rename to resources/ui/units/vehicles/M1043 HMMWV Armament_24.jpg diff --git a/resources/ui/units/vehicles/M1097 Avenger_24.jpg b/resources/ui/units/vehicles/M1097 Avenger_24.jpg new file mode 100644 index 00000000..1ed50975 Binary files /dev/null and b/resources/ui/units/vehicles/M1097 Avenger_24.jpg differ diff --git a/resources/ui/units/vehicles/M1A2_24.jpg b/resources/ui/units/vehicles/M1A2_24.jpg new file mode 100644 index 00000000..d0bf9a66 Binary files /dev/null and b/resources/ui/units/vehicles/M1A2_24.jpg differ diff --git a/resources/ui/units/vehicles/M2A2_24.jpg b/resources/ui/units/vehicles/M2A2_24.jpg new file mode 100644 index 00000000..037dddf1 Binary files /dev/null and b/resources/ui/units/vehicles/M2A2_24.jpg differ diff --git a/resources/ui/units/vehicles/M48 Chaparral_24.jpg b/resources/ui/units/vehicles/M48 Chaparral_24.jpg new file mode 100644 index 00000000..2d01a9cc Binary files /dev/null and b/resources/ui/units/vehicles/M48 Chaparral_24.jpg differ diff --git a/resources/ui/units/vehicles/M6 Linebacker_24.jpg b/resources/ui/units/vehicles/M6 Linebacker_24.jpg new file mode 100644 index 00000000..615368da Binary files /dev/null and b/resources/ui/units/vehicles/M6 Linebacker_24.jpg differ diff --git a/resources/ui/units/vehicles/M818_24.jpg b/resources/ui/units/vehicles/M818_24.jpg new file mode 100644 index 00000000..23372bf0 Binary files /dev/null and b/resources/ui/units/vehicles/M818_24.jpg differ diff --git a/resources/ui/units/vehicles/Marder_24.jpg b/resources/ui/units/vehicles/Marder_24.jpg new file mode 100644 index 00000000..3717e28d Binary files /dev/null and b/resources/ui/units/vehicles/Marder_24.jpg differ diff --git a/resources/ui/units/vehicles/Merkava Mk IV_24.jpg b/resources/ui/units/vehicles/Merkava Mk4_24.jpg similarity index 100% rename from resources/ui/units/vehicles/Merkava Mk IV_24.jpg rename to resources/ui/units/vehicles/Merkava Mk4_24.jpg diff --git a/resources/ui/units/vehicles/Osa 9A33 ln_24.jpg b/resources/ui/units/vehicles/Osa 9A33 ln_24.jpg new file mode 100644 index 00000000..5efab07a Binary files /dev/null and b/resources/ui/units/vehicles/Osa 9A33 ln_24.jpg differ diff --git a/resources/ui/units/vehicles/Roland ADS_24.jpg b/resources/ui/units/vehicles/Roland ADS_24.jpg new file mode 100644 index 00000000..3ee30605 Binary files /dev/null and b/resources/ui/units/vehicles/Roland ADS_24.jpg differ diff --git a/resources/ui/units/vehicles/Roland Radar_24.jpg b/resources/ui/units/vehicles/Roland Radar_24.jpg new file mode 100644 index 00000000..5969b934 Binary files /dev/null and b/resources/ui/units/vehicles/Roland Radar_24.jpg differ diff --git a/resources/ui/units/vehicles/T-80UD_24.jpg b/resources/ui/units/vehicles/T-80UD_24.jpg new file mode 100644 index 00000000..8537a9b3 Binary files /dev/null and b/resources/ui/units/vehicles/T-80UD_24.jpg differ diff --git a/resources/ui/units/vehicles/ZU-23 Ural_24.jpg b/resources/ui/units/vehicles/ZU-23 Ural_24.jpg new file mode 100644 index 00000000..57179b9d Binary files /dev/null and b/resources/ui/units/vehicles/ZU-23 Ural_24.jpg differ diff --git a/resources/ui/units/vehicles/ZU-23_24.jpg b/resources/ui/units/vehicles/ZU-23_24.jpg new file mode 100644 index 00000000..4dbf6d31 Binary files /dev/null and b/resources/ui/units/vehicles/ZU-23_24.jpg differ diff --git a/theater/controlpoint.py b/theater/controlpoint.py index 8f0d59c9..a35cd698 100644 --- a/theater/controlpoint.py +++ b/theater/controlpoint.py @@ -108,14 +108,23 @@ class ControlPoint(MissionTarget): @property def is_carrier(self): + """ + :return: Whether this control point is an aircraft carrier + """ return self.cptype in [ControlPointType.AIRCRAFT_CARRIER_GROUP, ControlPointType.LHA_GROUP] @property def is_fleet(self): + """ + :return: Whether this control point is a boat (mobile) + """ return self.cptype in [ControlPointType.AIRCRAFT_CARRIER_GROUP, ControlPointType.LHA_GROUP] @property def is_lha(self): + """ + :return: Whether this control point is an LHA + """ return self.cptype in [ControlPointType.LHA_GROUP] @property @@ -128,6 +137,20 @@ class ControlPoint(MissionTarget): result.append(r) return result + @property + def available_aircraft_slots(self): + """ + :return: The maximum number of aircraft that can be stored in this control point + """ + if self.cptype == ControlPointType.AIRBASE: + return len(self.airport.parking_slots) + elif self.is_lha: + return 20 + elif self.is_carrier: + return 90 + else: + return 0 + def connect(self, to): self.connected_points.append(to) self.stances[to.id] = CombatStance.DEFENSIVE