Compare commits

..

3 Commits

Author SHA1 Message Date
Dan Albert
2891649531 Fix pyinstaller spec for release.
final and buildnumber are optional files. Move them into resources to
avoid naming them explicitly.

(cherry picked from commit fae9650f56)
2020-11-14 13:13:18 -08:00
Dan Albert
4b40739918 Fix versioning for release builds.
(cherry picked from commit 9019cbfd2b)
2020-11-14 13:13:18 -08:00
C. Perreau
e26e7f53c5 Merge pull request #367 from Khopa/develop_2_2_x
Release 2.2.0
2020-11-14 21:46:59 +01:00
45 changed files with 308 additions and 1251 deletions

View File

@@ -31,10 +31,6 @@ First, a big thanks to shdwp, for starting the original DCS Liberation project.
Then, DCS Liberation uses [pydcs](http://github.com/pydcs/dcs) for mission generation, and nothing would be possible without this. Then, DCS Liberation uses [pydcs](http://github.com/pydcs/dcs) for mission generation, and nothing would be possible without this.
It also uses the popular [Mist](https://github.com/mrSkortch/MissionScriptingTools) lua framework for mission scripting. It also uses the popular [Mist](https://github.com/mrSkortch/MissionScriptingTools) lua framework for mission scripting.
And for the JTAC feature, DCS Liberation embed Ciribob's JTAC Autolase [script](https://github.com/ciribob/DCS-JTACAutoLaze).
Excellent lua scripts DCS Liberation uses as plugins:
* For the JTAC feature, DCS Liberation embeds Ciribob's JTAC Autolase [script](https://github.com/ciribob/DCS-JTACAutoLaze).
* Walder's [Skynet-IADS](https://github.com/walder/Skynet-IADS) is used for Integrated Air Defense System.
Please also show some support to these projects ! Please also show some support to these projects !

View File

@@ -1,21 +1,3 @@
# 2.2.1
# Features/Improvements
* **[Factions]** Added factions : Georgia 2008, USN 1985, France 2005 Frenchpack by HerrTom
* **[Factions]** Added map Persian Gulf full by Plob
* **[Flight Planner]** Player flights with start delays under ten minutes will spawn immediately.
* **[UI]** Mission start screen now informs players about delayed flights.
* **[Units]** Added support for F-14A-135-GR
* **[Modding]** Possible to setup liveries overrides in factions definition files
## Fixes :
* **[Flight Planner]** Hold, join, and split points are planned cautiously near enemy airfields. Ascend/descend points are no longer planned.
* **[Flight Planner]** Custom waypoints are usable again. Not that in most cases custom flight plans will revert to the 2.1 flight planning behavior.
* **[Flight Planner]** Fixed UI bug that made it possible to create empty flights which would throw an error.
* **[Flight Planner]** Player flights from carriers will now be delayed correctly according to the player's settings.
* **[Misc]** Spitfire variant with clipped wings was not seen as flyable by DCS Liberation (hence could not be setup as client/player slot)
* **[Misc]** Updated Syria terrain parking slots database, the out-of-date database could end up generating aircraft in wrong slots (We are still experiencing issues with somes airbases, such as Khalkhalah though)
# 2.2.0 # 2.2.0
## Features/Improvements : ## Features/Improvements :

View File

@@ -16,8 +16,6 @@ class Doctrine:
sead_max_range: int sead_max_range: int
rendezvous_altitude: int rendezvous_altitude: int
hold_distance: int
push_distance: int
join_distance: int join_distance: int
split_distance: int split_distance: int
ingress_egress_distance: int ingress_egress_distance: int
@@ -46,8 +44,6 @@ MODERN_DOCTRINE = Doctrine(
strike_max_range=1500000, strike_max_range=1500000,
sead_max_range=1500000, sead_max_range=1500000,
rendezvous_altitude=feet_to_meter(25000), rendezvous_altitude=feet_to_meter(25000),
hold_distance=nm_to_meter(15),
push_distance=nm_to_meter(20),
join_distance=nm_to_meter(20), join_distance=nm_to_meter(20),
split_distance=nm_to_meter(20), split_distance=nm_to_meter(20),
ingress_egress_distance=nm_to_meter(45), ingress_egress_distance=nm_to_meter(45),
@@ -73,8 +69,6 @@ COLDWAR_DOCTRINE = Doctrine(
strike_max_range=1500000, strike_max_range=1500000,
sead_max_range=1500000, sead_max_range=1500000,
rendezvous_altitude=feet_to_meter(22000), rendezvous_altitude=feet_to_meter(22000),
hold_distance=nm_to_meter(10),
push_distance=nm_to_meter(10),
join_distance=nm_to_meter(10), join_distance=nm_to_meter(10),
split_distance=nm_to_meter(10), split_distance=nm_to_meter(10),
ingress_egress_distance=nm_to_meter(30), ingress_egress_distance=nm_to_meter(30),
@@ -99,8 +93,6 @@ WWII_DOCTRINE = Doctrine(
antiship=True, antiship=True,
strike_max_range=1500000, strike_max_range=1500000,
sead_max_range=1500000, sead_max_range=1500000,
hold_distance=nm_to_meter(5),
push_distance=nm_to_meter(5),
join_distance=nm_to_meter(5), join_distance=nm_to_meter(5),
split_distance=nm_to_meter(5), split_distance=nm_to_meter(5),
rendezvous_altitude=feet_to_meter(10000), rendezvous_altitude=feet_to_meter(10000),

View File

@@ -2,7 +2,9 @@ from datetime import datetime
from enum import Enum from enum import Enum
from typing import Dict, List, Optional, Tuple, Type, Union from typing import Dict, List, Optional, Tuple, Type, Union
from dcs import Mission
from dcs.countries import country_dict from dcs.countries import country_dict
from dcs.country import Country
from dcs.helicopters import ( from dcs.helicopters import (
AH_1W, AH_1W,
AH_64A, AH_64A,
@@ -44,7 +46,6 @@ from dcs.planes import (
FW_190A8, FW_190A8,
FW_190D9, FW_190D9,
F_117A, F_117A,
F_14A_135_GR,
F_14B, F_14B,
F_15C, F_15C,
F_15E, F_15E,
@@ -104,7 +105,7 @@ from dcs.planes import (
Tu_95MS, Tu_95MS,
WingLoong_I, WingLoong_I,
Yak_40, Yak_40,
plane_map plane_map,
) )
from dcs.ships import ( from dcs.ships import (
Armed_speedboat, Armed_speedboat,
@@ -154,6 +155,7 @@ from dcs.vehicles import (
) )
import pydcs_extensions.frenchpack.frenchpack as frenchpack import pydcs_extensions.frenchpack.frenchpack as frenchpack
from game.factions.faction import Faction
# PATCH pydcs data with MODS # PATCH pydcs data with MODS
from game.factions.faction_loader import FactionLoader from game.factions.faction_loader import FactionLoader
from pydcs_extensions.a4ec.a4ec import A_4E_C from pydcs_extensions.a4ec.a4ec import A_4E_C
@@ -202,6 +204,7 @@ vehicle_map["Toyota_vert"] = frenchpack.DIM__TOYOTA_GREEN
vehicle_map["Toyota_desert"] = frenchpack.DIM__TOYOTA_DESERT vehicle_map["Toyota_desert"] = frenchpack.DIM__TOYOTA_DESERT
vehicle_map["Kamikaze"] = frenchpack.DIM__KAMIKAZE vehicle_map["Kamikaze"] = frenchpack.DIM__KAMIKAZE
""" """
---------- BEGINNING OF CONFIGURATION SECTION ---------- BEGINNING OF CONFIGURATION SECTION
""" """
@@ -270,7 +273,6 @@ PRICES = {
F_15E: 24, F_15E: 24,
F_16C_50: 20, F_16C_50: 20,
F_16A: 14, F_16A: 14,
F_14A_135_GR: 20,
F_14B: 24, F_14B: 24,
Tornado_IDS: 20, Tornado_IDS: 20,
Tornado_GR4: 20, Tornado_GR4: 20,
@@ -399,20 +401,20 @@ PRICES = {
Unarmed.Transport_M818: 3, Unarmed.Transport_M818: 3,
# WW2 # WW2
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G: 24, Armor.MT_Pz_Kpfw_V_Panther_Ausf_G:24,
Armor.MT_Pz_Kpfw_IV_Ausf_H: 16, Armor.MT_Pz_Kpfw_IV_Ausf_H:16,
Armor.HT_Pz_Kpfw_VI_Tiger_I: 24, Armor.HT_Pz_Kpfw_VI_Tiger_I:24,
Armor.HT_Pz_Kpfw_VI_Ausf__B_Tiger_II: 26, Armor.HT_Pz_Kpfw_VI_Ausf__B_Tiger_II:26,
Armor.TD_Jagdpanther_G1: 18, Armor.TD_Jagdpanther_G1: 18,
Armor.TD_Jagdpanzer_IV: 11, Armor.TD_Jagdpanzer_IV: 11,
Armor.Sd_Kfz_184_Elefant: 18, Armor.Sd_Kfz_184_Elefant: 18,
Armor.APC_Sd_Kfz_251: 4, Armor.APC_Sd_Kfz_251:4,
Armor.AC_Sd_Kfz_234_2_Puma: 8, Armor.AC_Sd_Kfz_234_2_Puma:8,
Armor.MT_M4_Sherman: 12, Armor.MT_M4_Sherman:12,
Armor.MT_M4A4_Sherman_Firefly: 16, Armor.MT_M4A4_Sherman_Firefly:16,
Armor.CT_Cromwell_IV: 12, Armor.CT_Cromwell_IV:12,
Armor.M30_Cargo_Carrier: 2, Armor.M30_Cargo_Carrier:2,
Armor.APC_M2A1: 4, Armor.APC_M2A1:4,
Armor.CT_Centaur_IV: 10, Armor.CT_Centaur_IV: 10,
Armor.HIT_Churchill_VII: 16, Armor.HIT_Churchill_VII: 16,
Armor.LAC_M8_Greyhound: 8, Armor.LAC_M8_Greyhound: 8,
@@ -575,7 +577,6 @@ UNIT_BY_TASK = {
MiG_31, MiG_31,
FA_18C_hornet, FA_18C_hornet,
F_15C, F_15C,
F_14A_135_GR,
F_14B, F_14B,
F_16A, F_16A,
F_16C_50, F_16C_50,
@@ -901,6 +902,7 @@ SAM_CONVERT = {
} }
} }
""" """
Units that will always be spawned in the air Units that will always be spawned in the air
""" """
@@ -911,7 +913,7 @@ TAKEOFF_BAN: List[Type[FlyingType]] = [
Units that will be always spawned in the air if launched from the carrier Units that will be always spawned in the air if launched from the carrier
""" """
CARRIER_TAKEOFF_BAN: List[Type[FlyingType]] = [ CARRIER_TAKEOFF_BAN: List[Type[FlyingType]] = [
Su_33, # Kuznecow is bugged in a way that only 2 aircraft could be spawned Su_33, # Kuznecow is bugged in a way that only 2 aircraft could be spawned
] ]
""" """
@@ -922,7 +924,6 @@ FACTIONS = FactionLoader()
CARRIER_TYPE_BY_PLANE = { CARRIER_TYPE_BY_PLANE = {
FA_18C_hornet: CVN_74_John_C__Stennis, FA_18C_hornet: CVN_74_John_C__Stennis,
F_14A_135_GR: CVN_74_John_C__Stennis,
F_14B: CVN_74_John_C__Stennis, F_14B: CVN_74_John_C__Stennis,
Ka_50: LHA_1_Tarawa, Ka_50: LHA_1_Tarawa,
SA342M: LHA_1_Tarawa, SA342M: LHA_1_Tarawa,
@@ -996,7 +997,6 @@ PLANE_PAYLOAD_OVERRIDES: Dict[Type[PlaneType], Dict[Type[Task], str]] = {
AV8BNA: COMMON_OVERRIDE, AV8BNA: COMMON_OVERRIDE,
C_101CC: COMMON_OVERRIDE, C_101CC: COMMON_OVERRIDE,
F_5E_3: COMMON_OVERRIDE, F_5E_3: COMMON_OVERRIDE,
F_14A_135_GR: COMMON_OVERRIDE,
F_14B: COMMON_OVERRIDE, F_14B: COMMON_OVERRIDE,
F_15C: COMMON_OVERRIDE, F_15C: COMMON_OVERRIDE,
F_16C_50: COMMON_OVERRIDE, F_16C_50: COMMON_OVERRIDE,
@@ -1006,14 +1006,14 @@ PLANE_PAYLOAD_OVERRIDES: Dict[Type[PlaneType], Dict[Type[Task], str]] = {
MiG_19P: COMMON_OVERRIDE, MiG_19P: COMMON_OVERRIDE,
MiG_21Bis: COMMON_OVERRIDE, MiG_21Bis: COMMON_OVERRIDE,
AJS37: COMMON_OVERRIDE, AJS37: COMMON_OVERRIDE,
Su_25T: COMMON_OVERRIDE, Su_25T:COMMON_OVERRIDE,
Su_25: COMMON_OVERRIDE, Su_25:COMMON_OVERRIDE,
Su_27: COMMON_OVERRIDE, Su_27:COMMON_OVERRIDE,
Su_33: COMMON_OVERRIDE, Su_33:COMMON_OVERRIDE,
MiG_29A: COMMON_OVERRIDE, MiG_29A:COMMON_OVERRIDE,
MiG_29G: COMMON_OVERRIDE, MiG_29G:COMMON_OVERRIDE,
MiG_29S: COMMON_OVERRIDE, MiG_29S:COMMON_OVERRIDE,
Su_24M: COMMON_OVERRIDE, Su_24M:COMMON_OVERRIDE,
Su_30: COMMON_OVERRIDE, Su_30: COMMON_OVERRIDE,
Su_34: COMMON_OVERRIDE, Su_34: COMMON_OVERRIDE,
Su_57: COMMON_OVERRIDE, Su_57: COMMON_OVERRIDE,
@@ -1022,21 +1022,21 @@ PLANE_PAYLOAD_OVERRIDES: Dict[Type[PlaneType], Dict[Type[Task], str]] = {
Tornado_GR4: COMMON_OVERRIDE, Tornado_GR4: COMMON_OVERRIDE,
Tornado_IDS: COMMON_OVERRIDE, Tornado_IDS: COMMON_OVERRIDE,
Mirage_2000_5: COMMON_OVERRIDE, Mirage_2000_5: COMMON_OVERRIDE,
MiG_31: COMMON_OVERRIDE, MiG_31:COMMON_OVERRIDE,
SA342M: COMMON_OVERRIDE, SA342M:COMMON_OVERRIDE,
SA342L: COMMON_OVERRIDE, SA342L:COMMON_OVERRIDE,
SA342Mistral: COMMON_OVERRIDE, SA342Mistral:COMMON_OVERRIDE,
Mi_8MT: COMMON_OVERRIDE, Mi_8MT:COMMON_OVERRIDE,
Mi_24V: COMMON_OVERRIDE, Mi_24V:COMMON_OVERRIDE,
Mi_28N: COMMON_OVERRIDE, Mi_28N:COMMON_OVERRIDE,
Ka_50: COMMON_OVERRIDE, Ka_50:COMMON_OVERRIDE,
L_39ZA: COMMON_OVERRIDE, L_39ZA:COMMON_OVERRIDE,
L_39C: COMMON_OVERRIDE, L_39C:COMMON_OVERRIDE,
Su_17M4: COMMON_OVERRIDE, Su_17M4: COMMON_OVERRIDE,
F_4E: COMMON_OVERRIDE, F_4E: COMMON_OVERRIDE,
P_47D_30: COMMON_OVERRIDE, P_47D_30:COMMON_OVERRIDE,
P_47D_30bl1: COMMON_OVERRIDE, P_47D_30bl1:COMMON_OVERRIDE,
P_47D_40: COMMON_OVERRIDE, P_47D_40:COMMON_OVERRIDE,
B_17G: COMMON_OVERRIDE, B_17G: COMMON_OVERRIDE,
P_51D: COMMON_OVERRIDE, P_51D: COMMON_OVERRIDE,
P_51D_30_NA: COMMON_OVERRIDE, P_51D_30_NA: COMMON_OVERRIDE,
@@ -1129,7 +1129,6 @@ PLAYER_BUDGET_BASE = 20
CARRIER_CAPABLE = [ CARRIER_CAPABLE = [
FA_18C_hornet, FA_18C_hornet,
F_14A_135_GR,
F_14B, F_14B,
AV8BNA, AV8BNA,
Su_33, Su_33,
@@ -1165,6 +1164,7 @@ LHA_CAPABLE = [
SA342Mistral SA342Mistral
] ]
""" """
---------- END OF CONFIGURATION SECTION ---------- END OF CONFIGURATION SECTION
""" """
@@ -1216,20 +1216,16 @@ def find_unittype(for_task: Task, country_name: str) -> List[UnitType]:
def find_infantry(country_name: str) -> List[UnitType]: def find_infantry(country_name: str) -> List[UnitType]:
inf = [ inf = [
Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS,
Infantry.Paratrooper_AKS,
Infantry.Soldier_RPG, Infantry.Soldier_RPG,
Infantry.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4,
Infantry.Soldier_M249, Infantry.Soldier_M249,
Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK,
Infantry.Paratrooper_RPG_16, Infantry.Paratrooper_RPG_16,
Infantry.Georgian_soldier_with_M4, Infantry.Georgian_soldier_with_M4, Infantry.Georgian_soldier_with_M4, Infantry.Georgian_soldier_with_M4, Infantry.Georgian_soldier_with_M4, Infantry.Georgian_soldier_with_M4, Infantry.Georgian_soldier_with_M4,
Infantry.Georgian_soldier_with_M4, Infantry.Infantry_Soldier_Rus, Infantry.Infantry_Soldier_Rus, Infantry.Infantry_Soldier_Rus, Infantry.Infantry_Soldier_Rus,
Infantry.Infantry_Soldier_Rus, Infantry.Infantry_Soldier_Rus, Infantry.Infantry_Soldier_Rus,
Infantry.Infantry_Soldier_Rus,
Infantry.Infantry_SMLE_No_4_Mk_1, Infantry.Infantry_SMLE_No_4_Mk_1, Infantry.Infantry_SMLE_No_4_Mk_1, Infantry.Infantry_SMLE_No_4_Mk_1, Infantry.Infantry_SMLE_No_4_Mk_1, Infantry.Infantry_SMLE_No_4_Mk_1,
Infantry.Infantry_Mauser_98, Infantry.Infantry_Mauser_98, Infantry.Infantry_Mauser_98, Infantry.Infantry_Mauser_98, Infantry.Infantry_Mauser_98, Infantry.Infantry_Mauser_98, Infantry.Infantry_Mauser_98,
Infantry.Infantry_Mauser_98,
Infantry.Infantry_M1_Garand, Infantry.Infantry_M1_Garand, Infantry.Infantry_M1_Garand, Infantry.Infantry_M1_Garand, Infantry.Infantry_M1_Garand, Infantry.Infantry_M1_Garand,
Infantry.Infantry_Soldier_Insurgents, Infantry.Infantry_Soldier_Insurgents, Infantry.Infantry_Soldier_Insurgents Infantry.Infantry_Soldier_Insurgents, Infantry.Infantry_Soldier_Insurgents, Infantry.Infantry_Soldier_Insurgents
] ]
@@ -1360,7 +1356,7 @@ def unitdict_from(fd: AssignedUnitsDict) -> Dict:
def country_id_from_name(name): def country_id_from_name(name):
for k, v in country_dict.items(): for k,v in country_dict.items():
if v.name == name: if v.name == name:
return k return k
return -1 return -1
@@ -1378,10 +1374,8 @@ def _validate_db():
for unit_type in total_set: for unit_type in total_set:
assert unit_type in PRICES, "{} not in prices".format(unit_type) assert unit_type in PRICES, "{} not in prices".format(unit_type)
_validate_db() _validate_db()
class DefaultLiveries: class DefaultLiveries:
class Default(Enum): class Default(Enum):
af_standard = "" af_standard = ""
@@ -1391,4 +1385,4 @@ OH_58D.Liveries = DefaultLiveries
F_16C_50.Liveries = DefaultLiveries F_16C_50.Liveries = DefaultLiveries
P_51D_30_NA.Liveries = DefaultLiveries P_51D_30_NA.Liveries = DefaultLiveries
Ju_88A4.Liveries = DefaultLiveries Ju_88A4.Liveries = DefaultLiveries
B_17G.Liveries = DefaultLiveries B_17G.Liveries = DefaultLiveries

View File

@@ -105,9 +105,6 @@ class Faction:
# List of available buildings for this faction # List of available buildings for this faction
building_set: List[str] = field(default_factory=list) building_set: List[str] = field(default_factory=list)
# List of default livery overrides
liveries_overrides: Dict[UnitType, List[str]] = field(default_factory=dict)
@classmethod @classmethod
def from_json(cls: Type[Faction], json: Dict[str, Any]) -> Faction: def from_json(cls: Type[Faction], json: Dict[str, Any]) -> Faction:
@@ -186,14 +183,6 @@ class Faction:
else: else:
faction.building_set = DEFAULT_AVAILABLE_BUILDINGS faction.building_set = DEFAULT_AVAILABLE_BUILDINGS
# Load liveries override
faction.liveries_overrides = {}
liveries_overrides = json.get("liveries_overrides", {})
for k, v in liveries_overrides.items():
k = load_aircraft(k)
if k is not None:
faction.liveries_overrides[k] = [s.lower() for s in v]
return faction return faction
@property @property

View File

@@ -154,7 +154,7 @@ class Game:
reward = PLAYER_BUDGET_BASE * len(self.theater.player_points()) reward = PLAYER_BUDGET_BASE * len(self.theater.player_points())
for cp in self.theater.player_points(): for cp in self.theater.player_points():
for g in cp.ground_objects: for g in cp.ground_objects:
if g.category in REWARDS.keys() and not g.is_dead: if g.category in REWARDS.keys():
reward = reward + REWARDS[g.category] reward = reward + REWARDS[g.category]
return reward return reward
else: else:
@@ -277,7 +277,7 @@ class Game:
production = 0.0 production = 0.0
for enemy_point in self.theater.enemy_points(): for enemy_point in self.theater.enemy_points():
for g in enemy_point.ground_objects: for g in enemy_point.ground_objects:
if g.category in REWARDS.keys() and not g.is_dead: if g.category in REWARDS.keys():
production = production + REWARDS[g.category] production = production + REWARDS[g.category]
production = production * 0.75 production = production * 0.75

View File

@@ -21,7 +21,7 @@ class Settings:
self.night_disabled = False self.night_disabled = False
self.external_views_allowed = True self.external_views_allowed = True
self.supercarrier = False self.supercarrier = False
self.multiplier = 1.0 self.multiplier = 1
self.generate_marks = True self.generate_marks = True
self.sams = True # Legacy parameter do not use self.sams = True # Legacy parameter do not use
self.cold_start = False # Legacy parameter do not use self.cold_start = False # Legacy parameter do not use

View File

@@ -713,17 +713,6 @@ class AircraftConflictGenerator:
for unit_instance in group.units: for unit_instance in group.units:
unit_instance.livery_id = db.PLANE_LIVERY_OVERRIDES[unit_type] unit_instance.livery_id = db.PLANE_LIVERY_OVERRIDES[unit_type]
# Override livery by faction file data
if flight.from_cp.captured:
faction = self.game.player_faction
else:
faction = self.game.enemy_faction
if unit_type in faction.liveries_overrides:
livery = random.choice(faction.liveries_overrides[unit_type])
for unit_instance in group.units:
unit_instance.livery_id = livery
for idx in range(0, min(len(group.units), flight.client_count)): for idx in range(0, min(len(group.units), flight.client_count)):
unit = group.units[idx] unit = group.units[idx]
if self.use_client: if self.use_client:
@@ -1173,13 +1162,12 @@ class AircraftConflictGenerator:
viggen_target_points = [ viggen_target_points = [
(idx, point) for idx, point in enumerate(filtered_points) if point.waypoint_type in TARGET_WAYPOINTS (idx, point) for idx, point in enumerate(filtered_points) if point.waypoint_type in TARGET_WAYPOINTS
] ]
if viggen_target_points: keep_target = viggen_target_points[random.randint(0, len(viggen_target_points) - 1)]
keep_target = viggen_target_points[random.randint(0, len(viggen_target_points) - 1)] filtered_points = [
filtered_points = [ point for idx, point in enumerate(filtered_points) if (
point for idx, point in enumerate(filtered_points) if ( point.waypoint_type not in TARGET_WAYPOINTS or idx == keep_target[0]
point.waypoint_type not in TARGET_WAYPOINTS or idx == keep_target[0] )
) ]
]
for idx, point in enumerate(filtered_points): for idx, point in enumerate(filtered_points):
PydcsWaypointBuilder.for_waypoint( PydcsWaypointBuilder.for_waypoint(
@@ -1199,12 +1187,6 @@ class AircraftConflictGenerator:
if not flight.client_count: if not flight.client_count:
return True return True
if start_time < timedelta(minutes=10):
# Don't bother delaying client flights with short start delays. Much
# more than ten minutes starts to eat into fuel a bit more
# (espeicially for something fuel limited like a Harrier).
return False
return not self.settings.never_delay_player_flights return not self.settings.never_delay_player_flights
def set_takeoff_time(self, waypoint: FlightWaypoint, package: Package, def set_takeoff_time(self, waypoint: FlightWaypoint, package: Package,
@@ -1231,6 +1213,15 @@ class AircraftConflictGenerator:
@staticmethod @staticmethod
def should_activate_late(flight: Flight) -> bool: def should_activate_late(flight: Flight) -> bool:
if flight.client_count:
# Never delay players. Note that cold start player flights with
# AI members will still be marked as uncontrolled until the start
# trigger fires to postpone engine start.
#
# Player flights that start on the runway or in the air will start
# immediately, and AI flight members will not be delayed.
return False
if flight.start_type != "Cold": if flight.start_type != "Cold":
# Avoid spawning aircraft in the air or on the runway until it's # Avoid spawning aircraft in the air or on the runway until it's
# time for their mission. Also avoid burning through gas spawning # time for their mission. Also avoid burning through gas spawning

View File

@@ -144,16 +144,6 @@ class Conflict:
position = middle_point.point_from_heading(attack_heading, strength_delta * attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE) position = middle_point.point_from_heading(attack_heading, strength_delta * attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE)
return position, _opposite_heading(attack_heading) return position, _opposite_heading(attack_heading)
@classmethod
def flight_frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> Tuple[Point, int, int]:
"""Returns the frontline vector without regard for exclusion zones, used in CAS flight plan"""
frontline = cls.frontline_position(theater, from_cp, to_cp)
center_position, heading = frontline
left_position = center_position.point_from_heading(_heading_sum(heading, -90), int(FRONTLINE_LENGTH/2))
right_position = center_position.point_from_heading(_heading_sum(heading, 90), int(FRONTLINE_LENGTH/2))
return left_position, _heading_sum(heading, 90), int(right_position.distance_to_point(left_position))
@classmethod @classmethod
def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> Tuple[Point, int, int]: def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> Tuple[Point, int, int]:

View File

@@ -27,7 +27,6 @@ from dcs.planes import (
FW_190A8, FW_190A8,
FW_190D9, FW_190D9,
F_117A, F_117A,
F_14A_135_GR,
F_14B, F_14B,
F_15C, F_15C,
F_15E, F_15E,
@@ -105,7 +104,6 @@ INTERCEPT_CAPABLE = [
Mirage_2000_5, Mirage_2000_5,
Rafale_M, Rafale_M,
F_14A_135_GR,
F_14B, F_14B,
F_15C, F_15C,
@@ -137,7 +135,6 @@ CAP_CAPABLE = [
F_86F_Sabre, F_86F_Sabre,
F_4E, F_4E,
F_5E_3, F_5E_3,
F_14A_135_GR,
F_14B, F_14B,
F_15C, F_15C,
F_15E, F_15E,
@@ -186,7 +183,6 @@ CAP_PREFERRED = [
Mirage_2000_5, Mirage_2000_5,
F_86F_Sabre, F_86F_Sabre,
F_14A_135_GR,
F_14B, F_14B,
F_15C, F_15C,
@@ -230,7 +226,6 @@ CAS_CAPABLE = [
F_86F_Sabre, F_86F_Sabre,
F_5E_3, F_5E_3,
F_14A_135_GR,
F_14B, F_14B,
F_15E, F_15E,
F_16A, F_16A,
@@ -395,7 +390,6 @@ STRIKE_CAPABLE = [
F_86F_Sabre, F_86F_Sabre,
F_5E_3, F_5E_3,
F_14A_135_GR,
F_14B, F_14B,
F_15E, F_15E,
F_16A, F_16A,

View File

@@ -7,7 +7,6 @@ generating the waypoints for the mission.
""" """
from __future__ import annotations from __future__ import annotations
import math
from datetime import timedelta from datetime import timedelta
from functools import cached_property from functools import cached_property
import logging import logging
@@ -276,14 +275,18 @@ class PatrollingFlightPlan(FlightPlan):
@dataclass(frozen=True) @dataclass(frozen=True)
class BarCapFlightPlan(PatrollingFlightPlan): class BarCapFlightPlan(PatrollingFlightPlan):
takeoff: FlightWaypoint takeoff: FlightWaypoint
ascent: FlightWaypoint
descent: FlightWaypoint
land: FlightWaypoint land: FlightWaypoint
@property @property
def waypoints(self) -> List[FlightWaypoint]: def waypoints(self) -> List[FlightWaypoint]:
return [ return [
self.takeoff, self.takeoff,
self.ascent,
self.patrol_start, self.patrol_start,
self.patrol_end, self.patrol_end,
self.descent,
self.land, self.land,
] ]
@@ -291,16 +294,20 @@ class BarCapFlightPlan(PatrollingFlightPlan):
@dataclass(frozen=True) @dataclass(frozen=True)
class CasFlightPlan(PatrollingFlightPlan): class CasFlightPlan(PatrollingFlightPlan):
takeoff: FlightWaypoint takeoff: FlightWaypoint
ascent: FlightWaypoint
target: FlightWaypoint target: FlightWaypoint
descent: FlightWaypoint
land: FlightWaypoint land: FlightWaypoint
@property @property
def waypoints(self) -> List[FlightWaypoint]: def waypoints(self) -> List[FlightWaypoint]:
return [ return [
self.takeoff, self.takeoff,
self.ascent,
self.patrol_start, self.patrol_start,
self.target, self.target,
self.patrol_end, self.patrol_end,
self.descent,
self.land, self.land,
] ]
@@ -314,14 +321,18 @@ class CasFlightPlan(PatrollingFlightPlan):
@dataclass(frozen=True) @dataclass(frozen=True)
class FrontLineCapFlightPlan(PatrollingFlightPlan): class FrontLineCapFlightPlan(PatrollingFlightPlan):
takeoff: FlightWaypoint takeoff: FlightWaypoint
ascent: FlightWaypoint
descent: FlightWaypoint
land: FlightWaypoint land: FlightWaypoint
@property @property
def waypoints(self) -> List[FlightWaypoint]: def waypoints(self) -> List[FlightWaypoint]:
return [ return [
self.takeoff, self.takeoff,
self.ascent,
self.patrol_start, self.patrol_start,
self.patrol_end, self.patrol_end,
self.descent,
self.land, self.land,
] ]
@@ -349,24 +360,28 @@ class FrontLineCapFlightPlan(PatrollingFlightPlan):
@dataclass(frozen=True) @dataclass(frozen=True)
class StrikeFlightPlan(FormationFlightPlan): class StrikeFlightPlan(FormationFlightPlan):
takeoff: FlightWaypoint takeoff: FlightWaypoint
ascent: FlightWaypoint
hold: FlightWaypoint hold: FlightWaypoint
join: FlightWaypoint join: FlightWaypoint
ingress: FlightWaypoint ingress: FlightWaypoint
targets: List[FlightWaypoint] targets: List[FlightWaypoint]
egress: FlightWaypoint egress: FlightWaypoint
split: FlightWaypoint split: FlightWaypoint
descent: FlightWaypoint
land: FlightWaypoint land: FlightWaypoint
@property @property
def waypoints(self) -> List[FlightWaypoint]: def waypoints(self) -> List[FlightWaypoint]:
return [ return [
self.takeoff, self.takeoff,
self.ascent,
self.hold, self.hold,
self.join, self.join,
self.ingress self.ingress
] + self.targets + [ ] + self.targets + [
self.egress, self.egress,
self.split, self.split,
self.descent,
self.land, self.land,
] ]
@@ -558,8 +573,8 @@ class FlightPlanBuilder:
def regenerate_package_waypoints(self) -> None: def regenerate_package_waypoints(self) -> None:
ingress_point = self._ingress_point() ingress_point = self._ingress_point()
egress_point = self._egress_point() egress_point = self._egress_point()
join_point = self._rendezvous_point(ingress_point) join_point = self._join_point(ingress_point)
split_point = self._rendezvous_point(egress_point) split_point = self._split_point(egress_point)
from gen.ato import PackageWaypoints from gen.ato import PackageWaypoints
self.package.waypoints = PackageWaypoints( self.package.waypoints = PackageWaypoints(
@@ -659,15 +674,18 @@ class FlightPlanBuilder:
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine) builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
start, end = builder.race_track(start, end, patrol_alt) start, end = builder.race_track(start, end, patrol_alt)
descent, land = builder.rtb(flight.from_cp)
return BarCapFlightPlan( return BarCapFlightPlan(
package=self.package, package=self.package,
flight=flight, flight=flight,
patrol_duration=self.doctrine.cap_duration, patrol_duration=self.doctrine.cap_duration,
takeoff=builder.takeoff(flight.from_cp), takeoff=builder.takeoff(flight.from_cp),
ascent=builder.ascent(flight.from_cp),
patrol_start=start, patrol_start=start,
patrol_end=end, patrol_end=end,
land=builder.land(flight.from_cp) descent=descent,
land=land
) )
def generate_frontline_cap(self, flight: Flight) -> FrontLineCapFlightPlan: def generate_frontline_cap(self, flight: Flight) -> FrontLineCapFlightPlan:
@@ -706,8 +724,9 @@ class FlightPlanBuilder:
# Create points # Create points
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine) builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
start, end = builder.race_track(orbit0p, orbit1p, patrol_alt)
start, end = builder.race_track(orbit0p, orbit1p, patrol_alt)
descent, land = builder.rtb(flight.from_cp)
return FrontLineCapFlightPlan( return FrontLineCapFlightPlan(
package=self.package, package=self.package,
flight=flight, flight=flight,
@@ -717,9 +736,11 @@ class FlightPlanBuilder:
# duration of the escorted mission, or until it is winchester/bingo. # duration of the escorted mission, or until it is winchester/bingo.
patrol_duration=self.doctrine.cap_duration, patrol_duration=self.doctrine.cap_duration,
takeoff=builder.takeoff(flight.from_cp), takeoff=builder.takeoff(flight.from_cp),
ascent=builder.ascent(flight.from_cp),
patrol_start=start, patrol_start=start,
patrol_end=end, patrol_end=end,
land=builder.land(flight.from_cp) descent=descent,
land=land
) )
def generate_dead(self, flight: Flight, def generate_dead(self, flight: Flight,
@@ -778,18 +799,21 @@ class FlightPlanBuilder:
ingress, target, egress = builder.escort( ingress, target, egress = builder.escort(
self.package.waypoints.ingress, self.package.target, self.package.waypoints.ingress, self.package.target,
self.package.waypoints.egress) self.package.waypoints.egress)
descent, land = builder.rtb(flight.from_cp)
return StrikeFlightPlan( return StrikeFlightPlan(
package=self.package, package=self.package,
flight=flight, flight=flight,
takeoff=builder.takeoff(flight.from_cp), takeoff=builder.takeoff(flight.from_cp),
ascent=builder.ascent(flight.from_cp),
hold=builder.hold(self._hold_point(flight)), hold=builder.hold(self._hold_point(flight)),
join=builder.join(self.package.waypoints.join), join=builder.join(self.package.waypoints.join),
ingress=ingress, ingress=ingress,
targets=[target], targets=[target],
egress=egress, egress=egress,
split=builder.split(self.package.waypoints.split), split=builder.split(self.package.waypoints.split),
land=builder.land(flight.from_cp) descent=descent,
land=land
) )
def generate_cas(self, flight: Flight) -> CasFlightPlan: def generate_cas(self, flight: Flight) -> CasFlightPlan:
@@ -803,7 +827,7 @@ class FlightPlanBuilder:
if not isinstance(location, FrontLine): if not isinstance(location, FrontLine):
raise InvalidObjectiveLocation(flight.flight_type, location) raise InvalidObjectiveLocation(flight.flight_type, location)
ingress, heading, distance = Conflict.flight_frontline_vector( ingress, heading, distance = Conflict.frontline_vector(
location.control_points[0], location.control_points[1], location.control_points[0], location.control_points[1],
self.game.theater self.game.theater
) )
@@ -811,16 +835,19 @@ class FlightPlanBuilder:
egress = ingress.point_from_heading(heading, distance) egress = ingress.point_from_heading(heading, distance)
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine) builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
descent, land = builder.rtb(flight.from_cp)
return CasFlightPlan( return CasFlightPlan(
package=self.package, package=self.package,
flight=flight, flight=flight,
patrol_duration=self.doctrine.cas_duration, patrol_duration=self.doctrine.cas_duration,
takeoff=builder.takeoff(flight.from_cp), takeoff=builder.takeoff(flight.from_cp),
ascent=builder.ascent(flight.from_cp),
patrol_start=builder.ingress_cas(ingress, location), patrol_start=builder.ingress_cas(ingress, location),
target=builder.cas(center), target=builder.cas(center),
patrol_end=builder.egress(egress, location), patrol_end=builder.egress(egress, location),
land=builder.land(flight.from_cp) descent=descent,
land=land
) )
@staticmethod @staticmethod
@@ -844,52 +871,36 @@ class FlightPlanBuilder:
return builder.strike_area(location) return builder.strike_area(location)
def _hold_point(self, flight: Flight) -> Point: def _hold_point(self, flight: Flight) -> Point:
assert self.package.waypoints is not None heading = flight.from_cp.position.heading_between_point(
origin = flight.from_cp.position self.package.target.position
target = self.package.target.position )
join = self.package.waypoints.join return flight.from_cp.position.point_from_heading(
origin_to_target = origin.distance_to_point(target) heading, nm_to_meter(15)
join_to_target = join.distance_to_point(target)
if origin_to_target < join_to_target:
# If the origin airfield is closer to the target than the join
# point, plan the hold point such that it retreats from the origin
# airfield.
return join.point_from_heading(target.heading_between_point(origin),
self.doctrine.push_distance)
heading_to_join = origin.heading_between_point(join)
hold_point = origin.point_from_heading(heading_to_join,
self.doctrine.push_distance)
if hold_point.distance_to_point(join) >= self.doctrine.push_distance:
# Hold point is between the origin airfield and the join point and
# spaced sufficiently.
return hold_point
# The hold point is between the origin airfield and the join point, but
# the distance between the hold point and the join point is too short.
# Bend the hold point out to extend the distance while maintaining the
# minimum distance from the origin airfield to keep the AI flying
# properly.
origin_to_join = origin.distance_to_point(join)
cos_theta = (
(self.doctrine.hold_distance ** 2 +
origin_to_join ** 2 -
self.doctrine.join_distance ** 2) /
(2 * self.doctrine.hold_distance * origin_to_join)
) )
try:
theta = math.acos(cos_theta)
except ValueError:
# No solution that maintains hold and join distances. Extend the
# hold point away from the target.
return origin.point_from_heading(
target.heading_between_point(origin),
self.doctrine.hold_distance)
return origin.point_from_heading(heading_to_join - theta,
self.doctrine.hold_distance)
# TODO: Make a model for the waypoint builder and use that in the UI. # TODO: Make a model for the waypoint builder and use that in the UI.
def generate_ascend_point(self, flight: Flight,
departure: ControlPoint) -> FlightWaypoint:
"""Generate ascend point.
Args:
flight: The flight to generate the descend point for.
departure: Departure airfield or carrier.
"""
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
return builder.ascent(departure)
def generate_descend_point(self, flight: Flight,
arrival: ControlPoint) -> FlightWaypoint:
"""Generate approach/descend point.
Args:
flight: The flight to generate the descend point for.
arrival: Arrival airfield or carrier.
"""
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
return builder.descent(arrival)
def generate_rtb_waypoint(self, flight: Flight, def generate_rtb_waypoint(self, flight: Flight,
arrival: ControlPoint) -> FlightWaypoint: arrival: ControlPoint) -> FlightWaypoint:
"""Generate RTB landing point. """Generate RTB landing point.
@@ -928,54 +939,31 @@ class FlightPlanBuilder:
target_waypoints.append( target_waypoints.append(
self.target_area_waypoint(flight, location, builder)) self.target_area_waypoint(flight, location, builder))
descent, land = builder.rtb(flight.from_cp)
return StrikeFlightPlan( return StrikeFlightPlan(
package=self.package, package=self.package,
flight=flight, flight=flight,
takeoff=builder.takeoff(flight.from_cp), takeoff=builder.takeoff(flight.from_cp),
ascent=builder.ascent(flight.from_cp),
hold=builder.hold(self._hold_point(flight)), hold=builder.hold(self._hold_point(flight)),
join=builder.join(self.package.waypoints.join), join=builder.join(self.package.waypoints.join),
ingress=ingress, ingress=ingress,
targets=target_waypoints, targets=target_waypoints,
egress=builder.egress(self.package.waypoints.egress, location), egress=builder.egress(self.package.waypoints.egress, location),
split=builder.split(self.package.waypoints.split), split=builder.split(self.package.waypoints.split),
land=builder.land(flight.from_cp) descent=descent,
land=land
) )
def _retreating_rendezvous_point(self, attack_transition: Point) -> Point: def _join_point(self, ingress_point: Point) -> Point:
"""Creates a rendezvous point that retreats from the origin airfield.""" heading = self._heading_to_package_airfield(ingress_point)
return attack_transition.point_from_heading( return ingress_point.point_from_heading(heading,
self.package.target.position.heading_between_point( -self.doctrine.join_distance)
self.package_airfield().position),
self.doctrine.join_distance)
def _advancing_rendezvous_point(self, attack_transition: Point) -> Point: def _split_point(self, egress_point: Point) -> Point:
"""Creates a rendezvous point that advances toward the target.""" heading = self._heading_to_package_airfield(egress_point)
heading = self._heading_to_package_airfield(attack_transition) return egress_point.point_from_heading(heading,
return attack_transition.point_from_heading(heading, -self.doctrine.split_distance)
-self.doctrine.join_distance)
def _rendezvous_should_retreat(self, attack_transition: Point) -> bool:
transition_target_distance = attack_transition.distance_to_point(
self.package.target.position
)
origin_target_distance = self._distance_to_package_airfield(
self.package.target.position
)
# If the origin point is closer to the target than the ingress point,
# the rendezvous point should be positioned in a position that retreats
# from the origin airfield.
return origin_target_distance < transition_target_distance
def _rendezvous_point(self, attack_transition: Point) -> Point:
"""Returns the position of the rendezvous point.
Args:
attack_transition: The ingress or egress point for this rendezvous.
"""
if self._rendezvous_should_retreat(attack_transition):
return self._retreating_rendezvous_point(attack_transition)
return self._advancing_rendezvous_point(attack_transition)
def _ingress_point(self) -> Point: def _ingress_point(self) -> Point:
heading = self._target_heading_to_package_airfield() heading = self._target_heading_to_package_airfield()
@@ -995,9 +983,6 @@ class FlightPlanBuilder:
def _heading_to_package_airfield(self, point: Point) -> int: def _heading_to_package_airfield(self, point: Point) -> int:
return self.package_airfield().position.heading_between_point(point) return self.package_airfield().position.heading_between_point(point)
def _distance_to_package_airfield(self, point: Point) -> int:
return self.package_airfield().position.distance_to_point(point)
def package_airfield(self) -> ControlPoint: def package_airfield(self) -> ControlPoint:
# We'll always have a package, but if this is being planned via the UI # We'll always have a package, but if this is being planned via the UI
# it could be the first flight in the package. # it could be the first flight in the package.

View File

@@ -96,11 +96,6 @@ class TotEstimator:
def mission_start_time(self, flight: Flight) -> timedelta: def mission_start_time(self, flight: Flight) -> timedelta:
takeoff_time = self.takeoff_time_for_flight(flight) takeoff_time = self.takeoff_time_for_flight(flight)
if takeoff_time is None:
# Could not determine takeoff time, probably due to a custom flight
# plan. Start immediately.
return timedelta()
startup_time = self.estimate_startup(flight) startup_time = self.estimate_startup(flight)
ground_ops_time = self.estimate_ground_ops(flight) ground_ops_time = self.estimate_ground_ops(flight)
start_time = takeoff_time - startup_time - ground_ops_time start_time = takeoff_time - startup_time - ground_ops_time
@@ -115,15 +110,13 @@ class TotEstimator:
# Round down so *barely* above zero start times are just zero. # Round down so *barely* above zero start times are just zero.
return timedelta(seconds=math.floor(start_time.total_seconds())) return timedelta(seconds=math.floor(start_time.total_seconds()))
def takeoff_time_for_flight(self, flight: Flight) -> Optional[timedelta]: def takeoff_time_for_flight(self, flight: Flight) -> timedelta:
travel_time = self.travel_time_to_rendezvous_or_target(flight) travel_time = self.travel_time_to_rendezvous_or_target(flight)
if travel_time is None: if travel_time is None:
from gen.flights.flightplan import CustomFlightPlan logging.warning("Found no join point or patrol point. Cannot "
if not isinstance(flight.flight_plan, CustomFlightPlan): f"estimate takeoff time takeoff time for {flight}")
logging.warning( # Takeoff immediately.
"Found no rendezvous or target point. Cannot estimate " return timedelta()
f"takeoff time takeoff time for {flight}.")
return None
from gen.flights.flightplan import FormationFlightPlan from gen.flights.flightplan import FormationFlightPlan
if isinstance(flight.flight_plan, FormationFlightPlan): if isinstance(flight.flight_plan, FormationFlightPlan):
@@ -133,7 +126,7 @@ class TotEstimator:
logging.warning( logging.warning(
"Could not determine the TOT of the join point. Takeoff " "Could not determine the TOT of the join point. Takeoff "
f"time for {flight} will be immediate.") f"time for {flight} will be immediate.")
return None return timedelta()
else: else:
tot = self.package.time_over_target tot = self.package.time_over_target
return tot - travel_time - self.HOLD_TIME return tot - travel_time - self.HOLD_TIME

View File

@@ -7,9 +7,11 @@ from dcs.mapping import Point
from dcs.unit import Unit from dcs.unit import Unit
from game.data.doctrine import Doctrine from game.data.doctrine import Doctrine
from game.utils import nm_to_meter
from game.weather import Conditions from game.weather import Conditions
from theater import ControlPoint, MissionTarget, TheaterGroundObject from theater import ControlPoint, MissionTarget, TheaterGroundObject
from .flight import Flight, FlightWaypoint, FlightWaypointType from .flight import Flight, FlightWaypoint, FlightWaypointType
from ..runways import RunwayAssigner
@dataclass(frozen=True) @dataclass(frozen=True)
@@ -55,6 +57,52 @@ class WaypointBuilder:
waypoint.pretty_name = "Takeoff" waypoint.pretty_name = "Takeoff"
return waypoint return waypoint
def ascent(self, departure: ControlPoint) -> FlightWaypoint:
"""Create ascent waypoint for the given departure airfield or carrier.
Args:
departure: Departure airfield or carrier.
"""
heading = RunwayAssigner(self.conditions).takeoff_heading(departure)
position = departure.position.point_from_heading(
heading, nm_to_meter(5)
)
waypoint = FlightWaypoint(
FlightWaypointType.ASCEND_POINT,
position.x,
position.y,
500 if self.is_helo else self.doctrine.pattern_altitude
)
waypoint.name = "ASCEND"
waypoint.alt_type = "RADIO"
waypoint.description = "Ascend"
waypoint.pretty_name = "Ascend"
return waypoint
def descent(self, arrival: ControlPoint) -> FlightWaypoint:
"""Create descent waypoint for the given arrival airfield or carrier.
Args:
arrival: Arrival airfield or carrier.
"""
landing_heading = RunwayAssigner(self.conditions).landing_heading(
arrival)
heading = (landing_heading + 180) % 360
position = arrival.position.point_from_heading(
heading, nm_to_meter(5)
)
waypoint = FlightWaypoint(
FlightWaypointType.DESCENT_POINT,
position.x,
position.y,
300 if self.is_helo else self.doctrine.pattern_altitude
)
waypoint.name = "DESCEND"
waypoint.alt_type = "RADIO"
waypoint.description = "Descend to pattern altitude"
waypoint.pretty_name = "Descend"
return waypoint
@staticmethod @staticmethod
def land(arrival: ControlPoint) -> FlightWaypoint: def land(arrival: ControlPoint) -> FlightWaypoint:
"""Create descent waypoint for the given arrival airfield or carrier. """Create descent waypoint for the given arrival airfield or carrier.
@@ -183,7 +231,6 @@ class WaypointBuilder:
waypoint.description = description waypoint.description = description
waypoint.pretty_name = description waypoint.pretty_name = description
waypoint.name = target.name waypoint.name = target.name
waypoint.alt_type = "RADIO"
# The target waypoints are only for the player's benefit. AI tasks for # The target waypoints are only for the player's benefit. AI tasks for
# the target are set on the ingress point so they begin their attack # the target are set on the ingress point so they begin their attack
# *before* reaching the target. # *before* reaching the target.
@@ -210,7 +257,6 @@ class WaypointBuilder:
waypoint.description = name waypoint.description = name
waypoint.pretty_name = name waypoint.pretty_name = name
waypoint.name = name waypoint.name = name
waypoint.alt_type = "RADIO"
# The target waypoints are only for the player's benefit. AI tasks for # The target waypoints are only for the player's benefit. AI tasks for
# the target are set on the ingress point so they begin their attack # the target are set on the ingress point so they begin their attack
# *before* reaching the target. # *before* reaching the target.
@@ -280,6 +326,15 @@ class WaypointBuilder:
return (self.race_track_start(start, altitude), return (self.race_track_start(start, altitude),
self.race_track_end(end, altitude)) self.race_track_end(end, altitude))
def rtb(self,
arrival: ControlPoint) -> Tuple[FlightWaypoint, FlightWaypoint]:
"""Creates descent ant landing waypoints for the given control point.
Args:
arrival: Arrival airfield or carrier.
"""
return self.descent(arrival), self.land(arrival)
def escort(self, ingress: Point, target: MissionTarget, egress: Point) -> \ def escort(self, ingress: Point, target: MissionTarget, egress: Point) -> \
Tuple[FlightWaypoint, FlightWaypoint, FlightWaypoint]: Tuple[FlightWaypoint, FlightWaypoint, FlightWaypoint]:
"""Creates the waypoints needed to escort the package. """Creates the waypoints needed to escort the package.

View File

@@ -134,7 +134,7 @@ RADIOS: List[Radio] = [
Radio("RSIU-4V", MHz(100), MHz(150), step=MHz(1)), Radio("RSIU-4V", MHz(100), MHz(150), step=MHz(1)),
# MiG-21bis # MiG-21bis
Radio("RSIU-5V", MHz(118), MHz(140), step=MHz(1)), Radio("RSIU-5V", MHz(100), MHz(150), step=MHz(1)),
# Ka-50 # Ka-50
# Note: Also capable of 100MHz-150MHz, but we can't model gaps. # Note: Also capable of 100MHz-150MHz, but we can't model gaps.

View File

@@ -1,5 +1,3 @@
from __future__ import annotations
from dcs.action import MarkToAll from dcs.action import MarkToAll
from dcs.condition import TimeAfter from dcs.condition import TimeAfter
from dcs.mission import Mission from dcs.mission import Mission
@@ -7,7 +5,7 @@ from dcs.task import Option
from dcs.translation import String from dcs.translation import String
from dcs.triggers import Event, TriggerOnce from dcs.triggers import Event, TriggerOnce
from dcs.unit import Skill from dcs.unit import Skill
from dcs.unitgroup import FlyingGroup
from .conflictgen import Conflict from .conflictgen import Conflict
PUSH_TRIGGER_SIZE = 3000 PUSH_TRIGGER_SIZE = 3000
@@ -75,9 +73,8 @@ class TriggersGenerator:
continue continue
for country in coalition.countries.values(): for country in coalition.countries.values():
flying_groups = country.plane_group + country.helicopter_group # type: FlyingGroup for plane_group in country.plane_group:
for flying_group in flying_groups: for plane_unit in plane_group.units:
for plane_unit in flying_group.units:
if plane_unit.skill != Skill.Client and plane_unit.skill != Skill.Player: if plane_unit.skill != Skill.Client and plane_unit.skill != Skill.Player:
plane_unit.skill = Skill(skill_level[0]) plane_unit.skill = Skill(skill_level[0])

2
pydcs

Submodule pydcs updated: 2883be31c2...fa9195fbcc

View File

@@ -48,14 +48,12 @@ class QTopPanel(QFrame):
self.passTurnButton.setIcon(CONST.ICONS["PassTurn"]) self.passTurnButton.setIcon(CONST.ICONS["PassTurn"])
self.passTurnButton.setProperty("style", "btn-primary") self.passTurnButton.setProperty("style", "btn-primary")
self.passTurnButton.clicked.connect(self.passTurn) self.passTurnButton.clicked.connect(self.passTurn)
if not self.game:
self.passTurnButton.setEnabled(False)
self.proceedButton = QPushButton("Take off") self.proceedButton = QPushButton("Take off")
self.proceedButton.setIcon(CONST.ICONS["Proceed"]) self.proceedButton.setIcon(CONST.ICONS["Proceed"])
self.proceedButton.setProperty("style", "start-button") self.proceedButton.setProperty("style", "start-button")
self.proceedButton.clicked.connect(self.launch_mission) self.proceedButton.clicked.connect(self.launch_mission)
if not self.game or self.game.turn == 0: if self.game and self.game.turn == 0:
self.proceedButton.setEnabled(False) self.proceedButton.setEnabled(False)
self.factionsInfos = QFactionsInfos(self.game) self.factionsInfos = QFactionsInfos(self.game)
@@ -103,8 +101,6 @@ class QTopPanel(QFrame):
self.budgetBox.setGame(game) self.budgetBox.setGame(game)
self.factionsInfos.setGame(game) self.factionsInfos.setGame(game)
self.passTurnButton.setEnabled(True)
if game and game.turn == 0: if game and game.turn == 0:
self.proceedButton.setEnabled(False) self.proceedButton.setEnabled(False)
else: else:

View File

@@ -1,10 +1,9 @@
"""Combo box for selecting a departure airfield.""" """Combo box for selecting a departure airfield."""
from typing import Iterable from typing import Iterable
from PySide2.QtCore import Signal
from PySide2.QtWidgets import QComboBox from PySide2.QtWidgets import QComboBox
from dcs.planes import PlaneType
from dcs.planes import PlaneType
from game.inventory import GlobalAircraftInventory from game.inventory import GlobalAircraftInventory
from theater.controlpoint import ControlPoint from theater.controlpoint import ControlPoint
@@ -16,8 +15,6 @@ class QOriginAirfieldSelector(QComboBox):
that have unassigned inventory of the given aircraft type. that have unassigned inventory of the given aircraft type.
""" """
availability_changed = Signal(int)
def __init__(self, global_inventory: GlobalAircraftInventory, def __init__(self, global_inventory: GlobalAircraftInventory,
origins: Iterable[ControlPoint], origins: Iterable[ControlPoint],
aircraft: PlaneType) -> None: aircraft: PlaneType) -> None:
@@ -26,7 +23,6 @@ class QOriginAirfieldSelector(QComboBox):
self.origins = list(origins) self.origins = list(origins)
self.aircraft = aircraft self.aircraft = aircraft
self.rebuild_selector() self.rebuild_selector()
self.currentIndexChanged.connect(self.index_changed)
def change_aircraft(self, aircraft: PlaneType) -> None: def change_aircraft(self, aircraft: PlaneType) -> None:
if self.aircraft == aircraft: if self.aircraft == aircraft:
@@ -51,10 +47,3 @@ class QOriginAirfieldSelector(QComboBox):
return 0 return 0
inventory = self.global_inventory.for_control_point(origin) inventory = self.global_inventory.for_control_point(origin)
return inventory.available(self.aircraft) return inventory.available(self.aircraft)
def index_changed(self, index: int) -> None:
origin = self.itemData(index)
if origin is None:
return
inventory = self.global_inventory.for_control_point(origin)
self.availability_changed.emit(inventory.available(self.aircraft))

View File

@@ -44,6 +44,7 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
i = 0 i = 0
def add_model_item(i, model, name, wpt): def add_model_item(i, model, name, wpt):
print(name)
item = QStandardItem(name) item = QStandardItem(name)
model.setItem(i, 0, item) model.setItem(i, 0, item)
self.wpts.append(wpt) self.wpts.append(wpt)
@@ -78,7 +79,7 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
0 0
) )
wpt.alt_type = "RADIO" wpt.alt_type = "RADIO"
wpt.name = ground_object.waypoint_name wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + ground_object.category + " #" + str(ground_object.object_id)
wpt.pretty_name = wpt.name wpt.pretty_name = wpt.name
wpt.obj_name = ground_object.obj_name wpt.obj_name = ground_object.obj_name
wpt.targets.append(ground_object) wpt.targets.append(ground_object)

View File

@@ -241,12 +241,11 @@ class QLiberationWindow(QMainWindow):
"<h4>Authors</h4>" + \ "<h4>Authors</h4>" + \
"<p>DCS Liberation was originally developed by <b>shdwp</b>, DCS Liberation 2.0 is a partial rewrite based on this work by <b>Khopa</b>." \ "<p>DCS Liberation was originally developed by <b>shdwp</b>, DCS Liberation 2.0 is a partial rewrite based on this work by <b>Khopa</b>." \
"<h4>Contributors</h4>" + \ "<h4>Contributors</h4>" + \
"shdwp, Khopa, ColonelPanic, Roach, Wrycu, calvinmorrow, JohanAberg, Deus, root0fall, Captain Cody, steveveepee, pedromagueija, parithon, bwRavencl, davidp57" + \ "shdwp, Khopa, ColonelPanic, Wrycu, calvinmorrow, JohanAberg, Deus, root0fall, Captain Cody, steveveepee, pedromagueija, parithon, bwRavencl, davidp57" + \
"<h4>Special Thanks :</h4>" \ "<h4>Special Thanks :</h4>" \
"<b>rp-</b> <i>for the pydcs framework</i><br/>"\ "<b>rp-</b> <i>for the pydcs framework</i><br/>"\
"<b>Grimes (mrSkortch)</b> & <b>Speed</b> <i>for the MIST framework</i><br/>"\ "<b>Grimes (mrSkortch)</b> & <b>Speed</b> <i>for the MIST framework</i><br/>"\
"<b>Ciribob </b> <i>for the JTACAutoLase.lua script</i><br/>"\ "<b>Ciribob </b> <i>for the JTACAutoLase.lua script</i><br/>"
"<b>Walder </b> <i>for the Skynet-IADS script</i><br/>"
about = QMessageBox() about = QMessageBox()
about.setWindowTitle("About DCS Liberation") about.setWindowTitle("About DCS Liberation")
about.setIcon(QMessageBox.Icon.Information) about.setIcon(QMessageBox.Icon.Information)

View File

@@ -13,9 +13,8 @@ from PySide2.QtWidgets import (
QLabel, QLabel,
QMessageBox, QMessageBox,
QPushButton, QPushButton,
QTextBrowser, QTextEdit,
) )
from jinja2 import Environment, FileSystemLoader, select_autoescape
from game.debriefing import Debriefing, wait_for_debriefing from game.debriefing import Debriefing, wait_for_debriefing
from game.game import Event, Game, logging from game.game import Event, Game, logging
@@ -66,21 +65,27 @@ class QWaitingForMissionResultWindow(QDialog):
self.layout.addWidget(header, 0, 0) self.layout.addWidget(header, 0, 0)
self.gridLayout = QGridLayout() self.gridLayout = QGridLayout()
TEXT = "" + \
"<b>You are clear for takeoff</b>" + \
"" + \
"<h2>For Singleplayer :</h2>\n" + \
"In DCS, open the Mission Editor, and load the file : \n" + \
"<i>liberation_nextturn</i>\n" + \
"<p>Then once the mission is loaded in ME, in menu \"Flight\",\n" + \
"click on FLY Mission to launch.</p>\n" + \
"" + \
"<h2>For Multiplayer :</h2>" + \
"In DCS, open the Mission Editor, and load the file : " + \
"<i>liberation_nextturn</i>" + \
"<p>Click on File/Save. Then exit the mission editor, and go to Multiplayer.</p>" + \
"<p>Then host a server with the mission, and tell your friends to join !</p>" + \
"<i>(The step in the mission editor is important, and fix a game breaking bug.)</i>" + \
"<h2>Finishing</h2>" + \
"<p>Once you have played the mission, click on the \"Accept Results\" button.</p>" + \
"<p>If DCS Liberation does not detect mission end, use the manually submit button, and choose the state.json file.</p>"
jinja = Environment( self.instructions_text = QTextEdit(TEXT)
loader=FileSystemLoader("resources/ui/templates"), self.instructions_text.setReadOnly(True)
autoescape=select_autoescape(
disabled_extensions=("",),
default_for_string=True,
default=True,
),
trim_blocks=True,
lstrip_blocks=True,
)
self.instructions_text = QTextBrowser()
self.instructions_text.setHtml(
jinja.get_template("mission_start_EN.j2").render())
self.instructions_text.setOpenExternalLinks(True)
self.gridLayout.addWidget(self.instructions_text, 1, 0) self.gridLayout.addWidget(self.instructions_text, 1, 0)
progress = QLabel("") progress = QLabel("")

View File

@@ -54,11 +54,11 @@ class QFlightCreator(QDialog):
[cp for cp in game.theater.controlpoints if cp.captured], [cp for cp in game.theater.controlpoints if cp.captured],
self.aircraft_selector.currentData() self.aircraft_selector.currentData()
) )
self.airfield_selector.availability_changed.connect(self.update_max_size) self.airfield_selector.currentIndexChanged.connect(self.update_max_size)
layout.addLayout(QLabeledWidget("Airfield:", self.airfield_selector)) layout.addLayout(QLabeledWidget("Airfield:", self.airfield_selector))
self.flight_size_spinner = QFlightSizeSpinner() self.flight_size_spinner = QFlightSizeSpinner()
self.update_max_size(self.airfield_selector.available) self.update_max_size()
layout.addLayout(QLabeledWidget("Size:", self.flight_size_spinner)) layout.addLayout(QLabeledWidget("Size:", self.flight_size_spinner))
self.client_slots_spinner = QFlightSizeSpinner( self.client_slots_spinner = QFlightSizeSpinner(
@@ -91,8 +91,6 @@ class QFlightCreator(QDialog):
return f"{origin.name} has no {aircraft.id} available." return f"{origin.name} has no {aircraft.id} available."
if size > available: if size > available:
return f"{origin.name} has only {available} {aircraft.id} available." return f"{origin.name} has only {available} {aircraft.id} available."
if size <= 0:
return f"Flight must have at least one aircraft."
return None return None
def create_flight(self) -> None: def create_flight(self) -> None:
@@ -122,8 +120,7 @@ class QFlightCreator(QDialog):
new_aircraft = self.aircraft_selector.itemData(index) new_aircraft = self.aircraft_selector.itemData(index)
self.airfield_selector.change_aircraft(new_aircraft) self.airfield_selector.change_aircraft(new_aircraft)
def update_max_size(self, available: int) -> None: def update_max_size(self) -> None:
self.flight_size_spinner.setMaximum(min(available, 4)) self.flight_size_spinner.setMaximum(
if self.flight_size_spinner.maximum() >= 2: min(self.airfield_selector.available, 4)
if self.flight_size_spinner.value() < 2: )
self.flight_size_spinner.setValue(2)

View File

@@ -1,4 +1,4 @@
from typing import Iterable, List, Optional from typing import List, Optional
from PySide2.QtCore import Signal from PySide2.QtCore import Signal
from PySide2.QtWidgets import ( from PySide2.QtWidgets import (
@@ -12,15 +12,11 @@ from PySide2.QtWidgets import (
from game import Game from game import Game
from gen.ato import Package from gen.ato import Package
from gen.flights.flight import Flight, FlightType, FlightWaypoint from gen.flights.flight import Flight, FlightType
from gen.flights.flightplan import ( from gen.flights.flightplan import FlightPlanBuilder
CustomFlightPlan,
FlightPlanBuilder,
StrikeFlightPlan,
)
from qt_ui.windows.mission.flight.waypoints.QFlightWaypointList import \ from qt_ui.windows.mission.flight.waypoints.QFlightWaypointList import \
QFlightWaypointList QFlightWaypointList
from qt_ui.windows.mission.flight.waypoints \ from qt_ui.windows.mission.flight.waypoints\
.QPredefinedWaypointSelectionWindow import \ .QPredefinedWaypointSelectionWindow import \
QPredefinedWaypointSelectionWindow QPredefinedWaypointSelectionWindow
from theater import FrontLine from theater import FrontLine
@@ -38,6 +34,8 @@ class QFlightWaypointTab(QFrame):
self.planner = FlightPlanBuilder(self.game, package, is_player=True) self.planner = FlightPlanBuilder(self.game, package, is_player=True)
self.flight_waypoint_list: Optional[QFlightWaypointList] = None self.flight_waypoint_list: Optional[QFlightWaypointList] = None
self.ascend_waypoint: Optional[QPushButton] = None
self.descend_waypoint: Optional[QPushButton] = None
self.rtb_waypoint: Optional[QPushButton] = None self.rtb_waypoint: Optional[QPushButton] = None
self.delete_selected: Optional[QPushButton] = None self.delete_selected: Optional[QPushButton] = None
self.open_fast_waypoint_button: Optional[QPushButton] = None self.open_fast_waypoint_button: Optional[QPushButton] = None
@@ -80,6 +78,14 @@ class QFlightWaypointTab(QFrame):
rlayout.addWidget(QLabel("<strong>Advanced : </strong>")) rlayout.addWidget(QLabel("<strong>Advanced : </strong>"))
rlayout.addWidget(QLabel("<small>Do not use for AI flights</small>")) rlayout.addWidget(QLabel("<small>Do not use for AI flights</small>"))
self.ascend_waypoint = QPushButton("Add Ascend Waypoint")
self.ascend_waypoint.clicked.connect(self.on_ascend_waypoint)
rlayout.addWidget(self.ascend_waypoint)
self.descend_waypoint = QPushButton("Add Descend Waypoint")
self.descend_waypoint.clicked.connect(self.on_descend_waypoint)
rlayout.addWidget(self.descend_waypoint)
self.rtb_waypoint = QPushButton("Add RTB Waypoint") self.rtb_waypoint = QPushButton("Add RTB Waypoint")
self.rtb_waypoint.clicked.connect(self.on_rtb_waypoint) self.rtb_waypoint.clicked.connect(self.on_rtb_waypoint)
rlayout.addWidget(self.rtb_waypoint) rlayout.addWidget(self.rtb_waypoint)
@@ -97,51 +103,35 @@ class QFlightWaypointTab(QFrame):
def on_delete_waypoint(self): def on_delete_waypoint(self):
wpt = self.flight_waypoint_list.selectionModel().currentIndex().row() wpt = self.flight_waypoint_list.selectionModel().currentIndex().row()
if wpt > 0: if wpt > 0:
self.delete_waypoint(self.flight.flight_plan.waypoints[wpt]) del self.flight.points[wpt-1]
self.flight_waypoint_list.update_list() self.flight_waypoint_list.update_list()
self.on_change() self.on_change()
def delete_waypoint(self, waypoint: FlightWaypoint) -> None:
# Need to degrade to a custom flight plan and remove the waypoint.
# If the waypoint is a target waypoint and is not the last target
# waypoint, we don't need to degrade.
if isinstance(self.flight.flight_plan, StrikeFlightPlan):
is_target = waypoint in self.flight.flight_plan.targets
if is_target and len(self.flight.flight_plan.targets) > 1:
self.flight.flight_plan.targets.remove(waypoint)
return
self.degrade_to_custom_flight_plan()
self.flight.flight_plan.waypoints.remove(waypoint)
def on_fast_waypoint(self): def on_fast_waypoint(self):
self.subwindow = QPredefinedWaypointSelectionWindow(self.game, self.flight, self.flight_waypoint_list) self.subwindow = QPredefinedWaypointSelectionWindow(self.game, self.flight, self.flight_waypoint_list)
self.subwindow.waypoints_added.connect(self.on_waypoints_added) self.subwindow.finished.connect(self.on_change)
self.subwindow.show() self.subwindow.show()
def on_waypoints_added(self, waypoints: Iterable[FlightWaypoint]) -> None: def on_ascend_waypoint(self):
if not waypoints: ascend = self.planner.generate_ascend_point(self.flight,
return self.flight.from_cp)
self.degrade_to_custom_flight_plan() self.flight.points.append(ascend)
self.flight.flight_plan.waypoints.extend(waypoints)
self.flight_waypoint_list.update_list() self.flight_waypoint_list.update_list()
self.on_change() self.on_change()
def on_rtb_waypoint(self): def on_rtb_waypoint(self):
rtb = self.planner.generate_rtb_waypoint(self.flight, rtb = self.planner.generate_rtb_waypoint(self.flight,
self.flight.from_cp) self.flight.from_cp)
self.degrade_to_custom_flight_plan() self.flight.points.append(rtb)
self.flight.flight_plan.waypoints.append(rtb)
self.flight_waypoint_list.update_list() self.flight_waypoint_list.update_list()
self.on_change() self.on_change()
def degrade_to_custom_flight_plan(self) -> None: def on_descend_waypoint(self):
if not isinstance(self.flight.flight_plan, CustomFlightPlan): descend = self.planner.generate_descend_point(self.flight,
self.flight.flight_plan = CustomFlightPlan( self.flight.from_cp)
package=self.flight.package, self.flight.points.append(descend)
flight=self.flight, self.flight_waypoint_list.update_list()
custom_waypoints=self.flight.flight_plan.waypoints self.on_change()
)
def confirm_recreate(self, task: FlightType) -> None: def confirm_recreate(self, task: FlightType) -> None:
result = QMessageBox.question( result = QMessageBox.question(

View File

@@ -1,20 +1,11 @@
from PySide2.QtCore import Qt, Signal from PySide2.QtCore import Qt
from PySide2.QtWidgets import ( from PySide2.QtWidgets import QDialog, QLabel, QHBoxLayout, QVBoxLayout, QPushButton, QCheckBox
QCheckBox,
QDialog,
QHBoxLayout,
QLabel,
QPushButton,
QVBoxLayout,
)
from game import Game from game import Game
from gen.flights.flight import Flight from gen.flights.flight import Flight
from qt_ui.uiconstants import EVENT_ICONS from qt_ui.uiconstants import EVENT_ICONS
from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import \ from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox
QPredefinedWaypointSelectionComboBox from qt_ui.windows.mission.flight.waypoints.QFlightWaypointInfoBox import QFlightWaypointInfoBox
from qt_ui.windows.mission.flight.waypoints.QFlightWaypointInfoBox import \
QFlightWaypointInfoBox
PREDEFINED_WAYPOINT_CATEGORIES = [ PREDEFINED_WAYPOINT_CATEGORIES = [
"Frontline (CAS AREA)", "Frontline (CAS AREA)",
@@ -26,8 +17,6 @@ PREDEFINED_WAYPOINT_CATEGORIES = [
class QPredefinedWaypointSelectionWindow(QDialog): class QPredefinedWaypointSelectionWindow(QDialog):
# List of FlightWaypoint
waypoints_added = Signal(list)
def __init__(self, game: Game, flight: Flight, flight_waypoint_list): def __init__(self, game: Game, flight: Flight, flight_waypoint_list):
super(QPredefinedWaypointSelectionWindow, self).__init__() super(QPredefinedWaypointSelectionWindow, self).__init__()
@@ -55,6 +44,7 @@ class QPredefinedWaypointSelectionWindow(QDialog):
self.init_ui() self.init_ui()
self.on_select_wpt_changed() self.on_select_wpt_changed()
print("DONE")
def init_ui(self): def init_ui(self):
@@ -87,5 +77,12 @@ class QPredefinedWaypointSelectionWindow(QDialog):
self.add_button.setDisabled(False) self.add_button.setDisabled(False)
def add_waypoint(self): def add_waypoint(self):
self.waypoints_added.emit(self.selected_waypoints)
for wpt in self.selected_waypoints:
self.flight.points.append(wpt)
self.flight_waypoint_list.update_list()
self.close() self.close()

View File

@@ -62,9 +62,7 @@ class NewGameWizard(QtWidgets.QWizard):
timePeriod = db.TIME_PERIODS[list(db.TIME_PERIODS.keys())[self.field("timePeriod")]] timePeriod = db.TIME_PERIODS[list(db.TIME_PERIODS.keys())[self.field("timePeriod")]]
midGame = self.field("midGame") midGame = self.field("midGame")
# QSlider forces integers, so we use 1 to 50 and divide by 10 to give multiplier = self.field("multiplier")
# 0.1 to 5.0.
multiplier = self.field("multiplier") / 10
no_carrier = self.field("no_carrier") no_carrier = self.field("no_carrier")
no_lha = self.field("no_lha") no_lha = self.field("no_lha")
supercarrier = self.field("supercarrier") supercarrier = self.field("supercarrier")
@@ -326,44 +324,6 @@ class BudgetInputs(QtWidgets.QGridLayout):
self.addWidget(self.starting_money, 1, 1) self.addWidget(self.starting_money, 1, 1)
class ForceMultiplierSpinner(QtWidgets.QSpinBox):
def __init__(self, minimum: Optional[int] = None,
maximum: Optional[int] = None,
initial: Optional[int] = None) -> None:
super().__init__()
if minimum is not None:
self.setMinimum(minimum)
if maximum is not None:
self.setMaximum(maximum)
if initial is not None:
self.setValue(initial)
def textFromValue(self, val: int) -> str:
return f"X {val / 10:.1f}"
class ForceMultiplierInputs(QtWidgets.QGridLayout):
def __init__(self) -> None:
super().__init__()
self.addWidget(QtWidgets.QLabel("Enemy forces multiplier"), 0, 0)
minimum = 1
maximum = 50
initial = 10
slider = QtWidgets.QSlider(Qt.Horizontal)
slider.setMinimum(minimum)
slider.setMaximum(maximum)
slider.setValue(initial)
self.multiplier = ForceMultiplierSpinner(minimum, maximum, initial)
slider.valueChanged.connect(lambda x: self.multiplier.setValue(x))
self.multiplier.valueChanged.connect(lambda x: slider.setValue(x))
self.addWidget(slider, 1, 0)
self.addWidget(self.multiplier, 1, 1)
class MiscOptions(QtWidgets.QWizardPage): class MiscOptions(QtWidgets.QWizardPage):
def __init__(self, parent=None): def __init__(self, parent=None):
super(MiscOptions, self).__init__(parent) super(MiscOptions, self).__init__(parent)
@@ -374,12 +334,14 @@ class MiscOptions(QtWidgets.QWizardPage):
QtGui.QPixmap('./resources/ui/wizard/logo1.png')) QtGui.QPixmap('./resources/ui/wizard/logo1.png'))
midGame = QtWidgets.QCheckBox() midGame = QtWidgets.QCheckBox()
multiplier_inputs = ForceMultiplierInputs() multiplier = QtWidgets.QSpinBox()
self.registerField('multiplier', multiplier_inputs.multiplier) multiplier.setEnabled(False)
multiplier.setMinimum(1)
multiplier.setMaximum(5)
miscSettingsGroup = QtWidgets.QGroupBox("Misc Settings") miscSettingsGroup = QtWidgets.QGroupBox("Misc Settings")
self.registerField('midGame', midGame) self.registerField('midGame', midGame)
self.registerField('multiplier', multiplier)
# Campaign settings # Campaign settings
generatorSettingsGroup = QtWidgets.QGroupBox("Generator Settings") generatorSettingsGroup = QtWidgets.QGroupBox("Generator Settings")
@@ -397,7 +359,8 @@ class MiscOptions(QtWidgets.QWizardPage):
layout = QtWidgets.QGridLayout() layout = QtWidgets.QGridLayout()
layout.addWidget(QtWidgets.QLabel("Start at mid game"), 1, 0) layout.addWidget(QtWidgets.QLabel("Start at mid game"), 1, 0)
layout.addWidget(midGame, 1, 1) layout.addWidget(midGame, 1, 1)
layout.addLayout(multiplier_inputs, 2, 0) layout.addWidget(QtWidgets.QLabel("Ennemy forces multiplier [Disabled for Now]"), 2, 0)
layout.addWidget(multiplier, 2, 1)
miscSettingsGroup.setLayout(layout) miscSettingsGroup.setLayout(layout)
generatorLayout = QtWidgets.QGridLayout() generatorLayout = QtWidgets.QGridLayout()

View File

@@ -16,7 +16,6 @@ We do not have a single vehicle available to hold our position. The situation i
{% if frontline.enemy_zero %} {% if frontline.enemy_zero %}
The enemy forces have been crushed, we will be able to make significant progress toward {{ frontline.enemy_base.name }} The enemy forces have been crushed, we will be able to make significant progress toward {{ frontline.enemy_base.name }}
{% endif %} {% endif %}
{% if not frontline.player_zero %}
{# Pick a random sentence to describe each frontline #} {# Pick a random sentence to describe each frontline #}
{% set fl_sent1 %}There are combats between {{ frontline.player_base.name }} and {{frontline.enemy_base.name}}. {%+ endset %} {% set fl_sent1 %}There are combats between {{ frontline.player_base.name }} and {{frontline.enemy_base.name}}. {%+ endset %}
{% set fl_sent2 %}The war on the ground is still going on between {{frontline.player_base.name}} and {{frontline.enemy_base.name}}. {%+ endset %} {% set fl_sent2 %}The war on the ground is still going on between {{frontline.player_base.name}} and {{frontline.enemy_base.name}}. {%+ endset %}
@@ -58,9 +57,8 @@ On this location, our ground forces have been ordered to hold still, and defend
{# TODO: Write a retreat sentence #} {# TODO: Write a retreat sentence #}
{% endif %} {% endif %}
{% endif %} {% endif %}
{% endif %}
{%+ endfor %}{% endif %} {% endfor %}{% endif %}
Your flights: Your flights:
==================== ====================

View File

@@ -1,137 +0,0 @@
{
"name": "Persian Gulf - Full Map",
"theater": "Persian Gulf",
"authors": "Plob",
"description": "<p>In this scenario, you start at Liwa Airfield, and must work your way north through the whole map.</p>",
"player_points": [
{
"type": "airbase",
"id": "Liwa Airbase",
"size": 1000,
"importance": 0.2
},
{
"type": "lha",
"id": 1002,
"x": -164000,
"y": -257000,
"captured_invert": true
},
{
"type": "carrier",
"id": 1001,
"x": -124000,
"y": -303000,
"captured_invert": true
}
],
"enemy_points": [
{
"type": "airbase",
"id": "Al Ain International Airport",
"size": 1000,
"importance": 1
},
{
"type": "airbase",
"id": "Al Dhafra AB",
"size": 2000,
"importance": 1
},
{
"type": "airbase",
"id": "Al Minhad AB",
"size": 1000,
"importance": 1
},
{
"type": "airbase",
"id": "Ras Al Khaimah",
"size": 1000,
"importance": 1
},
{
"type": "airbase",
"id": "Khasab",
"size": 1000,
"importance": 1
},
{
"type": "airbase",
"id": "Bandar Abbas Intl",
"size": 2000,
"importance": 1
},
{
"type": "airbase",
"id": "Jiroft Airport",
"size": 2000,
"importance": 1.4
},
{
"type": "airbase",
"id": "Kerman Airport",
"size": 2000,
"importance": 1.7,
"captured_invert": true
},
{
"type": "airbase",
"id": "Lar Airbase",
"size": 1000,
"importance": 1.4
},
{
"type": "airbase",
"id": "Shiraz International Airport",
"size": 2000,
"importance": 1
}
],
"links": [
[
"Al Dhafra AB",
"Liwa Airbase"
],
[
"Al Dhafra AB",
"Al Ain International Airport"
],
[
"Al Ain International Airport",
"Al Minhad AB"
],
[
"Al Dhafra AB",
"Al Minhad AB"
],
[
"Al Minhad AB",
"Ras Al Khaimah"
],
[
"Khasab",
"Ras Al Khaimah"
],
[
"Bandar Abbas Intl",
"Lar Airbase"
],
[
"Shiraz International Airport",
"Lar Airbase"
],
[
"Shiraz International Airport",
"Kerman Airport"
],
[
"Jiroft Airport",
"Lar Airbase"
],
[
"Jiroft Airport",
"Kerman Airport"
]
]
}

View File

@@ -5,37 +5,28 @@ local unitPayloads = {
["name"] = "CAS", ["name"] = "CAS",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{RB75}", ["CLSID"] = "{ARAKM70BHE}",
["num"] = 5,
},
[2] = {
["CLSID"] = "{RB75}",
["num"] = 3, ["num"] = 3,
}, },
[3] = { [2] = {
["CLSID"] = "{RB75}", ["CLSID"] = "{ARAKM70BHE}",
["num"] = 2, ["num"] = 2,
}, },
[4] = { [3] = {
["CLSID"] = "{RB75}",
["num"] = 6,
},
[5] = {
["CLSID"] = "{Robot24J}",
["num"] = 1,
},
[6] = {
["CLSID"] = "{Robot24J}",
["num"] = 7,
},
[7] = {
["CLSID"] = "{VIGGEN_X-TANK}", ["CLSID"] = "{VIGGEN_X-TANK}",
["num"] = 4, ["num"] = 4,
}, },
[4] = {
["CLSID"] = "{ARAKM70BHE}",
["num"] = 5,
},
[5] = {
["CLSID"] = "{ARAKM70BHE}",
["num"] = 6,
},
}, },
["tasks"] = { ["tasks"] = {
[1] = 32, [1] = 31,
[2] = 31,
}, },
}, },
[2] = { [2] = {

View File

@@ -1,343 +0,0 @@
local unitPayloads = {
["name"] = "F-14A",
["payloads"] = {
[1] = {
["name"] = "CAP",
["pylons"] = {
[1] = {
["CLSID"] = "{LAU-138 wtip - AIM-9M}",
["num"] = 10,
},
[2] = {
["CLSID"] = "{LAU-138 wtip - AIM-9M}",
["num"] = 1,
},
[3] = {
["CLSID"] = "{SHOULDER AIM_54C_Mk47 L}",
["num"] = 2,
},
[4] = {
["CLSID"] = "{SHOULDER AIM_54C_Mk47 R}",
["num"] = 9,
},
[5] = {
["CLSID"] = "{F14-300gal}",
["num"] = 8,
},
[6] = {
["CLSID"] = "{F14-300gal}",
["num"] = 3,
},
[7] = {
["CLSID"] = "{AIM_54C_Mk47}",
["num"] = 7,
},
[8] = {
["CLSID"] = "{AIM_54C_Mk47}",
["num"] = 4,
},
[9] = {
["CLSID"] = "{AIM_54C_Mk47}",
["num"] = 6,
},
[10] = {
["CLSID"] = "{AIM_54C_Mk47}",
["num"] = 5,
},
},
["tasks"] = {
[1] = 10,
},
},
[2] = {
["name"] = "CAS",
["pylons"] = {
[1] = {
["CLSID"] = "{LAU-138 wtip - AIM-9M}",
["num"] = 10,
},
[2] = {
["CLSID"] = "{LAU-138 wtip - AIM-9M}",
["num"] = 1,
},
[3] = {
["CLSID"] = "{F14-LANTIRN-TP}",
["num"] = 9,
},
[4] = {
["CLSID"] = "{PHXBRU3242_2*LAU10 LS}",
["num"] = 2,
},
[5] = {
["CLSID"] = "{F14-300gal}",
["num"] = 8,
},
[6] = {
["CLSID"] = "{F14-300gal}",
["num"] = 3,
},
[7] = {
["CLSID"] = "{BRU-32 MK-82}",
["num"] = 7,
},
[8] = {
["CLSID"] = "{BRU-32 MK-82}",
["num"] = 4,
},
[9] = {
["CLSID"] = "{BRU-32 MK-82}",
["num"] = 6,
},
[10] = {
["CLSID"] = "{BRU-32 MK-82}",
["num"] = 5,
},
},
["tasks"] = {
[1] = 10,
},
},
[3] = {
["name"] = "SEAD",
["pylons"] = {
[1] = {
["CLSID"] = "{LAU-138 wtip - AIM-9M}",
["num"] = 10,
},
[2] = {
["CLSID"] = "{LAU-138 wtip - AIM-9M}",
["num"] = 1,
},
[3] = {
["CLSID"] = "{SHOULDER AIM_54C_Mk47 R}",
["num"] = 9,
},
[4] = {
["CLSID"] = "{SHOULDER AIM_54C_Mk47 L}",
["num"] = 2,
},
[5] = {
["CLSID"] = "{F14-300gal}",
["num"] = 8,
},
[6] = {
["CLSID"] = "{F14-300gal}",
["num"] = 3,
},
[7] = {
["CLSID"] = "{BRU3242_ADM141}",
["num"] = 7,
},
[8] = {
["CLSID"] = "{BRU3242_ADM141}",
["num"] = 4,
},
[9] = {
["CLSID"] = "{BRU3242_ADM141}",
["num"] = 6,
},
[10] = {
["CLSID"] = "{BRU3242_ADM141}",
["num"] = 5,
},
},
["tasks"] = {
[1] = 10,
},
},
[4] = {
["name"] = "DEAD",
["pylons"] = {
[1] = {
["CLSID"] = "{LAU-138 wtip - AIM-9M}",
["num"] = 10,
},
[2] = {
["CLSID"] = "{LAU-138 wtip - AIM-9M}",
["num"] = 1,
},
[3] = {
["CLSID"] = "{F14-LANTIRN-TP}",
["num"] = 9,
},
[4] = {
["CLSID"] = "{PHXBRU3242_2*LAU10 LS}",
["num"] = 2,
},
[5] = {
["CLSID"] = "{F14-300gal}",
["num"] = 8,
},
[6] = {
["CLSID"] = "{F14-300gal}",
["num"] = 3,
},
[7] = {
["CLSID"] = "{BRU-32 GBU-12}",
["num"] = 7,
},
[8] = {
["CLSID"] = "{BRU-32 GBU-12}",
["num"] = 4,
},
[9] = {
["CLSID"] = "{BRU-32 GBU-12}",
["num"] = 6,
},
[10] = {
["CLSID"] = "{BRU-32 GBU-12}",
["num"] = 5,
},
},
["tasks"] = {
[1] = 10,
},
},
[5] = {
["name"] = "STRIKE",
["pylons"] = {
[1] = {
["CLSID"] = "{LAU-138 wtip - AIM-9M}",
["num"] = 10,
},
[2] = {
["CLSID"] = "{LAU-138 wtip - AIM-9M}",
["num"] = 1,
},
[3] = {
["CLSID"] = "{F14-LANTIRN-TP}",
["num"] = 9,
},
[4] = {
["CLSID"] = "{SHOULDER AIM-7MH}",
["num"] = 2,
},
[5] = {
["CLSID"] = "{F14-300gal}",
["num"] = 8,
},
[6] = {
["CLSID"] = "{F14-300gal}",
["num"] = 3,
},
[7] = {
["CLSID"] = "{BRU-32 GBU-16}",
["num"] = 7,
},
[8] = {
["CLSID"] = "{BRU-32 GBU-16}",
["num"] = 4,
},
[9] = {
["CLSID"] = "{BRU-32 GBU-16}",
["num"] = 6,
},
[10] = {
["CLSID"] = "{BRU-32 GBU-16}",
["num"] = 5,
},
},
["tasks"] = {
[1] = 10,
},
},
[6] = {
["name"] = "BAI",
["pylons"] = {
[1] = {
["CLSID"] = "{LAU-138 wtip - AIM-9M}",
["num"] = 10,
},
[2] = {
["CLSID"] = "{LAU-138 wtip - AIM-9M}",
["num"] = 1,
},
[3] = {
["CLSID"] = "{F14-LANTIRN-TP}",
["num"] = 9,
},
[4] = {
["CLSID"] = "{PHXBRU3242_2*LAU10 LS}",
["num"] = 2,
},
[5] = {
["CLSID"] = "{F14-300gal}",
["num"] = 8,
},
[6] = {
["CLSID"] = "{F14-300gal}",
["num"] = 3,
},
[7] = {
["CLSID"] = "{BRU-32 MK-82}",
["num"] = 7,
},
[8] = {
["CLSID"] = "{BRU-32 MK-82}",
["num"] = 4,
},
[9] = {
["CLSID"] = "{BRU-32 MK-20}",
["num"] = 6,
},
[10] = {
["CLSID"] = "{BRU-32 MK-20}",
["num"] = 5,
},
},
["tasks"] = {
[1] = 10,
},
},
[7] = {
["name"] = "ANTISHIP",
["pylons"] = {
[1] = {
["CLSID"] = "{F14-LANTIRN-TP}",
["num"] = 9,
},
[2] = {
["CLSID"] = "{LAU-138 wtip - AIM-9M}",
["num"] = 10,
},
[3] = {
["CLSID"] = "{LAU-138 wtip - AIM-9M}",
["num"] = 1,
},
[4] = {
["CLSID"] = "{PHXBRU3242_2*LAU10 LS}",
["num"] = 2,
},
[5] = {
["CLSID"] = "{BRU-32 GBU-16}",
["num"] = 7,
},
[6] = {
["CLSID"] = "{BRU-32 GBU-16}",
["num"] = 4,
},
[7] = {
["CLSID"] = "{BRU3242_ADM141}",
["num"] = 6,
},
[8] = {
["CLSID"] = "{BRU3242_ADM141}",
["num"] = 5,
},
[9] = {
["CLSID"] = "{F14-300gal}",
["num"] = 3,
},
[10] = {
["CLSID"] = "{F14-300gal}",
["num"] = 8,
},
},
["tasks"] = {
[1] = 10,
},
},
},
["unitType"] = "F-14A-135-GR",
}
return unitPayloads

View File

@@ -4,7 +4,6 @@
"authors": "Khopa", "authors": "Khopa",
"description": "<p>A generic bluefor coldwar faction.</p>", "description": "<p>A generic bluefor coldwar faction.</p>",
"aircrafts": [ "aircrafts": [
"F_14A_135_GR",
"F_14B", "F_14B",
"F_4E", "F_4E",
"F_5E_3", "F_5E_3",

View File

@@ -4,7 +4,6 @@
"authors": "Khopa", "authors": "Khopa",
"description": "<p>A generic bluefor coldwar faction. (With the A-4E-C mod)</p>", "description": "<p>A generic bluefor coldwar faction. (With the A-4E-C mod)</p>",
"aircrafts": [ "aircrafts": [
"F_14A_135_GR",
"F_14B", "F_14B",
"F_4E", "F_4E",
"F_5E_3", "F_5E_3",

View File

@@ -4,7 +4,6 @@
"authors": "Khopa", "authors": "Khopa",
"description": "<p>A generic bluefor coldwar faction. (With the A-4E-C and the MB-339 mods)</p>", "description": "<p>A generic bluefor coldwar faction. (With the A-4E-C and the MB-339 mods)</p>",
"aircrafts": [ "aircrafts": [
"F_14A_135_GR",
"F_14B", "F_14B",
"F_4E", "F_4E",
"F_5E_3", "F_5E_3",

View File

@@ -1,84 +0,0 @@
{
"country": "France",
"name": "France 2005 (Frenchpack)",
"authors": "HerrTom",
"description": "<p>French equipment using the Frenchpack, but without the Rafale mod.</p>",
"aircrafts": [
"M_2000C",
"Mirage_2000_5",
"SA342M",
"SA342L",
"SA342Mistral"
],
"awacs": [
"E_3A"
],
"tankers": [
"KC_135",
"KC130"
],
"frontline_units": [
"AMX_10RCR",
"AMX_10RCR_SEPAR",
"ERC_90",
"TRM_2000_PAMELA",
"VAB__50",
"VAB_MEPHISTO",
"VAB_T20_13",
"VAB_T20_13",
"VBL__50",
"VBL_AANF1",
"VBAE_CRAB",
"VBAE_CRAB_MMP",
"AMX_30B2",
"Leclerc_Serie_XXI"
],
"artillery_units": [
"MLRS_M270",
"SPH_M109_Paladin"
],
"logistics_units": [
"Transport_M818"
],
"infantry_units": [
"Infantry_M4",
"Soldier_M249",
"Stinger_MANPADS"
],
"shorads": [
"HQ7Generator",
"RolandGenerator"
],
"sams": [
"RolandGenerator",
"HawkGenerator"
],
"aircraft_carrier": [
"CVN_74_John_C__Stennis"
],
"helicopter_carrier": [
"LHA_1_Tarawa"
],
"destroyers": [
"USS_Arleigh_Burke_IIa"
],
"cruisers": [
"Ticonderoga_class"
],
"requirements": {
"frenchpack V3.5": "https://forums.eagle.ru/showthread.php?t=279974"
},
"carrier_names": [
"L9013 Mistral",
"L9014 Tonerre",
"L9015 Dixmude"
],
"helicopter_carrier_names": [
"Jeanne d'Arc"
],
"navy_generators": [
"ArleighBurkeGroupGenerator"
],
"has_jtac": true,
"jtac_unit": "MQ_9_Reaper"
}

View File

@@ -1,46 +0,0 @@
{
"country": "Georgia",
"name": "Georgia 2008",
"authors": "HerrTom",
"description": "<p>A faction that represents Georgia during the South Ossetian War. They will have a lot more aircraft than historically, and no real A2A capability.</p>",
"aircrafts": [
"L_39ZA",
"Su_25",
"Mi_8MT",
"Mi_24V",
"UH_1H"
],
"frontline_units": [
"APC_BTR_80",
"APC_MTLB",
"APC_Cobra",
"IFV_BMP_1",
"IFV_BMP_2",
"MBT_T_72B",
"MBT_T_55"
],
"artillery_units": [
"MLRS_BM21_Grad",
"SPH_2S1_Gvozdika",
"SPH_2S3_Akatsia"
],
"logistics_units": [
"Transport_Ural_375",
"Transport_UAZ_469"
],
"infantry_units": [
"Paratrooper_AKS",
"Paratrooper_RPG_16"
],
"shorads": [
"SA13Generator",
"SA8Generator"
],
"sams": [
"SA6Generator",
"SA11Generator"
],
"requirements": {},
"has_jtac": true,
"jtac_unit": "MQ_9_Reaper"
}

View File

@@ -8,7 +8,7 @@
"MiG_29A", "MiG_29A",
"F_4E", "F_4E",
"F_5E_3", "F_5E_3",
"F_14A_135_GR", "F_14B",
"Su_17M4", "Su_17M4",
"Su_24M", "Su_24M",
"Su_25", "Su_25",

View File

@@ -61,24 +61,5 @@
"OliverHazardPerryGroupGenerator" "OliverHazardPerryGroupGenerator"
], ],
"has_jtac": true, "has_jtac": true,
"jtac_unit": "MQ_9_Reaper", "jtac_unit": "MQ_9_Reaper"
"liveries_overrides": {
"FA_18C_hornet": [
"NSAWC brown splinter",
"NAWDC black",
"VFC-12"
],
"F_15C": [
"65th Aggressor SQN (WA) MiG",
"65th Aggressor SQN (WA) MiG",
"65th Aggressor SQN (WA) SUPER_Flanker"
],
"F_16C_50": [
"usaf 64th aggressor sqn - shark",
"usaf 64th aggressor sqn-splinter",
"64th_aggressor_squadron_ghost"
], "F_14B": [
"vf-74 adversary"
]
}
} }

View File

@@ -6,7 +6,7 @@
"aircrafts": [ "aircrafts": [
"F_5E_3", "F_5E_3",
"F_4E", "F_4E",
"F_14A_135_GR", "F_14B",
"B_52H", "B_52H",
"UH_1H" "UH_1H"
], ],

View File

@@ -6,7 +6,6 @@
"aircrafts": [ "aircrafts": [
"F_15C", "F_15C",
"F_15E", "F_15E",
"F_14A_135_GR",
"F_14B", "F_14B",
"FA_18C_hornet", "FA_18C_hornet",
"F_16C_50", "F_16C_50",
@@ -86,25 +85,5 @@
"ArleighBurkeGroupGenerator" "ArleighBurkeGroupGenerator"
], ],
"has_jtac": true, "has_jtac": true,
"jtac_unit": "MQ_9_Reaper", "jtac_unit": "MQ_9_Reaper"
"liveries_overrides": {
"FA_18C_hornet": [
"VFA-37",
"VFA-106",
"VFA-113",
"VFA-122",
"VFA-131",
"VFA-192",
"VFA-34",
"VFA-83",
"VFA-87",
"VFA-97",
"VMFA-122",
"VMFA-132",
"VMFA-251",
"VMFA-312",
"VMFA-314",
"VMFA-323"
]
}
} }

View File

@@ -86,25 +86,5 @@
"OliverHazardPerryGroupGenerator" "OliverHazardPerryGroupGenerator"
], ],
"has_jtac": true, "has_jtac": true,
"jtac_unit": "MQ_9_Reaper", "jtac_unit": "MQ_9_Reaper"
"liveries_overrides": {
"FA_18C_hornet": [
"VFA-37",
"VFA-106",
"VFA-113",
"VFA-122",
"VFA-131",
"VFA-192",
"VFA-34",
"VFA-83",
"VFA-87",
"VFA-97",
"VMFA-122",
"VMFA-132",
"VMFA-251",
"VMFA-312",
"VMFA-314",
"VMFA-323"
]
}
} }

View File

@@ -1,88 +0,0 @@
{
"country": "USA",
"name": "US Navy 1985",
"authors": "HerrTom",
"description": "<p>Highway to the Danger Zone! For Tomcat lovers.</p>",
"aircrafts": [
"F_4E",
"F_14A_135_GR",
"F_14B",
"S_3B",
"UH_1H",
"AH_1W"
],
"awacs": [
"E_3A"
],
"tankers": [
"S_3B_Tanker"
],
"frontline_units": [
"MBT_M60A3_Patton",
"APC_M113",
"APC_M1025_HMMWV"
],
"artillery_units": [
"SPH_M109_Paladin",
"MLRS_M270"
],
"logistics_units": [
"Transport_M818"
],
"infantry_units": [
"Infantry_M4",
"Soldier_M249"
],
"shorads": [
"VulcanGenerator",
"ChaparralGenerator"
],
"sams": [
"HawkGenerator",
"ChaparralGenerator"
],
"aircraft_carrier": [
"CVN_74_John_C__Stennis"
],
"helicopter_carrier": [
"LHA_1_Tarawa"
],
"destroyers": [
"Oliver_Hazzard_Perry_class"
],
"cruisers": [
"Ticonderoga_class"
],
"carrier_names": [
"CVN-71 Theodore Roosevelt",
"CVN-72 Abraham Lincoln",
"CVN-73 George Washington",
"CVN-74 John C. Stennis"
],
"helicopter_carrier_names": [
"LHA-1 Tarawa",
"LHA-2 Saipan",
"LHA-3 Belleau Wood",
"LHA-4 Nassau",
"LHA-5 Peleliu"
],
"navy_generators": [
"OliverHazardPerryGroupGenerator"
],
"requirements": {},
"doctrine": "coldwar",
"liveries_overrides": {
"FA_18C_hornet": [
"VFA-37",
"VFA-106",
"VFA-113",
"VFA-122",
"VFA-131",
"VFA-192",
"VFA-34",
"VFA-83",
"VFA-87",
"VFA-97"
]
}
}

View File

@@ -1,58 +0,0 @@
<b>You are clear for takeoff</b>
<p>
Some player flights may be delayed to start. For such flights, it will not be
possible to enter the cockpit for a delayed flight until its mission start
time, shown in the flight information window.
</p>
<p>
To reduce delays, schedule packages with player flights with an earlier TOT.
Note that if some flights within the package will take a long time to reach the
target, a player flight may still be delayed.
</p>
<p>
To avoid delays entirely, use the "Never delay player flights" option in the
mission generation settings. Note that this will <strong>not</strong> adjust
the timing of your mission; this option only allows you to wait in the
cockpit.
</p>
<p>
For more information, see the mission planning documentation on
<a href="https://github.com/Khopa/dcs_liberation/wiki/Mission-planning">
the wiki</a>.
</p>
<h2>For Singleplayer:</h2>
<p>
In DCS, open the Mission Editor and load the file: <i>liberation_nextturn</i>.
</p>
<p>
Once the mission is loaded in the ME, use the "FLY" option in the "Flight"
menu to launch.
</p>
<h2>For Multiplayer:</h2>
<p>
In DCS, open the Mission Editor, and load the file: <i>liberation_nextturn</i>
</p>
<p>Select File/Save, exit the mission editor, and then select Multiplayer.</p>
<p>Then host a server with the mission, and tell your friends to join!</p>
<i>(The step in the mission editor is important, and fix a game breaking bug.)</i>
<h2>Finishing</h2>
<p>Once you have played the mission, click on the \"Accept Results\" button.</p>
<p>
If DCS Liberation does not detect mission end, use the manually submit button,
and choose the state.json file.
</p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1010 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1010 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1010 B

View File

@@ -99,10 +99,6 @@ class TheaterGroundObject(MissionTarget):
"""The name of the unit group.""" """The name of the unit group."""
return f"{self.category}|{self.group_id}" return f"{self.category}|{self.group_id}"
@property
def waypoint_name(self) -> str:
return f"[{self.name}] {self.category}"
def __str__(self) -> str: def __str__(self) -> str:
return NAME_BY_CATEGORY[self.category] return NAME_BY_CATEGORY[self.category]
@@ -140,10 +136,6 @@ class BuildingGroundObject(TheaterGroundObject):
"""The name of the unit group.""" """The name of the unit group."""
return f"{self.category}|{self.group_id}|{self.object_id}" return f"{self.category}|{self.group_id}|{self.object_id}"
@property
def waypoint_name(self) -> str:
return f"{super().waypoint_name} #{self.object_id}"
class GenericCarrierGroundObject(TheaterGroundObject): class GenericCarrierGroundObject(TheaterGroundObject):
pass pass