Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41445c3092 | ||
|
|
2a3bf9821b | ||
|
|
819d775282 | ||
|
|
efbc6fe3ae | ||
|
|
262ba6c113 | ||
|
|
c41ecb6735 | ||
|
|
f5c32c6b98 | ||
|
|
a1886e37f8 | ||
|
|
7dd3367203 | ||
|
|
e72c82521a | ||
|
|
aca415db23 | ||
|
|
98b2d8b3b9 | ||
|
|
f8ef5db5a3 | ||
|
|
a44cbe5972 | ||
|
|
2066a2e9bc | ||
|
|
f381bf85a4 | ||
|
|
44dcdcc8bb | ||
|
|
01220800f3 | ||
|
|
8ecb4cdcf4 | ||
|
|
8402d108c0 | ||
|
|
75af2d468e | ||
|
|
72ce37f008 | ||
|
|
e48e884286 | ||
|
|
f9d5c1f8de | ||
|
|
0873dcab0a | ||
|
|
a1343c2849 | ||
|
|
f732cc54d0 | ||
|
|
473a7d5fa4 | ||
|
|
8054a0b62f | ||
|
|
7f9cba5d37 | ||
|
|
f68e6387e6 | ||
|
|
f032001bee | ||
|
|
3bae591c04 | ||
|
|
18a1f0af94 | ||
|
|
afbd4a4716 | ||
|
|
a98da14c6f | ||
|
|
a2a70213a7 | ||
|
|
8b0f877041 | ||
|
|
bf7ad4cad2 | ||
|
|
66b659c0af | ||
|
|
8709ea948f | ||
|
|
7236c10403 | ||
|
|
dde703ec41 | ||
|
|
aa2e9b123c | ||
|
|
737e04d09e | ||
|
|
0fe59efd72 | ||
|
|
ddb50e6254 | ||
|
|
72e6ae4186 | ||
|
|
4d510f643a | ||
|
|
66f607b5e6 | ||
|
|
1a125c62e7 | ||
|
|
84da44a27b | ||
|
|
5e35efcaef |
39
.github/workflows/build.yml
vendored
Normal file
@@ -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/
|
||||
1
.gitignore
vendored
@@ -21,3 +21,4 @@ logs/liberation.log
|
||||
qt_ui/logs/liberation.log
|
||||
|
||||
*.psd
|
||||
resources/scripts/plugins/*
|
||||
|
||||
4
.gitmodules
vendored
@@ -1,4 +1,4 @@
|
||||
[submodule "pydcs"]
|
||||
path = pydcs
|
||||
url = https://github.com/khopa/dcs
|
||||
branch = dataexport
|
||||
url = https://github.com/pydcs/dcs
|
||||
branch = master
|
||||
|
||||
11
.vscode/launch.json
vendored
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
44
changelog.md
@@ -1,3 +1,39 @@
|
||||
# 2.1.5
|
||||
|
||||
## Features/Improvements :
|
||||
* **[Units/Factions]** Enabled EPLRS for ground units that supports it (so they appear on A-10C II TAD and Helmet)
|
||||
|
||||
## Fixes :
|
||||
* **[UI]** Fixed an issue that prevent saving after aborting a mission
|
||||
* **[Mission Generator]** Fixed aircraft landing point type being wrong
|
||||
|
||||
# 2.1.4
|
||||
|
||||
## Fixes :
|
||||
* **[UI]** Fixed an issue that prevent generating the mission (take off button no working) on old savegames.
|
||||
|
||||
# 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 :
|
||||
@@ -5,9 +41,9 @@
|
||||
* **[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.
|
||||
information. (Thanks to contributor danalbert)
|
||||
* **[Radios]** Allocate separate intra-flight channels for most aircraft to reduce global
|
||||
chatter.
|
||||
chatter. (Thanks to contributor danalbert)
|
||||
* **[Radios]** Configure radio channel presets for most aircraft. Currently supported are:
|
||||
* AJS37
|
||||
* AV-8B
|
||||
@@ -15,7 +51,7 @@
|
||||
* F-16C
|
||||
* F/A-18C
|
||||
* JF-17
|
||||
* M-2000C
|
||||
* 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
|
||||
@@ -25,7 +61,7 @@
|
||||
* **[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
|
||||
|
||||
@@ -192,6 +192,7 @@ PRICES = {
|
||||
|
||||
A_10A: 16,
|
||||
A_10C: 22,
|
||||
A_10C_2: 24,
|
||||
|
||||
# heli
|
||||
Ka_50: 13,
|
||||
@@ -498,6 +499,7 @@ UNIT_BY_TASK = {
|
||||
AJS37,
|
||||
A_10A,
|
||||
A_10C,
|
||||
A_10C_2,
|
||||
Su_17M4,
|
||||
Su_25,
|
||||
Su_25T,
|
||||
@@ -957,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,
|
||||
|
||||
@@ -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": [
|
||||
|
||||
|
||||
@@ -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": [
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ USA_2005 = {
|
||||
FA_18C_hornet,
|
||||
F_16C_50,
|
||||
A_10C,
|
||||
A_10C_2,
|
||||
AV8BNA,
|
||||
MQ_9_Reaper,
|
||||
|
||||
|
||||
@@ -190,9 +190,9 @@ class Game:
|
||||
|
||||
def is_player_attack(self, event):
|
||||
if isinstance(event, Event):
|
||||
return event.attacker_name == self.player_name
|
||||
return event and event.attacker_name and event.attacker_name == self.player_name
|
||||
else:
|
||||
return event.name == self.player_name
|
||||
return event and event.name and event.name == self.player_name
|
||||
|
||||
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint] = None):
|
||||
|
||||
|
||||
@@ -68,26 +68,8 @@ class Operation:
|
||||
def initialize(self, mission: Mission, conflict: Conflict):
|
||||
self.current_mission = mission
|
||||
self.conflict = conflict
|
||||
self.radio_registry = RadioRegistry()
|
||||
self.tacan_registry = TacanRegistry()
|
||||
self.airgen = AircraftConflictGenerator(
|
||||
mission, conflict, self.game.settings, self.game,
|
||||
self.radio_registry)
|
||||
self.airsupportgen = AirSupportConflictGenerator(
|
||||
mission, conflict, self.game, self.radio_registry,
|
||||
self.tacan_registry)
|
||||
self.triggersgen = TriggersGenerator(mission, conflict, self.game)
|
||||
self.visualgen = VisualGenerator(mission, conflict, self.game)
|
||||
self.envgen = EnviromentGenerator(mission, conflict, self.game)
|
||||
self.forcedoptionsgen = ForcedOptionsGenerator(mission, conflict, self.game)
|
||||
self.groundobjectgen = GroundObjectsGenerator(
|
||||
mission,
|
||||
conflict,
|
||||
self.game,
|
||||
self.radio_registry,
|
||||
self.tacan_registry
|
||||
)
|
||||
self.briefinggen = BriefingGenerator(mission, conflict, self.game)
|
||||
self.briefinggen = BriefingGenerator(self.current_mission,
|
||||
self.conflict, self.game)
|
||||
|
||||
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||
with open("resources/default_options.lua", "r") as f:
|
||||
@@ -127,6 +109,9 @@ class Operation:
|
||||
self.defenders_starting_position = self.to_cp.at
|
||||
|
||||
def generate(self):
|
||||
radio_registry = RadioRegistry()
|
||||
tacan_registry = TacanRegistry()
|
||||
|
||||
# Dedup beacon/radio frequencies, since some maps have some frequencies
|
||||
# used multiple times.
|
||||
beacons = load_beacons_for_terrain(self.game.theater.terrain.name)
|
||||
@@ -138,7 +123,7 @@ class Operation:
|
||||
logging.error(
|
||||
f"TACAN beacon has no channel: {beacon.callsign}")
|
||||
else:
|
||||
self.tacan_registry.reserve(beacon.tacan_channel)
|
||||
tacan_registry.reserve(beacon.tacan_channel)
|
||||
|
||||
for airfield, data in AIRFIELD_DATA.items():
|
||||
if data.theater == self.game.theater.terrain.name:
|
||||
@@ -150,16 +135,26 @@ class Operation:
|
||||
# beacon list.
|
||||
|
||||
for frequency in unique_map_frequencies:
|
||||
self.radio_registry.reserve(frequency)
|
||||
radio_registry.reserve(frequency)
|
||||
|
||||
# Generate meteo
|
||||
envgen = EnviromentGenerator(self.current_mission, self.conflict,
|
||||
self.game)
|
||||
if self.environment_settings is None:
|
||||
self.environment_settings = self.envgen.generate()
|
||||
self.environment_settings = envgen.generate()
|
||||
else:
|
||||
self.envgen.load(self.environment_settings)
|
||||
envgen.load(self.environment_settings)
|
||||
|
||||
# Generate ground object first
|
||||
self.groundobjectgen.generate()
|
||||
|
||||
groundobjectgen = GroundObjectsGenerator(
|
||||
self.current_mission,
|
||||
self.conflict,
|
||||
self.game,
|
||||
radio_registry,
|
||||
tacan_registry
|
||||
)
|
||||
groundobjectgen.generate()
|
||||
|
||||
# Generate destroyed units
|
||||
for d in self.game.get_destroyed_units():
|
||||
@@ -180,11 +175,16 @@ class Operation:
|
||||
dead=True,
|
||||
)
|
||||
|
||||
|
||||
# Air Support (Tanker & Awacs)
|
||||
self.airsupportgen.generate(self.is_awacs_enabled)
|
||||
airsupportgen = AirSupportConflictGenerator(
|
||||
self.current_mission, self.conflict, self.game, radio_registry,
|
||||
tacan_registry)
|
||||
airsupportgen.generate(self.is_awacs_enabled)
|
||||
|
||||
# Generate Activity on the map
|
||||
airgen = AircraftConflictGenerator(
|
||||
self.current_mission, self.conflict, self.game.settings, self.game,
|
||||
radio_registry)
|
||||
for cp in self.game.theater.controlpoints:
|
||||
side = cp.captured
|
||||
if side:
|
||||
@@ -192,11 +192,11 @@ class Operation:
|
||||
else:
|
||||
country = self.current_mission.country(self.game.enemy_country)
|
||||
if cp.id in self.game.planners.keys():
|
||||
self.airgen.generate_flights(
|
||||
airgen.generate_flights(
|
||||
cp,
|
||||
country,
|
||||
self.game.planners[cp.id],
|
||||
self.groundobjectgen.runways
|
||||
groundobjectgen.runways
|
||||
)
|
||||
|
||||
# Generate ground units on frontline everywhere
|
||||
@@ -221,66 +221,128 @@ class Operation:
|
||||
self.current_mission.groundControl.red_tactical_commander = self.ca_slots
|
||||
|
||||
# Triggers
|
||||
if self.game.is_player_attack(self.conflict.attackers_country):
|
||||
cp = self.conflict.from_cp
|
||||
else:
|
||||
cp = self.conflict.to_cp
|
||||
self.triggersgen.generate()
|
||||
triggersgen = TriggersGenerator(self.current_mission, self.conflict,
|
||||
self.game)
|
||||
triggersgen.generate()
|
||||
|
||||
# Options
|
||||
self.forcedoptionsgen.generate()
|
||||
forcedoptionsgen = ForcedOptionsGenerator(self.current_mission,
|
||||
self.conflict, self.game)
|
||||
forcedoptionsgen.generate()
|
||||
|
||||
# Generate Visuals Smoke Effects
|
||||
visualgen = VisualGenerator(self.current_mission, self.conflict,
|
||||
self.game)
|
||||
if self.game.settings.perf_smoke_gen:
|
||||
self.visualgen.generate()
|
||||
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 = []
|
||||
plugin_file_path = Path("./resources/scripts/plugins/__plugins.lst")
|
||||
if plugin_file_path.exists():
|
||||
for line in plugin_file_path.read_text().splitlines():
|
||||
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)
|
||||
else:
|
||||
logging.info(
|
||||
f"Not loading plugins, {plugin_file_path} does not exist")
|
||||
|
||||
# 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)
|
||||
|
||||
self.assign_channels_to_flights()
|
||||
# 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(airgen.flights,
|
||||
airsupportgen.air_support)
|
||||
|
||||
kneeboard_generator = KneeboardGenerator(self.current_mission)
|
||||
|
||||
for dynamic_runway in self.groundobjectgen.runways.values():
|
||||
for dynamic_runway in groundobjectgen.runways.values():
|
||||
self.briefinggen.add_dynamic_runway(dynamic_runway)
|
||||
|
||||
for tanker in self.airsupportgen.air_support.tankers:
|
||||
for tanker in airsupportgen.air_support.tankers:
|
||||
self.briefinggen.add_tanker(tanker)
|
||||
kneeboard_generator.add_tanker(tanker)
|
||||
|
||||
if self.is_awacs_enabled:
|
||||
for awacs in self.airsupportgen.air_support.awacs:
|
||||
for awacs in airsupportgen.air_support.awacs:
|
||||
self.briefinggen.add_awacs(awacs)
|
||||
kneeboard_generator.add_awacs(awacs)
|
||||
|
||||
@@ -288,21 +350,23 @@ class Operation:
|
||||
self.briefinggen.add_jtac(jtac)
|
||||
kneeboard_generator.add_jtac(jtac)
|
||||
|
||||
for flight in self.airgen.flights:
|
||||
for flight in airgen.flights:
|
||||
self.briefinggen.add_flight(flight)
|
||||
kneeboard_generator.add_flight(flight)
|
||||
|
||||
self.briefinggen.generate()
|
||||
kneeboard_generator.generate()
|
||||
|
||||
def assign_channels_to_flights(self) -> None:
|
||||
def assign_channels_to_flights(self, flights: List[FlightData],
|
||||
air_support: AirSupport) -> None:
|
||||
"""Assigns preset radio channels for client flights."""
|
||||
for flight in self.airgen.flights:
|
||||
for flight in flights:
|
||||
if not flight.client_units:
|
||||
continue
|
||||
self.assign_channels_to_flight(flight)
|
||||
self.assign_channels_to_flight(flight, air_support)
|
||||
|
||||
def assign_channels_to_flight(self, flight: FlightData) -> None:
|
||||
def assign_channels_to_flight(self, flight: FlightData,
|
||||
air_support: AirSupport) -> None:
|
||||
"""Assigns preset radio channels for a client flight."""
|
||||
airframe = flight.aircraft_type
|
||||
|
||||
@@ -313,4 +377,5 @@ class Operation:
|
||||
return
|
||||
|
||||
aircraft_data.channel_allocator.assign_channels_for_flight(
|
||||
flight, self.airsupportgen.air_support)
|
||||
flight, air_support
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
),
|
||||
|
||||
@@ -950,6 +952,7 @@ class AircraftConflictGenerator:
|
||||
# pt.tasks.append(engagetgt)
|
||||
elif point.waypoint_type == FlightWaypointType.LANDING_POINT:
|
||||
pt.type = "Land"
|
||||
pt.action = PointAction.Landing
|
||||
elif point.waypoint_type == FlightWaypointType.INGRESS_STRIKE:
|
||||
|
||||
if group.units[0].unit_type == B_17G:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -183,6 +183,11 @@ class GroundConflictGenerator:
|
||||
return
|
||||
|
||||
for dcs_group, group in ally_groups:
|
||||
|
||||
if hasattr(group.units[0], 'eplrs'):
|
||||
if group.units[0].eplrs:
|
||||
dcs_group.points[0].tasks.append(EPLRS(dcs_group.id))
|
||||
|
||||
if group.role == CombatGroupRole.ARTILLERY:
|
||||
# Fire on any ennemy in range
|
||||
if self.game.settings.perf_artillery:
|
||||
|
||||
@@ -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,
|
||||
@@ -98,6 +99,7 @@ CAS_CAPABLE = [
|
||||
|
||||
A_10A,
|
||||
A_10C,
|
||||
A_10C_2,
|
||||
AV8BNA,
|
||||
|
||||
F_86F_Sabre,
|
||||
@@ -108,6 +110,9 @@ CAS_CAPABLE = [
|
||||
F_16C_50,
|
||||
FA_18C_hornet,
|
||||
|
||||
Tornado_IDS,
|
||||
Tornado_GR4,
|
||||
|
||||
C_101CC,
|
||||
MB_339PAN,
|
||||
L_39ZA,
|
||||
@@ -121,7 +126,6 @@ CAS_CAPABLE = [
|
||||
AH_64D,
|
||||
AH_1W,
|
||||
|
||||
|
||||
UH_1H,
|
||||
|
||||
Mi_8MT,
|
||||
@@ -168,6 +172,9 @@ SEAD_CAPABLE = [
|
||||
Su_34,
|
||||
MiG_27K,
|
||||
|
||||
Tornado_IDS,
|
||||
Tornado_GR4,
|
||||
|
||||
A_4E_C,
|
||||
Rafale_A_S
|
||||
]
|
||||
@@ -191,6 +198,7 @@ STRIKE_CAPABLE = [
|
||||
|
||||
A_10A,
|
||||
A_10C,
|
||||
A_10C_2,
|
||||
AV8BNA,
|
||||
|
||||
F_86F_Sabre,
|
||||
@@ -201,6 +209,9 @@ STRIKE_CAPABLE = [
|
||||
F_16C_50,
|
||||
FA_18C_hornet,
|
||||
|
||||
Tornado_IDS,
|
||||
Tornado_GR4,
|
||||
|
||||
C_101CC,
|
||||
L_39ZA,
|
||||
AJS37,
|
||||
@@ -235,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
|
||||
]
|
||||
|
||||
@@ -80,6 +80,10 @@ class GroundObjectsGenerator:
|
||||
vehicle.heading = u.heading
|
||||
vehicle.player_can_drive = True
|
||||
vg.add_unit(vehicle)
|
||||
|
||||
if hasattr(utype, 'eplrs'):
|
||||
if utype.eplrs:
|
||||
vg.points[0].tasks.append(EPLRS(vg.id))
|
||||
else:
|
||||
vg = self.m.ship_group(side, g.name, utype, position=g.position,
|
||||
heading=g.units[0].heading)
|
||||
|
||||
@@ -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
|
||||
|
||||
2
pydcs
@@ -25,7 +25,7 @@ class ERC_90(unittype.VehicleType):
|
||||
detection_range = 0
|
||||
threat_range = 4000
|
||||
air_weapon_dist = 4000
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
|
||||
class VAB__50(unittype.VehicleType):
|
||||
@@ -34,7 +34,7 @@ class VAB__50(unittype.VehicleType):
|
||||
detection_range = 0
|
||||
threat_range = 1200
|
||||
air_weapon_dist = 1200
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
|
||||
class VAB_T20_13(unittype.VehicleType):
|
||||
@@ -43,7 +43,7 @@ class VAB_T20_13(unittype.VehicleType):
|
||||
detection_range = 0
|
||||
threat_range = 2000
|
||||
air_weapon_dist = 2000
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
|
||||
class VAB_MEPHISTO(unittype.VehicleType):
|
||||
@@ -52,7 +52,7 @@ class VAB_MEPHISTO(unittype.VehicleType):
|
||||
detection_range = 0
|
||||
threat_range = 4000
|
||||
air_weapon_dist = 4000
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
|
||||
class VBL__50(unittype.VehicleType):
|
||||
@@ -61,7 +61,7 @@ class VBL__50(unittype.VehicleType):
|
||||
detection_range = 0
|
||||
threat_range = 1200
|
||||
air_weapon_dist = 1200
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
|
||||
class VBL_AANF1(unittype.VehicleType):
|
||||
@@ -70,7 +70,7 @@ class VBL_AANF1(unittype.VehicleType):
|
||||
detection_range = 0
|
||||
threat_range = 1000
|
||||
air_weapon_dist = 1000
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
|
||||
class VBAE_CRAB(unittype.VehicleType):
|
||||
@@ -79,7 +79,7 @@ class VBAE_CRAB(unittype.VehicleType):
|
||||
detection_range = 0
|
||||
threat_range = 3500
|
||||
air_weapon_dist = 3500
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
|
||||
class VBAE_CRAB_MMP(unittype.VehicleType):
|
||||
@@ -88,7 +88,7 @@ class VBAE_CRAB_MMP(unittype.VehicleType):
|
||||
detection_range = 0
|
||||
threat_range = 3500
|
||||
air_weapon_dist = 3500
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
|
||||
class AMX_30B2(unittype.VehicleType):
|
||||
@@ -121,7 +121,7 @@ class DIM__TOYOTA_BLUE(unittype.VehicleType):
|
||||
detection_range = 0
|
||||
threat_range = 1200
|
||||
air_weapon_dist = 1200
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
|
||||
class DIM__TOYOTA_GREEN(unittype.VehicleType):
|
||||
@@ -130,7 +130,7 @@ class DIM__TOYOTA_GREEN(unittype.VehicleType):
|
||||
detection_range = 0
|
||||
threat_range = 1200
|
||||
air_weapon_dist = 1200
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
|
||||
class DIM__TOYOTA_DESERT(unittype.VehicleType):
|
||||
@@ -139,7 +139,7 @@ class DIM__TOYOTA_DESERT(unittype.VehicleType):
|
||||
detection_range = 0
|
||||
threat_range = 1200
|
||||
air_weapon_dist = 1200
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
|
||||
class DIM__KAMIKAZE(unittype.VehicleType):
|
||||
@@ -148,7 +148,7 @@ class DIM__KAMIKAZE(unittype.VehicleType):
|
||||
detection_range = 0
|
||||
threat_range = 50
|
||||
air_weapon_dist = 50
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
## FORTIFICATION
|
||||
|
||||
@@ -187,7 +187,7 @@ class TRM_2000(unittype.VehicleType):
|
||||
detection_range = 3500
|
||||
threat_range = 0
|
||||
air_weapon_dist = 0
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
class TRM_2000_Fuel(unittype.VehicleType):
|
||||
id = "TRM2000_Citerne"
|
||||
@@ -195,7 +195,7 @@ class TRM_2000_Fuel(unittype.VehicleType):
|
||||
detection_range = 3500
|
||||
threat_range = 0
|
||||
air_weapon_dist = 0
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
class VAB_MEDICAL(unittype.VehicleType):
|
||||
id = "VABH"
|
||||
@@ -203,7 +203,7 @@ class VAB_MEDICAL(unittype.VehicleType):
|
||||
detection_range = 0
|
||||
threat_range = 0
|
||||
air_weapon_dist = 0
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
class VAB(unittype.VehicleType):
|
||||
id = "VAB_RADIO"
|
||||
@@ -211,7 +211,7 @@ class VAB(unittype.VehicleType):
|
||||
detection_range = 0
|
||||
threat_range = 0
|
||||
air_weapon_dist = 0
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
class VBL(unittype.VehicleType):
|
||||
id = "VBL-Radio"
|
||||
@@ -219,7 +219,7 @@ class VBL(unittype.VehicleType):
|
||||
detection_range = 0
|
||||
threat_range = 0
|
||||
air_weapon_dist = 0
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
class Tracma_TD_1500(unittype.VehicleType):
|
||||
id = "Tracma"
|
||||
@@ -236,7 +236,7 @@ class SMOKE_SAM_IR(unittype.VehicleType):
|
||||
detection_range = 20000
|
||||
threat_range = 20000
|
||||
air_weapon_dist = 20000
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
class _53T2(unittype.VehicleType):
|
||||
id = "AA20"
|
||||
@@ -251,7 +251,7 @@ class TRM_2000_53T2(unittype.VehicleType):
|
||||
detection_range = 6000
|
||||
threat_range = 2000
|
||||
air_weapon_dist = 2000
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
class TRM_2000_PAMELA(unittype.VehicleType):
|
||||
id = "TRMMISTRAL"
|
||||
@@ -259,7 +259,7 @@ class TRM_2000_PAMELA(unittype.VehicleType):
|
||||
detection_range = 8000
|
||||
threat_range = 10000
|
||||
air_weapon_dist = 10000
|
||||
eprls = True
|
||||
eplrs = True
|
||||
|
||||
## INFANTRY
|
||||
|
||||
@@ -285,4 +285,4 @@ class VAB_MORTIER(unittype.VehicleType):
|
||||
detection_range = 0
|
||||
threat_range = 15000
|
||||
air_weapon_dist = 15000
|
||||
eprls = True
|
||||
eplrs = True
|
||||
@@ -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.1"
|
||||
VERSION_STRING = "2.1.5"
|
||||
|
||||
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():
|
||||
|
||||
@@ -221,7 +221,7 @@ class QLiberationWindow(QMainWindow):
|
||||
"<h4>Authors</h4>" + \
|
||||
"<p>DCS Liberation was originally developed by <b>shdwp</b>, DCS Liberation 2.0 is a partial rewrite based on this work by <b>Khopa</b>." \
|
||||
"<h4>Contributors</h4>" + \
|
||||
"shdwp, Khopa, ColonelPanic, Wrycu, calvinmorrow, JohanAberg, Deus, root0fall, Captain Cody, steveveepee, pedromagueija, parithon, bwRavencl" + \
|
||||
"shdwp, Khopa, ColonelPanic, Wrycu, calvinmorrow, JohanAberg, Deus, root0fall, Captain Cody, steveveepee, pedromagueija, parithon, bwRavencl, davidp57" + \
|
||||
"<h4>Special Thanks :</h4>" \
|
||||
"<b>rp-</b> <i>for the pydcs framework</i><br/>"\
|
||||
"<b>Grimes (mrSkortch)</b> & <b>Speed</b> <i>for the MIST framework</i><br/>"\
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import logging
|
||||
|
||||
from PySide2.QtWidgets import QLabel, QPushButton, \
|
||||
QSizePolicy, QSpacerItem, QGroupBox, QHBoxLayout
|
||||
from dcs.unittype import UnitType
|
||||
@@ -11,11 +13,14 @@ class QRecruitBehaviour:
|
||||
deliveryEvent = None
|
||||
existing_units_labels = None
|
||||
bought_amount_labels = None
|
||||
maximum_units = -1
|
||||
recruitable_types = []
|
||||
BUDGET_FORMAT = "Available Budget: <b>${}M</b>"
|
||||
|
||||
def __init__(self):
|
||||
self.bought_amount_labels = {}
|
||||
self.existing_units_labels = {}
|
||||
self.recruitable_types = []
|
||||
self.update_available_budget()
|
||||
|
||||
def add_purchase_row(self, unit_type, layout, row):
|
||||
@@ -66,7 +71,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)
|
||||
@@ -102,10 +106,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.game.budget >= price:
|
||||
self.deliveryEvent.deliver({unit_type: 1})
|
||||
self.game.budget -= price
|
||||
else:
|
||||
# TODO : display modal warning
|
||||
logging.info("Not enough money !")
|
||||
self._update_count_label(unit_type)
|
||||
self.update_available_budget()
|
||||
|
||||
@@ -123,3 +136,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
|
||||
@@ -1,28 +1,35 @@
|
||||
from PySide2.QtCore import Qt
|
||||
from PySide2.QtWidgets import QVBoxLayout, QGridLayout, QGroupBox, QScrollArea, QFrame, QWidget
|
||||
from PySide2.QtWidgets import QVBoxLayout, QGridLayout, QGroupBox, QScrollArea, QFrame, QWidget, QHBoxLayout, QLabel
|
||||
|
||||
from game.event import UnitsDeliveryEvent
|
||||
from qt_ui.uiconstants import ICONS
|
||||
from qt_ui.windows.basemenu.QRecruitBehaviour import QRecruitBehaviour
|
||||
from theater import ControlPoint, CAP, CAS, db
|
||||
from theater import ControlPoint, CAP, CAS, db, ControlPointType
|
||||
from game import Game
|
||||
|
||||
|
||||
class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour):
|
||||
|
||||
def __init__(self, cp:ControlPoint, game:Game):
|
||||
def __init__(self, cp: ControlPoint, game: Game):
|
||||
QFrame.__init__(self)
|
||||
self.cp = cp
|
||||
self.game = game
|
||||
|
||||
self.bought_amount_labels = {}
|
||||
self.existing_units_labels = {}
|
||||
|
||||
for event in self.game.events:
|
||||
if event.__class__ == UnitsDeliveryEvent and event.from_cp == self.cp:
|
||||
self.deliveryEvent = event
|
||||
if not self.deliveryEvent:
|
||||
self.deliveryEvent = self.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):
|
||||
@@ -57,5 +64,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("<strong>{}/{}</strong>".format(current_amount, max_amount))
|
||||
|
||||
@@ -83,7 +83,7 @@ class NewGameWizard(QtWidgets.QWizard):
|
||||
print("Enemy name : " + enemy_name)
|
||||
print("Player name : " + player_name)
|
||||
print("Midgame : " + str(midgame))
|
||||
start_generator.generate_inital_units(conflictTheater, enemy_name, True, multiplier)
|
||||
start_generator.generate_initial_units(conflictTheater, enemy_name, True, multiplier)
|
||||
|
||||
print("-- Initial units generated")
|
||||
game = Game(player_name=player_name,
|
||||
|
||||
219
resources/customized_payloads/A-10C_2.lua
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -968,7 +968,7 @@ function OBJDEF:new(args)
|
||||
return setmetatable(new, OBJDEF)
|
||||
end
|
||||
|
||||
return OBJDEF:new()
|
||||
json = OBJDEF:new()
|
||||
|
||||
--
|
||||
-- Version history:
|
||||
|
||||
29
resources/scripts/plugins/__plugins.lst.sample
Normal file
@@ -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
|
||||
BIN
resources/ui/misc/hangar.png
Normal file
|
After Width: | Height: | Size: 661 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/2S6 Tunguska_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/Bofors 40 mm_24.jpg
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
resources/ui/units/vehicles/Gepard_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/HAWK ANMPG-46 TR_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/HAWK ANMPQ-50 SR_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/HAWK M192 LN_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/HQ-7 Mobile Launcher_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/HQ-7 Mobile Radar_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/M-163 Vulcan_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/M1097 Avenger_24.jpg
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
resources/ui/units/vehicles/M1A2_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/M2A2_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/M48 Chaparral_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/M6 Linebacker_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/M818_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/Marder_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/Osa 9A33 ln_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/Roland ADS_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/Roland Radar_24.jpg
Normal file
|
After Width: | Height: | Size: 1014 B |
BIN
resources/ui/units/vehicles/T-80UD_24.jpg
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
resources/ui/units/vehicles/ZU-23 Ural_24.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/units/vehicles/ZU-23_24.jpg
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
@@ -102,14 +102,23 @@ class ControlPoint:
|
||||
|
||||
@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
|
||||
@@ -122,6 +131,20 @@ class ControlPoint:
|
||||
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
|
||||
|
||||
@@ -27,7 +27,7 @@ COUNT_BY_TASK = {
|
||||
}
|
||||
|
||||
|
||||
def generate_inital_units(theater: ConflictTheater, enemy_country: str, sams: bool, multiplier: float):
|
||||
def generate_initial_units(theater: ConflictTheater, enemy_country: str, sams: bool, multiplier: float):
|
||||
for cp in theater.enemy_points():
|
||||
if cp.captured:
|
||||
continue
|
||||
|
||||