Compare commits

...

20 Commits

Author SHA1 Message Date
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
144 changed files with 3116 additions and 1148 deletions

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) * [Getting Started](https://github.com/Khopa/dcs_liberation/wiki/Getting-started)
* [Tutorials](https://github.com/Khopa/dcs_liberation/wiki/Tutorial-01-:-UI) * [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,73 @@
#2.0 RC 7 # 2.0 RC 7
## Features/Improvements :
##Features/Improvements :
* **[Units/Factions]** Added P-47D-30 for factions allies_1944 * **[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]** AI Flight generator has been reworked
* **[Mission Generator]** Add PP points for JF-17 on STRIKE missions * **[Mission Generator]** Add PP points for JF-17 on STRIKE missions
* **[Mission Generator]** Add ST point for F-14B 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]** 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]** Tacan for carrier will only be in Mode X from now
* **[Mission Generator]** RTB waypoints for autogenerated flights * **[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 buildings in info panel
* **[Info Panel]** Added information about destroyed units at SAM site 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 * **[Debriefing]** 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 destroyed buildings in the debriefing window
* **[Map]** Tooltip now contains the list of building for Strike targets on the map * **[Map]** Tooltip now contains the list of building for Strike targets on the map
* **[Map]** Added "Oil derrick" building * **[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]** 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]** 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 * **[Mission Generator]** Change power station template. (Buildings could end up superposed).
* **[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).
#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. Saves file from RC5 are not compatible with the new version.
Sorry :( 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]** 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]** 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 * **[Units/Factions]** Factions US 2005 / 1990 will now sometimes have Arleigh Burke class ships instead of Perry as carrier escorts
@@ -47,7 +81,7 @@ Sorry :(
* **[Mission Generator]** Using Late Activation & Trigger in attempt to improve performance & reduce stutter (Previously they were spawned through 'ETA' feature) * **[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 * **[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]** 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 "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]** Game generation does not work when the player selected faction has no AWACS

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.unittype import *
from dcs.unitgroup import * from dcs.unitgroup import *
from game.factions.bluefor_coldwar import BLUEFOR_COLDWAR
from game.factions.china_2000 import China_2000 from game.factions.china_2000 import China_2000
from game.factions.france_1995 import France_1995 from game.factions.france_1995 import France_1995
from game.factions.france_2005 import France_2005 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.germany_1990 import Germany_1990
from game.factions.insurgent import Insurgent from game.factions.insurgent import Insurgent
from game.factions.iran_2015 import Iran_2015 from game.factions.iran_2015 import Iran_2015
@@ -85,13 +87,13 @@ PRICES = {
JF_17: 20, JF_17: 20,
Su_30: 24, Su_30: 24,
SpitfireLFMkIX: 8, SpitfireLFMkIX: 14,
SpitfireLFMkIXCW: 8, SpitfireLFMkIXCW: 14,
Bf_109K_4: 8, Bf_109K_4: 14,
FW_190D9: 8, FW_190D9: 16,
FW_190A8: 8, FW_190A8: 14,
A_20G: 12, A_20G: 22,
Ju_88A4: 12, Ju_88A4: 24,
F_5E_3: 8, F_5E_3: 8,
MiG_15bis: 4, MiG_15bis: 4,
@@ -139,7 +141,7 @@ PRICES = {
OH_58D: 6, OH_58D: 6,
# Bombers # Bombers
B_52H: 25, B_52H: 35,
B_1B: 50, B_1B: 50,
# special # special
@@ -157,9 +159,10 @@ PRICES = {
C_130: 8, C_130: 8,
# WW2 # WW2
P_51D_30_NA: 6, P_51D_30_NA: 18,
P_51D: 6, P_51D: 16,
P_47D_30: 6, P_47D_30: 18,
B_17G: 30,
# armor # armor
Armor.APC_MTLB: 4, Armor.APC_MTLB: 4,
@@ -233,31 +236,30 @@ PRICES = {
AirDefence.SAM_Chaparral_M48: 10, AirDefence.SAM_Chaparral_M48: 10,
# WW2 # WW2
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G:18, Armor.MT_Pz_Kpfw_V_Panther_Ausf_G:24,
Armor.MT_Pz_Kpfw_IV_Ausf_H:8, Armor.MT_Pz_Kpfw_IV_Ausf_H:16,
Armor.HT_Pz_Kpfw_VI_Tiger_I:22, Armor.HT_Pz_Kpfw_VI_Tiger_I:24,
Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II:26, Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II:26,
Armor.TD_Jagdpanther_G1: 16, Armor.TD_Jagdpanther_G1: 18,
Armor.TD_Jagdpanzer_IV: 10, Armor.TD_Jagdpanzer_IV: 11,
Armor.Sd_Kfz_184_Elefant: 18, Armor.Sd_Kfz_184_Elefant: 18,
Armor.APC_Sd_Kfz_251:2, Armor.APC_Sd_Kfz_251:4,
Armor.IFV_Sd_Kfz_234_2_Puma:6, Armor.IFV_Sd_Kfz_234_2_Puma:8,
Armor.MT_M4_Sherman:5, Armor.MT_M4_Sherman:12,
Armor.MT_M4A4_Sherman_Firefly:8, Armor.MT_M4A4_Sherman_Firefly:16,
Armor.CT_Cromwell_IV:8, Armor.CT_Cromwell_IV:12,
Armor.M30_Cargo_Carrier:2, Armor.M30_Cargo_Carrier:2,
Armor.APC_M2A1:2, Armor.APC_M2A1:4,
Armor.ST_Centaur_IV: 8, Armor.ST_Centaur_IV: 10,
Armor.HIT_Churchill_VII: 12, Armor.HIT_Churchill_VII: 16,
Armor.LAC_M8_Greyhound: 4, Armor.LAC_M8_Greyhound: 8,
Armor.TD_M10_GMC: 8, Armor.TD_M10_GMC: 14,
Armor.StuG_III_Ausf__G: 6, Armor.StuG_III_Ausf__G: 12,
AirDefence.AAA_Bofors_40mm: 4, AirDefence.AAA_Bofors_40mm: 8,
AirDefence.AAA_8_8cm_Flak_36: 6, AirDefence.AAA_8_8cm_Flak_36: 8,
AirDefence.AAA_8_8cm_Flak_18: 4, AirDefence.AAA_8_8cm_Flak_18: 12,
Artillery.M12_GMC: 2, Artillery.M12_GMC: 10,
Artillery.Sturmpanzer_IV_Brummbär: 2, Artillery.Sturmpanzer_IV_Brummbär: 10,
# ship # ship
CV_1143_5_Admiral_Kuznetsov: 100, CV_1143_5_Admiral_Kuznetsov: 100,
@@ -348,6 +350,7 @@ UNIT_BY_TASK = {
A_20G, A_20G,
P_47D_30, P_47D_30,
Ju_88A4, Ju_88A4,
B_17G
], ],
Transport: [ Transport: [
IL_76MD, 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 country : DCS Country name
""" """
FACTIONS = { FACTIONS = {
@@ -609,14 +612,18 @@ FACTIONS = {
"China 2000": China_2000, "China 2000": China_2000,
"North Korea 2000": NorthKorea_2000, "North Korea 2000": NorthKorea_2000,
"Insurgent": Insurgent, "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 2005": USA_2005,
"USA 1990": USA_1990, "USA 1990": USA_1990,
"USA 1965": USA_1965, "USA 1965": USA_1965,
"USA 1960": USA_1960, "USA 1960": USA_1960,
"USA 1955 (Require WW2 Pack)": USA_1955, "USA 1955 (WW2 Pack)": USA_1955,
"Allies 1944 (Require WW2 Pack)": USA_1944, "Allies 1944 (WW2 Pack)": USA_1944,
"Bluefor Modern": BLUEFOR_MODERN,
"France 2005": France_2005, "France 2005": France_2005,
"France 1995": France_1995, "France 1995": France_1995,
"Germany 1990": Germany_1990, "Germany 1990": Germany_1990,
@@ -631,7 +638,7 @@ FACTIONS = {
"Israel 2000": Israel_2000, "Israel 2000": Israel_2000,
"Turkey 2005": Turkey_2005, "Turkey 2005": Turkey_2005,
"United Arab Emirates 2005": UAE_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"] BLUEFOR_FACTIONS = [FACTIONS[f]["country"] for f in FACTIONS if FACTIONS[f]["side"] == "blue"]
print(BLUEFOR_FACTIONS) print(BLUEFOR_FACTIONS)
@@ -733,6 +740,15 @@ PLANE_PAYLOAD_OVERRIDES = {
L_39C:COMMON_OVERRIDE, L_39C:COMMON_OVERRIDE,
Su_17M4: COMMON_OVERRIDE, Su_17M4: COMMON_OVERRIDE,
F_4E: 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:{ AH_64D:{
CAS: "AGM-114K*16" CAS: "AGM-114K*16"
@@ -1047,4 +1063,5 @@ class DefaultLiveries:
OH_58D.Liveries = DefaultLiveries OH_58D.Liveries = DefaultLiveries
F_16C_50.Liveries = DefaultLiveries F_16C_50.Liveries = DefaultLiveries
P_51D_30_NA.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) 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: if ally_units_alive == 0:
player_won = False player_won = False
delta = STRONG_DEFEAT_INFLUENCE delta = STRONG_DEFEAT_INFLUENCE
@@ -296,11 +298,21 @@ class Event:
else: else:
delta = DEFEAT_INFLUENCE delta = DEFEAT_INFLUENCE
elif ally_casualties > enemy_casualties: 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 delta = STRONG_DEFEAT_INFLUENCE
else: 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 # No progress with defensive strategies
if player_won and cp.stances[enemy_cp.id] in [CombatStance.DEFENSIVE, CombatStance.AMBUSH]: 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) enemy_cp.base.affect_strength(delta)
cp.base.affect_strength(-delta) cp.base.affect_strength(-delta)
info = Information("Frontline Report", 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.turn)
self.game.informations.append(info) 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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
BLUEFOR_MODERN = { BLUEFOR_MODERN = {
"country": "USA", "country": "USA",
@@ -31,6 +31,8 @@ BLUEFOR_MODERN = {
UH_1H, UH_1H,
AH_64D, AH_64D,
Ka_50, Ka_50,
SA342M,
SA342L,
Armor.MBT_M1A2_Abrams, Armor.MBT_M1A2_Abrams,
Armor.MBT_Leopard_2, Armor.MBT_Leopard_2,
@@ -74,5 +76,7 @@ BLUEFOR_MODERN = {
"LHA-3 Belleau Wood", "LHA-3 Belleau Wood",
"LHA-4 Nassau", "LHA-4 Nassau",
"LHA-5 Peleliu" "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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
China_2000 = { China_2000 = {
"country": "China", "country": "China",
@@ -59,5 +59,7 @@ China_2000 = {
], "carrier_names": [ ], "carrier_names": [
"001 Liaoning", "001 Liaoning",
"002 Shandong", "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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
France_1995 = { France_1995 = {
"country": "France", "country": "France",
@@ -40,5 +40,7 @@ France_1995 = {
], "shorad": [ ], "shorad": [
AirDefence.HQ_7_Self_Propelled_LN, AirDefence.HQ_7_Self_Propelled_LN,
AirDefence.SAM_Roland_ADS 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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
France_2005 = { France_2005 = {
"country": "France", "country": "France",
@@ -55,5 +55,7 @@ France_2005 = {
"L9013 Mistral", "L9013 Mistral",
"L9014 Tonerre", "L9014 Tonerre",
"L9015 Dixmude" "L9015 Dixmude"
], "boat":[
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
] ]
} }

View File

@@ -1,6 +1,9 @@
from dcs.planes import * from dcs.planes import *
from dcs.vehicles import * from dcs.vehicles import *
from game.data.building_data import WW2_GERMANY_BUILDINGS
from game.data.doctrine import WWII_DOCTRINE
Germany_1944 = { Germany_1944 = {
"country": "Third Reich", "country": "Third Reich",
"side": "red", "side": "red",
@@ -30,7 +33,13 @@ Germany_1944 = {
Infantry.Infantry_Mauser_98, Infantry.Infantry_Mauser_98,
AirDefence.AAA_8_8cm_Flak_36, AirDefence.AAA_8_8cm_Flak_36,
], ],
"shorad":[ "shorad": [
AirDefence.AAA_8_8cm_Flak_36, 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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Germany_1990 = { Germany_1990 = {
"country": "Germany", "country": "Germany",
@@ -39,5 +39,7 @@ Germany_1990 = {
"shorad":[ "shorad":[
AirDefence.SPAAA_Gepard, AirDefence.SPAAA_Gepard,
AirDefence.SAM_Roland_ADS, 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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
India_2010 = { India_2010 = {
"country": "India", "country": "India",
@@ -49,5 +49,7 @@ India_2010 = {
FSG_1241_1MP_Molniya, FSG_1241_1MP_Molniya,
], "carrier_names": [ ], "carrier_names": [
"INS Vikramaditya" "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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Iran_2015 = { Iran_2015 = {
"country": "Iran", "country": "Iran",
@@ -52,5 +52,7 @@ Iran_2015 = {
"shorad":[ "shorad":[
AirDefence.HQ_7_Self_Propelled_LN, AirDefence.HQ_7_Self_Propelled_LN,
AirDefence.AAA_ZU_23_Insurgent_Closed 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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Israel_2000 = { Israel_2000 = {
"country": "Israel", "country": "Israel",
@@ -31,7 +31,9 @@ Israel_2000 = {
LHA_1_Tarawa, LHA_1_Tarawa,
Armed_speedboat, Armed_speedboat,
], ],
"shorad":[ "shorad": [
AirDefence.SAM_Avenger_M1097 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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Italy_1990 = { Italy_1990 = {
"country": "Italy", "country": "Italy",
@@ -42,5 +42,7 @@ Italy_1990 = {
], "lha_names": [ ], "lha_names": [
"Giuseppe Garibaldi", "Giuseppe Garibaldi",
"Cavour", "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.helicopters import *
from dcs.planes import *
from dcs.vehicles import *
Lybia_2011 = { Lybia_2011 = {
"country": "Russia", "country": "Russia",
@@ -44,5 +43,7 @@ Lybia_2011 = {
"shorad":[ "shorad":[
AirDefence.HQ_7_Self_Propelled_LN, AirDefence.HQ_7_Self_Propelled_LN,
AirDefence.SAM_SA_8_Osa_9A33, 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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Netherlands_1990 = { Netherlands_1990 = {
"country": "The Netherlands", "country": "The Netherlands",
@@ -30,7 +30,9 @@ Netherlands_1990 = {
LHA_1_Tarawa, LHA_1_Tarawa,
Armed_speedboat, Armed_speedboat,
], ],
"shorad":[ "shorad": [
AirDefence.SAM_Avenger_M1097 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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
NorthKorea_2000 = { NorthKorea_2000 = {
"country": "North Korea", "country": "North Korea",
@@ -46,5 +46,8 @@ NorthKorea_2000 = {
"shorad":[ "shorad":[
AirDefence.AAA_ZU_23_Emplacement, AirDefence.AAA_ZU_23_Emplacement,
AirDefence.SPAAA_ZSU_23_4_Shilka 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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Pakistan_2015 = { Pakistan_2015 = {
"country": "Pakistan", "country": "Pakistan",
@@ -34,5 +34,7 @@ Pakistan_2015 = {
AirDefence.HQ_7_Self_Propelled_LN, AirDefence.HQ_7_Self_Propelled_LN,
AirDefence.AAA_ZU_23_Insurgent_on_Ural_375, AirDefence.AAA_ZU_23_Insurgent_on_Ural_375,
AirDefence.AAA_ZU_23_Closed AirDefence.AAA_ZU_23_Closed
], "boat": [
"Type54GroupGenerator", "OliverHazardPerryGroupGenerator"
] ]
} }

View File

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

View File

@@ -65,5 +65,7 @@ Russia_1975 = {
FF_1135M_Rezky, FF_1135M_Rezky,
], "cruiser": [ ], "cruiser": [
CGN_1144_2_Pyotr_Velikiy, 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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Russia_1990 = { Russia_1990 = {
"country": "Russia", "country": "Russia",
@@ -64,5 +64,7 @@ Russia_1990 = {
FF_1135M_Rezky, FF_1135M_Rezky,
], "cruiser": [ ], "cruiser": [
FSG_1241_1MP_Molniya, 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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Russia_2010 = { Russia_2010 = {
"country": "Russia", "country": "Russia",
@@ -63,5 +63,7 @@ Russia_2010 = {
FF_1135M_Rezky, FF_1135M_Rezky,
], "cruiser": [ ], "cruiser": [
FSG_1241_1MP_Molniya, 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.planes import *
from dcs.ships import *
from dcs.vehicles import *
Spain_1990 = { Spain_1990 = {
"country": "Spain", "country": "Spain",
@@ -44,5 +44,7 @@ Spain_1990 = {
"Principe de Asturias", "Principe de Asturias",
], "lhanames": [ ], "lhanames": [
"Juan Carlos I", "Juan Carlos I",
], "boat":[
"OliverHazardPerryGroupGenerator"
] ]
} }

View File

@@ -25,7 +25,7 @@ Sweden_1990 = {
Dry_cargo_ship_Ivanov, Dry_cargo_ship_Ivanov,
Tanker_Elnya_160, Tanker_Elnya_160,
], ],
"shorad":[ "shorad": [
AirDefence.SAM_Avenger_M1097 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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Turkey_2005 = { Turkey_2005 = {
"country": "Turkey", "country": "Turkey",
@@ -36,5 +36,7 @@ Turkey_2005 = {
], "shorad":[ ], "shorad":[
AirDefence.AAA_ZU_23_Emplacement, AirDefence.AAA_ZU_23_Emplacement,
AirDefence.SPAAA_ZSU_23_4_Shilka 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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
UAE_2005 = { UAE_2005 = {
"country": "United Arab Emirates", "country": "United Arab Emirates",
@@ -30,5 +30,7 @@ UAE_2005 = {
CVN_74_John_C__Stennis, CVN_74_John_C__Stennis,
LHA_1_Tarawa, LHA_1_Tarawa,
Armed_speedboat, 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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
UnitedKingdom_1990 = { UnitedKingdom_1990 = {
"country": "UK", "country": "UK",
@@ -46,5 +46,7 @@ UnitedKingdom_1990 = {
"HMS Invincible", "HMS Invincible",
"HMS Illustrious", "HMS Illustrious",
"HMS Ark Royal", "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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Ukraine_2010 = { Ukraine_2010 = {
"country": "Ukraine", "country": "Ukraine",
@@ -47,5 +47,7 @@ Ukraine_2010 = {
AirDefence.SAM_SA_19_Tunguska_2S6, AirDefence.SAM_SA_19_Tunguska_2S6,
AirDefence.SAM_SA_13_Strela_10M3_9A35M3, AirDefence.SAM_SA_13_Strela_10M3_9A35M3,
AirDefence.AAA_ZU_23_on_Ural_375 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.ships import *
from dcs.vehicles import * from dcs.vehicles import *
from game.data.building_data import WW2_ALLIES_BUILDINGS
from game.data.doctrine import WWII_DOCTRINE
USA_1944 = { USA_1944 = {
"country": "USA", "country": "USA",
"side": "blue", "side": "blue",
@@ -12,6 +15,7 @@ USA_1944 = {
SpitfireLFMkIX, SpitfireLFMkIX,
SpitfireLFMkIXCW, SpitfireLFMkIXCW,
A_20G, A_20G,
B_17G,
Armor.MT_M4_Sherman, Armor.MT_M4_Sherman,
Armor.MT_M4A4_Sherman_Firefly, Armor.MT_M4A4_Sherman_Firefly,
@@ -36,5 +40,9 @@ USA_1944 = {
AirDefence.AAA_Bofors_40mm, AirDefence.AAA_Bofors_40mm,
], "shorad":[ ], "shorad":[
AirDefence.AAA_Bofors_40mm, 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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
USA_1965 = { USA_1965 = {
"country": "USA", "country": "USA",
@@ -36,5 +36,7 @@ USA_1965 = {
"shorad":[ "shorad":[
AirDefence.AAA_Vulcan_M163, AirDefence.AAA_Vulcan_M163,
AirDefence.SAM_Chaparral_M48 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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
USA_1990 = { USA_1990 = {
"country": "USA", "country": "USA",
@@ -59,5 +59,7 @@ USA_1990 = {
"LHA-3 Belleau Wood", "LHA-3 Belleau Wood",
"LHA-4 Nassau", "LHA-4 Nassau",
"LHA-5 Peleliu" "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.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
USA_2005 = { USA_2005 = {
"country": "USA", "country": "USA",
@@ -51,7 +51,6 @@ USA_2005 = {
], "helicopter_carrier": [ ], "helicopter_carrier": [
LHA_1_Tarawa, LHA_1_Tarawa,
], "destroyer": [ ], "destroyer": [
Oliver_Hazzard_Perry_class,
USS_Arleigh_Burke_IIa, USS_Arleigh_Burke_IIa,
], "cruiser": [ ], "cruiser": [
Ticonderoga_class, Ticonderoga_class,
@@ -66,5 +65,7 @@ USA_2005 = {
"LHA-3 Belleau Wood", "LHA-3 Belleau Wood",
"LHA-4 Nassau", "LHA-4 Nassau",
"LHA-5 Peleliu" "LHA-5 Peleliu"
], "boat":[
"ArleighBurkeGroupGenerator"
] ]
} }

View File

@@ -1,6 +1,6 @@
from datetime import datetime, timedelta 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 game.game_stats import GameStats
from gen.flights.ai_flight_planner import FlightPlanner from gen.flights.ai_flight_planner import FlightPlanner
from gen.ground_forces.ai_ground_planner import GroundPlanner from gen.ground_forces.ai_ground_planner import GroundPlanner
@@ -72,6 +72,16 @@ class Game:
self.ground_planners = {} self.ground_planners = {}
self.informations = [] self.informations = []
self.informations.append(Information("Game Start", "-" * 40, 0)) 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): def _roll(self, prob, mult):
if self.settings.version == "dev": if self.settings.version == "dev":
@@ -144,7 +154,7 @@ class Game:
self.events.remove(event) self.events.remove(event)
def initiate_event(self, event: Event): def initiate_event(self, event: Event):
assert event in self.events #assert event in self.events
logging.info("Generating {} (regular)".format(event)) logging.info("Generating {} (regular)".format(event))
event.generate() event.generate()
@@ -178,6 +188,7 @@ class Game:
return 1 return 1
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint] = None): def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint] = None):
logging.info("Pass turn") logging.info("Pass turn")
self.informations.append(Information("End of turn #" + str(self.turn), "-" * 40, 0)) self.informations.append(Information("End of turn #" + str(self.turn), "-" * 40, 0))
self.turn = self.turn + 1 self.turn = self.turn + 1
@@ -193,7 +204,7 @@ class Game:
self._enemy_reinforcement() self._enemy_reinforcement()
self._budget_player() self._budget_player()
if not no_action: if not no_action and self.turn > 1:
for cp in self.theater.player_points(): for cp in self.theater.player_points():
cp.base.affect_strength(+PLAYER_BASE_STRENGTH_RECOVERY) cp.base.affect_strength(+PLAYER_BASE_STRENGTH_RECOVERY)
else: else:
@@ -212,6 +223,7 @@ class Game:
self.game_stats.update(self) self.game_stats.update(self)
# Plan flights & combat for next turn # Plan flights & combat for next turn
self.__culling_points = self.compute_conflicts_position()
self.planners = {} self.planners = {}
self.ground_planners = {} self.ground_planners = {}
for cp in self.theater.controlpoints: for cp in self.theater.controlpoints:
@@ -317,3 +329,49 @@ class Game:
""" """
self.current_group_id += 1 self.current_group_id += 1
return self.current_group_id 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): 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 # Generate ground object first
self.groundobjectgen.generate() self.groundobjectgen.generate()
@@ -156,17 +162,7 @@ class Operation:
cp = self.conflict.from_cp cp = self.conflict.from_cp
else: else:
cp = self.conflict.to_cp cp = self.conflict.to_cp
self.triggersgen.generate()
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)
# options # options
self.forcedoptionsgen.generate() self.forcedoptionsgen.generate()

View File

@@ -9,8 +9,10 @@ class Settings:
labels = "Full" labels = "Full"
only_player_takeoff = True # Legacy parameter do not use only_player_takeoff = True # Legacy parameter do not use
night_disabled = False night_disabled = False
external_views_allowed = True
supercarrier = False supercarrier = False
multiplier = 1 multiplier = 1
generate_marks = True
sams = True # Legacy parameter do not use sams = True # Legacy parameter do not use
cold_start = False # Legacy parameter do not use cold_start = False # Legacy parameter do not use
version = None version = None
@@ -23,4 +25,8 @@ class Settings:
perf_infantry = True perf_infantry = True
perf_ai_parking_start = 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 dcs.triggers import TriggerOnce, Event
from game.settings import Settings 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 gen.flights.flight import Flight, FlightType, FlightWaypointType
from .conflictgen import * from .conflictgen import *
from .naming 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_AIRSPEED = 120
WARM_START_HELI_ALT = 500 WARM_START_HELI_ALT = 500
WARM_START_ALTITUDE = 3000 WARM_START_ALTITUDE = 3000
WARM_START_AIRSPEED = 550 WARM_START_AIRSPEED = 550
INTERCEPTION_AIRSPEED = 1000 CAP_DURATION = 30 # minutes
BARCAP_RACETRACK_DISTANCE = 20000
ATTACK_CIRCLE_ALT = 1000
ATTACK_CIRCLE_DURATION = 15
CAS_ALTITUDE = 800
RTB_ALTITUDE = 800 RTB_ALTITUDE = 800
RTB_DISTANCE = 5000 RTB_DISTANCE = 5000
HELI_ALT = 500 HELI_ALT = 500
TRANSPORT_LANDING_ALT = 2000
DEFENCE_ENGAGEMENT_MAX_DISTANCE = 60000
INTERCEPT_MAX_DISTANCE = 200000
class AircraftConflictGenerator: class AircraftConflictGenerator:
escort_targets = [] # type: typing.List[typing.Tuple[FlyingGroup, int]] escort_targets = [] # type: typing.List[typing.Tuple[FlyingGroup, int]]
@@ -56,11 +40,15 @@ class AircraftConflictGenerator:
did_load_loadout = False did_load_loadout = False
unit_type = group.units[0].unit_type unit_type = group.units[0].unit_type
print("SETUP GROUP : " + str(for_task) + " -- " + str(group.name))
if unit_type in db.PLANE_PAYLOAD_OVERRIDES: if unit_type in db.PLANE_PAYLOAD_OVERRIDES:
override_loadout = db.PLANE_PAYLOAD_OVERRIDES[unit_type] override_loadout = db.PLANE_PAYLOAD_OVERRIDES[unit_type]
if type(override_loadout) == dict: 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]: if for_task in db.PLANE_PAYLOAD_OVERRIDES[unit_type]:
payload_name = db.PLANE_PAYLOAD_OVERRIDES[unit_type][for_task] payload_name = db.PLANE_PAYLOAD_OVERRIDES[unit_type][for_task]
group.load_loadout(payload_name) group.load_loadout(payload_name)
@@ -246,6 +234,11 @@ class AircraftConflictGenerator:
def generate_flights(self, cp, country, flight_planner:FlightPlanner): def generate_flights(self, cp, country, flight_planner:FlightPlanner):
for flight in flight_planner.flights: 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) group = self.generate_planned_flight(cp, country, flight)
if flight.flight_type == FlightType.INTERCEPTION: if flight.flight_type == FlightType.INTERCEPTION:
self.setup_group_as_intercept_flight(group, flight) self.setup_group_as_intercept_flight(group, flight)
@@ -258,7 +251,7 @@ class AircraftConflictGenerator:
def setup_group_activation_trigger(self, flight, group): def setup_group_activation_trigger(self, flight, group):
if flight.scheduled_in > 0 and flight.client_count == 0: 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.late_activation = False
group.uncontrolled = True group.uncontrolled = True
@@ -370,9 +363,9 @@ class AircraftConflictGenerator:
group.task = SEAD.name group.task = SEAD.name
self._setup_group(group, SEAD, flight.client_count) self._setup_group(group, SEAD, flight.client_count)
group.points[0].tasks.clear() 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(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)) group.points[0].tasks.append(OptRestrictJettison(True))
elif flight_type in [FlightType.STRIKE]: elif flight_type in [FlightType.STRIKE]:
group.task = PinpointStrike.name 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): 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) pt = group.add_waypoint(Point(point.x, point.y), point.alt)
if point.waypoint_type == FlightWaypointType.PATROL_TRACK: if point.waypoint_type == FlightWaypointType.PATROL_TRACK:
action = OrbitAction(altitude=pt.alt, pattern=OrbitAction.OrbitPattern.RaceTrack) action = ControlledTask(OrbitAction(altitude=pt.alt, pattern=OrbitAction.OrbitPattern.RaceTrack))
pt.tasks.append(action) action.stop_after_duration(CAP_DURATION * 60)
#for tgt in point.targets: #for tgt in point.targets:
# if hasattr(tgt, "position"): # if hasattr(tgt, "position"):
# engagetgt = EngageTargetsInZone(tgt.position, radius=CAP_DEFAULT_ENGAGE_DISTANCE, targets=[Targets.All.Air]) # 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: elif point.waypoint_type == FlightWaypointType.LANDING_POINT:
pt.type = "Land" pt.type = "Land"
elif point.waypoint_type == FlightWaypointType.INGRESS_STRIKE: 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): 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: if group.units[0].unit_type == JF_17 and j < 4:
group.add_nav_target_point(t.position, "PP" + str(j + 1)) group.add_nav_target_point(t.position, "PP" + str(j + 1))
if group.units[0].unit_type == F_14B and j == 0: if group.units[0].unit_type == F_14B and j == 0:

View File

@@ -39,36 +39,60 @@ class BriefingGenerator:
return return
flight_unit_name = db.unit_type_name(flight.unit_type) 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 += 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): for i, wpt in enumerate(flight.points):
self.description += "#" + str(1+i) + " -- " + wpt.name + " : " + wpt.description + "\n" self.description += "#" + str(1+i) + " -- " + wpt.name + " : " + wpt.description + "\n"
self.description += "#" + str(len(flight.points) + 1) + " -- RTB\n" self.description += "#" + str(len(flight.points) + 1) + " -- RTB\n"
self.description += "-" * 50 + "\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): def generate(self):
self.description = "" self.description = ""
self.description += "DCS Liberation turn #" + str(self.game.turn) + "\n" 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 planner in self.game.planners.values():
for flight in planner.flights: for flight in planner.flights:
self.add_flight_description(flight) 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: if self.freqs:
self.description += "\n\nComms Frequencies:\n" self.description += "\n\nComms Frequencies:\n"
self.description += "-" * 50 + "\n" self.description += "=" * 15 + "\n"
for name, freq in self.freqs: for name, freq in self.freqs:
self.description += "\n{}: {}".format(name, freq) self.description += "{}: {}\n".format(name, freq)
self.description += "\n" + ("-" * 50) + "\n" self.description += ("-" * 50) + "\n"
for cp in self.game.theater.controlpoints: for cp in self.game.theater.controlpoints:
if cp.captured and cp.cptype in [ControlPointType.LHA_GROUP, ControlPointType.AIRCRAFT_CARRIER_GROUP]: 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) self.description += str(cp.tacanN)
if cp.tacanY: if cp.tacanY:

View File

@@ -115,7 +115,7 @@ class EnviromentGenerator:
if weather_type == 1: if weather_type == 1:
# thunderstorm # thunderstorm
self._generate_base_weather() 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_density = random.randint(9, 10)
self.mission.weather.clouds_iprecptns = Weather.Preceptions.Thunderstorm 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_density = random.randint(5, 8)
self.mission.weather.clouds_iprecptns = Weather.Preceptions.Rain 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: elif weather_type == 3:
# clouds # clouds
self._generate_base_weather() 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, MolniyaGroupGenerator)
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 game import db
from gen.fleet.carrier_group import CarrierGroupGenerator 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.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): 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(2, 6)):
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(random.randint(2, 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 math
import operator import operator
import typing
import random import random
from game import db 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 import Conflict
from gen.flights.ai_flight_planner_db import INTERCEPT_CAPABLE, CAP_CAPABLE, CAS_CAPABLE, SEAD_CAPABLE, STRIKE_CAPABLE 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 from gen.flights.flight import Flight, FlightType, FlightWaypoint, FlightWaypointType
def meter_to_feet(value_in_meter): MISSION_DURATION = 80
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)
class FlightPlanner: class FlightPlanner:
@@ -55,6 +23,17 @@ class FlightPlanner:
self.game = game self.game = game
self.aircraft_inventory = {} # local copy of the airbase inventory 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): def reset(self):
""" """
Reset the planned flights and available units Reset the planned flights and available units
@@ -112,7 +91,7 @@ class FlightPlanner:
""" """
# At least try to generate one interceptor group # 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] possible_interceptors = [k for k in self.aircraft_inventory.keys() if k in INTERCEPT_CAPABLE]
if len(possible_interceptors) <= 0: 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}) inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft})
offset = random.randint(0,5) 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: try:
unit = random.choice([k for k, v in inventory.items() if v >= 2]) unit = random.choice([k for k, v in inventory.items() if v >= 2])
@@ -153,67 +132,16 @@ class FlightPlanner:
break break
inventory[unit] = inventory[unit] - 2 inventory[unit] = inventory[unit] - 2
ftype = FlightType.BARCAP if self.from_cp.is_carrier else FlightType.CAP flight = Flight(unit, 2, self.from_cp, FlightType.CAP)
flight = Flight(unit, 2, self.from_cp, ftype)
flight.points = [] 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: if len(self._get_cas_locations()) > 0:
loc = random.choice(self._get_cas_locations()) enemy_cp = random.choice(self._get_cas_locations())
ingress, heading, distance = Conflict.frontline_vector(self.from_cp, loc, self.game.theater) self.generate_frontline_cap(flight, flight.from_cp, enemy_cp)
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)
else: else:
loc = self.from_cp.position.point_from_heading(random.randint(0, 360), random.randint(nm_to_meter(5), nm_to_meter(40))) self.generate_barcap(flight, flight.from_cp)
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.cap_flights.append(flight) self.cap_flights.append(flight)
self.flights.append(flight) self.flights.append(flight)
@@ -234,7 +162,7 @@ class FlightPlanner:
if len(cas_location) > 0: if len(cas_location) > 0:
offset = random.randint(0,5) 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: try:
unit = random.choice([k for k, v in inventory.items() if v >= 2]) unit = random.choice([k for k, v in inventory.items() if v >= 2])
@@ -243,47 +171,11 @@ class FlightPlanner:
inventory[unit] = inventory[unit] - 2 inventory[unit] = inventory[unit] - 2
flight = Flight(unit, 2, self.from_cp, FlightType.CAS) flight = Flight(unit, 2, self.from_cp, FlightType.CAS)
flight.points = [] 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) 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) self.generate_cas(flight, flight.from_cp, location)
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.cas_flights.append(flight) self.cas_flights.append(flight)
self.flights.append(flight) self.flights.append(flight)
@@ -303,7 +195,7 @@ class FlightPlanner:
if len(self.potential_sead_targets) > 0: if len(self.potential_sead_targets) > 0:
offset = random.randint(0,5) 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: if len(self.potential_sead_targets) <= 0:
break break
@@ -317,51 +209,12 @@ class FlightPlanner:
flight = Flight(unit, 2, self.from_cp, random.choice([FlightType.SEAD, FlightType.DEAD])) flight = Flight(unit, 2, self.from_cp, random.choice([FlightType.SEAD, FlightType.DEAD]))
flight.points = [] flight.points = []
flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5) flight.scheduled_in = offset + i*random.randint(self.doctrine["SEAD_EVERY_X_MINUTES"] - 5, self.doctrine["SEAD_EVERY_X_MINUTES"] + 5)
ascend = self.generate_ascend_point(self.from_cp)
flight.points.append(ascend)
location = self.potential_sead_targets[0][0] location = self.potential_sead_targets[0][0]
self.potential_sead_targets.pop(0) self.potential_sead_targets.pop(0)
heading = self.from_cp.position.heading_between_point(location.position) self.generate_sead(flight, location, [])
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.sead_flights.append(flight) self.sead_flights.append(flight)
self.flights.append(flight) self.flights.append(flight)
@@ -381,7 +234,7 @@ class FlightPlanner:
if len(self.potential_strike_targets) > 0: if len(self.potential_strike_targets) > 0:
offset = random.randint(0,5) 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: if len(self.potential_strike_targets) <= 0:
break break
@@ -395,77 +248,12 @@ class FlightPlanner:
flight = Flight(unit, 2, self.from_cp, FlightType.STRIKE) flight = Flight(unit, 2, self.from_cp, FlightType.STRIKE)
flight.points = [] flight.points = []
flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5) flight.scheduled_in = offset + i*random.randint(self.doctrine["STRIKE_EVERY_X_MINUTES"] - 5, self.doctrine["STRIKE_EVERY_X_MINUTES"] + 5)
ascend = self.generate_ascend_point(self.from_cp)
flight.points.append(ascend)
location = self.potential_strike_targets[0][0] location = self.potential_strike_targets[0][0]
self.potential_strike_targets.pop(0) self.potential_strike_targets.pop(0)
heading = self.from_cp.position.heading_between_point(location.position) self.generate_strike(flight, location)
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.strike_flights.append(flight) self.strike_flights.append(flight)
self.flights.append(flight) self.flights.append(flight)
@@ -475,9 +263,12 @@ class FlightPlanner:
self.aircraft_inventory[k] = v self.aircraft_inventory[k] = v
def _get_cas_locations(self): 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 = [] cas_locations = []
for cp in self.from_cp.connected_points: for cp in for_cp.connected_points:
if cp.captured != self.from_cp.captured: if cp.captured != for_cp.captured:
cas_locations.append(cp) cas_locations.append(cp)
return cas_locations return cas_locations
@@ -495,7 +286,7 @@ class FlightPlanner:
distance = math.hypot(cp.position.x - self.from_cp.position.x, distance = math.hypot(cp.position.x - self.from_cp.position.x,
cp.position.y - self.from_cp.position.y) 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 # Then it's unlikely any child ground object is in range
return return
@@ -507,7 +298,7 @@ class FlightPlanner:
distance = math.hypot(cp.position.x - self.from_cp.position.x, distance = math.hypot(cp.position.x - self.from_cp.position.x,
cp.position.y - self.from_cp.position.y) 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)) self.potential_strike_targets.append((g, distance))
added_group.append(g) added_group.append(g)
@@ -528,7 +319,7 @@ class FlightPlanner:
cp.position.y - self.from_cp.position.y) cp.position.y - self.from_cp.position.y)
# Then it's unlikely any ground object is range # Then it's unlikely any ground object is range
if distance > 2*SEAD_MAX_RANGE: if distance > 2*self.doctrine["SEAD_MAX_RANGE"]:
return return
for g in cp.ground_objects: for g in cp.ground_objects:
@@ -536,8 +327,7 @@ class FlightPlanner:
if g.dcs_identifier == "AA": if g.dcs_identifier == "AA":
# Check that there is at least one unit with a radar in the ground objects unit groups # 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") 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])
and db.unit_type_from_name(r.type).detection_range > 1000]) for group in g.groups])
if number_of_units <= 0: if number_of_units <= 0:
continue continue
@@ -545,7 +335,7 @@ class FlightPlanner:
distance = math.hypot(cp.position.x - self.from_cp.position.x, distance = math.hypot(cp.position.x - self.from_cp.position.x,
cp.position.y - self.from_cp.position.y) 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.append((g, distance))
self.potential_sead_targets.sort(key=operator.itemgetter(1)) self.potential_sead_targets.sort(key=operator.itemgetter(1))
@@ -563,29 +353,341 @@ class FlightPlanner:
del base_aircraft_inventory[f.unit_type] del base_aircraft_inventory[f.unit_type]
return base_aircraft_inventory 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): 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 ascend_heading = from_cp.heading
pos_ascend = from_cp.position.point_from_heading(ascend_heading, 10000) 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.name = "ASCEND"
ascend.alt_type = "RADIO" ascend.alt_type = "RADIO"
ascend.description = "Ascend to alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL], then proceed to next waypoint" ascend.description = "Ascend"
ascend.pretty_name = "Ascend to alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL]" ascend.pretty_name = "Ascend"
ascend.waypoint_type = FlightWaypointType.ASCEND_POINT ascend.waypoint_type = FlightWaypointType.ASCEND_POINT
return ascend return ascend
def generate_descend_point(self, from_cp): 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 ascend_heading = from_cp.heading
descend = from_cp.position.point_from_heading(ascend_heading - 180, 30000) descend = from_cp.position.point_from_heading(ascend_heading - 180, 10000)
descend = FlightWaypoint(descend.x, descend.y, PATTERN_ALTITUDE) descend = FlightWaypoint(descend.x, descend.y, self.doctrine["PATTERN_ALTITUDE"])
descend.name = "DESCEND" descend.name = "DESCEND"
descend.alt_type = "RADIO" descend.alt_type = "RADIO"
descend.description = "Descend to pattern alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL], contact tower, and land" descend.description = "Descend to pattern alt"
descend.pretty_name = "Descend to pattern alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL]" descend.pretty_name = "Descend to pattern alt"
descend.waypoint_type = FlightWaypointType.DESCENT_POINT descend.waypoint_type = FlightWaypointType.DESCENT_POINT
return descend return descend
def generate_rtb_waypoint(self, from_cp): 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 = from_cp.position
rtb = FlightWaypoint(rtb.x, rtb.y, 0) rtb = FlightWaypoint(rtb.x, rtb.y, 0)
rtb.name = "LANDING" rtb.name = "LANDING"
@@ -593,5 +695,4 @@ class FlightPlanner:
rtb.description = "RTB" rtb.description = "RTB"
rtb.pretty_name = "RTB" rtb.pretty_name = "RTB"
rtb.waypoint_type = FlightWaypointType.LANDING_POINT rtb.waypoint_type = FlightWaypointType.LANDING_POINT
return rtb return rtb

View File

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

View File

@@ -46,6 +46,18 @@ class FlightWaypointType(Enum):
TARGET_POINT = 12 # A target building or static object, position TARGET_POINT = 12 # A target building or static object, position
TARGET_GROUP_LOC = 13 # A target group approximate location TARGET_GROUP_LOC = 13 # A target group approximate location
TARGET_SHIP = 14 # A target ship known 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: class FlightWaypoint:
@@ -58,10 +70,13 @@ class FlightWaypoint:
self.name = "" self.name = ""
self.description = "" self.description = ""
self.targets = [] self.targets = []
self.targetGroup = None
self.obj_name = "" self.obj_name = ""
self.pretty_name = "" self.pretty_name = ""
self.waypoint_type = FlightWaypointType.TAKEOFF # type: FlightWaypointType self.waypoint_type = FlightWaypointType.TAKEOFF # type: FlightWaypointType
self.category = PredefinedWaypointCategory.NOT_PREDEFINED# type: PredefinedWaypointCategory
self.only_for_player = False self.only_for_player = False
self.data = None
class Flight: class Flight:

View File

@@ -22,15 +22,22 @@ class ForcedOptionsGenerator:
self.game = game self.game = game
def _set_options_view(self): 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 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 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 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 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): def _set_labels(self):
if self.game.settings.labels == "Abbreviated": if self.game.settings.labels == "Abbreviated":
self.mission.forced_options.labels = int(Labels.Abbreviated) self.mission.forced_options.labels = int(Labels.Abbreviated)
@@ -41,5 +48,8 @@ class ForcedOptionsGenerator:
def generate(self): def generate(self):
self._set_options_view() self._set_options_view()
self._set_external_views()
self._set_labels() self._set_labels()

View File

@@ -1,6 +1,7 @@
import logging import logging
from game import db from game import db
from game.data.building_data import FORTIFICATION_UNITS_ID, FORTIFICATION_UNITS
from game.db import unit_type_from_name from game.db import unit_type_from_name
from .conflictgen import * from .conflictgen import *
from .naming import * from .naming import *
@@ -50,9 +51,6 @@ class GroundObjectsGenerator:
else: else:
cp = self.conflict.from_cp cp = self.conflict.from_cp
consumed_farps = set()
for cp in self.game.theater.controlpoints: for cp in self.game.theater.controlpoints:
if cp.captured: if cp.captured:
@@ -63,26 +61,43 @@ class GroundObjectsGenerator:
for ground_object in cp.ground_objects: for ground_object in cp.ground_objects:
if ground_object.dcs_identifier == "AA": if ground_object.dcs_identifier == "AA":
if self.game.position_culled(ground_object.position):
continue
for g in ground_object.groups: for g in ground_object.groups:
if len(g.units) > 0: if len(g.units) > 0:
utype = unit_type_from_name(g.units[0].type) 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) if not ground_object.sea_object:
vg.units[0].name = self.m.string(g.units[0].name) vg = self.m.vehicle_group(side, g.name, utype, position=g.position, heading=g.units[0].heading)
for i, u in enumerate(g.units): vg.units[0].name = self.m.string(g.units[0].name)
if i > 0: for i, u in enumerate(g.units):
vehicle = Vehicle(self.m.next_unit_id(), self.m.string(u.name), u.type) if i > 0:
vehicle.position.x = u.position.x vehicle = Vehicle(self.m.next_unit_id(), self.m.string(u.name), u.type)
vehicle.position.y = u.position.y vehicle.position.x = u.position.x
vehicle.heading = u.heading vehicle.position.y = u.position.y
vg.add_unit(vehicle) 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: if self.game.settings.perf_red_alert_state:
vg.points[0].tasks.append(OptAlarmState(2)) vg.points[0].tasks.append(OptAlarmState(2))
else: else:
vg.points[0].tasks.append(OptAlarmState(1)) vg.points[0].tasks.append(OptAlarmState(1))
elif ground_object.dcs_identifier in ["CARRIER", "LHA"]: elif ground_object.dcs_identifier in ["CARRIER", "LHA"]:
for g in ground_object.groups: for g in ground_object.groups:
if len(g.units) > 0: if len(g.units) > 0:
@@ -102,63 +117,61 @@ class GroundObjectsGenerator:
ship.heading = u.heading ship.heading = u.heading
sg.add_unit(ship) sg.add_unit(ship)
# TODO : make sure the point is not on Land # Find carrier direction (In the wind)
sg.add_waypoint(sg.points[0].position.point_from_heading(g.units[0].heading, 100000)) 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" 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))
if ground_object.dcs_identifier == "CARRIER" and hasattr(cp, "icls"): if ground_object.dcs_identifier == "CARRIER" and hasattr(cp, "icls"):
sg.points[0].tasks.append(ActivateICLSCommand(cp.icls, unit_id=sg.units[0].id)) sg.points[0].tasks.append(ActivateICLSCommand(cp.icls, unit_id=sg.units[0].id))
else: else:
if self.game.position_culled(ground_object.position):
continue
static_type = None
if ground_object.dcs_identifier in warehouse_map: if ground_object.dcs_identifier in warehouse_map:
static_type = warehouse_map[ground_object.dcs_identifier] 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] static_type = fortification_map[ground_object.dcs_identifier]
elif ground_object.dcs_identifier in FORTIFICATION_UNITS_ID:
if not static_type: 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)) print("Didn't find {} in static _map(s)!".format(ground_object.dcs_identifier))
continue continue
group = self.m.static_group( if static_type is None:
country=side, if not ground_object.is_dead:
name=ground_object.string_identifier, group = self.m.vehicle_group(
_type=static_type, country=side,
position=ground_object.position, name=ground_object.string_identifier,
heading=ground_object.heading, _type=unit_type,
dead=ground_object.is_dead, 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)) logging.info("generated {}object identifier {} with mission id {}".format(
"dead " if ground_object.is_dead else "", group.name, group.id))
else:
def farp_aa(mission_obj, country, name, position: mapping.Point): group = self.m.static_group(
""" country=side,
Add AAA to a FARP :) name=ground_object.string_identifier,
:param mission_obj: _type=static_type,
:param country: position=ground_object.position,
:param name: heading=ground_object.heading,
:param position: dead=ground_object.is_dead,
: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
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", "TERMITE", "URCHIN", "SHRIMP", "TURKEY", "TOUCAN", "TETRA", "HUSKY", "STARFISH", "SWAN",
"FROG", "SQUIRREL", "WALRUS", "WARTHOG", "CORGI", "WEASEL", "WOMBAT", "WOLVERINE", "MAMMOTH", "FROG", "SQUIRREL", "WALRUS", "WARTHOG", "CORGI", "WEASEL", "WOMBAT", "WOLVERINE", "MAMMOTH",
"TOAD", "WOLF", "ZEBU", "SEAL", "SKATE", "JELLYFISH", "MOSQUITO", "LOCUST", "SLUG", "SNAIL", "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): def __init__(self):

View File

@@ -15,7 +15,7 @@ class FlakGenerator(GroupGenerator):
grid_x = random.randint(2, 4) grid_x = random.randint(2, 4)
grid_y = random.randint(2, 4) grid_y = random.randint(2, 4)
spacing = random.randint(10,40) spacing = random.randint(30,60)
index = 0 index = 0
mixed = random.choice([True, False]) mixed = random.choice([True, False])
@@ -25,15 +25,29 @@ class FlakGenerator(GroupGenerator):
for j in range(grid_y): for j in range(grid_y):
index = index+1 index = index+1
self.add_unit(unit_type, "AAA#" + str(index), self.add_unit(unit_type, "AAA#" + str(index),
self.position.x + spacing*i, self.position.x + spacing*i + random.randint(1,5),
self.position.y + spacing*j, self.heading) self.position.y + spacing*j + random.randint(1,5), self.heading)
if(mixed): if(mixed):
unit_type = random.choice(GFLAK) unit_type = random.choice(GFLAK)
# Enough Opel Blitz truck to transport the guns # Search lights
for i in range(grid_x): search_pos = self.get_circular_position(random.randint(2,5), 90)
for j in range(grid_y): 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.add_unit(Unarmed.Blitz_3_6_6700A, "AAA#" + str(index),
self.position.x + 200 + 9*i, self.position.x + 200 + 15*i + random.randint(1,5),
self.position.y + 9*j, 90) self.position.y + 15*j + random.randint(1,5), 90)

View File

@@ -42,100 +42,19 @@ class TriggersGenerator:
self.conflict = conflict self.conflict = conflict
self.game = game 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): def _set_allegiances(self, player_coalition: str, enemy_coalition: str):
"""
Set airbase initial coalition
"""
for cp in self.game.theater.controlpoints: for cp in self.game.theater.controlpoints:
if cp.is_global: if cp.is_global:
continue continue
self.mission.terrain.airport_by_id(cp.at.id).set_coalition(cp.captured and player_coalition or enemy_coalition) 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): 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(): for coalition_name, coalition in self.mission.coalition.items():
if coalition_name == player_coalition: if coalition_name == player_coalition:
skill_level = self.game.settings.player_skill, self.game.settings.player_skill 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: for vehicle_group in country.vehicle_group:
vehicle_group.set_skill(Skill(skill_level[1])) 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 Generate markers on F10 map for each existing objective
: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
""" """
trigger_one = TriggerOnce(Event.NoEvent, "Activation trigger") if self.game.settings.generate_marks:
trigger_one.add_condition(FlagEquals(1, 1)) mark_trigger = TriggerOnce(Event.NoEvent, "Marks generator")
trigger_one.add_action(SetFlagValue(flag_id, 1)) 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() def generate(self):
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):
player_coalition = self.game.player_country in db.BLUEFOR_FACTIONS and "blue" or "red" player_coalition = self.game.player_country in db.BLUEFOR_FACTIONS and "blue" or "red"
enemy_coalition = player_coalition == "blue" and "red" or "blue" enemy_coalition = player_coalition == "blue" and "red" or "blue"
self.mission.coalition["blue"].bullseye = {"x": self.conflict.position.x, 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, self.mission.coalition["red"].bullseye = {"x": self.conflict.position.x,
"y": self.conflict.position.y} "y": self.conflict.position.y}
self._set_skill(player_coalition, enemy_coalition) self._set_skill(player_coalition, enemy_coalition)
self._set_allegiances(player_coalition, enemy_coalition) self._set_allegiances(player_coalition, enemy_coalition)
self._gen_markers()
#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)

View File

@@ -1,19 +1,23 @@
from userdata import logging_config
# Logging setup
VERSION_STRING = "2.0RC7"
logging_config.init_logging(VERSION_STRING)
import logging import logging
import os import os
import sys import sys
from shutil import copyfile
import dcs import dcs
from PySide2 import QtWidgets from PySide2 import QtWidgets
from PySide2.QtGui import QPixmap from PySide2.QtGui import QPixmap
from PySide2.QtWidgets import QApplication, QSplashScreen from PySide2.QtWidgets import QApplication, QSplashScreen
from dcs import installation
from qt_ui import uiconstants from qt_ui import uiconstants
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from qt_ui.windows.QLiberationWindow import QLiberationWindow from qt_ui.windows.QLiberationWindow import QLiberationWindow
from qt_ui.windows.preferences.QLiberationFirstStartWindow import QLiberationFirstStartWindow 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__": if __name__ == "__main__":
@@ -23,9 +27,8 @@ if __name__ == "__main__":
with open("./resources/stylesheets/style.css") as stylesheet: with open("./resources/stylesheets/style.css") as stylesheet:
app.setStyleSheet(stylesheet.read()) app.setStyleSheet(stylesheet.read())
# Logging setup
VERSION_STRING = "2.0RC6"
logging_module.setup_version_string(VERSION_STRING)
# Inject custom payload in pydcs framework # Inject custom payload in pydcs framework
custom_payloads = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..\\resources\\customized_payloads") 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("QT App terminated with status code : " + str(qt_execution_code))
logging.info("Attempt to restore original mission scripting file") logging.info("Attempt to restore original mission scripting file")
liberation_install.restore_original_mission_scripting() 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_Persian_Gulf"] = QPixmap("./resources/ui/terrain_pg.gif")
ICONS["Terrain_Nevada"] = QPixmap("./resources/ui/terrain_nevada.gif") ICONS["Terrain_Nevada"] = QPixmap("./resources/ui/terrain_nevada.gif")
ICONS["Terrain_Normandy"] = QPixmap("./resources/ui/terrain_normandy.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["Dawn"] = QPixmap("./resources/ui/daytime/dawn.png")
ICONS["Day"] = QPixmap("./resources/ui/daytime/day.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] = QPixmap("./resources/ui/ground_assets/" + category + ".png")
ICONS[category + "_blue"] = QPixmap("./resources/ui/ground_assets/" + category + "_blue.png") ICONS[category + "_blue"] = QPixmap("./resources/ui/ground_assets/" + category + "_blue.png")
ICONS["destroyed"] = QPixmap("./resources/ui/ground_assets/destroyed.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["Generator"] = QPixmap("./resources/ui/misc/generator.png")
ICONS["Missile"] = QPixmap("./resources/ui/misc/missile.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.setProperty("style", "btn-primary")
self.passTurnButton.clicked.connect(self.passTurn) self.passTurnButton.clicked.connect(self.passTurn)
self.proceedButton = QPushButton("Proceed") self.proceedButton = QPushButton("Mission Planning")
self.proceedButton.setIcon(CONST.ICONS["Proceed"]) self.proceedButton.setIcon(CONST.ICONS["Proceed"])
self.proceedButton.setProperty("style", "btn-primary") self.proceedButton.setProperty("style", "btn-primary")
self.proceedButton.clicked.connect(self.proceed) 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 import typing
from typing import Dict from typing import Dict
from PySide2 import QtCore
from PySide2.QtCore import Qt, QRect, QPointF from PySide2.QtCore import Qt, QRect, QPointF
from PySide2.QtGui import QPixmap, QBrush, QColor, QWheelEvent, QPen, QFont from PySide2.QtGui import QPixmap, QBrush, QColor, QWheelEvent, QPen, QFont
from PySide2.QtWidgets import QGraphicsView, QFrame, QGraphicsOpacityEffect from PySide2.QtWidgets import QGraphicsView, QFrame, QGraphicsOpacityEffect
@@ -9,6 +10,7 @@ from dcs.mapping import point_from_heading
import qt_ui.uiconstants as CONST import qt_ui.uiconstants as CONST
from game import Game, db from game import Game, db
from game.data.radar_db import UNITS_WITH_RADAR
from game.event import UnitsDeliveryEvent, Event, ControlPointType from game.event import UnitsDeliveryEvent, Event, ControlPointType
from gen import Conflict from gen import Conflict
from qt_ui.widgets.map.QLiberationScene import QLiberationScene from qt_ui.widgets.map.QLiberationScene import QLiberationScene
@@ -40,6 +42,7 @@ class QLiberationMap(QGraphicsView):
self.setMinimumSize(800,600) self.setMinimumSize(800,600)
self.setMaximumHeight(2160) self.setMaximumHeight(2160)
self._zoom = 0 self._zoom = 0
self.factor = 1
self.init_scene() self.init_scene()
self.connectSignals() self.connectSignals()
self.setGame(game) self.setGame(game)
@@ -62,12 +65,60 @@ class QLiberationMap(QGraphicsView):
if self.game is not None: if self.game is not None:
self.reload_scene() 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): def reload_scene(self):
scene = self.scene() scene = self.scene()
scene.clear() scene.clear()
self.addBackground() 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: 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"): if ground_object.category == "aa" and self.get_display_rule("sam"):
max_range = 0 max_range = 0
has_radar = False
if ground_object.groups: if ground_object.groups:
for g in ground_object.groups: for g in ground_object.groups:
for u in g.units: for u in g.units:
unit = db.unit_type_from_name(u.type) unit = db.unit_type_from_name(u.type)
if unit in UNITS_WITH_RADAR:
has_radar = True
if unit.threat_range > max_range: if unit.threat_range > max_range:
max_range = unit.threat_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) 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) added_objects.append(ground_object.obj_name)
@@ -218,6 +272,7 @@ class QLiberationMap(QGraphicsView):
else: else:
self._zoom = -5 self._zoom = -5
def _transform_point(self, p: Point, treshold=30) -> (int, int): def _transform_point(self, p: Point, treshold=30) -> (int, int):
point_a = list(self.game.theater.reference_points.keys())[0] point_a = list(self.game.theater.reference_points.keys())[0]
point_a_img = self.game.theater.reference_points[point_a] point_a_img = self.game.theater.reference_points[point_a]
@@ -246,56 +301,6 @@ class QLiberationMap(QGraphicsView):
return X > treshold and X or treshold, Y > treshold and Y or treshold 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): def addBackground(self):
scene = self.scene() scene = self.scene()

View File

@@ -17,7 +17,7 @@ class QMapGroundObject(QGraphicsRectItem):
self.setAcceptHoverEvents(True) self.setAcceptHoverEvents(True)
self.setZValue(2) self.setZValue(2)
self.buildings = buildings self.buildings = buildings
#self.setFlag(QGraphicsItem.ItemIgnoresTransformations, True) self.setFlag(QGraphicsItem.ItemIgnoresTransformations, False)
if len(self.model.groups) > 0: if len(self.model.groups) > 0:
units = {} units = {}
@@ -44,10 +44,15 @@ class QMapGroundObject(QGraphicsRectItem):
#super(QMapControlPoint, self).paint(painter, option, widget) #super(QMapControlPoint, self).paint(painter, option, widget)
if self.parent.get_display_rule("go"): if self.parent.get_display_rule("go"):
painter.save() 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: 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: 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: else:
painter.drawPixmap(option.rect, CONST.ICONS["destroyed"]) painter.drawPixmap(option.rect, CONST.ICONS["destroyed"])
painter.restore() 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 sys
import webbrowser import webbrowser
@@ -163,12 +164,12 @@ class QLiberationWindow(QMainWindow):
wizard.accepted.connect(lambda: self.onGameGenerated(wizard.generatedGame)) wizard.accepted.connect(lambda: self.onGameGenerated(wizard.generatedGame))
def saveGame(self): def saveGame(self):
print("Saving game") logging.info("Saving game")
persistency.save_game(self.game) persistency.save_game(self.game)
GameUpdateSignal.get_instance().updateGame(self.game) GameUpdateSignal.get_instance().updateGame(self.game)
def onGameGenerated(self, game: Game): def onGameGenerated(self, game: Game):
print("On Game generated") logging.info("On Game generated")
self.game = game self.game = game
GameUpdateSignal.get_instance().updateGame(self.game) GameUpdateSignal.get_instance().updateGame(self.game)
@@ -198,7 +199,7 @@ class QLiberationWindow(QMainWindow):
about.setWindowTitle("About DCS Liberation") about.setWindowTitle("About DCS Liberation")
about.setIcon(QMessageBox.Icon.Information) about.setIcon(QMessageBox.Icon.Information)
about.setText(text) about.setText(text)
print(about.textFormat()) logging.info(about.textFormat())
about.exec_() about.exec_()
def showLiberationDialog(self): def showLiberationDialog(self):
@@ -206,6 +207,6 @@ class QLiberationWindow(QMainWindow):
self.subwindow.show() self.subwindow.show()
def onDebriefing(self, debrief: DebriefingSignal): def onDebriefing(self, debrief: DebriefingSignal):
print("On Debriefing") logging.info("On Debriefing")
self.debriefing = QDebriefingWindow(debrief.debriefing, debrief.gameEvent, debrief.game) self.debriefing = QDebriefingWindow(debrief.debriefing, debrief.gameEvent, debrief.game)
self.debriefing.show() self.debriefing.show()

View File

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

View File

@@ -22,14 +22,14 @@ class QBaseDefenseGroupInfo(QGroupBox):
unit_dict[u.type] = 1 unit_dict[u.type] = 1
i = 0 i = 0
for k, v in unit_dict.items(): for k, v in unit_dict.items():
icon = QLabel() #icon = QLabel()
if k in VEHICLES_ICONS.keys(): #if k in VEHICLES_ICONS.keys():
icon.setPixmap(VEHICLES_ICONS[k]) # icon.setPixmap(VEHICLES_ICONS[k])
else: #else:
icon.setText("<b>" + k[:6] + "</b>") # icon.setText("<b>" + k[:6] + "</b>")
icon.setProperty("style", "icon-plane") #icon.setProperty("style", "icon-plane")
layout.addWidget(icon, i, 0) #layout.addWidget(icon, i, 0)
layout.addWidget(QLabel(str(v) + " x " + k), i, 1) layout.addWidget(QLabel(str(v) + " x " + "<strong>" + k + "</strong>"), i, 1)
i = i + 1 i = i + 1
self.setLayout(layout) 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 game import db
from qt_ui.uiconstants import AIRCRAFT_ICONS, VEHICLES_ICONS 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 from theater import ControlPoint, Airport
class QBaseInformation(QGroupBox): class QBaseInformation(QFrame):
def __init__(self, cp:ControlPoint, airport:Airport): def __init__(self, cp:ControlPoint, airport:Airport):
super(QBaseInformation, self).__init__("Base defenses") super(QBaseInformation, self).__init__()
self.cp = cp self.cp = cp
self.airport = airport self.airport = airport
self.setMinimumWidth(500) self.setMinimumWidth(500)

View File

@@ -16,7 +16,7 @@ class QChooseAirbase(QGroupBox):
self.depart_from_label = QLabel("Airbase : ") self.depart_from_label = QLabel("Airbase : ")
self.depart_from = QComboBox() 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.addItem(str(cp.name), cp)
self.depart_from.setCurrentIndex(0) self.depart_from.setCurrentIndex(0)
self.depart_from.currentTextChanged.connect(self._on_airbase_selected) 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])+"] " self.setText("["+str(self.flight.flight_type.name[:6])+"] "
+ str(self.flight.count) + " x " + db.unit_type_name(self.flight.unit_type) + str(self.flight.count) + " x " + db.unit_type_name(self.flight.unit_type)
+ " in " + str(self.flight.scheduled_in) + " minutes") + " 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__() super(QMissionPlanning, self).__init__()
self.game = game self.game = game
self.setWindowFlags(Qt.WindowStaysOnTopHint) self.setWindowFlags(Qt.WindowStaysOnTopHint)
self.setMinimumSize(800, 420) self.setMinimumSize(1000, 420)
self.setModal(True) self.setModal(True)
self.setWindowTitle("Mission Preparation") self.setWindowTitle("Mission Preparation")
self.setWindowIcon(EVENT_ICONS["strike"]) self.setWindowIcon(EVENT_ICONS["strike"])
@@ -42,9 +42,11 @@ class QMissionPlanning(QDialog):
self.planned_flight_view.selectionModel().selectionChanged.connect(self.on_flight_selection_change) self.planned_flight_view.selectionModel().selectionChanged.connect(self.on_flight_selection_change)
if len(self.planned_flight_view.flight_planner.flights) > 0: 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: 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 = QPushButton("Add Flight")
self.add_flight_button.clicked.connect(self.on_add_flight) self.add_flight_button.clicked.connect(self.on_add_flight)
@@ -88,7 +90,7 @@ class QMissionPlanning(QDialog):
print("On flight selection change") print("On flight selection change")
index = self.planned_flight_view.selectionModel().currentIndex().row() 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: if self.flight_planner is not None:
self.flight_planner.clearTabs() self.flight_planner.clearTabs()
@@ -97,9 +99,12 @@ class QMissionPlanning(QDialog):
flight = self.planner.flights[index] flight = self.planner.flights[index]
except IndexError: except IndexError:
flight = None 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) 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): def on_add_flight(self):
possible_aircraft_type = list(self.selected_cp.base.aircraft.keys()) possible_aircraft_type = list(self.selected_cp.base.aircraft.keys())
@@ -124,10 +129,14 @@ class QMissionPlanning(QDialog):
def on_start(self): def on_start(self):
# TODO : refactor this nonsense
self.gameEvent = None
for event in self.game.events: for event in self.game.events:
if isinstance(event, FrontlineAttackEvent) and event.is_player_attacking: if isinstance(event, FrontlineAttackEvent) and event.is_player_attacking:
self.gameEvent = event 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: #if self.awacs_checkbox.isChecked() == 1:
# self.gameEvent.is_awacs_enabled = True # self.gameEvent.is_awacs_enabled = True
# self.game.awacs_expense_commit() # self.game.awacs_expense_commit()

View File

@@ -12,14 +12,21 @@ class QPlannedFlightsView(QListView):
super(QPlannedFlightsView, self).__init__() super(QPlannedFlightsView, self).__init__()
self.model = QStandardItemModel(self) self.model = QStandardItemModel(self)
self.setModel(self.model) self.setModel(self.model)
self.flightitems = []
self.setIconSize(QSize(91, 24)) self.setIconSize(QSize(91, 24))
self.setSelectionBehavior(QAbstractItemView.SelectItems) self.setSelectionBehavior(QAbstractItemView.SelectItems)
if flight_planner: if flight_planner:
self.set_flight_planner(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): 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.setSelectedFlight(row)
self.repaint() self.repaint()
@@ -38,4 +45,4 @@ class QPlannedFlightsView(QListView):
self.clear_layout() self.clear_layout()
self.flight_planner = flight_planner self.flight_planner = flight_planner
if self.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 PySide2.QtWidgets import QTabWidget, QFrame, QGridLayout, QLabel
from gen.flights.flight import Flight from gen.flights.flight import Flight
@@ -9,17 +10,25 @@ from qt_ui.windows.mission.flight.waypoints.QFlightWaypointTab import QFlightWay
class QFlightPlanner(QTabWidget): 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__() super(QFlightPlanner, self).__init__()
print(selected_tab)
self.tabCount = 0 self.tabCount = 0
if flight: if flight:
self.general_settings_tab = QGeneralFlightSettingsTab(flight, game, planner) 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.payload_tab = QFlightPayloadTab(flight, game)
self.waypoint_tab = QFlightWaypointTab(game, flight) 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.general_settings_tab, "General Flight settings")
self.addTab(self.payload_tab, "Payload") self.addTab(self.payload_tab, "Payload")
self.addTab(self.waypoint_tab, "Waypoints") self.addTab(self.waypoint_tab, "Waypoints")
self.tabCount = 3 self.tabCount = 3
self.setCurrentIndex(selected_tab)
else: else:
tabError = QFrame() tabError = QFrame()
l = QGridLayout() l = QGridLayout()
@@ -30,4 +39,4 @@ class QFlightPlanner(QTabWidget):
def clearTabs(self): def clearTabs(self):
for i in range(self.tabCount): 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) 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")] 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): for i, pylon in enumerate(pylons):
@@ -25,7 +26,9 @@ class QLoadoutEditor(QGroupBox):
layout.addWidget(label, i, 0) layout.addWidget(label, i, 0)
layout.addWidget(QPylonEditor(flight, pylon, i+1), i, 1) layout.addWidget(QPylonEditor(flight, pylon, i+1), i, 1)
self.setLayout(layout) hboxLayout.addLayout(layout)
hboxLayout.addStretch()
self.setLayout(hboxLayout)
def on_toggle(self): def on_toggle(self):
self.flight.use_custom_loadout = self.isChecked() self.flight.use_custom_loadout = self.isChecked()

View File

@@ -24,5 +24,7 @@ class QFlightDepartureEditor(QGroupBox):
layout.addWidget(self.minutes) layout.addWidget(self.minutes)
self.setLayout(layout) self.setLayout(layout)
self.changed = self.departure_delta.valueChanged
def change_scheduled(self): def change_scheduled(self):
self.flight.scheduled_in = int(self.departure_delta.value()) 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 PySide2.QtWidgets import QFrame, QGridLayout, QVBoxLayout
from gen.flights.flight import Flight
from game import Game 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.QFlightDepartureEditor import QFlightDepartureEditor
from qt_ui.windows.mission.flight.settings.QFlightSlotEditor import QFlightSlotEditor from qt_ui.windows.mission.flight.settings.QFlightSlotEditor import QFlightSlotEditor
from qt_ui.windows.mission.flight.settings.QFlightStartType import QFlightStartType 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): class QGeneralFlightSettingsTab(QFrame):
on_flight_settings_changed = Signal()
def __init__(self, flight: Flight, game: Game, planner): def __init__(self, flight: Flight, game: Game, planner):
super(QGeneralFlightSettingsTab, self).__init__() super(QGeneralFlightSettingsTab, self).__init__()
@@ -19,18 +21,19 @@ class QGeneralFlightSettingsTab(QFrame):
def init_ui(self): def init_ui(self):
layout = QGridLayout() layout = QGridLayout()
self.flight_info = QFlightTypeTaskInfo(self.flight) flight_info = QFlightTypeTaskInfo(self.flight)
self.flight_departure = QFlightDepartureEditor(self.flight) flight_departure = QFlightDepartureEditor(self.flight)
self.flight_slots = QFlightSlotEditor(self.flight, self.game, self.planner) flight_slots = QFlightSlotEditor(self.flight, self.game, self.planner)
self.flight_start_type = QFlightStartType(self.flight) flight_start_type = QFlightStartType(self.flight)
layout.addWidget(self.flight_info, 0, 0) layout.addWidget(flight_info, 0, 0)
layout.addWidget(self.flight_departure, 1, 0) layout.addWidget(flight_departure, 1, 0)
layout.addWidget(self.flight_slots, 2, 0) layout.addWidget(flight_slots, 2, 0)
layout.addWidget(self.flight_start_type, 3, 0) layout.addWidget(flight_start_type, 3, 0)
vstretch = QVBoxLayout() vstretch = QVBoxLayout()
vstretch.addStretch() vstretch.addStretch()
layout.addLayout(vstretch, 3, 0) layout.addLayout(vstretch, 3, 0)
self.setLayout(layout) self.setLayout(layout)
self.flight_start_type.setEnabled(self.flight.client_count > 0) 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_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): def __init__(self, point: FlightWaypoint, number):
super(QWaypointItem, self).__init__() super(QWaypointItem, self).__init__()
self.number = number self.number = number
self.setText("%02d" % self.number + ' | ' + '{:<16}'.format(point.pretty_name)) self.setText('{:<16}'.format(point.pretty_name))
self.setEditable(False) self.setEditable(False)

View File

@@ -1,17 +1,24 @@
from PySide2.QtCore import QItemSelectionModel, QPoint from PySide2.QtCore import QItemSelectionModel, QPoint
from PySide2.QtGui import QStandardItemModel from PySide2.QtGui import QStandardItemModel, QStandardItem
from PySide2.QtWidgets import QListView from PySide2.QtWidgets import QTableView, QHeaderView
from game.utils import meter_to_feet
from gen.flights.flight import Flight, FlightWaypoint from gen.flights.flight import Flight, FlightWaypoint
from qt_ui.windows.mission.flight.waypoints.QFlightWaypointItem import QWaypointItem from qt_ui.windows.mission.flight.waypoints.QFlightWaypointItem import QWaypointItem
class QFlightWaypointList(QListView): class QFlightWaypointList(QTableView):
def __init__(self, flight: Flight): def __init__(self, flight: Flight):
super(QFlightWaypointList, self).__init__() super(QFlightWaypointList, self).__init__()
self.model = QStandardItemModel(self) self.model = QStandardItemModel(self)
self.setModel(self.model) 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 self.flight = flight
if len(self.flight.points) > 0: if len(self.flight.points) > 0:
@@ -26,10 +33,14 @@ class QFlightWaypointList(QListView):
def update_list(self): def update_list(self):
self.model.clear() self.model.clear()
self.model.setHorizontalHeaderLabels(["Name", "Alt"])
takeoff = FlightWaypoint(self.flight.from_cp.position.x, self.flight.from_cp.position.y, 0) takeoff = FlightWaypoint(self.flight.from_cp.position.x, self.flight.from_cp.position.y, 0)
takeoff.description = "Take Off" takeoff.description = "Take Off"
takeoff.name = takeoff.pretty_name = "Take Off from " + self.flight.from_cp.name takeoff.name = takeoff.pretty_name = "Take Off from " + self.flight.from_cp.name
self.model.appendRow(QWaypointItem(takeoff, 0)) self.model.appendRow(QWaypointItem(takeoff, 0))
self.model.setItem(0, 1, QStandardItem("0 ft AGL"))
for i, point in enumerate(self.flight.points): 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) 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 PySide2.QtWidgets import QFrame, QGridLayout, QLabel, QPushButton, QVBoxLayout
from game import Game
from gen.flights.flight import Flight 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.QFlightWaypointList import QFlightWaypointList
from qt_ui.windows.mission.flight.waypoints.QPredefinedWaypointSelectionWindow import QPredefinedWaypointSelectionWindow from qt_ui.windows.mission.flight.waypoints.QPredefinedWaypointSelectionWindow import QPredefinedWaypointSelectionWindow
from game import Game
class QFlightWaypointTab(QFrame): class QFlightWaypointTab(QFrame):
on_flight_changed = Signal()
def __init__(self, game: Game, flight: Flight): def __init__(self, game: Game, flight: Flight):
super(QFlightWaypointTab, self).__init__() super(QFlightWaypointTab, self).__init__()
self.flight = flight self.flight = flight
self.game = game self.game = game
self.planner = self.game.planners[self.flight.from_cp.id]
self.init_ui() self.init_ui()
def init_ui(self): def init_ui(self):
@@ -19,10 +28,44 @@ class QFlightWaypointTab(QFrame):
self.flight_waypoint_list = QFlightWaypointList(self.flight) self.flight_waypoint_list = QFlightWaypointList(self.flight)
self.open_fast_waypoint_button = QPushButton("Add Waypoint") self.open_fast_waypoint_button = QPushButton("Add Waypoint")
self.open_fast_waypoint_button.clicked.connect(self.on_fast_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 = QPushButton("Delete Selected")
self.delete_selected.clicked.connect(self.on_delete_waypoint) 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.open_fast_waypoint_button)
rlayout.addWidget(self.delete_selected) rlayout.addWidget(self.delete_selected)
rlayout.addStretch() rlayout.addStretch()
@@ -34,7 +77,53 @@ class QFlightWaypointTab(QFrame):
if wpt > 0: if wpt > 0:
del self.flight.points[wpt-1] del self.flight.points[wpt-1]
self.flight_waypoint_list.update_list() self.flight_waypoint_list.update_list()
self.on_change()
def on_fast_waypoint(self): def on_fast_waypoint(self):
self.subwindow = QPredefinedWaypointSelectionWindow(self.game, self.flight, self.flight_waypoint_list) 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.QtCore import Qt
from PySide2.QtWidgets import QDialog, QGridLayout, QLabel, QComboBox, QHBoxLayout, QVBoxLayout, QPushButton, QCheckBox from PySide2.QtWidgets import QDialog, QLabel, QHBoxLayout, QVBoxLayout, QPushButton, QCheckBox
from dcs import Point
from game import Game 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.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 qt_ui.windows.mission.flight.waypoints.QFlightWaypointInfoBox import QFlightWaypointInfoBox
from theater import ControlPointType
PREDEFINED_WAYPOINT_CATEGORIES = [ PREDEFINED_WAYPOINT_CATEGORIES = [
"Frontline (CAS AREA)", "Frontline (CAS AREA)",

View File

@@ -1,12 +1,14 @@
from PySide2.QtCore import QSize, Qt, QItemSelectionModel, QPoint from PySide2.QtCore import QSize, Qt, QItemSelectionModel, QPoint
from PySide2.QtGui import QStandardItemModel, QStandardItem from PySide2.QtGui import QStandardItemModel, QStandardItem
from PySide2.QtWidgets import QLabel, QDialog, QGridLayout, QListView, QStackedLayout, QComboBox, QWidget, \ 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 import qt_ui.uiconstants as CONST
from game.game import Game from game.game import Game
from game.infos.information import Information from game.infos.information import Information
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from qt_ui.windows.finances.QFinancesMenu import QHorizontalSeparationLine
class QSettingsWindow(QDialog): class QSettingsWindow(QDialog):
@@ -118,6 +120,32 @@ class QSettingsWindow(QDialog):
self.difficultyLayout.addWidget(QLabel("No night missions"), 4, 0) self.difficultyLayout.addWidget(QLabel("No night missions"), 4, 0)
self.difficultyLayout.addWidget(self.noNightMission, 4, 1, Qt.AlignRight) 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): def initGeneratorLayout(self):
self.generatorPage = QWidget() self.generatorPage = QWidget()
@@ -134,8 +162,14 @@ class QSettingsWindow(QDialog):
self.supercarrier.setChecked(self.game.settings.supercarrier) self.supercarrier.setChecked(self.game.settings.supercarrier)
self.supercarrier.toggled.connect(self.applySettings) 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(QLabel("Use Supercarrier Module"), 0, 0)
self.gameplayLayout.addWidget(self.supercarrier, 0, 1, Qt.AlignRight) 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.performance = QGroupBox("Performance")
self.performanceLayout = QGridLayout(); 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.setChecked(self.game.settings.perf_ai_parking_start)
self.ai_parking_start.toggled.connect(self.applySettings) 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(QLabel("Smoke visual effect on frontline"), 0, 0)
self.performanceLayout.addWidget(self.smoke, 0, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(self.smoke, 0, 1, alignment=Qt.AlignRight)
self.performanceLayout.addWidget(QLabel("SAM starts in RED alert mode"), 1, 0) 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(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(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(self.gameplay)
self.generatorLayout.addWidget(QLabel("Disabling settings below may improve performance, but will impact the overall quality of the experience.")) self.generatorLayout.addWidget(QLabel("Disabling settings below may improve performance, but will impact the overall quality of the experience."))
self.generatorLayout.addWidget(self.performance) 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.enemy_vehicle_skill = CONST.SKILL_OPTIONS[self.enemyAASkill.currentIndex()]
self.game.settings.labels = CONST.LABELS_OPTIONS[self.difficultyLabel.currentIndex()] self.game.settings.labels = CONST.LABELS_OPTIONS[self.difficultyLabel.currentIndex()]
self.game.settings.night_disabled = self.noNightMission.isChecked() 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.supercarrier = self.supercarrier.isChecked()
self.game.settings.perf_red_alert_state = self.red_alert.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_infantry = self.infantry.isChecked()
self.game.settings.perf_ai_parking_start = self.ai_parking_start.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) GameUpdateSignal.get_instance().updateGame(self.game)
def onSelectionChanged(self): def onSelectionChanged(self):

Binary file not shown.

Before

Width:  |  Height:  |  Size: 800 KiB

After

Width:  |  Height:  |  Size: 800 KiB

BIN
resources/channellandmap.p Normal file

Binary file not shown.

View File

@@ -0,0 +1,69 @@
local unitPayloads = {
["name"] = "B-17G",
["payloads"] = {
[1] = {
["name"] = "STRIKE",
["pylons"] = {
[1] = {
["CLSID"] = "{12xM64}",
["num"] = 1,
},
},
["tasks"] = {
[1] = 32,
},
},
[2] = {
["name"] = "CAP",
["pylons"] = {
[1] = {
["CLSID"] = "{12xM64}",
["num"] = 1,
},
},
["tasks"] = {
[1] = 32,
},
},
[3] = {
["name"] = "SEAD",
["pylons"] = {
[1] = {
["CLSID"] = "{12xM64}",
["num"] = 1,
},
},
["tasks"] = {
[1] = 32,
},
},
[4] = {
["name"] = "ANTISHIP",
["pylons"] = {
[1] = {
["CLSID"] = "{12xM64}",
["num"] = 1,
},
},
["tasks"] = {
[1] = 32,
},
},
[5] = {
["name"] = "CAS",
["pylons"] = {
[1] = {
["CLSID"] = "{12xM64}",
["num"] = 1,
},
},
["tasks"] = {
[1] = 32,
},
},
},
["tasks"] = {
},
["unitType"] = "B-17G",
}
return unitPayloads

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