Compare commits

...

34 Commits

Author SHA1 Message Date
Khopa
c5d055c19b Prepared version RC9 2020-06-24 23:54:23 +02:00
Khopa
8f7b51a3df Fixed an issue with MolniyaGroup generator causing an error on campaign start. 2020-06-24 23:51:23 +02:00
Khopa
44e8cc810f Fixed an issue with MolniyaGroup generator causing an error on campaign start. 2020-06-24 23:49:27 +02:00
Khopa
8d5d703cbe Merge branches 'develop' and 'master' of https://github.com/khopa/dcs_liberation into develop 2020-06-24 23:22:34 +02:00
Khopa
1c1936d8f8 Fix : Carrier TACAN was wrongfully set up as an A/A TACAN 2020-06-24 23:22:07 +02:00
C. Perreau
4bdf11eb79 Merge pull request #9 from DeusEx010101/New-Icons
changed AA icon
2020-06-24 22:29:56 +02:00
Donnie
81f4c7303f changed AA icon 2020-06-24 15:39:08 -04:00
C. Perreau
0641907ea0 Merge pull request #8 from DeusEx010101/New-Icons
Icons updated to be more consistent
2020-06-24 21:31:32 +02:00
Donnie
ff0f32fcf5 Icons updated to be more consistent 2020-06-24 14:52:06 -04:00
Khopa
768049ca36 Changelog update 2020-06-23 12:57:46 +02:00
Khopa
88c8fe7cca Fixed zoom on map. 2020-06-23 12:56:01 +02:00
Khopa
5e7facfeb6 Fixed issue with WW2 LST group (boats superposed) 2020-06-23 12:06:19 +02:00
Khopa
6e43467ef6 Fix : Reduced maximum number of uboat 2020-06-23 12:05:15 +02:00
Khopa
cd4ef8ae32 Fix frequency for P-47 2020-06-23 11:46:57 +02:00
Khopa
93a4463f22 Fixed logging issues and SEAD flights departing without waiting. 2020-06-22 23:20:14 +02:00
Khopa
56cf6bdaa4 Mission planning polishing. 2020-06-22 21:31:25 +02:00
Khopa
fe02df27a2 Do not allow selection of non existing carrier in mission planner 2020-06-22 12:44:49 +02:00
Khopa
a60ab68287 Aircraft Carrier will try to sail in the wind when possible. 2020-06-21 18:15:58 +02:00
Khopa
83e46ddc97 SEAD AI will only target the designated target group. 2020-06-21 17:50:06 +02:00
Khopa
9c89ad7c72 Reduced maximum wind in generated missions. 2020-06-21 15:05:57 +02:00
Khopa
0518abdedc Balance for WW2 aircraft. WW2 B_17 bombing mode is now much more effective. 2020-06-20 15:28:42 +02:00
Khopa
93bcd07c7f Added aircraft icons + fixed ww2 empty aircraft loadout not being loaded. 2020-06-20 13:44:11 +02:00
Khopa
5f1f4f8d81 Culling optimizations + fixed some aircraft icons 2020-06-19 14:31:25 +02:00
Khopa
e78120d9c6 Fix : Egress waypoint for SEAD mission is wrongfully named INGRESS 2020-06-17 23:15:44 +02:00
Khopa
cc5986d435 Updated readme 2020-06-17 13:48:33 +02:00
Khopa
5192306b06 Generate fleet and missiles sites 2020-06-17 13:04:55 +02:00
Khopa
826935eb7d Ship icons for ship groups 2020-06-14 17:31:45 +02:00
Khopa
cd41bcf45c Generate WW2 Ship groups, added B17 to allies. Implemented modifiable doctrine to setup flight generator. 2020-06-13 18:53:43 +02:00
Khopa
601375d06f Channel map support. 2020-06-12 19:10:58 +02:00
Khopa
c708abafc8 Started refactoring on ground objects. WW2 factions will have WW2 style buildings. Added ground objects templates for : "village", "ww2bunker", "allycamp" 2020-06-11 21:50:09 +02:00
Khopa
90d588353c Icon and background image for channel map. Updated Normandy map background. 2020-06-11 21:48:16 +02:00
Khopa
2d4287df2a Polished mission generator, fixed sam bug on map view. 2020-06-10 22:34:31 +02:00
Khopa
fde3a988b7 Added missions generators in flight planner. + refactoring 2020-06-09 02:13:46 +02:00
Khopa
397164e667 Added multiple settings (external views, map views) and added marker generators. 2020-06-07 19:55:38 +02:00
297 changed files with 3155 additions and 1156 deletions

8
.gitignore vendored
View File

