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.
It also uses the popular [Mist](https://github.com/mrSkortch/MissionScriptingTools) lua framework for mission scripting.
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.
And for the JTAC feature, DCS Liberation embed Ciribob's JTAC Autolase [script](https://github.com/ciribob/DCS-JTACAutoLaze).
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
## Features/Improvements :

View File

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

View File

@@ -2,7 +2,9 @@ from datetime import datetime
from enum import Enum
from typing import Dict, List, Optional, Tuple, Type, Union
from dcs import Mission
from dcs.countries import country_dict
from dcs.country import Country
from dcs.helicopters import (
AH_1W,
AH_64A,
@@ -44,7 +46,6 @@ from dcs.planes import (
FW_190A8,
FW_190D9,
F_117A,
F_14A_135_GR,
F_14B,
F_15C,
F_15E,
@@ -104,7 +105,7 @@ from dcs.planes import (
Tu_95MS,
WingLoong_I,
Yak_40,
plane_map
plane_map,
)
from dcs.ships import (
Armed_speedboat,
@@ -154,6 +155,7 @@ from dcs.vehicles import (
)
import pydcs_extensions.frenchpack.frenchpack as frenchpack
from game.factions.faction import Faction
# PATCH pydcs data with MODS
from game.factions.faction_loader import FactionLoader
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["Kamikaze"] = frenchpack.DIM__KAMIKAZE
"""
---------- BEGINNING OF CONFIGURATION SECTION
"""
@@ -270,7 +273,6 @@ PRICES = {
F_15E: 24,
F_16C_50: 20,
F_16A: 14,
F_14A_135_GR: 20,
F_14B: 24,
Tornado_IDS: 20,
Tornado_GR4: 20,
@@ -399,20 +401,20 @@ PRICES = {
Unarmed.Transport_M818: 3,
# WW2
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.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.AC_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: 4,
Armor.APC_Sd_Kfz_251:4,
Armor.AC_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:4,
Armor.CT_Centaur_IV: 10,
Armor.HIT_Churchill_VII: 16,
Armor.LAC_M8_Greyhound: 8,
@@ -575,7 +577,6 @@ UNIT_BY_TASK = {
MiG_31,
FA_18C_hornet,
F_15C,
F_14A_135_GR,
F_14B,
F_16A,
F_16C_50,
@@ -901,6 +902,7 @@ SAM_CONVERT = {
}
}
"""
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
"""
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 = {
FA_18C_hornet: CVN_74_John_C__Stennis,
F_14A_135_GR: CVN_74_John_C__Stennis,
F_14B: CVN_74_John_C__Stennis,
Ka_50: LHA_1_Tarawa,
SA342M: LHA_1_Tarawa,
@@ -996,7 +997,6 @@ PLANE_PAYLOAD_OVERRIDES: Dict[Type[PlaneType], Dict[Type[Task], str]] = {
AV8BNA: COMMON_OVERRIDE,
C_101CC: COMMON_OVERRIDE,
F_5E_3: COMMON_OVERRIDE,
F_14A_135_GR: COMMON_OVERRIDE,
F_14B: COMMON_OVERRIDE,
F_15C: 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_21Bis: COMMON_OVERRIDE,
AJS37: COMMON_OVERRIDE,
Su_25T: COMMON_OVERRIDE,
Su_25: COMMON_OVERRIDE,
Su_27: COMMON_OVERRIDE,
Su_33: COMMON_OVERRIDE,
MiG_29A: COMMON_OVERRIDE,
MiG_29G: COMMON_OVERRIDE,
MiG_29S: COMMON_OVERRIDE,
Su_24M: COMMON_OVERRIDE,
Su_25T:COMMON_OVERRIDE,
Su_25:COMMON_OVERRIDE,
Su_27:COMMON_OVERRIDE,
Su_33:COMMON_OVERRIDE,
MiG_29A:COMMON_OVERRIDE,
MiG_29G:COMMON_OVERRIDE,
MiG_29S:COMMON_OVERRIDE,
Su_24M:COMMON_OVERRIDE,
Su_30: COMMON_OVERRIDE,
Su_34: 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_IDS: COMMON_OVERRIDE,
Mirage_2000_5: COMMON_OVERRIDE,
MiG_31: COMMON_OVERRIDE,
SA342M: COMMON_OVERRIDE,
SA342L: COMMON_OVERRIDE,
SA342Mistral: COMMON_OVERRIDE,
Mi_8MT: COMMON_OVERRIDE,
Mi_24V: COMMON_OVERRIDE,
Mi_28N: COMMON_OVERRIDE,
Ka_50: COMMON_OVERRIDE,
L_39ZA: COMMON_OVERRIDE,
L_39C: COMMON_OVERRIDE,
MiG_31:COMMON_OVERRIDE,
SA342M:COMMON_OVERRIDE,
SA342L:COMMON_OVERRIDE,
SA342Mistral:COMMON_OVERRIDE,
Mi_8MT:COMMON_OVERRIDE,
Mi_24V:COMMON_OVERRIDE,
Mi_28N:COMMON_OVERRIDE,
Ka_50:COMMON_OVERRIDE,
L_39ZA:COMMON_OVERRIDE,
L_39C:COMMON_OVERRIDE,
Su_17M4: COMMON_OVERRIDE,
F_4E: COMMON_OVERRIDE,
P_47D_30: COMMON_OVERRIDE,
P_47D_30bl1: COMMON_OVERRIDE,
P_47D_40: COMMON_OVERRIDE,
P_47D_30:COMMON_OVERRIDE,
P_47D_30bl1:COMMON_OVERRIDE,
P_47D_40:COMMON_OVERRIDE,
B_17G: COMMON_OVERRIDE,
P_51D: COMMON_OVERRIDE,
P_51D_30_NA: COMMON_OVERRIDE,
@@ -1129,7 +1129,6 @@ PLAYER_BUDGET_BASE = 20
CARRIER_CAPABLE = [
FA_18C_hornet,
F_14A_135_GR,
F_14B,
AV8BNA,
Su_33,
@@ -1165,6 +1164,7 @@ LHA_CAPABLE = [
SA342Mistral
]
"""
---------- 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]:
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.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4,
Infantry.Soldier_M249,
Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK,
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.Infantry_Soldier_Rus, Infantry.Infantry_Soldier_Rus, Infantry.Infantry_Soldier_Rus,
Infantry.Infantry_Soldier_Rus,
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_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_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):
for k, v in country_dict.items():
for k,v in country_dict.items():
if v.name == name:
return k
return -1
@@ -1378,10 +1374,8 @@ def _validate_db():
for unit_type in total_set:
assert unit_type in PRICES, "{} not in prices".format(unit_type)
_validate_db()
class DefaultLiveries:
class Default(Enum):
af_standard = ""
@@ -1391,4 +1385,4 @@ OH_58D.Liveries = DefaultLiveries
F_16C_50.Liveries = DefaultLiveries
P_51D_30_NA.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
building_set: List[str] = field(default_factory=list)
# List of default livery overrides
liveries_overrides: Dict[UnitType, List[str]] = field(default_factory=dict)
@classmethod
def from_json(cls: Type[Faction], json: Dict[str, Any]) -> Faction:
@@ -186,14 +183,6 @@ class Faction:
else:
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
@property

View File

@@ -154,7 +154,7 @@ class Game:
reward = PLAYER_BUDGET_BASE * len(self.theater.player_points())
for cp in self.theater.player_points():
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]
return reward
else:
@@ -277,7 +277,7 @@ class Game:
production = 0.0
for enemy_point in self.theater.enemy_points():
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 * 0.75

View File

@@ -21,7 +21,7 @@ class Settings:
self.night_disabled = False
self.external_views_allowed = True
self.supercarrier = False
self.multiplier = 1.0
self.multiplier = 1
self.generate_marks = True
self.sams = True # 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:
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)):
unit = group.units[idx]
if self.use_client:
@@ -1173,13 +1162,12 @@ class AircraftConflictGenerator:
viggen_target_points = [
(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)]
filtered_points = [
point for idx, point in enumerate(filtered_points) if (
point.waypoint_type not in TARGET_WAYPOINTS or idx == keep_target[0]
)
]
keep_target = viggen_target_points[random.randint(0, len(viggen_target_points) - 1)]
filtered_points = [
point for idx, point in enumerate(filtered_points) if (
point.waypoint_type not in TARGET_WAYPOINTS or idx == keep_target[0]
)
]
for idx, point in enumerate(filtered_points):
PydcsWaypointBuilder.for_waypoint(
@@ -1199,12 +1187,6 @@ class AircraftConflictGenerator:
if not flight.client_count:
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
def set_takeoff_time(self, waypoint: FlightWaypoint, package: Package,
@@ -1231,6 +1213,15 @@ class AircraftConflictGenerator:
@staticmethod
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":
# Avoid spawning aircraft in the air or on the runway until it's
# 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)
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
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_190D9,
F_117A,
F_14A_135_GR,
F_14B,
F_15C,
F_15E,
@@ -105,7 +104,6 @@ INTERCEPT_CAPABLE = [
Mirage_2000_5,
Rafale_M,
F_14A_135_GR,
F_14B,
F_15C,
@@ -137,7 +135,6 @@ CAP_CAPABLE = [
F_86F_Sabre,
F_4E,
F_5E_3,
F_14A_135_GR,
F_14B,
F_15C,
F_15E,
@@ -186,7 +183,6 @@ CAP_PREFERRED = [
Mirage_2000_5,
F_86F_Sabre,
F_14A_135_GR,
F_14B,
F_15C,
@@ -230,7 +226,6 @@ CAS_CAPABLE = [
F_86F_Sabre,
F_5E_3,
F_14A_135_GR,
F_14B,
F_15E,
F_16A,
@@ -395,7 +390,6 @@ STRIKE_CAPABLE = [
F_86F_Sabre,
F_5E_3,
F_14A_135_GR,
F_14B,
F_15E,
F_16A,

View File

@@ -7,7 +7,6 @@ generating the waypoints for the mission.
"""
from __future__ import annotations
import math
from datetime import timedelta
from functools import cached_property
import logging
@@ -276,14 +275,18 @@ class PatrollingFlightPlan(FlightPlan):
@dataclass(frozen=True)
class BarCapFlightPlan(PatrollingFlightPlan):
takeoff: FlightWaypoint
ascent: FlightWaypoint
descent: FlightWaypoint
land: FlightWaypoint
@property
def waypoints(self) -> List[FlightWaypoint]:
return [
self.takeoff,
self.ascent,
self.patrol_start,
self.patrol_end,
self.descent,
self.land,
]
@@ -291,16 +294,20 @@ class BarCapFlightPlan(PatrollingFlightPlan):
@dataclass(frozen=True)
class CasFlightPlan(PatrollingFlightPlan):
takeoff: FlightWaypoint
ascent: FlightWaypoint
target: FlightWaypoint
descent: FlightWaypoint
land: FlightWaypoint
@property
def waypoints(self) -> List[FlightWaypoint]:
return [
self.takeoff,
self.ascent,
self.patrol_start,
self.target,
self.patrol_end,
self.descent,
self.land,
]
@@ -314,14 +321,18 @@ class CasFlightPlan(PatrollingFlightPlan):
@dataclass(frozen=True)
class FrontLineCapFlightPlan(PatrollingFlightPlan):
takeoff: FlightWaypoint
ascent: FlightWaypoint
descent: FlightWaypoint
land: FlightWaypoint
@property
def waypoints(self) -> List[FlightWaypoint]:
return [
self.takeoff,
self.ascent,
self.patrol_start,
self.patrol_end,
self.descent,
self.land,
]
@@ -349,24 +360,28 @@ class FrontLineCapFlightPlan(PatrollingFlightPlan):
@dataclass(frozen=True)
class StrikeFlightPlan(FormationFlightPlan):
takeoff: FlightWaypoint
ascent: FlightWaypoint
hold: FlightWaypoint
join: FlightWaypoint
ingress: FlightWaypoint
targets: List[FlightWaypoint]
egress: FlightWaypoint
split: FlightWaypoint
descent: FlightWaypoint
land: FlightWaypoint
@property
def waypoints(self) -> List[FlightWaypoint]:
return [
self.takeoff,
self.ascent,
self.hold,
self.join,
self.ingress
] + self.targets + [
self.egress,
self.split,
self.descent,
self.land,
]
@@ -558,8 +573,8 @@ class FlightPlanBuilder:
def regenerate_package_waypoints(self) -> None:
ingress_point = self._ingress_point()
egress_point = self._egress_point()
join_point = self._rendezvous_point(ingress_point)
split_point = self._rendezvous_point(egress_point)
join_point = self._join_point(ingress_point)
split_point = self._split_point(egress_point)
from gen.ato import PackageWaypoints
self.package.waypoints = PackageWaypoints(
@@ -659,15 +674,18 @@ class FlightPlanBuilder:
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
start, end = builder.race_track(start, end, patrol_alt)
descent, land = builder.rtb(flight.from_cp)
return BarCapFlightPlan(
package=self.package,
flight=flight,
patrol_duration=self.doctrine.cap_duration,
takeoff=builder.takeoff(flight.from_cp),
ascent=builder.ascent(flight.from_cp),
patrol_start=start,
patrol_end=end,
land=builder.land(flight.from_cp)
descent=descent,
land=land
)
def generate_frontline_cap(self, flight: Flight) -> FrontLineCapFlightPlan:
@@ -706,8 +724,9 @@ class FlightPlanBuilder:
# Create points
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(
package=self.package,
flight=flight,
@@ -717,9 +736,11 @@ class FlightPlanBuilder:
# duration of the escorted mission, or until it is winchester/bingo.
patrol_duration=self.doctrine.cap_duration,
takeoff=builder.takeoff(flight.from_cp),
ascent=builder.ascent(flight.from_cp),
patrol_start=start,
patrol_end=end,
land=builder.land(flight.from_cp)
descent=descent,
land=land
)
def generate_dead(self, flight: Flight,
@@ -778,18 +799,21 @@ class FlightPlanBuilder:
ingress, target, egress = builder.escort(
self.package.waypoints.ingress, self.package.target,
self.package.waypoints.egress)
descent, land = builder.rtb(flight.from_cp)
return StrikeFlightPlan(
package=self.package,
flight=flight,
takeoff=builder.takeoff(flight.from_cp),
ascent=builder.ascent(flight.from_cp),
hold=builder.hold(self._hold_point(flight)),
join=builder.join(self.package.waypoints.join),
ingress=ingress,
targets=[target],
egress=egress,
split=builder.split(self.package.waypoints.split),
land=builder.land(flight.from_cp)
descent=descent,
land=land
)
def generate_cas(self, flight: Flight) -> CasFlightPlan:
@@ -803,7 +827,7 @@ class FlightPlanBuilder:
if not isinstance(location, FrontLine):
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],
self.game.theater
)
@@ -811,16 +835,19 @@ class FlightPlanBuilder:
egress = ingress.point_from_heading(heading, distance)
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
descent, land = builder.rtb(flight.from_cp)
return CasFlightPlan(
package=self.package,
flight=flight,
patrol_duration=self.doctrine.cas_duration,
takeoff=builder.takeoff(flight.from_cp),
ascent=builder.ascent(flight.from_cp),
patrol_start=builder.ingress_cas(ingress, location),
target=builder.cas(center),
patrol_end=builder.egress(egress, location),
land=builder.land(flight.from_cp)
descent=descent,
land=land
)
@staticmethod
@@ -844,52 +871,36 @@ class FlightPlanBuilder:
return builder.strike_area(location)
def _hold_point(self, flight: Flight) -> Point:
assert self.package.waypoints is not None
origin = flight.from_cp.position
target = self.package.target.position
join = self.package.waypoints.join
origin_to_target = origin.distance_to_point(target)
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)
heading = flight.from_cp.position.heading_between_point(
self.package.target.position
)
return flight.from_cp.position.point_from_heading(
heading, nm_to_meter(15)
)
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.
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,
arrival: ControlPoint) -> FlightWaypoint:
"""Generate RTB landing point.
@@ -928,54 +939,31 @@ class FlightPlanBuilder:
target_waypoints.append(
self.target_area_waypoint(flight, location, builder))
descent, land = builder.rtb(flight.from_cp)
return StrikeFlightPlan(
package=self.package,
flight=flight,
takeoff=builder.takeoff(flight.from_cp),
ascent=builder.ascent(flight.from_cp),
hold=builder.hold(self._hold_point(flight)),
join=builder.join(self.package.waypoints.join),
ingress=ingress,
targets=target_waypoints,
egress=builder.egress(self.package.waypoints.egress, location),
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:
"""Creates a rendezvous point that retreats from the origin airfield."""
return attack_transition.point_from_heading(
self.package.target.position.heading_between_point(
self.package_airfield().position),
self.doctrine.join_distance)
def _join_point(self, ingress_point: Point) -> Point:
heading = self._heading_to_package_airfield(ingress_point)
return ingress_point.point_from_heading(heading,
-self.doctrine.join_distance)
def _advancing_rendezvous_point(self, attack_transition: Point) -> Point:
"""Creates a rendezvous point that advances toward the target."""
heading = self._heading_to_package_airfield(attack_transition)
return attack_transition.point_from_heading(heading,
-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 _split_point(self, egress_point: Point) -> Point:
heading = self._heading_to_package_airfield(egress_point)
return egress_point.point_from_heading(heading,
-self.doctrine.split_distance)
def _ingress_point(self) -> Point:
heading = self._target_heading_to_package_airfield()
@@ -995,9 +983,6 @@ class FlightPlanBuilder:
def _heading_to_package_airfield(self, point: Point) -> int:
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:
# We'll always have a package, but if this is being planned via the UI
# 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:
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)
ground_ops_time = self.estimate_ground_ops(flight)
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.
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)
if travel_time is None:
from gen.flights.flightplan import CustomFlightPlan
if not isinstance(flight.flight_plan, CustomFlightPlan):
logging.warning(
"Found no rendezvous or target point. Cannot estimate "
f"takeoff time takeoff time for {flight}.")
return None
logging.warning("Found no join point or patrol point. Cannot "
f"estimate takeoff time takeoff time for {flight}")
# Takeoff immediately.
return timedelta()
from gen.flights.flightplan import FormationFlightPlan
if isinstance(flight.flight_plan, FormationFlightPlan):
@@ -133,7 +126,7 @@ class TotEstimator:
logging.warning(
"Could not determine the TOT of the join point. Takeoff "
f"time for {flight} will be immediate.")
return None
return timedelta()
else:
tot = self.package.time_over_target
return tot - travel_time - self.HOLD_TIME

View File

@@ -7,9 +7,11 @@ from dcs.mapping import Point
from dcs.unit import Unit
from game.data.doctrine import Doctrine
from game.utils import nm_to_meter
from game.weather import Conditions
from theater import ControlPoint, MissionTarget, TheaterGroundObject
from .flight import Flight, FlightWaypoint, FlightWaypointType
from ..runways import RunwayAssigner
@dataclass(frozen=True)
@@ -55,6 +57,52 @@ class WaypointBuilder:
waypoint.pretty_name = "Takeoff"
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
def land(arrival: ControlPoint) -> FlightWaypoint:
"""Create descent waypoint for the given arrival airfield or carrier.
@@ -183,7 +231,6 @@ class WaypointBuilder:
waypoint.description = description
waypoint.pretty_name = description
waypoint.name = target.name
waypoint.alt_type = "RADIO"
# 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
# *before* reaching the target.
@@ -210,7 +257,6 @@ class WaypointBuilder:
waypoint.description = name
waypoint.pretty_name = name
waypoint.name = name
waypoint.alt_type = "RADIO"
# 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
# *before* reaching the target.
@@ -280,6 +326,15 @@ class WaypointBuilder:
return (self.race_track_start(start, 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) -> \
Tuple[FlightWaypoint, FlightWaypoint, FlightWaypoint]:
"""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)),
# MiG-21bis
Radio("RSIU-5V", MHz(118), MHz(140), step=MHz(1)),
Radio("RSIU-5V", MHz(100), MHz(150), step=MHz(1)),
# Ka-50
# 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.condition import TimeAfter
from dcs.mission import Mission
@@ -7,7 +5,7 @@ from dcs.task import Option
from dcs.translation import String
from dcs.triggers import Event, TriggerOnce
from dcs.unit import Skill
from dcs.unitgroup import FlyingGroup
from .conflictgen import Conflict
PUSH_TRIGGER_SIZE = 3000
@@ -75,9 +73,8 @@ class TriggersGenerator:
continue
for country in coalition.countries.values():
flying_groups = country.plane_group + country.helicopter_group # type: FlyingGroup
for flying_group in flying_groups:
for plane_unit in flying_group.units:
for plane_group in country.plane_group:
for plane_unit in plane_group.units:
if plane_unit.skill != Skill.Client and plane_unit.skill != Skill.Player:
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.setProperty("style", "btn-primary")
self.passTurnButton.clicked.connect(self.passTurn)
if not self.game:
self.passTurnButton.setEnabled(False)
self.proceedButton = QPushButton("Take off")
self.proceedButton.setIcon(CONST.ICONS["Proceed"])
self.proceedButton.setProperty("style", "start-button")
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.factionsInfos = QFactionsInfos(self.game)
@@ -103,8 +101,6 @@ class QTopPanel(QFrame):
self.budgetBox.setGame(game)
self.factionsInfos.setGame(game)
self.passTurnButton.setEnabled(True)
if game and game.turn == 0:
self.proceedButton.setEnabled(False)
else:

View File

@@ -1,10 +1,9 @@
"""Combo box for selecting a departure airfield."""
from typing import Iterable
from PySide2.QtCore import Signal
from PySide2.QtWidgets import QComboBox
from dcs.planes import PlaneType
from dcs.planes import PlaneType
from game.inventory import GlobalAircraftInventory
from theater.controlpoint import ControlPoint
@@ -16,8 +15,6 @@ class QOriginAirfieldSelector(QComboBox):
that have unassigned inventory of the given aircraft type.
"""
availability_changed = Signal(int)
def __init__(self, global_inventory: GlobalAircraftInventory,
origins: Iterable[ControlPoint],
aircraft: PlaneType) -> None:
@@ -26,7 +23,6 @@ class QOriginAirfieldSelector(QComboBox):
self.origins = list(origins)
self.aircraft = aircraft
self.rebuild_selector()
self.currentIndexChanged.connect(self.index_changed)
def change_aircraft(self, aircraft: PlaneType) -> None:
if self.aircraft == aircraft:
@@ -51,10 +47,3 @@ class QOriginAirfieldSelector(QComboBox):
return 0
inventory = self.global_inventory.for_control_point(origin)
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
def add_model_item(i, model, name, wpt):
print(name)
item = QStandardItem(name)
model.setItem(i, 0, item)
self.wpts.append(wpt)
@@ -78,7 +79,7 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
0
)
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.obj_name = ground_object.obj_name
wpt.targets.append(ground_object)

View File

@@ -241,12 +241,11 @@ class QLiberationWindow(QMainWindow):
"<h4>Authors</h4>" + \
"<p>DCS Liberation was originally developed by <b>shdwp</b>, DCS Liberation 2.0 is a partial rewrite based on this work by <b>Khopa</b>." \
"<h4>Contributors</h4>" + \
"shdwp, Khopa, ColonelPanic, 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>" \
"<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>Ciribob </b> <i>for the JTACAutoLase.lua script</i><br/>"\
"<b>Walder </b> <i>for the Skynet-IADS script</i><br/>"
"<b>Ciribob </b> <i>for the JTACAutoLase.lua script</i><br/>"
about = QMessageBox()
about.setWindowTitle("About DCS Liberation")
about.setIcon(QMessageBox.Icon.Information)

View File

@@ -13,9 +13,8 @@ from PySide2.QtWidgets import (
QLabel,
QMessageBox,
QPushButton,
QTextBrowser,
QTextEdit,
)
from jinja2 import Environment, FileSystemLoader, select_autoescape
from game.debriefing import Debriefing, wait_for_debriefing
from game.game import Event, Game, logging
@@ -66,21 +65,27 @@ class QWaitingForMissionResultWindow(QDialog):
self.layout.addWidget(header, 0, 0)
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(
loader=FileSystemLoader("resources/ui/templates"),
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.instructions_text = QTextEdit(TEXT)
self.instructions_text.setReadOnly(True)
self.gridLayout.addWidget(self.instructions_text, 1, 0)
progress = QLabel("")

View File

@@ -54,11 +54,11 @@ class QFlightCreator(QDialog):
[cp for cp in game.theater.controlpoints if cp.captured],
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))
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))
self.client_slots_spinner = QFlightSizeSpinner(
@@ -91,8 +91,6 @@ class QFlightCreator(QDialog):
return f"{origin.name} has no {aircraft.id} available."
if size > 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
def create_flight(self) -> None:
@@ -122,8 +120,7 @@ class QFlightCreator(QDialog):
new_aircraft = self.aircraft_selector.itemData(index)
self.airfield_selector.change_aircraft(new_aircraft)
def update_max_size(self, available: int) -> None:
self.flight_size_spinner.setMaximum(min(available, 4))
if self.flight_size_spinner.maximum() >= 2:
if self.flight_size_spinner.value() < 2:
self.flight_size_spinner.setValue(2)
def update_max_size(self) -> None:
self.flight_size_spinner.setMaximum(
min(self.airfield_selector.available, 4)
)

View File

@@ -1,4 +1,4 @@
from typing import Iterable, List, Optional
from typing import List, Optional
from PySide2.QtCore import Signal
from PySide2.QtWidgets import (
@@ -12,15 +12,11 @@ from PySide2.QtWidgets import (
from game import Game
from gen.ato import Package
from gen.flights.flight import Flight, FlightType, FlightWaypoint
from gen.flights.flightplan import (
CustomFlightPlan,
FlightPlanBuilder,
StrikeFlightPlan,
)
from gen.flights.flight import Flight, FlightType
from gen.flights.flightplan import FlightPlanBuilder
from qt_ui.windows.mission.flight.waypoints.QFlightWaypointList import \
QFlightWaypointList
from qt_ui.windows.mission.flight.waypoints \
from qt_ui.windows.mission.flight.waypoints\
.QPredefinedWaypointSelectionWindow import \
QPredefinedWaypointSelectionWindow
from theater import FrontLine
@@ -38,6 +34,8 @@ class QFlightWaypointTab(QFrame):
self.planner = FlightPlanBuilder(self.game, package, is_player=True)
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.delete_selected: 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("<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.clicked.connect(self.on_rtb_waypoint)
rlayout.addWidget(self.rtb_waypoint)
@@ -97,51 +103,35 @@ class QFlightWaypointTab(QFrame):
def on_delete_waypoint(self):
wpt = self.flight_waypoint_list.selectionModel().currentIndex().row()
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.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):
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()
def on_waypoints_added(self, waypoints: Iterable[FlightWaypoint]) -> None:
if not waypoints:
return
self.degrade_to_custom_flight_plan()
self.flight.flight_plan.waypoints.extend(waypoints)
def on_ascend_waypoint(self):
ascend = self.planner.generate_ascend_point(self.flight,
self.flight.from_cp)
self.flight.points.append(ascend)
self.flight_waypoint_list.update_list()
self.on_change()
def on_rtb_waypoint(self):
rtb = self.planner.generate_rtb_waypoint(self.flight,
self.flight.from_cp)
self.degrade_to_custom_flight_plan()
self.flight.flight_plan.waypoints.append(rtb)
self.flight.points.append(rtb)
self.flight_waypoint_list.update_list()
self.on_change()
def degrade_to_custom_flight_plan(self) -> None:
if not isinstance(self.flight.flight_plan, CustomFlightPlan):
self.flight.flight_plan = CustomFlightPlan(
package=self.flight.package,
flight=self.flight,
custom_waypoints=self.flight.flight_plan.waypoints
)
def on_descend_waypoint(self):
descend = self.planner.generate_descend_point(self.flight,
self.flight.from_cp)
self.flight.points.append(descend)
self.flight_waypoint_list.update_list()
self.on_change()
def confirm_recreate(self, task: FlightType) -> None:
result = QMessageBox.question(

View File

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

View File

@@ -62,9 +62,7 @@ class NewGameWizard(QtWidgets.QWizard):
timePeriod = db.TIME_PERIODS[list(db.TIME_PERIODS.keys())[self.field("timePeriod")]]
midGame = self.field("midGame")
# QSlider forces integers, so we use 1 to 50 and divide by 10 to give
# 0.1 to 5.0.
multiplier = self.field("multiplier") / 10
multiplier = self.field("multiplier")
no_carrier = self.field("no_carrier")
no_lha = self.field("no_lha")
supercarrier = self.field("supercarrier")
@@ -326,44 +324,6 @@ class BudgetInputs(QtWidgets.QGridLayout):
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):
def __init__(self, parent=None):
super(MiscOptions, self).__init__(parent)
@@ -374,12 +334,14 @@ class MiscOptions(QtWidgets.QWizardPage):
QtGui.QPixmap('./resources/ui/wizard/logo1.png'))
midGame = QtWidgets.QCheckBox()
multiplier_inputs = ForceMultiplierInputs()
self.registerField('multiplier', multiplier_inputs.multiplier)
multiplier = QtWidgets.QSpinBox()
multiplier.setEnabled(False)
multiplier.setMinimum(1)
multiplier.setMaximum(5)
miscSettingsGroup = QtWidgets.QGroupBox("Misc Settings")
self.registerField('midGame', midGame)
self.registerField('multiplier', multiplier)
# Campaign settings
generatorSettingsGroup = QtWidgets.QGroupBox("Generator Settings")
@@ -397,7 +359,8 @@ class MiscOptions(QtWidgets.QWizardPage):
layout = QtWidgets.QGridLayout()
layout.addWidget(QtWidgets.QLabel("Start at mid game"), 1, 0)
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)
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 %}
The enemy forces have been crushed, we will be able to make significant progress toward {{ frontline.enemy_base.name }}
{% endif %}
{% if not frontline.player_zero %}
{# 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_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 #}
{% endif %}
{% endif %}
{% endif %}
{%+ endfor %}{% endif %}
{% endfor %}{% endif %}
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",
["pylons"] = {
[1] = {
["CLSID"] = "{RB75}",
["num"] = 5,
},
[2] = {
["CLSID"] = "{RB75}",
["CLSID"] = "{ARAKM70BHE}",
["num"] = 3,
},
[3] = {
["CLSID"] = "{RB75}",
[2] = {
["CLSID"] = "{ARAKM70BHE}",
["num"] = 2,
},
[4] = {
["CLSID"] = "{RB75}",
["num"] = 6,
},
[5] = {
["CLSID"] = "{Robot24J}",
["num"] = 1,
},
[6] = {
["CLSID"] = "{Robot24J}",
["num"] = 7,
},
[7] = {
[3] = {
["CLSID"] = "{VIGGEN_X-TANK}",
["num"] = 4,
},
[4] = {
["CLSID"] = "{ARAKM70BHE}",
["num"] = 5,
},
[5] = {
["CLSID"] = "{ARAKM70BHE}",
["num"] = 6,
},
},
["tasks"] = {
[1] = 32,
[2] = 31,
[1] = 31,
},
},
[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",
"description": "<p>A generic bluefor coldwar faction.</p>",
"aircrafts": [
"F_14A_135_GR",
"F_14B",
"F_4E",
"F_5E_3",

View File

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

View File

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

View File

@@ -61,24 +61,5 @@
"OliverHazardPerryGroupGenerator"
],
"has_jtac": true,
"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"
]
}
"jtac_unit": "MQ_9_Reaper"
}

View File

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

View File

@@ -6,7 +6,6 @@
"aircrafts": [
"F_15C",
"F_15E",
"F_14A_135_GR",
"F_14B",
"FA_18C_hornet",
"F_16C_50",
@@ -86,25 +85,5 @@
"ArleighBurkeGroupGenerator"
],
"has_jtac": true,
"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"
]
}
"jtac_unit": "MQ_9_Reaper"
}

View File

@@ -86,25 +86,5 @@
"OliverHazardPerryGroupGenerator"
],
"has_jtac": true,
"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"
]
}
"jtac_unit": "MQ_9_Reaper"
}

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."""
return f"{self.category}|{self.group_id}"
@property
def waypoint_name(self) -> str:
return f"[{self.name}] {self.category}"
def __str__(self) -> str:
return NAME_BY_CATEGORY[self.category]
@@ -140,10 +136,6 @@ class BuildingGroundObject(TheaterGroundObject):
"""The name of the unit group."""
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):
pass