mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
768049ca36 | ||
|
|
88c8fe7cca | ||
|
|
5e7facfeb6 | ||
|
|
6e43467ef6 | ||
|
|
cd4ef8ae32 | ||
|
|
93a4463f22 | ||
|
|
56cf6bdaa4 | ||
|
|
fe02df27a2 | ||
|
|
a60ab68287 | ||
|
|
83e46ddc97 | ||
|
|
9c89ad7c72 | ||
|
|
0518abdedc | ||
|
|
93bcd07c7f | ||
|
|
5f1f4f8d81 | ||
|
|
e78120d9c6 | ||
|
|
cc5986d435 | ||
|
|
5192306b06 | ||
|
|
826935eb7d | ||
|
|
cd41bcf45c | ||
|
|
601375d06f | ||
|
|
c708abafc8 | ||
|
|
90d588353c | ||
|
|
2d4287df2a | ||
|
|
fde3a988b7 | ||
|
|
397164e667 | ||
|
|
a7824039e3 | ||
|
|
bb9247e821 | ||
|
|
4bc04ec031 | ||
|
|
8fd91e6c5c | ||
|
|
ee137d086a | ||
|
|
fcd81850cb | ||
|
|
aa4b07d024 | ||
|
|
16a096d288 | ||
|
|
b219b2a71b | ||
|
|
ce70242c35 | ||
|
|
dec01e2611 | ||
|
|
4373d89661 | ||
|
|
c73290eebb | ||
|
|
a022f9c2e1 | ||
|
|
5adb25a695 | ||
|
|
08c2972cf9 | ||
|
|
8132c7e676 | ||
|
|
8c68c9f703 | ||
|
|
000b6142fd | ||
|
|
c203ded1cd | ||
|
|
64c5c39b2a | ||
|
|
a8d2a1e371 | ||
|
|
9e5846b24a | ||
|
|
836ff9122c | ||
|
|
75d836358b | ||
|
|
bb11e7f90c | ||
|
|
cf6a71ab86 | ||
|
|
7f7288937d | ||
|
|
a38f9c2183 | ||
|
|
7ee880cadc | ||
|
|
9ae34d474b | ||
|
|
2817e2f2c8 | ||
|
|
02886a09d3 | ||
|
|
0b9d827ad6 | ||
|
|
ab3ea84d70 |
13
README.md
13
README.md
@@ -10,3 +10,16 @@ and [Mist](https://github.com/mrSkortch/MissionScriptingTools) for mission scrip
|
||||
* [Getting Started](https://github.com/Khopa/dcs_liberation/wiki/Getting-started)
|
||||
|
||||
* [Tutorials](https://github.com/Khopa/dcs_liberation/wiki/Tutorial-01-:-UI)
|
||||
|
||||
## Development Guide (WIP)
|
||||
|
||||
Develop is the main development branch which is updated regularly.
|
||||
Master branch will be updated less regularly and on release on new version.
|
||||
|
||||
Other branch might be used for feature development.
|
||||
|
||||
**Note :**
|
||||
If you have errors with pydcs object not being defined, please check that you have the latest version installed. Sometimes the dev branch will use an even more recent version of pydcs that has not been published yet, so you might want to download pydcs directly from the pydcs repository, and copy it in your Python (or virtual env) ./Libs/site-package directory.
|
||||
|
||||
|
||||
|
||||
|
||||
64
__init__.py
64
__init__.py
@@ -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()
|
||||
|
||||
99
changelog.md
Normal file
99
changelog.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# 2.0 RC 8
|
||||
|
||||
## Fixed issues :
|
||||
* **[Mission Generator]** Frequency for P-47D-30 changed to 124Mhz (Generated mission with 251Mhz would not work)
|
||||
* **[Mission Generator]** Reduced the maximum number of uboat per generated group
|
||||
* **[Mission Generator]** Fixed an issue with the WW2 LST groups (superposed units).
|
||||
* **[UI]** Fixed issue with the zoom
|
||||
|
||||
# 2.0 RC 7
|
||||
|
||||
## Features/Improvements :
|
||||
|
||||
* **[Units/Factions]** Added P-47D-30 for factions allies_1944
|
||||
* **[Units/Factions]** Added factions : Bluefor Coldwar, Germany 1944 Easy
|
||||
|
||||
* **[Campaign/Map]** Added a campaign in the Channel map
|
||||
* **[Campaign/Map]** Changed the Normandy campaign map
|
||||
* **[Campaign/Map]** Added new campaign Normandy Small
|
||||
|
||||
* **[Mission Generator]** AI Flight generator has been reworked
|
||||
* **[Mission Generator]** Add PP points for JF-17 on STRIKE missions
|
||||
* **[Mission Generator]** Add ST point for F-14B on STRIKE missions
|
||||
* **[Mission Generator]** Flights with client slots will never be delayed
|
||||
* **[Mission Generator]** AI units can start from parking (With a new setting in Settings Window to disable it)
|
||||
* **[Mission Generator]** Tacan for carrier will only be in Mode X from now
|
||||
* **[Mission Generator]** RTB waypoints for autogenerated flights
|
||||
|
||||
* **[Flight Planner]** Added CAS mission generator
|
||||
* **[Flight Planner]** Added CAP mission generator
|
||||
* **[Flight Planner]** Added SEAD mission generator
|
||||
* **[Flight Planner]** Added STRIKE mission generator
|
||||
* **[Flight Planner]** Added buttons to add autogenerated waypoints (ASCEND, DESCEND, RTB)
|
||||
* **[Flight Planner]** Improved waypoint list
|
||||
* **[Flight Planner]** WW2 factions uses different parameters for flight planning.
|
||||
|
||||
* **[Settings]** Added settings to disallow external views
|
||||
* **[Settings]** Added settings to choose F10 Map mode (All, Allies only, Player only, Fog of War, Map Only)
|
||||
* **[Settings]** Added settings to choose whether to auto-generate objective marks on the F10 map
|
||||
|
||||
* **[Info Panel]** Added information about destroyed buildings in info panel
|
||||
* **[Info Panel]** Added information about destroyed units at SAM site in info panel
|
||||
* **[Debriefing]** Added information about units destroyed outside the frontline in the debriefing window
|
||||
* **[Debriefing]** Added destroyed buildings in the debriefing window
|
||||
|
||||
* **[Map]** Tooltip now contains the list of building for Strike targets on the map
|
||||
* **[Map]** Added "Oil derrick" building
|
||||
* **[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
|
||||
|
||||
* **[Mission Generator]** When playing as RED the activation trigger would not be properly generated
|
||||
* **[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]** Change power station template. (Buildings could end up superposed).
|
||||
|
||||
* **[Maps/Campaign]** Now using Vasiani airbase instead of Soganlung airport in Caucasus campaigns (more parking slot)
|
||||
* **[Info Panel]** Message displayed on base capture event stated that the enemy captured an airbase, while it was the player who captured it.
|
||||
* **[Map View]** Graphical glitch on map when one building of an objective was destroyed, but not the others
|
||||
* **[Mission Planner]** The list of flights was not updated on departure time change.
|
||||
|
||||
|
||||
# 2.0 RC 6
|
||||
|
||||
Saves file from RC5 are not compatible with the new version.
|
||||
Sorry :(
|
||||
|
||||
## Features/Improvements :
|
||||
* **[Units/Factions]** Supercarrier support (You have to go to settings to enable it, if you have the supercarrier module)
|
||||
* **[Units/Factions]** Added 'Modern Bluefor' factions, containing all most popular DCS flyable units
|
||||
* **[Units/Factions]** Factions US 2005 / 1990 will now sometimes have Arleigh Burke class ships instead of Perry as carrier escorts
|
||||
* **[Units/Factions]** Added support for newest WW2 Units
|
||||
* **[Campaign logic]** When a base is captured, refill the "base defenses" group with units for the new owner.
|
||||
* **[Mission Generator]** Carrier ICLS channel will now be configured (check your briefing)
|
||||
* **[Mission Generator]** SAM units will spawn on RED Alarm state
|
||||
* **[Mission Generator]** AI Flight planner now creates its own STRIKE flights
|
||||
* **[Mission Generator]** AI units assigned to Strike flight will now actually engage the buildings they have been assigned.
|
||||
* **[Mission Generator]** Added performance settings to allow disabling : smoke, artillery strike, moving units, infantry, SAM Red alert mode.
|
||||
* **[Mission Generator]** Using Late Activation & Trigger in attempt to improve performance & reduce stutter (Previously they were spawned through 'ETA' feature)
|
||||
* **[UX]** : Improved flight selection behaviour in the Mission Planning Window
|
||||
|
||||
## Fixed issues :
|
||||
* **[Mission Generator]** Payloads were not correctly assigned in the release version.
|
||||
* **[Mission Generator]** Game generation does not work when "no night mission" settings was selected and the current time was "day"
|
||||
* **[Mission Generator]** Game generation does not work when the player selected faction has no AWACS
|
||||
* **[Mission Generator]** Planned flights will spawn even if their home base has been captured or is being contested by enemy ground units.
|
||||
* **[Campaign Generator]** Base defenses would not be generated on Normandy map and in some rare cases on others maps as well
|
||||
* **[Mission Planning]** CAS waypoints created from the "Predefined waypoint selector" would not be at the exact location of the frontline
|
||||
* **[Naming]** CAP mission flown from airbase are not named BARCAP anymore (CAP from carrier is still named BARCAP)
|
||||
21
game/data/aaa_db.py
Normal file
21
game/data/aaa_db.py
Normal 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
|
||||
]
|
||||
16
game/data/building_data.py
Normal file
16
game/data/building_data.py
Normal 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
95
game/data/doctrine.py
Normal 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
54
game/data/radar_db.py
Normal 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,
|
||||
]
|
||||
174
game/db.py
174
game/db.py
@@ -13,9 +13,11 @@ from dcs.unit import *
|
||||
from dcs.unittype import *
|
||||
from dcs.unitgroup import *
|
||||
|
||||
from game.factions.bluefor_coldwar import BLUEFOR_COLDWAR
|
||||
from game.factions.china_2000 import China_2000
|
||||
from game.factions.france_1995 import France_1995
|
||||
from game.factions.france_2005 import France_2005
|
||||
from game.factions.germany_1944_easy import Germany_1944_Easy
|
||||
from game.factions.germany_1990 import Germany_1990
|
||||
from game.factions.insurgent import Insurgent
|
||||
from game.factions.iran_2015 import Iran_2015
|
||||
@@ -44,6 +46,7 @@ from game.factions.usa_1960 import USA_1960
|
||||
from game.factions.usa_1965 import USA_1965
|
||||
from game.factions.usa_1990 import USA_1990
|
||||
from game.factions.usa_2005 import USA_2005
|
||||
from game.factions.bluefor_modern import BLUEFOR_MODERN
|
||||
|
||||
"""
|
||||
---------- BEGINNING OF CONFIGURATION SECTION
|
||||
@@ -83,11 +86,14 @@ PRICES = {
|
||||
J_11A: 26,
|
||||
JF_17: 20,
|
||||
Su_30: 24,
|
||||
SpitfireLFMkIX:3,
|
||||
SpitfireLFMkIXCW:3,
|
||||
Bf_109K_4:3,
|
||||
FW_190D9:3,
|
||||
FW_190A8:3,
|
||||
|
||||
SpitfireLFMkIX: 14,
|
||||
SpitfireLFMkIXCW: 14,
|
||||
Bf_109K_4: 14,
|
||||
FW_190D9: 16,
|
||||
FW_190A8: 14,
|
||||
A_20G: 22,
|
||||
Ju_88A4: 24,
|
||||
|
||||
F_5E_3: 8,
|
||||
MiG_15bis: 4,
|
||||
@@ -135,7 +141,7 @@ PRICES = {
|
||||
OH_58D: 6,
|
||||
|
||||
# Bombers
|
||||
B_52H: 25,
|
||||
B_52H: 35,
|
||||
B_1B: 50,
|
||||
|
||||
# special
|
||||
@@ -146,18 +152,21 @@ PRICES = {
|
||||
S_3B_Tanker: 13,
|
||||
IL_78M: 13,
|
||||
KC_135: 13,
|
||||
KC130: 13,
|
||||
|
||||
A_50: 8,
|
||||
E_3A: 8,
|
||||
C_130: 8,
|
||||
|
||||
# WW2
|
||||
P_51D_30_NA: 3,
|
||||
P_51D: 3,
|
||||
P_51D_30_NA: 18,
|
||||
P_51D: 16,
|
||||
P_47D_30: 18,
|
||||
B_17G: 30,
|
||||
|
||||
# armor
|
||||
Armor.APC_MTLB: 4,
|
||||
Armor.ARV_MTLB_U_BOMAN: 5,
|
||||
Armor.FDDM_Grad: 5,
|
||||
Armor.ARV_BRDM_2: 6,
|
||||
Armor.ARV_BTR_RD: 8,
|
||||
Armor.APC_BTR_80: 8,
|
||||
@@ -227,22 +236,30 @@ PRICES = {
|
||||
AirDefence.SAM_Chaparral_M48: 10,
|
||||
|
||||
# WW2
|
||||
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G:7,
|
||||
Armor.MT_Pz_Kpfw_IV_Ausf_H:4,
|
||||
Armor.HT_Pz_Kpfw_VI_Tiger_I:10,
|
||||
Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II:12,
|
||||
Armor.APC_Sd_Kfz_251:3,
|
||||
Armor.IFV_Sd_Kfz_234_2_Puma:4,
|
||||
Armor.MT_M4_Sherman:4,
|
||||
Armor.MT_M4A4_Sherman_Firefly:6,
|
||||
Armor.CT_Cromwell_IV:8,
|
||||
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G:24,
|
||||
Armor.MT_Pz_Kpfw_IV_Ausf_H:16,
|
||||
Armor.HT_Pz_Kpfw_VI_Tiger_I:24,
|
||||
Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II:26,
|
||||
Armor.TD_Jagdpanther_G1: 18,
|
||||
Armor.TD_Jagdpanzer_IV: 11,
|
||||
Armor.Sd_Kfz_184_Elefant: 18,
|
||||
Armor.APC_Sd_Kfz_251:4,
|
||||
Armor.IFV_Sd_Kfz_234_2_Puma:8,
|
||||
Armor.MT_M4_Sherman:12,
|
||||
Armor.MT_M4A4_Sherman_Firefly:16,
|
||||
Armor.CT_Cromwell_IV:12,
|
||||
Armor.M30_Cargo_Carrier:2,
|
||||
Armor.APC_M2A1:2,
|
||||
AirDefence.AAA_Bofors_40mm:4,
|
||||
AirDefence.AAA_Flak_36:6,
|
||||
AirDefence.AAA_Flak_18:4,
|
||||
Artillery.M12_GMC:2,
|
||||
Artillery.Sturmpanzer_IV_Brummbär:2,
|
||||
Armor.APC_M2A1:4,
|
||||
Armor.ST_Centaur_IV: 10,
|
||||
Armor.HIT_Churchill_VII: 16,
|
||||
Armor.LAC_M8_Greyhound: 8,
|
||||
Armor.TD_M10_GMC: 14,
|
||||
Armor.StuG_III_Ausf__G: 12,
|
||||
AirDefence.AAA_Bofors_40mm: 8,
|
||||
AirDefence.AAA_8_8cm_Flak_36: 8,
|
||||
AirDefence.AAA_8_8cm_Flak_18: 12,
|
||||
Artillery.M12_GMC: 10,
|
||||
Artillery.Sturmpanzer_IV_Brummbär: 10,
|
||||
|
||||
# ship
|
||||
CV_1143_5_Admiral_Kuznetsov: 100,
|
||||
@@ -299,7 +316,7 @@ UNIT_BY_TASK = {
|
||||
FW_190D9,
|
||||
FW_190A8,
|
||||
SpitfireLFMkIXCW,
|
||||
SpitfireLFMkIX
|
||||
SpitfireLFMkIX,
|
||||
],
|
||||
CAS: [
|
||||
F_86F_Sabre,
|
||||
@@ -330,6 +347,10 @@ UNIT_BY_TASK = {
|
||||
Mi_28N,
|
||||
Mi_24V,
|
||||
MiG_27K,
|
||||
A_20G,
|
||||
P_47D_30,
|
||||
Ju_88A4,
|
||||
B_17G
|
||||
],
|
||||
Transport: [
|
||||
IL_76MD,
|
||||
@@ -342,6 +363,7 @@ UNIT_BY_TASK = {
|
||||
Refueling: [
|
||||
IL_78M,
|
||||
KC_135,
|
||||
KC130,
|
||||
S_3B_Tanker,
|
||||
],
|
||||
AWACS: [E_3A, A_50, ],
|
||||
@@ -351,11 +373,11 @@ UNIT_BY_TASK = {
|
||||
Armor.APC_MTLB,
|
||||
Armor.APC_MTLB,
|
||||
Armor.APC_MTLB,
|
||||
Armor.ARV_MTLB_U_BOMAN,
|
||||
Armor.ARV_MTLB_U_BOMAN,
|
||||
Armor.ARV_MTLB_U_BOMAN,
|
||||
Armor.ARV_MTLB_U_BOMAN,
|
||||
Armor.ARV_MTLB_U_BOMAN,
|
||||
Armor.FDDM_Grad,
|
||||
Armor.FDDM_Grad,
|
||||
Armor.FDDM_Grad,
|
||||
Armor.FDDM_Grad,
|
||||
Armor.FDDM_Grad,
|
||||
Armor.ARV_BRDM_2,
|
||||
Armor.ARV_BRDM_2,
|
||||
Armor.ARV_BRDM_2,
|
||||
@@ -450,6 +472,33 @@ UNIT_BY_TASK = {
|
||||
Armor.APC_M2A1,
|
||||
Armor.APC_M2A1,
|
||||
Armor.APC_M2A1,
|
||||
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
|
||||
Armor.MT_Pz_Kpfw_IV_Ausf_H,
|
||||
Armor.HT_Pz_Kpfw_VI_Tiger_I,
|
||||
Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II,
|
||||
Armor.TD_Jagdpanther_G1,
|
||||
Armor.TD_Jagdpanzer_IV,
|
||||
Armor.Sd_Kfz_184_Elefant,
|
||||
Armor.APC_Sd_Kfz_251,
|
||||
Armor.IFV_Sd_Kfz_234_2_Puma,
|
||||
Armor.MT_M4_Sherman,
|
||||
Armor.MT_M4A4_Sherman_Firefly,
|
||||
Armor.CT_Cromwell_IV,
|
||||
Armor.M30_Cargo_Carrier,
|
||||
Armor.M30_Cargo_Carrier,
|
||||
Armor.M30_Cargo_Carrier,
|
||||
Armor.APC_M2A1,
|
||||
Armor.APC_M2A1,
|
||||
Armor.ST_Centaur_IV,
|
||||
Armor.ST_Centaur_IV,
|
||||
Armor.HIT_Churchill_VII,
|
||||
Armor.LAC_M8_Greyhound,
|
||||
Armor.LAC_M8_Greyhound,
|
||||
Armor.TD_M10_GMC,
|
||||
Armor.TD_M10_GMC,
|
||||
Armor.StuG_III_Ausf__G,
|
||||
Artillery.M12_GMC,
|
||||
Artillery.Sturmpanzer_IV_Brummbär,
|
||||
|
||||
Artillery.MLRS_M270,
|
||||
Artillery.SPH_M109_Paladin,
|
||||
@@ -534,6 +583,7 @@ SAM_CONVERT = {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
"""
|
||||
Units that will always be spawned in the air
|
||||
"""
|
||||
@@ -548,7 +598,7 @@ CARRIER_TAKEOFF_BAN = [
|
||||
]
|
||||
|
||||
"""
|
||||
Units separated by country. Currently only Russia and USA are supported.
|
||||
Units separated by country.
|
||||
country : DCS Country name
|
||||
"""
|
||||
FACTIONS = {
|
||||
@@ -562,15 +612,20 @@ FACTIONS = {
|
||||
"China 2000": China_2000,
|
||||
"North Korea 2000": NorthKorea_2000,
|
||||
"Insurgent": Insurgent,
|
||||
"Germany 1944 (WW2 Pack)": Germany_1944,
|
||||
"Germany 1944 Easy (WW2 Pack)": Germany_1944_Easy,
|
||||
|
||||
"Bluefor Modern": BLUEFOR_MODERN,
|
||||
"Bluefor Cold War 1970s": BLUEFOR_COLDWAR,
|
||||
"USA 2005": USA_2005,
|
||||
"USA 1990": USA_1990,
|
||||
"USA 1965": USA_1965,
|
||||
"USA 1960": USA_1960,
|
||||
"USA 1955 (Require WW2 Pack)": USA_1955,
|
||||
"USA 1944 (Require WW2 Pack)": USA_1944,
|
||||
"France 1995": France_1995,
|
||||
"USA 1955 (WW2 Pack)": USA_1955,
|
||||
"Allies 1944 (WW2 Pack)": USA_1944,
|
||||
|
||||
"France 2005": France_2005,
|
||||
"France 1995": France_1995,
|
||||
"Germany 1990": Germany_1990,
|
||||
"Netherlands 1990": Netherlands_1990,
|
||||
"United Kingdown 1990": UnitedKingdom_1990,
|
||||
@@ -583,7 +638,7 @@ FACTIONS = {
|
||||
"Israel 2000": Israel_2000,
|
||||
"Turkey 2005": Turkey_2005,
|
||||
"United Arab Emirates 2005": UAE_2005,
|
||||
"Germany 1944 (Require WW2 Pack)": Germany_1944
|
||||
|
||||
}
|
||||
BLUEFOR_FACTIONS = [FACTIONS[f]["country"] for f in FACTIONS if FACTIONS[f]["side"] == "blue"]
|
||||
print(BLUEFOR_FACTIONS)
|
||||
@@ -637,7 +692,15 @@ PLANE_PAYLOAD_OVERRIDES = {
|
||||
AntishipStrike: "ANTISHIP",
|
||||
GroundAttack: "STRIKE"
|
||||
},
|
||||
|
||||
F_A_18C: {
|
||||
CAP: "CAP HEAVY",
|
||||
Intercept: "CAP HEAVY",
|
||||
CAS: "CAS MAVERICK F",
|
||||
PinpointStrike: "STRIKE",
|
||||
SEAD: "SEAD",
|
||||
AntishipStrike: "ANTISHIP",
|
||||
GroundAttack: "STRIKE"
|
||||
},
|
||||
A_10A: COMMON_OVERRIDE,
|
||||
A_10C: COMMON_OVERRIDE,
|
||||
AV8BNA: COMMON_OVERRIDE,
|
||||
@@ -677,6 +740,15 @@ PLANE_PAYLOAD_OVERRIDES = {
|
||||
L_39C:COMMON_OVERRIDE,
|
||||
Su_17M4: COMMON_OVERRIDE,
|
||||
F_4E: COMMON_OVERRIDE,
|
||||
P_47D_30:COMMON_OVERRIDE,
|
||||
B_17G: COMMON_OVERRIDE,
|
||||
P_51D: COMMON_OVERRIDE,
|
||||
P_51D_30_NA: COMMON_OVERRIDE,
|
||||
FW_190D9: COMMON_OVERRIDE,
|
||||
FW_190A8: COMMON_OVERRIDE,
|
||||
Bf_109K_4: COMMON_OVERRIDE,
|
||||
SpitfireLFMkIXCW: COMMON_OVERRIDE,
|
||||
SpitfireLFMkIX: COMMON_OVERRIDE,
|
||||
|
||||
AH_64D:{
|
||||
CAS: "AGM-114K*16"
|
||||
@@ -741,7 +813,8 @@ TIME_PERIODS = {
|
||||
|
||||
REWARDS = {
|
||||
"power": 4, "warehouse": 2, "fuel": 2, "ammo": 2,
|
||||
"farp": 1, "fob": 1, "factory": 10, "comms": 10, "oil": 10
|
||||
"farp": 1, "fob": 1, "factory": 10, "comms": 10, "oil": 10,
|
||||
"derrick": 8
|
||||
}
|
||||
|
||||
# Base post-turn bonus value
|
||||
@@ -797,6 +870,21 @@ TaskForceDict = typing.Dict[typing.Type[Task], AssignedUnitsDict]
|
||||
StartingPosition = typing.Optional[typing.Union[ShipGroup, StaticGroup, Airport, Point]]
|
||||
|
||||
|
||||
def upgrade_to_supercarrier(unit, name: str):
|
||||
if unit == CVN_74_John_C__Stennis:
|
||||
if name == "CVN-71 Theodore Roosevelt":
|
||||
return CVN_71_Theodore_Roosevelt
|
||||
elif name == "CVN-72 Abraham Lincoln":
|
||||
return CVN_72_Abraham_Lincoln
|
||||
elif name == "CVN-73 George Washington":
|
||||
return CVN_73_George_Washington
|
||||
else:
|
||||
return CVN_71_Theodore_Roosevelt
|
||||
elif unit == CV_1143_5_Admiral_Kuznetsov:
|
||||
return CV_1143_5_Admiral_Kuznetsov_2017
|
||||
else:
|
||||
return unit
|
||||
|
||||
def unit_task(unit: UnitType) -> Task:
|
||||
for task, units in UNIT_BY_TASK.items():
|
||||
if unit in units:
|
||||
@@ -962,14 +1050,6 @@ def _validate_db():
|
||||
assert unit_type not in total_set, "{} is duplicate for task {}".format(unit_type, t)
|
||||
total_set.add(unit_type)
|
||||
|
||||
# check country allegiance
|
||||
for unit_type in total_set:
|
||||
did_find = False
|
||||
for country_units_list in FACTIONS.values():
|
||||
if unit_type in country_units_list["units"]:
|
||||
did_find = True
|
||||
print("WARN : {} not in country list".format(unit_type))
|
||||
|
||||
# check prices
|
||||
for unit_type in total_set:
|
||||
assert unit_type in PRICES, "{} not in prices".format(unit_type)
|
||||
@@ -982,4 +1062,6 @@ class DefaultLiveries:
|
||||
|
||||
OH_58D.Liveries = DefaultLiveries
|
||||
F_16C_50.Liveries = DefaultLiveries
|
||||
P_51D_30_NA.Liveries = DefaultLiveries
|
||||
P_51D_30_NA.Liveries = DefaultLiveries
|
||||
Ju_88A4.Liveries = DefaultLiveries
|
||||
B_17G.Liveries = DefaultLiveries
|
||||
@@ -13,6 +13,7 @@ from theater import *
|
||||
from gen.environmentgen import EnvironmentSettings
|
||||
from gen.conflictgen import Conflict
|
||||
from game.db import assigned_units_from, unitdict_from
|
||||
from theater.start_generator import generate_airbase_defense_group
|
||||
|
||||
from userdata.debriefing import Debriefing
|
||||
from userdata import persistency
|
||||
@@ -126,17 +127,6 @@ class Event:
|
||||
self.operation.current_mission.save(persistency.mission_path_for("liberation_nextturn.miz"))
|
||||
self.environment_settings = self.operation.environment_settings
|
||||
|
||||
def generate_quick(self):
|
||||
pass
|
||||
# TODO : This is not needed anymore. The player can start mission in flight from the flight planner if he want it to be quick.
|
||||
# TODO : remove this method
|
||||
#self.operation.is_awacs_enabled = self.is_awacs_enabled
|
||||
#self.operation.environment_settings = self.environment_settings
|
||||
#
|
||||
#self.operation.prepare(self.game.theater.terrain, is_quick=True)
|
||||
#self.operation.generate()
|
||||
#self.operation.current_mission.save(persistency.mission_path_for("liberation_nextturn_quick.miz"))
|
||||
|
||||
def commit(self, debriefing: Debriefing):
|
||||
|
||||
logging.info("Commiting mission results")
|
||||
@@ -189,16 +179,30 @@ class Event:
|
||||
logging.info("cp {} killing ground object {}".format(cp, ground_object.string_identifier))
|
||||
cp.ground_objects[i].is_dead = True
|
||||
|
||||
info = Information("Building destroyed",
|
||||
ground_object.dcs_identifier + " has been destroyed at location " + ground_object.obj_name,
|
||||
self.game.turn)
|
||||
self.game.informations.append(info)
|
||||
|
||||
|
||||
# -- AA Site groups
|
||||
destroyed_units = 0
|
||||
info = Information("Units destroyed at " + ground_object.obj_name,
|
||||
"",
|
||||
self.game.turn)
|
||||
for i, ground_object in enumerate(cp.ground_objects):
|
||||
if ground_object.dcs_identifier in ["AA", "CARRIER", "LHA"]:
|
||||
for g in ground_object.groups:
|
||||
for u in g.units:
|
||||
if u.name == destroyed_ground_unit_name:
|
||||
g.units.remove(u)
|
||||
destroyed_units = destroyed_units + 1
|
||||
info.text = u.type
|
||||
ucount = sum([len(g.units) for g in ground_object.groups])
|
||||
if ucount == 0:
|
||||
ground_object.is_dead = True
|
||||
if destroyed_units > 0:
|
||||
self.game.informations.append(info)
|
||||
|
||||
# ------------------------------
|
||||
# Captured bases
|
||||
@@ -212,30 +216,40 @@ class Event:
|
||||
id = int(captured.split("||")[0])
|
||||
new_owner_coalition = int(captured.split("||")[1])
|
||||
|
||||
captured_cps = []
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if cp.id == id:
|
||||
|
||||
if cp.captured and new_owner_coalition != coalition:
|
||||
cp.captured = False
|
||||
cp.base.aircraft = {}
|
||||
cp.base.armor = {}
|
||||
cp.base.aa = {}
|
||||
for g in cp.ground_objects:
|
||||
g.groups = []
|
||||
info = Information(cp.name + " lost !",
|
||||
"The ennemy took control of " + cp.name + "\nShame on us !",
|
||||
self.game.turn)
|
||||
info = Information(cp.name + " lost !", "The ennemy took control of " + cp.name + "\nShame on us !", self.game.turn)
|
||||
self.game.informations.append(info)
|
||||
pname = self.game.enemy_name
|
||||
captured_cps.append(cp)
|
||||
elif not(cp.captured) and new_owner_coalition == coalition:
|
||||
cp.captured = True
|
||||
cp.base.aircraft = {}
|
||||
cp.base.armor = {}
|
||||
cp.base.aa = {}
|
||||
for g in cp.ground_objects:
|
||||
g.groups = []
|
||||
info = Information(cp.name + " captured !",
|
||||
"The ennemy took control of " + cp.name + "\nShame on us !",
|
||||
self.game.turn)
|
||||
info = Information(cp.name + " captured !", "We took control of " + cp.name + "! Great job !", self.game.turn)
|
||||
self.game.informations.append(info)
|
||||
pname = self.game.player_name
|
||||
captured_cps.append(cp)
|
||||
else:
|
||||
continue
|
||||
|
||||
cp.base.aircraft = {}
|
||||
cp.base.armor = {}
|
||||
|
||||
airbase_def_id = 0
|
||||
for g in cp.ground_objects:
|
||||
g.groups = []
|
||||
if g.airbase_group and pname != "":
|
||||
generate_airbase_defense_group(airbase_def_id, g, pname, self.game, cp)
|
||||
airbase_def_id = airbase_def_id + 1
|
||||
|
||||
for cp in captured_cps:
|
||||
logging.info("Will run redeploy for " + cp.name)
|
||||
self.redeploy_units(cp)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
@@ -260,6 +274,8 @@ class Event:
|
||||
|
||||
ratio = (1.0 + enemy_casualties) / (1.0 + ally_casualties)
|
||||
|
||||
player_aggresive = cp.stances[enemy_cp.id] in [CombatStance.AGGRESIVE, CombatStance.ELIMINATION, CombatStance.BREAKTHROUGH]
|
||||
|
||||
if ally_units_alive == 0:
|
||||
player_won = False
|
||||
delta = STRONG_DEFEAT_INFLUENCE
|
||||
@@ -282,11 +298,21 @@ class Event:
|
||||
else:
|
||||
delta = DEFEAT_INFLUENCE
|
||||
elif ally_casualties > enemy_casualties:
|
||||
player_won = False
|
||||
if cp.stances[enemy_cp.id] == CombatStance.BREAKTHROUGH:
|
||||
|
||||
if ally_units_alive > 2*enemy_units_alive and player_aggresive:
|
||||
# Even with casualties if the enemy is overwhelmed, they are going to lose ground
|
||||
player_won = True
|
||||
delta = MINOR_DEFEAT_INFLUENCE
|
||||
elif ally_units_alive > 3*enemy_units_alive and player_aggresive:
|
||||
player_won = True
|
||||
delta = STRONG_DEFEAT_INFLUENCE
|
||||
else:
|
||||
delta = STRONG_DEFEAT_INFLUENCE
|
||||
# But is the enemy is not outnumbered, we lose
|
||||
player_won = False
|
||||
if cp.stances[enemy_cp.id] == CombatStance.BREAKTHROUGH:
|
||||
delta = STRONG_DEFEAT_INFLUENCE
|
||||
else:
|
||||
delta = STRONG_DEFEAT_INFLUENCE
|
||||
|
||||
# No progress with defensive strategies
|
||||
if player_won and cp.stances[enemy_cp.id] in [CombatStance.DEFENSIVE, CombatStance.AMBUSH]:
|
||||
@@ -306,13 +332,54 @@ class Event:
|
||||
enemy_cp.base.affect_strength(delta)
|
||||
cp.base.affect_strength(-delta)
|
||||
info = Information("Frontline Report",
|
||||
"Our ground forces from " + cp.name + " are losing ground against the enemy forces from" + enemy_cp.name,
|
||||
"Our ground forces from " + cp.name + " are losing ground against the enemy forces from " + enemy_cp.name,
|
||||
self.game.turn)
|
||||
self.game.informations.append(info)
|
||||
|
||||
def skip(self):
|
||||
pass
|
||||
|
||||
def redeploy_units(self, cp):
|
||||
""""
|
||||
Auto redeploy units to newly captured base
|
||||
"""
|
||||
|
||||
ally_connected_cps = [ocp for ocp in cp.connected_points if cp.captured == ocp.captured]
|
||||
enemy_connected_cps = [ocp for ocp in cp.connected_points if cp.captured != ocp.captured]
|
||||
|
||||
# If the newly captured cp does not have enemy connected cp,
|
||||
# then it is not necessary to redeploy frontline units there.
|
||||
if len(enemy_connected_cps) == 0:
|
||||
return
|
||||
else:
|
||||
# From each ally cp, send reinforcements
|
||||
for ally_cp in ally_connected_cps:
|
||||
total_units_redeployed = 0
|
||||
own_enemy_cp = [ocp for ocp in ally_cp.connected_points if ally_cp.captured != ocp.captured]
|
||||
|
||||
moved_units = {}
|
||||
|
||||
# If the connected base, does not have any more enemy cp connected.
|
||||
# Or if it is not the opponent redeploying forces there (enemy AI will never redeploy all their forces at once)
|
||||
if len(own_enemy_cp) > 0 or not cp.captured:
|
||||
for frontline_unit, count in ally_cp.base.armor.items():
|
||||
moved_units[frontline_unit] = int(count/2)
|
||||
total_units_redeployed = total_units_redeployed + int(count/2)
|
||||
else: # So if the old base, does not have any more enemy cp connected, or if it is an enemy base
|
||||
for frontline_unit, count in ally_cp.base.armor.items():
|
||||
moved_units[frontline_unit] = count
|
||||
total_units_redeployed = total_units_redeployed + count
|
||||
|
||||
cp.base.commision_units(moved_units)
|
||||
ally_cp.base.commit_losses(moved_units)
|
||||
|
||||
if total_units_redeployed > 0:
|
||||
info = Information("Units redeployed", "", self.game.turn)
|
||||
info.text = str(total_units_redeployed) + " units have been redeployed from " + ally_cp.name + " to " + cp.name
|
||||
self.game.informations.append(info)
|
||||
logging.info(info.text)
|
||||
|
||||
|
||||
|
||||
class UnitsDeliveryEvent(Event):
|
||||
informational = True
|
||||
|
||||
63
game/factions/bluefor_coldwar.py
Normal file
63
game/factions/bluefor_coldwar.py
Normal 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"
|
||||
]
|
||||
}
|
||||
82
game/factions/bluefor_modern.py
Normal file
82
game/factions/bluefor_modern.py
Normal file
@@ -0,0 +1,82 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
BLUEFOR_MODERN = {
|
||||
"country": "USA",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
|
||||
F_15C,
|
||||
F_14B,
|
||||
FA_18C_hornet,
|
||||
F_16C_50,
|
||||
JF_17,
|
||||
M_2000C,
|
||||
F_5E_3,
|
||||
Su_27,
|
||||
|
||||
Su_25T,
|
||||
A_10A,
|
||||
A_10C,
|
||||
AV8BNA,
|
||||
AJS37,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
UH_1H,
|
||||
AH_64D,
|
||||
Ka_50,
|
||||
SA342M,
|
||||
SA342L,
|
||||
|
||||
Armor.MBT_M1A2_Abrams,
|
||||
Armor.MBT_Leopard_2,
|
||||
Armor.ATGM_M1134_Stryker,
|
||||
Armor.IFV_M2A2_Bradley,
|
||||
Armor.IFV_Marder,
|
||||
Armor.APC_M1043_HMMWV_Armament,
|
||||
|
||||
Artillery.MLRS_M270,
|
||||
Artillery.SPH_M109_Paladin,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
AirDefence.SAM_Patriot_EPP_III,
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
], "shorad": [
|
||||
AirDefence.SAM_Avenger_M1097,
|
||||
], "aircraft_carrier": [
|
||||
CVN_74_John_C__Stennis,
|
||||
], "helicopter_carrier": [
|
||||
LHA_1_Tarawa,
|
||||
], "destroyer": [
|
||||
Oliver_Hazzard_Perry_class,
|
||||
USS_Arleigh_Burke_IIa,
|
||||
], "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":[
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
China_2000 = {
|
||||
"country": "China",
|
||||
@@ -59,5 +59,7 @@ China_2000 = {
|
||||
], "carrier_names": [
|
||||
"001 Liaoning",
|
||||
"002 Shandong",
|
||||
], "boat":[
|
||||
"ChineseNavyGroupGenerator", "Type54GroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
France_1995 = {
|
||||
"country": "France",
|
||||
@@ -11,7 +11,7 @@ France_1995 = {
|
||||
Mirage_2000_5,
|
||||
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
@@ -40,5 +40,7 @@ France_1995 = {
|
||||
], "shorad": [
|
||||
AirDefence.HQ_7_Self_Propelled_LN,
|
||||
AirDefence.SAM_Roland_ADS
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
France_2005 = {
|
||||
"country": "France",
|
||||
@@ -12,7 +12,7 @@ France_2005 = {
|
||||
FA_18C_hornet, # Standing as Rafale M
|
||||
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
@@ -55,5 +55,7 @@ France_2005 = {
|
||||
"L9013 Mistral",
|
||||
"L9014 Tonerre",
|
||||
"L9015 Dixmude"
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
from dcs.planes import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
from game.data.building_data import WW2_GERMANY_BUILDINGS
|
||||
from game.data.doctrine import WWII_DOCTRINE
|
||||
|
||||
Germany_1944 = {
|
||||
"country": "Third Reich",
|
||||
"side": "red",
|
||||
@@ -9,6 +12,7 @@ Germany_1944 = {
|
||||
FW_190A8,
|
||||
FW_190D9,
|
||||
Bf_109K_4,
|
||||
Ju_88A4,
|
||||
|
||||
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
|
||||
Armor.MT_Pz_Kpfw_IV_Ausf_H,
|
||||
@@ -16,13 +20,26 @@ Germany_1944 = {
|
||||
Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II,
|
||||
Armor.APC_Sd_Kfz_251,
|
||||
Armor.IFV_Sd_Kfz_234_2_Puma,
|
||||
Armor.Sd_Kfz_184_Elefant,
|
||||
Armor.TD_Jagdpanther_G1,
|
||||
Armor.TD_Jagdpanzer_IV,
|
||||
|
||||
Artillery.Sturmpanzer_IV_Brummbär,
|
||||
|
||||
Unarmed.Sd_Kfz_2,
|
||||
Unarmed.Sd_Kfz_7,
|
||||
Unarmed.Kübelwagen_82,
|
||||
|
||||
Infantry.Infantry_Mauser_98,
|
||||
|
||||
AirDefence.AAA_Flak_36,
|
||||
]
|
||||
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
|
||||
}
|
||||
38
game/factions/germany_1944_easy.py
Normal file
38
game/factions/germany_1944_easy.py
Normal 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
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Germany_1990 = {
|
||||
"country": "Germany",
|
||||
@@ -12,7 +12,7 @@ Germany_1990 = {
|
||||
F_4E,
|
||||
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
@@ -39,5 +39,7 @@ Germany_1990 = {
|
||||
"shorad":[
|
||||
AirDefence.SPAAA_Gepard,
|
||||
AirDefence.SAM_Roland_ADS,
|
||||
], "boat":[
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
India_2010 = {
|
||||
"country": "India",
|
||||
@@ -15,7 +15,7 @@ India_2010 = {
|
||||
Su_30,
|
||||
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
@@ -49,5 +49,7 @@ India_2010 = {
|
||||
FSG_1241_1MP_Molniya,
|
||||
], "carrier_names": [
|
||||
"INS Vikramaditya"
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator", "MolniyaGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Iran_2015 = {
|
||||
"country": "Iran",
|
||||
@@ -52,5 +52,7 @@ Iran_2015 = {
|
||||
"shorad":[
|
||||
AirDefence.HQ_7_Self_Propelled_LN,
|
||||
AirDefence.AAA_ZU_23_Insurgent_Closed
|
||||
], "boat":[
|
||||
"GrishaGroupGenerator", "MolniyaGroupGenerator", "KiloSubGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Israel_2000 = {
|
||||
"country": "Israel",
|
||||
@@ -12,7 +12,7 @@ Israel_2000 = {
|
||||
F_4E,
|
||||
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
@@ -31,7 +31,9 @@ Israel_2000 = {
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
],
|
||||
"shorad":[
|
||||
"shorad": [
|
||||
AirDefence.SAM_Avenger_M1097
|
||||
], "boat": [
|
||||
"ArleighBurkeGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Italy_1990 = {
|
||||
"country": "Italy",
|
||||
@@ -42,5 +42,7 @@ Italy_1990 = {
|
||||
], "lha_names": [
|
||||
"Giuseppe Garibaldi",
|
||||
"Cavour",
|
||||
], "boat":[
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Lybia_2011 = {
|
||||
"country": "Russia",
|
||||
@@ -28,7 +27,7 @@ Lybia_2011 = {
|
||||
AirDefence.HQ_7_Self_Propelled_LN,
|
||||
|
||||
Armor.IFV_BMP_1,
|
||||
Armor.ARV_MTLB_U_BOMAN,
|
||||
Armor.FDDM_Grad,
|
||||
Armor.ARV_BRDM_2,
|
||||
Armor.MBT_T_55,
|
||||
Armor.MBT_T_72B,
|
||||
@@ -44,5 +43,7 @@ Lybia_2011 = {
|
||||
"shorad":[
|
||||
AirDefence.HQ_7_Self_Propelled_LN,
|
||||
AirDefence.SAM_SA_8_Osa_9A33,
|
||||
], "boat": [
|
||||
"GrishaGroupGenerator", "MolniyaGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Netherlands_1990 = {
|
||||
"country": "The Netherlands",
|
||||
@@ -11,7 +11,7 @@ Netherlands_1990 = {
|
||||
F_5E_3,
|
||||
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
@@ -30,7 +30,9 @@ Netherlands_1990 = {
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
],
|
||||
"shorad":[
|
||||
"shorad": [
|
||||
AirDefence.SAM_Avenger_M1097
|
||||
], "boat": [
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
NorthKorea_2000 = {
|
||||
"country": "North Korea",
|
||||
@@ -46,5 +46,8 @@ NorthKorea_2000 = {
|
||||
"shorad":[
|
||||
AirDefence.AAA_ZU_23_Emplacement,
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka
|
||||
],
|
||||
"boat": [
|
||||
"GrishaGroupGenerator", "MolniyaGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Pakistan_2015 = {
|
||||
"country": "Pakistan",
|
||||
@@ -34,5 +34,7 @@ Pakistan_2015 = {
|
||||
AirDefence.HQ_7_Self_Propelled_LN,
|
||||
AirDefence.AAA_ZU_23_Insurgent_on_Ural_375,
|
||||
AirDefence.AAA_ZU_23_Closed
|
||||
], "boat": [
|
||||
"Type54GroupGenerator", "OliverHazardPerryGroupGenerator"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ Russia_1955 = {
|
||||
AirDefence.AAA_ZU_23_Closed,
|
||||
AirDefence.AAA_ZU_23_on_Ural_375,
|
||||
Armor.ARV_BRDM_2,
|
||||
Armor.ARV_MTLB_U_BOMAN,
|
||||
Armor.FDDM_Grad,
|
||||
Armor.APC_MTLB,
|
||||
Armor.MBT_T_55,
|
||||
Artillery.MLRS_BM_21_Grad,
|
||||
|
||||
@@ -49,5 +49,7 @@ Russia_1965 = {
|
||||
],
|
||||
"shorad":[
|
||||
AirDefence.AAA_ZU_23_Closed
|
||||
], "boat": [
|
||||
"GrishaGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -65,5 +65,7 @@ Russia_1975 = {
|
||||
FF_1135M_Rezky,
|
||||
], "cruiser": [
|
||||
CGN_1144_2_Pyotr_Velikiy,
|
||||
], "boat": [
|
||||
"RussianNavyGroupGenerator", "KiloSubGroupGenerator", "MolniyaGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Russia_1990 = {
|
||||
"country": "Russia",
|
||||
@@ -64,5 +64,7 @@ Russia_1990 = {
|
||||
FF_1135M_Rezky,
|
||||
], "cruiser": [
|
||||
FSG_1241_1MP_Molniya,
|
||||
], "boat":[
|
||||
"RussianNavyGroupGenerator", "KiloSubGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Russia_2010 = {
|
||||
"country": "Russia",
|
||||
@@ -63,5 +63,7 @@ Russia_2010 = {
|
||||
FF_1135M_Rezky,
|
||||
], "cruiser": [
|
||||
FSG_1241_1MP_Molniya,
|
||||
], "boat": [
|
||||
"RussianNavyGroupGenerator", "KiloSubGroupGenerator"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Spain_1990 = {
|
||||
"country": "Spain",
|
||||
@@ -12,7 +12,7 @@ Spain_1990 = {
|
||||
C_101CC,
|
||||
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
@@ -44,5 +44,7 @@ Spain_1990 = {
|
||||
"Principe de Asturias",
|
||||
], "lhanames": [
|
||||
"Juan Carlos I",
|
||||
], "boat":[
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -25,7 +25,7 @@ Sweden_1990 = {
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160,
|
||||
],
|
||||
"shorad":[
|
||||
"shorad": [
|
||||
AirDefence.SAM_Avenger_M1097
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Turkey_2005 = {
|
||||
"country": "Turkey",
|
||||
@@ -11,7 +11,7 @@ Turkey_2005 = {
|
||||
F_4E,
|
||||
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
@@ -36,5 +36,7 @@ Turkey_2005 = {
|
||||
], "shorad":[
|
||||
AirDefence.AAA_ZU_23_Emplacement,
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka
|
||||
], "boat":[
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
UAE_2005 = {
|
||||
"country": "United Arab Emirates",
|
||||
@@ -12,7 +12,7 @@ UAE_2005 = {
|
||||
F_16C_50,
|
||||
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
@@ -30,5 +30,7 @@ UAE_2005 = {
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
], "boat":[
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
UnitedKingdom_1990 = {
|
||||
"country": "UK",
|
||||
@@ -12,7 +12,7 @@ UnitedKingdom_1990 = {
|
||||
F_4E,
|
||||
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
@@ -46,5 +46,7 @@ UnitedKingdom_1990 = {
|
||||
"HMS Invincible",
|
||||
"HMS Illustrious",
|
||||
"HMS Ark Royal",
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Ukraine_2010 = {
|
||||
"country": "Ukraine",
|
||||
@@ -47,5 +47,7 @@ Ukraine_2010 = {
|
||||
AirDefence.SAM_SA_19_Tunguska_2S6,
|
||||
AirDefence.SAM_SA_13_Strela_10M3_9A35M3,
|
||||
AirDefence.AAA_ZU_23_on_Ural_375
|
||||
], "boat":[
|
||||
"GrishaGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -2,23 +2,35 @@ from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
from game.data.building_data import WW2_ALLIES_BUILDINGS
|
||||
from game.data.doctrine import WWII_DOCTRINE
|
||||
|
||||
USA_1944 = {
|
||||
"country": "USA",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
P_51D,
|
||||
P_51D_30_NA,
|
||||
P_47D_30,
|
||||
SpitfireLFMkIX,
|
||||
SpitfireLFMkIXCW,
|
||||
A_20G,
|
||||
B_17G,
|
||||
|
||||
Armor.MT_M4_Sherman,
|
||||
Armor.MT_M4A4_Sherman_Firefly,
|
||||
Armor.CT_Cromwell_IV,
|
||||
Armor.M30_Cargo_Carrier,
|
||||
Armor.APC_M2A1,
|
||||
Armor.CT_Cromwell_IV,
|
||||
Armor.ST_Centaur_IV,
|
||||
Armor.HIT_Churchill_VII,
|
||||
Armor.LAC_M8_Greyhound,
|
||||
Armor.TD_M10_GMC,
|
||||
Artillery.M12_GMC,
|
||||
|
||||
Infantry.Infantry_M1_Garand,
|
||||
Infantry.Infantry_SMLE_No_4_Mk_1,
|
||||
|
||||
LS_Samuel_Chase,
|
||||
LST_Mk_II,
|
||||
@@ -26,5 +38,11 @@ USA_1944 = {
|
||||
|
||||
Unarmed.CCKW_353,
|
||||
AirDefence.AAA_Bofors_40mm,
|
||||
]
|
||||
], "shorad":[
|
||||
AirDefence.AAA_Bofors_40mm,
|
||||
],
|
||||
"objects": WW2_ALLIES_BUILDINGS,
|
||||
"doctrine": WWII_DOCTRINE,
|
||||
"boat": ["WW2LSTGroupGenerator"],
|
||||
"boat_count": 2
|
||||
}
|
||||
@@ -11,7 +11,7 @@ USA_1955 = {
|
||||
P_51D,
|
||||
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ USA_1960 = {
|
||||
P_51D,
|
||||
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
USA_1965 = {
|
||||
"country": "USA",
|
||||
@@ -12,7 +12,7 @@ USA_1965 = {
|
||||
F_4E,
|
||||
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
@@ -36,5 +36,7 @@ USA_1965 = {
|
||||
"shorad":[
|
||||
AirDefence.AAA_Vulcan_M163,
|
||||
AirDefence.SAM_Chaparral_M48
|
||||
], "boat":[
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
USA_1990 = {
|
||||
"country": "USA",
|
||||
@@ -17,7 +17,7 @@ USA_1990 = {
|
||||
B_1B,
|
||||
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
@@ -46,6 +46,7 @@ USA_1990 = {
|
||||
LHA_1_Tarawa,
|
||||
], "destroyer": [
|
||||
Oliver_Hazzard_Perry_class,
|
||||
USS_Arleigh_Burke_IIa,
|
||||
], "cruiser": [
|
||||
Ticonderoga_class,
|
||||
], "carrier_names": [
|
||||
@@ -58,5 +59,7 @@ USA_1990 = {
|
||||
"LHA-3 Belleau Wood",
|
||||
"LHA-4 Nassau",
|
||||
"LHA-5 Peleliu"
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
USA_2005 = {
|
||||
"country": "USA",
|
||||
@@ -12,12 +12,11 @@ USA_2005 = {
|
||||
FA_18C_hornet,
|
||||
F_16C_50,
|
||||
JF_17,
|
||||
|
||||
A_10C,
|
||||
AV8BNA,
|
||||
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
@@ -52,12 +51,13 @@ USA_2005 = {
|
||||
], "helicopter_carrier": [
|
||||
LHA_1_Tarawa,
|
||||
], "destroyer": [
|
||||
Oliver_Hazzard_Perry_class,
|
||||
USS_Arleigh_Burke_IIa,
|
||||
], "cruiser": [
|
||||
Ticonderoga_class,
|
||||
], "carrier_names": [
|
||||
"CVN-71 Theodore Roosevelt",
|
||||
"CVN-72 Abraham Lincoln",
|
||||
"CVN-73 Georges Washington",
|
||||
"CVN-73 George Washington",
|
||||
"CVN-74 John C. Stennis",
|
||||
], "lhanames": [
|
||||
"LHA-1 Tarawa",
|
||||
@@ -65,5 +65,7 @@ USA_2005 = {
|
||||
"LHA-3 Belleau Wood",
|
||||
"LHA-4 Nassau",
|
||||
"LHA-5 Peleliu"
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator"
|
||||
]
|
||||
}
|
||||
|
||||
100
game/game.py
100
game/game.py
@@ -1,24 +1,11 @@
|
||||
import logging
|
||||
import typing
|
||||
import random
|
||||
import math
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from dcs.task import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
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.infos.information import Information
|
||||
from gen.conflictgen import Conflict
|
||||
from gen.flights.ai_flight_planner import FlightPlanner
|
||||
from gen.ground_forces.ai_ground_planner import GroundPlanner
|
||||
from userdata.debriefing import Debriefing
|
||||
from theater import *
|
||||
|
||||
from . import db
|
||||
from .settings import Settings
|
||||
from .event import *
|
||||
from datetime import datetime, timedelta
|
||||
from .settings import Settings
|
||||
|
||||
COMMISION_UNIT_VARIETY = 4
|
||||
COMMISION_LIMITS_SCALE = 1.5
|
||||
@@ -85,6 +72,16 @@ class Game:
|
||||
self.ground_planners = {}
|
||||
self.informations = []
|
||||
self.informations.append(Information("Game Start", "-" * 40, 0))
|
||||
self.__culling_points = self.compute_conflicts_position()
|
||||
|
||||
|
||||
@property
|
||||
def player_faction(self):
|
||||
return db.FACTIONS[self.player_name]
|
||||
|
||||
@property
|
||||
def enemy_faction(self):
|
||||
return db.FACTIONS[self.enemy_name]
|
||||
|
||||
def _roll(self, prob, mult):
|
||||
if self.settings.version == "dev":
|
||||
@@ -157,12 +154,9 @@ class Game:
|
||||
self.events.remove(event)
|
||||
|
||||
def initiate_event(self, event: Event):
|
||||
assert event in self.events
|
||||
|
||||
#assert event in self.events
|
||||
logging.info("Generating {} (regular)".format(event))
|
||||
event.generate()
|
||||
logging.info("Generating {} (quick)".format(event))
|
||||
event.generate_quick()
|
||||
|
||||
def finish_event(self, event: Event, debriefing: Debriefing):
|
||||
logging.info("Finishing event {}".format(event))
|
||||
@@ -181,7 +175,20 @@ class Game:
|
||||
else:
|
||||
return event.name == self.player_name
|
||||
|
||||
def get_player_coalition_id(self):
|
||||
if self.player_country in db.BLUEFOR_FACTIONS:
|
||||
return 2
|
||||
else:
|
||||
return 1
|
||||
|
||||
def get_enemy_coalition_id(self):
|
||||
if self.get_player_coalition_id() == 1:
|
||||
return 2
|
||||
else:
|
||||
return 1
|
||||
|
||||
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint] = None):
|
||||
|
||||
logging.info("Pass turn")
|
||||
self.informations.append(Information("End of turn #" + str(self.turn), "-" * 40, 0))
|
||||
self.turn = self.turn + 1
|
||||
@@ -197,7 +204,7 @@ class Game:
|
||||
self._enemy_reinforcement()
|
||||
self._budget_player()
|
||||
|
||||
if not no_action:
|
||||
if not no_action and self.turn > 1:
|
||||
for cp in self.theater.player_points():
|
||||
cp.base.affect_strength(+PLAYER_BASE_STRENGTH_RECOVERY)
|
||||
else:
|
||||
@@ -216,6 +223,7 @@ class Game:
|
||||
self.game_stats.update(self)
|
||||
|
||||
# Plan flights & combat for next turn
|
||||
self.__culling_points = self.compute_conflicts_position()
|
||||
self.planners = {}
|
||||
self.ground_planners = {}
|
||||
for cp in self.theater.controlpoints:
|
||||
@@ -230,6 +238,10 @@ class Game:
|
||||
self.ground_planners[cp.id] = gplanner
|
||||
|
||||
def _enemy_reinforcement(self):
|
||||
"""
|
||||
Compute and commision reinforcement for enemy bases
|
||||
"""
|
||||
|
||||
MAX_ARMOR = 30 * self.settings.multiplier
|
||||
MAX_AIRCRAFT = 25 * self.settings.multiplier
|
||||
|
||||
@@ -317,3 +329,49 @@ class Game:
|
||||
"""
|
||||
self.current_group_id += 1
|
||||
return self.current_group_id
|
||||
|
||||
def compute_conflicts_position(self):
|
||||
"""
|
||||
Compute the current conflict center position(s), mainly used for culling calculation
|
||||
:return: List of points of interests
|
||||
"""
|
||||
points = []
|
||||
|
||||
# By default, use the existing frontline conflict position
|
||||
for conflict in self.theater.conflicts():
|
||||
points.append(Conflict.frontline_position(self.theater, conflict[0], conflict[1])[0])
|
||||
|
||||
# If there is no conflict take the center point between the two nearest opposing bases
|
||||
if len(points) == 0:
|
||||
cpoint = None
|
||||
min_distance = sys.maxsize
|
||||
for cp in self.theater.player_points():
|
||||
for cp2 in self.theater.enemy_points():
|
||||
d = cp.position.distance_to_point(cp2.position)
|
||||
if d < min_distance:
|
||||
min_distance = d
|
||||
cpoint = Point((cp.position.x + cp2.position.x) / 2, (cp.position.y + cp2.position.y) / 2)
|
||||
if cpoint is not None:
|
||||
points.append(cpoint)
|
||||
|
||||
# Else 0,0, since we need a default value
|
||||
# (in this case this means the whole map is owned by the same player, so it is not an issue)
|
||||
if len(points) == 0:
|
||||
points.append(Point(0, 0))
|
||||
|
||||
return points
|
||||
|
||||
def position_culled(self, pos):
|
||||
"""
|
||||
Check if unit can be generated at given position depending on culling performance settings
|
||||
:param pos: Position you are tryng to spawn stuff at
|
||||
:return: True if units can not be added at given position
|
||||
"""
|
||||
if self.settings.perf_culling == False:
|
||||
return False
|
||||
else:
|
||||
for c in self.__culling_points:
|
||||
if c.distance_to_point(pos) < self.settings.perf_culling_distance * 1000:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@@ -47,11 +47,9 @@ class GameStats:
|
||||
for cp in game.theater.controlpoints:
|
||||
if cp.captured:
|
||||
turn_data.allied_units.aircraft_count += sum(cp.base.aircraft.values())
|
||||
turn_data.allied_units.sam_count += sum(cp.base.aa.values())
|
||||
turn_data.allied_units.vehicles_count += sum(cp.base.armor.values())
|
||||
else:
|
||||
turn_data.enemy_units.aircraft_count += sum(cp.base.aircraft.values())
|
||||
turn_data.enemy_units.sam_count += sum(cp.base.aa.values())
|
||||
turn_data.enemy_units.vehicles_count += sum(cp.base.armor.values())
|
||||
|
||||
self.data_per_turn.append(turn_data)
|
||||
|
||||
@@ -64,7 +64,7 @@ class Operation:
|
||||
def initialize(self, mission: Mission, conflict: Conflict):
|
||||
self.current_mission = mission
|
||||
self.conflict = conflict
|
||||
self.airgen = AircraftConflictGenerator(mission, conflict, self.game.settings)
|
||||
self.airgen = AircraftConflictGenerator(mission, conflict, self.game.settings, self.game)
|
||||
self.shipgen = ShipGenerator(mission, conflict)
|
||||
self.airsupportgen = AirSupportConflictGenerator(mission, conflict, self.game)
|
||||
self.triggersgen = TriggersGenerator(mission, conflict, self.game)
|
||||
@@ -116,6 +116,12 @@ class Operation:
|
||||
|
||||
def generate(self):
|
||||
|
||||
# Generate meteo
|
||||
if self.environment_settings is None:
|
||||
self.environment_settings = self.envgen.generate()
|
||||
else:
|
||||
self.envgen.load(self.environment_settings)
|
||||
|
||||
# Generate ground object first
|
||||
self.groundobjectgen.generate()
|
||||
|
||||
@@ -156,23 +162,14 @@ class Operation:
|
||||
cp = self.conflict.from_cp
|
||||
else:
|
||||
cp = self.conflict.to_cp
|
||||
|
||||
self.triggersgen.generate(player_cp=cp,
|
||||
is_quick=False,
|
||||
activation_trigger_radius=self.trigger_radius,
|
||||
awacs_enabled=self.is_awacs_enabled)
|
||||
|
||||
# env settings
|
||||
if self.environment_settings is None:
|
||||
self.environment_settings = self.envgen.generate()
|
||||
else:
|
||||
self.envgen.load(self.environment_settings)
|
||||
self.triggersgen.generate()
|
||||
|
||||
# options
|
||||
self.forcedoptionsgen.generate()
|
||||
|
||||
# Generate Visuals Smoke Effects
|
||||
self.visualgen.generate()
|
||||
if self.game.settings.perf_smoke_gen:
|
||||
self.visualgen.generate()
|
||||
|
||||
# Inject Lua Scripts
|
||||
load_mist = TriggerStart(comment="Load Mist Lua Framework")
|
||||
@@ -198,8 +195,6 @@ class Operation:
|
||||
self.briefinggen.append_frequency("AWACS", "133 MHz AM")
|
||||
|
||||
self.briefinggen.append_frequency("Flight", "251 MHz AM")
|
||||
if self.departure_cp.is_global or self.conflict.to_cp.is_global:
|
||||
self.briefinggen.append_frequency("Carrier", "20X/ICLS CHAN1")
|
||||
|
||||
# Generate the briefing
|
||||
self.briefinggen.generate()
|
||||
|
||||
@@ -1,14 +1,32 @@
|
||||
|
||||
class Settings:
|
||||
|
||||
# Difficulty settings
|
||||
player_skill = "Good"
|
||||
enemy_skill = "Average"
|
||||
enemy_vehicle_skill = "Average"
|
||||
map_coalition_visibility = "All Units"
|
||||
labels = "Full"
|
||||
only_player_takeoff = True
|
||||
only_player_takeoff = True # Legacy parameter do not use
|
||||
night_disabled = False
|
||||
external_views_allowed = True
|
||||
supercarrier = False
|
||||
multiplier = 1
|
||||
sams = True
|
||||
cold_start = False
|
||||
generate_marks = True
|
||||
sams = True # Legacy parameter do not use
|
||||
cold_start = False # Legacy parameter do not use
|
||||
version = None
|
||||
|
||||
# Performance oriented
|
||||
perf_red_alert_state = True
|
||||
perf_smoke_gen = True
|
||||
perf_artillery = True
|
||||
perf_moving_units = True
|
||||
perf_infantry = True
|
||||
perf_ai_parking_start = True
|
||||
|
||||
# Performance culling
|
||||
perf_culling = False
|
||||
perf_culling_distance = 100
|
||||
|
||||
|
||||
|
||||
14
game/utils.py
Normal file
14
game/utils.py
Normal 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)
|
||||
578
gen/aircraft.py
578
gen/aircraft.py
@@ -1,53 +1,33 @@
|
||||
import logging
|
||||
|
||||
from dcs.action import ActivateGroup, AITaskPush
|
||||
from dcs.condition import TimeAfter, CoalitionHasAirdrome
|
||||
from dcs.helicopters import UH_1H
|
||||
from dcs.terrain.terrain import NoParkingSlotError
|
||||
from dcs.triggers import TriggerOnce, Event
|
||||
|
||||
from game import db
|
||||
from game.settings import Settings
|
||||
from gen.flights.ai_flight_planner import FlightPlanner
|
||||
from gen.flights.flight import Flight, FlightType
|
||||
from gen.flights.flight import Flight, FlightType, FlightWaypointType
|
||||
from .conflictgen import *
|
||||
from .naming import *
|
||||
from .triggergen import TRIGGER_WAYPOINT_OFFSET
|
||||
|
||||
from dcs.mission import *
|
||||
from dcs.unitgroup import *
|
||||
from dcs.unittype import *
|
||||
from dcs.task import *
|
||||
from dcs.terrain.terrain import NoParkingSlotError, RunwayOccupiedError
|
||||
|
||||
SPREAD_DISTANCE_FACTOR = 1, 2
|
||||
ESCORT_ENGAGEMENT_MAX_DIST = 100000
|
||||
WORKAROUND_WAYP_DIST = 1000
|
||||
|
||||
WARM_START_HELI_AIRSPEED = 120
|
||||
WARM_START_HELI_ALT = 500
|
||||
|
||||
WARM_START_ALTITUDE = 3000
|
||||
WARM_START_AIRSPEED = 550
|
||||
|
||||
INTERCEPTION_AIRSPEED = 1000
|
||||
BARCAP_RACETRACK_DISTANCE = 20000
|
||||
CAP_DURATION = 30 # minutes
|
||||
|
||||
ATTACK_CIRCLE_ALT = 1000
|
||||
ATTACK_CIRCLE_DURATION = 15
|
||||
|
||||
CAS_ALTITUDE = 800
|
||||
RTB_ALTITUDE = 800
|
||||
RTB_DISTANCE = 5000
|
||||
HELI_ALT = 500
|
||||
|
||||
TRANSPORT_LANDING_ALT = 2000
|
||||
|
||||
DEFENCE_ENGAGEMENT_MAX_DISTANCE = 60000
|
||||
INTERCEPT_MAX_DISTANCE = 200000
|
||||
|
||||
|
||||
class AircraftConflictGenerator:
|
||||
escort_targets = [] # type: typing.List[typing.Tuple[FlyingGroup, int]]
|
||||
|
||||
def __init__(self, mission: Mission, conflict: Conflict, settings: Settings):
|
||||
def __init__(self, mission: Mission, conflict: Conflict, settings: Settings, game):
|
||||
self.m = mission
|
||||
self.game = game
|
||||
self.settings = settings
|
||||
self.conflict = conflict
|
||||
self.escort_targets = []
|
||||
@@ -55,43 +35,20 @@ class AircraftConflictGenerator:
|
||||
def _start_type(self) -> StartType:
|
||||
return self.settings.cold_start and StartType.Cold or StartType.Warm
|
||||
|
||||
def _group_point(self, point) -> Point:
|
||||
distance = randint(
|
||||
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[0]),
|
||||
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[1]),
|
||||
)
|
||||
return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_FACTOR[0])
|
||||
|
||||
def _split_to_groups(self, dict: db.PlaneDict, clients: db.PlaneDict = None) -> typing.Collection[typing.Tuple[FlyingType, int, int]]:
|
||||
for flying_type, count in dict.items():
|
||||
if clients:
|
||||
client_count = clients.get(flying_type, 0)
|
||||
else:
|
||||
client_count = 0
|
||||
|
||||
if flying_type == F_14B:
|
||||
# workaround since 2 and 3 tomcat collide on carrier
|
||||
group_size = 2
|
||||
else:
|
||||
group_size = 4
|
||||
|
||||
while count > 0:
|
||||
group_size = min(count, group_size)
|
||||
client_size = max(min(client_count, group_size), 0)
|
||||
|
||||
yield (flying_type, group_size, client_size)
|
||||
count -= group_size
|
||||
client_count -= client_size
|
||||
|
||||
def _setup_group(self, group: FlyingGroup, for_task: typing.Type[Task], client_count: int):
|
||||
did_load_loadout = False
|
||||
unit_type = group.units[0].unit_type
|
||||
|
||||
print("SETUP GROUP : " + str(for_task) + " -- " + str(group.name))
|
||||
|
||||
if unit_type in db.PLANE_PAYLOAD_OVERRIDES:
|
||||
override_loadout = db.PLANE_PAYLOAD_OVERRIDES[unit_type]
|
||||
if type(override_loadout) == dict:
|
||||
|
||||
# Clear pylons
|
||||
for p in group.units:
|
||||
p.pylons.clear()
|
||||
|
||||
# Now load loadout
|
||||
if for_task in db.PLANE_PAYLOAD_OVERRIDES[unit_type]:
|
||||
payload_name = db.PLANE_PAYLOAD_OVERRIDES[unit_type][for_task]
|
||||
group.load_loadout(payload_name)
|
||||
@@ -133,7 +90,7 @@ class AircraftConflictGenerator:
|
||||
if unit_type in helicopters.helicopter_map.values() and unit_type not in [UH_1H]:
|
||||
group.set_frequency(127.5)
|
||||
else:
|
||||
if unit_type not in [P_51D_30_NA, P_51D, SpitfireLFMkIX, SpitfireLFMkIXCW, FW_190A8, FW_190D9, Bf_109K_4]:
|
||||
if unit_type not in [P_51D_30_NA, P_51D, SpitfireLFMkIX, SpitfireLFMkIXCW, FW_190A8, FW_190D9, Bf_109K_4, P_47D_30]:
|
||||
group.set_frequency(251.0)
|
||||
else:
|
||||
# WW2
|
||||
@@ -255,37 +212,6 @@ class AircraftConflictGenerator:
|
||||
else:
|
||||
assert False
|
||||
|
||||
def _generate_escort(self, side: Country, units: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition, cp, is_quick=False, should_orbit=False):
|
||||
groups = []
|
||||
for flying_type, count, client_count in self._split_to_groups(units, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(side, cp.id, flying_type),
|
||||
side=side,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at)
|
||||
|
||||
group.task = Escort.name
|
||||
self._setup_group(group, CAP, client_count)
|
||||
|
||||
for escorted_group, waypoint_index in self.escort_targets:
|
||||
waypoint_index += 1
|
||||
if not is_quick:
|
||||
waypoint_index += TRIGGER_WAYPOINT_OFFSET
|
||||
|
||||
group.points[0].tasks.append(EscortTaskAction(escorted_group.id, engagement_max_dist=ESCORT_ENGAGEMENT_MAX_DIST, lastwpt=waypoint_index))
|
||||
|
||||
if should_orbit:
|
||||
orbit_task = ControlledTask(OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
|
||||
orbit_task.stop_after_duration(ATTACK_CIRCLE_DURATION * 60)
|
||||
|
||||
orbit_waypoint = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE)
|
||||
orbit_waypoint.tasks.append(orbit_task)
|
||||
orbit_waypoint.tasks.append(EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
|
||||
|
||||
groups.append(group)
|
||||
return groups
|
||||
|
||||
def _setup_custom_payload(self, flight, group:FlyingGroup):
|
||||
if flight.use_custom_loadout:
|
||||
@@ -307,47 +233,61 @@ class AircraftConflictGenerator:
|
||||
|
||||
def generate_flights(self, cp, country, flight_planner:FlightPlanner):
|
||||
|
||||
for flight in flight_planner.interceptor_flights:
|
||||
group = self.generate_planned_flight(cp, country, flight)
|
||||
self.setup_group_as_intercept_flight(group, flight)
|
||||
self._setup_custom_payload(flight, group)
|
||||
for flight in flight_planner.flights:
|
||||
|
||||
for flight in flight_planner.cap_flights:
|
||||
group = self.generate_planned_flight(cp, country, flight)
|
||||
self.setup_group_as_cap_flight(group, flight)
|
||||
self._setup_custom_payload(flight, group)
|
||||
if flight.client_count == 0 and self.game.position_culled(flight.from_cp.position):
|
||||
logging.info("Flight not generated : culled")
|
||||
continue
|
||||
|
||||
for flight in flight_planner.cas_flights:
|
||||
group = self.generate_planned_flight(cp, country, flight)
|
||||
self.setup_group_as_cas_flight(group, flight)
|
||||
self._setup_custom_payload(flight, group)
|
||||
|
||||
for flight in flight_planner.sead_flights:
|
||||
group = self.generate_planned_flight(cp, country, flight)
|
||||
self.setup_group_as_sead_flight(group, flight)
|
||||
self._setup_custom_payload(flight, group)
|
||||
|
||||
for flight in flight_planner.custom_flights:
|
||||
group = self.generate_planned_flight(cp, country, flight)
|
||||
if flight.flight_type == FlightType.INTERCEPTION:
|
||||
self.setup_group_as_intercept_flight(group, flight)
|
||||
elif flight.flight_type in [FlightType.CAP, FlightType.TARCAP, FlightType.BARCAP]:
|
||||
self.setup_group_as_cap_flight(group, flight)
|
||||
elif flight.flight_type in [FlightType.CAS, FlightType.BAI]:
|
||||
self.setup_group_as_cas_flight(group, flight)
|
||||
elif flight.flight_type in [FlightType.STRIKE]:
|
||||
self.setup_group_as_strike_flight(group, flight)
|
||||
elif flight.flight_type in [FlightType.ANTISHIP]:
|
||||
self.setup_group_as_antiship_flight(group, flight)
|
||||
elif flight.flight_type in [FlightType.SEAD, FlightType.DEAD]:
|
||||
self.setup_group_as_sead_flight(group, flight)
|
||||
self._setup_custom_payload(flight, group)
|
||||
else:
|
||||
self.setup_group_as_cap_flight(group, flight)
|
||||
self._setup_custom_payload(flight, group)
|
||||
self.setup_flight_group(group, flight, flight.flight_type)
|
||||
self.setup_group_activation_trigger(flight, group)
|
||||
|
||||
|
||||
def setup_group_activation_trigger(self, flight, group):
|
||||
if flight.scheduled_in > 0 and flight.client_count == 0:
|
||||
|
||||
if flight.start_type != "In Flight" and flight.from_cp.cptype not in [ControlPointType.AIRCRAFT_CARRIER_GROUP, ControlPointType.LHA_GROUP]:
|
||||
group.late_activation = False
|
||||
group.uncontrolled = True
|
||||
|
||||
activation_trigger = TriggerOnce(Event.NoEvent, "LiberationControlTriggerForGroup" + str(group.id))
|
||||
activation_trigger.add_condition(TimeAfter(seconds=flight.scheduled_in * 60))
|
||||
if (flight.from_cp.cptype == ControlPointType.AIRBASE):
|
||||
if flight.from_cp.captured:
|
||||
activation_trigger.add_condition(
|
||||
CoalitionHasAirdrome(self.game.get_player_coalition_id(), flight.from_cp.id))
|
||||
else:
|
||||
activation_trigger.add_condition(
|
||||
CoalitionHasAirdrome(self.game.get_enemy_coalition_id(), flight.from_cp.id))
|
||||
|
||||
group.add_trigger_action(StartCommand())
|
||||
activation_trigger.add_action(AITaskPush(group.id, len(group.tasks)))
|
||||
self.m.triggerrules.triggers.append(activation_trigger)
|
||||
else:
|
||||
group.late_activation = True
|
||||
activation_trigger = TriggerOnce(Event.NoEvent, "LiberationActivationTriggerForGroup" + str(group.id))
|
||||
activation_trigger.add_condition(TimeAfter(seconds=flight.scheduled_in*60))
|
||||
|
||||
if(flight.from_cp.cptype == ControlPointType.AIRBASE):
|
||||
if flight.from_cp.captured:
|
||||
activation_trigger.add_condition(CoalitionHasAirdrome(self.game.get_player_coalition_id(), flight.from_cp.id))
|
||||
else:
|
||||
activation_trigger.add_condition(CoalitionHasAirdrome(self.game.get_enemy_coalition_id(), flight.from_cp.id))
|
||||
|
||||
activation_trigger.add_action(ActivateGroup(group.id))
|
||||
self.m.triggerrules.triggers.append(activation_trigger)
|
||||
|
||||
def generate_planned_flight(self, cp, country, flight:Flight):
|
||||
try:
|
||||
if flight.start_type == "In Flight" or flight.client_count == 0:
|
||||
if flight.client_count == 0 and self.game.settings.perf_ai_parking_start:
|
||||
flight.start_type = "Warm"
|
||||
|
||||
if flight.start_type == "In Flight":
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(country, cp.id, flight.unit_type),
|
||||
side=country,
|
||||
@@ -356,7 +296,6 @@ class AircraftConflictGenerator:
|
||||
client_count=0,
|
||||
at=cp.position)
|
||||
else:
|
||||
|
||||
st = StartType.Runway
|
||||
if flight.start_type == "Cold":
|
||||
st = StartType.Cold
|
||||
@@ -383,6 +322,7 @@ class AircraftConflictGenerator:
|
||||
start_type=st)
|
||||
except Exception:
|
||||
# Generated when there is no place on Runway or on Parking Slots
|
||||
flight.start_type = "In Flight"
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(country, cp.id, flight.unit_type),
|
||||
side=country,
|
||||
@@ -391,7 +331,6 @@ class AircraftConflictGenerator:
|
||||
client_count=0,
|
||||
at=cp.position)
|
||||
group.points[0].alt = 1500
|
||||
group.points[0].ETA = flight.scheduled_in * 60
|
||||
|
||||
return group
|
||||
|
||||
@@ -403,53 +342,109 @@ class AircraftConflictGenerator:
|
||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
||||
|
||||
|
||||
def setup_group_as_cap_flight(self, group, flight):
|
||||
self._setup_group(group, CAP, flight.client_count)
|
||||
for point in flight.points:
|
||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
||||
def setup_flight_group(self, group, flight, flight_type):
|
||||
|
||||
def setup_group_as_cas_flight(self, group, flight):
|
||||
group.task = CAS.name
|
||||
self._setup_group(group, CAS, flight.client_count)
|
||||
if flight_type in [FlightType.CAP, FlightType.BARCAP, FlightType.TARCAP]:
|
||||
group.task = CAP.name
|
||||
self._setup_group(group, CAP, flight.client_count)
|
||||
# group.points[0].tasks.clear()
|
||||
# group.tasks.clear()
|
||||
# group.tasks.append(EngageTargets(max_distance=40, targets=[Targets.All.Air]))
|
||||
# group.tasks.append(EngageTargets(max_distance=nm_to_meter(120), targets=[Targets.All.Air]))
|
||||
pass
|
||||
elif flight_type in [FlightType.CAS, FlightType.BAI]:
|
||||
group.task = CAS.name
|
||||
self._setup_group(group, CAS, flight.client_count)
|
||||
group.points[0].tasks.clear()
|
||||
group.points[0].tasks.append(CASTaskAction())
|
||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFireWeaponFree))
|
||||
elif flight_type in [FlightType.SEAD, FlightType.DEAD]:
|
||||
group.task = SEAD.name
|
||||
self._setup_group(group, SEAD, flight.client_count)
|
||||
group.points[0].tasks.clear()
|
||||
group.points[0].tasks.append(NoTask())
|
||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFire))
|
||||
group.points[0].tasks.append(OptRestrictJettison(True))
|
||||
elif flight_type in [FlightType.STRIKE]:
|
||||
group.task = PinpointStrike.name
|
||||
self._setup_group(group, GroundAttack, flight.client_count)
|
||||
group.points[0].tasks.clear()
|
||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFire))
|
||||
group.points[0].tasks.append(OptRestrictJettison(True))
|
||||
elif flight_type in [FlightType.ANTISHIP]:
|
||||
group.task = AntishipStrike.name
|
||||
self._setup_group(group, AntishipStrike, flight.client_count)
|
||||
group.points[0].tasks.clear()
|
||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFire))
|
||||
group.points[0].tasks.append(OptRestrictJettison(True))
|
||||
|
||||
group.points[0].tasks.clear()
|
||||
group.points[0].tasks.append(CASTaskAction())
|
||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFireWeaponFree))
|
||||
group.points[0].tasks.append(OptRestrictJettison(True))
|
||||
for i, point in enumerate(flight.points):
|
||||
if not point.only_for_player or (point.only_for_player and flight.client_count > 0):
|
||||
pt = group.add_waypoint(Point(point.x, point.y), point.alt)
|
||||
if point.waypoint_type == FlightWaypointType.PATROL_TRACK:
|
||||
action = ControlledTask(OrbitAction(altitude=pt.alt, pattern=OrbitAction.OrbitPattern.RaceTrack))
|
||||
action.stop_after_duration(CAP_DURATION * 60)
|
||||
#for tgt in point.targets:
|
||||
# if hasattr(tgt, "position"):
|
||||
# engagetgt = EngageTargetsInZone(tgt.position, radius=CAP_DEFAULT_ENGAGE_DISTANCE, targets=[Targets.All.Air])
|
||||
# pt.tasks.append(engagetgt)
|
||||
elif point.waypoint_type == FlightWaypointType.LANDING_POINT:
|
||||
pt.type = "Land"
|
||||
elif point.waypoint_type == FlightWaypointType.INGRESS_STRIKE:
|
||||
|
||||
for point in flight.points:
|
||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
||||
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:
|
||||
|
||||
def setup_group_as_sead_flight(self, group, flight):
|
||||
group.task = SEAD.name
|
||||
self._setup_group(group, SEAD, flight.client_count)
|
||||
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)
|
||||
|
||||
group.points[0].tasks.clear()
|
||||
group.points[0].tasks.append(SEADTaskAction())
|
||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFireWeaponFree))
|
||||
group.points[0].tasks.append(OptRestrictJettison(True))
|
||||
for j, t in enumerate(point.targets):
|
||||
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")
|
||||
|
||||
i = 1
|
||||
for point in flight.points:
|
||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
||||
group.points[i].tasks.clear()
|
||||
group.points[i].tasks.append(SEADTaskAction())
|
||||
i = i + 1
|
||||
if pt is not None:
|
||||
pt.alt_type = point.alt_type
|
||||
pt.name = String(point.name)
|
||||
|
||||
def setup_group_as_strike_flight(self, group, flight):
|
||||
group.task = PinpointStrike.name
|
||||
self._setup_group(group, GroundAttack, flight.client_count)
|
||||
self._setup_custom_payload(flight, group)
|
||||
|
||||
group.points[0].tasks.clear()
|
||||
group.points[0].tasks.append(CASTaskAction())
|
||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFireWeaponFree))
|
||||
group.points[0].tasks.append(OptRestrictJettison(True))
|
||||
|
||||
for point in flight.points:
|
||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
||||
|
||||
def setup_group_as_antiship_flight(self, group, flight):
|
||||
group.task = AntishipStrike.name
|
||||
@@ -464,263 +459,4 @@ class AircraftConflictGenerator:
|
||||
for point in flight.points:
|
||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
||||
|
||||
def generate_cas_strikegroup(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None, escort=True):
|
||||
assert not escort or len(self.escort_targets) == 0
|
||||
|
||||
for flying_type, count, client_count in self._split_to_groups(attackers, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
|
||||
side=self.conflict.attackers_country,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||
|
||||
waypoint = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
if self.conflict.is_vector:
|
||||
self._add_radio_waypoint(group, self.conflict.tail, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
|
||||
group.task = CAS.name
|
||||
self._setup_group(group, CAS, client_count)
|
||||
if escort:
|
||||
self.escort_targets.append((group, group.points.index(waypoint)))
|
||||
self._rtb_for(group, self.conflict.from_cp, at)
|
||||
|
||||
def generate_ground_attack_strikegroup(self, strikegroup: db.PlaneDict, clients: db.PlaneDict, targets: typing.List[typing.Tuple[str, Point]], at: db.StartingPosition = None, escort=True):
|
||||
assert not escort or len(self.escort_targets) == 0
|
||||
|
||||
for flying_type, count, client_count in self._split_to_groups(strikegroup, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
|
||||
side=self.conflict.attackers_country,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||
|
||||
escort_until_waypoint = None
|
||||
|
||||
for name, pos in targets:
|
||||
waypoint = group.add_waypoint(pos, 0, WARM_START_AIRSPEED, self.m.translation.create_string(name))
|
||||
waypoint.tasks.append(Bombing(pos, attack_qty=2))
|
||||
if escort_until_waypoint is None:
|
||||
escort_until_waypoint = waypoint
|
||||
|
||||
group.task = GroundAttack.name
|
||||
self._setup_group(group, GroundAttack, client_count)
|
||||
if escort:
|
||||
self.escort_targets.append((group, group.points.index(escort_until_waypoint)))
|
||||
self._rtb_for(group, self.conflict.from_cp, at)
|
||||
|
||||
def generate_sead_strikegroup(self, strikegroup: db.PlaneDict, clients: db.PlaneDict, targets: typing.List[typing.Tuple[str, Point]], at: db.StartingPosition, escort=True):
|
||||
assert not escort or len(self.escort_targets) == 0
|
||||
|
||||
for flying_type, count, client_count in self._split_to_groups(strikegroup, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
|
||||
side=self.conflict.attackers_country,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||
|
||||
escort_until_waypoint = None
|
||||
|
||||
for name, pos in targets:
|
||||
waypoint = group.add_waypoint(pos, 0, WARM_START_AIRSPEED, self.m.translation.create_string(name))
|
||||
if escort_until_waypoint is None:
|
||||
escort_until_waypoint = waypoint
|
||||
|
||||
group.task = SEAD.name
|
||||
self._setup_group(group, SEAD, client_count)
|
||||
if escort:
|
||||
self.escort_targets.append((group, group.points.index(escort_until_waypoint)))
|
||||
|
||||
self._rtb_for(group, self.conflict.from_cp, at)
|
||||
|
||||
def generate_defenders_cas(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None, escort=True):
|
||||
assert not escort or len(self.escort_targets) == 0
|
||||
|
||||
for flying_type, count, client_count in self._split_to_groups(defenders, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.defenders_country, self.conflict.to_cp.id, flying_type),
|
||||
side=self.conflict.defenders_country,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
||||
|
||||
location = self._group_point(self.conflict.air_defenders_location)
|
||||
insertion_point = self.conflict.find_insertion_point(location)
|
||||
waypoint = self._add_radio_waypoint(group, insertion_point, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
|
||||
if self.conflict.is_vector:
|
||||
destination_tail = self.conflict.tail.distance_to_point(insertion_point) > self.conflict.position.distance_to_point(insertion_point)
|
||||
self._add_radio_waypoint(group, destination_tail and self.conflict.tail or self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
|
||||
group.task = CAS.name
|
||||
self._setup_group(group, CAS, client_count)
|
||||
if escort:
|
||||
self.escort_targets.append((group, group.points.index(waypoint)))
|
||||
self._rtb_for(group, self.conflict.to_cp, at)
|
||||
|
||||
def generate_ship_strikegroup(self, attackers: db.PlaneDict, clients: db.PlaneDict, target_groups: typing.Collection[ShipGroup], at: db.StartingPosition = None, escort=True):
|
||||
assert not escort or len(self.escort_targets) == 0
|
||||
|
||||
for flying_type, count, client_count in self._split_to_groups(attackers, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
|
||||
side=self.conflict.attackers_country,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||
|
||||
wayp = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
for target_group in target_groups:
|
||||
wayp.tasks.append(AttackGroup(target_group.id))
|
||||
|
||||
group.task = AntishipStrike.name
|
||||
self._setup_group(group, AntishipStrike, client_count)
|
||||
if escort:
|
||||
self.escort_targets.append((group, group.points.index(wayp)))
|
||||
self._rtb_for(group, self.conflict.from_cp, at)
|
||||
|
||||
def generate_attackers_escort(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||
for g in self._generate_escort(
|
||||
side=self.conflict.attackers_country,
|
||||
units=attackers,
|
||||
clients=clients,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location),
|
||||
is_quick=at is None,
|
||||
cp=self.conflict.from_cp,
|
||||
should_orbit=True):
|
||||
self._rtb_for(g, self.conflict.from_cp, at)
|
||||
|
||||
def generate_defenders_escort(self, escort: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||
for g in self._generate_escort(
|
||||
side=self.conflict.defenders_country,
|
||||
units=escort,
|
||||
clients=clients,
|
||||
at=at and at or self._group_point(self.conflict.air_defenders_location),
|
||||
is_quick=at is None,
|
||||
cp=self.conflict.to_cp,
|
||||
should_orbit=False):
|
||||
self._rtb_for(g, self.conflict.to_cp, at)
|
||||
|
||||
def generate_defense(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||
for flying_type, count, client_count in self._split_to_groups(defenders, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.to_cp.id, flying_type),
|
||||
side=self.conflict.defenders_country,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
||||
|
||||
group.task = CAP.name
|
||||
wayp = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
wayp.tasks.append(dcs.task.EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
|
||||
wayp.tasks.append(dcs.task.OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
|
||||
self._setup_group(group, CAP, client_count)
|
||||
self._rtb_for(group, self.conflict.to_cp, at)
|
||||
|
||||
def generate_migcap(self, patrol: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||
for flying_type, count, client_count in self._split_to_groups(patrol, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
|
||||
side=self.conflict.attackers_country,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||
|
||||
waypoint = self._add_radio_waypoint(group, self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||
if self.conflict.is_vector:
|
||||
self._add_radio_waypoint(group, self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||
|
||||
group.task = CAP.name
|
||||
self._setup_group(group, CAP, client_count)
|
||||
self._rtb_for(group, self.conflict.from_cp, at)
|
||||
|
||||
def generate_barcap(self, patrol: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||
for flying_type, count, client_count in self._split_to_groups(patrol, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
|
||||
side=self.conflict.defenders_country,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
||||
|
||||
waypoint = self._add_radio_waypoint(group, self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||
if self.conflict.is_vector:
|
||||
self._add_radio_waypoint(group, self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||
else:
|
||||
heading = group.position.heading_between_point(self.conflict.position)
|
||||
waypoint = self._add_radio_waypoint(group, self.conflict.position.point_from_heading(heading, BARCAP_RACETRACK_DISTANCE),
|
||||
WARM_START_ALTITUDE,
|
||||
WARM_START_AIRSPEED)
|
||||
waypoint.tasks.append(OrbitAction(WARM_START_ALTITUDE, WARM_START_AIRSPEED))
|
||||
|
||||
group.task = CAP.name
|
||||
self._setup_group(group, CAP, client_count)
|
||||
self._rtb_for(group, self.conflict.to_cp, at)
|
||||
|
||||
def generate_transport(self, transport: db.PlaneDict, destination: Airport, escort=True):
|
||||
assert not escort or len(self.escort_targets) == 0
|
||||
|
||||
for flying_type, count, client_count in self._split_to_groups(transport):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
|
||||
side=self.conflict.defenders_country,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=self._group_point(self.conflict.air_defenders_location))
|
||||
|
||||
waypoint = self._rtb_for(group, self.conflict.to_cp)
|
||||
if escort:
|
||||
self.escort_targets.append((group, group.points.index(waypoint)))
|
||||
|
||||
self._add_radio_waypoint(group, destination.position, RTB_ALTITUDE)
|
||||
group.task = Transport.name
|
||||
group.land_at(destination)
|
||||
|
||||
def generate_interception(self, interceptors: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||
for flying_type, count, client_count in self._split_to_groups(interceptors, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
|
||||
side=self.conflict.attackers_country,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||
|
||||
group.task = CAP.name
|
||||
group.points[0].tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
||||
|
||||
wayp = self._add_radio_waypoint(group, self.conflict.position, WARM_START_ALTITUDE, INTERCEPTION_AIRSPEED)
|
||||
wayp.tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
||||
|
||||
if self.conflict.is_vector:
|
||||
self._add_radio_waypoint(group, self.conflict.tail, CAS_ALTITUDE, WARM_START_ALTITUDE)
|
||||
|
||||
self._setup_group(group, CAP, client_count)
|
||||
self._rtb_for(group, self.conflict.from_cp, at)
|
||||
|
||||
def generate_passenger_transport(self, helis: db.HeliDict, clients: db.HeliDict, at: db.StartingPosition):
|
||||
for heli_type, count, client_count in self._split_to_groups(helis, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, heli_type),
|
||||
side=self.conflict.attackers_country,
|
||||
unit_type=heli_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location)
|
||||
)
|
||||
|
||||
self._add_radio_waypoint(group, self.conflict.position, HELI_ALT)
|
||||
self._setup_group(group, Transport, client_count)
|
||||
|
||||
|
||||
|
||||
@@ -53,17 +53,20 @@ class AirSupportConflictGenerator:
|
||||
tanker_group.points[0].tasks.append(SetImmortalCommand(True))
|
||||
|
||||
if is_awacs_enabled:
|
||||
awacs_unit = db.find_unittype(AWACS, self.conflict.attackers_side)[0]
|
||||
awacs_flight = self.mission.awacs_flight(
|
||||
country=self.mission.country(self.game.player_country),
|
||||
name=namegen.next_awacs_name(self.mission.country(self.game.player_country)),
|
||||
plane_type=awacs_unit,
|
||||
altitude=AWACS_ALT,
|
||||
airport=None,
|
||||
position=self.conflict.position.random_point_within(AWACS_DISTANCE, AWACS_DISTANCE),
|
||||
frequency=133,
|
||||
start_type=StartType.Warm,
|
||||
)
|
||||
try:
|
||||
awacs_unit = db.find_unittype(AWACS, self.conflict.attackers_side)[0]
|
||||
awacs_flight = self.mission.awacs_flight(
|
||||
country=self.mission.country(self.game.player_country),
|
||||
name=namegen.next_awacs_name(self.mission.country(self.game.player_country)),
|
||||
plane_type=awacs_unit,
|
||||
altitude=AWACS_ALT,
|
||||
airport=None,
|
||||
position=self.conflict.position.random_point_within(AWACS_DISTANCE, AWACS_DISTANCE),
|
||||
frequency=133,
|
||||
start_type=StartType.Warm,
|
||||
)
|
||||
awacs_flight.points[0].tasks.append(SetInvisibleCommand(True))
|
||||
awacs_flight.points[0].tasks.append(SetImmortalCommand(True))
|
||||
except:
|
||||
print("No AWACS for faction")
|
||||
|
||||
awacs_flight.points[0].tasks.append(SetInvisibleCommand(True))
|
||||
awacs_flight.points[0].tasks.append(SetImmortalCommand(True))
|
||||
|
||||
15
gen/armor.py
15
gen/armor.py
@@ -96,6 +96,11 @@ class GroundConflictGenerator:
|
||||
|
||||
|
||||
def gen_infantry_group_for_group(self, group, is_player, side:Country, forward_heading):
|
||||
|
||||
# Disable infantry unit gen if disabled
|
||||
if not self.game.settings.perf_infantry:
|
||||
return
|
||||
|
||||
infantry_position = group.points[0].position.random_point_within(250, 50)
|
||||
|
||||
if side == self.conflict.attackers_country:
|
||||
@@ -135,12 +140,16 @@ class GroundConflictGenerator:
|
||||
|
||||
def plan_action_for_groups(self, stance, ally_groups, enemy_groups, forward_heading, from_cp, to_cp):
|
||||
|
||||
if not self.game.settings.perf_moving_units:
|
||||
return
|
||||
|
||||
for dcs_group, group in ally_groups:
|
||||
if group.role == CombatGroupRole.ARTILLERY:
|
||||
# Fire on any ennemy in range
|
||||
target = self.get_artillery_target_in_range(dcs_group, group, enemy_groups)
|
||||
if target is not None:
|
||||
dcs_group.points[0].tasks.append(FireAtPoint(target, len(group.units) * 10, 100))
|
||||
if self.game.settings.perf_artillery:
|
||||
target = self.get_artillery_target_in_range(dcs_group, group, enemy_groups)
|
||||
if target is not None:
|
||||
dcs_group.points[0].tasks.append(FireAtPoint(target, len(group.units) * 10, 100))
|
||||
elif group.role in [CombatGroupRole.TANK, CombatGroupRole.IFV]:
|
||||
if stance == CombatStance.AGGRESIVE:
|
||||
# Attack nearest enemy if any
|
||||
|
||||
@@ -39,41 +39,60 @@ class BriefingGenerator:
|
||||
return
|
||||
|
||||
flight_unit_name = db.unit_type_name(flight.unit_type)
|
||||
self.description += 2 * "\n" + "-" * 50 + "\n"
|
||||
self.description += "-" * 50 + "\n"
|
||||
self.description += flight_unit_name + " x " + str(flight.count) + 2 * "\n"
|
||||
|
||||
self.description += "#0 -- TAKEOFF : Take off\n"
|
||||
self.description += "#0 -- TAKEOFF : Take off from " + flight.from_cp.name + "\n"
|
||||
for i, wpt in enumerate(flight.points):
|
||||
self.description += "#" + str(1+i) + " -- " + wpt.name + " : " + wpt.description + "\n"
|
||||
self.description += "#" + str(len(flight.points) + 1) + " -- RTB\n"
|
||||
|
||||
self.description += "-" * 50 + "\n"
|
||||
|
||||
def add_ally_flight_description(self, flight):
|
||||
if flight.client_count == 0:
|
||||
flight_unit_name = db.unit_type_name(flight.unit_type)
|
||||
self.description += flight.flight_type.name + " " + flight_unit_name + " x " + str(flight.count) + ", departing in " + str(flight.scheduled_in) + " minutes \n"
|
||||
|
||||
|
||||
def generate(self):
|
||||
|
||||
self.description = ""
|
||||
|
||||
self.description += "DCS Liberation turn #" + str(self.game.turn) + "\n"
|
||||
self.description += "-"*50 + "\n"
|
||||
self.description += "=" * 15 + "\n\n"
|
||||
|
||||
self.description += "Current situation:\n"
|
||||
self.description += "=" * 15 + "\n\n"
|
||||
|
||||
self.description += "\n"*2
|
||||
self.description += "Your flights:" + "\n"
|
||||
self.description += "=" * 15 + "\n\n"
|
||||
|
||||
for planner in self.game.planners.values():
|
||||
for flight in planner.cap_flights:
|
||||
self.add_flight_description(flight)
|
||||
for flight in planner.cas_flights:
|
||||
self.add_flight_description(flight)
|
||||
for flight in planner.sead_flights:
|
||||
for flight in planner.flights:
|
||||
self.add_flight_description(flight)
|
||||
|
||||
self.description += "\n"*2
|
||||
self.description += "Planned ally flights:" + "\n"
|
||||
self.description += "=" * 15 + "\n"
|
||||
for planner in self.game.planners.values():
|
||||
if planner.from_cp.captured and len(planner.flights) > 0:
|
||||
self.description += "\nFrom " + planner.from_cp.full_name + " \n"
|
||||
self.description += "-" * 50 + "\n\n"
|
||||
for flight in planner.flights:
|
||||
self.add_ally_flight_description(flight)
|
||||
|
||||
if self.freqs:
|
||||
self.description += "\n\nComms Frequencies:\n"
|
||||
self.description += "-" * 50 + "\n"
|
||||
self.description += "=" * 15 + "\n"
|
||||
for name, freq in self.freqs:
|
||||
self.description += "\n{}: {}".format(name, freq)
|
||||
|
||||
self.description += "{}: {}\n".format(name, freq)
|
||||
self.description += ("-" * 50) + "\n"
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if cp.captured and cp.cptype in [ControlPointType.LHA_GROUP, ControlPointType.AIRCRAFT_CARRIER_GROUP]:
|
||||
self.description += "\n"
|
||||
self.description += cp.name + " TACAN : "
|
||||
self.description += cp.name + "\n"
|
||||
self.description += "TACAN : "
|
||||
|
||||
self.description += str(cp.tacanN)
|
||||
if cp.tacanY:
|
||||
@@ -82,6 +101,9 @@ class BriefingGenerator:
|
||||
self.description += "X"
|
||||
self.description += " " + str(cp.tacanI) + "\n"
|
||||
|
||||
if cp.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP and hasattr(cp, "icls"):
|
||||
self.description += "ICLS Channel : " + str(cp.icls) + "\n"
|
||||
self.description += "-" * 50 + "\n"
|
||||
|
||||
self.m.set_description_text(self.description)
|
||||
|
||||
|
||||
@@ -172,13 +172,6 @@ class Conflict:
|
||||
position = middle_point.point_from_heading(attack_heading, strength_delta * attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE)
|
||||
return position, _opposite_heading(attack_heading)
|
||||
|
||||
ground_position = cls._find_ground_position(position, attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE, attack_heading, theater)
|
||||
if ground_position:
|
||||
return ground_position, _opposite_heading(attack_heading)
|
||||
else:
|
||||
logging.warning("Coudn't find frontline position between {} and {}!".format(from_cp, to_cp))
|
||||
return position, _opposite_heading(attack_heading)
|
||||
|
||||
|
||||
@classmethod
|
||||
def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> typing.Optional[typing.Tuple[Point, int, int]]:
|
||||
|
||||
@@ -61,12 +61,14 @@ class EnviromentGenerator:
|
||||
logging.info("Skip Night mission due to user settings")
|
||||
if daytime == "dawn":
|
||||
time_range = (8, 9)
|
||||
elif daytime == "noon":
|
||||
elif daytime == "day":
|
||||
time_range = (10, 12)
|
||||
elif daytime == "dusk":
|
||||
time_range = (12, 14)
|
||||
elif daytime == "night":
|
||||
time_range = (14, 17)
|
||||
else:
|
||||
time_range = (10, 12)
|
||||
else:
|
||||
time_range = self.game.theater.daytime_map[daytime]
|
||||
|
||||
@@ -113,7 +115,7 @@ class EnviromentGenerator:
|
||||
if weather_type == 1:
|
||||
# thunderstorm
|
||||
self._generate_base_weather()
|
||||
self._generate_wind(random.randint(8, 12))
|
||||
self._generate_wind(random.randint(0, 8))
|
||||
|
||||
self.mission.weather.clouds_density = random.randint(9, 10)
|
||||
self.mission.weather.clouds_iprecptns = Weather.Preceptions.Thunderstorm
|
||||
@@ -123,7 +125,7 @@ class EnviromentGenerator:
|
||||
self.mission.weather.clouds_density = random.randint(5, 8)
|
||||
self.mission.weather.clouds_iprecptns = Weather.Preceptions.Rain
|
||||
|
||||
self._generate_wind(random.randint(4, 8))
|
||||
self._generate_wind(random.randint(0, 6))
|
||||
elif weather_type == 3:
|
||||
# clouds
|
||||
self._generate_base_weather()
|
||||
|
||||
42
gen/fleet/cn_dd_group.py
Normal file
42
gen/fleet/cn_dd_group.py
Normal 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
27
gen/fleet/dd_group.py
Normal 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
59
gen/fleet/ru_dd_group.py
Normal 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
19
gen/fleet/schnellboot.py
Normal 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
|
||||
@@ -1,6 +1,50 @@
|
||||
import logging
|
||||
import random
|
||||
|
||||
from game import db
|
||||
from gen.fleet.carrier_group import CarrierGroupGenerator
|
||||
from gen.fleet.cn_dd_group import ChineseNavyGroupGenerator, Type54GroupGenerator
|
||||
from gen.fleet.dd_group import ArleighBurkeGroupGenerator, OliverHazardPerryGroupGenerator
|
||||
from gen.fleet.lha_group import LHAGroupGenerator
|
||||
from gen.fleet.ru_dd_group import RussianNavyGroupGenerator, GrishaGroupGenerator, MolniyaGroupGenerator, \
|
||||
KiloSubGroupGenerator, TangoSubGroupGenerator
|
||||
from gen.fleet.schnellboot import SchnellbootGroupGenerator
|
||||
from gen.fleet.uboat import UBoatGroupGenerator
|
||||
from gen.fleet.ww2lst import WW2LSTGroupGenerator
|
||||
|
||||
SHIP_MAP = {
|
||||
"SchnellbootGroupGenerator": SchnellbootGroupGenerator,
|
||||
"WW2LSTGroupGenerator": WW2LSTGroupGenerator,
|
||||
"UBoatGroupGenerator": UBoatGroupGenerator,
|
||||
"OliverHazardPerryGroupGenerator": OliverHazardPerryGroupGenerator,
|
||||
"ArleighBurkeGroupGenerator": ArleighBurkeGroupGenerator,
|
||||
"RussianNavyGroupGenerator": RussianNavyGroupGenerator,
|
||||
"ChineseNavyGroupGenerator": ChineseNavyGroupGenerator,
|
||||
"GrishaGroupGenerator": GrishaGroupGenerator,
|
||||
"MolniyaGroupGenerator": MolniyaGroupGenerator,
|
||||
"KiloSubGroupGenerator": KiloSubGroupGenerator,
|
||||
"TangoSubGroupGenerator": TangoSubGroupGenerator,
|
||||
"Type54GroupGenerator": Type54GroupGenerator
|
||||
}
|
||||
|
||||
|
||||
def generate_ship_group(game, ground_object, faction:str):
|
||||
"""
|
||||
This generate a ship group
|
||||
:return: Nothing, but put the group reference inside the ground object
|
||||
"""
|
||||
faction = db.FACTIONS[faction]
|
||||
if "boat" in faction.keys():
|
||||
generators = faction["boat"]
|
||||
if len(generators) > 0:
|
||||
gen = random.choice(generators)
|
||||
if gen in SHIP_MAP.keys():
|
||||
generator = SHIP_MAP[gen](game, ground_object, faction)
|
||||
generator.generate()
|
||||
return generator.get_generated_group()
|
||||
else:
|
||||
logging.info("Unable to generate ship group, generator : " + str(gen) + "does not exists")
|
||||
return None
|
||||
|
||||
|
||||
def generate_carrier_group(faction:str, game, ground_object):
|
||||
|
||||
19
gen/fleet/uboat.py
Normal file
19
gen/fleet/uboat.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import random
|
||||
|
||||
from dcs.ships import Uboat_VIIC_U_flak
|
||||
|
||||
from gen.sam.group_generator import GroupGenerator
|
||||
|
||||
|
||||
class UBoatGroupGenerator(GroupGenerator):
|
||||
|
||||
def __init__(self, game, ground_object, faction):
|
||||
super(UBoatGroupGenerator, self).__init__(game, ground_object)
|
||||
self.faction = faction
|
||||
|
||||
def generate(self):
|
||||
|
||||
for i in range(random.randint(1, 4)):
|
||||
self.add_unit(Uboat_VIIC_U_flak, "Uboat" + str(i), self.position.x + i * random.randint(100, 250), self.position.y + (random.randint(100, 200)-100), self.heading)
|
||||
|
||||
self.get_generated_group().points[0].speed = 20
|
||||
22
gen/fleet/ww2lst.py
Normal file
22
gen/fleet/ww2lst.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import random
|
||||
|
||||
from dcs.ships import LS_Samuel_Chase, LST_Mk_II
|
||||
|
||||
from gen.sam.group_generator import GroupGenerator
|
||||
|
||||
|
||||
class WW2LSTGroupGenerator(GroupGenerator):
|
||||
|
||||
def __init__(self, game, ground_object, faction):
|
||||
super(WW2LSTGroupGenerator, self).__init__(game, ground_object)
|
||||
self.faction = faction
|
||||
|
||||
def generate(self):
|
||||
|
||||
# Add LS Samuel Chase
|
||||
self.add_unit(LS_Samuel_Chase, "SamuelChase", self.position.x, self.position.y, self.heading)
|
||||
|
||||
for i in range(1, random.randint(3, 4)):
|
||||
self.add_unit(LST_Mk_II, "LST" + str(i), self.position.x + i * random.randint(800, 1200), self.position.y, self.heading)
|
||||
|
||||
self.get_generated_group().points[0].speed = 20
|
||||
@@ -1,22 +1,17 @@
|
||||
import math
|
||||
import operator
|
||||
import typing
|
||||
import random
|
||||
|
||||
from game import db
|
||||
from game.data.doctrine import MODERN_DOCTRINE
|
||||
from game.data.radar_db import UNITS_WITH_RADAR
|
||||
from game.utils import meter_to_feet, nm_to_meter
|
||||
from gen import Conflict
|
||||
from gen.flights.ai_flight_planner_db import INTERCEPT_CAPABLE, CAP_CAPABLE, CAS_CAPABLE, SEAD_CAPABLE
|
||||
from gen.flights.flight import Flight, FlightType, FlightWaypoint
|
||||
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
|
||||
|
||||
# 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
|
||||
MISSION_DURATION = 80
|
||||
|
||||
|
||||
class FlightPlanner:
|
||||
@@ -28,6 +23,17 @@ class FlightPlanner:
|
||||
self.game = game
|
||||
self.aircraft_inventory = {} # local copy of the airbase inventory
|
||||
|
||||
if from_cp.captured:
|
||||
self.faction = self.game.player_faction
|
||||
else:
|
||||
self.faction = self.game.enemy_faction
|
||||
|
||||
if "doctrine" in self.faction.keys():
|
||||
self.doctrine = self.faction["doctrine"]
|
||||
else:
|
||||
self.doctrine = MODERN_DOCTRINE
|
||||
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
Reset the planned flights and available units
|
||||
@@ -53,7 +59,7 @@ class FlightPlanner:
|
||||
#self.commision_interceptors()
|
||||
|
||||
# Then some CAP patrol for the next 2 hours
|
||||
self.commision_barcap()
|
||||
self.commision_cap()
|
||||
|
||||
# Then setup cas
|
||||
self.commision_cas()
|
||||
@@ -61,7 +67,9 @@ class FlightPlanner:
|
||||
# Then prepare some sead flights if required
|
||||
self.commision_sead()
|
||||
|
||||
# TODO : commision STRIKE / ANTISHIP
|
||||
self.commision_strike()
|
||||
|
||||
# TODO : commision ANTISHIP
|
||||
|
||||
def remove_flight(self, index):
|
||||
try:
|
||||
@@ -83,7 +91,7 @@ class FlightPlanner:
|
||||
"""
|
||||
|
||||
# At least try to generate one interceptor group
|
||||
number_of_interceptor_groups = min(max(sum([v for k, v in self.aircraft_inventory.items()]) / 4, MAX_NUMBER_OF_INTERCEPTION_GROUP), 1)
|
||||
number_of_interceptor_groups = min(max(sum([v for k, v in self.aircraft_inventory.items()]) / 4, self.doctrine["MAX_NUMBER_OF_INTERCEPTION_GROUP"]), 1)
|
||||
possible_interceptors = [k for k in self.aircraft_inventory.keys() if k in INTERCEPT_CAPABLE]
|
||||
|
||||
if len(possible_interceptors) <= 0:
|
||||
@@ -107,7 +115,7 @@ class FlightPlanner:
|
||||
for k, v in inventory.items():
|
||||
self.aircraft_inventory[k] = v
|
||||
|
||||
def commision_barcap(self):
|
||||
def commision_cap(self):
|
||||
"""
|
||||
Pick some aircraft to assign them to defensive CAP roles (BARCAP)
|
||||
"""
|
||||
@@ -116,7 +124,7 @@ class FlightPlanner:
|
||||
inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft})
|
||||
|
||||
offset = random.randint(0,5)
|
||||
for i in range(int(MISSION_DURATION/CAP_EVERY_X_MINUTES)):
|
||||
for i in range(int(MISSION_DURATION/self.doctrine["CAP_EVERY_X_MINUTES"])):
|
||||
|
||||
try:
|
||||
unit = random.choice([k for k, v in inventory.items() if v >= 2])
|
||||
@@ -124,32 +132,16 @@ class FlightPlanner:
|
||||
break
|
||||
|
||||
inventory[unit] = inventory[unit] - 2
|
||||
flight = Flight(unit, 2, self.from_cp, FlightType.BARCAP)
|
||||
flight = Flight(unit, 2, self.from_cp, FlightType.CAP)
|
||||
|
||||
# Flight path : fly over each ground object (TODO : improve)
|
||||
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(3600, 7000)
|
||||
|
||||
patrolled = []
|
||||
for ground_object in self.from_cp.ground_objects:
|
||||
if ground_object.group_id not in patrolled and not ground_object.airbase_group:
|
||||
point = FlightWaypoint(ground_object.position.x, ground_object.position.y, patrol_alt)
|
||||
point.name = "Patrol point"
|
||||
point.description = "Patrol #" + str(len(flight.points))
|
||||
point.pretty_name = "Patrol #" + str(len(flight.points))
|
||||
flight.points.append(point)
|
||||
patrolled.append(ground_object.group_id)
|
||||
|
||||
if len(flight.points) == 0:
|
||||
for i in range(3):
|
||||
pos = self.from_cp.position.point_from_heading(random.randint(0,360), random.randint(30000, 80000))
|
||||
point = FlightWaypoint(pos.x, pos.y, patrol_alt)
|
||||
point.name = "Patrol point"
|
||||
point.description = "Patrol #" + str(len(flight.points))
|
||||
point.pretty_name = "Patrol #" + str(len(flight.points))
|
||||
flight.points.append(point)
|
||||
if len(self._get_cas_locations()) > 0:
|
||||
enemy_cp = random.choice(self._get_cas_locations())
|
||||
self.generate_frontline_cap(flight, flight.from_cp, enemy_cp)
|
||||
else:
|
||||
self.generate_barcap(flight, flight.from_cp)
|
||||
|
||||
self.cap_flights.append(flight)
|
||||
self.flights.append(flight)
|
||||
@@ -170,7 +162,7 @@ class FlightPlanner:
|
||||
if len(cas_location) > 0:
|
||||
|
||||
offset = random.randint(0,5)
|
||||
for i in range(int(MISSION_DURATION/CAS_EVERY_X_MINUTES)):
|
||||
for i in range(int(MISSION_DURATION/self.doctrine["CAS_EVERY_X_MINUTES"])):
|
||||
|
||||
try:
|
||||
unit = random.choice([k for k, v in inventory.items() if v >= 2])
|
||||
@@ -179,34 +171,11 @@ class FlightPlanner:
|
||||
|
||||
inventory[unit] = inventory[unit] - 2
|
||||
flight = Flight(unit, 2, self.from_cp, FlightType.CAS)
|
||||
|
||||
flight.points = []
|
||||
flight.scheduled_in = offset + i*random.randint(CAS_EVERY_X_MINUTES-5, CAS_EVERY_X_MINUTES+5)
|
||||
|
||||
flight.scheduled_in = offset + i * random.randint(self.doctrine["CAS_EVERY_X_MINUTES"] - 5, self.doctrine["CAS_EVERY_X_MINUTES"] + 5)
|
||||
location = random.choice(cas_location)
|
||||
ingress, heading, distance = Conflict.frontline_vector(self.from_cp, location, self.game.theater)
|
||||
center = ingress.point_from_heading(heading, distance/2)
|
||||
egress = ingress.point_from_heading(heading, distance)
|
||||
|
||||
flight.targets.append(center)
|
||||
|
||||
ingress_point = FlightWaypoint(ingress.x, ingress.y, 1000)
|
||||
ingress_point.name = "INGRESS"
|
||||
ingress_point.pretty_name = "INGRESS"
|
||||
ingress_point.description = "Ingress into CAS area"
|
||||
flight.points.append(ingress_point)
|
||||
|
||||
center_point = FlightWaypoint(center.x, center.y, 1000)
|
||||
center_point.description = "Provide CAS"
|
||||
center_point.name = "CAS"
|
||||
center_point.pretty_name = "INGRESS"
|
||||
flight.points.append(center_point)
|
||||
|
||||
egress_point = FlightWaypoint(egress.x, egress.y, 1000)
|
||||
egress_point.description = "Egress from CAS area"
|
||||
egress_point.name = "EGRESS"
|
||||
egress_point.pretty_name = "EGRESS"
|
||||
flight.points.append(egress_point)
|
||||
self.generate_cas(flight, flight.from_cp, location)
|
||||
|
||||
self.cas_flights.append(flight)
|
||||
self.flights.append(flight)
|
||||
@@ -226,7 +195,7 @@ class FlightPlanner:
|
||||
if len(self.potential_sead_targets) > 0:
|
||||
|
||||
offset = random.randint(0,5)
|
||||
for i in range(int(MISSION_DURATION/SEAD_EVERY_X_MINUTES)):
|
||||
for i in range(int(MISSION_DURATION/self.doctrine["SEAD_EVERY_X_MINUTES"])):
|
||||
|
||||
if len(self.potential_sead_targets) <= 0:
|
||||
break
|
||||
@@ -240,16 +209,12 @@ class FlightPlanner:
|
||||
flight = Flight(unit, 2, self.from_cp, random.choice([FlightType.SEAD, FlightType.DEAD]))
|
||||
|
||||
flight.points = []
|
||||
flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5)
|
||||
flight.scheduled_in = offset + i*random.randint(self.doctrine["SEAD_EVERY_X_MINUTES"] - 5, self.doctrine["SEAD_EVERY_X_MINUTES"] + 5)
|
||||
|
||||
location = self.potential_sead_targets[0][0]
|
||||
self.potential_sead_targets.pop(0)
|
||||
|
||||
point = FlightWaypoint(location.position.x, location.position.y, 1000)
|
||||
point.description = "SEAD"
|
||||
point.pretty_name = "SEAD"
|
||||
point.targets.append(location)
|
||||
flight.points.append(point)
|
||||
self.generate_sead(flight, location, [])
|
||||
|
||||
self.sead_flights.append(flight)
|
||||
self.flights.append(flight)
|
||||
@@ -258,10 +223,52 @@ class FlightPlanner:
|
||||
for k, v in inventory.items():
|
||||
self.aircraft_inventory[k] = v
|
||||
|
||||
|
||||
def commision_strike(self):
|
||||
"""
|
||||
Pick some aircraft to assign them to STRIKE tasks
|
||||
"""
|
||||
possible_aircraft = [k for k, v in self.aircraft_inventory.items() if k in STRIKE_CAPABLE and v >= 2]
|
||||
inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft})
|
||||
|
||||
if len(self.potential_strike_targets) > 0:
|
||||
|
||||
offset = random.randint(0,5)
|
||||
for i in range(int(MISSION_DURATION/self.doctrine["STRIKE_EVERY_X_MINUTES"])):
|
||||
|
||||
if len(self.potential_strike_targets) <= 0:
|
||||
break
|
||||
|
||||
try:
|
||||
unit = random.choice([k for k, v in inventory.items() if v >= 2])
|
||||
except IndexError:
|
||||
break
|
||||
|
||||
inventory[unit] = inventory[unit] - 2
|
||||
flight = Flight(unit, 2, self.from_cp, FlightType.STRIKE)
|
||||
|
||||
flight.points = []
|
||||
flight.scheduled_in = offset + i*random.randint(self.doctrine["STRIKE_EVERY_X_MINUTES"] - 5, self.doctrine["STRIKE_EVERY_X_MINUTES"] + 5)
|
||||
|
||||
location = self.potential_strike_targets[0][0]
|
||||
self.potential_strike_targets.pop(0)
|
||||
|
||||
self.generate_strike(flight, location)
|
||||
|
||||
self.strike_flights.append(flight)
|
||||
self.flights.append(flight)
|
||||
|
||||
# Update inventory
|
||||
for k, v in inventory.items():
|
||||
self.aircraft_inventory[k] = v
|
||||
|
||||
def _get_cas_locations(self):
|
||||
return self._get_cas_locations_for_cp(self.from_cp)
|
||||
|
||||
def _get_cas_locations_for_cp(self, for_cp):
|
||||
cas_locations = []
|
||||
for cp in self.from_cp.connected_points:
|
||||
if cp.captured != self.from_cp.captured:
|
||||
for cp in for_cp.connected_points:
|
||||
if cp.captured != for_cp.captured:
|
||||
cas_locations.append(cp)
|
||||
return cas_locations
|
||||
|
||||
@@ -279,19 +286,19 @@ class FlightPlanner:
|
||||
distance = math.hypot(cp.position.x - self.from_cp.position.x,
|
||||
cp.position.y - self.from_cp.position.y)
|
||||
|
||||
if distance > 2*STRIKE_MAX_RANGE:
|
||||
if distance > 2*self.doctrine["STRIKE_MAX_RANGE"]:
|
||||
# Then it's unlikely any child ground object is in range
|
||||
return
|
||||
|
||||
added_group = []
|
||||
for g in cp.ground_objects:
|
||||
if g.group_id in added_group: continue
|
||||
if g.group_id in added_group or g.is_dead: continue
|
||||
|
||||
# Compute distance to current cp
|
||||
distance = math.hypot(cp.position.x - self.from_cp.position.x,
|
||||
cp.position.y - self.from_cp.position.y)
|
||||
|
||||
if distance < SEAD_MAX_RANGE:
|
||||
if distance < self.doctrine["SEAD_MAX_RANGE"]:
|
||||
self.potential_strike_targets.append((g, distance))
|
||||
added_group.append(g)
|
||||
|
||||
@@ -312,7 +319,7 @@ class FlightPlanner:
|
||||
cp.position.y - self.from_cp.position.y)
|
||||
|
||||
# Then it's unlikely any ground object is range
|
||||
if distance > 2*SEAD_MAX_RANGE:
|
||||
if distance > 2*self.doctrine["SEAD_MAX_RANGE"]:
|
||||
return
|
||||
|
||||
for g in cp.ground_objects:
|
||||
@@ -320,8 +327,7 @@ class FlightPlanner:
|
||||
if g.dcs_identifier == "AA":
|
||||
|
||||
# Check that there is at least one unit with a radar in the ground objects unit groups
|
||||
number_of_units = sum([len([r for r in group.units if hasattr(db.unit_type_from_name(r.type), "detection_range")
|
||||
and db.unit_type_from_name(r.type).detection_range > 1000]) for group in g.groups])
|
||||
number_of_units = sum([len([r for r in group.units if db.unit_type_from_name(r.type) in UNITS_WITH_RADAR]) for group in g.groups])
|
||||
if number_of_units <= 0:
|
||||
continue
|
||||
|
||||
@@ -329,7 +335,7 @@ class FlightPlanner:
|
||||
distance = math.hypot(cp.position.x - self.from_cp.position.x,
|
||||
cp.position.y - self.from_cp.position.y)
|
||||
|
||||
if distance < SEAD_MAX_RANGE:
|
||||
if distance < self.doctrine["SEAD_MAX_RANGE"]:
|
||||
self.potential_sead_targets.append((g, distance))
|
||||
|
||||
self.potential_sead_targets.sort(key=operator.itemgetter(1))
|
||||
@@ -347,3 +353,346 @@ class FlightPlanner:
|
||||
del base_aircraft_inventory[f.unit_type]
|
||||
return base_aircraft_inventory
|
||||
|
||||
|
||||
def generate_strike(self, flight, location):
|
||||
|
||||
flight.flight_type = FlightType.STRIKE
|
||||
ascend = self.generate_ascend_point(flight.from_cp)
|
||||
flight.points.append(ascend)
|
||||
|
||||
heading = flight.from_cp.position.heading_between_point(location.position)
|
||||
ingress_heading = heading - 180 + 25
|
||||
egress_heading = heading - 180 - 25
|
||||
|
||||
ingress_pos = location.position.point_from_heading(ingress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"])
|
||||
ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, self.doctrine["INGRESS_ALT"])
|
||||
ingress_point.pretty_name = "INGRESS on " + location.obj_name
|
||||
ingress_point.description = "INGRESS on " + location.obj_name
|
||||
ingress_point.name = "INGRESS"
|
||||
ingress_point.waypoint_type = FlightWaypointType.INGRESS_STRIKE
|
||||
flight.points.append(ingress_point)
|
||||
|
||||
if len(location.groups) > 0 and location.dcs_identifier == "AA":
|
||||
for g in location.groups:
|
||||
for j, u in enumerate(g.units):
|
||||
point = FlightWaypoint(u.position.x, u.position.y, 0)
|
||||
point.description = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
|
||||
point.pretty_name = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
|
||||
point.name = location.obj_name + "#" + str(j)
|
||||
point.only_for_player = True
|
||||
ingress_point.targets.append(location)
|
||||
flight.points.append(point)
|
||||
else:
|
||||
if hasattr(location, "obj_name"):
|
||||
buildings = self.game.theater.find_ground_objects_by_obj_name(location.obj_name)
|
||||
print(buildings)
|
||||
for building in buildings:
|
||||
print("BUILDING " + str(building.is_dead) + " " + str(building.dcs_identifier))
|
||||
if building.is_dead:
|
||||
continue
|
||||
|
||||
point = FlightWaypoint(building.position.x, building.position.y, 0)
|
||||
point.description = "STRIKE on " + building.obj_name + " " + building.category + " [" + str(building.dcs_identifier) + " ]"
|
||||
point.pretty_name = "STRIKE on " + building.obj_name + " " + building.category + " [" + str(building.dcs_identifier) + " ]"
|
||||
point.name = building.obj_name
|
||||
point.only_for_player = True
|
||||
ingress_point.targets.append(building)
|
||||
flight.points.append(point)
|
||||
else:
|
||||
point = FlightWaypoint(location.position.x, location.position.y, 0)
|
||||
point.description = "STRIKE on " + location.obj_name
|
||||
point.pretty_name = "STRIKE on " + location.obj_name
|
||||
point.name = location.obj_name
|
||||
point.only_for_player = True
|
||||
ingress_point.targets.append(location)
|
||||
flight.points.append(point)
|
||||
|
||||
egress_pos = location.position.point_from_heading(egress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"])
|
||||
egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, self.doctrine["EGRESS_ALT"])
|
||||
egress_point.name = "EGRESS"
|
||||
egress_point.pretty_name = "EGRESS from " + location.obj_name
|
||||
egress_point.description = "EGRESS from " + location.obj_name
|
||||
egress_point.waypoint_type = FlightWaypointType.EGRESS
|
||||
flight.points.append(egress_point)
|
||||
|
||||
descend = self.generate_descend_point(flight.from_cp)
|
||||
flight.points.append(descend)
|
||||
|
||||
rtb = self.generate_rtb_waypoint(flight.from_cp)
|
||||
flight.points.append(rtb)
|
||||
|
||||
def generate_barcap(self, flight, for_cp):
|
||||
"""
|
||||
Generate a barcap flight at a given location
|
||||
:param flight: Flight to setup
|
||||
:param for_cp: CP to protect
|
||||
"""
|
||||
flight.flight_type = FlightType.BARCAP if for_cp.is_carrier else FlightType.CAP
|
||||
patrol_alt = random.randint(self.doctrine["PATROL_ALT_RANGE"][0], self.doctrine["PATROL_ALT_RANGE"][1])
|
||||
|
||||
if len(for_cp.ground_objects) > 0:
|
||||
loc = random.choice(for_cp.ground_objects)
|
||||
hdg = for_cp.position.heading_between_point(loc.position)
|
||||
radius = random.randint(self.doctrine["CAP_PATTERN_LENGTH"][0], self.doctrine["CAP_PATTERN_LENGTH"][1])
|
||||
orbit0p = loc.position.point_from_heading(hdg - 90, radius)
|
||||
orbit1p = loc.position.point_from_heading(hdg + 90, radius)
|
||||
else:
|
||||
loc = for_cp.position.point_from_heading(random.randint(0, 360), random.randint(self.doctrine["CAP_DISTANCE_FROM_CP"][0], self.doctrine["CAP_DISTANCE_FROM_CP"][1]))
|
||||
hdg = for_cp.position.heading_between_point(loc)
|
||||
radius = random.randint(self.doctrine["CAP_PATTERN_LENGTH"][0], self.doctrine["CAP_PATTERN_LENGTH"][1])
|
||||
orbit0p = loc.point_from_heading(hdg - 90, radius)
|
||||
orbit1p = loc.point_from_heading(hdg + 90, radius)
|
||||
|
||||
# Create points
|
||||
ascend = self.generate_ascend_point(flight.from_cp)
|
||||
flight.points.append(ascend)
|
||||
|
||||
orbit0 = FlightWaypoint(orbit0p.x, orbit0p.y, patrol_alt)
|
||||
orbit0.name = "ORBIT 0"
|
||||
orbit0.description = "Standby between this point and the next one"
|
||||
orbit0.pretty_name = "Race-track start"
|
||||
orbit0.waypoint_type = FlightWaypointType.PATROL_TRACK
|
||||
flight.points.append(orbit0)
|
||||
|
||||
orbit1 = FlightWaypoint(orbit1p.x, orbit1p.y, patrol_alt)
|
||||
orbit1.name = "ORBIT 1"
|
||||
orbit1.description = "Standby between this point and the previous one"
|
||||
orbit1.pretty_name = "Race-track end"
|
||||
orbit1.waypoint_type = FlightWaypointType.PATROL
|
||||
flight.points.append(orbit1)
|
||||
|
||||
orbit0.targets.append(for_cp)
|
||||
obj_added = []
|
||||
for ground_object in for_cp.ground_objects:
|
||||
if ground_object.obj_name not in obj_added and not ground_object.airbase_group:
|
||||
orbit0.targets.append(ground_object)
|
||||
obj_added.append(ground_object.obj_name)
|
||||
|
||||
descend = self.generate_descend_point(flight.from_cp)
|
||||
flight.points.append(descend)
|
||||
|
||||
rtb = self.generate_rtb_waypoint(flight.from_cp)
|
||||
flight.points.append(rtb)
|
||||
|
||||
|
||||
def generate_frontline_cap(self, flight, ally_cp, enemy_cp):
|
||||
"""
|
||||
Generate a cap flight for the frontline between ally_cp and enemy cp in order to ensure air superiority and
|
||||
protect friendly CAP airbase
|
||||
:param flight: Flight to setup
|
||||
:param ally_cp: CP to protect
|
||||
:param enemy_cp: Enemy connected cp
|
||||
"""
|
||||
flight.flight_type = FlightType.CAP
|
||||
patrol_alt = random.randint(self.doctrine["PATROL_ALT_RANGE"][0], self.doctrine["PATROL_ALT_RANGE"][1])
|
||||
|
||||
# Find targets waypoints
|
||||
ingress, heading, distance = Conflict.frontline_vector(ally_cp, enemy_cp, self.game.theater)
|
||||
center = ingress.point_from_heading(heading, distance / 2)
|
||||
orbit_center = center.point_from_heading(heading - 90, random.randint(nm_to_meter(6), nm_to_meter(15)))
|
||||
|
||||
combat_width = distance / 2
|
||||
if combat_width > 500000:
|
||||
combat_width = 500000
|
||||
if combat_width < 35000:
|
||||
combat_width = 35000
|
||||
|
||||
radius = combat_width*1.25
|
||||
orbit0p = orbit_center.point_from_heading(heading, radius)
|
||||
orbit1p = orbit_center.point_from_heading(heading + 180, radius)
|
||||
|
||||
# Create points
|
||||
ascend = self.generate_ascend_point(flight.from_cp)
|
||||
flight.points.append(ascend)
|
||||
|
||||
orbit0 = FlightWaypoint(orbit0p.x, orbit0p.y, patrol_alt)
|
||||
orbit0.name = "ORBIT 0"
|
||||
orbit0.description = "Standby between this point and the next one"
|
||||
orbit0.pretty_name = "Race-track start"
|
||||
orbit0.waypoint_type = FlightWaypointType.PATROL_TRACK
|
||||
flight.points.append(orbit0)
|
||||
|
||||
orbit1 = FlightWaypoint(orbit1p.x, orbit1p.y, patrol_alt)
|
||||
orbit1.name = "ORBIT 1"
|
||||
orbit1.description = "Standby between this point and the previous one"
|
||||
orbit1.pretty_name = "Race-track end"
|
||||
orbit1.waypoint_type = FlightWaypointType.PATROL
|
||||
flight.points.append(orbit1)
|
||||
|
||||
# Note : Targets of a PATROL TRACK waypoints are the points to be defended
|
||||
orbit0.targets.append(flight.from_cp)
|
||||
orbit0.targets.append(center)
|
||||
|
||||
descend = self.generate_descend_point(flight.from_cp)
|
||||
flight.points.append(descend)
|
||||
|
||||
rtb = self.generate_rtb_waypoint(flight.from_cp)
|
||||
flight.points.append(rtb)
|
||||
|
||||
|
||||
def generate_sead(self, flight, location, custom_targets = []):
|
||||
"""
|
||||
Generate a sead flight at a given location
|
||||
:param flight: Flight to setup
|
||||
:param location: Location of the SEAD target
|
||||
:param custom_targets: Custom targets if any
|
||||
"""
|
||||
flight.points = []
|
||||
flight.flight_type = random.choice([FlightType.SEAD, FlightType.DEAD])
|
||||
|
||||
ascend = self.generate_ascend_point(flight.from_cp)
|
||||
flight.points.append(ascend)
|
||||
|
||||
heading = flight.from_cp.position.heading_between_point(location.position)
|
||||
ingress_heading = heading - 180 + 25
|
||||
egress_heading = heading - 180 - 25
|
||||
|
||||
ingress_pos = location.position.point_from_heading(ingress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"])
|
||||
ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, self.doctrine["INGRESS_ALT"])
|
||||
ingress_point.name = "INGRESS"
|
||||
ingress_point.pretty_name = "INGRESS on " + location.obj_name
|
||||
ingress_point.description = "INGRESS on " + location.obj_name
|
||||
ingress_point.waypoint_type = FlightWaypointType.INGRESS_SEAD
|
||||
flight.points.append(ingress_point)
|
||||
|
||||
if len(custom_targets) > 0:
|
||||
for target in custom_targets:
|
||||
point = FlightWaypoint(target.position.x, target.position.y, 0)
|
||||
point.alt_type = "RADIO"
|
||||
if flight.flight_type == FlightType.DEAD:
|
||||
point.description = "SEAD on " + target.type
|
||||
point.pretty_name = "SEAD on " + location.obj_name
|
||||
point.only_for_player = True
|
||||
else:
|
||||
point.description = "DEAD on " + location.obj_name
|
||||
point.pretty_name = "DEAD on " + location.obj_name
|
||||
point.only_for_player = True
|
||||
ingress_point.targets.append(location)
|
||||
ingress_point.targetGroup = location
|
||||
flight.points.append(point)
|
||||
else:
|
||||
point = FlightWaypoint(location.position.x, location.position.y, 0)
|
||||
point.alt_type = "RADIO"
|
||||
if flight.flight_type == FlightType.DEAD:
|
||||
point.description = "SEAD on " + location.obj_name
|
||||
point.pretty_name = "SEAD on " + location.obj_name
|
||||
point.only_for_player = True
|
||||
else:
|
||||
point.description = "DEAD on " + location.obj_name
|
||||
point.pretty_name = "DEAD on " + location.obj_name
|
||||
point.only_for_player = True
|
||||
ingress_point.targets.append(location)
|
||||
ingress_point.targetGroup = location
|
||||
flight.points.append(point)
|
||||
|
||||
egress_pos = location.position.point_from_heading(egress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"])
|
||||
egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, self.doctrine["EGRESS_ALT"])
|
||||
egress_point.name = "EGRESS"
|
||||
egress_point.pretty_name = "EGRESS from " + location.obj_name
|
||||
egress_point.description = "EGRESS from " + location.obj_name
|
||||
egress_point.waypoint_type = FlightWaypointType.EGRESS
|
||||
flight.points.append(egress_point)
|
||||
|
||||
descend = self.generate_descend_point(flight.from_cp)
|
||||
flight.points.append(descend)
|
||||
|
||||
rtb = self.generate_rtb_waypoint(flight.from_cp)
|
||||
flight.points.append(rtb)
|
||||
|
||||
|
||||
def generate_cas(self, flight, from_cp, location):
|
||||
"""
|
||||
Generate a CAS flight at a given location
|
||||
:param flight: Flight to setup
|
||||
:param location: Location of the CAS targets
|
||||
"""
|
||||
|
||||
flight.points = []
|
||||
flight.flight_type = FlightType.CAS
|
||||
|
||||
ingress, heading, distance = Conflict.frontline_vector(from_cp, location, self.game.theater)
|
||||
center = ingress.point_from_heading(heading, distance / 2)
|
||||
egress = ingress.point_from_heading(heading, distance)
|
||||
|
||||
ascend = self.generate_ascend_point(flight.from_cp)
|
||||
flight.points.append(ascend)
|
||||
|
||||
ingress_point = FlightWaypoint(ingress.x, ingress.y, 1000)
|
||||
ingress_point.alt_type = "RADIO"
|
||||
ingress_point.name = "INGRESS"
|
||||
ingress_point.pretty_name = "INGRESS"
|
||||
ingress_point.description = "Ingress into CAS area"
|
||||
ingress_point.waypoint_type = FlightWaypointType.INGRESS_CAS
|
||||
flight.points.append(ingress_point)
|
||||
|
||||
center_point = FlightWaypoint(center.x, center.y, 1000)
|
||||
center_point.alt_type = "RADIO"
|
||||
center_point.description = "Provide CAS"
|
||||
center_point.name = "CAS"
|
||||
center_point.pretty_name = "CAS"
|
||||
center_point.waypoint_type = FlightWaypointType.CAS
|
||||
flight.points.append(center_point)
|
||||
|
||||
egress_point = FlightWaypoint(egress.x, egress.y, 1000)
|
||||
egress_point.alt_type = "RADIO"
|
||||
egress_point.description = "Egress from CAS area"
|
||||
egress_point.name = "EGRESS"
|
||||
egress_point.pretty_name = "EGRESS"
|
||||
egress_point.waypoint_type = FlightWaypointType.EGRESS
|
||||
flight.points.append(egress_point)
|
||||
|
||||
descend = self.generate_descend_point(flight.from_cp)
|
||||
flight.points.append(descend)
|
||||
|
||||
rtb = self.generate_rtb_waypoint(flight.from_cp)
|
||||
flight.points.append(rtb)
|
||||
|
||||
|
||||
def generate_ascend_point(self, from_cp):
|
||||
"""
|
||||
Generate ascend point
|
||||
:param from_cp: Airport you're taking off from
|
||||
:return:
|
||||
"""
|
||||
ascend_heading = from_cp.heading
|
||||
pos_ascend = from_cp.position.point_from_heading(ascend_heading, 10000)
|
||||
ascend = FlightWaypoint(pos_ascend.x, pos_ascend.y, self.doctrine["PATTERN_ALTITUDE"])
|
||||
ascend.name = "ASCEND"
|
||||
ascend.alt_type = "RADIO"
|
||||
ascend.description = "Ascend"
|
||||
ascend.pretty_name = "Ascend"
|
||||
ascend.waypoint_type = FlightWaypointType.ASCEND_POINT
|
||||
return ascend
|
||||
|
||||
|
||||
def generate_descend_point(self, from_cp):
|
||||
"""
|
||||
Generate approach/descend point
|
||||
:param from_cp: Airport you're landing at
|
||||
:return:
|
||||
"""
|
||||
ascend_heading = from_cp.heading
|
||||
descend = from_cp.position.point_from_heading(ascend_heading - 180, 10000)
|
||||
descend = FlightWaypoint(descend.x, descend.y, self.doctrine["PATTERN_ALTITUDE"])
|
||||
descend.name = "DESCEND"
|
||||
descend.alt_type = "RADIO"
|
||||
descend.description = "Descend to pattern alt"
|
||||
descend.pretty_name = "Descend to pattern alt"
|
||||
descend.waypoint_type = FlightWaypointType.DESCENT_POINT
|
||||
return descend
|
||||
|
||||
|
||||
def generate_rtb_waypoint(self, from_cp):
|
||||
"""
|
||||
Generate RTB landing point
|
||||
:param from_cp: Airport you're landing at
|
||||
:return:
|
||||
"""
|
||||
rtb = from_cp.position
|
||||
rtb = FlightWaypoint(rtb.x, rtb.y, 0)
|
||||
rtb.name = "LANDING"
|
||||
rtb.alt_type = "RADIO"
|
||||
rtb.description = "RTB"
|
||||
rtb.pretty_name = "RTB"
|
||||
rtb.waypoint_type = FlightWaypointType.LANDING_POINT
|
||||
return rtb
|
||||
@@ -48,6 +48,7 @@ CAP_CAPABLE = [
|
||||
|
||||
P_51D_30_NA,
|
||||
P_51D,
|
||||
P_47D_30,
|
||||
|
||||
SpitfireLFMkIXCW,
|
||||
SpitfireLFMkIX,
|
||||
@@ -106,6 +107,8 @@ CAS_CAPABLE = [
|
||||
|
||||
P_51D_30_NA,
|
||||
P_51D,
|
||||
P_47D_30,
|
||||
A_20G,
|
||||
|
||||
SpitfireLFMkIXCW,
|
||||
SpitfireLFMkIX,
|
||||
@@ -119,7 +122,7 @@ CAS_CAPABLE = [
|
||||
SEAD_CAPABLE = [
|
||||
F_4E,
|
||||
FA_18C_hornet,
|
||||
F_16C_50,
|
||||
# F_16C_50, Not yet
|
||||
AV8BNA,
|
||||
JF_17,
|
||||
|
||||
@@ -164,10 +167,11 @@ STRIKE_CAPABLE = [
|
||||
L_39ZA,
|
||||
AJS37,
|
||||
|
||||
M_2000C,
|
||||
|
||||
P_51D_30_NA,
|
||||
P_51D,
|
||||
P_47D_30,
|
||||
A_20G,
|
||||
B_17G,
|
||||
|
||||
SpitfireLFMkIXCW,
|
||||
SpitfireLFMkIX,
|
||||
@@ -175,11 +179,18 @@ STRIKE_CAPABLE = [
|
||||
Bf_109K_4,
|
||||
FW_190D9,
|
||||
FW_190A8,
|
||||
|
||||
]
|
||||
|
||||
ANTISHIP_CAPABLE = [
|
||||
Su_24M,
|
||||
Su_17M4,
|
||||
F_A_18C,
|
||||
AV8BNA,
|
||||
JF_17
|
||||
JF_17,
|
||||
F_16C_50,
|
||||
A_10C,
|
||||
A_10A,
|
||||
|
||||
Ju_88A4,
|
||||
]
|
||||
@@ -30,17 +30,53 @@ class FlightType(Enum):
|
||||
EWAR = 16
|
||||
|
||||
|
||||
class FlightWaypoint():
|
||||
class FlightWaypointType(Enum):
|
||||
TAKEOFF = 0 # Take off point
|
||||
ASCEND_POINT = 1 # Ascension point after take off
|
||||
PATROL = 2 # Patrol point
|
||||
PATROL_TRACK = 3 # Patrol race track
|
||||
NAV = 4 # Nav point
|
||||
INGRESS_STRIKE = 5 # Ingress strike (For generator, means that this should have bombing on next TARGET_POINT points)
|
||||
INGRESS_SEAD = 6 # Ingress sead (For generator, means that this should attack groups on TARGET_GROUP_LOC points)
|
||||
INGRESS_CAS = 7 # Ingress cas (should start CAS task)
|
||||
CAS = 8 # Should do CAS there
|
||||
EGRESS = 9 # Should stop attack
|
||||
DESCENT_POINT = 10 # Should start descending to pattern alt
|
||||
LANDING_POINT = 11 # Should land there
|
||||
TARGET_POINT = 12 # A target building or static object, position
|
||||
TARGET_GROUP_LOC = 13 # A target group approximate location
|
||||
TARGET_SHIP = 14 # A target ship known location
|
||||
CUSTOM = 15 # User waypoint (no specific behaviour)
|
||||
|
||||
|
||||
class PredefinedWaypointCategory(Enum):
|
||||
NOT_PREDEFINED = 0
|
||||
ALLY_CP = 1
|
||||
ENEMY_CP = 2
|
||||
FRONTLINE = 3
|
||||
ENEMY_BUILDING = 4
|
||||
ENEMY_UNIT = 5
|
||||
ALLY_BUILDING = 6
|
||||
ALLY_UNIT = 7
|
||||
|
||||
|
||||
class FlightWaypoint:
|
||||
|
||||
def __init__(self, x: float, y: float, alt=0):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.alt = alt
|
||||
self.alt_type = "BARO"
|
||||
self.name = ""
|
||||
self.description = ""
|
||||
self.targets = []
|
||||
self.targetGroup = None
|
||||
self.obj_name = ""
|
||||
self.pretty_name = ""
|
||||
self.waypoint_type = FlightWaypointType.TAKEOFF # type: FlightWaypointType
|
||||
self.category = PredefinedWaypointCategory.NOT_PREDEFINED# type: PredefinedWaypointCategory
|
||||
self.only_for_player = False
|
||||
self.data = None
|
||||
|
||||
|
||||
class Flight:
|
||||
|
||||
@@ -22,15 +22,22 @@ class ForcedOptionsGenerator:
|
||||
self.game = game
|
||||
|
||||
def _set_options_view(self):
|
||||
if self.game.settings.map_coalition_visibility == "All Units":
|
||||
|
||||
if self.game.settings.map_coalition_visibility == ForcedOptions.Views.All:
|
||||
self.mission.forced_options.options_view = ForcedOptions.Views.All
|
||||
elif self.game.settings.map_coalition_visibility == "Allied Units":
|
||||
elif self.game.settings.map_coalition_visibility == ForcedOptions.Views.Allies:
|
||||
self.mission.forced_options.options_view = ForcedOptions.Views.Allies
|
||||
elif self.game.settings.map_coalition_visibility == "Own Aircraft":
|
||||
elif self.game.settings.map_coalition_visibility == ForcedOptions.Views.OnlyAllies:
|
||||
self.mission.forced_options.options_view = ForcedOptions.Views.OnlyAllies
|
||||
elif self.game.settings.map_coalition_visibility == ForcedOptions.Views.MyAircraft:
|
||||
self.mission.forced_options.options_view = ForcedOptions.Views.MyAircraft
|
||||
elif self.game.settings.map_coalition_visibility == "None":
|
||||
elif self.game.settings.map_coalition_visibility == ForcedOptions.Views.OnlyMap:
|
||||
self.mission.forced_options.options_view = ForcedOptions.Views.OnlyMap
|
||||
|
||||
def _set_external_views(self):
|
||||
if not self.game.settings.external_views_allowed:
|
||||
self.mission.forced_options.external_views = self.game.settings.external_views_allowed
|
||||
|
||||
def _set_labels(self):
|
||||
if self.game.settings.labels == "Abbreviated":
|
||||
self.mission.forced_options.labels = int(Labels.Abbreviated)
|
||||
@@ -41,5 +48,8 @@ class ForcedOptionsGenerator:
|
||||
|
||||
def generate(self):
|
||||
self._set_options_view()
|
||||
self._set_external_views()
|
||||
self._set_labels()
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ TYPE_TANKS = [
|
||||
Armor.MBT_M1A2_Abrams,
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.MBT_Merkava_Mk__4,
|
||||
Armor.ZTZ_96B,
|
||||
|
||||
# WW2
|
||||
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
|
||||
Armor.MT_Pz_Kpfw_IV_Ausf_H,
|
||||
Armor.HT_Pz_Kpfw_VI_Tiger_I,
|
||||
@@ -26,13 +29,21 @@ TYPE_TANKS = [
|
||||
Armor.MT_M4_Sherman,
|
||||
Armor.MT_M4A4_Sherman_Firefly,
|
||||
Armor.StuG_IV,
|
||||
Armor.ZTZ_96B
|
||||
Armor.ST_Centaur_IV,
|
||||
Armor.CT_Cromwell_IV,
|
||||
Armor.HIT_Churchill_VII,
|
||||
]
|
||||
|
||||
TYPE_ATGM = [
|
||||
Armor.ATGM_M1045_HMMWV_TOW,
|
||||
Armor.ATGM_M1134_Stryker,
|
||||
Armor.IFV_BMP_2,
|
||||
|
||||
# WW2 (Tank Destroyers)
|
||||
Armor.M30_Cargo_Carrier,
|
||||
Armor.TD_Jagdpanzer_IV,
|
||||
Armor.TD_Jagdpanther_G1,
|
||||
Armor.TD_M10_GMC,
|
||||
]
|
||||
|
||||
TYPE_IFV = [
|
||||
@@ -46,8 +57,10 @@ TYPE_IFV = [
|
||||
Armor.IFV_M2A2_Bradley,
|
||||
Armor.IFV_BMD_1,
|
||||
Armor.ZBD_04A,
|
||||
Armor.APC_Sd_Kfz_251,
|
||||
|
||||
# WW2
|
||||
Armor.IFV_Sd_Kfz_234_2_Puma,
|
||||
Armor.LAC_M8_Greyhound,
|
||||
]
|
||||
|
||||
TYPE_APC = [
|
||||
@@ -63,9 +76,11 @@ TYPE_APC = [
|
||||
Armor.TPz_Fuchs,
|
||||
Armor.ARV_BRDM_2,
|
||||
Armor.ARV_BTR_RD,
|
||||
Armor.ARV_MTLB_U_BOMAN,
|
||||
Armor.M30_Cargo_Carrier,
|
||||
Armor.FDDM_Grad,
|
||||
|
||||
# WW2
|
||||
Armor.APC_M2A1,
|
||||
Armor.APC_Sd_Kfz_251,
|
||||
]
|
||||
|
||||
TYPE_ARTILLERY = [
|
||||
@@ -79,9 +94,11 @@ TYPE_ARTILLERY = [
|
||||
Artillery.SPH_2S9_Nona,
|
||||
Artillery.SpGH_Dana,
|
||||
Artillery.SPH_2S19_Msta,
|
||||
Artillery.M12_GMC,
|
||||
Artillery.MLRS_FDDM,
|
||||
Artillery.Sturmpanzer_IV_Brummbär
|
||||
|
||||
# WW2
|
||||
Artillery.Sturmpanzer_IV_Brummbär,
|
||||
Artillery.M12_GMC
|
||||
]
|
||||
|
||||
TYPE_LOGI = [
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import logging
|
||||
|
||||
from game import db
|
||||
from game.data.building_data import FORTIFICATION_UNITS_ID, FORTIFICATION_UNITS
|
||||
from game.db import unit_type_from_name
|
||||
from .conflictgen import *
|
||||
from .naming import *
|
||||
@@ -50,9 +51,6 @@ class GroundObjectsGenerator:
|
||||
else:
|
||||
cp = self.conflict.from_cp
|
||||
|
||||
consumed_farps = set()
|
||||
|
||||
|
||||
for cp in self.game.theater.controlpoints:
|
||||
|
||||
if cp.captured:
|
||||
@@ -63,28 +61,54 @@ class GroundObjectsGenerator:
|
||||
|
||||
for ground_object in cp.ground_objects:
|
||||
if ground_object.dcs_identifier == "AA":
|
||||
|
||||
if self.game.position_culled(ground_object.position):
|
||||
continue
|
||||
|
||||
for g in ground_object.groups:
|
||||
if len(g.units) > 0:
|
||||
|
||||
utype = unit_type_from_name(g.units[0].type)
|
||||
|
||||
vg = self.m.vehicle_group(side, g.name, utype, position=g.position, heading=g.units[0].heading)
|
||||
vg.units[0].name = self.m.string(g.units[0].name)
|
||||
for i, u in enumerate(g.units):
|
||||
if i > 0:
|
||||
vehicle = Vehicle(self.m.next_unit_id(), self.m.string(u.name), u.type)
|
||||
vehicle.position.x = u.position.x
|
||||
vehicle.position.y = u.position.y
|
||||
vehicle.heading = u.heading
|
||||
vg.add_unit(vehicle)
|
||||
if not ground_object.sea_object:
|
||||
vg = self.m.vehicle_group(side, g.name, utype, position=g.position, heading=g.units[0].heading)
|
||||
vg.units[0].name = self.m.string(g.units[0].name)
|
||||
for i, u in enumerate(g.units):
|
||||
if i > 0:
|
||||
vehicle = Vehicle(self.m.next_unit_id(), self.m.string(u.name), u.type)
|
||||
vehicle.position.x = u.position.x
|
||||
vehicle.position.y = u.position.y
|
||||
vehicle.heading = u.heading
|
||||
vg.add_unit(vehicle)
|
||||
else:
|
||||
vg = self.m.ship_group(side, g.name, utype, position=g.position,
|
||||
heading=g.units[0].heading)
|
||||
vg.units[0].name = self.m.string(g.units[0].name)
|
||||
for i, u in enumerate(g.units):
|
||||
utype = unit_type_from_name(u.type)
|
||||
if i > 0:
|
||||
ship = Ship(self.m.next_unit_id(), self.m.string(u.name), utype)
|
||||
ship.position.x = u.position.x
|
||||
ship.position.y = u.position.y
|
||||
ship.heading = u.heading
|
||||
vg.add_unit(ship)
|
||||
|
||||
if self.game.settings.perf_red_alert_state:
|
||||
vg.points[0].tasks.append(OptAlarmState(2))
|
||||
else:
|
||||
vg.points[0].tasks.append(OptAlarmState(1))
|
||||
|
||||
|
||||
elif ground_object.dcs_identifier in ["CARRIER", "LHA"]:
|
||||
for g in ground_object.groups:
|
||||
if len(g.units) > 0:
|
||||
|
||||
utype = unit_type_from_name(g.units[0].type)
|
||||
sg = self.m.ship_group(side, g.name, utype, position=g.position, heading=g.units[0].heading)
|
||||
if ground_object.dcs_identifier == "CARRIER" and self.game.settings.supercarrier == True:
|
||||
utype = db.upgrade_to_supercarrier(utype, cp.name)
|
||||
|
||||
sg = self.m.ship_group(side, g.name, utype, position=g.position, heading=g.units[0].heading)
|
||||
sg.units[0].name = self.m.string(g.units[0].name)
|
||||
|
||||
for i, u in enumerate(g.units):
|
||||
if i > 0:
|
||||
ship = Ship(self.m.next_unit_id(), self.m.string(u.name), unit_type_from_name(u.type))
|
||||
@@ -93,70 +117,61 @@ class GroundObjectsGenerator:
|
||||
ship.heading = u.heading
|
||||
sg.add_unit(ship)
|
||||
|
||||
# TODO : make sure the point is not on Land
|
||||
sg.add_waypoint(sg.points[0].position.point_from_heading(g.units[0].heading, 100000))
|
||||
# Find carrier direction (In the wind)
|
||||
found_carrier_destination = False
|
||||
attempt = 0
|
||||
while not found_carrier_destination and attempt < 5:
|
||||
point = sg.points[0].position.point_from_heading(self.m.weather.wind_at_ground.direction, 100000-attempt*20000)
|
||||
if self.game.theater.is_in_sea(point):
|
||||
found_carrier_destination = True
|
||||
sg.add_waypoint(point)
|
||||
else:
|
||||
attempt = attempt + 1
|
||||
|
||||
# SET UP TACAN
|
||||
# Set UP TACAN and ICLS
|
||||
modeChannel = "X" if not cp.tacanY else "Y"
|
||||
sg.points[0].tasks.append(ActivateBeaconCommand(channel=cp.tacanN, modechannel=modeChannel, callsign=cp.tacanI, unit_id=sg.units[0].id))
|
||||
if ground_object.dcs_identifier == "CARRIER" and hasattr(cp, "icls"):
|
||||
sg.points[0].tasks.append(ActivateICLSCommand(cp.icls, unit_id=sg.units[0].id))
|
||||
|
||||
else:
|
||||
|
||||
if self.game.position_culled(ground_object.position):
|
||||
continue
|
||||
|
||||
static_type = None
|
||||
if ground_object.dcs_identifier in warehouse_map:
|
||||
static_type = warehouse_map[ground_object.dcs_identifier]
|
||||
else:
|
||||
elif ground_object.dcs_identifier in fortification_map:
|
||||
static_type = fortification_map[ground_object.dcs_identifier]
|
||||
|
||||
if not static_type:
|
||||
elif ground_object.dcs_identifier in FORTIFICATION_UNITS_ID:
|
||||
for f in FORTIFICATION_UNITS:
|
||||
if f.id == ground_object.dcs_identifier:
|
||||
unit_type = f
|
||||
break
|
||||
else:
|
||||
print("Didn't find {} in static _map(s)!".format(ground_object.dcs_identifier))
|
||||
continue
|
||||
|
||||
if ground_object.group_id not in consumed_farps:
|
||||
consumed_farps.add(ground_object.group_id)
|
||||
if random.randint(0, 100) > 50:
|
||||
farp_aa(
|
||||
self.m,
|
||||
side,
|
||||
ground_object.string_identifier,
|
||||
ground_object.position,
|
||||
if static_type is None:
|
||||
if not ground_object.is_dead:
|
||||
group = self.m.vehicle_group(
|
||||
country=side,
|
||||
name=ground_object.string_identifier,
|
||||
_type=unit_type,
|
||||
position=ground_object.position,
|
||||
heading=ground_object.heading,
|
||||
)
|
||||
logging.info("generated {}object identifier {} with mission id {}".format(
|
||||
"dead " if ground_object.is_dead else "", group.name, group.id))
|
||||
else:
|
||||
group = self.m.static_group(
|
||||
country=side,
|
||||
name=ground_object.string_identifier,
|
||||
_type=static_type,
|
||||
position=ground_object.position,
|
||||
heading=ground_object.heading,
|
||||
dead=ground_object.is_dead,
|
||||
)
|
||||
|
||||
group = self.m.static_group(
|
||||
country=side,
|
||||
name=ground_object.string_identifier,
|
||||
_type=static_type,
|
||||
position=ground_object.position,
|
||||
heading=ground_object.heading,
|
||||
dead=ground_object.is_dead,
|
||||
)
|
||||
|
||||
logging.info("generated {}object identifier {} with mission id {}".format("dead " if ground_object.is_dead else "", group.name, group.id))
|
||||
|
||||
|
||||
def farp_aa(mission_obj, country, name, position: mapping.Point):
|
||||
"""
|
||||
Add AAA to a FARP :)
|
||||
:param mission_obj:
|
||||
:param country:
|
||||
:param name:
|
||||
:param position:
|
||||
:return:
|
||||
"""
|
||||
vg = unitgroup.VehicleGroup(mission_obj.next_group_id(), mission_obj.string(name))
|
||||
|
||||
units = [
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka,
|
||||
AirDefence.AAA_ZU_23_Closed,
|
||||
]
|
||||
|
||||
v = mission_obj.vehicle(name + "_AAA", random.choice(units))
|
||||
v.position.x = position.x - random.randint(5, 30)
|
||||
v.position.y = position.y - random.randint(5, 30)
|
||||
v.heading = random.randint(0, 359)
|
||||
vg.add_unit(v)
|
||||
|
||||
wp = vg.add_waypoint(vg.units[0].position, PointAction.OffRoad, 0)
|
||||
wp.ETA_locked = True
|
||||
|
||||
country.add_vehicle_group(vg)
|
||||
return vg
|
||||
|
||||
logging.info("generated {}object identifier {} with mission id {}".format("dead " if ground_object.is_dead else "", group.name, group.id))
|
||||
27
gen/missiles/missiles_group_generator.py
Normal file
27
gen/missiles/missiles_group_generator.py
Normal 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
32
gen/missiles/v1_group.py
Normal 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)
|
||||
@@ -32,7 +32,10 @@ class NameGenerator:
|
||||
"TERMITE", "URCHIN", "SHRIMP", "TURKEY", "TOUCAN", "TETRA", "HUSKY", "STARFISH", "SWAN",
|
||||
"FROG", "SQUIRREL", "WALRUS", "WARTHOG", "CORGI", "WEASEL", "WOMBAT", "WOLVERINE", "MAMMOTH",
|
||||
"TOAD", "WOLF", "ZEBU", "SEAL", "SKATE", "JELLYFISH", "MOSQUITO", "LOCUST", "SLUG", "SNAIL",
|
||||
"HEDGEHOG", "PIGLET", "FENNEC", "BADGER", "ALPACA"
|
||||
"HEDGEHOG", "PIGLET", "FENNEC", "BADGER", "ALPACA", "DINGO", "COLT", "SKUNK", "BUNNY", "IMPALA",
|
||||
"GUANACO", "CAPYBARA", "ELK", "MINK", "PRONGHORN", "CROW", "BUMBLEBEE", "FAWN", "OTTER", "WATERBUCK",
|
||||
"JERBOA", "KITTEN", "ARGALI", "OX", "MARE", "FINCH", "BASILISK", "GOPHER", "HAMSTER", "CANARY", "WOODCHUCK",
|
||||
"ANACONDA"
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
|
||||
@@ -4,7 +4,7 @@ from dcs.vehicles import AirDefence, Unarmed
|
||||
|
||||
from gen.sam.group_generator import GroupGenerator
|
||||
|
||||
GFLAK = [AirDefence.AAA_Flak_Vierling_38, AirDefence.AAA_Flak_Vierling_38, AirDefence.AAA_Flak_18, AirDefence.AAA_Flak_36, AirDefence.AAA_Flak_37, AirDefence.AAA_Flak_38]
|
||||
GFLAK = [AirDefence.AAA_Flak_Vierling_38, AirDefence.AAA_8_8cm_Flak_18, AirDefence.AAA_8_8cm_Flak_36, AirDefence.AAA_8_8cm_Flak_37, AirDefence.AAA_8_8cm_Flak_41, AirDefence.AAA_Flak_38]
|
||||
|
||||
class FlakGenerator(GroupGenerator):
|
||||
"""
|
||||
@@ -15,7 +15,7 @@ class FlakGenerator(GroupGenerator):
|
||||
grid_x = random.randint(2, 4)
|
||||
grid_y = random.randint(2, 4)
|
||||
|
||||
spacing = random.randint(10,40)
|
||||
spacing = random.randint(30,60)
|
||||
|
||||
index = 0
|
||||
mixed = random.choice([True, False])
|
||||
@@ -25,15 +25,29 @@ class FlakGenerator(GroupGenerator):
|
||||
for j in range(grid_y):
|
||||
index = index+1
|
||||
self.add_unit(unit_type, "AAA#" + str(index),
|
||||
self.position.x + spacing*i,
|
||||
self.position.y + spacing*j, self.heading)
|
||||
self.position.x + spacing*i + random.randint(1,5),
|
||||
self.position.y + spacing*j + random.randint(1,5), self.heading)
|
||||
|
||||
if(mixed):
|
||||
unit_type = random.choice(GFLAK)
|
||||
|
||||
# Enough Opel truck to transport the guns
|
||||
for i in range(grid_x):
|
||||
for j in range(grid_y):
|
||||
# Search lights
|
||||
search_pos = self.get_circular_position(random.randint(2,5), 90)
|
||||
for index, pos in enumerate(search_pos):
|
||||
self.add_unit(AirDefence.Flak_Searchlight_37, "SearchLight#" + str(index), pos[0], pos[1], self.heading)
|
||||
|
||||
# Support
|
||||
self.add_unit(AirDefence.Maschinensatz_33, "MC33#", self.position.x-20, self.position.y-20, self.heading)
|
||||
self.add_unit(AirDefence.AAA_Kdo_G_40, "KDO#", self.position.x - 25, self.position.y - 20,
|
||||
self.heading)
|
||||
|
||||
# Commander
|
||||
self.add_unit(Unarmed.Kübelwagen_82, "Kubel#", self.position.x - 35, self.position.y - 20,
|
||||
self.heading)
|
||||
|
||||
# Some Opel Blitz trucks
|
||||
for i in range(int(max(1,grid_x/2))):
|
||||
for j in range(int(max(1,grid_x/2))):
|
||||
self.add_unit(Unarmed.Blitz_3_6_6700A, "AAA#" + str(index),
|
||||
self.position.x + 200 + 9*i,
|
||||
self.position.y + 9*j, 90)
|
||||
self.position.x + 200 + 15*i + random.randint(1,5),
|
||||
self.position.y + 15*j + random.randint(1,5), 90)
|
||||
@@ -50,7 +50,7 @@ SAM_MAP = {
|
||||
AirDefence.SAM_Patriot_EPP_III: PatriotGenerator,
|
||||
AirDefence.SAM_Chaparral_M48: ChaparralGenerator,
|
||||
AirDefence.AAA_Bofors_40mm: BoforsGenerator,
|
||||
AirDefence.AAA_Flak_36: FlakGenerator,
|
||||
AirDefence.AAA_8_8cm_Flak_36: FlakGenerator,
|
||||
AirDefence.SAM_SA_2_LN_SM_90: SA2Generator,
|
||||
AirDefence.SAM_SA_3_S_125_LN_5P73: SA3Generator,
|
||||
AirDefence.SAM_SA_6_Kub_LN_2P25: SA6Generator,
|
||||
|
||||
@@ -42,100 +42,19 @@ class TriggersGenerator:
|
||||
self.conflict = conflict
|
||||
self.game = game
|
||||
|
||||
def _gen_activation_trigger(self, radius: int, player_cp: ControlPoint, player_coalition: str, enemy_coalition: str):
|
||||
conflict_distance = player_cp.position.distance_to_point(self.conflict.position)
|
||||
minimum_radius = max(conflict_distance - TRIGGER_MIN_DISTANCE_FROM_START, TRIGGER_RADIUS_MINIMUM)
|
||||
if minimum_radius < 0:
|
||||
minimum_radius = 0
|
||||
radius = min(minimum_radius, radius)
|
||||
activation_trigger_zone = self.mission.triggers.add_triggerzone(
|
||||
self.conflict.position,
|
||||
radius,
|
||||
name="Activation zone",
|
||||
)
|
||||
activation_trigger = TriggerOnce(Event.NoEvent, "Activation trigger")
|
||||
activation_trigger.add_condition(PartOfCoalitionInZone(player_coalition, activation_trigger_zone.id))
|
||||
activation_trigger.add_condition(FlagIsTrue())
|
||||
activate_by_trigger = []
|
||||
flag_id = 2
|
||||
for coalition_name, coalition in self.mission.coalition.items():
|
||||
for country in coalition.countries.values():
|
||||
if coalition_name == enemy_coalition:
|
||||
for plane_group in country.plane_group + country.helicopter_group:
|
||||
plane_group.late_activation = True
|
||||
self.delayed_trigger(
|
||||
plane_group,
|
||||
flag_id,
|
||||
flag_lower_limit=180,
|
||||
flag_upper_limit=2400,
|
||||
)
|
||||
flag_id += 1
|
||||
|
||||
for vehicle_group in country.vehicle_group:
|
||||
vehicle_group.late_activation = True
|
||||
activate_by_trigger.append(vehicle_group)
|
||||
|
||||
for group in activate_by_trigger:
|
||||
activation_trigger.add_action(ActivateGroup(group.id))
|
||||
|
||||
self.mission.triggerrules.triggers.append(activation_trigger)
|
||||
|
||||
def _gen_push_trigger(self, player_cp: ControlPoint, player_coalition: str):
|
||||
push_by_trigger = []
|
||||
for coalition_name, coalition in self.mission.coalition.items():
|
||||
for country in coalition.countries.values():
|
||||
if coalition_name == player_coalition:
|
||||
for group in country.plane_group + country.helicopter_group:
|
||||
if group.task == AWACS.name or group.task == Refueling.name:
|
||||
continue
|
||||
|
||||
push_by_trigger.append(group)
|
||||
|
||||
"""if not group.units[0].is_human():
|
||||
regroup_heading = self.conflict.to_cp.position.heading_between_point(player_cp.position)
|
||||
|
||||
pos1 = group.position.point_from_heading(regroup_heading, REGROUP_ZONE_DISTANCE)
|
||||
pos2 = group.position.point_from_heading(regroup_heading, REGROUP_ZONE_DISTANCE+5000)
|
||||
w1 = group.add_waypoint(pos1, REGROUP_ALT)
|
||||
w2 = group.add_waypoint(pos2, REGROUP_ALT)
|
||||
|
||||
group.points.remove(w1)
|
||||
group.points.remove(w2)
|
||||
|
||||
group.points.insert(1, w2)
|
||||
group.points.insert(1, w1)
|
||||
|
||||
w1.tasks.append(Silence(True))
|
||||
|
||||
switch_waypoint_task = ControlledTask(SwitchWaypoint(from_waypoint=3, to_waypoint=2))
|
||||
switch_waypoint_task.start_if_user_flag(1, False)
|
||||
w2.tasks.append(switch_waypoint_task)
|
||||
group.points[3].tasks.append(Silence(False))
|
||||
|
||||
group.add_trigger_action(SwitchWaypoint(to_waypoint=4))"""
|
||||
|
||||
push_trigger = TriggerOnce(Event.NoEvent, "Push trigger")
|
||||
|
||||
for group in push_by_trigger:
|
||||
for unit in group.units:
|
||||
push_trigger.add_condition(UnitAltitudeHigherAGL(unit.id, PUSH_TRIGGER_ACTIVATION_AGL))
|
||||
|
||||
if not group.units[0].is_human():
|
||||
push_trigger.add_action(AITaskPush(group.id, 1))
|
||||
|
||||
message_string = self.mission.string("Task force is in the air, proceed with the objective.")
|
||||
push_trigger.add_action(MessageToAll(message_string, clearview=True))
|
||||
push_trigger.add_action(SetFlagValue())
|
||||
|
||||
self.mission.triggerrules.triggers.append(push_trigger)
|
||||
|
||||
def _set_allegiances(self, player_coalition: str, enemy_coalition: str):
|
||||
"""
|
||||
Set airbase initial coalition
|
||||
"""
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if cp.is_global:
|
||||
continue
|
||||
self.mission.terrain.airport_by_id(cp.at.id).set_coalition(cp.captured and player_coalition or enemy_coalition)
|
||||
|
||||
def _set_skill(self, player_coalition: str, enemy_coalition: str):
|
||||
"""
|
||||
Set skill level for all aircraft in the mission
|
||||
"""
|
||||
for coalition_name, coalition in self.mission.coalition.items():
|
||||
if coalition_name == player_coalition:
|
||||
skill_level = self.game.settings.player_skill, self.game.settings.player_skill
|
||||
@@ -153,53 +72,38 @@ class TriggersGenerator:
|
||||
for vehicle_group in country.vehicle_group:
|
||||
vehicle_group.set_skill(Skill(skill_level[1]))
|
||||
|
||||
def delayed_trigger(self, group, flag_id, flag_lower_limit, flag_upper_limit):
|
||||
def _gen_markers(self):
|
||||
"""
|
||||
Create an activation trigger a randomized amount after the main activation occurs
|
||||
:param group:
|
||||
group to activate
|
||||
:param flag_id:
|
||||
ID of the flag to use
|
||||
:param flag_lower_limit:
|
||||
lower limit of what the random time can be (shouldn't be negative)
|
||||
:param flag_upper_limit:
|
||||
uopper limit of what the random time can be
|
||||
:return:
|
||||
N/A
|
||||
Generate markers on F10 map for each existing objective
|
||||
"""
|
||||
trigger_one = TriggerOnce(Event.NoEvent, "Activation trigger")
|
||||
trigger_one.add_condition(FlagEquals(1, 1))
|
||||
trigger_one.add_action(SetFlagValue(flag_id, 1))
|
||||
if self.game.settings.generate_marks:
|
||||
mark_trigger = TriggerOnce(Event.NoEvent, "Marks generator")
|
||||
mark_trigger.add_condition(TimeAfter(1))
|
||||
v = 10
|
||||
for cp in self.game.theater.controlpoints:
|
||||
added = []
|
||||
for ground_object in cp.ground_objects:
|
||||
if ground_object.obj_name not in added:
|
||||
zone = self.mission.triggers.add_triggerzone(ground_object.position, radius=10, hidden=True, name="MARK")
|
||||
if cp.captured:
|
||||
name = ground_object.obj_name + " [ALLY]"
|
||||
else:
|
||||
name = ground_object.obj_name + " [ENEMY]"
|
||||
mark_trigger.add_action(MarkToAll(v, zone.id, String(name)))
|
||||
v = v + 1
|
||||
added.append(ground_object.obj_name)
|
||||
self.mission.triggerrules.triggers.append(mark_trigger)
|
||||
|
||||
trigger_two = TriggerCondition()
|
||||
trigger_two.add_condition(
|
||||
TimeSinceFlag(
|
||||
flag_id,
|
||||
seconds=random.randint(
|
||||
flag_lower_limit,
|
||||
flag_upper_limit
|
||||
)
|
||||
)
|
||||
)
|
||||
trigger_two.add_action(ActivateGroup(group.id))
|
||||
|
||||
self.mission.triggerrules.triggers.append(trigger_one)
|
||||
self.mission.triggerrules.triggers.append(trigger_two)
|
||||
|
||||
def generate(self, player_cp: ControlPoint, is_quick: bool, activation_trigger_radius: int, awacs_enabled: bool):
|
||||
def generate(self):
|
||||
player_coalition = self.game.player_country in db.BLUEFOR_FACTIONS and "blue" or "red"
|
||||
enemy_coalition = player_coalition == "blue" and "red" or "blue"
|
||||
|
||||
self.mission.coalition["blue"].bullseye = {"x": self.conflict.position.x,
|
||||
"y": self.conflict.position.y}
|
||||
"y": self.conflict.position.y}
|
||||
self.mission.coalition["red"].bullseye = {"x": self.conflict.position.x,
|
||||
"y": self.conflict.position.y}
|
||||
|
||||
self._set_skill(player_coalition, enemy_coalition)
|
||||
self._set_allegiances(player_coalition, enemy_coalition)
|
||||
|
||||
#if not is_quick:
|
||||
# # TODO: waypoint parts of this should not be post-hacked but added in airgen
|
||||
# self._gen_activation_trigger(activation_trigger_radius, player_cp, player_coalition, enemy_coalition)
|
||||
# self._gen_push_trigger(player_cp, player_coalition)
|
||||
self._gen_markers()
|
||||
|
||||
|
||||
@@ -1,31 +1,54 @@
|
||||
from userdata import logging_config
|
||||
|
||||
# Logging setup
|
||||
VERSION_STRING = "2.0RC7"
|
||||
logging_config.init_logging(VERSION_STRING)
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from shutil import copyfile
|
||||
|
||||
import dcs
|
||||
from PySide2 import QtWidgets
|
||||
from PySide2.QtGui import QPixmap
|
||||
from PySide2.QtWidgets import QApplication, QSplashScreen
|
||||
from dcs import installation
|
||||
|
||||
from qt_ui import uiconstants
|
||||
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
||||
from qt_ui.windows.QLiberationWindow import QLiberationWindow
|
||||
from userdata import persistency, logging as logging_module
|
||||
from qt_ui.windows.preferences.QLiberationFirstStartWindow import QLiberationFirstStartWindow
|
||||
from userdata import liberation_install, persistency
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
persistency.setup(installation.get_dcs_saved_games_directory())
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
css = ""
|
||||
with open("./resources/stylesheets/style.css") as stylesheet:
|
||||
app.setStyleSheet(stylesheet.read())
|
||||
|
||||
|
||||
|
||||
|
||||
# Inject custom payload in pydcs framework
|
||||
custom_payloads = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..\\resources\\customized_payloads")
|
||||
if os.path.exists(custom_payloads):
|
||||
dcs.planes.FlyingType.payload_dirs.append(custom_payloads)
|
||||
else:
|
||||
# For release version the path is different.
|
||||
custom_payloads = os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||
"resources\\customized_payloads")
|
||||
if os.path.exists(custom_payloads):
|
||||
dcs.planes.FlyingType.payload_dirs.append(custom_payloads)
|
||||
|
||||
VERSION_STRING = "2.0"
|
||||
logging_module.setup_version_string(VERSION_STRING)
|
||||
logging.info("Using {} as userdata folder".format(persistency.base_path()))
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
first_start = liberation_install.init()
|
||||
if first_start:
|
||||
window = QLiberationFirstStartWindow()
|
||||
window.exec_()
|
||||
|
||||
logging.info("Using {} as 'Saved Game Folder'".format(persistency.base_path()))
|
||||
logging.info("Using {} as 'DCS installation folder'".format(liberation_install.get_dcs_install_directory()))
|
||||
|
||||
# Splash screen setup
|
||||
pixmap = QPixmap("./resources/ui/splash_screen.png")
|
||||
@@ -38,24 +61,29 @@ if __name__ == "__main__":
|
||||
uiconstants.load_aircraft_icons()
|
||||
uiconstants.load_vehicle_icons()
|
||||
|
||||
|
||||
css = ""
|
||||
with open("./resources/stylesheets/style.css") as stylesheet:
|
||||
css = stylesheet.read()
|
||||
|
||||
# Replace DCS Mission scripting file to allow DCS Liberation to work
|
||||
print("Replace : " + installation.get_dcs_install_directory() + os.path.sep + "Scripts/MissionScripting.lua")
|
||||
copyfile("./resources/scripts/MissionScripting.lua", installation.get_dcs_install_directory() + os.path.sep + "Scripts/MissionScripting.lua")
|
||||
app.processEvents()
|
||||
try:
|
||||
liberation_install.replace_mission_scripting_file()
|
||||
except:
|
||||
error_dialog = QtWidgets.QErrorMessage()
|
||||
error_dialog.setWindowTitle("Wrong DCS installation directory.")
|
||||
error_dialog.showMessage("Unable to modify Mission Scripting file. Possible issues with rights. Try running as admin, or please perform the modification of the MissionScripting file manually.")
|
||||
error_dialog.exec_()
|
||||
|
||||
# Apply CSS (need works)
|
||||
app.setStyleSheet(css)
|
||||
GameUpdateSignal()
|
||||
|
||||
# Start window
|
||||
window = QLiberationWindow()
|
||||
window.showMaximized()
|
||||
|
||||
splash.finish(window)
|
||||
sys.exit(app.exec_())
|
||||
qt_execution_code = app.exec_()
|
||||
|
||||
# Restore Mission Scripting file
|
||||
logging.info("QT App terminated with status code : " + str(qt_execution_code))
|
||||
logging.info("Attempt to restore original mission scripting file")
|
||||
liberation_install.restore_original_mission_scripting()
|
||||
logging.info("QT process exited with code : " + str(qt_execution_code))
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@ from game.event import UnitsDeliveryEvent, FrontlineAttackEvent
|
||||
from theater.theatergroundobject import CATEGORY_MAP
|
||||
|
||||
URLS : Dict[str, str] = {
|
||||
"Manual": "https://github.com/shdwp/dcs_liberation/wiki/Manual",
|
||||
"Manual": "https://github.com/khopa/dcs_liberation/wiki",
|
||||
"Troubleshooting": "https://github.com/shdwp/dcs_liberation/wiki/Troubleshooting",
|
||||
"Modding": "https://github.com/shdwp/dcs_liberation/wiki/Modding-tutorial",
|
||||
"Repository": "https://github.com/shdwp/dcs_liberation",
|
||||
"Repository": "https://github.com/khopa/dcs_liberation",
|
||||
"ForumThread": "https://forums.eagle.ru/showthread.php?t=214834",
|
||||
"Issues": "https://github.com/shdwp/dcs_liberation/issues"
|
||||
"Issues": "https://github.com/khopa/dcs_liberation/issues"
|
||||
}
|
||||
|
||||
LABELS_OPTIONS = ["Full", "Abbreviated", "Dot Only", "Off"]
|
||||
@@ -54,6 +54,7 @@ def load_icons():
|
||||
ICONS["Terrain_Persian_Gulf"] = QPixmap("./resources/ui/terrain_pg.gif")
|
||||
ICONS["Terrain_Nevada"] = QPixmap("./resources/ui/terrain_nevada.gif")
|
||||
ICONS["Terrain_Normandy"] = QPixmap("./resources/ui/terrain_normandy.gif")
|
||||
ICONS["Terrain_Channel"] = QPixmap("./resources/ui/terrain_channel.gif")
|
||||
|
||||
ICONS["Dawn"] = QPixmap("./resources/ui/daytime/dawn.png")
|
||||
ICONS["Day"] = QPixmap("./resources/ui/daytime/day.png")
|
||||
@@ -73,6 +74,8 @@ def load_icons():
|
||||
ICONS[category] = QPixmap("./resources/ui/ground_assets/" + category + ".png")
|
||||
ICONS[category + "_blue"] = QPixmap("./resources/ui/ground_assets/" + category + "_blue.png")
|
||||
ICONS["destroyed"] = QPixmap("./resources/ui/ground_assets/destroyed.png")
|
||||
ICONS["ship"] = QPixmap("./resources/ui/ground_assets/ship.png")
|
||||
ICONS["ship_blue"] = QPixmap("./resources/ui/ground_assets/ship_blue.png")
|
||||
|
||||
ICONS["Generator"] = QPixmap("./resources/ui/misc/generator.png")
|
||||
ICONS["Missile"] = QPixmap("./resources/ui/misc/missile.png")
|
||||
|
||||
@@ -1,146 +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.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:
|
||||
wpt = FlightWaypoint((cp.position.x + ecp.position.x)/2, (cp.position.y + ecp.position.y)/2, 800)
|
||||
wpt.name = "Frontline " + cp.name + "/" + ecp.name + " [CAS]"
|
||||
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.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
|
||||
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.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + u.type + " #" + str(j)
|
||||
wpt.pretty_name = wpt.name
|
||||
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.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)
|
||||
@@ -32,7 +32,7 @@ class QTopPanel(QFrame):
|
||||
self.passTurnButton.setProperty("style", "btn-primary")
|
||||
self.passTurnButton.clicked.connect(self.passTurn)
|
||||
|
||||
self.proceedButton = QPushButton("Proceed")
|
||||
self.proceedButton = QPushButton("Mission Planning")
|
||||
self.proceedButton.setIcon(CONST.ICONS["Proceed"])
|
||||
self.proceedButton.setProperty("style", "btn-primary")
|
||||
self.proceedButton.clicked.connect(self.proceed)
|
||||
|
||||
53
qt_ui/widgets/combos/QFilteredComboBox.py
Normal file
53
qt_ui/widgets/combos/QFilteredComboBox.py
Normal 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)
|
||||
139
qt_ui/widgets/combos/QPredefinedWaypointSelectionComboBox.py
Normal file
139
qt_ui/widgets/combos/QPredefinedWaypointSelectionComboBox.py
Normal 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)
|
||||
77
qt_ui/widgets/combos/QSEADTargetSelectionComboBox.py
Normal file
77
qt_ui/widgets/combos/QSEADTargetSelectionComboBox.py
Normal 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)
|
||||
|
||||
|
||||
74
qt_ui/widgets/combos/QStrikeTargetSelectionComboBox.py
Normal file
74
qt_ui/widgets/combos/QStrikeTargetSelectionComboBox.py
Normal 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)
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import typing
|
||||
from typing import Dict
|
||||
|
||||
from PySide2 import QtCore
|
||||
from PySide2.QtCore import Qt, QRect, QPointF
|
||||
from PySide2.QtGui import QPixmap, QBrush, QColor, QWheelEvent, QPen, QFont
|
||||
from PySide2.QtWidgets import QGraphicsView, QFrame, QGraphicsOpacityEffect
|
||||
@@ -9,6 +10,7 @@ from dcs.mapping import point_from_heading
|
||||
|
||||
import qt_ui.uiconstants as CONST
|
||||
from game import Game, db
|
||||
from game.data.radar_db import UNITS_WITH_RADAR
|
||||
from game.event import UnitsDeliveryEvent, Event, ControlPointType
|
||||
from gen import Conflict
|
||||
from qt_ui.widgets.map.QLiberationScene import QLiberationScene
|
||||
@@ -40,6 +42,8 @@ class QLiberationMap(QGraphicsView):
|
||||
self.setMinimumSize(800,600)
|
||||
self.setMaximumHeight(2160)
|
||||
self._zoom = 0
|
||||
self.factor = 1
|
||||
self.factorized = 1
|
||||
self.init_scene()
|
||||
self.connectSignals()
|
||||
self.setGame(game)
|
||||
@@ -62,12 +66,59 @@ class QLiberationMap(QGraphicsView):
|
||||
if self.game is not None:
|
||||
self.reload_scene()
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
Uncomment to set up theather reference points
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
#super(QLiberationMap, self).keyPressEvent(event)
|
||||
|
||||
numpad_mod = int(event.modifiers()) & QtCore.Qt.KeypadModifier
|
||||
i = 0
|
||||
for k,v in self.game.theater.reference_points.items():
|
||||
if i == 0:
|
||||
point_0 = k
|
||||
else:
|
||||
point_1 = k
|
||||
i = i + 1
|
||||
|
||||
if event.key() == QtCore.Qt.Key_Down:
|
||||
self.game.theater.reference_points[point_0] = self.game.theater.reference_points[point_0][0] + 100, self.game.theater.reference_points[point_0][1]
|
||||
if event.key() == QtCore.Qt.Key_Up:
|
||||
self.game.theater.reference_points[point_0] = self.game.theater.reference_points[point_0][0] - 100, self.game.theater.reference_points[point_0][1]
|
||||
if event.key() == QtCore.Qt.Key_Left:
|
||||
self.game.theater.reference_points[point_0] = self.game.theater.reference_points[point_0][0], self.game.theater.reference_points[point_0][1] + 100
|
||||
if event.key() == QtCore.Qt.Key_Right:
|
||||
self.game.theater.reference_points[point_0] = self.game.theater.reference_points[point_0][0], self.game.theater.reference_points[point_0][1] - 100
|
||||
|
||||
|
||||
if event.key() == QtCore.Qt.Key_2 and numpad_mod:
|
||||
self.game.theater.reference_points[point_1] = self.game.theater.reference_points[point_1][0] + 100, self.game.theater.reference_points[point_1][1]
|
||||
if event.key() == QtCore.Qt.Key_8 and numpad_mod:
|
||||
self.game.theater.reference_points[point_1] = self.game.theater.reference_points[point_1][0] - 100, self.game.theater.reference_points[point_1][1]
|
||||
if event.key() == QtCore.Qt.Key_4 and numpad_mod:
|
||||
self.game.theater.reference_points[point_1] = self.game.theater.reference_points[point_1][0], self.game.theater.reference_points[point_1][1] + 100
|
||||
if event.key() == QtCore.Qt.Key_6 and numpad_mod:
|
||||
self.game.theater.reference_points[point_1] = self.game.theater.reference_points[point_1][0], self.game.theater.reference_points[point_1][1] - 100
|
||||
|
||||
print(self.game.theater.reference_points)
|
||||
self.reload_scene()
|
||||
"""
|
||||
|
||||
|
||||
|
||||
def reload_scene(self):
|
||||
scene = self.scene()
|
||||
scene.clear()
|
||||
|
||||
self.addBackground()
|
||||
#self.add_game_events()
|
||||
|
||||
# Uncomment below to help set up theater reference points
|
||||
#for i, r in enumerate(self.game.theater.reference_points.items()):
|
||||
# text = scene.addText(str(r), font=QFont("Trebuchet MS", 10, weight=5, italic=False))
|
||||
# text.setPos(0, i * 24)
|
||||
|
||||
for cp in self.game.theater.controlpoints:
|
||||
|
||||
@@ -83,22 +134,31 @@ class QLiberationMap(QGraphicsView):
|
||||
pen = QPen(brush=CONST.COLORS["red"])
|
||||
brush = CONST.COLORS["red_transparent"]
|
||||
|
||||
added_objects = []
|
||||
for ground_object in cp.ground_objects:
|
||||
if ground_object.obj_name in added_objects:
|
||||
continue
|
||||
|
||||
|
||||
go_pos = self._transform_point(ground_object.position)
|
||||
if not ground_object.airbase_group:
|
||||
scene.addItem(QMapGroundObject(self, go_pos[0], go_pos[1], 12, 12, cp, ground_object))
|
||||
buildings = self.game.theater.find_ground_objects_by_obj_name(ground_object.obj_name)
|
||||
scene.addItem(QMapGroundObject(self, go_pos[0], go_pos[1], 12, 12, cp, ground_object, buildings))
|
||||
|
||||
if ground_object.category == "aa" and self.get_display_rule("sam"):
|
||||
max_range = 0
|
||||
has_radar = False
|
||||
if ground_object.groups:
|
||||
for g in ground_object.groups:
|
||||
for u in g.units:
|
||||
unit = db.unit_type_from_name(u.type)
|
||||
if unit in UNITS_WITH_RADAR:
|
||||
has_radar = True
|
||||
if unit.threat_range > max_range:
|
||||
max_range = unit.threat_range
|
||||
if max_range >= 6000:
|
||||
if has_radar:
|
||||
scene.addEllipse(go_pos[0] - max_range/300.0 + 8, go_pos[1] - max_range/300.0 + 8, max_range/150.0, max_range/150.0, pen, brush)
|
||||
added_objects.append(ground_object.obj_name)
|
||||
|
||||
for cp in self.game.theater.enemy_points():
|
||||
if self.get_display_rule("lines"):
|
||||
@@ -203,14 +263,21 @@ class QLiberationMap(QGraphicsView):
|
||||
if event.angleDelta().y() > 0:
|
||||
factor = 1.25
|
||||
self._zoom += 1
|
||||
if self._zoom < 10:
|
||||
self.scale(factor, factor)
|
||||
self.factorized *= factor
|
||||
else:
|
||||
self._zoom = 9
|
||||
else:
|
||||
factor = 0.8
|
||||
self._zoom -= 1
|
||||
if self._zoom > -5:
|
||||
self.scale(factor, factor)
|
||||
self.factorized *= factor
|
||||
else:
|
||||
self._zoom = -4
|
||||
|
||||
if self._zoom > -5:
|
||||
self.scale(factor, factor)
|
||||
else:
|
||||
self._zoom = -5
|
||||
#print(self.factorized, factor, self._zoom)
|
||||
|
||||
def _transform_point(self, p: Point, treshold=30) -> (int, int):
|
||||
point_a = list(self.game.theater.reference_points.keys())[0]
|
||||
@@ -240,56 +307,6 @@ class QLiberationMap(QGraphicsView):
|
||||
|
||||
return X > treshold and X or treshold, Y > treshold and Y or treshold
|
||||
|
||||
def add_game_events(self):
|
||||
|
||||
occupied_rects = []
|
||||
|
||||
for cp in self.game.theater.controlpoints:
|
||||
point = self._transform_point(cp.position)
|
||||
occupied_rects.append(QRect(point[0] - 16, point[1] - 16, 32, 48))
|
||||
|
||||
def _location_to_rect(location: Point) -> QRect:
|
||||
nonlocal occupied_rects
|
||||
point = self._transform_point(location)
|
||||
rect = QRect(point[0] - 16, point[1] - 16, 32, 32)
|
||||
|
||||
i = 0
|
||||
while True:
|
||||
result = True
|
||||
for occupied_rect in occupied_rects:
|
||||
if rect.intersects(occupied_rect):
|
||||
i += 1
|
||||
if i % 2:
|
||||
rect.setY(rect.y() + occupied_rect.height())
|
||||
else:
|
||||
rect.setX(rect.x() + occupied_rect.width())
|
||||
result = False
|
||||
break
|
||||
if result:
|
||||
break
|
||||
occupied_rects.append(rect)
|
||||
return rect
|
||||
|
||||
def _events_priority_key(event: Event) -> int:
|
||||
priority_list = [InfantryTransportEvent, StrikeEvent, BaseAttackEvent, UnitsDeliveryEvent]
|
||||
if type(event) not in priority_list:
|
||||
return 0
|
||||
else:
|
||||
return priority_list.index(type(event)) + 1
|
||||
|
||||
scene = self.scene()
|
||||
events = self.game.events
|
||||
events.sort(key=_events_priority_key, reverse=True)
|
||||
|
||||
for event in events:
|
||||
|
||||
location = event.location
|
||||
if type(event) in [FrontlineAttackEvent, FrontlinePatrolEvent, ConvoyStrikeEvent]:
|
||||
location = self._frontline_center(event.from_cp, event.to_cp)
|
||||
|
||||
rect = _location_to_rect(location)
|
||||
scene.addItem(QMapEvent(self, rect.x(), rect.y(), 32, 32, event))
|
||||
|
||||
def addBackground(self):
|
||||
scene = self.scene()
|
||||
|
||||
|
||||
@@ -9,14 +9,15 @@ from theater import TheaterGroundObject, ControlPoint
|
||||
|
||||
class QMapGroundObject(QGraphicsRectItem):
|
||||
|
||||
def __init__(self, parent, x: float, y: float, w: float, h: float, cp: ControlPoint, model: TheaterGroundObject):
|
||||
def __init__(self, parent, x: float, y: float, w: float, h: float, cp: ControlPoint, model: TheaterGroundObject, buildings=[]):
|
||||
super(QMapGroundObject, self).__init__(x, y, w, h)
|
||||
self.model = model
|
||||
self.cp = cp
|
||||
self.parent = parent
|
||||
self.setAcceptHoverEvents(True)
|
||||
self.setZValue(2)
|
||||
#self.setFlag(QGraphicsItem.ItemIgnoresTransformations, True)
|
||||
self.buildings = buildings
|
||||
self.setFlag(QGraphicsItem.ItemIgnoresTransformations, False)
|
||||
|
||||
if len(self.model.groups) > 0:
|
||||
units = {}
|
||||
@@ -32,17 +33,26 @@ class QMapGroundObject(QGraphicsRectItem):
|
||||
tooltip = tooltip + str(unit) + "x" + str(units[unit]) + "\n"
|
||||
self.setToolTip(tooltip[:-1])
|
||||
else:
|
||||
self.setToolTip("[" + self.model.obj_name + "] : " + self.model.category)
|
||||
tooltip = "[" + self.model.obj_name + "]" + "\n"
|
||||
for building in buildings:
|
||||
if not building.is_dead:
|
||||
tooltip = tooltip + str(building.dcs_identifier) + "\n"
|
||||
self.setToolTip(tooltip[:-1])
|
||||
|
||||
|
||||
def paint(self, painter, option, widget=None):
|
||||
#super(QMapControlPoint, self).paint(painter, option, widget)
|
||||
if self.parent.get_display_rule("go"):
|
||||
painter.save()
|
||||
|
||||
cat = self.model.category
|
||||
if cat == "aa" and self.model.sea_object:
|
||||
cat = "ship"
|
||||
|
||||
if not self.model.is_dead and not self.cp.captured:
|
||||
painter.drawPixmap(option.rect, CONST.ICONS[self.model.category])
|
||||
painter.drawPixmap(option.rect, CONST.ICONS[cat])
|
||||
elif not self.model.is_dead:
|
||||
painter.drawPixmap(option.rect, CONST.ICONS[self.model.category + "_blue"])
|
||||
painter.drawPixmap(option.rect, CONST.ICONS[cat + "_blue"])
|
||||
else:
|
||||
painter.drawPixmap(option.rect, CONST.ICONS["destroyed"])
|
||||
painter.restore()
|
||||
|
||||
42
qt_ui/widgets/views/QSeadTargetInfoView.py
Normal file
42
qt_ui/widgets/views/QSeadTargetInfoView.py
Normal 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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
75
qt_ui/widgets/views/QStrikeTargetInfoView.py
Normal file
75
qt_ui/widgets/views/QStrikeTargetInfoView.py
Normal 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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -57,6 +57,14 @@ class QDebriefingWindow(QDialog):
|
||||
except:
|
||||
print("Issue adding " + str(unit_type) + " to debriefing information")
|
||||
|
||||
for building, count in self.debriefing.player_dead_buildings_dict.items():
|
||||
try:
|
||||
lostUnitsLayout.addWidget(QLabel(building, row, 0))
|
||||
lostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
|
||||
row += 1
|
||||
except:
|
||||
print("Issue adding " + str(building) + " to debriefing information")
|
||||
|
||||
self.layout.addWidget(lostUnits)
|
||||
|
||||
# Enemy lost units
|
||||
@@ -87,6 +95,14 @@ class QDebriefingWindow(QDialog):
|
||||
enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
|
||||
row += 1
|
||||
|
||||
for building, count in self.debriefing.enemy_dead_buildings_dict.items():
|
||||
try:
|
||||
enemylostUnitsLayout.addWidget(QLabel(building), row, 0)
|
||||
enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
|
||||
row += 1
|
||||
except:
|
||||
print("Issue adding " + str(building) + " to debriefing information")
|
||||
|
||||
self.layout.addWidget(enemylostUnits)
|
||||
|
||||
# confirm button
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import logging
|
||||
import sys
|
||||
import webbrowser
|
||||
|
||||
from PySide2 import QtGui
|
||||
from PySide2.QtCore import Qt
|
||||
from PySide2.QtGui import QIcon
|
||||
from PySide2.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QMainWindow, QAction, QMessageBox, QDesktopWidget, \
|
||||
from PySide2.QtWidgets import QWidget, QVBoxLayout, QMainWindow, QAction, QMessageBox, QDesktopWidget, \
|
||||
QSplitter
|
||||
|
||||
import qt_ui.uiconstants as CONST
|
||||
@@ -12,10 +12,12 @@ from game import Game
|
||||
from qt_ui.uiconstants import URLS
|
||||
from qt_ui.widgets.QTopPanel import QTopPanel
|
||||
from qt_ui.widgets.map.QLiberationMap import QLiberationMap
|
||||
from qt_ui.windows.preferences import QLiberationPreferences
|
||||
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal, DebriefingSignal
|
||||
from qt_ui.windows.QDebriefingWindow import QDebriefingWindow
|
||||
from qt_ui.windows.QNewGameWizard import NewGameWizard
|
||||
from qt_ui.windows.infos.QInfoPanel import QInfoPanel
|
||||
from qt_ui.windows.preferences.QLiberationPreferencesWindow import QLiberationPreferencesWindow
|
||||
from userdata import persistency
|
||||
|
||||
|
||||
@@ -79,6 +81,10 @@ class QLiberationWindow(QMainWindow):
|
||||
self.showAboutDialogAction.setIcon(QIcon.fromTheme("help-about"))
|
||||
self.showAboutDialogAction.triggered.connect(self.showAboutDialog)
|
||||
|
||||
self.showLiberationPrefDialogAction = QAction("Preferences", self)
|
||||
self.showLiberationPrefDialogAction.setIcon(QIcon.fromTheme("help-about"))
|
||||
self.showLiberationPrefDialogAction.triggered.connect(self.showLiberationDialog)
|
||||
|
||||
def initToolbar(self):
|
||||
self.tool_bar = self.addToolBar("File")
|
||||
self.tool_bar.addAction(self.newGameAction)
|
||||
@@ -92,17 +98,21 @@ class QLiberationWindow(QMainWindow):
|
||||
file_menu.addAction(self.newGameAction)
|
||||
#file_menu.addAction(QIcon(CONST.ICONS["Open"]), "Open") # TODO : implement
|
||||
file_menu.addAction(self.saveGameAction)
|
||||
file_menu.addSeparator()
|
||||
file_menu.addAction(self.showLiberationPrefDialogAction)
|
||||
file_menu.addSeparator()
|
||||
#file_menu.addAction("Save As") # TODO : implement
|
||||
#file_menu.addAction("Close Current Game", lambda: self.closeGame()) # Not working
|
||||
file_menu.addAction("Exit" , lambda: self.exit())
|
||||
|
||||
|
||||
help_menu = self.menu.addMenu("Help")
|
||||
#help_menu.addAction("Online Manual", lambda: webbrowser.open_new_tab(URLS["Manual"]))
|
||||
help_menu.addAction("Online Manual", lambda: webbrowser.open_new_tab(URLS["Manual"]))
|
||||
help_menu.addAction("Discord", lambda: webbrowser.open_new_tab("https://" + "discord.gg" + "/" + "bKrt" + "rkJ"))
|
||||
#help_menu.addAction("Troubleshooting Guide", lambda: webbrowser.open_new_tab(URLS["Troubleshooting"]))
|
||||
#help_menu.addAction("Modding Guide", lambda: webbrowser.open_new_tab(URLS["Modding"]))
|
||||
#help_menu.addSeparator() ----> Note from Khopa : I disable these links since it's not up to date for this branch
|
||||
help_menu.addAction("Contribute", lambda: webbrowser.open_new_tab(URLS["Repository"]))
|
||||
#help_menu.addAction("Contribute", lambda: webbrowser.open_new_tab(URLS["Repository"]))
|
||||
help_menu.addAction("Forum Thread", lambda: webbrowser.open_new_tab(URLS["ForumThread"]))
|
||||
help_menu.addAction("Report an issue", lambda: webbrowser.open_new_tab(URLS["Issues"]))
|
||||
help_menu.addSeparator()
|
||||
@@ -154,12 +164,12 @@ class QLiberationWindow(QMainWindow):
|
||||
wizard.accepted.connect(lambda: self.onGameGenerated(wizard.generatedGame))
|
||||
|
||||
def saveGame(self):
|
||||
print("Saving game")
|
||||
logging.info("Saving game")
|
||||
persistency.save_game(self.game)
|
||||
GameUpdateSignal.get_instance().updateGame(self.game)
|
||||
|
||||
def onGameGenerated(self, game: Game):
|
||||
print("On Game generated")
|
||||
logging.info("On Game generated")
|
||||
self.game = game
|
||||
GameUpdateSignal.get_instance().updateGame(self.game)
|
||||
|
||||
@@ -189,10 +199,14 @@ class QLiberationWindow(QMainWindow):
|
||||
about.setWindowTitle("About DCS Liberation")
|
||||
about.setIcon(QMessageBox.Icon.Information)
|
||||
about.setText(text)
|
||||
print(about.textFormat())
|
||||
logging.info(about.textFormat())
|
||||
about.exec_()
|
||||
|
||||
def showLiberationDialog(self):
|
||||
self.subwindow = QLiberationPreferencesWindow()
|
||||
self.subwindow.show()
|
||||
|
||||
def onDebriefing(self, debrief: DebriefingSignal):
|
||||
print("On Debriefing")
|
||||
logging.info("On Debriefing")
|
||||
self.debriefing = QDebriefingWindow(debrief.debriefing, debrief.gameEvent, debrief.game)
|
||||
self.debriefing.show()
|
||||
|
||||
@@ -8,8 +8,7 @@ from dcs.task import CAP, CAS
|
||||
import qt_ui.uiconstants as CONST
|
||||
from game import db, Game
|
||||
from gen import namegen
|
||||
from theater import start_generator, persiangulf, nevada, caucasus, ConflictTheater, normandy
|
||||
from userdata.logging import version_string
|
||||
from theater import start_generator, persiangulf, nevada, caucasus, ConflictTheater, normandy, thechannel
|
||||
|
||||
|
||||
class NewGameWizard(QtWidgets.QWizard):
|
||||
@@ -38,8 +37,12 @@ class NewGameWizard(QtWidgets.QWizard):
|
||||
isTerrainNttr = self.field("isTerrainNttr")
|
||||
isTerrainCaucasusSmall = self.field("isTerrainCaucasusSmall")
|
||||
isTerrainCaucasusSmallInverted = self.field("isTerrainCaucasusSmallInverted")
|
||||
isTerrainCaucasusNorth= self.field("isTerrainCaucasusNorth")
|
||||
isIranianCampaignTheater = self.field("isIranianCampaignTheater")
|
||||
isTerrainNormandy = self.field("isTerrainNormandy")
|
||||
isTerrainNormandySmall = self.field("isTerrainNormandySmall")
|
||||
isTerrainChannel = self.field("isTerrainChannel")
|
||||
isTerrainChannelComplete = self.field("isTerrainChannelComplete")
|
||||
isTerrainEmirates = self.field("isTerrainEmirates")
|
||||
timePeriod = db.TIME_PERIODS[list(db.TIME_PERIODS.keys())[self.field("timePeriod")]]
|
||||
midGame = self.field("midGame")
|
||||
@@ -56,12 +59,20 @@ class NewGameWizard(QtWidgets.QWizard):
|
||||
conflicttheater = caucasus.WesternGeorgia()
|
||||
elif isTerrainCaucasusSmallInverted:
|
||||
conflicttheater = caucasus.WesternGeorgiaInverted()
|
||||
elif isTerrainCaucasusNorth:
|
||||
conflicttheater = caucasus.NorthCaucasus()
|
||||
elif isIranianCampaignTheater:
|
||||
conflicttheater = persiangulf.IranianCampaign()
|
||||
elif isTerrainEmirates:
|
||||
conflicttheater = persiangulf.Emirates()
|
||||
elif isTerrainNormandy:
|
||||
conflicttheater = normandy.NormandyTheater()
|
||||
elif isTerrainNormandySmall:
|
||||
conflicttheater = normandy.NormandySmall()
|
||||
elif isTerrainChannel:
|
||||
conflicttheater = thechannel.ChannelTheater()
|
||||
elif isTerrainChannelComplete:
|
||||
conflicttheater = thechannel.ChannelTheaterComplete()
|
||||
else:
|
||||
conflicttheater = caucasus.CaucasusTheater()
|
||||
|
||||
@@ -97,7 +108,7 @@ class NewGameWizard(QtWidgets.QWizard):
|
||||
game.budget = int(game.budget * multiplier)
|
||||
game.settings.multiplier = multiplier
|
||||
game.settings.sams = True
|
||||
game.settings.version = version_string()
|
||||
game.settings.version = "2.0RC7"
|
||||
|
||||
if midgame:
|
||||
game.budget = game.budget * 4 * len(list(conflicttheater.conflicts()))
|
||||
@@ -218,12 +229,14 @@ class TheaterConfiguration(QtWidgets.QWizardPage):
|
||||
|
||||
# Terrain selection
|
||||
terrainGroup = QtWidgets.QGroupBox("Terrain")
|
||||
terrainCaucasusSmall = QtWidgets.QRadioButton("Caucasus - Western Georgia [RECOMMENDED]")
|
||||
terrainCaucasusSmall = QtWidgets.QRadioButton("Caucasus - Western Georgia [RECOMMENDED - Early Cold War Era]")
|
||||
terrainCaucasusSmall.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Caucasus"]))
|
||||
terrainCaucasusSmallInverted = QtWidgets.QRadioButton("Caucasus - Western Georgia Inverted [RECOMMENDED]")
|
||||
terrainCaucasusSmallInverted = QtWidgets.QRadioButton("Caucasus - Western Georgia Inverted [RECOMMENDED - Early Cold War Era]")
|
||||
terrainCaucasusSmallInverted.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Caucasus"]))
|
||||
terrainCaucasus = QtWidgets.QRadioButton("Caucasus - Full map [NOT TESTED]")
|
||||
terrainCaucasus.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Caucasus"]))
|
||||
terrainCaucasusNorth = QtWidgets.QRadioButton("Caucasus - North - [RECOMMENDED - Modern Era]")
|
||||
terrainCaucasusNorth.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Caucasus"]))
|
||||
|
||||
terrainPg = QtWidgets.QRadioButton("Persian Gulf - Full Map [NOT TESTED]")
|
||||
terrainPg.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Persian_Gulf"]))
|
||||
@@ -233,8 +246,14 @@ class TheaterConfiguration(QtWidgets.QWizardPage):
|
||||
terrainEmirates.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Persian_Gulf"]))
|
||||
terrainNttr = QtWidgets.QRadioButton("Nevada - North Nevada [RECOMMENDED]")
|
||||
terrainNttr.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Nevada"]))
|
||||
terrainNormandy = QtWidgets.QRadioButton("Normandy [Alpha]")
|
||||
terrainNormandy = QtWidgets.QRadioButton("Normandy")
|
||||
terrainNormandy.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Normandy"]))
|
||||
terrainNormandySmall = QtWidgets.QRadioButton("Normandy Small")
|
||||
terrainNormandySmall.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Normandy"]))
|
||||
terrainChannel = QtWidgets.QRadioButton("The Channel : Start in Dunkirk")
|
||||
terrainChannel.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Channel"]))
|
||||
terrainChannelComplete = QtWidgets.QRadioButton("The Channel : Battle of Britain")
|
||||
terrainChannelComplete.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Channel"]))
|
||||
terrainCaucasusSmall.setChecked(True)
|
||||
|
||||
# Time Period
|
||||
@@ -250,23 +269,31 @@ class TheaterConfiguration(QtWidgets.QWizardPage):
|
||||
self.registerField('isTerrainCaucasus', terrainCaucasus)
|
||||
self.registerField('isTerrainCaucasusSmall', terrainCaucasusSmall)
|
||||
self.registerField('isTerrainCaucasusSmallInverted', terrainCaucasusSmallInverted)
|
||||
self.registerField('isTerrainCaucasusNorth', terrainCaucasusNorth)
|
||||
self.registerField('isTerrainPg', terrainPg)
|
||||
self.registerField('isIranianCampaignTheater', terrainIran)
|
||||
self.registerField('isTerrainEmirates', terrainEmirates)
|
||||
self.registerField('isTerrainNttr', terrainNttr)
|
||||
self.registerField('isTerrainNormandy', terrainNormandy)
|
||||
self.registerField('isTerrainNormandySmall', terrainNormandySmall)
|
||||
self.registerField('isTerrainChannel', terrainChannel)
|
||||
self.registerField('isTerrainChannelComplete', terrainChannelComplete)
|
||||
self.registerField('timePeriod', timePeriodSelect)
|
||||
|
||||
# Build layout
|
||||
terrainGroupLayout = QtWidgets.QVBoxLayout()
|
||||
terrainGroupLayout.addWidget(terrainCaucasusSmall)
|
||||
terrainGroupLayout.addWidget(terrainCaucasusSmallInverted)
|
||||
terrainGroupLayout.addWidget(terrainCaucasusNorth)
|
||||
terrainGroupLayout.addWidget(terrainCaucasus)
|
||||
terrainGroupLayout.addWidget(terrainIran)
|
||||
terrainGroupLayout.addWidget(terrainEmirates)
|
||||
terrainGroupLayout.addWidget(terrainPg)
|
||||
terrainGroupLayout.addWidget(terrainNttr)
|
||||
terrainGroupLayout.addWidget(terrainNormandy)
|
||||
terrainGroupLayout.addWidget(terrainNormandySmall)
|
||||
terrainGroupLayout.addWidget(terrainChannelComplete)
|
||||
terrainGroupLayout.addWidget(terrainChannel)
|
||||
terrainGroup.setLayout(terrainGroupLayout)
|
||||
|
||||
timeGroupLayout = QtWidgets.QGridLayout()
|
||||
|
||||
@@ -22,14 +22,14 @@ class QBaseDefenseGroupInfo(QGroupBox):
|
||||
unit_dict[u.type] = 1
|
||||
i = 0
|
||||
for k, v in unit_dict.items():
|
||||
icon = QLabel()
|
||||
if k in VEHICLES_ICONS.keys():
|
||||
icon.setPixmap(VEHICLES_ICONS[k])
|
||||
else:
|
||||
icon.setText("<b>" + k[:6] + "</b>")
|
||||
icon.setProperty("style", "icon-plane")
|
||||
layout.addWidget(icon, i, 0)
|
||||
layout.addWidget(QLabel(str(v) + " x " + k), i, 1)
|
||||
#icon = QLabel()
|
||||
#if k in VEHICLES_ICONS.keys():
|
||||
# icon.setPixmap(VEHICLES_ICONS[k])
|
||||
#else:
|
||||
# icon.setText("<b>" + k[:6] + "</b>")
|
||||
#icon.setProperty("style", "icon-plane")
|
||||
#layout.addWidget(icon, i, 0)
|
||||
layout.addWidget(QLabel(str(v) + " x " + "<strong>" + k + "</strong>"), i, 1)
|
||||
i = i + 1
|
||||
self.setLayout(layout)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from PySide2.QtWidgets import QGridLayout, QLabel, QGroupBox, QVBoxLayout
|
||||
from PySide2.QtWidgets import QGridLayout, QLabel, QGroupBox, QVBoxLayout, QFrame
|
||||
|
||||
from game import db
|
||||
from qt_ui.uiconstants import AIRCRAFT_ICONS, VEHICLES_ICONS
|
||||
@@ -6,10 +6,10 @@ from qt_ui.windows.basemenu.base_defenses.QBaseDefenseGroupInfo import QBaseDefe
|
||||
from theater import ControlPoint, Airport
|
||||
|
||||
|
||||
class QBaseInformation(QGroupBox):
|
||||
class QBaseInformation(QFrame):
|
||||
|
||||
def __init__(self, cp:ControlPoint, airport:Airport):
|
||||
super(QBaseInformation, self).__init__("Base defenses")
|
||||
super(QBaseInformation, self).__init__()
|
||||
self.cp = cp
|
||||
self.airport = airport
|
||||
self.setMinimumWidth(500)
|
||||
|
||||
@@ -16,7 +16,7 @@ class QChooseAirbase(QGroupBox):
|
||||
self.depart_from_label = QLabel("Airbase : ")
|
||||
self.depart_from = QComboBox()
|
||||
|
||||
for i, cp in enumerate([b for b in self.game.theater.controlpoints if b.captured]):
|
||||
for i, cp in enumerate([b for b in self.game.theater.controlpoints if b.captured and b.id in self.game.planners]):
|
||||
self.depart_from.addItem(str(cp.name), cp)
|
||||
self.depart_from.setCurrentIndex(0)
|
||||
self.depart_from.currentTextChanged.connect(self._on_airbase_selected)
|
||||
|
||||
@@ -18,3 +18,9 @@ class QFlightItem(QStandardItem):
|
||||
self.setText("["+str(self.flight.flight_type.name[:6])+"] "
|
||||
+ str(self.flight.count) + " x " + db.unit_type_name(self.flight.unit_type)
|
||||
+ " in " + str(self.flight.scheduled_in) + " minutes")
|
||||
|
||||
def update(self, flight):
|
||||
self.flight = flight
|
||||
self.setText("[" + str(self.flight.flight_type.name[:6]) + "] "
|
||||
+ str(self.flight.count) + " x " + db.unit_type_name(self.flight.unit_type)
|
||||
+ " in " + str(self.flight.scheduled_in) + " minutes")
|
||||
@@ -16,7 +16,7 @@ class QMissionPlanning(QDialog):
|
||||
super(QMissionPlanning, self).__init__()
|
||||
self.game = game
|
||||
self.setWindowFlags(Qt.WindowStaysOnTopHint)
|
||||
self.setMinimumSize(800, 420)
|
||||
self.setMinimumSize(1000, 420)
|
||||
self.setModal(True)
|
||||
self.setWindowTitle("Mission Preparation")
|
||||
self.setWindowIcon(EVENT_ICONS["strike"])
|
||||
@@ -38,13 +38,15 @@ class QMissionPlanning(QDialog):
|
||||
self.planned_flight_view.set_flight_planner(self.planner)
|
||||
self.selected_cp = self.captured_cp[0]
|
||||
|
||||
self.planned_flight_view.selectionModel().setCurrentIndex(self.planned_flight_view.indexAt(QPoint(1, 1)), QItemSelectionModel.Select)
|
||||
self.planned_flight_view.selectionModel().setCurrentIndex(self.planned_flight_view.indexAt(QPoint(1, 1)), QItemSelectionModel.Rows)
|
||||
self.planned_flight_view.selectionModel().selectionChanged.connect(self.on_flight_selection_change)
|
||||
|
||||
if len(self.planned_flight_view.flight_planner.flights) > 0:
|
||||
self.flight_planner = QFlightPlanner(self.planned_flight_view.flight_planner.flights[0], self.game, self.planned_flight_view.flight_planner)
|
||||
self.flight_planner = QFlightPlanner(self.planned_flight_view.flight_planner.flights[0], self.game, self.planned_flight_view.flight_planner, 0)
|
||||
self.flight_planner.on_planned_flight_changed.connect(self.update_planned_flight_view)
|
||||
else:
|
||||
self.flight_planner = QFlightPlanner(None, self.game, self.planned_flight_view.flight_planner)
|
||||
self.flight_planner = QFlightPlanner(None, self.game, self.planned_flight_view.flight_planner, 0)
|
||||
self.flight_planner.on_planned_flight_changed.connect(self.update_planned_flight_view)
|
||||
|
||||
self.add_flight_button = QPushButton("Add Flight")
|
||||
self.add_flight_button.clicked.connect(self.on_add_flight)
|
||||
@@ -83,15 +85,27 @@ class QMissionPlanning(QDialog):
|
||||
else:
|
||||
self.planned_flight_view.set_flight_planner(None)
|
||||
|
||||
print(self.selected_cp.id)
|
||||
|
||||
def on_flight_selection_change(self):
|
||||
index = self.planned_flight_view.selectionModel().currentIndex().row()
|
||||
flight = self.planner.flights[index]
|
||||
|
||||
self.flight_planner = QFlightPlanner(flight, self.game, self.planner)
|
||||
print("On flight selection change")
|
||||
|
||||
index = self.planned_flight_view.selectionModel().currentIndex().row()
|
||||
self.planned_flight_view.repaint()
|
||||
|
||||
if self.flight_planner is not None:
|
||||
self.flight_planner.clearTabs()
|
||||
|
||||
try:
|
||||
flight = self.planner.flights[index]
|
||||
except IndexError:
|
||||
flight = None
|
||||
self.flight_planner = QFlightPlanner(flight, self.game, self.planner, self.flight_planner.currentIndex())
|
||||
self.flight_planner.on_planned_flight_changed.connect(self.update_planned_flight_view)
|
||||
self.layout.addWidget(self.flight_planner, 0, 1)
|
||||
|
||||
def update_planned_flight_view(self):
|
||||
self.planned_flight_view.update_content()
|
||||
|
||||
def on_add_flight(self):
|
||||
possible_aircraft_type = list(self.selected_cp.base.aircraft.keys())
|
||||
|
||||
@@ -110,14 +124,19 @@ class QMissionPlanning(QDialog):
|
||||
def on_delete_flight(self):
|
||||
index = self.planned_flight_view.selectionModel().currentIndex().row()
|
||||
self.planner.remove_flight(index)
|
||||
self.planned_flight_view.set_flight_planner(self.planner)
|
||||
self.planned_flight_view.set_flight_planner(self.planner, index)
|
||||
|
||||
|
||||
def on_start(self):
|
||||
|
||||
# TODO : refactor this nonsense
|
||||
self.gameEvent = None
|
||||
for event in self.game.events:
|
||||
if isinstance(event, FrontlineAttackEvent) and event.is_player_attacking:
|
||||
self.gameEvent = event
|
||||
|
||||
if self.gameEvent is None:
|
||||
self.gameEvent = FrontlineAttackEvent(self.game, self.game.theater.controlpoints[0], self.game.theater.controlpoints[0],
|
||||
self.game.theater.controlpoints[0].position, self.game.player_name, self.game.enemy_name)
|
||||
#if self.awacs_checkbox.isChecked() == 1:
|
||||
# self.gameEvent.is_awacs_enabled = True
|
||||
# self.game.awacs_expense_commit()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from PySide2.QtCore import QSize
|
||||
from PySide2.QtCore import QSize, QItemSelectionModel, QPoint
|
||||
from PySide2.QtGui import QStandardItemModel
|
||||
from PySide2.QtWidgets import QListView
|
||||
from PySide2.QtWidgets import QListView, QAbstractItemView
|
||||
|
||||
from gen.flights.ai_flight_planner import FlightPlanner
|
||||
from qt_ui.windows.mission.QFlightItem import QFlightItem
|
||||
@@ -12,19 +12,37 @@ class QPlannedFlightsView(QListView):
|
||||
super(QPlannedFlightsView, self).__init__()
|
||||
self.model = QStandardItemModel(self)
|
||||
self.setModel(self.model)
|
||||
self.flightitems = []
|
||||
self.setIconSize(QSize(91, 24))
|
||||
self.setSelectionBehavior(QAbstractItemView.SelectItems)
|
||||
if flight_planner:
|
||||
self.set_flight_planner(flight_planner)
|
||||
|
||||
def update_content(self):
|
||||
for i, f in enumerate(self.flight_planner.flights):
|
||||
self.model.appendRow(QFlightItem(f))
|
||||
self.flightitems[i].update(f)
|
||||
|
||||
def setup_content(self, row=0):
|
||||
for i, f in enumerate(self.flight_planner.flights):
|
||||
item = QFlightItem(f)
|
||||
self.model.appendRow(item)
|
||||
self.flightitems.append(item)
|
||||
self.setSelectedFlight(row)
|
||||
self.repaint()
|
||||
|
||||
def setSelectedFlight(self, row):
|
||||
self.selectionModel().clearSelection()
|
||||
index = self.model.index(row, 0)
|
||||
if not index.isValid():
|
||||
index = self.model.index(0, 0)
|
||||
self.selectionModel().setCurrentIndex(index, QItemSelectionModel.Select)
|
||||
self.repaint()
|
||||
|
||||
def clear_layout(self):
|
||||
self.model.removeRows(0, self.model.rowCount())
|
||||
|
||||
def set_flight_planner(self, flight_planner: FlightPlanner):
|
||||
def set_flight_planner(self, flight_planner: FlightPlanner, row=0):
|
||||
self.clear_layout()
|
||||
self.flight_planner = flight_planner
|
||||
if self.flight_planner:
|
||||
self.update_content()
|
||||
self.setup_content(row)
|
||||
|
||||
@@ -111,6 +111,6 @@ class QFlightCreator(QDialog):
|
||||
self.planner.flights.append(flight)
|
||||
self.planner.custom_flights.append(flight)
|
||||
if self.flight_view is not None:
|
||||
self.flight_view.set_flight_planner(self.planner)
|
||||
self.flight_view.set_flight_planner(self.planner, len(self.planner.flights)-1)
|
||||
self.close()
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from PySide2.QtCore import Signal
|
||||
from PySide2.QtWidgets import QTabWidget, QFrame, QGridLayout, QLabel
|
||||
|
||||
from gen.flights.flight import Flight
|
||||
@@ -9,18 +10,33 @@ from qt_ui.windows.mission.flight.waypoints.QFlightWaypointTab import QFlightWay
|
||||
|
||||
class QFlightPlanner(QTabWidget):
|
||||
|
||||
def __init__(self, flight: Flight, game: Game, planner):
|
||||
on_planned_flight_changed = Signal()
|
||||
|
||||
def __init__(self, flight: Flight, game: Game, planner, selected_tab):
|
||||
super(QFlightPlanner, self).__init__()
|
||||
|
||||
print(selected_tab)
|
||||
|
||||
self.tabCount = 0
|
||||
if flight:
|
||||
self.general_settings_tab = QGeneralFlightSettingsTab(flight, game, planner)
|
||||
self.general_settings_tab.on_flight_settings_changed.connect(lambda: self.on_planned_flight_changed.emit())
|
||||
self.payload_tab = QFlightPayloadTab(flight, game)
|
||||
self.waypoint_tab = QFlightWaypointTab(game, flight)
|
||||
self.waypoint_tab.on_flight_changed.connect(lambda: self.on_planned_flight_changed.emit())
|
||||
self.addTab(self.general_settings_tab, "General Flight settings")
|
||||
self.addTab(self.payload_tab, "Payload")
|
||||
self.addTab(self.waypoint_tab, "Waypoints")
|
||||
self.tabCount = 3
|
||||
self.setCurrentIndex(selected_tab)
|
||||
else:
|
||||
tabError = QFrame()
|
||||
l = QGridLayout()
|
||||
l.addWidget(QLabel("No flight selected"))
|
||||
tabError.setLayout(l)
|
||||
self.addTab(tabError, "No flight")
|
||||
self.addTab(tabError, "No flight")
|
||||
self.tabCount = 1
|
||||
|
||||
def clearTabs(self):
|
||||
for i in range(self.tabCount):
|
||||
self.removeTab(i)
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user