@@ -11,3 +11,11 @@ resources/tools/a.miz
tests/**
# User-specific stuff
.idea/
liberation_preferences.json
logs/liberation.log
qt_ui/logs/liberation.log
*.psd

View File

@@ -10,3 +10,16 @@ and [Mist](https://github.com/mrSkortch/MissionScriptingTools) for mission scrip
* [Getting Started](https://github.com/Khopa/dcs_liberation/wiki/Getting-started)
* [Tutorials](https://github.com/Khopa/dcs_liberation/wiki/Tutorial-01-:-UI)
## Development Guide (WIP)
Develop is the main development branch which is updated regularly.
Master branch will be updated less regularly and on release on new version.
Other branch might be used for feature development.
**Note :**
If you have errors with pydcs object not being defined, please check that you have the latest version installed. Sometimes the dev branch will use an even more recent version of pydcs that has not been published yet, so you might want to download pydcs directly from the pydcs repository, and copy it in your Python (or virtual env) ./Libs/site-package directory.

View File

@@ -1,64 +0,0 @@
#!/usr/bin/env python3
import logging
import os
import re
import sys
import dcs
import ui.corruptedsavemenu
import ui.mainmenu
import ui.newgamemenu
import ui.window
from game.game import Game
from userdata import persistency, logging as logging_module
assert len(sys.argv) >= 3, "__init__.py should be started with two mandatory arguments: %UserProfile% location and application version"
persistency.setup(sys.argv[1])
dcs.planes.FlyingType.payload_dirs = [os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources\\payloads")]
VERSION_STRING = sys.argv[2]
logging_module.setup_version_string(VERSION_STRING)
logging.info("Using {} as userdata folder".format(persistency.base_path()))
def proceed_to_main_menu(game: Game):
m = ui.mainmenu.MainMenu(w, None, game)
m.display()
def is_version_compatible(save_version):
current_version_components = re.split(r"[\._]", VERSION_STRING)
save_version_components = re.split(r"[\._]", save_version)
if "--ignore-save" in sys.argv:
return False
if current_version_components == save_version_components:
return True
if save_version in ["1.4_rc1", "1.4_rc2", "1.4_rc3", "1.4_rc4", "1.4_rc5", "1.4_rc6"]:
return False
if current_version_components[:2] == save_version_components[:2]:
return True
return False
w = ui.window.Window()
try:
game = persistency.restore_game()
if not game or not is_version_compatible(game.settings.version):
ui.newgamemenu.NewGameMenu(w, w.start_new_game).display()
else:
game.settings.version = VERSION_STRING
proceed_to_main_menu(game)
except Exception as e:
logging.exception(e)
ui.corruptedsavemenu.CorruptedSaveMenu(w).display()
w.run()

View File

@@ -1,39 +1,90 @@
#2.0 RC 7
# 2.0 RC 9
## Features/Improvements :
* **[UI/UX]** New icons from contributor Deus
## Fixed issues :
* **[Mission Generator]** Carrier TACAN was wrongfully set up as an A/A TACAN
* **[Campaign Generator]** Fixed issue with Russian navy group generator causing a random crash on campaign creation.
# 2.0 RC 8
## Fixed issues :
* **[Mission Generator]** Frequency for P-47D-30 changed to 124Mhz (Generated mission with 251Mhz would not work)
* **[Mission Generator]** Reduced the maximum number of uboat per generated group
* **[Mission Generator]** Fixed an issue with the WW2 LST groups (superposed units).
* **[UI]** Fixed issue with the zoom
# 2.0 RC 7
## Features/Improvements :
##Features/Improvements :
* **[Units/Factions]** Added P-47D-30 for factions allies_1944
* **[Units/Factions]** Replaced S3-B Tanker by KC130 for most factions
* **[Units/Factions]** Added factions : Bluefor Coldwar, Germany 1944 Easy
* **[Campaign/Map]** Added a campaign in the Channel map
* **[Campaign/Map]** Changed the Normandy campaign map
* **[Campaign/Map]** Added new campaign Normandy Small
* **[Mission Generator]** AI Flight generator has been reworked
* **[Mission Generator]** Add PP points for JF-17 on STRIKE missions
* **[Mission Generator]** Add ST point for F-14B on STRIKE missions
* **[Mission Generator]** Flights with client slots will never be delayed
* **[Mission Generator]** AI units can start from parking (Added a new setting)
* **[Mission Generator]** AI units can start from parking (With a new setting in Settings Window to disable it)
* **[Mission Generator]** Tacan for carrier will only be in Mode X from now
* **[Mission Generator]** RTB waypoints for autogenerated flights
* **[Flight Planner]** Added CAS mission generator
* **[Flight Planner]** Added CAP mission generator
* **[Flight Planner]** Added SEAD mission generator
* **[Flight Planner]** Added STRIKE mission generator
* **[Flight Planner]** Added buttons to add autogenerated waypoints (ASCEND, DESCEND, RTB)
* **[Flight Planner]** Improved waypoint list
* **[Flight Planner]** WW2 factions uses different parameters for flight planning.
* **[Settings]** Added settings to disallow external views
* **[Settings]** Added settings to choose F10 Map mode (All, Allies only, Player only, Fog of War, Map Only)
* **[Settings]** Added settings to choose whether to auto-generate objective marks on the F10 map
* **[Info Panel]** Added information about destroyed buildings in info panel
* **[Info Panel]** Added information about destroyed units at SAM site in info panel
* **[Info Panel]** Added information about units destroyed outside the frontline in the debriefing window
* **[Info Panel]** Added information about buildings destroyed in the debriefing window
* **[Debriefing]** Added information about units destroyed outside the frontline in the debriefing window
* **[Debriefing]** Added destroyed buildings in the debriefing window
* **[Map]** Tooltip now contains the list of building for Strike targets on the map
* **[Map]** Added "Oil derrick" building
* **[Misc]** Made it possible to setup DCS Saved Games directory and DCS installation directory manually
* **[Map]** Added "ww2 bunker" building (WW2)
* **[Map]** Added "ally camp" building (WW2)
* **[Map]** Added "V1 Site" (WW2)
* **[Misc]** Made it possible to setup DCS Saved Games directory and DCS installation directory manually at first start
* **[Misc]** Added culling performance settings
## Fixed issues :
* **[Units/Factions]** Replaced S3-B Tanker by KC130 for most factions (More fuel)
* **[Units/Factions]** WW2 factions will not have offshore oil station and other modern buildings generated. No more third-reich operated offshore stations will spawn on normandy's coast.
* **[Units/Factions]** Aircraft carrier will try to move in the wind direction
* **[Units/Factions]** Missing icons added for some aircraft
##Fixed issues :
* **[Mission Generator]** When playing as RED the activation trigger would not be properly generated
* **[Mission Generator]** Changed "strike" payload for Su-24M that was innefective
* **[Mission Generator]** FW-190A8 is now properly considered as a flyable aircraft
* **[Mission Generator]** Changed "strike" payload for Su-24M that was ineffective
* **[Mission Generator]** Changed "strike" payload for JF-17 to use LS-6 bombs instead of GBU
* **[Mission Generator]** FW-190A8 is now properly considered as a flyable
* **[Maps/Campaign]** Now using Vasiani airport instead of Soganlung in North Caucasus campaign (More parking slots)
* **[Info Panel]** Message displayed on base capture event stated that the ennemy captured an airbase, while it was the player who captured it.
* **[Map]** Graphical glitch on map when one building of an objective was destroyed, but not the others
* **[Map]** Change power station template. (Buildings could end up superposed).
* **[Mission Generator]** Change power station template. (Buildings could end up superposed).
#2.0 RC 6
* **[Maps/Campaign]** Now using Vasiani airbase instead of Soganlung airport in Caucasus campaigns (more parking slot)
* **[Info Panel]** Message displayed on base capture event stated that the enemy captured an airbase, while it was the player who captured it.
* **[Map View]** Graphical glitch on map when one building of an objective was destroyed, but not the others
* **[Mission Planner]** The list of flights was not updated on departure time change.
# 2.0 RC 6
Saves file from RC5 are not compatible with the new version.
Sorry :(
##Features/Improvements :
## Features/Improvements :
* **[Units/Factions]** Supercarrier support (You have to go to settings to enable it, if you have the supercarrier module)
* **[Units/Factions]** Added 'Modern Bluefor' factions, containing all most popular DCS flyable units
* **[Units/Factions]** Factions US 2005 / 1990 will now sometimes have Arleigh Burke class ships instead of Perry as carrier escorts
@@ -47,11 +98,11 @@ Sorry :(
* **[Mission Generator]** Using Late Activation & Trigger in attempt to improve performance & reduce stutter (Previously they were spawned through 'ETA' feature)
* **[UX]** : Improved flight selection behaviour in the Mission Planning Window
##Fixed issues :
## Fixed issues :
* **[Mission Generator]** Payloads were not correctly assigned in the release version.
* **[Mission Generator]** Game generation does not work when "no night mission" settings was selected and the current time was "day"
* **[Mission Generator]** Game generation does not work when the player selected faction has no AWACS
* **[Mission Generator]** Planned flights will spawn even if their home base has been captured or is being contested by enemy ground units.
* **[Campaign Generator]** Base defenses would not be generated on Normandy map and in some rare cases on others maps as well
* **[Mission Planning]** CAS waypoints created from the "Predefined waypoint selector" would not be at the exact location of the frontline
* **[Naming]** CAP mission flown from airbase are not named BARCAP anymore (CAP from carrier is still named BARCAP)
* **[Naming]** CAP mission flown from airbase are not named BARCAP anymore (CAP from carrier is still named BARCAP)

21
game/data/aaa_db.py Normal file
View File

@@ -0,0 +1,21 @@
from dcs.vehicles import AirDefence
AAA_UNITS = [
AirDefence.SPAAA_Gepard,
AirDefence.SPAAA_ZSU_23_4_Shilka,
AirDefence.AAA_Vulcan_M163,
AirDefence.AAA_ZU_23_Closed,
AirDefence.AAA_ZU_23_Emplacement,
AirDefence.AAA_ZU_23_on_Ural_375,
AirDefence.AAA_ZU_23_Insurgent_Closed,
AirDefence.AAA_ZU_23_Insurgent_on_Ural_375,
AirDefence.AAA_ZU_23_Insurgent,
AirDefence.AAA_8_8cm_Flak_18,
AirDefence.AAA_Flak_38,
AirDefence.AAA_8_8cm_Flak_36,
AirDefence.AAA_8_8cm_Flak_37,
AirDefence.AAA_Flak_Vierling_38,
AirDefence.AAA_Kdo_G_40,
AirDefence.AAA_8_8cm_Flak_41,
AirDefence.AAA_Bofors_40mm
]

View File

@@ -0,0 +1,16 @@
import inspect
import dcs
DEFAULT_AVAILABLE_BUILDINGS = ['fuel', 'ammo', 'comms', 'oil', 'ware', 'farp', 'fob', 'power', 'factory', 'derrick', 'aa']
WW2_GERMANY_BUILDINGS = ['fuel', 'factory', 'ww2bunker', 'ww2bunker', 'ww2bunker', 'allycamp', 'allycamp', 'aa']
WW2_ALLIES_BUILDINGS = ['fuel', 'factory', 'allycamp', 'allycamp', 'allycamp', 'allycamp', 'allycamp', 'aa']
FORTIFICATION_BUILDINGS = ['Siegfried Line', 'Concertina Wire', 'Czech hedgehogs 1', 'Czech hedgehogs 2',
'Dragonteeth 1', 'Dragonteeth 2', 'Dragonteeth 3', 'Dragonteeth 4', 'Dragonteeth 5',
'Haystack 1', 'Haystack 2', 'Haystack 3', 'Haystack 4', 'Hemmkurvenvenhindernis',
'Log posts 1', 'Log posts 2', 'Log posts 3', 'Log ramps 1', 'Log ramps 2', 'Log ramps 3',
'Belgian Gate', 'Container white']
FORTIFICATION_UNITS = [c for c in vars(dcs.vehicles.Fortification).values() if inspect.isclass(c)]
FORTIFICATION_UNITS_ID = [c.id for c in vars(dcs.vehicles.Fortification).values() if inspect.isclass(c)]

95
game/data/doctrine.py Normal file
View File

@@ -0,0 +1,95 @@
from game.utils import nm_to_meter, feet_to_meter
MODERN_DOCTRINE = {
"GENERATORS": {
"CAS": True,
"CAP": True,
"SEAD": True,
"STRIKE": True,
"ANTISHIP": True,
},
"STRIKE_MAX_RANGE": 1500000,
"SEAD_MAX_RANGE": 1500000,
"CAP_EVERY_X_MINUTES": 20,
"CAS_EVERY_X_MINUTES": 30,
"SEAD_EVERY_X_MINUTES": 40,
"STRIKE_EVERY_X_MINUTES": 40,
"INGRESS_EGRESS_DISTANCE": nm_to_meter(45),
"INGRESS_ALT": feet_to_meter(20000),
"EGRESS_ALT": feet_to_meter(20000),
"PATROL_ALT_RANGE": (feet_to_meter(15000), feet_to_meter(33000)),
"PATTERN_ALTITUDE": feet_to_meter(5000),
"CAP_PATTERN_LENGTH": (nm_to_meter(15), nm_to_meter(40)),
"FRONTLINE_CAP_DISTANCE_FROM_FRONTLINE": (nm_to_meter(6), nm_to_meter(15)),
"CAP_DISTANCE_FROM_CP": (nm_to_meter(10), nm_to_meter(40)),
"MAX_NUMBER_OF_INTERCEPTION_GROUP": 3,
}
COLDWAR_DOCTRINE = {
"GENERATORS": {
"CAS": True,
"CAP": True,
"SEAD": True,
"STRIKE": True,
"ANTISHIP": True,
},
"STRIKE_MAX_RANGE": 1500000,
"SEAD_MAX_RANGE": 1500000,
"CAP_EVERY_X_MINUTES": 20,
"CAS_EVERY_X_MINUTES": 30,
"SEAD_EVERY_X_MINUTES": 40,
"STRIKE_EVERY_X_MINUTES": 40,
"INGRESS_EGRESS_DISTANCE": nm_to_meter(30),
"INGRESS_ALT": feet_to_meter(18000),
"EGRESS_ALT": feet_to_meter(18000),
"PATROL_ALT_RANGE": (feet_to_meter(10000), feet_to_meter(24000)),
"PATTERN_ALTITUDE": feet_to_meter(5000),
"CAP_PATTERN_LENGTH": (nm_to_meter(12), nm_to_meter(24)),
"FRONTLINE_CAP_DISTANCE_FROM_FRONTLINE": (nm_to_meter(2), nm_to_meter(8)),
"CAP_DISTANCE_FROM_CP": (nm_to_meter(8), nm_to_meter(25)),
"MAX_NUMBER_OF_INTERCEPTION_GROUP": 3,
}
WWII_DOCTRINE = {
"GENERATORS": {
"CAS": True,
"CAP": True,
"SEAD": False,
"STRIKE": True,
"ANTISHIP": True,
},
"STRIKE_MAX_RANGE": 1500000,
"SEAD_MAX_RANGE": 1500000,
"CAP_EVERY_X_MINUTES": 20,
"CAS_EVERY_X_MINUTES": 30,
"SEAD_EVERY_X_MINUTES": 40,
"STRIKE_EVERY_X_MINUTES": 40,
"INGRESS_EGRESS_DISTANCE": nm_to_meter(7),
"INGRESS_ALT": feet_to_meter(8000),
"EGRESS_ALT": feet_to_meter(8000),
"PATROL_ALT_RANGE": (feet_to_meter(4000), feet_to_meter(15000)),
"PATTERN_ALTITUDE": feet_to_meter(5000),
"CAP_PATTERN_LENGTH": (nm_to_meter(8), nm_to_meter(18)),
"FRONTLINE_CAP_DISTANCE_FROM_FRONTLINE": (nm_to_meter(1), nm_to_meter(6)),
"CAP_DISTANCE_FROM_CP": (nm_to_meter(0), nm_to_meter(5)),
"MAX_NUMBER_OF_INTERCEPTION_GROUP": 3,
}

54
game/data/radar_db.py Normal file
View File

@@ -0,0 +1,54 @@
from dcs.vehicles import AirDefence
from dcs.ships import *
UNITS_WITH_RADAR = [
# Radars
AirDefence.SAM_SA_15_Tor_9A331,
AirDefence.SAM_SA_11_Buk_CC_9S470M1,
AirDefence.SAM_Patriot_AMG_AN_MRC_137,
AirDefence.SAM_Patriot_ECS_AN_MSQ_104,
AirDefence.SPAAA_Gepard,
AirDefence.AAA_Vulcan_M163,
AirDefence.SPAAA_ZSU_23_4_Shilka,
AirDefence.EWR_1L13,
AirDefence.SAM_SA_6_Kub_STR_9S91,
AirDefence.SAM_SA_10_S_300PS_TR_30N6,
AirDefence.SAM_SA_10_S_300PS_SR_5N66M,
AirDefence.EWR_55G6,
AirDefence.SAM_SA_10_S_300PS_SR_64H6E,
AirDefence.SAM_SA_11_Buk_SR_9S18M1,
AirDefence.CP_9S80M1_Sborka,
AirDefence.SAM_Hawk_TR_AN_MPQ_46,
AirDefence.SAM_Hawk_SR_AN_MPQ_50,
AirDefence.SAM_Patriot_STR_AN_MPQ_53,
AirDefence.SAM_Hawk_CWAR_AN_MPQ_55,
AirDefence.SAM_SR_P_19,
AirDefence.SAM_Roland_EWR,
AirDefence.SAM_SA_3_S_125_TR_SNR,
AirDefence.SAM_SA_2_TR_SNR_75_Fan_Song,
AirDefence.HQ_7_Self_Propelled_STR,
# Ships
CVN_70_Carl_Vinson,
Oliver_Hazzard_Perry_class,
Ticonderoga_class,
FFL_1124_4_Grisha,
CV_1143_5_Admiral_Kuznetsov,
FSG_1241_1MP_Molniya,
CG_1164_Moskva,
FFG_11540_Neustrashimy,
CGN_1144_2_Pyotr_Velikiy,
FF_1135M_Rezky,
CV_1143_5_Admiral_Kuznetsov_2017,
CVN_74_John_C__Stennis,
CVN_71_Theodore_Roosevelt,
CVN_72_Abraham_Lincoln,
CVN_73_George_Washington,
USS_Arleigh_Burke_IIa,
LHA_1_Tarawa,
Type_052B_Destroyer,
Type_054A_Frigate,
Type_052C_Destroyer,
Type_093,
]

View File

@@ -13,9 +13,11 @@ from dcs.unit import *
from dcs.unittype import *
from dcs.unitgroup import *
from game.factions.bluefor_coldwar import BLUEFOR_COLDWAR
from game.factions.china_2000 import China_2000
from game.factions.france_1995 import France_1995
from game.factions.france_2005 import France_2005
from game.factions.germany_1944_easy import Germany_1944_Easy
from game.factions.germany_1990 import Germany_1990
from game.factions.insurgent import Insurgent
from game.factions.iran_2015 import Iran_2015
@@ -85,13 +87,13 @@ PRICES = {
JF_17: 20,
Su_30: 24,
SpitfireLFMkIX: 8,
SpitfireLFMkIXCW: 8,
Bf_109K_4: 8,
FW_190D9: 8,
FW_190A8: 8,
A_20G: 12,
Ju_88A4: 12,
SpitfireLFMkIX: 14,
SpitfireLFMkIXCW: 14,
Bf_109K_4: 14,
FW_190D9: 16,
FW_190A8: 14,
A_20G: 22,
Ju_88A4: 24,
F_5E_3: 8,
MiG_15bis: 4,
@@ -139,7 +141,7 @@ PRICES = {
OH_58D: 6,
# Bombers
B_52H: 25,
B_52H: 35,
B_1B: 50,
# special
@@ -157,9 +159,10 @@ PRICES = {
C_130: 8,
# WW2
P_51D_30_NA: 6,
P_51D: 6,
P_47D_30: 6,
P_51D_30_NA: 18,
P_51D: 16,
P_47D_30: 18,
B_17G: 30,
# armor
Armor.APC_MTLB: 4,
@@ -233,31 +236,30 @@ PRICES = {
AirDefence.SAM_Chaparral_M48: 10,
# WW2
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G:18,
Armor.MT_Pz_Kpfw_IV_Ausf_H:8,
Armor.HT_Pz_Kpfw_VI_Tiger_I:22,
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G:24,
Armor.MT_Pz_Kpfw_IV_Ausf_H:16,
Armor.HT_Pz_Kpfw_VI_Tiger_I:24,
Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II:26,
Armor.TD_Jagdpanther_G1: 16,
Armor.TD_Jagdpanzer_IV: 10,
Armor.TD_Jagdpanther_G1: 18,
Armor.TD_Jagdpanzer_IV: 11,
Armor.Sd_Kfz_184_Elefant: 18,
Armor.APC_Sd_Kfz_251:2,
Armor.IFV_Sd_Kfz_234_2_Puma:6,
Armor.MT_M4_Sherman:5,
Armor.MT_M4A4_Sherman_Firefly:8,
Armor.CT_Cromwell_IV:8,
Armor.APC_Sd_Kfz_251:4,
Armor.IFV_Sd_Kfz_234_2_Puma:8,
Armor.MT_M4_Sherman:12,
Armor.MT_M4A4_Sherman_Firefly:16,
Armor.CT_Cromwell_IV:12,
Armor.M30_Cargo_Carrier:2,
Armor.APC_M2A1:2,
Armor.ST_Centaur_IV: 8,
Armor.HIT_Churchill_VII: 12,
Armor.LAC_M8_Greyhound: 4,
Armor.TD_M10_GMC: 8,
Armor.StuG_III_Ausf__G: 6,
AirDefence.AAA_Bofors_40mm: 4,
AirDefence.AAA_8_8cm_Flak_36: 6,
AirDefence.AAA_8_8cm_Flak_18: 4,
Artillery.M12_GMC: 2,
Artillery.Sturmpanzer_IV_Brummbär: 2,
Armor.APC_M2A1:4,
Armor.ST_Centaur_IV: 10,
Armor.HIT_Churchill_VII: 16,
Armor.LAC_M8_Greyhound: 8,
Armor.TD_M10_GMC: 14,
Armor.StuG_III_Ausf__G: 12,
AirDefence.AAA_Bofors_40mm: 8,
AirDefence.AAA_8_8cm_Flak_36: 8,
AirDefence.AAA_8_8cm_Flak_18: 12,
Artillery.M12_GMC: 10,
Artillery.Sturmpanzer_IV_Brummbär: 10,
# ship
CV_1143_5_Admiral_Kuznetsov: 100,
@@ -348,6 +350,7 @@ UNIT_BY_TASK = {
A_20G,
P_47D_30,
Ju_88A4,
B_17G
],
Transport: [
IL_76MD,
@@ -595,7 +598,7 @@ CARRIER_TAKEOFF_BAN = [
]
"""
Units separated by country. Currently only Russia and USA are supported.
Units separated by country.
country : DCS Country name
"""
FACTIONS = {
@@ -609,14 +612,18 @@ FACTIONS = {
"China 2000": China_2000,
"North Korea 2000": NorthKorea_2000,
"Insurgent": Insurgent,
"Germany 1944 (WW2 Pack)": Germany_1944,
"Germany 1944 Easy (WW2 Pack)": Germany_1944_Easy,
"Bluefor Modern": BLUEFOR_MODERN,
"Bluefor Cold War 1970s": BLUEFOR_COLDWAR,
"USA 2005": USA_2005,
"USA 1990": USA_1990,
"USA 1965": USA_1965,
"USA 1960": USA_1960,
"USA 1955 (Require WW2 Pack)": USA_1955,
"Allies 1944 (Require WW2 Pack)": USA_1944,
"Bluefor Modern": BLUEFOR_MODERN,
"USA 1955 (WW2 Pack)": USA_1955,
"Allies 1944 (WW2 Pack)": USA_1944,
"France 2005": France_2005,
"France 1995": France_1995,
"Germany 1990": Germany_1990,
@@ -631,7 +638,7 @@ FACTIONS = {
"Israel 2000": Israel_2000,
"Turkey 2005": Turkey_2005,
"United Arab Emirates 2005": UAE_2005,
"Germany 1944 (Require WW2 Pack)": Germany_1944
}
BLUEFOR_FACTIONS = [FACTIONS[f]["country"] for f in FACTIONS if FACTIONS[f]["side"] == "blue"]
print(BLUEFOR_FACTIONS)
@@ -733,6 +740,15 @@ PLANE_PAYLOAD_OVERRIDES = {
L_39C:COMMON_OVERRIDE,
Su_17M4: COMMON_OVERRIDE,
F_4E: COMMON_OVERRIDE,
P_47D_30:COMMON_OVERRIDE,
B_17G: COMMON_OVERRIDE,
P_51D: COMMON_OVERRIDE,
P_51D_30_NA: COMMON_OVERRIDE,
FW_190D9: COMMON_OVERRIDE,
FW_190A8: COMMON_OVERRIDE,
Bf_109K_4: COMMON_OVERRIDE,
SpitfireLFMkIXCW: COMMON_OVERRIDE,
SpitfireLFMkIX: COMMON_OVERRIDE,
AH_64D:{
CAS: "AGM-114K*16"
@@ -1047,4 +1063,5 @@ class DefaultLiveries:
OH_58D.Liveries = DefaultLiveries
F_16C_50.Liveries = DefaultLiveries
P_51D_30_NA.Liveries = DefaultLiveries
Ju_88A4.Liveries = DefaultLiveries
Ju_88A4.Liveries = DefaultLiveries
B_17G.Liveries = DefaultLiveries

View File

@@ -274,6 +274,8 @@ 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]
if ally_units_alive == 0:
player_won = False
delta = STRONG_DEFEAT_INFLUENCE
@@ -296,11 +298,21 @@ class Event:
else:
delta = DEFEAT_INFLUENCE
elif ally_casualties > enemy_casualties:
player_won = False
if cp.stances[enemy_cp.id] == CombatStance.BREAKTHROUGH:
if ally_units_alive > 2*enemy_units_alive and player_aggresive:
# Even with casualties if the enemy is overwhelmed, they are going to lose ground
player_won = True
delta = MINOR_DEFEAT_INFLUENCE
elif ally_units_alive > 3*enemy_units_alive and player_aggresive:
player_won = True
delta = STRONG_DEFEAT_INFLUENCE
else:
delta = STRONG_DEFEAT_INFLUENCE
# But is the enemy is not outnumbered, we lose
player_won = False
if cp.stances[enemy_cp.id] == CombatStance.BREAKTHROUGH:
delta = STRONG_DEFEAT_INFLUENCE
else:
delta = STRONG_DEFEAT_INFLUENCE
# No progress with defensive strategies
if player_won and cp.stances[enemy_cp.id] in [CombatStance.DEFENSIVE, CombatStance.AMBUSH]:
@@ -320,7 +332,7 @@ class Event:
enemy_cp.base.affect_strength(delta)
cp.base.affect_strength(-delta)
info = Information("Frontline Report",
"Our ground forces from " + cp.name + " are losing ground against the enemy forces from" + enemy_cp.name,
"Our ground forces from " + cp.name + " are losing ground against the enemy forces from " + enemy_cp.name,
self.game.turn)
self.game.informations.append(info)

View File

@@ -0,0 +1,63 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
BLUEFOR_COLDWAR = {
"country": "USA",
"side": "blue",
"units": [
F_14B,
F_4E,
F_5E_3,
A_10A,
AJS37,
KC_135,
KC130,
C_130,
E_3A,
UH_1H,
SA342M,
SA342L,
Armor.MBT_M60A3_Patton,
Armor.APC_M113,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Hawk_PCP,
AirDefence.SAM_Chaparral_M48,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad": [
AirDefence.AAA_Vulcan_M163,
], "aircraft_carrier": [
CVN_74_John_C__Stennis,
], "helicopter_carrier": [
LHA_1_Tarawa,
], "destroyer": [
Oliver_Hazzard_Perry_class,
], "cruiser": [
Ticonderoga_class,
], "carrier_names": [
"CVN-71 Theodore Roosevelt",
"CVN-72 Abraham Lincoln",
"CVN-73 George Washington",
"CVN-74 John C. Stennis",
], "lhanames": [
"LHA-1 Tarawa",
"LHA-2 Saipan",
"LHA-3 Belleau Wood",
"LHA-4 Nassau",
"LHA-5 Peleliu"
], "boat":[
"OliverHazardPerryGroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
BLUEFOR_MODERN = {
"country": "USA",
@@ -31,6 +31,8 @@ BLUEFOR_MODERN = {
UH_1H,
AH_64D,
Ka_50,
SA342M,
SA342L,
Armor.MBT_M1A2_Abrams,
Armor.MBT_Leopard_2,
@@ -74,5 +76,7 @@ BLUEFOR_MODERN = {
"LHA-3 Belleau Wood",
"LHA-4 Nassau",
"LHA-5 Peleliu"
], "boat":[
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
China_2000 = {
"country": "China",
@@ -59,5 +59,7 @@ China_2000 = {
], "carrier_names": [
"001 Liaoning",
"002 Shandong",
], "boat":[
"ChineseNavyGroupGenerator", "Type54GroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
France_1995 = {
"country": "France",
@@ -40,5 +40,7 @@ France_1995 = {
], "shorad": [
AirDefence.HQ_7_Self_Propelled_LN,
AirDefence.SAM_Roland_ADS
], "boat":[
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
France_2005 = {
"country": "France",
@@ -55,5 +55,7 @@ France_2005 = {
"L9013 Mistral",
"L9014 Tonerre",
"L9015 Dixmude"
], "boat":[
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
]
}

View File

@@ -1,6 +1,9 @@
from dcs.planes import *
from dcs.vehicles import *
from game.data.building_data import WW2_GERMANY_BUILDINGS
from game.data.doctrine import WWII_DOCTRINE
Germany_1944 = {
"country": "Third Reich",
"side": "red",
@@ -30,7 +33,13 @@ Germany_1944 = {
Infantry.Infantry_Mauser_98,
AirDefence.AAA_8_8cm_Flak_36,
],
"shorad":[
"shorad": [
AirDefence.AAA_8_8cm_Flak_36,
]
],
"objects": WW2_GERMANY_BUILDINGS,
"doctrine": WWII_DOCTRINE,
"boat": ["UBoatGroupGenerator", "SchnellbootGroupGenerator"],
"boat_count": 4,
"missiles": ["V1GroupGenerator"],
"missiles_count": 1
}

View File

@@ -0,0 +1,38 @@
from dcs.planes import *
from dcs.vehicles import *
from game.data.building_data import WW2_GERMANY_BUILDINGS
from game.data.doctrine import WWII_DOCTRINE
Germany_1944_Easy = {
"country": "Third Reich",
"side": "red",
"units": [
FW_190A8,
FW_190D9,
Bf_109K_4,
Ju_88A4,
Armor.MT_Pz_Kpfw_IV_Ausf_H,
Armor.APC_Sd_Kfz_251,
Armor.IFV_Sd_Kfz_234_2_Puma,
Artillery.Sturmpanzer_IV_Brummbär,
Unarmed.Sd_Kfz_2,
Unarmed.Sd_Kfz_7,
Unarmed.Kübelwagen_82,
Infantry.Infantry_Mauser_98,
AirDefence.AAA_8_8cm_Flak_36,
],
"shorad":[
AirDefence.AAA_8_8cm_Flak_36,
],
"objects": WW2_GERMANY_BUILDINGS,
"doctrine": WWII_DOCTRINE,
"boat": ["UBoatGroupGenerator", "SchnellbootGroupGenerator"],
"boat_count": 4,
"missiles": ["V1GroupGenerator"],
"missiles_count": 1
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Germany_1990 = {
"country": "Germany",
@@ -39,5 +39,7 @@ Germany_1990 = {
"shorad":[
AirDefence.SPAAA_Gepard,
AirDefence.SAM_Roland_ADS,
], "boat":[
"OliverHazardPerryGroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
India_2010 = {
"country": "India",
@@ -49,5 +49,7 @@ India_2010 = {
FSG_1241_1MP_Molniya,
], "carrier_names": [
"INS Vikramaditya"
], "boat":[
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator", "MolniyaGroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Iran_2015 = {
"country": "Iran",
@@ -52,5 +52,7 @@ Iran_2015 = {
"shorad":[
AirDefence.HQ_7_Self_Propelled_LN,
AirDefence.AAA_ZU_23_Insurgent_Closed
], "boat":[
"GrishaGroupGenerator", "MolniyaGroupGenerator", "KiloSubGroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Israel_2000 = {
"country": "Israel",
@@ -31,7 +31,9 @@ Israel_2000 = {
LHA_1_Tarawa,
Armed_speedboat,
],
"shorad":[
"shorad": [
AirDefence.SAM_Avenger_M1097
], "boat": [
"ArleighBurkeGroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Italy_1990 = {
"country": "Italy",
@@ -42,5 +42,7 @@ Italy_1990 = {
], "lha_names": [
"Giuseppe Garibaldi",
"Cavour",
], "boat":[
"OliverHazardPerryGroupGenerator"
]
}

View File

@@ -1,7 +1,6 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.vehicles import *
Lybia_2011 = {
"country": "Russia",
@@ -44,5 +43,7 @@ Lybia_2011 = {
"shorad":[
AirDefence.HQ_7_Self_Propelled_LN,
AirDefence.SAM_SA_8_Osa_9A33,
], "boat": [
"GrishaGroupGenerator", "MolniyaGroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Netherlands_1990 = {
"country": "The Netherlands",
@@ -30,7 +30,9 @@ Netherlands_1990 = {
LHA_1_Tarawa,
Armed_speedboat,
],
"shorad":[
"shorad": [
AirDefence.SAM_Avenger_M1097
], "boat": [
"OliverHazardPerryGroupGenerator"
]
}
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
NorthKorea_2000 = {
"country": "North Korea",
@@ -46,5 +46,8 @@ NorthKorea_2000 = {
"shorad":[
AirDefence.AAA_ZU_23_Emplacement,
AirDefence.SPAAA_ZSU_23_4_Shilka
],
"boat": [
"GrishaGroupGenerator", "MolniyaGroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Pakistan_2015 = {
"country": "Pakistan",
@@ -34,5 +34,7 @@ Pakistan_2015 = {
AirDefence.HQ_7_Self_Propelled_LN,
AirDefence.AAA_ZU_23_Insurgent_on_Ural_375,
AirDefence.AAA_ZU_23_Closed
], "boat": [
"Type54GroupGenerator", "OliverHazardPerryGroupGenerator"
]
}

View File

@@ -49,5 +49,7 @@ Russia_1965 = {
],
"shorad":[
AirDefence.AAA_ZU_23_Closed
], "boat": [
"GrishaGroupGenerator"
]
}

View File

@@ -65,5 +65,7 @@ Russia_1975 = {
FF_1135M_Rezky,
], "cruiser": [
CGN_1144_2_Pyotr_Velikiy,
], "boat": [
"RussianNavyGroupGenerator", "KiloSubGroupGenerator", "MolniyaGroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Russia_1990 = {
"country": "Russia",
@@ -64,5 +64,7 @@ Russia_1990 = {
FF_1135M_Rezky,
], "cruiser": [
FSG_1241_1MP_Molniya,
], "boat":[
"RussianNavyGroupGenerator", "KiloSubGroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Russia_2010 = {
"country": "Russia",
@@ -63,5 +63,7 @@ Russia_2010 = {
FF_1135M_Rezky,
], "cruiser": [
FSG_1241_1MP_Molniya,
], "boat": [
"RussianNavyGroupGenerator", "KiloSubGroupGenerator"
]
}

View File

@@ -1,6 +1,6 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Spain_1990 = {
"country": "Spain",
@@ -44,5 +44,7 @@ Spain_1990 = {
"Principe de Asturias",
], "lhanames": [
"Juan Carlos I",
], "boat":[
"OliverHazardPerryGroupGenerator"
]
}

View File

@@ -25,7 +25,7 @@ Sweden_1990 = {
Dry_cargo_ship_Ivanov,
Tanker_Elnya_160,
],
"shorad":[
"shorad": [
AirDefence.SAM_Avenger_M1097
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Turkey_2005 = {
"country": "Turkey",
@@ -36,5 +36,7 @@ Turkey_2005 = {
], "shorad":[
AirDefence.AAA_ZU_23_Emplacement,
AirDefence.SPAAA_ZSU_23_4_Shilka
], "boat":[
"OliverHazardPerryGroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
UAE_2005 = {
"country": "United Arab Emirates",
@@ -30,5 +30,7 @@ UAE_2005 = {
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "boat":[
"OliverHazardPerryGroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
UnitedKingdom_1990 = {
"country": "UK",
@@ -46,5 +46,7 @@ UnitedKingdom_1990 = {
"HMS Invincible",
"HMS Illustrious",
"HMS Ark Royal",
], "boat":[
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Ukraine_2010 = {
"country": "Ukraine",
@@ -47,5 +47,7 @@ Ukraine_2010 = {
AirDefence.SAM_SA_19_Tunguska_2S6,
AirDefence.SAM_SA_13_Strela_10M3_9A35M3,
AirDefence.AAA_ZU_23_on_Ural_375
], "boat":[
"GrishaGroupGenerator"
]
}

View File

@@ -2,6 +2,9 @@ from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
from game.data.building_data import WW2_ALLIES_BUILDINGS
from game.data.doctrine import WWII_DOCTRINE
USA_1944 = {
"country": "USA",
"side": "blue",
@@ -12,6 +15,7 @@ USA_1944 = {
SpitfireLFMkIX,
SpitfireLFMkIXCW,
A_20G,
B_17G,
Armor.MT_M4_Sherman,
Armor.MT_M4A4_Sherman_Firefly,
@@ -36,5 +40,9 @@ USA_1944 = {
AirDefence.AAA_Bofors_40mm,
], "shorad":[
AirDefence.AAA_Bofors_40mm,
]
],
"objects": WW2_ALLIES_BUILDINGS,
"doctrine": WWII_DOCTRINE,
"boat": ["WW2LSTGroupGenerator"],
"boat_count": 2
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
USA_1965 = {
"country": "USA",
@@ -36,5 +36,7 @@ USA_1965 = {
"shorad":[
AirDefence.AAA_Vulcan_M163,
AirDefence.SAM_Chaparral_M48
], "boat":[
"OliverHazardPerryGroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
USA_1990 = {
"country": "USA",
@@ -59,5 +59,7 @@ USA_1990 = {
"LHA-3 Belleau Wood",
"LHA-4 Nassau",
"LHA-5 Peleliu"
], "boat":[
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
]
}

View File

@@ -1,7 +1,7 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
USA_2005 = {
"country": "USA",
@@ -51,7 +51,6 @@ USA_2005 = {
], "helicopter_carrier": [
LHA_1_Tarawa,
], "destroyer": [
Oliver_Hazzard_Perry_class,
USS_Arleigh_Burke_IIa,
], "cruiser": [
Ticonderoga_class,
@@ -66,5 +65,7 @@ USA_2005 = {
"LHA-3 Belleau Wood",
"LHA-4 Nassau",
"LHA-5 Peleliu"
], "boat":[
"ArleighBurkeGroupGenerator"
]
}

View File

@@ -1,6 +1,6 @@
from datetime import datetime, timedelta
from game.db import REWARDS, PLAYER_BUDGET_BASE
from game.db import REWARDS, PLAYER_BUDGET_BASE, sys
from game.game_stats import GameStats
from gen.flights.ai_flight_planner import FlightPlanner
from gen.ground_forces.ai_ground_planner import GroundPlanner
@@ -72,6 +72,16 @@ class Game:
self.ground_planners = {}
self.informations = []
self.informations.append(Information("Game Start", "-" * 40, 0))
self.__culling_points = self.compute_conflicts_position()
@property
def player_faction(self):
return db.FACTIONS[self.player_name]
@property
def enemy_faction(self):
return db.FACTIONS[self.enemy_name]
def _roll(self, prob, mult):
if self.settings.version == "dev":
@@ -144,7 +154,7 @@ class Game:
self.events.remove(event)
def initiate_event(self, event: Event):
assert event in self.events
#assert event in self.events
logging.info("Generating {} (regular)".format(event))
event.generate()
@@ -178,6 +188,7 @@ class Game:
return 1
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint] = None):
logging.info("Pass turn")
self.informations.append(Information("End of turn #" + str(self.turn), "-" * 40, 0))
self.turn = self.turn + 1
@@ -193,7 +204,7 @@ class Game:
self._enemy_reinforcement()
self._budget_player()
if not no_action:
if not no_action and self.turn > 1:
for cp in self.theater.player_points():
cp.base.affect_strength(+PLAYER_BASE_STRENGTH_RECOVERY)
else:
@@ -212,6 +223,7 @@ class Game:
self.game_stats.update(self)
# Plan flights & combat for next turn
self.__culling_points = self.compute_conflicts_position()
self.planners = {}
self.ground_planners = {}
for cp in self.theater.controlpoints:
@@ -317,3 +329,49 @@ class Game:
"""
self.current_group_id += 1
return self.current_group_id
def compute_conflicts_position(self):
"""
Compute the current conflict center position(s), mainly used for culling calculation
:return: List of points of interests
"""
points = []
# By default, use the existing frontline conflict position
for conflict in self.theater.conflicts():
points.append(Conflict.frontline_position(self.theater, conflict[0], conflict[1])[0])
# If there is no conflict take the center point between the two nearest opposing bases
if len(points) == 0:
cpoint = None
min_distance = sys.maxsize
for cp in self.theater.player_points():
for cp2 in self.theater.enemy_points():
d = cp.position.distance_to_point(cp2.position)
if d < min_distance:
min_distance = d
cpoint = Point((cp.position.x + cp2.position.x) / 2, (cp.position.y + cp2.position.y) / 2)
if cpoint is not None:
points.append(cpoint)
# Else 0,0, since we need a default value
# (in this case this means the whole map is owned by the same player, so it is not an issue)
if len(points) == 0:
points.append(Point(0, 0))
return points
def position_culled(self, pos):
"""
Check if unit can be generated at given position depending on culling performance settings
:param pos: Position you are tryng to spawn stuff at
:return: True if units can not be added at given position
"""
if self.settings.perf_culling == False:
return False
else:
for c in self.__culling_points:
if c.distance_to_point(pos) < self.settings.perf_culling_distance * 1000:
return False
return True

View File

@@ -116,6 +116,12 @@ class Operation:
def generate(self):
# Generate meteo
if self.environment_settings is None:
self.environment_settings = self.envgen.generate()
else:
self.envgen.load(self.environment_settings)
# Generate ground object first
self.groundobjectgen.generate()
@@ -156,17 +162,7 @@ class Operation:
cp = self.conflict.from_cp
else:
cp = self.conflict.to_cp
self.triggersgen.generate(player_cp=cp,
is_quick=False,
activation_trigger_radius=self.trigger_radius,
awacs_enabled=self.is_awacs_enabled)
# env settings
if self.environment_settings is None:
self.environment_settings = self.envgen.generate()
else:
self.envgen.load(self.environment_settings)
self.triggersgen.generate()
# options
self.forcedoptionsgen.generate()

View File

@@ -9,8 +9,10 @@ class Settings:
labels = "Full"
only_player_takeoff = True # Legacy parameter do not use
night_disabled = False
external_views_allowed = True
supercarrier = False
multiplier = 1
generate_marks = True
sams = True # Legacy parameter do not use
cold_start = False # Legacy parameter do not use
version = None
@@ -23,4 +25,8 @@ class Settings:
perf_infantry = True
perf_ai_parking_start = True
# Performance culling
perf_culling = False
perf_culling_distance = 100

14
game/utils.py Normal file
View File

@@ -0,0 +1,14 @@
def meter_to_feet(value_in_meter):
return int(3.28084 * value_in_meter)
def feet_to_meter(value_in_feet):
return int(float(value_in_feet)/3.048)
def meter_to_nm(value_in_meter):
return int(float(value_in_meter)*0.000539957)
def nm_to_meter(value_in_nm):
return int(float(value_in_nm)*1852)

View File

@@ -5,38 +5,22 @@ from dcs.terrain.terrain import NoParkingSlotError
from dcs.triggers import TriggerOnce, Event
from game.settings import Settings
from gen.flights.ai_flight_planner import FlightPlanner, CAP_DEFAULT_ENGAGE_DISTANCE, nm_to_meter
from gen.flights.ai_flight_planner import FlightPlanner
from gen.flights.flight import Flight, FlightType, FlightWaypointType
from .conflictgen import *
from .naming import *
from .triggergen import TRIGGER_WAYPOINT_OFFSET
SPREAD_DISTANCE_FACTOR = 1, 2
ESCORT_ENGAGEMENT_MAX_DIST = 100000
WORKAROUND_WAYP_DIST = 1000
WARM_START_HELI_AIRSPEED = 120
WARM_START_HELI_ALT = 500
WARM_START_ALTITUDE = 3000
WARM_START_AIRSPEED = 550
INTERCEPTION_AIRSPEED = 1000
BARCAP_RACETRACK_DISTANCE = 20000
CAP_DURATION = 30 # minutes
ATTACK_CIRCLE_ALT = 1000
ATTACK_CIRCLE_DURATION = 15
CAS_ALTITUDE = 800
RTB_ALTITUDE = 800
RTB_DISTANCE = 5000
HELI_ALT = 500
TRANSPORT_LANDING_ALT = 2000
DEFENCE_ENGAGEMENT_MAX_DISTANCE = 60000
INTERCEPT_MAX_DISTANCE = 200000
class AircraftConflictGenerator:
escort_targets = [] # type: typing.List[typing.Tuple[FlyingGroup, int]]
@@ -56,11 +40,15 @@ class AircraftConflictGenerator:
did_load_loadout = False
unit_type = group.units[0].unit_type
print("SETUP GROUP : " + str(for_task) + " -- " + str(group.name))
if unit_type in db.PLANE_PAYLOAD_OVERRIDES:
override_loadout = db.PLANE_PAYLOAD_OVERRIDES[unit_type]
if type(override_loadout) == dict:
# Clear pylons
for p in group.units:
p.pylons.clear()
# Now load loadout
if for_task in db.PLANE_PAYLOAD_OVERRIDES[unit_type]:
payload_name = db.PLANE_PAYLOAD_OVERRIDES[unit_type][for_task]
group.load_loadout(payload_name)
@@ -102,7 +90,7 @@ class AircraftConflictGenerator:
if unit_type in helicopters.helicopter_map.values() and unit_type not in [UH_1H]:
group.set_frequency(127.5)
else:
if unit_type not in [P_51D_30_NA, P_51D, SpitfireLFMkIX, SpitfireLFMkIXCW, FW_190A8, FW_190D9, Bf_109K_4]:
if unit_type not in [P_51D_30_NA, P_51D, SpitfireLFMkIX, SpitfireLFMkIXCW, FW_190A8, FW_190D9, Bf_109K_4, P_47D_30]:
group.set_frequency(251.0)
else:
# WW2
@@ -246,6 +234,11 @@ class AircraftConflictGenerator:
def generate_flights(self, cp, country, flight_planner:FlightPlanner):
for flight in flight_planner.flights:
if flight.client_count == 0 and self.game.position_culled(flight.from_cp.position):
logging.info("Flight not generated : culled")
continue
group = self.generate_planned_flight(cp, country, flight)
if flight.flight_type == FlightType.INTERCEPTION:
self.setup_group_as_intercept_flight(group, flight)
@@ -258,7 +251,7 @@ class AircraftConflictGenerator:
def setup_group_activation_trigger(self, flight, group):
if flight.scheduled_in > 0 and flight.client_count == 0:
if flight.start_type != "In Flight":
if flight.start_type != "In Flight" and flight.from_cp.cptype not in [ControlPointType.AIRCRAFT_CARRIER_GROUP, ControlPointType.LHA_GROUP]:
group.late_activation = False
group.uncontrolled = True
@@ -370,9 +363,9 @@ class AircraftConflictGenerator:
group.task = SEAD.name
self._setup_group(group, SEAD, flight.client_count)
group.points[0].tasks.clear()
group.points[0].tasks.append(SEADTaskAction())
group.points[0].tasks.append(NoTask())
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFireWeaponFree))
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFire))
group.points[0].tasks.append(OptRestrictJettison(True))
elif flight_type in [FlightType.STRIKE]:
group.task = PinpointStrike.name
@@ -393,8 +386,8 @@ class AircraftConflictGenerator:
if not point.only_for_player or (point.only_for_player and flight.client_count > 0):
pt = group.add_waypoint(Point(point.x, point.y), point.alt)
if point.waypoint_type == FlightWaypointType.PATROL_TRACK:
action = OrbitAction(altitude=pt.alt, pattern=OrbitAction.OrbitPattern.RaceTrack)
pt.tasks.append(action)
action = ControlledTask(OrbitAction(altitude=pt.alt, pattern=OrbitAction.OrbitPattern.RaceTrack))
action.stop_after_duration(CAP_DURATION * 60)
#for tgt in point.targets:
# if hasattr(tgt, "position"):
# engagetgt = EngageTargetsInZone(tgt.position, radius=CAP_DEFAULT_ENGAGE_DISTANCE, targets=[Targets.All.Air])
@@ -402,11 +395,45 @@ class AircraftConflictGenerator:
elif point.waypoint_type == FlightWaypointType.LANDING_POINT:
pt.type = "Land"
elif point.waypoint_type == FlightWaypointType.INGRESS_STRIKE:
print("TGTS :")
print(point.targets)
if group.units[0].unit_type == B_17G:
if len(point.targets) > 0:
bcenter = Point(0,0)
for j, t in enumerate(point.targets):
bcenter.x += t.position.x
bcenter.y += t.position.y
bcenter.x = bcenter.x / len(point.targets)
bcenter.y = bcenter.y / len(point.targets)
bombing = Bombing(bcenter)
bombing.params["expend"] = "All"
bombing.params["attackQtyLimit"] = False
bombing.params["directionEnabled"] = False
bombing.params["altitudeEnabled"] = False
bombing.params["weaponType"] = 2032
bombing.params["groupAttack"] = True
pt.tasks.append(bombing)
else:
for j, t in enumerate(point.targets):
print(t.position)
pt.tasks.append(Bombing(t.position))
if group.units[0].unit_type == JF_17 and j < 4:
group.add_nav_target_point(t.position, "PP" + str(j + 1))
if group.units[0].unit_type == F_14B and j == 0:
group.add_nav_target_point(t.position, "ST")
elif point.waypoint_type == FlightWaypointType.INGRESS_SEAD:
tgroup = self.m.find_group(point.targetGroup.group_identifier)
if tgroup is not None:
task = AttackGroup(tgroup.id)
task.params["expend"] = "All"
task.params["attackQtyLimit"] = False
task.params["directionEnabled"] = False
task.params["altitudeEnabled"] = False
task.params["weaponType"] = 268402702 # Guided Weapons
task.params["groupAttack"] = True
pt.tasks.append(task)
for j, t in enumerate(point.targets):
print(t.position)
pt.tasks.append(Bombing(t.position))
if group.units[0].unit_type == JF_17 and j < 4:
group.add_nav_target_point(t.position, "PP" + str(j + 1))
if group.units[0].unit_type == F_14B and j == 0:

View File

@@ -39,36 +39,60 @@ class BriefingGenerator:
return
flight_unit_name = db.unit_type_name(flight.unit_type)
self.description += 2 * "\n" + "-" * 50 + "\n"
self.description += "-" * 50 + "\n"
self.description += flight_unit_name + " x " + str(flight.count) + 2 * "\n"
self.description += "#0 -- TAKEOFF : Take off\n"
self.description += "#0 -- TAKEOFF : Take off from " + flight.from_cp.name + "\n"
for i, wpt in enumerate(flight.points):
self.description += "#" + str(1+i) + " -- " + wpt.name + " : " + wpt.description + "\n"
self.description += "#" + str(len(flight.points) + 1) + " -- RTB\n"
self.description += "-" * 50 + "\n"
def add_ally_flight_description(self, flight):
if flight.client_count == 0:
flight_unit_name = db.unit_type_name(flight.unit_type)
self.description += flight.flight_type.name + " " + flight_unit_name + " x " + str(flight.count) + ", departing in " + str(flight.scheduled_in) + " minutes \n"
def generate(self):
self.description = ""
self.description += "DCS Liberation turn #" + str(self.game.turn) + "\n"
self.description += "-"*50 + "\n"
self.description += "=" * 15 + "\n\n"
self.description += "Current situation:\n"
self.description += "=" * 15 + "\n\n"
self.description += "\n"*2
self.description += "Your flights:" + "\n"
self.description += "=" * 15 + "\n\n"
for planner in self.game.planners.values():
for flight in planner.flights:
self.add_flight_description(flight)
self.description += "\n"*2
self.description += "Planned ally flights:" + "\n"
self.description += "=" * 15 + "\n"
for planner in self.game.planners.values():
if planner.from_cp.captured and len(planner.flights) > 0:
self.description += "\nFrom " + planner.from_cp.full_name + " \n"
self.description += "-" * 50 + "\n\n"
for flight in planner.flights:
self.add_ally_flight_description(flight)
if self.freqs:
self.description += "\n\nComms Frequencies:\n"
self.description += "-" * 50 + "\n"
self.description += "=" * 15 + "\n"
for name, freq in self.freqs:
self.description += "\n{}: {}".format(name, freq)
self.description += "\n" + ("-" * 50) + "\n"
self.description += "{}: {}\n".format(name, freq)
self.description += ("-" * 50) + "\n"
for cp in self.game.theater.controlpoints:
if cp.captured and cp.cptype in [ControlPointType.LHA_GROUP, ControlPointType.AIRCRAFT_CARRIER_GROUP]:
self.description += cp.name + " TACAN : "
self.description += cp.name + "\n"
self.description += "TACAN : "
self.description += str(cp.tacanN)
if cp.tacanY:

View File

@@ -115,7 +115,7 @@ class EnviromentGenerator:
if weather_type == 1:
# thunderstorm
self._generate_base_weather()
self._generate_wind(random.randint(8, 12))
self._generate_wind(random.randint(0, 8))
self.mission.weather.clouds_density = random.randint(9, 10)
self.mission.weather.clouds_iprecptns = Weather.Preceptions.Thunderstorm
@@ -125,7 +125,7 @@ class EnviromentGenerator:
self.mission.weather.clouds_density = random.randint(5, 8)
self.mission.weather.clouds_iprecptns = Weather.Preceptions.Rain
self._generate_wind(random.randint(4, 8))
self._generate_wind(random.randint(0, 6))
elif weather_type == 3:
# clouds
self._generate_base_weather()

42
gen/fleet/cn_dd_group.py Normal file
View File

@@ -0,0 +1,42 @@
import random
from gen.fleet.dd_group import DDGroupGenerator
from gen.sam.group_generator import GroupGenerator
from dcs.ships import *
class ChineseNavyGroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
super(ChineseNavyGroupGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self):
include_frigate = random.choice([True, True, False])
include_dd = random.choice([True, False])
if include_dd:
include_cc = random.choice([True, False])
else:
include_cc = False
if include_frigate:
self.add_unit(Type_054A_Frigate, "FF1", self.position.x + 1200, self.position.y + 900, self.heading)
self.add_unit(Type_054A_Frigate, "FF2", self.position.x + 1200, self.position.y - 900, self.heading)
if include_dd:
dd_type = random.choice([Type_052C_Destroyer, Type_052B_Destroyer])
self.add_unit(dd_type, "FF1", self.position.x + 2400, self.position.y + 900, self.heading)
self.add_unit(dd_type, "FF2", self.position.x + 2400, self.position.y - 900, self.heading)
if include_cc:
cc_type = random.choice([Type_093, CGN_1144_2_Pyotr_Velikiy])
self.add_unit(cc_type, "CC1", self.position.x, self.position.y, self.heading)
self.get_generated_group().points[0].speed = 20
class Type54GroupGenerator(DDGroupGenerator):
def __init__(self, game, ground_object, faction):
super(Type54GroupGenerator, self).__init__(game, ground_object, faction, Type_054A_Frigate)

27
gen/fleet/dd_group.py Normal file
View File

@@ -0,0 +1,27 @@
import random
from gen.sam.group_generator import GroupGenerator
from dcs.ships import *
class DDGroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction, ddtype):
super(DDGroupGenerator, self).__init__(game, ground_object)
self.faction = faction
self.ddtype = ddtype
def generate(self):
self.add_unit(self.ddtype, "DD1", self.position.x + 500, self.position.y + 900, self.heading)
self.add_unit(self.ddtype, "DD2", self.position.x + 500, self.position.y - 900, self.heading)
self.get_generated_group().points[0].speed = 20
class OliverHazardPerryGroupGenerator(DDGroupGenerator):
def __init__(self, game, ground_object, faction):
super(OliverHazardPerryGroupGenerator, self).__init__(game, ground_object, faction, Oliver_Hazzard_Perry_class)
class ArleighBurkeGroupGenerator(DDGroupGenerator):
def __init__(self, game, ground_object, faction):
super(ArleighBurkeGroupGenerator, self).__init__(game, ground_object, faction, USS_Arleigh_Burke_IIa)

59
gen/fleet/ru_dd_group.py Normal file
View File

@@ -0,0 +1,59 @@
import random
from gen.fleet.dd_group import DDGroupGenerator
from gen.sam.group_generator import GroupGenerator
from dcs.ships import *
class RussianNavyGroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
super(RussianNavyGroupGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self):
include_frigate = random.choice([True, True, False])
include_dd = random.choice([True, False])
if include_dd:
include_cc = random.choice([True, False])
else:
include_cc = False
if include_frigate:
frigate_type = random.choice([FFL_1124_4_Grisha, FSG_1241_1MP_Molniya])
self.add_unit(frigate_type, "FF1", self.position.x + 1200, self.position.y + 900, self.heading)
self.add_unit(frigate_type, "FF2", self.position.x + 1200, self.position.y - 900, self.heading)
if include_dd:
dd_type = random.choice([FFG_11540_Neustrashimy, FF_1135M_Rezky])
self.add_unit(dd_type, "FF1", self.position.x + 2400, self.position.y + 900, self.heading)
self.add_unit(dd_type, "FF2", self.position.x + 2400, self.position.y - 900, self.heading)
if include_cc:
cc_type = random.choice([CG_1164_Moskva, CGN_1144_2_Pyotr_Velikiy])
self.add_unit(cc_type, "CC1", self.position.x, self.position.y, self.heading)
self.get_generated_group().points[0].speed = 20
class GrishaGroupGenerator(DDGroupGenerator):
def __init__(self, game, ground_object, faction):
super(GrishaGroupGenerator, self).__init__(game, ground_object, faction, FFL_1124_4_Grisha)
class MolniyaGroupGenerator(DDGroupGenerator):
def __init__(self, game, ground_object, faction):
super(MolniyaGroupGenerator, self).__init__(game, ground_object, faction, FSG_1241_1MP_Molniya)
class KiloSubGroupGenerator(DDGroupGenerator):
def __init__(self, game, ground_object, faction):
super(KiloSubGroupGenerator, self).__init__(game, ground_object, faction, SSK_877)
class TangoSubGroupGenerator(DDGroupGenerator):
def __init__(self, game, ground_object, faction):
super(TangoSubGroupGenerator, self).__init__(game, ground_object, faction, SSK_641B)

19
gen/fleet/schnellboot.py Normal file
View File

@@ -0,0 +1,19 @@
import random
from dcs.ships import Schnellboot_type_S130
from gen.sam.group_generator import GroupGenerator
class SchnellbootGroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
super(SchnellbootGroupGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self):
for i in range(random.randint(2, 4)):
self.add_unit(Schnellboot_type_S130, "Schnellboot" + str(i), self.position.x + i * random.randint(100, 250), self.position.y + (random.randint(100, 200)-100), self.heading)
self.get_generated_group().points[0].speed = 20

View File

@@ -1,6 +1,50 @@
import logging
import random
from game import db
from gen.fleet.carrier_group import CarrierGroupGenerator
from gen.fleet.cn_dd_group import ChineseNavyGroupGenerator, Type54GroupGenerator
from gen.fleet.dd_group import ArleighBurkeGroupGenerator, OliverHazardPerryGroupGenerator
from gen.fleet.lha_group import LHAGroupGenerator
from gen.fleet.ru_dd_group import RussianNavyGroupGenerator, GrishaGroupGenerator, MolniyaGroupGenerator, \
KiloSubGroupGenerator, TangoSubGroupGenerator
from gen.fleet.schnellboot import SchnellbootGroupGenerator
from gen.fleet.uboat import UBoatGroupGenerator
from gen.fleet.ww2lst import WW2LSTGroupGenerator
SHIP_MAP = {
"SchnellbootGroupGenerator": SchnellbootGroupGenerator,
"WW2LSTGroupGenerator": WW2LSTGroupGenerator,
"UBoatGroupGenerator": UBoatGroupGenerator,
"OliverHazardPerryGroupGenerator": OliverHazardPerryGroupGenerator,
"ArleighBurkeGroupGenerator": ArleighBurkeGroupGenerator,
"RussianNavyGroupGenerator": RussianNavyGroupGenerator,
"ChineseNavyGroupGenerator": ChineseNavyGroupGenerator,
"GrishaGroupGenerator": GrishaGroupGenerator,
"MolniyaGroupGenerator": MolniyaGroupGenerator,
"KiloSubGroupGenerator": KiloSubGroupGenerator,
"TangoSubGroupGenerator": TangoSubGroupGenerator,
"Type54GroupGenerator": Type54GroupGenerator
}
def generate_ship_group(game, ground_object, faction:str):
"""
This generate a ship group
:return: Nothing, but put the group reference inside the ground object
"""
faction = db.FACTIONS[faction]
if "boat" in faction.keys():
generators = faction["boat"]
if len(generators) > 0:
gen = random.choice(generators)
if gen in SHIP_MAP.keys():
generator = SHIP_MAP[gen](game, ground_object, faction)
generator.generate()
return generator.get_generated_group()
else:
logging.info("Unable to generate ship group, generator : " + str(gen) + "does not exists")
return None
def generate_carrier_group(faction:str, game, ground_object):

19
gen/fleet/uboat.py Normal file
View File

@@ -0,0 +1,19 @@
import random
from dcs.ships import Uboat_VIIC_U_flak
from gen.sam.group_generator import GroupGenerator
class UBoatGroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
super(UBoatGroupGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self):
for i in range(random.randint(1, 4)):
self.add_unit(Uboat_VIIC_U_flak, "Uboat" + str(i), self.position.x + i * random.randint(100, 250), self.position.y + (random.randint(100, 200)-100), self.heading)
self.get_generated_group().points[0].speed = 20

22
gen/fleet/ww2lst.py Normal file
View File

@@ -0,0 +1,22 @@
import random
from dcs.ships import LS_Samuel_Chase, LST_Mk_II
from gen.sam.group_generator import GroupGenerator
class WW2LSTGroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
super(WW2LSTGroupGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self):
# Add LS Samuel Chase
self.add_unit(LS_Samuel_Chase, "SamuelChase", self.position.x, self.position.y, self.heading)
for i in range(1, random.randint(3, 4)):
self.add_unit(LST_Mk_II, "LST" + str(i), self.position.x + i * random.randint(800, 1200), self.position.y, self.heading)
self.get_generated_group().points[0].speed = 20

View File

@@ -1,49 +1,17 @@
import math
import operator
import typing
import random
from game import db
from game.data.doctrine import MODERN_DOCTRINE
from game.data.radar_db import UNITS_WITH_RADAR
from game.utils import meter_to_feet, nm_to_meter
from gen import Conflict
from gen.flights.ai_flight_planner_db import INTERCEPT_CAPABLE, CAP_CAPABLE, CAS_CAPABLE, SEAD_CAPABLE, STRIKE_CAPABLE
from gen.flights.flight import Flight, FlightType, FlightWaypoint, FlightWaypointType
def meter_to_feet(value_in_meter):
return int(3.28084 * value_in_meter)
def feet_to_meter(value_in_feet):
return int(float(value_in_feet)/3.048)
def meter_to_nm(value_in_meter):
return int(float(value_in_meter)*0.000539957)
def nm_to_meter(value_in_nm):
return int(float(value_in_nm)*1852)
# TODO : Ideally should be based on the aircraft type instead / Availability of fuel
STRIKE_MAX_RANGE = 1500000
SEAD_MAX_RANGE = 1500000
MAX_NUMBER_OF_INTERCEPTION_GROUP = 3
MISSION_DURATION = 120 # in minutes
CAP_EVERY_X_MINUTES = 20
CAS_EVERY_X_MINUTES = 30
SEAD_EVERY_X_MINUTES = 40
STRIKE_EVERY_X_MINUTES = 40
INGRESS_EGRESS_DISTANCE = nm_to_meter(45)
INGRESS_ALT = feet_to_meter(20000)
EGRESS_ALT = feet_to_meter(20000)
PATROL_ALT_RANGE = (feet_to_meter(15000), feet_to_meter(33000))
NAV_ALT = 9144
PATTERN_ALTITUDE = feet_to_meter(5000)
CAP_DEFAULT_ENGAGE_DISTANCE = nm_to_meter(40)
MISSION_DURATION = 80
class FlightPlanner:
@@ -55,6 +23,17 @@ class FlightPlanner:
self.game = game
self.aircraft_inventory = {} # local copy of the airbase inventory
if from_cp.captured:
self.faction = self.game.player_faction
else:
self.faction = self.game.enemy_faction
if "doctrine" in self.faction.keys():
self.doctrine = self.faction["doctrine"]
else:
self.doctrine = MODERN_DOCTRINE
def reset(self):
"""
Reset the planned flights and available units
@@ -112,7 +91,7 @@ class FlightPlanner:
"""
# At least try to generate one interceptor group
number_of_interceptor_groups = min(max(sum([v for k, v in self.aircraft_inventory.items()]) / 4, MAX_NUMBER_OF_INTERCEPTION_GROUP), 1)
number_of_interceptor_groups = min(max(sum([v for k, v in self.aircraft_inventory.items()]) / 4, self.doctrine["MAX_NUMBER_OF_INTERCEPTION_GROUP"]), 1)
possible_interceptors = [k for k in self.aircraft_inventory.keys() if k in INTERCEPT_CAPABLE]
if len(possible_interceptors) <= 0:
@@ -145,7 +124,7 @@ class FlightPlanner:
inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft})
offset = random.randint(0,5)
for i in range(int(MISSION_DURATION/CAP_EVERY_X_MINUTES)):
for i in range(int(MISSION_DURATION/self.doctrine["CAP_EVERY_X_MINUTES"])):
try:
unit = random.choice([k for k, v in inventory.items() if v >= 2])
@@ -153,67 +132,16 @@ class FlightPlanner:
break
inventory[unit] = inventory[unit] - 2
ftype = FlightType.BARCAP if self.from_cp.is_carrier else FlightType.CAP
flight = Flight(unit, 2, self.from_cp, ftype)
flight = Flight(unit, 2, self.from_cp, FlightType.CAP)
flight.points = []
flight.scheduled_in = offset + i*random.randint(CAP_EVERY_X_MINUTES-5, CAP_EVERY_X_MINUTES+5)
flight.scheduled_in = offset + i*random.randint(self.doctrine["CAP_EVERY_X_MINUTES"] - 5, self.doctrine["CAP_EVERY_X_MINUTES"] + 5)
patrol_alt = random.randint(PATROL_ALT_RANGE[0], PATROL_ALT_RANGE[1])
# Choose a location for CAP patrols (Either behind frontline if there is one, or to protect ground objects)
if len(self._get_cas_locations()) > 0:
loc = random.choice(self._get_cas_locations())
ingress, heading, distance = Conflict.frontline_vector(self.from_cp, loc, self.game.theater)
center = ingress.point_from_heading(heading, distance / 2)
orbit_center = center.point_from_heading(heading - 90, random.randint(nm_to_meter(6), nm_to_meter(15)))
radius = distance * 2
orbit0p = orbit_center.point_from_heading(heading, radius)
orbit1p = orbit_center.point_from_heading(heading + 180, radius)
elif len(self.from_cp.ground_objects) > 0:
loc = random.choice(self.from_cp.ground_objects)
hdg = self.from_cp.position.heading_between_point(loc.position)
radius = random.randint(nm_to_meter(5), nm_to_meter(10))
orbit0p = loc.position.point_from_heading(hdg - 90, radius)
orbit1p = loc.position.point_from_heading(hdg + 90, radius)
enemy_cp = random.choice(self._get_cas_locations())
self.generate_frontline_cap(flight, flight.from_cp, enemy_cp)
else:
loc = self.from_cp.position.point_from_heading(random.randint(0, 360), random.randint(nm_to_meter(5), nm_to_meter(40)))
hdg = self.from_cp.position.heading_between_point(loc.position)
radius = random.randint(nm_to_meter(40), nm_to_meter(120))
orbit0p = loc.position.point_from_heading(hdg - 90, radius)
orbit1p = loc.position.point_from_heading(hdg + 90, radius)
# Create points
ascend = self.generate_ascend_point(self.from_cp)
flight.points.append(ascend)
orbit0 = FlightWaypoint(orbit0p.x, orbit0p.y, patrol_alt)
orbit0.name = "ORBIT 0"
orbit0.description = "Standby between this point and the next one"
orbit0.pretty_name = "Orbit race-track start"
orbit0.waypoint_type = FlightWaypointType.PATROL_TRACK
flight.points.append(orbit0)
orbit1 = FlightWaypoint(orbit1p.x, orbit1p.y, patrol_alt)
orbit1.name = "ORBIT 1"
orbit1.description = "Standby between this point and the previous one"
orbit1.pretty_name = "Orbit race-track end"
orbit1.waypoint_type = FlightWaypointType.PATROL
flight.points.append(orbit1)
orbit0.targets.append(self.from_cp)
obj_added = []
for ground_object in self.from_cp.ground_objects:
if ground_object.obj_name not in obj_added and not ground_object.airbase_group:
orbit0.targets.append(ground_object)
obj_added.append(ground_object.obj_name)
descend = self.generate_descend_point(self.from_cp)
flight.points.append(descend)
rtb = self.generate_rtb_waypoint(self.from_cp)
flight.points.append(rtb)
self.generate_barcap(flight, flight.from_cp)
self.cap_flights.append(flight)
self.flights.append(flight)
@@ -234,7 +162,7 @@ class FlightPlanner:
if len(cas_location) > 0:
offset = random.randint(0,5)
for i in range(int(MISSION_DURATION/CAS_EVERY_X_MINUTES)):
for i in range(int(MISSION_DURATION/self.doctrine["CAS_EVERY_X_MINUTES"])):
try:
unit = random.choice([k for k, v in inventory.items() if v >= 2])
@@ -243,47 +171,11 @@ class FlightPlanner:
inventory[unit] = inventory[unit] - 2
flight = Flight(unit, 2, self.from_cp, FlightType.CAS)
flight.points = []
flight.scheduled_in = offset + i*random.randint(CAS_EVERY_X_MINUTES-5, CAS_EVERY_X_MINUTES+5)
flight.scheduled_in = offset + i * random.randint(self.doctrine["CAS_EVERY_X_MINUTES"] - 5, self.doctrine["CAS_EVERY_X_MINUTES"] + 5)
location = random.choice(cas_location)
ingress, heading, distance = Conflict.frontline_vector(self.from_cp, location, self.game.theater)
center = ingress.point_from_heading(heading, distance/2)
egress = ingress.point_from_heading(heading, distance)
ascend = self.generate_ascend_point(self.from_cp)
flight.points.append(ascend)
ingress_point = FlightWaypoint(ingress.x, ingress.y, 1000)
ingress_point.alt_type = "RADIO"
ingress_point.name = "INGRESS"
ingress_point.pretty_name = "INGRESS"
ingress_point.description = "Ingress into CAS area"
ingress_point.waypoint_type = FlightWaypointType.INGRESS_CAS
flight.points.append(ingress_point)
center_point = FlightWaypoint(center.x, center.y, 1000)
center_point.alt_type = "RADIO"
center_point.description = "Provide CAS"
center_point.name = "CAS"
center_point.pretty_name = "CAS"
center_point.waypoint_type = FlightWaypointType.CAS
flight.points.append(center_point)
egress_point = FlightWaypoint(egress.x, egress.y, 1000)
egress_point.alt_type = "RADIO"
egress_point.description = "Egress from CAS area"
egress_point.name = "EGRESS"
egress_point.pretty_name = "EGRESS"
egress_point.waypoint_type = FlightWaypointType.EGRESS
flight.points.append(egress_point)
descend = self.generate_descend_point(self.from_cp)
flight.points.append(descend)
rtb = self.generate_rtb_waypoint(self.from_cp)
flight.points.append(rtb)
self.generate_cas(flight, flight.from_cp, location)
self.cas_flights.append(flight)
self.flights.append(flight)
@@ -303,7 +195,7 @@ class FlightPlanner:
if len(self.potential_sead_targets) > 0:
offset = random.randint(0,5)
for i in range(int(MISSION_DURATION/SEAD_EVERY_X_MINUTES)):
for i in range(int(MISSION_DURATION/self.doctrine["SEAD_EVERY_X_MINUTES"])):
if len(self.potential_sead_targets) <= 0:
break
@@ -317,51 +209,12 @@ class FlightPlanner:
flight = Flight(unit, 2, self.from_cp, random.choice([FlightType.SEAD, FlightType.DEAD]))
flight.points = []
flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5)
ascend = self.generate_ascend_point(self.from_cp)
flight.points.append(ascend)
flight.scheduled_in = offset + i*random.randint(self.doctrine["SEAD_EVERY_X_MINUTES"] - 5, self.doctrine["SEAD_EVERY_X_MINUTES"] + 5)
location = self.potential_sead_targets[0][0]
self.potential_sead_targets.pop(0)
heading = self.from_cp.position.heading_between_point(location.position)
ingress_heading = heading - 180 + 25
egress_heading = heading - 180 - 25
ingress_pos = location.position.point_from_heading(ingress_heading, INGRESS_EGRESS_DISTANCE)
ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, INGRESS_ALT)
ingress_point.pretty_name = "INGRESS on " + location.obj_name
ingress_point.description = "INGRESS on " + location.obj_name
ingress_point.waypoint_type = FlightWaypointType.INGRESS_SEAD
flight.points.append(ingress_point)
point = FlightWaypoint(location.position.x, location.position.y, 0)
point.alt_type = "RADIO"
if flight.flight_type == FlightType.DEAD:
point.description = "SEAD on " + location.obj_name
point.pretty_name = "SEAD on " + location.obj_name
point.only_for_player = True
else:
point.description = "DEAD on " + location.obj_name
point.pretty_name = "DEAD on " + location.obj_name
point.only_for_player = True
ingress_point.targets.append(location)
flight.points.append(point)
egress_pos = location.position.point_from_heading(egress_heading, INGRESS_EGRESS_DISTANCE)
egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, EGRESS_ALT)
egress_point.pretty_name = "EGRESS from " + location.obj_name
egress_point.description = "EGRESS from " + location.obj_name
egress_point.waypoint_type = FlightWaypointType.EGRESS
flight.points.append(egress_point)
descend = self.generate_descend_point(self.from_cp)
flight.points.append(descend)
rtb = self.generate_rtb_waypoint(self.from_cp)
flight.points.append(rtb)
self.generate_sead(flight, location, [])
self.sead_flights.append(flight)
self.flights.append(flight)
@@ -381,7 +234,7 @@ class FlightPlanner:
if len(self.potential_strike_targets) > 0:
offset = random.randint(0,5)
for i in range(int(MISSION_DURATION/STRIKE_EVERY_X_MINUTES)):
for i in range(int(MISSION_DURATION/self.doctrine["STRIKE_EVERY_X_MINUTES"])):
if len(self.potential_strike_targets) <= 0:
break
@@ -395,77 +248,12 @@ class FlightPlanner:
flight = Flight(unit, 2, self.from_cp, FlightType.STRIKE)
flight.points = []
flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5)
ascend = self.generate_ascend_point(self.from_cp)
flight.points.append(ascend)
flight.scheduled_in = offset + i*random.randint(self.doctrine["STRIKE_EVERY_X_MINUTES"] - 5, self.doctrine["STRIKE_EVERY_X_MINUTES"] + 5)
location = self.potential_strike_targets[0][0]
self.potential_strike_targets.pop(0)
heading = self.from_cp.position.heading_between_point(location.position)
ingress_heading = heading - 180 + 25
egress_heading = heading - 180 - 25
ingress_pos = location.position.point_from_heading(ingress_heading, INGRESS_EGRESS_DISTANCE)
ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, INGRESS_ALT)
ingress_point.pretty_name = "INGRESS on " + location.obj_name
ingress_point.description = "INGRESS on " + location.obj_name
ingress_point.name = "INGRESS"
ingress_point.waypoint_type = FlightWaypointType.INGRESS_STRIKE
flight.points.append(ingress_point)
if len(location.groups) > 0 and location.dcs_identifier == "AA":
for g in location.groups:
for j, u in enumerate(g.units):
point = FlightWaypoint(u.position.x, u.position.y, 0)
point.description = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
point.pretty_name = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
point.name = location.obj_name + "#" + str(j)
point.only_for_player = True
ingress_point.targets.append(location)
flight.points.append(point)
else:
if hasattr(location, "obj_name"):
buildings = self.game.theater.find_ground_objects_by_obj_name(location.obj_name)
print(buildings)
for building in buildings:
print("BUILDING " + str(building.is_dead) + " " + str(building.dcs_identifier))
if building.is_dead:
continue
point = FlightWaypoint(building.position.x, building.position.y, 0)
point.description = "STRIKE on " + building.obj_name + " " + str(building.category)
point.pretty_name = "STRIKE on " + building.obj_name + " " + str(building.category)
point.name = building.obj_name
point.only_for_player = True
ingress_point.targets.append(building)
flight.points.append(point)
else:
point = FlightWaypoint(location.position.x, location.position.y, 0)
point.description = "STRIKE on " + location.obj_name + " " + str(location.category)
point.pretty_name = "STRIKE on " + location.obj_name + " " + str(location.category)
point.name = location.obj_name
point.only_for_player = True
ingress_point.targets.append(location)
flight.points.append(point)
egress_pos = location.position.point_from_heading(egress_heading, INGRESS_EGRESS_DISTANCE)
egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, EGRESS_ALT)
egress_point.name = "EGRESS"
egress_point.pretty_name = "EGRESS from " + location.obj_name
egress_point.description = "EGRESS from " + location.obj_name
egress_point.waypoint_type = FlightWaypointType.EGRESS
flight.points.append(egress_point)
descend = self.generate_descend_point(self.from_cp)
flight.points.append(descend)
rtb = self.generate_rtb_waypoint(self.from_cp)
flight.points.append(rtb)
self.generate_strike(flight, location)
self.strike_flights.append(flight)
self.flights.append(flight)
@@ -475,9 +263,12 @@ class FlightPlanner:
self.aircraft_inventory[k] = v
def _get_cas_locations(self):
return self._get_cas_locations_for_cp(self.from_cp)
def _get_cas_locations_for_cp(self, for_cp):
cas_locations = []
for cp in self.from_cp.connected_points:
if cp.captured != self.from_cp.captured:
for cp in for_cp.connected_points:
if cp.captured != for_cp.captured:
cas_locations.append(cp)
return cas_locations
@@ -495,7 +286,7 @@ class FlightPlanner:
distance = math.hypot(cp.position.x - self.from_cp.position.x,
cp.position.y - self.from_cp.position.y)
if distance > 2*STRIKE_MAX_RANGE:
if distance > 2*self.doctrine["STRIKE_MAX_RANGE"]:
# Then it's unlikely any child ground object is in range
return
@@ -507,7 +298,7 @@ class FlightPlanner:
distance = math.hypot(cp.position.x - self.from_cp.position.x,
cp.position.y - self.from_cp.position.y)
if distance < SEAD_MAX_RANGE:
if distance < self.doctrine["SEAD_MAX_RANGE"]:
self.potential_strike_targets.append((g, distance))
added_group.append(g)
@@ -528,7 +319,7 @@ class FlightPlanner:
cp.position.y - self.from_cp.position.y)
# Then it's unlikely any ground object is range
if distance > 2*SEAD_MAX_RANGE:
if distance > 2*self.doctrine["SEAD_MAX_RANGE"]:
return
for g in cp.ground_objects:
@@ -536,8 +327,7 @@ class FlightPlanner:
if g.dcs_identifier == "AA":
# Check that there is at least one unit with a radar in the ground objects unit groups
number_of_units = sum([len([r for r in group.units if hasattr(db.unit_type_from_name(r.type), "detection_range")
and db.unit_type_from_name(r.type).detection_range > 1000]) for group in g.groups])
number_of_units = sum([len([r for r in group.units if db.unit_type_from_name(r.type) in UNITS_WITH_RADAR]) for group in g.groups])
if number_of_units <= 0:
continue
@@ -545,7 +335,7 @@ class FlightPlanner:
distance = math.hypot(cp.position.x - self.from_cp.position.x,
cp.position.y - self.from_cp.position.y)
if distance < SEAD_MAX_RANGE:
if distance < self.doctrine["SEAD_MAX_RANGE"]:
self.potential_sead_targets.append((g, distance))
self.potential_sead_targets.sort(key=operator.itemgetter(1))
@@ -563,29 +353,341 @@ class FlightPlanner:
del base_aircraft_inventory[f.unit_type]
return base_aircraft_inventory
def generate_strike(self, flight, location):
flight.flight_type = FlightType.STRIKE
ascend = self.generate_ascend_point(flight.from_cp)
flight.points.append(ascend)
heading = flight.from_cp.position.heading_between_point(location.position)
ingress_heading = heading - 180 + 25
egress_heading = heading - 180 - 25
ingress_pos = location.position.point_from_heading(ingress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"])
ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, self.doctrine["INGRESS_ALT"])
ingress_point.pretty_name = "INGRESS on " + location.obj_name
ingress_point.description = "INGRESS on " + location.obj_name
ingress_point.name = "INGRESS"
ingress_point.waypoint_type = FlightWaypointType.INGRESS_STRIKE
flight.points.append(ingress_point)
if len(location.groups) > 0 and location.dcs_identifier == "AA":
for g in location.groups:
for j, u in enumerate(g.units):
point = FlightWaypoint(u.position.x, u.position.y, 0)
point.description = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
point.pretty_name = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
point.name = location.obj_name + "#" + str(j)
point.only_for_player = True
ingress_point.targets.append(location)
flight.points.append(point)
else:
if hasattr(location, "obj_name"):
buildings = self.game.theater.find_ground_objects_by_obj_name(location.obj_name)
print(buildings)
for building in buildings:
print("BUILDING " + str(building.is_dead) + " " + str(building.dcs_identifier))
if building.is_dead:
continue
point = FlightWaypoint(building.position.x, building.position.y, 0)
point.description = "STRIKE on " + building.obj_name + " " + building.category + " [" + str(building.dcs_identifier) + " ]"
point.pretty_name = "STRIKE on " + building.obj_name + " " + building.category + " [" + str(building.dcs_identifier) + " ]"
point.name = building.obj_name
point.only_for_player = True
ingress_point.targets.append(building)
flight.points.append(point)
else:
point = FlightWaypoint(location.position.x, location.position.y, 0)
point.description = "STRIKE on " + location.obj_name
point.pretty_name = "STRIKE on " + location.obj_name
point.name = location.obj_name
point.only_for_player = True
ingress_point.targets.append(location)
flight.points.append(point)
egress_pos = location.position.point_from_heading(egress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"])
egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, self.doctrine["EGRESS_ALT"])
egress_point.name = "EGRESS"
egress_point.pretty_name = "EGRESS from " + location.obj_name
egress_point.description = "EGRESS from " + location.obj_name
egress_point.waypoint_type = FlightWaypointType.EGRESS
flight.points.append(egress_point)
descend = self.generate_descend_point(flight.from_cp)
flight.points.append(descend)
rtb = self.generate_rtb_waypoint(flight.from_cp)
flight.points.append(rtb)
def generate_barcap(self, flight, for_cp):
"""
Generate a barcap flight at a given location
:param flight: Flight to setup
:param for_cp: CP to protect
"""
flight.flight_type = FlightType.BARCAP if for_cp.is_carrier else FlightType.CAP
patrol_alt = random.randint(self.doctrine["PATROL_ALT_RANGE"][0], self.doctrine["PATROL_ALT_RANGE"][1])
if len(for_cp.ground_objects) > 0:
loc = random.choice(for_cp.ground_objects)
hdg = for_cp.position.heading_between_point(loc.position)
radius = random.randint(self.doctrine["CAP_PATTERN_LENGTH"][0], self.doctrine["CAP_PATTERN_LENGTH"][1])
orbit0p = loc.position.point_from_heading(hdg - 90, radius)
orbit1p = loc.position.point_from_heading(hdg + 90, radius)
else:
loc = for_cp.position.point_from_heading(random.randint(0, 360), random.randint(self.doctrine["CAP_DISTANCE_FROM_CP"][0], self.doctrine["CAP_DISTANCE_FROM_CP"][1]))
hdg = for_cp.position.heading_between_point(loc)
radius = random.randint(self.doctrine["CAP_PATTERN_LENGTH"][0], self.doctrine["CAP_PATTERN_LENGTH"][1])
orbit0p = loc.point_from_heading(hdg - 90, radius)
orbit1p = loc.point_from_heading(hdg + 90, radius)
# Create points
ascend = self.generate_ascend_point(flight.from_cp)
flight.points.append(ascend)
orbit0 = FlightWaypoint(orbit0p.x, orbit0p.y, patrol_alt)
orbit0.name = "ORBIT 0"
orbit0.description = "Standby between this point and the next one"
orbit0.pretty_name = "Race-track start"
orbit0.waypoint_type = FlightWaypointType.PATROL_TRACK
flight.points.append(orbit0)
orbit1 = FlightWaypoint(orbit1p.x, orbit1p.y, patrol_alt)
orbit1.name = "ORBIT 1"
orbit1.description = "Standby between this point and the previous one"
orbit1.pretty_name = "Race-track end"
orbit1.waypoint_type = FlightWaypointType.PATROL
flight.points.append(orbit1)
orbit0.targets.append(for_cp)
obj_added = []
for ground_object in for_cp.ground_objects:
if ground_object.obj_name not in obj_added and not ground_object.airbase_group:
orbit0.targets.append(ground_object)
obj_added.append(ground_object.obj_name)
descend = self.generate_descend_point(flight.from_cp)
flight.points.append(descend)
rtb = self.generate_rtb_waypoint(flight.from_cp)
flight.points.append(rtb)
def generate_frontline_cap(self, flight, ally_cp, enemy_cp):
"""
Generate a cap flight for the frontline between ally_cp and enemy cp in order to ensure air superiority and
protect friendly CAP airbase
:param flight: Flight to setup
:param ally_cp: CP to protect
:param enemy_cp: Enemy connected cp
"""
flight.flight_type = FlightType.CAP
patrol_alt = random.randint(self.doctrine["PATROL_ALT_RANGE"][0], self.doctrine["PATROL_ALT_RANGE"][1])
# Find targets waypoints
ingress, heading, distance = Conflict.frontline_vector(ally_cp, enemy_cp, self.game.theater)
center = ingress.point_from_heading(heading, distance / 2)
orbit_center = center.point_from_heading(heading - 90, random.randint(nm_to_meter(6), nm_to_meter(15)))
combat_width = distance / 2
if combat_width > 500000:
combat_width = 500000
if combat_width < 35000:
combat_width = 35000
radius = combat_width*1.25
orbit0p = orbit_center.point_from_heading(heading, radius)
orbit1p = orbit_center.point_from_heading(heading + 180, radius)
# Create points
ascend = self.generate_ascend_point(flight.from_cp)
flight.points.append(ascend)
orbit0 = FlightWaypoint(orbit0p.x, orbit0p.y, patrol_alt)
orbit0.name = "ORBIT 0"
orbit0.description = "Standby between this point and the next one"
orbit0.pretty_name = "Race-track start"
orbit0.waypoint_type = FlightWaypointType.PATROL_TRACK
flight.points.append(orbit0)
orbit1 = FlightWaypoint(orbit1p.x, orbit1p.y, patrol_alt)
orbit1.name = "ORBIT 1"
orbit1.description = "Standby between this point and the previous one"
orbit1.pretty_name = "Race-track end"
orbit1.waypoint_type = FlightWaypointType.PATROL
flight.points.append(orbit1)
# Note : Targets of a PATROL TRACK waypoints are the points to be defended
orbit0.targets.append(flight.from_cp)
orbit0.targets.append(center)
descend = self.generate_descend_point(flight.from_cp)
flight.points.append(descend)
rtb = self.generate_rtb_waypoint(flight.from_cp)
flight.points.append(rtb)
def generate_sead(self, flight, location, custom_targets = []):
"""
Generate a sead flight at a given location
:param flight: Flight to setup
:param location: Location of the SEAD target
:param custom_targets: Custom targets if any
"""
flight.points = []
flight.flight_type = random.choice([FlightType.SEAD, FlightType.DEAD])
ascend = self.generate_ascend_point(flight.from_cp)
flight.points.append(ascend)
heading = flight.from_cp.position.heading_between_point(location.position)
ingress_heading = heading - 180 + 25
egress_heading = heading - 180 - 25
ingress_pos = location.position.point_from_heading(ingress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"])
ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, self.doctrine["INGRESS_ALT"])
ingress_point.name = "INGRESS"
ingress_point.pretty_name = "INGRESS on " + location.obj_name
ingress_point.description = "INGRESS on " + location.obj_name
ingress_point.waypoint_type = FlightWaypointType.INGRESS_SEAD
flight.points.append(ingress_point)
if len(custom_targets) > 0:
for target in custom_targets:
point = FlightWaypoint(target.position.x, target.position.y, 0)
point.alt_type = "RADIO"
if flight.flight_type == FlightType.DEAD:
point.description = "SEAD on " + target.type
point.pretty_name = "SEAD on " + location.obj_name
point.only_for_player = True
else:
point.description = "DEAD on " + location.obj_name
point.pretty_name = "DEAD on " + location.obj_name
point.only_for_player = True
ingress_point.targets.append(location)
ingress_point.targetGroup = location
flight.points.append(point)
else:
point = FlightWaypoint(location.position.x, location.position.y, 0)
point.alt_type = "RADIO"
if flight.flight_type == FlightType.DEAD:
point.description = "SEAD on " + location.obj_name
point.pretty_name = "SEAD on " + location.obj_name
point.only_for_player = True
else:
point.description = "DEAD on " + location.obj_name
point.pretty_name = "DEAD on " + location.obj_name
point.only_for_player = True
ingress_point.targets.append(location)
ingress_point.targetGroup = location
flight.points.append(point)
egress_pos = location.position.point_from_heading(egress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"])
egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, self.doctrine["EGRESS_ALT"])
egress_point.name = "EGRESS"
egress_point.pretty_name = "EGRESS from " + location.obj_name
egress_point.description = "EGRESS from " + location.obj_name
egress_point.waypoint_type = FlightWaypointType.EGRESS
flight.points.append(egress_point)
descend = self.generate_descend_point(flight.from_cp)
flight.points.append(descend)
rtb = self.generate_rtb_waypoint(flight.from_cp)
flight.points.append(rtb)
def generate_cas(self, flight, from_cp, location):
"""
Generate a CAS flight at a given location
:param flight: Flight to setup
:param location: Location of the CAS targets
"""
flight.points = []
flight.flight_type = FlightType.CAS
ingress, heading, distance = Conflict.frontline_vector(from_cp, location, self.game.theater)
center = ingress.point_from_heading(heading, distance / 2)
egress = ingress.point_from_heading(heading, distance)
ascend = self.generate_ascend_point(flight.from_cp)
flight.points.append(ascend)
ingress_point = FlightWaypoint(ingress.x, ingress.y, 1000)
ingress_point.alt_type = "RADIO"
ingress_point.name = "INGRESS"
ingress_point.pretty_name = "INGRESS"
ingress_point.description = "Ingress into CAS area"
ingress_point.waypoint_type = FlightWaypointType.INGRESS_CAS
flight.points.append(ingress_point)
center_point = FlightWaypoint(center.x, center.y, 1000)
center_point.alt_type = "RADIO"
center_point.description = "Provide CAS"
center_point.name = "CAS"
center_point.pretty_name = "CAS"
center_point.waypoint_type = FlightWaypointType.CAS
flight.points.append(center_point)
egress_point = FlightWaypoint(egress.x, egress.y, 1000)
egress_point.alt_type = "RADIO"
egress_point.description = "Egress from CAS area"
egress_point.name = "EGRESS"
egress_point.pretty_name = "EGRESS"
egress_point.waypoint_type = FlightWaypointType.EGRESS
flight.points.append(egress_point)
descend = self.generate_descend_point(flight.from_cp)
flight.points.append(descend)
rtb = self.generate_rtb_waypoint(flight.from_cp)
flight.points.append(rtb)
def generate_ascend_point(self, from_cp):
"""
Generate ascend point
:param from_cp: Airport you're taking off from
:return:
"""
ascend_heading = from_cp.heading
pos_ascend = from_cp.position.point_from_heading(ascend_heading, 10000)
ascend = FlightWaypoint(pos_ascend.x, pos_ascend.y, PATTERN_ALTITUDE)
ascend = FlightWaypoint(pos_ascend.x, pos_ascend.y, self.doctrine["PATTERN_ALTITUDE"])
ascend.name = "ASCEND"
ascend.alt_type = "RADIO"
ascend.description = "Ascend to alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL], then proceed to next waypoint"
ascend.pretty_name = "Ascend to alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL]"
ascend.description = "Ascend"
ascend.pretty_name = "Ascend"
ascend.waypoint_type = FlightWaypointType.ASCEND_POINT
return ascend
def generate_descend_point(self, from_cp):
"""
Generate approach/descend point
:param from_cp: Airport you're landing at
:return:
"""
ascend_heading = from_cp.heading
descend = from_cp.position.point_from_heading(ascend_heading - 180, 30000)
descend = FlightWaypoint(descend.x, descend.y, PATTERN_ALTITUDE)
descend = from_cp.position.point_from_heading(ascend_heading - 180, 10000)
descend = FlightWaypoint(descend.x, descend.y, self.doctrine["PATTERN_ALTITUDE"])
descend.name = "DESCEND"
descend.alt_type = "RADIO"
descend.description = "Descend to pattern alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL], contact tower, and land"
descend.pretty_name = "Descend to pattern alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL]"
descend.description = "Descend to pattern alt"
descend.pretty_name = "Descend to pattern alt"
descend.waypoint_type = FlightWaypointType.DESCENT_POINT
return descend
def generate_rtb_waypoint(self, from_cp):
"""
Generate RTB landing point
:param from_cp: Airport you're landing at
:return:
"""
rtb = from_cp.position
rtb = FlightWaypoint(rtb.x, rtb.y, 0)
rtb.name = "LANDING"
@@ -593,5 +695,4 @@ class FlightPlanner:
rtb.description = "RTB"
rtb.pretty_name = "RTB"
rtb.waypoint_type = FlightWaypointType.LANDING_POINT
return rtb
return rtb

View File

@@ -171,6 +171,7 @@ STRIKE_CAPABLE = [
P_51D,
P_47D_30,
A_20G,
B_17G,
SpitfireLFMkIXCW,
SpitfireLFMkIX,

View File

@@ -46,6 +46,18 @@ class FlightWaypointType(Enum):
TARGET_POINT = 12 # A target building or static object, position
TARGET_GROUP_LOC = 13 # A target group approximate location
TARGET_SHIP = 14 # A target ship known location
CUSTOM = 15 # User waypoint (no specific behaviour)
class PredefinedWaypointCategory(Enum):
NOT_PREDEFINED = 0
ALLY_CP = 1
ENEMY_CP = 2
FRONTLINE = 3
ENEMY_BUILDING = 4
ENEMY_UNIT = 5
ALLY_BUILDING = 6
ALLY_UNIT = 7
class FlightWaypoint:
@@ -58,10 +70,13 @@ class FlightWaypoint:
self.name = ""
self.description = ""
self.targets = []
self.targetGroup = None
self.obj_name = ""
self.pretty_name = ""
self.waypoint_type = FlightWaypointType.TAKEOFF # type: FlightWaypointType
self.category = PredefinedWaypointCategory.NOT_PREDEFINED# type: PredefinedWaypointCategory
self.only_for_player = False
self.data = None
class Flight:

View File

@@ -22,15 +22,22 @@ class ForcedOptionsGenerator:
self.game = game
def _set_options_view(self):
if self.game.settings.map_coalition_visibility == "All Units":
if self.game.settings.map_coalition_visibility == ForcedOptions.Views.All:
self.mission.forced_options.options_view = ForcedOptions.Views.All
elif self.game.settings.map_coalition_visibility == "Allied Units":
elif self.game.settings.map_coalition_visibility == ForcedOptions.Views.Allies:
self.mission.forced_options.options_view = ForcedOptions.Views.Allies
elif self.game.settings.map_coalition_visibility == "Own Aircraft":
elif self.game.settings.map_coalition_visibility == ForcedOptions.Views.OnlyAllies:
self.mission.forced_options.options_view = ForcedOptions.Views.OnlyAllies
elif self.game.settings.map_coalition_visibility == ForcedOptions.Views.MyAircraft:
self.mission.forced_options.options_view = ForcedOptions.Views.MyAircraft
elif self.game.settings.map_coalition_visibility == "None":
elif self.game.settings.map_coalition_visibility == ForcedOptions.Views.OnlyMap:
self.mission.forced_options.options_view = ForcedOptions.Views.OnlyMap
def _set_external_views(self):
if not self.game.settings.external_views_allowed:
self.mission.forced_options.external_views = self.game.settings.external_views_allowed
def _set_labels(self):
if self.game.settings.labels == "Abbreviated":
self.mission.forced_options.labels = int(Labels.Abbreviated)
@@ -41,5 +48,8 @@ class ForcedOptionsGenerator:
def generate(self):
self._set_options_view()
self._set_external_views()
self._set_labels()

View File

@@ -1,6 +1,7 @@
import logging
from game import db
from game.data.building_data import FORTIFICATION_UNITS_ID, FORTIFICATION_UNITS
from game.db import unit_type_from_name
from .conflictgen import *
from .naming import *
@@ -50,9 +51,6 @@ class GroundObjectsGenerator:
else:
cp = self.conflict.from_cp
consumed_farps = set()
for cp in self.game.theater.controlpoints:
if cp.captured:
@@ -63,26 +61,43 @@ class GroundObjectsGenerator:
for ground_object in cp.ground_objects:
if ground_object.dcs_identifier == "AA":
if self.game.position_culled(ground_object.position):
continue
for g in ground_object.groups:
if len(g.units) > 0:
utype = unit_type_from_name(g.units[0].type)
vg = self.m.vehicle_group(side, g.name, utype, position=g.position, heading=g.units[0].heading)
vg.units[0].name = self.m.string(g.units[0].name)
for i, u in enumerate(g.units):
if i > 0:
vehicle = Vehicle(self.m.next_unit_id(), self.m.string(u.name), u.type)
vehicle.position.x = u.position.x
vehicle.position.y = u.position.y
vehicle.heading = u.heading
vg.add_unit(vehicle)
if not ground_object.sea_object:
vg = self.m.vehicle_group(side, g.name, utype, position=g.position, heading=g.units[0].heading)
vg.units[0].name = self.m.string(g.units[0].name)
for i, u in enumerate(g.units):
if i > 0:
vehicle = Vehicle(self.m.next_unit_id(), self.m.string(u.name), u.type)
vehicle.position.x = u.position.x
vehicle.position.y = u.position.y
vehicle.heading = u.heading
vg.add_unit(vehicle)
else:
vg = self.m.ship_group(side, g.name, utype, position=g.position,
heading=g.units[0].heading)
vg.units[0].name = self.m.string(g.units[0].name)
for i, u in enumerate(g.units):
utype = unit_type_from_name(u.type)
if i > 0:
ship = Ship(self.m.next_unit_id(), self.m.string(u.name), utype)
ship.position.x = u.position.x
ship.position.y = u.position.y
ship.heading = u.heading
vg.add_unit(ship)
if self.game.settings.perf_red_alert_state:
vg.points[0].tasks.append(OptAlarmState(2))
else:
vg.points[0].tasks.append(OptAlarmState(1))
elif ground_object.dcs_identifier in ["CARRIER", "LHA"]:
for g in ground_object.groups:
if len(g.units) > 0:
@@ -102,63 +117,61 @@ class GroundObjectsGenerator:
ship.heading = u.heading
sg.add_unit(ship)
# TODO : make sure the point is not on Land
sg.add_waypoint(sg.points[0].position.point_from_heading(g.units[0].heading, 100000))
# Find carrier direction (In the wind)
found_carrier_destination = False
attempt = 0
while not found_carrier_destination and attempt < 5:
point = sg.points[0].position.point_from_heading(self.m.weather.wind_at_ground.direction, 100000-attempt*20000)
if self.game.theater.is_in_sea(point):
found_carrier_destination = True
sg.add_waypoint(point)
else:
attempt = attempt + 1
# SET UP TACAN
# Set UP TACAN and ICLS
modeChannel = "X" if not cp.tacanY else "Y"
sg.points[0].tasks.append(ActivateBeaconCommand(channel=cp.tacanN, modechannel=modeChannel, callsign=cp.tacanI, unit_id=sg.units[0].id))
sg.points[0].tasks.append(ActivateBeaconCommand(channel=cp.tacanN, modechannel=modeChannel, callsign=cp.tacanI, unit_id=sg.units[0].id, aa=False))
if ground_object.dcs_identifier == "CARRIER" and hasattr(cp, "icls"):
sg.points[0].tasks.append(ActivateICLSCommand(cp.icls, unit_id=sg.units[0].id))
else:
if self.game.position_culled(ground_object.position):
continue
static_type = None
if ground_object.dcs_identifier in warehouse_map:
static_type = warehouse_map[ground_object.dcs_identifier]
else:
elif ground_object.dcs_identifier in fortification_map:
static_type = fortification_map[ground_object.dcs_identifier]
if not static_type:
elif ground_object.dcs_identifier in FORTIFICATION_UNITS_ID:
for f in FORTIFICATION_UNITS:
if f.id == ground_object.dcs_identifier:
unit_type = f
break
else:
print("Didn't find {} in static _map(s)!".format(ground_object.dcs_identifier))
continue
group = self.m.static_group(
country=side,
name=ground_object.string_identifier,
_type=static_type,
position=ground_object.position,
heading=ground_object.heading,
dead=ground_object.is_dead,
)
logging.info("generated {}object identifier {} with mission id {}".format("dead " if ground_object.is_dead else "", group.name, group.id))
def farp_aa(mission_obj, country, name, position: mapping.Point):
"""
Add AAA to a FARP :)
:param mission_obj:
:param country:
:param name:
:param position:
:return:
"""
vg = unitgroup.VehicleGroup(mission_obj.next_group_id(), mission_obj.string(name))
units = [
AirDefence.SPAAA_ZSU_23_4_Shilka,
AirDefence.AAA_ZU_23_Closed,
]
v = mission_obj.vehicle(name + "_AAA", random.choice(units))
v.position.x = position.x - random.randint(5, 30)
v.position.y = position.y - random.randint(5, 30)
v.heading = random.randint(0, 359)
vg.add_unit(v)
wp = vg.add_waypoint(vg.units[0].position, PointAction.OffRoad, 0)
wp.ETA_locked = True
country.add_vehicle_group(vg)
return vg
if static_type is None:
if not ground_object.is_dead:
group = self.m.vehicle_group(
country=side,
name=ground_object.string_identifier,
_type=unit_type,
position=ground_object.position,
heading=ground_object.heading,
)
logging.info("generated {}object identifier {} with mission id {}".format(
"dead " if ground_object.is_dead else "", group.name, group.id))
else:
group = self.m.static_group(
country=side,
name=ground_object.string_identifier,
_type=static_type,
position=ground_object.position,
heading=ground_object.heading,
dead=ground_object.is_dead,
)
logging.info("generated {}object identifier {} with mission id {}".format("dead " if ground_object.is_dead else "", group.name, group.id))

View File

@@ -0,0 +1,27 @@
import logging
import random
from game import db
from gen.missiles.v1_group import V1GroupGenerator
MISSILES_MAP = {
"V1GroupGenerator": V1GroupGenerator,
}
def generate_missile_group(game, ground_object, faction:str):
"""
This generate a ship group
:return: Nothing, but put the group reference inside the ground object
"""
faction = db.FACTIONS[faction]
if "missiles" in faction.keys():
generators = faction["missiles"]
if len(generators) > 0:
gen = random.choice(generators)
if gen in MISSILES_MAP.keys():
generator = MISSILES_MAP[gen](game, ground_object, faction)
generator.generate()
return generator.get_generated_group()
else:
logging.info("Unable to generate missile group, generator : " + str(gen) + "does not exists")
return None

32
gen/missiles/v1_group.py Normal file
View File

@@ -0,0 +1,32 @@
import random
from dcs.vehicles import Unarmed, MissilesSS, AirDefence
from gen.sam.group_generator import GroupGenerator
class V1GroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
super(V1GroupGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self):
# Ramps
self.add_unit(MissilesSS.V_1_ramp, "V1#0", self.position.x, self.position.y + random.randint(1, 8), self.heading)
self.add_unit(MissilesSS.V_1_ramp, "V1#1", self.position.x + 50, self.position.y + random.randint(1, 8), self.heading)
self.add_unit(MissilesSS.V_1_ramp, "V1#2", self.position.x + 100, self.position.y + random.randint(1, 8), self.heading)
# Commander
self.add_unit(Unarmed.Kübelwagen_82, "Kubel#0", self.position.x - 35, self.position.y - 20,
self.heading)
# Self defense flak
flak_unit = random.choice([AirDefence.AAA_Flak_Vierling_38, AirDefence.AAA_Flak_38])
self.add_unit(flak_unit, "FLAK#0", self.position.x - 55, self.position.y - 38,
self.heading)
self.add_unit(Unarmed.Blitz_3_6_6700A, "Blitz#0",
self.position.x + 200, self.position.y + 15, 90)

View File

@@ -32,7 +32,10 @@ class NameGenerator:
"TERMITE", "URCHIN", "SHRIMP", "TURKEY", "TOUCAN", "TETRA", "HUSKY", "STARFISH", "SWAN",
"FROG", "SQUIRREL", "WALRUS", "WARTHOG", "CORGI", "WEASEL", "WOMBAT", "WOLVERINE", "MAMMOTH",
"TOAD", "WOLF", "ZEBU", "SEAL", "SKATE", "JELLYFISH", "MOSQUITO", "LOCUST", "SLUG", "SNAIL",
"HEDGEHOG", "PIGLET", "FENNEC", "BADGER", "ALPACA"
"HEDGEHOG", "PIGLET", "FENNEC", "BADGER", "ALPACA", "DINGO", "COLT", "SKUNK", "BUNNY", "IMPALA",
"GUANACO", "CAPYBARA", "ELK", "MINK", "PRONGHORN", "CROW", "BUMBLEBEE", "FAWN", "OTTER", "WATERBUCK",
"JERBOA", "KITTEN", "ARGALI", "OX", "MARE", "FINCH", "BASILISK", "GOPHER", "HAMSTER", "CANARY", "WOODCHUCK",
"ANACONDA"
]
def __init__(self):

View File

@@ -15,7 +15,7 @@ class FlakGenerator(GroupGenerator):
grid_x = random.randint(2, 4)
grid_y = random.randint(2, 4)
spacing = random.randint(10,40)
spacing = random.randint(30,60)
index = 0
mixed = random.choice([True, False])
@@ -25,15 +25,29 @@ class FlakGenerator(GroupGenerator):
for j in range(grid_y):
index = index+1
self.add_unit(unit_type, "AAA#" + str(index),
self.position.x + spacing*i,
self.position.y + spacing*j, self.heading)
self.position.x + spacing*i + random.randint(1,5),
self.position.y + spacing*j + random.randint(1,5), self.heading)
if(mixed):
unit_type = random.choice(GFLAK)
# Enough Opel Blitz truck to transport the guns
for i in range(grid_x):
for j in range(grid_y):
# Search lights
search_pos = self.get_circular_position(random.randint(2,5), 90)
for index, pos in enumerate(search_pos):
self.add_unit(AirDefence.Flak_Searchlight_37, "SearchLight#" + str(index), pos[0], pos[1], self.heading)
# Support
self.add_unit(AirDefence.Maschinensatz_33, "MC33#", self.position.x-20, self.position.y-20, self.heading)
self.add_unit(AirDefence.AAA_Kdo_G_40, "KDO#", self.position.x - 25, self.position.y - 20,
self.heading)
# Commander
self.add_unit(Unarmed.Kübelwagen_82, "Kubel#", self.position.x - 35, self.position.y - 20,
self.heading)
# Some Opel Blitz trucks
for i in range(int(max(1,grid_x/2))):
for j in range(int(max(1,grid_x/2))):
self.add_unit(Unarmed.Blitz_3_6_6700A, "AAA#" + str(index),
self.position.x + 200 + 9*i,
self.position.y + 9*j, 90)
self.position.x + 200 + 15*i + random.randint(1,5),
self.position.y + 15*j + random.randint(1,5), 90)

View File

@@ -42,100 +42,19 @@ class TriggersGenerator:
self.conflict = conflict
self.game = game
def _gen_activation_trigger(self, radius: int, player_cp: ControlPoint, player_coalition: str, enemy_coalition: str):
conflict_distance = player_cp.position.distance_to_point(self.conflict.position)
minimum_radius = max(conflict_distance - TRIGGER_MIN_DISTANCE_FROM_START, TRIGGER_RADIUS_MINIMUM)
if minimum_radius < 0:
minimum_radius = 0
radius = min(minimum_radius, radius)
activation_trigger_zone = self.mission.triggers.add_triggerzone(
self.conflict.position,
radius,
name="Activation zone",
)
activation_trigger = TriggerOnce(Event.NoEvent, "Activation trigger")
activation_trigger.add_condition(PartOfCoalitionInZone(player_coalition, activation_trigger_zone.id))
activation_trigger.add_condition(FlagIsTrue())
activate_by_trigger = []
flag_id = 2
for coalition_name, coalition in self.mission.coalition.items():
for country in coalition.countries.values():
if coalition_name == enemy_coalition:
for plane_group in country.plane_group + country.helicopter_group:
plane_group.late_activation = True
self.delayed_trigger(
plane_group,
flag_id,
flag_lower_limit=180,
flag_upper_limit=2400,
)
flag_id += 1
for vehicle_group in country.vehicle_group:
vehicle_group.late_activation = True
activate_by_trigger.append(vehicle_group)
for group in activate_by_trigger:
activation_trigger.add_action(ActivateGroup(group.id))
self.mission.triggerrules.triggers.append(activation_trigger)
def _gen_push_trigger(self, player_cp: ControlPoint, player_coalition: str):
push_by_trigger = []
for coalition_name, coalition in self.mission.coalition.items():
for country in coalition.countries.values():
if coalition_name == player_coalition:
for group in country.plane_group + country.helicopter_group:
if group.task == AWACS.name or group.task == Refueling.name:
continue
push_by_trigger.append(group)
"""if not group.units[0].is_human():
regroup_heading = self.conflict.to_cp.position.heading_between_point(player_cp.position)
pos1 = group.position.point_from_heading(regroup_heading, REGROUP_ZONE_DISTANCE)
pos2 = group.position.point_from_heading(regroup_heading, REGROUP_ZONE_DISTANCE+5000)
w1 = group.add_waypoint(pos1, REGROUP_ALT)
w2 = group.add_waypoint(pos2, REGROUP_ALT)
group.points.remove(w1)
group.points.remove(w2)
group.points.insert(1, w2)
group.points.insert(1, w1)
w1.tasks.append(Silence(True))
switch_waypoint_task = ControlledTask(SwitchWaypoint(from_waypoint=3, to_waypoint=2))
switch_waypoint_task.start_if_user_flag(1, False)
w2.tasks.append(switch_waypoint_task)
group.points[3].tasks.append(Silence(False))
group.add_trigger_action(SwitchWaypoint(to_waypoint=4))"""
push_trigger = TriggerOnce(Event.NoEvent, "Push trigger")
for group in push_by_trigger:
for unit in group.units:
push_trigger.add_condition(UnitAltitudeHigherAGL(unit.id, PUSH_TRIGGER_ACTIVATION_AGL))
if not group.units[0].is_human():
push_trigger.add_action(AITaskPush(group.id, 1))
message_string = self.mission.string("Task force is in the air, proceed with the objective.")
push_trigger.add_action(MessageToAll(message_string, clearview=True))
push_trigger.add_action(SetFlagValue())
self.mission.triggerrules.triggers.append(push_trigger)
def _set_allegiances(self, player_coalition: str, enemy_coalition: str):
"""
Set airbase initial coalition
"""
for cp in self.game.theater.controlpoints:
if cp.is_global:
continue
self.mission.terrain.airport_by_id(cp.at.id).set_coalition(cp.captured and player_coalition or enemy_coalition)
def _set_skill(self, player_coalition: str, enemy_coalition: str):
"""
Set skill level for all aircraft in the mission
"""
for coalition_name, coalition in self.mission.coalition.items():
if coalition_name == player_coalition:
skill_level = self.game.settings.player_skill, self.game.settings.player_skill
@@ -153,53 +72,38 @@ class TriggersGenerator:
for vehicle_group in country.vehicle_group:
vehicle_group.set_skill(Skill(skill_level[1]))
def delayed_trigger(self, group, flag_id, flag_lower_limit, flag_upper_limit):
def _gen_markers(self):
"""
Create an activation trigger a randomized amount after the main activation occurs
:param group:
group to activate
:param flag_id:
ID of the flag to use
:param flag_lower_limit:
lower limit of what the random time can be (shouldn't be negative)
:param flag_upper_limit:
uopper limit of what the random time can be
:return:
N/A
Generate markers on F10 map for each existing objective
"""
trigger_one = TriggerOnce(Event.NoEvent, "Activation trigger")
trigger_one.add_condition(FlagEquals(1, 1))
trigger_one.add_action(SetFlagValue(flag_id, 1))
if self.game.settings.generate_marks:
mark_trigger = TriggerOnce(Event.NoEvent, "Marks generator")
mark_trigger.add_condition(TimeAfter(1))
v = 10
for cp in self.game.theater.controlpoints:
added = []
for ground_object in cp.ground_objects:
if ground_object.obj_name not in added:
zone = self.mission.triggers.add_triggerzone(ground_object.position, radius=10, hidden=True, name="MARK")
if cp.captured:
name = ground_object.obj_name + " [ALLY]"
else:
name = ground_object.obj_name + " [ENEMY]"
mark_trigger.add_action(MarkToAll(v, zone.id, String(name)))
v = v + 1
added.append(ground_object.obj_name)
self.mission.triggerrules.triggers.append(mark_trigger)
trigger_two = TriggerCondition()
trigger_two.add_condition(
TimeSinceFlag(
flag_id,
seconds=random.randint(
flag_lower_limit,
flag_upper_limit
)
)
)
trigger_two.add_action(ActivateGroup(group.id))
self.mission.triggerrules.triggers.append(trigger_one)
self.mission.triggerrules.triggers.append(trigger_two)
def generate(self, player_cp: ControlPoint, is_quick: bool, activation_trigger_radius: int, awacs_enabled: bool):
def generate(self):
player_coalition = self.game.player_country in db.BLUEFOR_FACTIONS and "blue" or "red"
enemy_coalition = player_coalition == "blue" and "red" or "blue"
self.mission.coalition["blue"].bullseye = {"x": self.conflict.position.x,
"y": self.conflict.position.y}
"y": self.conflict.position.y}
self.mission.coalition["red"].bullseye = {"x": self.conflict.position.x,
"y": self.conflict.position.y}
self._set_skill(player_coalition, enemy_coalition)
self._set_allegiances(player_coalition, enemy_coalition)
#if not is_quick:
# # TODO: waypoint parts of this should not be post-hacked but added in airgen
# self._gen_activation_trigger(activation_trigger_radius, player_cp, player_coalition, enemy_coalition)
# self._gen_push_trigger(player_cp, player_coalition)
self._gen_markers()

View File

@@ -1,19 +1,23 @@
from userdata import logging_config
# Logging setup
VERSION_STRING = "2.0RC9"
logging_config.init_logging(VERSION_STRING)
import logging
import os
import sys
from shutil import copyfile
import dcs
from PySide2 import QtWidgets
from PySide2.QtGui import QPixmap
from PySide2.QtWidgets import QApplication, QSplashScreen
from dcs import installation
from qt_ui import uiconstants
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from qt_ui.windows.QLiberationWindow import QLiberationWindow
from qt_ui.windows.preferences.QLiberationFirstStartWindow import QLiberationFirstStartWindow
from userdata import persistency, logging as logging_module, liberation_install
from userdata import liberation_install, persistency
if __name__ == "__main__":
@@ -23,9 +27,8 @@ if __name__ == "__main__":
with open("./resources/stylesheets/style.css") as stylesheet:
app.setStyleSheet(stylesheet.read())
# Logging setup
VERSION_STRING = "2.0RC6"
logging_module.setup_version_string(VERSION_STRING)
# Inject custom payload in pydcs framework
custom_payloads = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..\\resources\\customized_payloads")
@@ -80,6 +83,7 @@ if __name__ == "__main__":
logging.info("QT App terminated with status code : " + str(qt_execution_code))
logging.info("Attempt to restore original mission scripting file")
liberation_install.restore_original_mission_scripting()
sys.exit(qt_execution_code)
logging.info("QT process exited with code : " + str(qt_execution_code))
sys.exit(0)

View File

@@ -54,6 +54,7 @@ def load_icons():
ICONS["Terrain_Persian_Gulf"] = QPixmap("./resources/ui/terrain_pg.gif")
ICONS["Terrain_Nevada"] = QPixmap("./resources/ui/terrain_nevada.gif")
ICONS["Terrain_Normandy"] = QPixmap("./resources/ui/terrain_normandy.gif")
ICONS["Terrain_Channel"] = QPixmap("./resources/ui/terrain_channel.gif")
ICONS["Dawn"] = QPixmap("./resources/ui/daytime/dawn.png")
ICONS["Day"] = QPixmap("./resources/ui/daytime/day.png")
@@ -73,6 +74,8 @@ def load_icons():
ICONS[category] = QPixmap("./resources/ui/ground_assets/" + category + ".png")
ICONS[category + "_blue"] = QPixmap("./resources/ui/ground_assets/" + category + "_blue.png")
ICONS["destroyed"] = QPixmap("./resources/ui/ground_assets/destroyed.png")
ICONS["ship"] = QPixmap("./resources/ui/ground_assets/ship.png")
ICONS["ship_blue"] = QPixmap("./resources/ui/ground_assets/ship_blue.png")
ICONS["Generator"] = QPixmap("./resources/ui/misc/generator.png")
ICONS["Missile"] = QPixmap("./resources/ui/misc/missile.png")

View File

@@ -1,153 +0,0 @@
from PySide2.QtCore import QSortFilterProxyModel, Qt, QModelIndex
from PySide2.QtGui import QStandardItem, QStandardItemModel
from PySide2.QtWidgets import QComboBox, QCompleter
from game import Game
from gen import Conflict
from gen.flights.flight import FlightWaypoint
from theater import ControlPointType
class QPredefinedWaypointSelectionComboBox(QComboBox):
def __init__(self, game: Game, parent=None):
super(QPredefinedWaypointSelectionComboBox, self).__init__(parent)
self.game = game
self.setFocusPolicy(Qt.StrongFocus)
self.setEditable(True)
self.completer = QCompleter(self)
# always show all completions
self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
self.pFilterModel = QSortFilterProxyModel(self)
self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
self.completer.setPopup(self.view())
self.setCompleter(self.completer)
self.lineEdit().textEdited.connect(self.pFilterModel.setFilterFixedString)
self.completer.activated.connect(self.setTextIfCompleterIsClicked)
self.find_possible_waypoints()
def setModel(self, model):
super(QPredefinedWaypointSelectionComboBox, self).setModel(model)
self.pFilterModel.setSourceModel(model)
self.completer.setModel(self.pFilterModel)
def setModelColumn(self, column):
self.completer.setCompletionColumn(column)
self.pFilterModel.setFilterKeyColumn(column)
super(QPredefinedWaypointSelectionComboBox, self).setModelColumn(column)
def view(self):
return self.completer.popup()
def index(self):
return self.currentIndex()
def setTextIfCompleterIsClicked(self, text):
if text:
index = self.findText(text)
self.setCurrentIndex(index)
def get_selected_waypoints(self, include_all_from_same_location=False):
n = self.currentText()
first_waypoint = None
for w in self.wpts:
if w.pretty_name == n:
first_waypoint = w
break
if first_waypoint is None:
return []
waypoints = [first_waypoint]
if include_all_from_same_location:
for w in self.wpts:
if w is not first_waypoint and w.obj_name and w.obj_name == first_waypoint.obj_name:
waypoints.append(w)
return waypoints
def find_possible_waypoints(self):
self.wpts = []
model = QStandardItemModel()
i = 0
def add_model_item(i, model, name, wpt):
print(name)
item = QStandardItem(name)
model.setItem(i, 0, item)
self.wpts.append(wpt)
return i + 1
for cp in self.game.theater.controlpoints:
print(cp)
if cp.captured:
enemy_cp = [ecp for ecp in cp.connected_points if ecp.captured != cp.captured]
for ecp in enemy_cp:
pos = Conflict.frontline_position(self.game.theater, cp, ecp)[0]
wpt = FlightWaypoint(pos.x, pos.y, 800)
wpt.name = "Frontline " + cp.name + "/" + ecp.name + " [CAS]"
wpt.alt_type = "RADIO"
wpt.pretty_name = wpt.name
wpt.description = "Frontline"
i = add_model_item(i, model, wpt.pretty_name, wpt)
for cp in self.game.theater.controlpoints:
for ground_object in cp.ground_objects:
if not ground_object.is_dead and not ground_object.dcs_identifier == "AA":
wpt = FlightWaypoint(ground_object.position.x,ground_object.position.y, 0)
wpt.alt_type = "RADIO"
wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + ground_object.category + " #" + str(ground_object.object_id)
wpt.pretty_name = wpt.name
wpt.obj_name = ground_object.obj_name
wpt.targets.append(ground_object)
if cp.captured:
wpt.description = "Friendly Building"
else:
wpt.description = "Enemy Building"
i = add_model_item(i, model, wpt.pretty_name, wpt)
for cp in self.game.theater.controlpoints:
for ground_object in cp.ground_objects:
if not ground_object.is_dead and ground_object.dcs_identifier == "AA":
for g in ground_object.groups:
for j, u in enumerate(g.units):
wpt = FlightWaypoint(u.position.x, u.position.y, 0)
wpt.alt_type = "RADIO"
wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + u.type + " #" + str(j)
wpt.pretty_name = wpt.name
wpt.targets.append(u)
wpt.obj_name = ground_object.obj_name
if cp.captured:
wpt.description = "Friendly unit : " + u.type
else:
wpt.description = "Enemy unit : " + u.type
i = add_model_item(i, model, wpt.pretty_name, wpt)
for cp in self.game.theater.controlpoints:
wpt = FlightWaypoint(cp.position.x, cp.position.y, 0)
wpt.alt_type = "RADIO"
wpt.name = cp.name
if cp.captured:
wpt.description = "Position of " + cp.name + " [Friendly Airbase]"
else:
wpt.description = "Position of " + cp.name + " [Enemy Airbase]"
if cp.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP:
wpt.pretty_name = cp.name + " (Aircraft Carrier Group)"
elif cp.cptype == ControlPointType.LHA_GROUP:
wpt.pretty_name = cp.name + " (LHA Group)"
else:
wpt.pretty_name = cp.name + " (Airbase)"
i = add_model_item(i, model, wpt.pretty_name, wpt)
self.setModel(model)

View File

@@ -32,7 +32,7 @@ class QTopPanel(QFrame):
self.passTurnButton.setProperty("style", "btn-primary")
self.passTurnButton.clicked.connect(self.passTurn)
self.proceedButton = QPushButton("Proceed")
self.proceedButton = QPushButton("Mission Planning")
self.proceedButton.setIcon(CONST.ICONS["Proceed"])
self.proceedButton.setProperty("style", "btn-primary")
self.proceedButton.clicked.connect(self.proceed)

View File

@@ -0,0 +1,53 @@
from PySide2.QtCore import QSortFilterProxyModel, Qt
from PySide2.QtWidgets import QComboBox, QCompleter
class QFilteredComboBox(QComboBox):
def __init__(self, parent=None, include_targets=True, include_airbases=True,
include_frontlines=True, include_units=True, include_enemy=True, include_friendly=True):
super(QFilteredComboBox, self).__init__(parent)
self.setFocusPolicy(Qt.StrongFocus)
self.setEditable(True)
self.completer = QCompleter(self)
self.include_targets = include_targets
self.include_airbases = include_airbases
self.include_frontlines = include_frontlines
self.include_units = include_units
self.include_enemy = include_enemy
self.include_friendly = include_friendly
# always show all completions
self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
self.pFilterModel = QSortFilterProxyModel(self)
self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
self.completer.setPopup(self.view())
self.setCompleter(self.completer)
self.lineEdit().textEdited.connect(self.pFilterModel.setFilterFixedString)
self.completer.activated.connect(self.setTextIfCompleterIsClicked)
def setModel(self, model):
super(QFilteredComboBox, self).setModel(model)
self.pFilterModel.setSourceModel(model)
self.completer.setModel(self.pFilterModel)
def setModelColumn(self, column):
self.completer.setCompletionColumn(column)
self.pFilterModel.setFilterKeyColumn(column)
super(QFilteredComboBox, self).setModelColumn(column)
def view(self):
return self.completer.popup()
def index(self):
return self.currentIndex()
def setTextIfCompleterIsClicked(self, text):
if text:
index = self.findText(text)
self.setCurrentIndex(index)

View File

@@ -0,0 +1,139 @@
from PySide2.QtCore import QSortFilterProxyModel, Qt, QModelIndex
from PySide2.QtGui import QStandardItem, QStandardItemModel
from PySide2.QtWidgets import QComboBox, QCompleter
from game import Game
from gen import Conflict, FlightWaypointType
from gen.flights.flight import FlightWaypoint, PredefinedWaypointCategory
from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox
from theater import ControlPointType
class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
def __init__(self, game: Game, parent=None, include_targets=True, include_airbases=True,
include_frontlines=True, include_units=True, include_enemy=True, include_friendly=True):
super(QPredefinedWaypointSelectionComboBox, self).__init__(parent)
self.game = game
self.include_targets = include_targets
self.include_airbases = include_airbases
self.include_frontlines = include_frontlines
self.include_units = include_units
self.include_enemy = include_enemy
self.include_friendly = include_friendly
self.find_possible_waypoints()
def get_selected_waypoints(self, include_all_from_same_location=False):
n = self.currentText()
first_waypoint = None
for w in self.wpts:
if w.pretty_name == n:
first_waypoint = w
break
if first_waypoint is None:
return []
waypoints = [first_waypoint]
if include_all_from_same_location:
for w in self.wpts:
if w is not first_waypoint and w.obj_name and w.obj_name == first_waypoint.obj_name:
waypoints.append(w)
return waypoints
def find_possible_waypoints(self):
self.wpts = []
model = QStandardItemModel()
i = 0
def add_model_item(i, model, name, wpt):
print(name)
item = QStandardItem(name)
model.setItem(i, 0, item)
self.wpts.append(wpt)
return i + 1
if self.include_frontlines:
for cp in self.game.theater.controlpoints:
if cp.captured:
enemy_cp = [ecp for ecp in cp.connected_points if ecp.captured != cp.captured]
for ecp in enemy_cp:
pos = Conflict.frontline_position(self.game.theater, cp, ecp)[0]
wpt = FlightWaypoint(pos.x, pos.y, 800)
wpt.name = "Frontline " + cp.name + "/" + ecp.name + " [CAS]"
wpt.alt_type = "RADIO"
wpt.pretty_name = wpt.name
wpt.description = "Frontline"
wpt.data = [cp, ecp]
wpt.waypoint_type = FlightWaypointType.CUSTOM
wpt.category = PredefinedWaypointCategory.FRONTLINE
i = add_model_item(i, model, wpt.pretty_name, wpt)
if self.include_targets:
for cp in self.game.theater.controlpoints:
if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured):
for ground_object in cp.ground_objects:
if not ground_object.is_dead and not ground_object.dcs_identifier == "AA":
wpt = FlightWaypoint(ground_object.position.x,ground_object.position.y, 0)
wpt.alt_type = "RADIO"
wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + ground_object.category + " #" + str(ground_object.object_id)
wpt.pretty_name = wpt.name
wpt.obj_name = ground_object.obj_name
wpt.targets.append(ground_object)
wpt.data = ground_object
wpt.waypoint_type = FlightWaypointType.CUSTOM
if cp.captured:
wpt.description = "Friendly Building"
wpt.category = PredefinedWaypointCategory.ALLY_BUILDING
else:
wpt.description = "Enemy Building"
wpt.category = PredefinedWaypointCategory.ENEMY_BUILDING
i = add_model_item(i, model, wpt.pretty_name, wpt)
if self.include_units:
for cp in self.game.theater.controlpoints:
if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured):
for ground_object in cp.ground_objects:
if not ground_object.is_dead and ground_object.dcs_identifier == "AA":
for g in ground_object.groups:
for j, u in enumerate(g.units):
wpt = FlightWaypoint(u.position.x, u.position.y, 0)
wpt.alt_type = "RADIO"
wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + u.type + " #" + str(j)
wpt.pretty_name = wpt.name
wpt.targets.append(u)
wpt.data = u
wpt.obj_name = ground_object.obj_name
wpt.waypoint_type = FlightWaypointType.CUSTOM
if cp.captured:
wpt.description = "Friendly unit : " + u.type
wpt.category = PredefinedWaypointCategory.ALLY_UNIT
else:
wpt.description = "Enemy unit : " + u.type
wpt.category = PredefinedWaypointCategory.ENEMY_UNIT
i = add_model_item(i, model, wpt.pretty_name, wpt)
if self.include_airbases:
for cp in self.game.theater.controlpoints:
if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured):
wpt = FlightWaypoint(cp.position.x, cp.position.y, 0)
wpt.alt_type = "RADIO"
wpt.name = cp.name
wpt.data = cp
wpt.waypoint_type = FlightWaypointType.CUSTOM
if cp.captured:
wpt.description = "Position of " + cp.name + " [Friendly Airbase]"
wpt.category = PredefinedWaypointCategory.ALLY_CP
else:
wpt.description = "Position of " + cp.name + " [Enemy Airbase]"
wpt.category = PredefinedWaypointCategory.ENEMY_CP
if cp.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP:
wpt.pretty_name = cp.name + " (Aircraft Carrier Group)"
elif cp.cptype == ControlPointType.LHA_GROUP:
wpt.pretty_name = cp.name + " (LHA Group)"
else:
wpt.pretty_name = cp.name + " (Airbase)"
i = add_model_item(i, model, wpt.pretty_name, wpt)
self.setModel(model)

View File

@@ -0,0 +1,77 @@
from PySide2.QtGui import QStandardItem, QStandardItemModel
from game import Game
from game.data.radar_db import UNITS_WITH_RADAR
from gen import db
from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox
class SEADTargetInfo:
def __init__(self):
self.name = ""
self.location = None
self.radars = []
self.threat_range = 0
self.detection_range = 0
class QSEADTargetSelectionComboBox(QFilteredComboBox):
def __init__(self, game: Game, parent=None):
super(QSEADTargetSelectionComboBox, self).__init__(parent)
self.game = game
self.find_possible_sead_targets()
def get_selected_target(self) -> SEADTargetInfo:
n = self.currentText()
for target in self.targets:
if target.name == n:
return target
def find_possible_sead_targets(self):
self.targets = []
i = 0
model = QStandardItemModel()
def add_model_item(i, model, target):
item = QStandardItem(target.name)
model.setItem(i, 0, item)
self.targets.append(target)
return i + 1
for cp in self.game.theater.controlpoints:
if cp.captured: continue
for g in cp.ground_objects:
radars = []
detection_range = 0
threat_range = 0
if g.dcs_identifier == "AA":
for group in g.groups:
for u in group.units:
utype = db.unit_type_from_name(u.type)
if utype in UNITS_WITH_RADAR:
if hasattr(utype, "detection_range") and utype.detection_range > 1000:
if utype.detection_range > detection_range:
detection_range = utype.detection_range
radars.append(u)
if hasattr(utype, "threat_range"):
if utype.threat_range > threat_range:
threat_range = utype.threat_range
if len(radars) > 0:
tgt_info = SEADTargetInfo()
tgt_info.name = g.obj_name + " [" + ",".join([db.unit_type_from_name(u.type).id for u in radars]) + " ]"
if len(tgt_info.name) > 25:
tgt_info.name = g.obj_name + " [" + str(len(radars)) + " units]"
tgt_info.radars = radars
tgt_info.location = g
tgt_info.threat_range = threat_range
tgt_info.detection_range = detection_range
i = add_model_item(i, model, tgt_info)
self.setModel(model)

View File

@@ -0,0 +1,74 @@
from PySide2.QtGui import QStandardItem, QStandardItemModel
from game import Game
from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox
class StrikeTargetInfo:
def __init__(self):
self.name = ""
self.location = None
self.units = []
self.buildings = []
class QStrikeTargetSelectionComboBox(QFilteredComboBox):
def __init__(self, game: Game, parent=None):
super(QStrikeTargetSelectionComboBox, self).__init__(parent)
self.game = game
self.find_possible_strike_targets()
for t in self.targets:
print(t.name + " - " + str(len(t.units)) + " " + str(len(t.buildings)))
def get_selected_target(self) -> StrikeTargetInfo:
n = self.currentText()
for target in self.targets:
if target.name == n:
return target
def find_possible_strike_targets(self):
self.targets = []
i = 0
model = QStandardItemModel()
def add_model_item(i, model, target):
item = QStandardItem(target.name)
model.setItem(i, 0, item)
self.targets.append(target)
return i + 1
for cp in self.game.theater.controlpoints:
if cp.captured: continue
added_obj_names = []
for g in cp.ground_objects:
if g.obj_name in added_obj_names: continue
target = StrikeTargetInfo()
target.location = g
target.name = g.obj_name
if g.dcs_identifier == "AA":
target.name = g.obj_name + " [units]"
for group in g.groups:
for u in group.units:
target.units.append(u)
else:
target.name = g.obj_name + " [" + g.category + "]"
for g2 in cp.ground_objects:
if g2 is not g and g2.obj_name == g.obj_name:
target.buildings.append(g2)
i = add_model_item(i, model, target)
added_obj_names.append(g.obj_name)
self.setModel(model)

View File

@@ -1,6 +1,7 @@
import typing
from typing import Dict
from PySide2 import QtCore
from PySide2.QtCore import Qt, QRect, QPointF
from PySide2.QtGui import QPixmap, QBrush, QColor, QWheelEvent, QPen, QFont
from PySide2.QtWidgets import QGraphicsView, QFrame, QGraphicsOpacityEffect
@@ -9,6 +10,7 @@ from dcs.mapping import point_from_heading
import qt_ui.uiconstants as CONST
from game import Game, db
from game.data.radar_db import UNITS_WITH_RADAR
from game.event import UnitsDeliveryEvent, Event, ControlPointType
from gen import Conflict
from qt_ui.widgets.map.QLiberationScene import QLiberationScene
@@ -40,6 +42,8 @@ class QLiberationMap(QGraphicsView):
self.setMinimumSize(800,600)
self.setMaximumHeight(2160)
self._zoom = 0
self.factor = 1
self.factorized = 1
self.init_scene()
self.connectSignals()
self.setGame(game)
@@ -62,12 +66,59 @@ class QLiberationMap(QGraphicsView):
if self.game is not None:
self.reload_scene()
"""
Uncomment to set up theather reference points
def keyPressEvent(self, event):
#super(QLiberationMap, self).keyPressEvent(event)
numpad_mod = int(event.modifiers()) & QtCore.Qt.KeypadModifier
i = 0
for k,v in self.game.theater.reference_points.items():
if i == 0:
point_0 = k
else:
point_1 = k
i = i + 1
if event.key() == QtCore.Qt.Key_Down:
self.game.theater.reference_points[point_0] = self.game.theater.reference_points[point_0][0] + 100, self.game.theater.reference_points[point_0][1]
if event.key() == QtCore.Qt.Key_Up:
self.game.theater.reference_points[point_0] = self.game.theater.reference_points[point_0][0] - 100, self.game.theater.reference_points[point_0][1]
if event.key() == QtCore.Qt.Key_Left:
self.game.theater.reference_points[point_0] = self.game.theater.reference_points[point_0][0], self.game.theater.reference_points[point_0][1] + 100
if event.key() == QtCore.Qt.Key_Right:
self.game.theater.reference_points[point_0] = self.game.theater.reference_points[point_0][0], self.game.theater.reference_points[point_0][1] - 100
if event.key() == QtCore.Qt.Key_2 and numpad_mod:
self.game.theater.reference_points[point_1] = self.game.theater.reference_points[point_1][0] + 100, self.game.theater.reference_points[point_1][1]
if event.key() == QtCore.Qt.Key_8 and numpad_mod:
self.game.theater.reference_points[point_1] = self.game.theater.reference_points[point_1][0] - 100, self.game.theater.reference_points[point_1][1]
if event.key() == QtCore.Qt.Key_4 and numpad_mod:
self.game.theater.reference_points[point_1] = self.game.theater.reference_points[point_1][0], self.game.theater.reference_points[point_1][1] + 100
if event.key() == QtCore.Qt.Key_6 and numpad_mod:
self.game.theater.reference_points[point_1] = self.game.theater.reference_points[point_1][0], self.game.theater.reference_points[point_1][1] - 100
print(self.game.theater.reference_points)
self.reload_scene()
"""
def reload_scene(self):
scene = self.scene()
scene.clear()
self.addBackground()
#self.add_game_events()
# Uncomment below to help set up theater reference points
#for i, r in enumerate(self.game.theater.reference_points.items()):
# text = scene.addText(str(r), font=QFont("Trebuchet MS", 10, weight=5, italic=False))
# text.setPos(0, i * 24)
for cp in self.game.theater.controlpoints:
@@ -96,13 +147,16 @@ class QLiberationMap(QGraphicsView):
if ground_object.category == "aa" and self.get_display_rule("sam"):
max_range = 0
has_radar = False
if ground_object.groups:
for g in ground_object.groups:
for u in g.units:
unit = db.unit_type_from_name(u.type)
if unit in UNITS_WITH_RADAR:
has_radar = True
if unit.threat_range > max_range:
max_range = unit.threat_range
if max_range >= 6000:
if has_radar:
scene.addEllipse(go_pos[0] - max_range/300.0 + 8, go_pos[1] - max_range/300.0 + 8, max_range/150.0, max_range/150.0, pen, brush)
added_objects.append(ground_object.obj_name)
@@ -209,14 +263,21 @@ class QLiberationMap(QGraphicsView):
if event.angleDelta().y() > 0:
factor = 1.25
self._zoom += 1
if self._zoom < 10:
self.scale(factor, factor)
self.factorized *= factor
else:
self._zoom = 9
else:
factor = 0.8
self._zoom -= 1
if self._zoom > -5:
self.scale(factor, factor)
self.factorized *= factor
else:
self._zoom = -4
if self._zoom > -5:
self.scale(factor, factor)
else:
self._zoom = -5
#print(self.factorized, factor, self._zoom)
def _transform_point(self, p: Point, treshold=30) -> (int, int):
point_a = list(self.game.theater.reference_points.keys())[0]
@@ -246,56 +307,6 @@ class QLiberationMap(QGraphicsView):
return X > treshold and X or treshold, Y > treshold and Y or treshold
def add_game_events(self):
occupied_rects = []
for cp in self.game.theater.controlpoints:
point = self._transform_point(cp.position)
occupied_rects.append(QRect(point[0] - 16, point[1] - 16, 32, 48))
def _location_to_rect(location: Point) -> QRect:
nonlocal occupied_rects
point = self._transform_point(location)
rect = QRect(point[0] - 16, point[1] - 16, 32, 32)
i = 0
while True:
result = True
for occupied_rect in occupied_rects:
if rect.intersects(occupied_rect):
i += 1
if i % 2:
rect.setY(rect.y() + occupied_rect.height())
else:
rect.setX(rect.x() + occupied_rect.width())
result = False
break
if result:
break
occupied_rects.append(rect)
return rect
def _events_priority_key(event: Event) -> int:
priority_list = [InfantryTransportEvent, StrikeEvent, BaseAttackEvent, UnitsDeliveryEvent]
if type(event) not in priority_list:
return 0
else:
return priority_list.index(type(event)) + 1
scene = self.scene()
events = self.game.events
events.sort(key=_events_priority_key, reverse=True)
for event in events:
location = event.location
if type(event) in [FrontlineAttackEvent, FrontlinePatrolEvent, ConvoyStrikeEvent]:
location = self._frontline_center(event.from_cp, event.to_cp)
rect = _location_to_rect(location)
scene.addItem(QMapEvent(self, rect.x(), rect.y(), 32, 32, event))
def addBackground(self):
scene = self.scene()

View File

@@ -17,7 +17,7 @@ class QMapGroundObject(QGraphicsRectItem):
self.setAcceptHoverEvents(True)
self.setZValue(2)
self.buildings = buildings
#self.setFlag(QGraphicsItem.ItemIgnoresTransformations, True)
self.setFlag(QGraphicsItem.ItemIgnoresTransformations, False)
if len(self.model.groups) > 0:
units = {}
@@ -44,10 +44,15 @@ class QMapGroundObject(QGraphicsRectItem):
#super(QMapControlPoint, self).paint(painter, option, widget)
if self.parent.get_display_rule("go"):
painter.save()
cat = self.model.category
if cat == "aa" and self.model.sea_object:
cat = "ship"
if not self.model.is_dead and not self.cp.captured:
painter.drawPixmap(option.rect, CONST.ICONS[self.model.category])
painter.drawPixmap(option.rect, CONST.ICONS[cat])
elif not self.model.is_dead:
painter.drawPixmap(option.rect, CONST.ICONS[self.model.category + "_blue"])
painter.drawPixmap(option.rect, CONST.ICONS[cat + "_blue"])
else:
painter.drawPixmap(option.rect, CONST.ICONS["destroyed"])
painter.restore()

View File

@@ -0,0 +1,42 @@
from PySide2.QtGui import QStandardItemModel, QStandardItem
from PySide2.QtWidgets import QGroupBox, QVBoxLayout, QListView, QAbstractItemView
from qt_ui.widgets.combos.QSEADTargetSelectionComboBox import SEADTargetInfo
class QSeadTargetInfoView(QGroupBox):
"""
UI Component to display info about a sead target
"""
def __init__(self, sead_target_infos: SEADTargetInfo):
if sead_target_infos is None:
sead_target_infos = SEADTargetInfo()
super(QSeadTargetInfoView, self).__init__("Target : " + sead_target_infos.name)
self.sead_target_infos = sead_target_infos
self.radar_list = QListView()
self.init_ui()
def init_ui(self):
layout = QVBoxLayout(self)
layout.setSpacing(0)
layout.addWidget(self.radar_list)
self.setLayout(layout)
def setTarget(self, target: SEADTargetInfo):
self.setTitle(target.name)
self.sead_target_infos = target
radar_list_model = QStandardItemModel()
self.radar_list.setSelectionMode(QAbstractItemView.NoSelection)
for r in self.sead_target_infos.radars:
radar_list_model.appendRow(QStandardItem(r.type))
self.radar_list.setModel(radar_list_model)

View File

@@ -0,0 +1,75 @@
import random
from PySide2.QtGui import QStandardItemModel, QStandardItem
from PySide2.QtWidgets import QGroupBox, QLabel, QWidget, QVBoxLayout, QListView, QAbstractItemView
from qt_ui.widgets.combos.QStrikeTargetSelectionComboBox import StrikeTargetInfo
class QStrikeTargetInfoView(QGroupBox):
"""
UI Component to display info about a strike target
"""
def __init__(self, strike_target_infos: StrikeTargetInfo):
if strike_target_infos is None:
strike_target_infos = StrikeTargetInfo()
super(QStrikeTargetInfoView, self).__init__("Target : " + strike_target_infos.name)
self.strike_target_infos = strike_target_infos
self.listView = QListView()
self.init_ui()
def init_ui(self):
layout = QVBoxLayout(self)
layout.setSpacing(0)
layout.addWidget(self.listView)
self.setLayout(layout)
def setTarget(self, target):
self.setTitle(target.name)
self.strike_target_infos = target
model = QStandardItemModel()
self.listView.setSelectionMode(QAbstractItemView.NoSelection)
if len(self.strike_target_infos.units) > 0:
dic = {}
for u in self.strike_target_infos.units:
if u.type in dic.keys():
dic[u.type] = dic[u.type] + 1
else:
dic[u.type] = 1
for k,v in dic.items():
model.appendRow(QStandardItem(k + " x " + str(v)))
print(k + " x " + str(v))
else:
dic = {}
for b in self.strike_target_infos.buildings:
id = b.dcs_identifier
if b.is_dead:
id = id + "[Destroyed]"
if id in dic.keys():
dic[id] = dic[id] + 1
else:
dic[id] = 1
for k, v in dic.items():
model.appendRow(QStandardItem(k + " x " + str(v)))
print(k + " x " + str(v))
self.listView.setModel(model)

View File

@@ -1,3 +1,4 @@
import logging
import sys
import webbrowser
@@ -163,12 +164,12 @@ class QLiberationWindow(QMainWindow):
wizard.accepted.connect(lambda: self.onGameGenerated(wizard.generatedGame))
def saveGame(self):
print("Saving game")
logging.info("Saving game")
persistency.save_game(self.game)
GameUpdateSignal.get_instance().updateGame(self.game)
def onGameGenerated(self, game: Game):
print("On Game generated")
logging.info("On Game generated")
self.game = game
GameUpdateSignal.get_instance().updateGame(self.game)
@@ -198,7 +199,7 @@ class QLiberationWindow(QMainWindow):
about.setWindowTitle("About DCS Liberation")
about.setIcon(QMessageBox.Icon.Information)
about.setText(text)
print(about.textFormat())
logging.info(about.textFormat())
about.exec_()
def showLiberationDialog(self):
@@ -206,6 +207,6 @@ class QLiberationWindow(QMainWindow):
self.subwindow.show()
def onDebriefing(self, debrief: DebriefingSignal):
print("On Debriefing")
logging.info("On Debriefing")
self.debriefing = QDebriefingWindow(debrief.debriefing, debrief.gameEvent, debrief.game)
self.debriefing.show()

View File

@@ -8,8 +8,7 @@ from dcs.task import CAP, CAS
import qt_ui.uiconstants as CONST
from game import db, Game
from gen import namegen
from theater import start_generator, persiangulf, nevada, caucasus, ConflictTheater, normandy
from userdata.logging import version_string
from theater import start_generator, persiangulf, nevada, caucasus, ConflictTheater, normandy, thechannel
class NewGameWizard(QtWidgets.QWizard):
@@ -41,6 +40,9 @@ class NewGameWizard(QtWidgets.QWizard):
isTerrainCaucasusNorth= self.field("isTerrainCaucasusNorth")
isIranianCampaignTheater = self.field("isIranianCampaignTheater")
isTerrainNormandy = self.field("isTerrainNormandy")
isTerrainNormandySmall = self.field("isTerrainNormandySmall")
isTerrainChannel = self.field("isTerrainChannel")
isTerrainChannelComplete = self.field("isTerrainChannelComplete")
isTerrainEmirates = self.field("isTerrainEmirates")
timePeriod = db.TIME_PERIODS[list(db.TIME_PERIODS.keys())[self.field("timePeriod")]]
midGame = self.field("midGame")
@@ -65,6 +67,12 @@ class NewGameWizard(QtWidgets.QWizard):
conflicttheater = persiangulf.Emirates()
elif isTerrainNormandy:
conflicttheater = normandy.NormandyTheater()
elif isTerrainNormandySmall:
conflicttheater = normandy.NormandySmall()
elif isTerrainChannel:
conflicttheater = thechannel.ChannelTheater()
elif isTerrainChannelComplete:
conflicttheater = thechannel.ChannelTheaterComplete()
else:
conflicttheater = caucasus.CaucasusTheater()
@@ -100,7 +108,7 @@ class NewGameWizard(QtWidgets.QWizard):
game.budget = int(game.budget * multiplier)
game.settings.multiplier = multiplier
game.settings.sams = True
game.settings.version = version_string()
game.settings.version = "2.0RC9"
if midgame:
game.budget = game.budget * 4 * len(list(conflicttheater.conflicts()))
@@ -238,8 +246,14 @@ class TheaterConfiguration(QtWidgets.QWizardPage):
terrainEmirates.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Persian_Gulf"]))
terrainNttr = QtWidgets.QRadioButton("Nevada - North Nevada [RECOMMENDED]")
terrainNttr.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Nevada"]))
terrainNormandy = QtWidgets.QRadioButton("Normandy [Alpha]")
terrainNormandy = QtWidgets.QRadioButton("Normandy")
terrainNormandy.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Normandy"]))
terrainNormandySmall = QtWidgets.QRadioButton("Normandy Small")
terrainNormandySmall.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Normandy"]))
terrainChannel = QtWidgets.QRadioButton("The Channel : Start in Dunkirk")
terrainChannel.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Channel"]))
terrainChannelComplete = QtWidgets.QRadioButton("The Channel : Battle of Britain")
terrainChannelComplete.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Channel"]))
terrainCaucasusSmall.setChecked(True)
# Time Period
@@ -261,6 +275,9 @@ class TheaterConfiguration(QtWidgets.QWizardPage):
self.registerField('isTerrainEmirates', terrainEmirates)
self.registerField('isTerrainNttr', terrainNttr)
self.registerField('isTerrainNormandy', terrainNormandy)
self.registerField('isTerrainNormandySmall', terrainNormandySmall)
self.registerField('isTerrainChannel', terrainChannel)
self.registerField('isTerrainChannelComplete', terrainChannelComplete)
self.registerField('timePeriod', timePeriodSelect)
# Build layout
@@ -274,6 +291,9 @@ class TheaterConfiguration(QtWidgets.QWizardPage):
terrainGroupLayout.addWidget(terrainPg)
terrainGroupLayout.addWidget(terrainNttr)
terrainGroupLayout.addWidget(terrainNormandy)
terrainGroupLayout.addWidget(terrainNormandySmall)
terrainGroupLayout.addWidget(terrainChannelComplete)
terrainGroupLayout.addWidget(terrainChannel)
terrainGroup.setLayout(terrainGroupLayout)
timeGroupLayout = QtWidgets.QGridLayout()

View File

@@ -22,14 +22,14 @@ class QBaseDefenseGroupInfo(QGroupBox):
unit_dict[u.type] = 1
i = 0
for k, v in unit_dict.items():
icon = QLabel()
if k in VEHICLES_ICONS.keys():
icon.setPixmap(VEHICLES_ICONS[k])
else:
icon.setText("<b>" + k[:6] + "</b>")
icon.setProperty("style", "icon-plane")
layout.addWidget(icon, i, 0)
layout.addWidget(QLabel(str(v) + " x " + k), i, 1)
#icon = QLabel()
#if k in VEHICLES_ICONS.keys():
# icon.setPixmap(VEHICLES_ICONS[k])
#else:
# icon.setText("<b>" + k[:6] + "</b>")
#icon.setProperty("style", "icon-plane")
#layout.addWidget(icon, i, 0)
layout.addWidget(QLabel(str(v) + " x " + "<strong>" + k + "</strong>"), i, 1)
i = i + 1
self.setLayout(layout)

View File

@@ -1,4 +1,4 @@
from PySide2.QtWidgets import QGridLayout, QLabel, QGroupBox, QVBoxLayout
from PySide2.QtWidgets import QGridLayout, QLabel, QGroupBox, QVBoxLayout, QFrame
from game import db
from qt_ui.uiconstants import AIRCRAFT_ICONS, VEHICLES_ICONS
@@ -6,10 +6,10 @@ from qt_ui.windows.basemenu.base_defenses.QBaseDefenseGroupInfo import QBaseDefe
from theater import ControlPoint, Airport
class QBaseInformation(QGroupBox):
class QBaseInformation(QFrame):
def __init__(self, cp:ControlPoint, airport:Airport):
super(QBaseInformation, self).__init__("Base defenses")
super(QBaseInformation, self).__init__()
self.cp = cp
self.airport = airport
self.setMinimumWidth(500)

View File

@@ -16,7 +16,7 @@ class QChooseAirbase(QGroupBox):
self.depart_from_label = QLabel("Airbase : ")
self.depart_from = QComboBox()
for i, cp in enumerate([b for b in self.game.theater.controlpoints if b.captured]):
for i, cp in enumerate([b for b in self.game.theater.controlpoints if b.captured and b.id in self.game.planners]):
self.depart_from.addItem(str(cp.name), cp)
self.depart_from.setCurrentIndex(0)
self.depart_from.currentTextChanged.connect(self._on_airbase_selected)

View File

@@ -18,3 +18,9 @@ class QFlightItem(QStandardItem):
self.setText("["+str(self.flight.flight_type.name[:6])+"] "
+ str(self.flight.count) + " x " + db.unit_type_name(self.flight.unit_type)
+ " in " + str(self.flight.scheduled_in) + " minutes")
def update(self, flight):
self.flight = flight
self.setText("[" + str(self.flight.flight_type.name[:6]) + "] "
+ str(self.flight.count) + " x " + db.unit_type_name(self.flight.unit_type)
+ " in " + str(self.flight.scheduled_in) + " minutes")

View File

@@ -16,7 +16,7 @@ class QMissionPlanning(QDialog):
super(QMissionPlanning, self).__init__()
self.game = game
self.setWindowFlags(Qt.WindowStaysOnTopHint)
self.setMinimumSize(800, 420)
self.setMinimumSize(1000, 420)
self.setModal(True)
self.setWindowTitle("Mission Preparation")
self.setWindowIcon(EVENT_ICONS["strike"])
@@ -42,9 +42,11 @@ class QMissionPlanning(QDialog):
self.planned_flight_view.selectionModel().selectionChanged.connect(self.on_flight_selection_change)
if len(self.planned_flight_view.flight_planner.flights) > 0:
self.flight_planner = QFlightPlanner(self.planned_flight_view.flight_planner.flights[0], self.game, self.planned_flight_view.flight_planner)
self.flight_planner = QFlightPlanner(self.planned_flight_view.flight_planner.flights[0], self.game, self.planned_flight_view.flight_planner, 0)
self.flight_planner.on_planned_flight_changed.connect(self.update_planned_flight_view)
else:
self.flight_planner = QFlightPlanner(None, self.game, self.planned_flight_view.flight_planner)
self.flight_planner = QFlightPlanner(None, self.game, self.planned_flight_view.flight_planner, 0)
self.flight_planner.on_planned_flight_changed.connect(self.update_planned_flight_view)
self.add_flight_button = QPushButton("Add Flight")
self.add_flight_button.clicked.connect(self.on_add_flight)
@@ -88,7 +90,7 @@ class QMissionPlanning(QDialog):
print("On flight selection change")
index = self.planned_flight_view.selectionModel().currentIndex().row()
self.planned_flight_view.repaint();
self.planned_flight_view.repaint()
if self.flight_planner is not None:
self.flight_planner.clearTabs()
@@ -97,9 +99,12 @@ class QMissionPlanning(QDialog):
flight = self.planner.flights[index]
except IndexError:
flight = None
self.flight_planner = QFlightPlanner(flight, self.game, self.planner)
self.flight_planner = QFlightPlanner(flight, self.game, self.planner, self.flight_planner.currentIndex())
self.flight_planner.on_planned_flight_changed.connect(self.update_planned_flight_view)
self.layout.addWidget(self.flight_planner, 0, 1)
def update_planned_flight_view(self):
self.planned_flight_view.update_content()
def on_add_flight(self):
possible_aircraft_type = list(self.selected_cp.base.aircraft.keys())
@@ -124,10 +129,14 @@ class QMissionPlanning(QDialog):
def on_start(self):
# TODO : refactor this nonsense
self.gameEvent = None
for event in self.game.events:
if isinstance(event, FrontlineAttackEvent) and event.is_player_attacking:
self.gameEvent = event
if self.gameEvent is None:
self.gameEvent = FrontlineAttackEvent(self.game, self.game.theater.controlpoints[0], self.game.theater.controlpoints[0],
self.game.theater.controlpoints[0].position, self.game.player_name, self.game.enemy_name)
#if self.awacs_checkbox.isChecked() == 1:
# self.gameEvent.is_awacs_enabled = True
# self.game.awacs_expense_commit()

View File

@@ -12,14 +12,21 @@ class QPlannedFlightsView(QListView):
super(QPlannedFlightsView, self).__init__()
self.model = QStandardItemModel(self)
self.setModel(self.model)
self.flightitems = []
self.setIconSize(QSize(91, 24))
self.setSelectionBehavior(QAbstractItemView.SelectItems)
if flight_planner:
self.set_flight_planner(flight_planner)
def update_content(self, row=0):
def update_content(self):
for i, f in enumerate(self.flight_planner.flights):
self.model.appendRow(QFlightItem(f))
self.flightitems[i].update(f)
def setup_content(self, row=0):
for i, f in enumerate(self.flight_planner.flights):
item = QFlightItem(f)
self.model.appendRow(item)
self.flightitems.append(item)
self.setSelectedFlight(row)
self.repaint()
@@ -38,4 +45,4 @@ class QPlannedFlightsView(QListView):
self.clear_layout()
self.flight_planner = flight_planner
if self.flight_planner:
self.update_content(row)
self.setup_content(row)

View File

@@ -1,3 +1,4 @@
from PySide2.QtCore import Signal
from PySide2.QtWidgets import QTabWidget, QFrame, QGridLayout, QLabel
from gen.flights.flight import Flight
@@ -9,17 +10,25 @@ from qt_ui.windows.mission.flight.waypoints.QFlightWaypointTab import QFlightWay
class QFlightPlanner(QTabWidget):
def __init__(self, flight: Flight, game: Game, planner):
on_planned_flight_changed = Signal()
def __init__(self, flight: Flight, game: Game, planner, selected_tab):
super(QFlightPlanner, self).__init__()
print(selected_tab)
self.tabCount = 0
if flight:
self.general_settings_tab = QGeneralFlightSettingsTab(flight, game, planner)
self.general_settings_tab.on_flight_settings_changed.connect(lambda: self.on_planned_flight_changed.emit())
self.payload_tab = QFlightPayloadTab(flight, game)
self.waypoint_tab = QFlightWaypointTab(game, flight)
self.waypoint_tab.on_flight_changed.connect(lambda: self.on_planned_flight_changed.emit())
self.addTab(self.general_settings_tab, "General Flight settings")
self.addTab(self.payload_tab, "Payload")
self.addTab(self.waypoint_tab, "Waypoints")
self.tabCount = 3
self.setCurrentIndex(selected_tab)
else:
tabError = QFrame()
l = QGridLayout()
@@ -30,4 +39,4 @@ class QFlightPlanner(QTabWidget):
def clearTabs(self):
for i in range(self.tabCount):
self.removeTab(i)
self.removeTab(i)

View File

@@ -0,0 +1,43 @@
from PySide2.QtCore import Qt
from PySide2.QtWidgets import QDialog, QPushButton
from game import Game
from gen.flights.flight import Flight
from qt_ui.uiconstants import EVENT_ICONS
from qt_ui.windows.mission.flight.waypoints.QFlightWaypointInfoBox import QFlightWaypointInfoBox
class QAbstractMissionGenerator(QDialog):
def __init__(self, game: Game, flight: Flight, flight_waypoint_list, title):
super(QAbstractMissionGenerator, self).__init__()
self.game = game
self.flight = flight
self.setWindowFlags(Qt.WindowStaysOnTopHint)
self.setMinimumSize(400, 250)
self.setModal(True)
self.setWindowTitle(title)
self.setWindowIcon(EVENT_ICONS["strike"])
self.flight_waypoint_list = flight_waypoint_list
self.planner = self.game.planners[self.flight.from_cp.id]
self.selected_waypoints = []
self.wpt_info = QFlightWaypointInfoBox()
self.ok_button = QPushButton("Ok")
self.ok_button.clicked.connect(self.apply)
def on_select_wpt_changed(self):
self.selected_waypoints = self.wpt_selection_box.get_selected_waypoints(False)
if self.selected_waypoints is None or len(self.selected_waypoints) <= 0:
self.ok_button.setDisabled(True)
else:
self.wpt_info.set_flight_waypoint(self.selected_waypoints[0])
self.ok_button.setDisabled(False)
def apply(self):
raise NotImplementedError()

View File

@@ -0,0 +1,52 @@
from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout
from game import Game
from gen.flights.flight import Flight, PredefinedWaypointCategory
from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox
from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator
class QCAPMissionGenerator(QAbstractMissionGenerator):
def __init__(self, game: Game, flight: Flight, flight_waypoint_list):
super(QCAPMissionGenerator, self).__init__(game, flight, flight_waypoint_list, "CAP Generator")
self.wpt_selection_box = QPredefinedWaypointSelectionComboBox(self.game, self, False, True, True, False, False, True)
self.wpt_selection_box.setMinimumWidth(200)
self.wpt_selection_box.currentTextChanged.connect(self.on_select_wpt_changed)
self.init_ui()
self.on_select_wpt_changed()
def init_ui(self):
layout = QVBoxLayout()
wpt_layout = QHBoxLayout()
wpt_layout.addWidget(QLabel("CAP mission on : "))
wpt_layout.addWidget(self.wpt_selection_box)
wpt_layout.addStretch()
layout.addLayout(wpt_layout)
layout.addWidget(self.wpt_info)
layout.addStretch()
layout.addWidget(self.ok_button)
self.setLayout(layout)
def apply(self):
self.flight.points = []
wpt = self.selected_waypoints[0]
if wpt.category == PredefinedWaypointCategory.FRONTLINE:
self.planner.generate_frontline_cap(self.flight, wpt.data[0], wpt.data[1])
elif wpt.category == PredefinedWaypointCategory.ALLY_CP:
self.planner.generate_barcap(self.flight, wpt.data)
else:
return
self.flight_waypoint_list.update_list()
self.close()

View File

@@ -0,0 +1,65 @@
from PySide2.QtGui import Qt
from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout, QGroupBox
from dcs import Point
from game import Game
from game.utils import meter_to_nm
from gen.flights.flight import Flight
from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox
from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator
class QCASMissionGenerator(QAbstractMissionGenerator):
def __init__(self, game: Game, flight: Flight, flight_waypoint_list):
super(QCASMissionGenerator, self).__init__(game, flight, flight_waypoint_list, "CAS Generator")
self.wpt_selection_box = QPredefinedWaypointSelectionComboBox(self.game, self, False, False, True, False, False)
self.wpt_selection_box.setMinimumWidth(200)
self.wpt_selection_box.currentTextChanged.connect(self.on_select_wpt_changed)
self.distanceToTargetLabel = QLabel("0 nm")
self.init_ui()
self.on_select_wpt_changed()
def on_select_wpt_changed(self):
super(QCASMissionGenerator, self).on_select_wpt_changed()
wpts = self.wpt_selection_box.get_selected_waypoints()
if len(wpts) > 0:
self.distanceToTargetLabel.setText("~" + str(meter_to_nm(self.flight.from_cp.position.distance_to_point(Point(wpts[0].x, wpts[0].y)))) + " nm")
else:
self.distanceToTargetLabel.setText("??? nm")
def init_ui(self):
layout = QVBoxLayout()
wpt_layout = QHBoxLayout()
wpt_layout.addWidget(QLabel("CAS : "))
wpt_layout.addWidget(self.wpt_selection_box)
wpt_layout.addStretch()
distToTargetBox = QGroupBox("Infos :")
distToTarget = QHBoxLayout()
distToTarget.addWidget(QLabel("Distance to target : "))
distToTarget.addStretch()
distToTarget.addWidget(self.distanceToTargetLabel, alignment=Qt.AlignRight)
distToTargetBox.setLayout(distToTarget)
layout.addLayout(wpt_layout)
layout.addWidget(self.wpt_info)
layout.addWidget(distToTargetBox)
layout.addStretch()
layout.addWidget(self.ok_button)
self.setLayout(layout)
def apply(self):
self.flight.points = []
self.planner.generate_cas(self.flight, self.selected_waypoints[0].data[0], self.selected_waypoints[0].data[1])
self.flight_waypoint_list.update_list()
self.close()

View File

@@ -0,0 +1,84 @@
from PySide2.QtGui import Qt
from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout, QGroupBox
from game import Game
from game.utils import meter_to_nm
from gen.flights.flight import Flight
from qt_ui.widgets.combos.QSEADTargetSelectionComboBox import QSEADTargetSelectionComboBox
from qt_ui.widgets.views.QSeadTargetInfoView import QSeadTargetInfoView
from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator
class QSEADMissionGenerator(QAbstractMissionGenerator):
def __init__(self, game: Game, flight: Flight, flight_waypoint_list):
super(QSEADMissionGenerator, self).__init__(game, flight, flight_waypoint_list, "SEAD/DEAD Generator")
self.tgt_selection_box = QSEADTargetSelectionComboBox(self.game)
self.tgt_selection_box.setMinimumWidth(200)
self.tgt_selection_box.currentTextChanged.connect(self.on_selected_target_changed)
self.distanceToTargetLabel = QLabel("0 nm")
self.threatRangeLabel = QLabel("0 nm")
self.detectionRangeLabel = QLabel("0 nm")
self.seadTargetInfoView = QSeadTargetInfoView(None)
self.init_ui()
self.on_selected_target_changed()
def on_selected_target_changed(self):
target = self.tgt_selection_box.get_selected_target()
if target is not None:
self.distanceToTargetLabel.setText("~" + str(meter_to_nm(self.flight.from_cp.position.distance_to_point(target.location.position))) + " nm")
self.threatRangeLabel.setText(str(meter_to_nm(target.threat_range)) + " nm")
self.detectionRangeLabel.setText(str(meter_to_nm(target.detection_range)) + " nm")
self.seadTargetInfoView.setTarget(target)
def init_ui(self):
layout = QVBoxLayout()
wpt_layout = QHBoxLayout()
wpt_layout.addWidget(QLabel("SEAD/DEAD target : "))
wpt_layout.addStretch()
wpt_layout.addWidget(self.tgt_selection_box, alignment=Qt.AlignRight)
distThreatBox = QGroupBox("Infos :")
threatLayout = QVBoxLayout()
distToTarget = QHBoxLayout()
distToTarget.addWidget(QLabel("Distance to site : "))
distToTarget.addStretch()
distToTarget.addWidget(self.distanceToTargetLabel, alignment=Qt.AlignRight)
threatRangeLayout = QHBoxLayout()
threatRangeLayout.addWidget(QLabel("Site threat range : "))
threatRangeLayout.addStretch()
threatRangeLayout.addWidget(self.threatRangeLabel, alignment=Qt.AlignRight)
detectionRangeLayout = QHBoxLayout()
detectionRangeLayout.addWidget(QLabel("Site radar detection range: "))
detectionRangeLayout.addStretch()
detectionRangeLayout.addWidget(self.detectionRangeLabel, alignment=Qt.AlignRight)
threatLayout.addLayout(distToTarget)
threatLayout.addLayout(threatRangeLayout)
threatLayout.addLayout(detectionRangeLayout)
distThreatBox.setLayout(threatLayout)
layout.addLayout(wpt_layout)
layout.addWidget(self.seadTargetInfoView)
layout.addWidget(distThreatBox)
layout.addStretch()
layout.addWidget(self.ok_button)
self.setLayout(layout)
def apply(self):
self.flight.points = []
target = self.tgt_selection_box.get_selected_target()
self.planner.generate_sead(self.flight, target.location, target.radars)
self.flight_waypoint_list.update_list()
self.close()

View File

@@ -0,0 +1,64 @@
from PySide2.QtGui import Qt
from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout, QGroupBox
from game import Game
from game.utils import meter_to_nm
from gen.flights.flight import Flight
from qt_ui.widgets.combos.QStrikeTargetSelectionComboBox import QStrikeTargetSelectionComboBox
from qt_ui.widgets.views.QStrikeTargetInfoView import QStrikeTargetInfoView
from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator
class QSTRIKEMissionGenerator(QAbstractMissionGenerator):
def __init__(self, game: Game, flight: Flight, flight_waypoint_list):
super(QSTRIKEMissionGenerator, self).__init__(game, flight, flight_waypoint_list, "SEAD/DEAD Generator")
self.tgt_selection_box = QStrikeTargetSelectionComboBox(self.game)
self.tgt_selection_box.setMinimumWidth(200)
self.tgt_selection_box.currentTextChanged.connect(self.on_selected_target_changed)
self.distanceToTargetLabel = QLabel("0 nm")
self.strike_infos = QStrikeTargetInfoView(None)
self.init_ui()
self.on_selected_target_changed()
def on_selected_target_changed(self):
target = self.tgt_selection_box.get_selected_target()
self.distanceToTargetLabel.setText("~" + str(meter_to_nm(self.flight.from_cp.position.distance_to_point(target.location.position))) + " nm")
self.strike_infos.setTarget(target)
def init_ui(self):
layout = QVBoxLayout()
wpt_layout = QHBoxLayout()
wpt_layout.addWidget(QLabel("SEAD/DEAD target : "))
wpt_layout.addStretch()
wpt_layout.addWidget(self.tgt_selection_box, alignment=Qt.AlignRight)
distToTargetBox = QGroupBox("Infos :")
distToTarget = QHBoxLayout()
distToTarget.addWidget(QLabel("Distance to target : "))
distToTarget.addStretch()
distToTarget.addWidget(self.distanceToTargetLabel, alignment=Qt.AlignRight)
distToTargetBox.setLayout(distToTarget)
layout.addLayout(wpt_layout)
layout.addWidget(self.strike_infos)
layout.addWidget(distToTargetBox)
layout.addStretch()
layout.addWidget(self.ok_button)
self.setLayout(layout)
def apply(self):
self.flight.points = []
target = self.tgt_selection_box.get_selected_target()
self.planner.generate_strike(self.flight, target.location)
self.flight_waypoint_list.update_list()
self.close()

View File

@@ -16,7 +16,8 @@ class QLoadoutEditor(QGroupBox):
self.toggled.connect(self.on_toggle)
layout = QGridLayout()
hboxLayout = QVBoxLayout(self)
layout = QGridLayout(self)
pylons = [v for v in self.flight.unit_type.__dict__.values() if inspect.isclass(v) and v.__name__.startswith("Pylon")]
for i, pylon in enumerate(pylons):
@@ -25,7 +26,9 @@ class QLoadoutEditor(QGroupBox):
layout.addWidget(label, i, 0)
layout.addWidget(QPylonEditor(flight, pylon, i+1), i, 1)
self.setLayout(layout)
hboxLayout.addLayout(layout)
hboxLayout.addStretch()
self.setLayout(hboxLayout)
def on_toggle(self):
self.flight.use_custom_loadout = self.isChecked()

View File

@@ -24,5 +24,7 @@ class QFlightDepartureEditor(QGroupBox):
layout.addWidget(self.minutes)
self.setLayout(layout)
self.changed = self.departure_delta.valueChanged
def change_scheduled(self):
self.flight.scheduled_in = int(self.departure_delta.value())

View File

@@ -1,7 +1,8 @@
from PySide2.QtCore import Signal
from PySide2.QtWidgets import QFrame, QGridLayout, QVBoxLayout
from gen.flights.flight import Flight
from game import Game
from gen.flights.flight import Flight
from qt_ui.windows.mission.flight.settings.QFlightDepartureEditor import QFlightDepartureEditor
from qt_ui.windows.mission.flight.settings.QFlightSlotEditor import QFlightSlotEditor
from qt_ui.windows.mission.flight.settings.QFlightStartType import QFlightStartType
@@ -9,6 +10,7 @@ from qt_ui.windows.mission.flight.settings.QFlightTypeTaskInfo import QFlightTyp
class QGeneralFlightSettingsTab(QFrame):
on_flight_settings_changed = Signal()
def __init__(self, flight: Flight, game: Game, planner):
super(QGeneralFlightSettingsTab, self).__init__()
@@ -19,18 +21,19 @@ class QGeneralFlightSettingsTab(QFrame):
def init_ui(self):
layout = QGridLayout()
self.flight_info = QFlightTypeTaskInfo(self.flight)
self.flight_departure = QFlightDepartureEditor(self.flight)
self.flight_slots = QFlightSlotEditor(self.flight, self.game, self.planner)
self.flight_start_type = QFlightStartType(self.flight)
layout.addWidget(self.flight_info, 0, 0)
layout.addWidget(self.flight_departure, 1, 0)
layout.addWidget(self.flight_slots, 2, 0)
layout.addWidget(self.flight_start_type, 3, 0)
flight_info = QFlightTypeTaskInfo(self.flight)
flight_departure = QFlightDepartureEditor(self.flight)
flight_slots = QFlightSlotEditor(self.flight, self.game, self.planner)
flight_start_type = QFlightStartType(self.flight)
layout.addWidget(flight_info, 0, 0)
layout.addWidget(flight_departure, 1, 0)
layout.addWidget(flight_slots, 2, 0)
layout.addWidget(flight_start_type, 3, 0)
vstretch = QVBoxLayout()
vstretch.addStretch()
layout.addLayout(vstretch, 3, 0)
self.setLayout(layout)
self.flight_start_type.setEnabled(self.flight.client_count > 0)
self.flight_slots.changed.connect(lambda: self.flight_start_type.setEnabled(self.flight.client_count > 0))
flight_start_type.setEnabled(self.flight.client_count > 0)
flight_slots.changed.connect(lambda: flight_start_type.setEnabled(self.flight.client_count > 0))
flight_departure.changed.connect(lambda: self.on_flight_settings_changed.emit())

View File

@@ -10,6 +10,6 @@ class QWaypointItem(QStandardItem):
def __init__(self, point: FlightWaypoint, number):
super(QWaypointItem, self).__init__()
self.number = number
self.setText("%02d" % self.number + ' | ' + '{:<16}'.format(point.pretty_name))
self.setText('{:<16}'.format(point.pretty_name))
self.setEditable(False)

View File

@@ -1,17 +1,24 @@
from PySide2.QtCore import QItemSelectionModel, QPoint
from PySide2.QtGui import QStandardItemModel
from PySide2.QtWidgets import QListView
from PySide2.QtGui import QStandardItemModel, QStandardItem
from PySide2.QtWidgets import QTableView, QHeaderView
from game.utils import meter_to_feet
from gen.flights.flight import Flight, FlightWaypoint
from qt_ui.windows.mission.flight.waypoints.QFlightWaypointItem import QWaypointItem
class QFlightWaypointList(QListView):
class QFlightWaypointList(QTableView):
def __init__(self, flight: Flight):
super(QFlightWaypointList, self).__init__()
self.model = QStandardItemModel(self)
self.setModel(self.model)
self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.model.setHorizontalHeaderLabels(["Name", "Alt"])
header = self.horizontalHeader()
header.setSectionResizeMode(0, QHeaderView.ResizeToContents)
self.flight = flight
if len(self.flight.points) > 0:
@@ -26,10 +33,14 @@ class QFlightWaypointList(QListView):
def update_list(self):
self.model.clear()
self.model.setHorizontalHeaderLabels(["Name", "Alt"])
takeoff = FlightWaypoint(self.flight.from_cp.position.x, self.flight.from_cp.position.y, 0)
takeoff.description = "Take Off"
takeoff.name = takeoff.pretty_name = "Take Off from " + self.flight.from_cp.name
self.model.appendRow(QWaypointItem(takeoff, 0))
self.model.setItem(0, 1, QStandardItem("0 ft AGL"))
for i, point in enumerate(self.flight.points):
self.model.appendRow(QWaypointItem(point, i + 1))
self.model.insertRow(self.model.rowCount())
self.model.setItem(self.model.rowCount()-1, 0, QWaypointItem(point, i + 1))
self.model.setItem(self.model.rowCount()-1, 1, QStandardItem(str(meter_to_feet(point.alt)) + " ft " + str(["AGL" if point.alt_type == "RADIO" else "MSL"][0])))
self.selectionModel().setCurrentIndex(self.indexAt(QPoint(1, 1)), QItemSelectionModel.Select)

View File

@@ -1,16 +1,25 @@
from PySide2.QtCore import Signal
from PySide2.QtWidgets import QFrame, QGridLayout, QLabel, QPushButton, QVBoxLayout
from game import Game
from gen.flights.flight import Flight
from qt_ui.windows.mission.flight.generator.QCAPMissionGenerator import QCAPMissionGenerator
from qt_ui.windows.mission.flight.generator.QCASMissionGenerator import QCASMissionGenerator
from qt_ui.windows.mission.flight.generator.QSEADMissionGenerator import QSEADMissionGenerator
from qt_ui.windows.mission.flight.generator.QSTRIKEMissionGenerator import QSTRIKEMissionGenerator
from qt_ui.windows.mission.flight.waypoints.QFlightWaypointList import QFlightWaypointList
from qt_ui.windows.mission.flight.waypoints.QPredefinedWaypointSelectionWindow import QPredefinedWaypointSelectionWindow
from game import Game
class QFlightWaypointTab(QFrame):
on_flight_changed = Signal()
def __init__(self, game: Game, flight: Flight):
super(QFlightWaypointTab, self).__init__()
self.flight = flight
self.game = game
self.planner = self.game.planners[self.flight.from_cp.id]
self.init_ui()
def init_ui(self):
@@ -19,10 +28,44 @@ class QFlightWaypointTab(QFrame):
self.flight_waypoint_list = QFlightWaypointList(self.flight)
self.open_fast_waypoint_button = QPushButton("Add Waypoint")
self.open_fast_waypoint_button.clicked.connect(self.on_fast_waypoint)
self.cas_generator = QPushButton("Gen. CAS")
self.cas_generator.clicked.connect(self.on_cas_generator)
self.cap_generator = QPushButton("Gen. CAP")
self.cap_generator.clicked.connect(self.on_cap_generator)
self.sead_generator = QPushButton("Gen. SEAD/DEAD")
self.sead_generator.clicked.connect(self.on_sead_generator)
self.strike_generator = QPushButton("Gen. STRIKE")
self.strike_generator.clicked.connect(self.on_strike_generator)
self.rtb_waypoint = QPushButton("Add RTB Waypoint")
self.rtb_waypoint.clicked.connect(self.on_rtb_waypoint)
self.ascend_waypoint = QPushButton("Add Ascend Waypoint")
self.ascend_waypoint.clicked.connect(self.on_ascend_waypoint)
self.descend_waypoint = QPushButton("Add Descend Waypoint")
self.descend_waypoint.clicked.connect(self.on_descend_waypoint)
self.delete_selected = QPushButton("Delete Selected")
self.delete_selected.clicked.connect(self.on_delete_waypoint)
layout.addWidget(self.flight_waypoint_list,0,0)
layout.addWidget(self.flight_waypoint_list, 0, 0)
rlayout.addWidget(QLabel("<strong>Generator :</strong>"))
rlayout.addWidget(QLabel("<small>AI compatible</small>"))
rlayout.addWidget(self.cas_generator)
rlayout.addWidget(self.cap_generator)
rlayout.addWidget(self.sead_generator)
rlayout.addWidget(self.strike_generator)
rlayout.addWidget(QLabel("<strong>Advanced : </strong>"))
rlayout.addWidget(QLabel("<small>Do not use for AI flights</small>"))
rlayout.addWidget(self.ascend_waypoint)
rlayout.addWidget(self.descend_waypoint)
rlayout.addWidget(self.rtb_waypoint)
rlayout.addWidget(self.open_fast_waypoint_button)
rlayout.addWidget(self.delete_selected)
rlayout.addStretch()
@@ -34,7 +77,53 @@ class QFlightWaypointTab(QFrame):
if wpt > 0:
del self.flight.points[wpt-1]
self.flight_waypoint_list.update_list()
self.on_change()
def on_fast_waypoint(self):
self.subwindow = QPredefinedWaypointSelectionWindow(self.game, self.flight, self.flight_waypoint_list)
self.subwindow.show()
self.subwindow.finished.connect(self.on_change)
self.subwindow.show()
def on_ascend_waypoint(self):
ascend = self.planner.generate_ascend_point(self.flight.from_cp)
self.flight.points.append(ascend)
self.flight_waypoint_list.update_list()
self.on_change()
def on_rtb_waypoint(self):
rtb = self.planner.generate_rtb_waypoint(self.flight.from_cp)
self.flight.points.append(rtb)
self.flight_waypoint_list.update_list()
self.on_change()
def on_descend_waypoint(self):
descend = self.planner.generate_descend_point(self.flight.from_cp)
self.flight.points.append(descend)
self.flight_waypoint_list.update_list()
self.on_change()
def on_cas_generator(self):
self.subwindow = QCASMissionGenerator(self.game, self.flight, self.flight_waypoint_list)
self.subwindow.finished.connect(self.on_change)
self.subwindow.show()
def on_cap_generator(self):
self.subwindow = QCAPMissionGenerator(self.game, self.flight, self.flight_waypoint_list)
self.subwindow.finished.connect(self.on_change)
self.subwindow.show()
def on_sead_generator(self):
self.subwindow = QSEADMissionGenerator(self.game, self.flight, self.flight_waypoint_list)
self.subwindow.finished.connect(self.on_change)
self.subwindow.show()
def on_strike_generator(self):
self.subwindow = QSTRIKEMissionGenerator(self.game, self.flight, self.flight_waypoint_list)
self.subwindow.finished.connect(self.on_change)
self.subwindow.show()
def on_change(self):
self.flight_waypoint_list.update_list()
self.on_flight_changed.emit()

View File

@@ -1,13 +1,11 @@
from PySide2.QtCore import Qt
from PySide2.QtWidgets import QDialog, QGridLayout, QLabel, QComboBox, QHBoxLayout, QVBoxLayout, QPushButton, QCheckBox
from dcs import Point
from PySide2.QtWidgets import QDialog, QLabel, QHBoxLayout, QVBoxLayout, QPushButton, QCheckBox
from game import Game
from gen.flights.flight import Flight, FlightWaypoint
from gen.flights.flight import Flight
from qt_ui.uiconstants import EVENT_ICONS
from qt_ui.widgets.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox
from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox
from qt_ui.windows.mission.flight.waypoints.QFlightWaypointInfoBox import QFlightWaypointInfoBox
from theater import ControlPointType
PREDEFINED_WAYPOINT_CATEGORIES = [
"Frontline (CAS AREA)",

View File

@@ -1,12 +1,14 @@
from PySide2.QtCore import QSize, Qt, QItemSelectionModel, QPoint
from PySide2.QtGui import QStandardItemModel, QStandardItem
from PySide2.QtWidgets import QLabel, QDialog, QGridLayout, QListView, QStackedLayout, QComboBox, QWidget, \
QAbstractItemView, QPushButton, QGroupBox, QCheckBox, QVBoxLayout
QAbstractItemView, QPushButton, QGroupBox, QCheckBox, QVBoxLayout, QSpinBox
from dcs.forcedoptions import ForcedOptions
import qt_ui.uiconstants as CONST
from game.game import Game
from game.infos.information import Information
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from qt_ui.windows.finances.QFinancesMenu import QHorizontalSeparationLine
class QSettingsWindow(QDialog):
@@ -118,6 +120,32 @@ class QSettingsWindow(QDialog):
self.difficultyLayout.addWidget(QLabel("No night missions"), 4, 0)
self.difficultyLayout.addWidget(self.noNightMission, 4, 1, Qt.AlignRight)
self.mapVisibiitySelection = QComboBox()
self.mapVisibiitySelection.addItem("All", ForcedOptions.Views.All)
if self.game.settings.map_coalition_visibility == ForcedOptions.Views.All:
self.mapVisibiitySelection.setCurrentIndex(0)
self.mapVisibiitySelection.addItem("Fog of War", ForcedOptions.Views.Allies)
if self.game.settings.map_coalition_visibility == ForcedOptions.Views.Allies:
self.mapVisibiitySelection.setCurrentIndex(1)
self.mapVisibiitySelection.addItem("Allies Only", ForcedOptions.Views.OnlyAllies)
if self.game.settings.map_coalition_visibility == ForcedOptions.Views.OnlyAllies:
self.mapVisibiitySelection.setCurrentIndex(2)
self.mapVisibiitySelection.addItem("Own Aircraft Only", ForcedOptions.Views.MyAircraft)
if self.game.settings.map_coalition_visibility == ForcedOptions.Views.MyAircraft:
self.mapVisibiitySelection.setCurrentIndex(3)
self.mapVisibiitySelection.addItem("Map Only", ForcedOptions.Views.OnlyMap)
if self.game.settings.map_coalition_visibility == ForcedOptions.Views.OnlyMap:
self.mapVisibiitySelection.setCurrentIndex(4)
self.mapVisibiitySelection.currentIndexChanged.connect(self.applySettings)
self.difficultyLayout.addWidget(QLabel("Map visibility options"), 5, 0)
self.difficultyLayout.addWidget(self.mapVisibiitySelection, 5, 1, Qt.AlignRight)
self.ext_views = QCheckBox()
self.ext_views.setChecked(self.game.settings.external_views_allowed)
self.ext_views.toggled.connect(self.applySettings)
self.difficultyLayout.addWidget(QLabel("Allow external views"), 6, 0)
self.difficultyLayout.addWidget(self.ext_views, 6, 1, Qt.AlignRight)
def initGeneratorLayout(self):
self.generatorPage = QWidget()
@@ -134,8 +162,14 @@ class QSettingsWindow(QDialog):
self.supercarrier.setChecked(self.game.settings.supercarrier)
self.supercarrier.toggled.connect(self.applySettings)
self.generate_marks = QCheckBox()
self.generate_marks.setChecked(self.game.settings.generate_marks)
self.generate_marks.toggled.connect(self.applySettings)
self.gameplayLayout.addWidget(QLabel("Use Supercarrier Module"), 0, 0)
self.gameplayLayout.addWidget(self.supercarrier, 0, 1, Qt.AlignRight)
self.gameplayLayout.addWidget(QLabel("Put Objective Markers on Map"), 1, 0)
self.gameplayLayout.addWidget(self.generate_marks, 1, 1, Qt.AlignRight)
self.performance = QGroupBox("Performance")
self.performanceLayout = QGridLayout();
@@ -166,6 +200,16 @@ class QSettingsWindow(QDialog):
self.ai_parking_start.setChecked(self.game.settings.perf_ai_parking_start)
self.ai_parking_start.toggled.connect(self.applySettings)
self.culling = QCheckBox()
self.culling.setChecked(self.game.settings.perf_culling)
self.culling.toggled.connect(self.applySettings)
self.culling_distance = QSpinBox()
self.culling_distance.setMinimum(50)
self.culling_distance.setMaximum(10000)
self.culling_distance.setValue(self.game.settings.perf_culling_distance)
self.culling_distance.valueChanged.connect(self.applySettings)
self.performanceLayout.addWidget(QLabel("Smoke visual effect on frontline"), 0, 0)
self.performanceLayout.addWidget(self.smoke, 0, 1, alignment=Qt.AlignRight)
self.performanceLayout.addWidget(QLabel("SAM starts in RED alert mode"), 1, 0)
@@ -179,6 +223,12 @@ class QSettingsWindow(QDialog):
self.performanceLayout.addWidget(QLabel("AI planes parking start (AI starts in flight if disabled)"), 5, 0)
self.performanceLayout.addWidget(self.ai_parking_start, 5, 1, alignment=Qt.AlignRight)
self.performanceLayout.addWidget(QHorizontalSeparationLine(), 6, 0, 1, 2)
self.performanceLayout.addWidget(QLabel("Culling of distant units enabled"), 7, 0)
self.performanceLayout.addWidget(self.culling, 7, 1, alignment=Qt.AlignRight)
self.performanceLayout.addWidget(QLabel("Culling distance (km)"), 8, 0)
self.performanceLayout.addWidget(self.culling_distance, 8, 1, alignment=Qt.AlignRight)
self.generatorLayout.addWidget(self.gameplay)
self.generatorLayout.addWidget(QLabel("Disabling settings below may improve performance, but will impact the overall quality of the experience."))
self.generatorLayout.addWidget(self.performance)
@@ -229,6 +279,12 @@ class QSettingsWindow(QDialog):
self.game.settings.enemy_vehicle_skill = CONST.SKILL_OPTIONS[self.enemyAASkill.currentIndex()]
self.game.settings.labels = CONST.LABELS_OPTIONS[self.difficultyLabel.currentIndex()]
self.game.settings.night_disabled = self.noNightMission.isChecked()
self.game.settings.map_coalition_visibility = self.mapVisibiitySelection.currentData()
self.game.settings.external_views_allowed = self.ext_views.isChecked()
self.game.settings.generate_marks = self.generate_marks.isChecked()
print(self.game.settings.map_coalition_visibility)
self.game.settings.supercarrier = self.supercarrier.isChecked()
self.game.settings.perf_red_alert_state = self.red_alert.isChecked()
@@ -238,6 +294,9 @@ class QSettingsWindow(QDialog):
self.game.settings.perf_infantry = self.infantry.isChecked()
self.game.settings.perf_ai_parking_start = self.ai_parking_start.isChecked()
self.game.settings.perf_culling = self.culling.isChecked()
self.game.settings.perf_culling_distance = int(self.culling_distance.value())
GameUpdateSignal.get_instance().updateGame(self.game)
def onSelectionChanged(self):

View File

@@ -1,4 +1,4 @@
pydcs>=0.9.4
Pyside2>=5.13.0
pyinstaller==3.5
pyinstaller==3.6
pyproj==2.6.1.post1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 800 KiB

After

Width:  |  Height:  |  Size: 800 KiB

Some files were not shown because too many files have changed in this diff Show More