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