mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Compare commits
45 Commits
pydcs-2-7-
...
2.5.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0e5a707fb | ||
|
|
4555a4968d | ||
|
|
ae34e4749b | ||
|
|
635eee9590 | ||
|
|
f0558c4c1e | ||
|
|
637ca8fbca | ||
|
|
e4e65df976 | ||
|
|
29579a2aec | ||
|
|
e32b43cffb | ||
|
|
de2e5f861b | ||
|
|
b27a7fc71b | ||
|
|
5861ce6146 | ||
|
|
c732ed556f | ||
|
|
be1a75e520 | ||
|
|
c41d10c581 | ||
|
|
157a59e3c4 | ||
|
|
d24c65c3aa | ||
|
|
d4d441ff9b | ||
|
|
f43fb1223f | ||
|
|
3db275414d | ||
|
|
6e0ff6c805 | ||
|
|
9c359efbff | ||
|
|
c5cc1ea8e8 | ||
|
|
afb6a33131 | ||
|
|
539a11f54d | ||
|
|
9324e549e6 | ||
|
|
c8f6b6df87 | ||
|
|
38f632097e | ||
|
|
e63743f537 | ||
|
|
ce13295cf0 | ||
|
|
23c02a3510 | ||
|
|
01ea7b9ee1 | ||
|
|
6fed1284a1 | ||
|
|
5574d849bd | ||
|
|
c2ce3a6992 | ||
|
|
b61d15fdf4 | ||
|
|
ad5cc83fb3 | ||
|
|
2f53edd775 | ||
|
|
923459c88b | ||
|
|
1192d26448 | ||
|
|
2d5e827417 | ||
|
|
a30d9276b8 | ||
|
|
0cd088122e | ||
|
|
b6f3467a89 | ||
|
|
52ce1a5959 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,6 +11,7 @@ a.py
|
||||
resources/tools/a.miz
|
||||
# User-specific stuff
|
||||
.idea/
|
||||
.env
|
||||
|
||||
/kneeboards
|
||||
/liberation_preferences.json
|
||||
|
||||
37
changelog.md
37
changelog.md
@@ -1,28 +1,51 @@
|
||||
# 2.5.1
|
||||
|
||||
## Features/Improvements
|
||||
|
||||
* **[UI]** Engagement ranges are now displayed by default.
|
||||
* **[UI]** Engagement range display generalized to work for all patrolling flight plans (BARCAP, TARCAP, and CAS).
|
||||
* **[Flight Planner]** Front lines no longer project threat zones to avoid pushing BARCAPs back so much. TARCAPs will be forcibly planned but strike packages will not route around front lines even if it is reasonable to do so.
|
||||
|
||||
## Fixes
|
||||
|
||||
* **[Campaigns]** EWRs associated with a base will now only be generated near the base.
|
||||
* **[Flight Planner]** Fixed error when generating AEW&C flight plans in campaigns with no front lines.
|
||||
|
||||
# 2.5.0
|
||||
|
||||
Saves from 2.4 are not compatible with 2.5.
|
||||
|
||||
## Features/Improvements
|
||||
|
||||
* **[Flight Planner]** (WIP) Added AEW&C missions. (by siKruger)
|
||||
* **[Kneeboard]** Added dark kneeboard option (by GvonH)
|
||||
* **[Engine]** DCS 2.7 Support
|
||||
* **[UI]** Improved FOB menu, added a custom banner, and do not display aircraft recruitment menu
|
||||
* **[Flight Planner]** Added AEW&C missions. (by siKruger)
|
||||
* **[Kneeboard]** Added dark kneeboard option (by GvonH)
|
||||
* **[Campaigns]** Multiple EWR sites may now be generated, and EWR sites may be generated outside bases (by SnappyComebacks)
|
||||
* **[Mission Generation]** Cloudy and rainy (but not thunderstorm) weather will use the cloud presets from DCS 2.7.
|
||||
* **[Plugins]** Added LotATC export plugin (by drsoran)
|
||||
* **[Plugins]** Added Splash Damage Plugin (by Wheelijoe)
|
||||
* **[Loadouts]** Replaced Litening with ATFLIR for all default F/A-18C loadouts.
|
||||
|
||||
## Fixes
|
||||
|
||||
* **[Flight Planner]** Front lines now project threat zones, so TARCAP/escorts will not be pruned for flights near the front. Packages may also route around the front line when practical.
|
||||
* **[Flight Planner]** Fixed error when planning BAI at SAMs with dead subgroups.
|
||||
* **[Flight Planner]** Mig-19 was not allowed for CAS roles fixed
|
||||
* **[Flight Planner]** Increased size of navigation planning area to avoid plannign failures with distant waypoints.
|
||||
* **[Flight Planner]** Fixed UI refresh when unchecking the "default loadout" box in the loadout editor.
|
||||
* **[Objective names]** Fixed typos in objective name : ARMADILLLO -> ARMADILLO (by SnappyComebacks)
|
||||
* **[Payloads]** F-86 Sabre was missing a custom payload
|
||||
* **[Payloads]** Added GAR-8 period restrictions (by Mustang-25)
|
||||
* **[Campaign]** Date now progresses.
|
||||
|
||||
# 2.4.4
|
||||
|
||||
## Fixes
|
||||
|
||||
* **[Campaign]** Added game over message when a coalition runs out of functioning airbases.
|
||||
* **[Mission Generation]** Fixed "invalid face handle" error in kneeboard generation that occurred on some machines.
|
||||
|
||||
## Regressions
|
||||
|
||||
* **[Mod Support]** Stopped support for 2.5.5 Rafale Mode, and removed factions that were using it
|
||||
* **[Mod Support]** Su-57 mod support might be out of date
|
||||
|
||||
# 2.4.3
|
||||
|
||||
## Features/Improvements
|
||||
|
||||
@@ -18,4 +18,5 @@ AAA_UNITS = [
|
||||
AirDefence.AAA_SP_Kdo_G_40,
|
||||
AirDefence.AAA_8_8cm_Flak_41,
|
||||
AirDefence.AAA_40mm_Bofors,
|
||||
AirDefence.AAA_S_60_57mm,
|
||||
]
|
||||
|
||||
@@ -523,6 +523,7 @@ PRICES = {
|
||||
Artillery.MLRS_9A52_Smerch_HE_300mm: 40,
|
||||
Artillery.Mortar_2B11_120mm: 4,
|
||||
Artillery.SPH_Dana_vz77_152mm: 26,
|
||||
Artillery.PLZ_05: 25,
|
||||
Unarmed.LUV_UAZ_469_Jeep: 3,
|
||||
Unarmed.Truck_Ural_375: 3,
|
||||
Infantry.Infantry_M4: 1,
|
||||
@@ -631,6 +632,7 @@ PRICES = {
|
||||
AirDefence.AAA_8_8cm_Flak_41: 10,
|
||||
AirDefence.EWR_FuMG_401_Freya_LZ: 25,
|
||||
AirDefence.AAA_40mm_Bofors: 8,
|
||||
AirDefence.AAA_S_60_57mm: 8,
|
||||
AirDefence.AAA_M1_37mm: 7,
|
||||
AirDefence.AAA_M45_Quadmount_HB_12_7mm: 4,
|
||||
AirDefence.AAA_QF_3_7: 10,
|
||||
@@ -974,6 +976,7 @@ UNIT_BY_TASK = {
|
||||
Artillery.MLRS_BM_27_Uragan_220mm,
|
||||
Artillery.MLRS_9A52_Smerch_HE_300mm,
|
||||
Artillery.SPH_Dana_vz77_152mm,
|
||||
Artillery.PLZ_05,
|
||||
Artillery.SPG_M12_GMC_155mm,
|
||||
Artillery.SPG_Sturmpanzer_IV_Brummbar,
|
||||
AirDefence.SPAAA_ZU_23_2_Mounted_Ural_375,
|
||||
@@ -997,6 +1000,7 @@ UNIT_BY_TASK = {
|
||||
AirDefence.AAA_8_8cm_Flak_37,
|
||||
AirDefence.AAA_8_8cm_Flak_41,
|
||||
AirDefence.AAA_40mm_Bofors,
|
||||
AirDefence.AAA_S_60_57mm,
|
||||
AirDefence.AAA_M1_37mm,
|
||||
AirDefence.AAA_QF_3_7,
|
||||
frenchpack.DIM__TOYOTA_BLUE,
|
||||
|
||||
@@ -446,6 +446,8 @@ class Operation:
|
||||
"AWACs": {},
|
||||
"JTACs": {},
|
||||
"TargetPoints": {},
|
||||
"RedAA": {},
|
||||
"BlueAA": {},
|
||||
} # type: ignore
|
||||
|
||||
for tanker in airsupportgen.air_support.tankers:
|
||||
@@ -503,6 +505,26 @@ class Operation:
|
||||
},
|
||||
}
|
||||
|
||||
for cp in cls.game.theater.controlpoints:
|
||||
for ground_object in cp.ground_objects:
|
||||
if ground_object.might_have_aa and not ground_object.is_dead:
|
||||
for g in ground_object.groups:
|
||||
threat_range = ground_object.threat_range(g)
|
||||
|
||||
if not threat_range:
|
||||
continue
|
||||
|
||||
faction = "BlueAA" if cp.captured else "RedAA"
|
||||
|
||||
luaData[faction][g.name] = {
|
||||
"name": ground_object.name,
|
||||
"range": threat_range.meters,
|
||||
"position": {
|
||||
"x": ground_object.position.x,
|
||||
"y": ground_object.position.y,
|
||||
},
|
||||
}
|
||||
|
||||
# set a LUA table with data from Liberation that we want to set
|
||||
# at the moment it contains Liberation's install path, and an overridable definition for the JTACAutoLase function
|
||||
# later, we'll add data about the units and points having been generated, in order to facilitate the configuration of the plugin lua scripts
|
||||
@@ -595,7 +617,33 @@ class Operation:
|
||||
-- list the aircraft carriers generated by Liberation
|
||||
-- dcsLiberation.Carriers = {}
|
||||
|
||||
-- later, we'll add more data to the table
|
||||
-- list the Red AA generated by Liberation
|
||||
dcsLiberation.RedAA = {
|
||||
"""
|
||||
for key in luaData["RedAA"]:
|
||||
data = luaData["RedAA"][key]
|
||||
name = data["name"]
|
||||
radius = data["range"]
|
||||
positionX = data["position"]["x"]
|
||||
positionY = data["position"]["y"]
|
||||
lua += f" {{dcsGroupName='{key}', name='{name}', range='{radius}', positionX='{positionX}', positionY='{positionY}' }}, \n"
|
||||
lua += "}"
|
||||
|
||||
lua += """
|
||||
|
||||
-- list the Blue AA generated by Liberation
|
||||
dcsLiberation.BlueAA = {
|
||||
"""
|
||||
for key in luaData["BlueAA"]:
|
||||
data = luaData["BlueAA"][key]
|
||||
name = data["name"]
|
||||
radius = data["range"]
|
||||
positionX = data["position"]["x"]
|
||||
positionY = data["position"]["y"]
|
||||
lua += f" {{dcsGroupName='{key}', name='{name}', range='{radius}', positionX='{positionX}', positionY='{positionY}' }}, \n"
|
||||
lua += "}"
|
||||
|
||||
lua += """
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@@ -118,6 +118,8 @@ class MizCampaignLoader:
|
||||
AirDefence.SAM_SA_3_S_125_Goa_LN.id,
|
||||
}
|
||||
|
||||
REQUIRED_EWR_UNIT_TYPE = AirDefence.EWR_1L13.id
|
||||
|
||||
BASE_DEFENSE_RADIUS = nautical_miles(2)
|
||||
|
||||
def __init__(self, miz: Path, theater: ConflictTheater) -> None:
|
||||
@@ -247,6 +249,12 @@ class MizCampaignLoader:
|
||||
if group.units[0].type in self.REQUIRED_MEDIUM_RANGE_SAM_UNIT_TYPES:
|
||||
yield group
|
||||
|
||||
@property
|
||||
def required_ewrs(self) -> Iterator[VehicleGroup]:
|
||||
for group in self.red.vehicle_group:
|
||||
if group.units[0].type in self.REQUIRED_EWR_UNIT_TYPE:
|
||||
yield group
|
||||
|
||||
@property
|
||||
def helipads(self) -> Iterator[StaticGroup]:
|
||||
for group in self.blue.static_group:
|
||||
@@ -356,9 +364,14 @@ class MizCampaignLoader:
|
||||
|
||||
for group in self.ewrs:
|
||||
closest, distance = self.objective_info(group)
|
||||
closest.preset_locations.ewrs.append(
|
||||
PointWithHeading.from_point(group.position, group.units[0].heading)
|
||||
)
|
||||
if distance < self.BASE_DEFENSE_RADIUS:
|
||||
closest.preset_locations.base_ewrs.append(
|
||||
PointWithHeading.from_point(group.position, group.units[0].heading)
|
||||
)
|
||||
else:
|
||||
closest.preset_locations.ewrs.append(
|
||||
PointWithHeading.from_point(group.position, group.units[0].heading)
|
||||
)
|
||||
|
||||
for group in self.offshore_strike_targets:
|
||||
closest, distance = self.objective_info(group)
|
||||
@@ -396,6 +409,12 @@ class MizCampaignLoader:
|
||||
PointWithHeading.from_point(group.position, group.units[0].heading)
|
||||
)
|
||||
|
||||
for group in self.required_ewrs:
|
||||
closest, distance = self.objective_info(group)
|
||||
closest.preset_locations.required_ewrs.append(
|
||||
PointWithHeading.from_point(group.position, group.units[0].heading)
|
||||
)
|
||||
|
||||
for group in self.helipads:
|
||||
closest, distance = self.objective_info(group)
|
||||
closest.helipads.append(
|
||||
|
||||
@@ -63,6 +63,7 @@ class LocationType(Enum):
|
||||
BaseAirDefense = "base air defense"
|
||||
Coastal = "coastal defense"
|
||||
Ewr = "EWR"
|
||||
BaseEwr = "Base EWR"
|
||||
Garrison = "garrison"
|
||||
MissileSite = "missile site"
|
||||
OffshoreStrikeTarget = "offshore strike target"
|
||||
@@ -86,6 +87,9 @@ class PresetLocations:
|
||||
#: Locations used by EWRs.
|
||||
ewrs: List[PointWithHeading] = field(default_factory=list)
|
||||
|
||||
#: Locations used by Base EWRs.
|
||||
base_ewrs: List[PointWithHeading] = field(default_factory=list)
|
||||
|
||||
#: Locations used by non-carrier ships. Carriers and LHAs are not random.
|
||||
ships: List[PointWithHeading] = field(default_factory=list)
|
||||
|
||||
@@ -107,6 +111,9 @@ class PresetLocations:
|
||||
#: Locations of medium range SAMs which should always be spawned.
|
||||
required_medium_range_sams: List[PointWithHeading] = field(default_factory=list)
|
||||
|
||||
#: Locations of EWRs which should always be spawned.
|
||||
required_ewrs: List[PointWithHeading] = field(default_factory=list)
|
||||
|
||||
@staticmethod
|
||||
def _random_from(points: List[PointWithHeading]) -> Optional[PointWithHeading]:
|
||||
"""Finds, removes, and returns a random position from the given list."""
|
||||
@@ -128,6 +135,8 @@ class PresetLocations:
|
||||
return self._random_from(self.coastal_defenses)
|
||||
if location_type == LocationType.Ewr:
|
||||
return self._random_from(self.ewrs)
|
||||
if location_type == LocationType.BaseEwr:
|
||||
return self._random_from(self.base_ewrs)
|
||||
if location_type == LocationType.Garrison:
|
||||
return self._random_from(self.base_garrisons)
|
||||
if location_type == LocationType.MissileSite:
|
||||
@@ -389,7 +398,7 @@ class ControlPoint(MissionTarget, ABC):
|
||||
for base_defense in self.base_defenses:
|
||||
p = PointWithHeading.from_point(base_defense.position, base_defense.heading)
|
||||
if isinstance(base_defense, EwrGroundObject):
|
||||
self.preset_locations.ewrs.append(p)
|
||||
self.preset_locations.base_ewrs.append(p)
|
||||
elif isinstance(base_defense, SamGroundObject):
|
||||
self.preset_locations.base_air_defense.append(p)
|
||||
elif isinstance(base_defense, VehicleGroupGroundObject):
|
||||
|
||||
@@ -37,10 +37,8 @@ from gen.fleet.ship_group_generator import (
|
||||
from gen.locations.preset_location_finder import MizDataLocationFinder
|
||||
from gen.missiles.missiles_group_generator import generate_missile_group
|
||||
from gen.sam.airdefensegroupgenerator import AirDefenseRange
|
||||
from gen.sam.sam_group_generator import (
|
||||
generate_anti_air_group,
|
||||
generate_ewr_group,
|
||||
)
|
||||
from gen.sam.sam_group_generator import generate_anti_air_group
|
||||
from gen.sam.ewr_group_generator import generate_ewr_group
|
||||
from . import (
|
||||
ConflictTheater,
|
||||
ControlPoint,
|
||||
@@ -457,14 +455,18 @@ class BaseDefenseGenerator:
|
||||
self.generate_base_defenses()
|
||||
|
||||
def generate_ewr(self) -> None:
|
||||
position = self.location_finder.location_for(LocationType.Ewr)
|
||||
position = self.location_finder.location_for(LocationType.BaseEwr)
|
||||
if position is None:
|
||||
return
|
||||
|
||||
group_id = self.game.next_group_id()
|
||||
|
||||
g = EwrGroundObject(
|
||||
namegen.random_objective_name(), group_id, position, self.control_point
|
||||
namegen.random_objective_name(),
|
||||
group_id,
|
||||
position,
|
||||
self.control_point,
|
||||
True,
|
||||
)
|
||||
|
||||
group = generate_ewr_group(self.game, g, self.faction)
|
||||
@@ -609,6 +611,7 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
||||
def generate_ground_points(self) -> None:
|
||||
"""Generate ground objects and AA sites for the control point."""
|
||||
skip_sams = self.generate_required_aa()
|
||||
skip_ewrs = self.generate_required_ewr()
|
||||
|
||||
if self.control_point.is_global:
|
||||
return
|
||||
@@ -625,6 +628,12 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
||||
skip_sams -= 1
|
||||
else:
|
||||
self.generate_aa_site()
|
||||
# 1 in 4 additional objectives are EWR.
|
||||
elif random.randint(0, 3) == 0:
|
||||
if skip_ewrs > 0:
|
||||
skip_ewrs -= 1
|
||||
else:
|
||||
self.generate_ewr_site()
|
||||
else:
|
||||
self.generate_ground_point()
|
||||
|
||||
@@ -656,6 +665,17 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
||||
presets.required_medium_range_sams
|
||||
)
|
||||
|
||||
def generate_required_ewr(self) -> int:
|
||||
"""Generates the EWR sites that are required by the campaign.
|
||||
|
||||
Returns:
|
||||
The number of EWR sites that were generated.
|
||||
"""
|
||||
presets = self.control_point.preset_locations
|
||||
for position in presets.required_ewrs:
|
||||
self.generate_ewr_at(position)
|
||||
return len(presets.required_ewrs)
|
||||
|
||||
def generate_ground_point(self) -> None:
|
||||
try:
|
||||
category = random.choice(self.faction.building_set)
|
||||
@@ -733,6 +753,33 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
|
||||
g.groups = groups
|
||||
self.control_point.connected_objectives.append(g)
|
||||
|
||||
def generate_ewr_site(self) -> None:
|
||||
position = self.location_finder.location_for(LocationType.Ewr)
|
||||
if position is None:
|
||||
return
|
||||
self.generate_ewr_at(position)
|
||||
|
||||
def generate_ewr_at(self, position: Point) -> None:
|
||||
group_id = self.game.next_group_id()
|
||||
|
||||
g = EwrGroundObject(
|
||||
namegen.random_objective_name(),
|
||||
group_id,
|
||||
position,
|
||||
self.control_point,
|
||||
for_airbase=False,
|
||||
)
|
||||
group = generate_ewr_group(self.game, g, self.faction)
|
||||
if group is None:
|
||||
logging.error(
|
||||
"Could not generate ewr group for %s at %s",
|
||||
g.name,
|
||||
self.control_point,
|
||||
)
|
||||
return
|
||||
g.groups = [group]
|
||||
self.control_point.connected_objectives.append(g)
|
||||
|
||||
def generate_missile_sites(self) -> None:
|
||||
for i in range(self.faction.missiles_group_count):
|
||||
self.generate_missile_site()
|
||||
|
||||
@@ -442,7 +442,12 @@ class VehicleGroupGroundObject(BaseDefenseGroundObject):
|
||||
|
||||
class EwrGroundObject(BaseDefenseGroundObject):
|
||||
def __init__(
|
||||
self, name: str, group_id: int, position: Point, control_point: ControlPoint
|
||||
self,
|
||||
name: str,
|
||||
group_id: int,
|
||||
position: Point,
|
||||
control_point: ControlPoint,
|
||||
for_airbase: bool,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
name=name,
|
||||
@@ -452,7 +457,7 @@ class EwrGroundObject(BaseDefenseGroundObject):
|
||||
heading=0,
|
||||
control_point=control_point,
|
||||
dcs_identifier="EWR",
|
||||
airbase_group=True,
|
||||
airbase_group=for_airbase,
|
||||
sea_object=False,
|
||||
)
|
||||
|
||||
|
||||
@@ -152,23 +152,6 @@ class ThreatZones:
|
||||
threat_zone = point.buffer(threat_range.meters)
|
||||
air_defenses.append(threat_zone)
|
||||
|
||||
for front_line in game.theater.conflicts(player):
|
||||
vector = Conflict.frontline_vector(
|
||||
front_line.control_point_a, front_line.control_point_b, game.theater
|
||||
)
|
||||
|
||||
start = vector[0]
|
||||
end = vector[0].point_from_heading(vector[1], vector[2])
|
||||
|
||||
line = LineString(
|
||||
[
|
||||
ShapelyPoint(start.x, start.y),
|
||||
ShapelyPoint(end.x, end.y),
|
||||
]
|
||||
)
|
||||
doctrine = game.faction_for(player).doctrine
|
||||
air_threats.append(line.buffer(doctrine.cap_engagement_range.meters))
|
||||
|
||||
return cls(
|
||||
airbases=unary_union(air_threats), air_defenses=unary_union(air_defenses)
|
||||
)
|
||||
|
||||
@@ -3,11 +3,12 @@ from __future__ import annotations
|
||||
import datetime
|
||||
import logging
|
||||
import random
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
from dcs.weather import Weather as PydcsWeather, Wind
|
||||
from dcs.cloud_presets import Clouds as PydcsClouds
|
||||
from dcs.weather import CloudPreset, Weather as PydcsWeather, Wind
|
||||
|
||||
from game.settings import Settings
|
||||
from game.utils import Distance, meters
|
||||
@@ -36,6 +37,23 @@ class Clouds:
|
||||
density: int
|
||||
thickness: int
|
||||
precipitation: PydcsWeather.Preceptions
|
||||
preset: Optional[CloudPreset] = field(default=None)
|
||||
|
||||
@classmethod
|
||||
def random_preset(cls, rain: bool) -> Clouds:
|
||||
clouds = (p.value for p in PydcsClouds)
|
||||
if rain:
|
||||
presets = [p for p in clouds if "Rain" in p.name]
|
||||
else:
|
||||
presets = [p for p in clouds if "Rain" not in p.name]
|
||||
preset = random.choice(presets)
|
||||
return Clouds(
|
||||
base=random.randint(preset.min_base, preset.max_base),
|
||||
density=0,
|
||||
thickness=0,
|
||||
precipitation=PydcsWeather.Preceptions.None_,
|
||||
preset=preset,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -101,12 +119,11 @@ class ClearSkies(Weather):
|
||||
|
||||
class Cloudy(Weather):
|
||||
def generate_clouds(self) -> Optional[Clouds]:
|
||||
return Clouds(
|
||||
base=self.random_cloud_base(),
|
||||
density=random.randint(1, 8),
|
||||
thickness=self.random_cloud_thickness(),
|
||||
precipitation=PydcsWeather.Preceptions.None_,
|
||||
)
|
||||
return Clouds.random_preset(rain=False)
|
||||
|
||||
def generate_fog(self) -> Optional[Fog]:
|
||||
# DCS 2.7 says to not use fog with the cloud presets.
|
||||
return None
|
||||
|
||||
def generate_wind(self) -> WindConditions:
|
||||
return self.random_wind(0, 4)
|
||||
@@ -114,12 +131,11 @@ class Cloudy(Weather):
|
||||
|
||||
class Raining(Weather):
|
||||
def generate_clouds(self) -> Optional[Clouds]:
|
||||
return Clouds(
|
||||
base=self.random_cloud_base(),
|
||||
density=random.randint(5, 8),
|
||||
thickness=self.random_cloud_thickness(),
|
||||
precipitation=PydcsWeather.Preceptions.Rain,
|
||||
)
|
||||
return Clouds.random_preset(rain=True)
|
||||
|
||||
def generate_fog(self) -> Optional[Fog]:
|
||||
# DCS 2.7 says to not use fog with the cloud presets.
|
||||
return None
|
||||
|
||||
def generate_wind(self) -> WindConditions:
|
||||
return self.random_wind(0, 6)
|
||||
|
||||
239
gen/airfields.py
239
gen/airfields.py
@@ -383,8 +383,8 @@ AIRFIELD_DATA = {
|
||||
"31": ("IVZ", MHz(108, 750)),
|
||||
},
|
||||
),
|
||||
# TODO : PERSIAN GULF MAP
|
||||
"Liwa Airbase": AirfieldData(
|
||||
# PERSIAN GULF MAP
|
||||
"Liwa AFB": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
icao="OMLW",
|
||||
elevation=400,
|
||||
@@ -394,7 +394,7 @@ AIRFIELD_DATA = {
|
||||
vor=("OMLW", MHz(117, 400)),
|
||||
atc=AtcData(MHz(4, 225), MHz(39, 350), MHz(119, 300), MHz(250, 950)),
|
||||
),
|
||||
"Al Dhafra AB": AirfieldData(
|
||||
"Al Dhafra AFB": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
icao="OMAM",
|
||||
elevation=52,
|
||||
@@ -402,50 +402,50 @@ AIRFIELD_DATA = {
|
||||
tacan=TacanChannel(96, TacanBand.X),
|
||||
tacan_callsign="MA",
|
||||
vor=("MA", MHz(114, 900)),
|
||||
atc=AtcData(MHz(4, 250), MHz(39, 400), MHz(126, 500), MHz(251, 000)),
|
||||
atc=AtcData(MHz(4, 300), MHz(39, 500), MHz(126, 500), MHz(251, 100)),
|
||||
ils={
|
||||
"13": ("MMA", MHz(111, 100)),
|
||||
"31": ("IMA", MHz(109, 100)),
|
||||
},
|
||||
),
|
||||
"Al-Bateen Airport": AirfieldData(
|
||||
"Al-Bateen": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
icao="OMAD",
|
||||
elevation=11,
|
||||
runway_length=6808,
|
||||
vor=("ALB", MHz(114, 0)),
|
||||
atc=AtcData(MHz(4, 25), MHz(38, 950), MHz(119, 900), MHz(250, 550)),
|
||||
atc=AtcData(MHz(4, 75), MHz(39, 50), MHz(119, 900), MHz(250, 600)),
|
||||
),
|
||||
"Sas Al Nakheel Airport": AirfieldData(
|
||||
"Sas Al Nakheel": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
icao="OMNK",
|
||||
elevation=9,
|
||||
runway_length=5387,
|
||||
vor=("SAS", MHz(128, 930)),
|
||||
atc=AtcData(MHz(3, 975), MHz(38, 850), MHz(128, 900), MHz(250, 450)),
|
||||
atc=AtcData(MHz(4, 0), MHz(38, 900), MHz(128, 900), MHz(250, 450)),
|
||||
),
|
||||
"Abu Dhabi International Airport": AirfieldData(
|
||||
"Abu Dhabi Intl": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
icao="OMAA",
|
||||
elevation=91,
|
||||
runway_length=12817,
|
||||
vor=("ADV", MHz(114, 250)),
|
||||
atc=AtcData(MHz(4, 000), MHz(38, 900), MHz(119, 200), MHz(250, 500)),
|
||||
atc=AtcData(MHz(4, 50), MHz(39, 0), MHz(119, 200), MHz(250, 550)),
|
||||
),
|
||||
"Al Ain International Airport": AirfieldData(
|
||||
"Al Ain Intl": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
icao="OMAL",
|
||||
elevation=813,
|
||||
runway_length=11267,
|
||||
vor=("ALN", MHz(112, 600)),
|
||||
atc=AtcData(MHz(4, 75), MHz(39, 50), MHz(119, 850), MHz(250, 650)),
|
||||
atc=AtcData(MHz(4, 125), MHz(39, 150), MHz(119, 850), MHz(250, 700)),
|
||||
),
|
||||
"Al Maktoum Intl": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
icao="OMDW",
|
||||
elevation=123,
|
||||
runway_length=11500,
|
||||
atc=AtcData(MHz(4, 300), MHz(39, 500), MHz(118, 650), MHz(251, 100)),
|
||||
atc=AtcData(MHz(4, 350), MHz(39, 600), MHz(118, 600), MHz(251, 200)),
|
||||
ils={
|
||||
"30": ("IJWA", MHz(109, 750)),
|
||||
"12": ("IMA", MHz(111, 750)),
|
||||
@@ -458,7 +458,7 @@ AIRFIELD_DATA = {
|
||||
runway_length=11865,
|
||||
tacan=TacanChannel(99, TacanBand.X),
|
||||
tacan_callsign="MIN",
|
||||
atc=AtcData(MHz(3, 800), MHz(38, 500), MHz(121, 800), MHz(250, 100)),
|
||||
atc=AtcData(MHz(3, 800), MHz(38, 500), MHz(118, 550), MHz(250, 100)),
|
||||
ils={
|
||||
"27": ("IMNR", MHz(110, 750)),
|
||||
"9": ("IMNW", MHz(110, 700)),
|
||||
@@ -469,7 +469,7 @@ AIRFIELD_DATA = {
|
||||
icao="OMDB",
|
||||
elevation=16,
|
||||
runway_length=11018,
|
||||
atc=AtcData(MHz(4, 275), MHz(39, 450), MHz(118, 750), MHz(251, 50)),
|
||||
atc=AtcData(MHz(4, 325), MHz(39, 550), MHz(118, 750), MHz(251, 150)),
|
||||
ils={
|
||||
"30": ("IDBL", MHz(110, 900)),
|
||||
"12": ("IDBR", MHz(110, 100)),
|
||||
@@ -480,7 +480,7 @@ AIRFIELD_DATA = {
|
||||
icao="OMSJ",
|
||||
elevation=98,
|
||||
runway_length=10535,
|
||||
atc=AtcData(MHz(3, 850), MHz(38, 600), MHz(118, 600), MHz(252, 200)),
|
||||
atc=AtcData(MHz(3, 850), MHz(38, 600), MHz(118, 600), MHz(250, 200)),
|
||||
ils={
|
||||
"30": ("ISHW", MHz(111, 950)),
|
||||
"12": ("ISRE", MHz(108, 550)),
|
||||
@@ -492,18 +492,18 @@ AIRFIELD_DATA = {
|
||||
elevation=60,
|
||||
runway_length=9437,
|
||||
vor=("FJV", MHz(113, 800)),
|
||||
atc=AtcData(MHz(4, 325), MHz(39, 550), MHz(124, 600), MHz(251, 150)),
|
||||
atc=AtcData(MHz(4, 375), MHz(39, 650), MHz(124, 600), MHz(251, 250)),
|
||||
ils={
|
||||
"29": ("IFJR", MHz(111, 500)),
|
||||
},
|
||||
),
|
||||
"Ras AL Khaimah": AirfieldData(
|
||||
"Ras Al Khaimah Intl": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
icao="OMRK",
|
||||
elevation=70,
|
||||
runway_length=8406,
|
||||
vor=("OMRK", MHz(113, 600)),
|
||||
atc=AtcData(MHz(4, 150), MHz(39, 200), MHz(121, 600), MHz(250, 800)),
|
||||
atc=AtcData(MHz(4, 200), MHz(39, 300), MHz(121, 600), MHz(250, 900)),
|
||||
),
|
||||
"Khasab": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
@@ -516,7 +516,11 @@ AIRFIELD_DATA = {
|
||||
},
|
||||
),
|
||||
"Sir Abu Nuayr": AirfieldData(
|
||||
theater="Persian Gulf", icao="OMSN", elevation=25, runway_length=2229
|
||||
theater="Persian Gulf",
|
||||
icao="OMSN",
|
||||
elevation=25,
|
||||
runway_length=2229,
|
||||
atc=AtcData(MHz(3, 900), MHz(38, 700), MHz(118, 0), MHz(250, 800)),
|
||||
),
|
||||
"Sirri Island": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
@@ -526,7 +530,7 @@ AIRFIELD_DATA = {
|
||||
vor=("SIR", MHz(113, 750)),
|
||||
atc=AtcData(MHz(3, 875), MHz(38, 650), MHz(135, 50), MHz(250, 250)),
|
||||
),
|
||||
"Abu Musa Island Airport": AirfieldData(
|
||||
"Abu Musa Island": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
icao="OIBA",
|
||||
elevation=16,
|
||||
@@ -555,13 +559,13 @@ AIRFIELD_DATA = {
|
||||
vor=("KHM", MHz(117, 100)),
|
||||
atc=AtcData(MHz(3, 825), MHz(38, 550), MHz(118, 50), MHz(250, 150)),
|
||||
),
|
||||
"Bandar-e-Jask airfield": AirfieldData(
|
||||
"Bandar-e-Jask": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
icao="OIZJ",
|
||||
elevation=26,
|
||||
runway_length=6842,
|
||||
vor=("KHM", MHz(116, 300)),
|
||||
atc=AtcData(MHz(3, 825), MHz(38, 550), MHz(118, 50), MHz(250, 150)),
|
||||
atc=AtcData(MHz(4, 25), MHz(38, 950), MHz(118, 150), MHz(250, 500)),
|
||||
),
|
||||
"Bandar Lengeh": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
@@ -569,26 +573,26 @@ AIRFIELD_DATA = {
|
||||
elevation=80,
|
||||
runway_length=7625,
|
||||
vor=("LEN", MHz(114, 800)),
|
||||
atc=AtcData(MHz(4, 225), MHz(39, 350), MHz(121, 700), MHz(250, 950)),
|
||||
atc=AtcData(MHz(4, 275), MHz(39, 450), MHz(121, 700), MHz(251, 50)),
|
||||
),
|
||||
"Kish International Airport": AirfieldData(
|
||||
"Kish Intl": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
icao="OIBK",
|
||||
elevation=114,
|
||||
runway_length=10617,
|
||||
tacan=TacanChannel(112, TacanBand.X),
|
||||
tacan_callsign="KIH",
|
||||
atc=AtcData(MHz(4, 50), MHz(39, 000), MHz(121, 650), MHz(250, 600)),
|
||||
atc=AtcData(MHz(4, 100), MHz(39, 100), MHz(121, 650), MHz(250, 650)),
|
||||
),
|
||||
"Lavan Island Airport": AirfieldData(
|
||||
"Lavan Island": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
icao="OIBV",
|
||||
elevation=75,
|
||||
runway_length=8234,
|
||||
vor=("LVA", MHz(116, 850)),
|
||||
atc=AtcData(MHz(4, 100), MHz(39, 100), MHz(128, 550), MHz(250, 700)),
|
||||
atc=AtcData(MHz(4, 150), MHz(39, 200), MHz(128, 550), MHz(250, 750)),
|
||||
),
|
||||
"Lar Airbase": AirfieldData(
|
||||
"Lar": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
icao="OISL",
|
||||
elevation=2635,
|
||||
@@ -603,7 +607,7 @@ AIRFIELD_DATA = {
|
||||
runway_length=7300,
|
||||
tacan=TacanChannel(47, TacanBand.X),
|
||||
tacan_callsign="HDR",
|
||||
atc=AtcData(MHz(4, 350), MHz(39, 600), MHz(123, 150), MHz(251, 200)),
|
||||
atc=AtcData(MHz(4, 400), MHz(39, 700), MHz(123, 150), MHz(251, 300)),
|
||||
ils={
|
||||
"8": ("IBHD", MHz(108, 900)),
|
||||
},
|
||||
@@ -616,19 +620,19 @@ AIRFIELD_DATA = {
|
||||
tacan=TacanChannel(78, TacanBand.X),
|
||||
tacan_callsign="BND",
|
||||
vor=("BND", MHz(117, 200)),
|
||||
atc=AtcData(MHz(4, 200), MHz(39, 300), MHz(118, 100), MHz(250, 900)),
|
||||
atc=AtcData(MHz(4, 250), MHz(39, 401), MHz(118, 100), MHz(251, 0)),
|
||||
ils={
|
||||
"21": ("IBND", MHz(333, 800)),
|
||||
},
|
||||
),
|
||||
"Jiroft Airport": AirfieldData(
|
||||
"Jiroft": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
icao="OIKJ",
|
||||
elevation=2664,
|
||||
runway_length=9160,
|
||||
atc=AtcData(MHz(4, 125), MHz(39, 120), MHz(136, 0), MHz(250, 750)),
|
||||
),
|
||||
"Kerman Airport": AirfieldData(
|
||||
"Kerman": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
icao="OIKK",
|
||||
elevation=5746,
|
||||
@@ -636,9 +640,9 @@ AIRFIELD_DATA = {
|
||||
tacan=TacanChannel(97, TacanBand.X),
|
||||
tacan_callsign="KER",
|
||||
vor=("KER", MHz(112, 0)),
|
||||
atc=AtcData(MHz(3, 900), MHz(38, 700), MHz(118, 250), MHz(250, 300)),
|
||||
atc=AtcData(MHz(3, 925), MHz(38, 750), MHz(118, 250), MHz(250, 300)),
|
||||
),
|
||||
"Shiraz International Airport": AirfieldData(
|
||||
"Shiraz Intl": AirfieldData(
|
||||
theater="Persian Gulf",
|
||||
icao="OISS",
|
||||
elevation=4878,
|
||||
@@ -646,7 +650,7 @@ AIRFIELD_DATA = {
|
||||
tacan=TacanChannel(94, TacanBand.X),
|
||||
tacan_callsign="SYZ1",
|
||||
vor=("SYZ", MHz(112, 0)),
|
||||
atc=AtcData(MHz(3, 925), MHz(38, 750), MHz(121, 900), MHz(250, 350)),
|
||||
atc=AtcData(MHz(3, 950), MHz(38, 800), MHz(121, 900), MHz(250, 350)),
|
||||
),
|
||||
# Syria Map
|
||||
"Adana Sakirpasa": AirfieldData(
|
||||
@@ -655,7 +659,7 @@ AIRFIELD_DATA = {
|
||||
elevation=55,
|
||||
runway_length=8115,
|
||||
vor=("ADA", MHz(112, 700)),
|
||||
atc=AtcData(MHz(4, 225), MHz(39, 350), MHz(121, 100), MHz(250, 900)),
|
||||
atc=AtcData(MHz(4, 275), MHz(39, 450), MHz(121, 100), MHz(251, 0)),
|
||||
ils={
|
||||
"05": ("IADA", MHz(108, 700)),
|
||||
},
|
||||
@@ -668,7 +672,7 @@ AIRFIELD_DATA = {
|
||||
tacan=TacanChannel(21, TacanBand.X),
|
||||
tacan_callsign="DAN",
|
||||
vor=("DAN", MHz(108, 400)),
|
||||
atc=AtcData(MHz(3, 850), MHz(38, 600), MHz(129, 400), MHz(360, 100)),
|
||||
atc=AtcData(MHz(3, 900), MHz(38, 700), MHz(122, 100), MHz(360, 100)),
|
||||
ils={
|
||||
"50": ("IDAN", MHz(109, 300)),
|
||||
"23": ("DANM", MHz(111, 700)),
|
||||
@@ -679,7 +683,7 @@ AIRFIELD_DATA = {
|
||||
icao="OS71",
|
||||
elevation=1614,
|
||||
runway_length=4648,
|
||||
atc=AtcData(MHz(4, 125), MHz(39, 150), MHz(120, 600), MHz(250, 700)),
|
||||
atc=AtcData(MHz(4, 175), MHz(39, 250), MHz(120, 600), MHz(250, 800)),
|
||||
),
|
||||
"Hatay": AirfieldData(
|
||||
theater="Syria",
|
||||
@@ -687,7 +691,7 @@ AIRFIELD_DATA = {
|
||||
elevation=253,
|
||||
runway_length=9052,
|
||||
vor=("HTY", MHz(112, 500)),
|
||||
atc=AtcData(MHz(3, 825), MHz(38, 550), MHz(128, 500), MHz(250, 150)),
|
||||
atc=AtcData(MHz(3, 875), MHz(38, 650), MHz(128, 500), MHz(250, 250)),
|
||||
ils={
|
||||
"22": ("IHTY", MHz(108, 150)),
|
||||
"04": ("IHAT", MHz(108, 900)),
|
||||
@@ -698,25 +702,21 @@ AIRFIELD_DATA = {
|
||||
icao="OS66",
|
||||
elevation=1200,
|
||||
runway_length=6662,
|
||||
atc=AtcData(MHz(4, 275), MHz(39, 450), MHz(120, 500), MHz(251)),
|
||||
atc=AtcData(MHz(4, 325), MHz(39, 550), MHz(120, 500), MHz(251, 100)),
|
||||
),
|
||||
"Aleppo": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="OSAP",
|
||||
elevation=1253,
|
||||
runway_length=8332,
|
||||
atc=AtcData(MHz(4, 150), MHz(39, 200), MHz(119, 100), MHz(250, 750)),
|
||||
ils={
|
||||
"50": ("IDAN", MHz(109, 300)),
|
||||
"23": ("DANM", MHz(111, 700)),
|
||||
},
|
||||
atc=AtcData(MHz(4, 200), MHz(39, 300), MHz(119, 100), MHz(250, 850)),
|
||||
),
|
||||
"Jirah": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="OS62",
|
||||
elevation=1170,
|
||||
runway_length=9090,
|
||||
atc=AtcData(MHz(3, 875), MHz(38, 650), MHz(118, 100), MHz(250, 200)),
|
||||
atc=AtcData(MHz(3, 925), MHz(38, 750), MHz(118, 100), MHz(250, 300)),
|
||||
),
|
||||
"Taftanaz": AirfieldData(
|
||||
theater="Syria",
|
||||
@@ -729,14 +729,14 @@ AIRFIELD_DATA = {
|
||||
icao="OS59",
|
||||
elevation=1083,
|
||||
runway_length=9036,
|
||||
atc=AtcData(MHz(4, 350), MHz(39, 600), MHz(118, 500), MHz(251, 150)),
|
||||
atc=AtcData(MHz(4, 500), MHz(39, 900), MHz(122, 800), MHz(251, 450)),
|
||||
),
|
||||
"Abu al-Dahur": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="OS57",
|
||||
elevation=820,
|
||||
runway_length=8728,
|
||||
atc=AtcData(MHz(3, 950), MHz(38, 800), MHz(122, 200), MHz(250, 350)),
|
||||
atc=AtcData(MHz(4, 0), MHz(38, 900), MHz(122, 200), MHz(250, 450)),
|
||||
),
|
||||
"Bassel Al-Assad": AirfieldData(
|
||||
theater="Syria",
|
||||
@@ -744,7 +744,7 @@ AIRFIELD_DATA = {
|
||||
elevation=93,
|
||||
runway_length=7305,
|
||||
vor=("LTK", MHz(114, 800)),
|
||||
atc=AtcData(MHz(4), MHz(38, 900), MHz(118, 100), MHz(250, 450)),
|
||||
atc=AtcData(MHz(4, 50), MHz(39, 0), MHz(118, 100), MHz(250, 550)),
|
||||
ils={
|
||||
"17": ("IBA", MHz(109, 100)),
|
||||
},
|
||||
@@ -754,28 +754,28 @@ AIRFIELD_DATA = {
|
||||
icao="OS58",
|
||||
elevation=983,
|
||||
runway_length=7957,
|
||||
atc=AtcData(MHz(3, 800), MHz(38, 500), MHz(118, 50), MHz(250, 100)),
|
||||
atc=AtcData(MHz(3, 850), MHz(38, 600), MHz(118, 50), MHz(250, 200)),
|
||||
),
|
||||
"Rene Mouawad": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="OLKA",
|
||||
elevation=14,
|
||||
runway_length=8614,
|
||||
atc=AtcData(MHz(4, 325), MHz(39, 550), MHz(129, 500), MHz(251, 100)),
|
||||
atc=AtcData(MHz(4, 375), MHz(39, 650), MHz(121, 0), MHz(251, 200)),
|
||||
),
|
||||
"Al Quasayr": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="OS70",
|
||||
elevation=1729,
|
||||
runway_length=8585,
|
||||
atc=AtcData(MHz(4, 400), MHz(39, 700), MHz(119, 200), MHz(251, 250)),
|
||||
atc=AtcData(MHz(4, 550), MHz(40, 0), MHz(119, 200), MHz(251, 550)),
|
||||
),
|
||||
"Palmyra": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="OSPR",
|
||||
elevation=1267,
|
||||
runway_length=8704,
|
||||
atc=AtcData(MHz(4, 175), MHz(39, 250), MHz(121, 900), MHz(250, 800)),
|
||||
atc=AtcData(MHz(4, 225), MHz(39, 350), MHz(121, 900), MHz(250, 900)),
|
||||
),
|
||||
"Wujah Al Hajar": AirfieldData(
|
||||
theater="Syria",
|
||||
@@ -783,14 +783,14 @@ AIRFIELD_DATA = {
|
||||
elevation=619,
|
||||
runway_length=4717,
|
||||
vor=("CAK", MHz(116, 200)),
|
||||
atc=AtcData(MHz(4, 425), MHz(39, 750), MHz(121, 500), MHz(251, 300)),
|
||||
atc=AtcData(MHz(4, 575), MHz(40, 50), MHz(121, 500), MHz(251, 600)),
|
||||
),
|
||||
"An Nasiriyah": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="OS64",
|
||||
elevation=2746,
|
||||
runway_length=8172,
|
||||
atc=AtcData(MHz(4, 450), MHz(39, 800), MHz(122, 300), MHz(251, 350)),
|
||||
atc=AtcData(MHz(4, 600), MHz(40, 100), MHz(122, 300), MHz(251, 650)),
|
||||
),
|
||||
"Rayak": AirfieldData(
|
||||
theater="Syria",
|
||||
@@ -798,7 +798,7 @@ AIRFIELD_DATA = {
|
||||
elevation=2934,
|
||||
runway_length=8699,
|
||||
vor=("HTY", MHz(124, 400)),
|
||||
atc=AtcData(MHz(4, 300), MHz(39, 500), MHz(124, 400), MHz(251, 50)),
|
||||
atc=AtcData(MHz(4, 350), MHz(39, 600), MHz(124, 400), MHz(251, 150)),
|
||||
),
|
||||
"Beirut-Rafic Hariri": AirfieldData(
|
||||
theater="Syria",
|
||||
@@ -806,7 +806,7 @@ AIRFIELD_DATA = {
|
||||
elevation=39,
|
||||
runway_length=9463,
|
||||
vor=("KAD", MHz(112, 600)),
|
||||
atc=AtcData(MHz(4, 475), MHz(39, 850), MHz(118, 900), MHz(251, 400)),
|
||||
atc=AtcData(MHz(4, 675), MHz(40, 250), MHz(118, 900), MHz(251, 800)),
|
||||
ils={
|
||||
"17": ("BIL", MHz(109, 500)),
|
||||
},
|
||||
@@ -816,32 +816,32 @@ AIRFIELD_DATA = {
|
||||
icao="OS61",
|
||||
elevation=2066,
|
||||
runway_length=8902,
|
||||
atc=AtcData(MHz(4, 550), MHz(40), MHz(120, 300), MHz(251, 550)),
|
||||
atc=AtcData(MHz(4, 750), MHz(40, 400), MHz(120, 300), MHz(251, 950)),
|
||||
),
|
||||
"Marj as Sultan North": AirfieldData(
|
||||
theater="Syria",
|
||||
elevation=2007,
|
||||
runway_length=268,
|
||||
atc=AtcData(MHz(4, 25), MHz(38, 950), MHz(122, 700), MHz(250, 500)),
|
||||
atc=AtcData(MHz(4, 75), MHz(38, 50), MHz(122, 700), MHz(250, 600)),
|
||||
),
|
||||
"Marj as Sultan South": AirfieldData(
|
||||
theater="Syria",
|
||||
elevation=2007,
|
||||
runway_length=166,
|
||||
atc=AtcData(MHz(4, 525), MHz(39, 950), MHz(122, 900), MHz(251, 500)),
|
||||
atc=AtcData(MHz(4, 725), MHz(40, 350), MHz(122, 900), MHz(251, 900)),
|
||||
),
|
||||
"Mezzeh": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="OS67",
|
||||
elevation=2355,
|
||||
runway_length=7522,
|
||||
atc=AtcData(MHz(4, 100), MHz(39, 100), MHz(120, 700), MHz(250, 650)),
|
||||
atc=AtcData(MHz(4, 150), MHz(39, 200), MHz(120, 700), MHz(250, 750)),
|
||||
),
|
||||
"Qabr as Sitt": AirfieldData(
|
||||
theater="Syria",
|
||||
elevation=2134,
|
||||
runway_length=489,
|
||||
atc=AtcData(MHz(4, 200), MHz(39, 300), MHz(122, 600), MHz(250, 850)),
|
||||
atc=AtcData(MHz(4, 250), MHz(39, 400), MHz(122, 600), MHz(250, 950)),
|
||||
),
|
||||
"Damascus": AirfieldData(
|
||||
theater="Syria",
|
||||
@@ -849,7 +849,7 @@ AIRFIELD_DATA = {
|
||||
elevation=2007,
|
||||
runway_length=11423,
|
||||
vor=("DAM", MHz(116)),
|
||||
atc=AtcData(MHz(4, 500), MHz(39, 900), MHz(118, 500), MHz(251, 450)),
|
||||
atc=AtcData(MHz(4, 700), MHz(40, 300), MHz(118, 500), MHz(251, 850)),
|
||||
ils={
|
||||
"24": ("IDA", MHz(109, 900)),
|
||||
},
|
||||
@@ -859,42 +859,42 @@ AIRFIELD_DATA = {
|
||||
icao="OS63",
|
||||
elevation=2160,
|
||||
runway_length=7576,
|
||||
atc=AtcData(MHz(4, 50), MHz(39), MHz(120, 800), MHz(250, 550)),
|
||||
atc=AtcData(MHz(4, 100), MHz(39, 100), MHz(120, 800), MHz(250, 6550)),
|
||||
),
|
||||
"Kiryat Shmona": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="LLKS",
|
||||
elevation=328,
|
||||
runway_length=3258,
|
||||
atc=AtcData(MHz(3, 975), MHz(38, 850), MHz(118, 400), MHz(250, 400)),
|
||||
atc=AtcData(MHz(4, 25), MHz(38, 950), MHz(118, 400), MHz(250, 500)),
|
||||
),
|
||||
"Khalkhalah": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="OS69",
|
||||
elevation=2337,
|
||||
runway_length=8248,
|
||||
atc=AtcData(MHz(3, 900), MHz(38, 700), MHz(122, 500), MHz(250, 250)),
|
||||
atc=AtcData(MHz(3, 950), MHz(38, 800), MHz(122, 500), MHz(250, 350)),
|
||||
),
|
||||
"Haifa": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="LLHA",
|
||||
elevation=19,
|
||||
runway_length=3253,
|
||||
atc=AtcData(MHz(3, 775), MHz(38, 450), MHz(127, 800), MHz(250, 50)),
|
||||
atc=AtcData(MHz(3, 825), MHz(38, 550), MHz(127, 800), MHz(250, 150)),
|
||||
),
|
||||
"Ramat David": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="LLRD",
|
||||
elevation=105,
|
||||
runway_length=7037,
|
||||
atc=AtcData(MHz(4, 250), MHz(39, 400), MHz(118, 600), MHz(250, 950)),
|
||||
atc=AtcData(MHz(4, 300), MHz(39, 500), MHz(118, 600), MHz(251, 50)),
|
||||
),
|
||||
"Megiddo": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="LLMG",
|
||||
elevation=180,
|
||||
runway_length=6098,
|
||||
atc=AtcData(MHz(4, 75), MHz(39, 50), MHz(119, 900), MHz(250, 600)),
|
||||
atc=AtcData(MHz(4, 125), MHz(39, 150), MHz(119, 900), MHz(250, 700)),
|
||||
),
|
||||
"Eyn Shemer": AirfieldData(
|
||||
theater="Syria",
|
||||
@@ -908,7 +908,66 @@ AIRFIELD_DATA = {
|
||||
icao="OJMF",
|
||||
elevation=2204,
|
||||
runway_length=8595,
|
||||
atc=AtcData(MHz(3, 925), MHz(38, 750), MHz(118, 300), MHz(250, 300)),
|
||||
atc=AtcData(MHz(3, 975), MHz(38, 850), MHz(118, 300), MHz(250, 400)),
|
||||
),
|
||||
"Tha'lah": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="OS60",
|
||||
elevation=2381,
|
||||
runway_length=8025,
|
||||
atc=AtcData(MHz(4, 650), MHz(40, 200), MHz(122, 400), MHz(251, 750)),
|
||||
),
|
||||
"Shayrat": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="OS60",
|
||||
elevation=2637,
|
||||
runway_length=8553,
|
||||
atc=AtcData(MHz(4, 450), MHz(39, 800), MHz(122, 200), MHz(251, 350)),
|
||||
),
|
||||
"Tiyas": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="OS72",
|
||||
elevation=1797,
|
||||
runway_length=9420,
|
||||
atc=AtcData(MHz(4, 525), MHz(39, 950), MHz(120, 500), MHz(251, 500)),
|
||||
),
|
||||
"Rosh Pina": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="LLIB",
|
||||
elevation=865,
|
||||
runway_length=2711,
|
||||
atc=AtcData(MHz(4, 400), MHz(39, 700), MHz(118, 450), MHz(251, 250)),
|
||||
),
|
||||
"Sayqal": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="OS68",
|
||||
elevation=2273,
|
||||
runway_length=8536,
|
||||
atc=AtcData(MHz(4, 425), MHz(39, 750), MHz(120, 400), MHz(251, 300)),
|
||||
),
|
||||
"H4": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="OJHR",
|
||||
elevation=2257,
|
||||
runway_length=7179,
|
||||
atc=AtcData(MHz(3, 800), MHz(38, 500), MHz(120, 400), MHz(250, 100)),
|
||||
),
|
||||
"Naqoura": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="",
|
||||
elevation=378,
|
||||
runway_length=0,
|
||||
atc=AtcData(MHz(4, 625), MHz(40, 150), MHz(122, 000), MHz(251, 700)),
|
||||
),
|
||||
"Gaziantep": AirfieldData(
|
||||
theater="Syria",
|
||||
icao="LTAJ",
|
||||
elevation=2287,
|
||||
runway_length=8871,
|
||||
atc=AtcData(MHz(3, 775), MHz(38, 450), MHz(120, 100), MHz(250, 50)),
|
||||
ils={
|
||||
"28": ("IGNP", MHz(109, 10)),
|
||||
},
|
||||
),
|
||||
# NTTR
|
||||
"Mina Airport 3Q0": AirfieldData(
|
||||
@@ -1304,55 +1363,73 @@ AIRFIELD_DATA = {
|
||||
"Detling": AirfieldData(
|
||||
theater="Channel",
|
||||
elevation=623,
|
||||
runway_length=2557,
|
||||
atc=AtcData(MHz(3, 950), MHz(118, 400), MHz(38, 800), MHz(250, 400)),
|
||||
runway_length=3482,
|
||||
atc=AtcData(MHz(4, 50), MHz(118, 600), MHz(39, 0), MHz(250, 600)),
|
||||
),
|
||||
"High Halden": AirfieldData(
|
||||
theater="Channel",
|
||||
elevation=104,
|
||||
runway_length=3296,
|
||||
atc=AtcData(MHz(3, 750), MHz(118, 800), MHz(38, 400), MHz(250, 0)),
|
||||
atc=AtcData(MHz(3, 800), MHz(118, 100), MHz(38, 500), MHz(250, 100)),
|
||||
),
|
||||
"Lympne": AirfieldData(
|
||||
theater="Channel",
|
||||
elevation=351,
|
||||
runway_length=2548,
|
||||
atc=AtcData(MHz(3, 925), MHz(118, 350), MHz(38, 750), MHz(250, 350)),
|
||||
runway_length=3054,
|
||||
atc=AtcData(MHz(4, 25), MHz(118, 550), MHz(38, 950), MHz(250, 550)),
|
||||
),
|
||||
"Hawkinge": AirfieldData(
|
||||
theater="Channel",
|
||||
elevation=524,
|
||||
runway_length=3013,
|
||||
atc=AtcData(MHz(3, 900), MHz(118, 300), MHz(38, 700), MHz(250, 300)),
|
||||
atc=AtcData(MHz(4, 0), MHz(118, 500), MHz(38, 900), MHz(250, 500)),
|
||||
),
|
||||
"Manston": AirfieldData(
|
||||
theater="Channel",
|
||||
elevation=160,
|
||||
runway_length=8626,
|
||||
atc=AtcData(MHz(3, 875), MHz(118, 250), MHz(38, 650), MHz(250, 250)),
|
||||
atc=AtcData(MHz(3, 975), MHz(118, 250), MHz(38, 650), MHz(250, 250)),
|
||||
),
|
||||
"Dunkirk Mardyck": AirfieldData(
|
||||
theater="Channel",
|
||||
elevation=16,
|
||||
runway_length=1737,
|
||||
atc=AtcData(MHz(3, 850), MHz(118, 200), MHz(38, 600), MHz(250, 200)),
|
||||
atc=AtcData(MHz(3, 950), MHz(118, 450), MHz(38, 850), MHz(250, 450)),
|
||||
),
|
||||
"Saint Omer Longuenesse": AirfieldData(
|
||||
theater="Channel",
|
||||
elevation=219,
|
||||
runway_length=1929,
|
||||
atc=AtcData(MHz(3, 825), MHz(118, 150), MHz(38, 550), MHz(250, 150)),
|
||||
atc=AtcData(MHz(3, 925), MHz(118, 350), MHz(38, 750), MHz(250, 350)),
|
||||
),
|
||||
"Merville Calonne": AirfieldData(
|
||||
theater="Channel",
|
||||
elevation=52,
|
||||
runway_length=7580,
|
||||
atc=AtcData(MHz(3, 800), MHz(118, 100), MHz(38, 500), MHz(250, 100)),
|
||||
atc=AtcData(MHz(3, 900), MHz(118, 300), MHz(38, 700), MHz(250, 300)),
|
||||
),
|
||||
"Abbeville Drucat": AirfieldData(
|
||||
theater="Channel",
|
||||
elevation=183,
|
||||
runway_length=4726,
|
||||
atc=AtcData(MHz(3, 875), MHz(118, 250), MHz(38, 650), MHz(250, 250)),
|
||||
),
|
||||
"Eastchurch": AirfieldData(
|
||||
theater="Channel",
|
||||
elevation=30,
|
||||
runway_length=2983,
|
||||
atc=AtcData(MHz(3, 775), MHz(118, 50), MHz(38, 450), MHz(250, 50)),
|
||||
),
|
||||
"Headcorn": AirfieldData(
|
||||
theater="Channel",
|
||||
elevation=114,
|
||||
runway_length=3680,
|
||||
atc=AtcData(MHz(3, 825), MHz(118, 150), MHz(38, 550), MHz(250, 150)),
|
||||
),
|
||||
"Biggin Hill": AirfieldData(
|
||||
theater="Channel",
|
||||
elevation=552,
|
||||
runway_length=3953,
|
||||
atc=AtcData(MHz(3, 850), MHz(118, 200), MHz(38, 600), MHz(250, 200)),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ class EnvironmentGenerator:
|
||||
self.mission.weather.clouds_thickness = clouds.thickness
|
||||
self.mission.weather.clouds_density = clouds.density
|
||||
self.mission.weather.clouds_iprecptns = clouds.precipitation
|
||||
self.mission.weather.clouds_preset = clouds.preset
|
||||
|
||||
def set_fog(self, fog: Optional[Fog]) -> None:
|
||||
if fog is None:
|
||||
|
||||
@@ -450,21 +450,31 @@ class ObjectiveFinder:
|
||||
c for c in self.game.theater.controlpoints if c.is_friendly(self.is_player)
|
||||
)
|
||||
|
||||
def farthest_friendly_control_point(self) -> ControlPoint:
|
||||
def farthest_friendly_control_point(self) -> Optional[ControlPoint]:
|
||||
"""
|
||||
Iterates over all friendly control points and find the one farthest away from the frontline
|
||||
BUT! prefer Cvs. Everybody likes CVs!
|
||||
"""
|
||||
from_frontline = 0
|
||||
cp = None
|
||||
first_friendly_cp = None
|
||||
|
||||
for c in self.game.theater.controlpoints:
|
||||
if c.is_carrier and c.is_friendly(self.is_player):
|
||||
return c
|
||||
if c.is_friendly(self.is_player) and c.has_frontline:
|
||||
if c.distance_to(self.front_lines().__next__()) > from_frontline:
|
||||
from_frontline = c.distance_to(self.front_lines().__next__())
|
||||
cp = c
|
||||
return cp
|
||||
if c.is_friendly(self.is_player):
|
||||
if first_friendly_cp is None:
|
||||
first_friendly_cp = c
|
||||
if c.is_carrier:
|
||||
return c
|
||||
if c.has_active_frontline:
|
||||
if c.distance_to(self.front_lines().__next__()) > from_frontline:
|
||||
from_frontline = c.distance_to(self.front_lines().__next__())
|
||||
cp = c
|
||||
|
||||
# If no frontlines on the map, return the first friendly cp
|
||||
if cp is None:
|
||||
return first_friendly_cp
|
||||
else:
|
||||
return cp
|
||||
|
||||
def enemy_control_points(self) -> Iterator[ControlPoint]:
|
||||
"""Iterates over all enemy control points."""
|
||||
@@ -546,9 +556,10 @@ class CoalitionMissionPlanner:
|
||||
|
||||
# Find farthest, friendly CP for AEWC
|
||||
cp = self.objective_finder.farthest_friendly_control_point()
|
||||
yield ProposedMission(
|
||||
cp, [ProposedFlight(FlightType.AEWC, 1, self.MAX_AWEC_RANGE)]
|
||||
)
|
||||
if cp is not None:
|
||||
yield ProposedMission(
|
||||
cp, [ProposedFlight(FlightType.AEWC, 1, self.MAX_AWEC_RANGE)]
|
||||
)
|
||||
|
||||
# Find friendly CPs within 100 nmi from an enemy airfield, plan CAP.
|
||||
for cp in self.objective_finder.vulnerable_control_points():
|
||||
@@ -579,9 +590,23 @@ class CoalitionMissionPlanner:
|
||||
front_line,
|
||||
[
|
||||
ProposedFlight(FlightType.CAS, 2, self.MAX_CAS_RANGE),
|
||||
ProposedFlight(
|
||||
FlightType.TARCAP, 2, self.MAX_CAP_RANGE, EscortType.AirToAir
|
||||
),
|
||||
# This is *not* an escort because front lines don't create a threat
|
||||
# zone. Generating threat zones from front lines causes the front
|
||||
# line to push back BARCAPs as it gets closer to the base. While
|
||||
# front lines do have the same problem of potentially pulling
|
||||
# BARCAPs off bases to engage a front line TARCAP, that's probably
|
||||
# the one time where we do want that.
|
||||
#
|
||||
# TODO: Use intercepts and extra TARCAPs to cover bases near fronts.
|
||||
# We don't have intercept missions yet so this isn't something we
|
||||
# can do today, but we should probably return to having the front
|
||||
# line project a threat zone (so that strike missions will route
|
||||
# around it) and instead *not plan* a BARCAP at bases near the
|
||||
# front, since there isn't a place to put a barrier. Instead, the
|
||||
# aircraft that would have been a BARCAP could be used as additional
|
||||
# interceptors and TARCAPs which will defend the base but won't be
|
||||
# trying to avoid front line contacts.
|
||||
ProposedFlight(FlightType.TARCAP, 2, self.MAX_CAP_RANGE),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ TYPE_ARTILLERY = [
|
||||
Artillery.MLRS_M270_227mm,
|
||||
Artillery.SPH_2S9_Nona_120mm_M,
|
||||
Artillery.SPH_Dana_vz77_152mm,
|
||||
Artillery.PLZ_05,
|
||||
Artillery.SPH_2S19_Msta_152mm,
|
||||
Artillery.MLRS_9A52_Smerch_CM_300mm,
|
||||
# WW2
|
||||
@@ -175,6 +176,7 @@ TYPE_SHORAD = [
|
||||
AirDefence.AAA_8_8cm_Flak_37,
|
||||
AirDefence.AAA_8_8cm_Flak_41,
|
||||
AirDefence.AAA_40mm_Bofors,
|
||||
AirDefence.AAA_S_60_57mm,
|
||||
AirDefence.AAA_M1_37mm,
|
||||
AirDefence.AAA_QF_3_7,
|
||||
]
|
||||
|
||||
@@ -68,7 +68,8 @@ class MizDataLocationFinder:
|
||||
for vehicle_group in m.country("Iran").vehicle_group:
|
||||
if (
|
||||
len(vehicle_group.units) > 0
|
||||
and vehicle_group.units[0].type == MissilesSS.AShM_SS_N_2_Silkworm.id
|
||||
and vehicle_group.units[0].type
|
||||
== MissilesSS.AShM_SS_N_2_Silkworm.id
|
||||
):
|
||||
antiship_locations.append(
|
||||
PresetLocation(
|
||||
|
||||
@@ -12,13 +12,13 @@ from gen.sam.group_generator import GroupGenerator
|
||||
class EarlyColdWarFlakGenerator(AirDefenseGroupGenerator):
|
||||
"""
|
||||
This generator attempt to mimic an early cold-war era flak AAA site.
|
||||
The Flak 18 88mm is used as the main long range gun and 2 Bofors 40mm guns provide short range protection.
|
||||
The Flak 18 88mm is used as the main long range gun, S-60 is used as a mid range gun and 2 Bofors 40mm guns provide short range protection.
|
||||
|
||||
This does not include search lights and telemeter computer (Kdo.G 40) because these are paid units only available in WW2 asset pack
|
||||
"""
|
||||
|
||||
name = "Early Cold War Flak Site"
|
||||
price = 58
|
||||
price = 74
|
||||
|
||||
def generate(self):
|
||||
|
||||
@@ -37,22 +37,38 @@ class EarlyColdWarFlakGenerator(AirDefenseGroupGenerator):
|
||||
self.heading,
|
||||
)
|
||||
|
||||
# Short range guns
|
||||
# Medium range guns
|
||||
self.add_unit(
|
||||
AirDefence.AAA_40mm_Bofors,
|
||||
AirDefence.AAA_S_60_57mm,
|
||||
"SHO#1",
|
||||
self.position.x - 40,
|
||||
self.position.y - 40,
|
||||
self.heading + 180,
|
||||
),
|
||||
self.add_unit(
|
||||
AirDefence.AAA_40mm_Bofors,
|
||||
AirDefence.AAA_S_60_57mm,
|
||||
"SHO#2",
|
||||
self.position.x + spacing * 2 + 40,
|
||||
self.position.y + spacing + 40,
|
||||
self.heading,
|
||||
),
|
||||
|
||||
# Short range guns
|
||||
self.add_unit(
|
||||
AirDefence.AAA_ZU_23_Closed_Emplacement,
|
||||
"SHO#3",
|
||||
self.position.x - 80,
|
||||
self.position.y - 40,
|
||||
self.heading + 180,
|
||||
),
|
||||
self.add_unit(
|
||||
AirDefence.AAA_ZU_23_Closed_Emplacement,
|
||||
"SHO#4",
|
||||
self.position.x + spacing * 2 + 80,
|
||||
self.position.y + spacing + 40,
|
||||
self.heading,
|
||||
),
|
||||
|
||||
# Add a truck
|
||||
self.add_unit(
|
||||
Unarmed.Truck_KAMAZ_43101,
|
||||
@@ -70,7 +86,7 @@ class EarlyColdWarFlakGenerator(AirDefenseGroupGenerator):
|
||||
class ColdWarFlakGenerator(AirDefenseGroupGenerator):
|
||||
"""
|
||||
This generator attempt to mimic a cold-war era flak AAA site.
|
||||
The Flak 18 88mm is used as the main long range gun while 2 Zu-23 guns provide short range protection.
|
||||
The Flak 18 88mm is used as the main long range gun, 2 S-60 57mm gun improve mid range firepower, while 2 Zu-23 guns even provide short range protection.
|
||||
The site is also fitted with a P-19 radar for early detection.
|
||||
"""
|
||||
|
||||
@@ -94,22 +110,38 @@ class ColdWarFlakGenerator(AirDefenseGroupGenerator):
|
||||
self.heading,
|
||||
)
|
||||
|
||||
# Short range guns
|
||||
# Medium range guns
|
||||
self.add_unit(
|
||||
AirDefence.AAA_ZU_23_Closed_Emplacement,
|
||||
AirDefence.AAA_S_60_57mm,
|
||||
"SHO#1",
|
||||
self.position.x - 40,
|
||||
self.position.y - 40,
|
||||
self.heading + 180,
|
||||
),
|
||||
self.add_unit(
|
||||
AirDefence.AAA_ZU_23_Closed_Emplacement,
|
||||
AirDefence.AAA_S_60_57mm,
|
||||
"SHO#2",
|
||||
self.position.x + spacing * 2 + 40,
|
||||
self.position.y + spacing + 40,
|
||||
self.heading,
|
||||
),
|
||||
|
||||
# Short range guns
|
||||
self.add_unit(
|
||||
AirDefence.AAA_ZU_23_Closed_Emplacement,
|
||||
"SHO#3",
|
||||
self.position.x - 80,
|
||||
self.position.y - 40,
|
||||
self.heading + 180,
|
||||
),
|
||||
self.add_unit(
|
||||
AirDefence.AAA_ZU_23_Closed_Emplacement,
|
||||
"SHO#4",
|
||||
self.position.x + spacing * 2 + 80,
|
||||
self.position.y + spacing + 40,
|
||||
self.heading,
|
||||
),
|
||||
|
||||
# Add a P19 Radar for EWR
|
||||
self.add_unit(
|
||||
AirDefence.SAM_P19_Flat_Face_SR__SA_2_3,
|
||||
|
||||
63
gen/sam/ewr_group_generator.py
Normal file
63
gen/sam/ewr_group_generator.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import random
|
||||
from typing import List, Optional, Type
|
||||
|
||||
from dcs.unitgroup import VehicleGroup
|
||||
|
||||
from game import Game
|
||||
from game.factions.faction import Faction
|
||||
from game.theater.theatergroundobject import EwrGroundObject
|
||||
from gen.sam.ewrs import (
|
||||
BigBirdGenerator,
|
||||
BoxSpringGenerator,
|
||||
DogEarGenerator,
|
||||
FlatFaceGenerator,
|
||||
HawkEwrGenerator,
|
||||
PatriotEwrGenerator,
|
||||
RolandEwrGenerator,
|
||||
SnowDriftGenerator,
|
||||
StraightFlushGenerator,
|
||||
TallRackGenerator,
|
||||
)
|
||||
from gen.sam.group_generator import GroupGenerator
|
||||
|
||||
EWR_MAP = {
|
||||
"BoxSpringGenerator": BoxSpringGenerator,
|
||||
"TallRackGenerator": TallRackGenerator,
|
||||
"DogEarGenerator": DogEarGenerator,
|
||||
"RolandEwrGenerator": RolandEwrGenerator,
|
||||
"FlatFaceGenerator": FlatFaceGenerator,
|
||||
"PatriotEwrGenerator": PatriotEwrGenerator,
|
||||
"BigBirdGenerator": BigBirdGenerator,
|
||||
"SnowDriftGenerator": SnowDriftGenerator,
|
||||
"StraightFlushGenerator": StraightFlushGenerator,
|
||||
"HawkEwrGenerator": HawkEwrGenerator,
|
||||
}
|
||||
|
||||
|
||||
def get_faction_possible_ewrs_generator(
|
||||
faction: Faction,
|
||||
) -> List[Type[GroupGenerator]]:
|
||||
"""
|
||||
Return the list of possible EWR generators for the given faction
|
||||
:param faction: Faction name to search units for
|
||||
"""
|
||||
return [EWR_MAP[s] for s in faction.ewrs]
|
||||
|
||||
|
||||
def generate_ewr_group(
|
||||
game: Game, ground_object: EwrGroundObject, faction: Faction
|
||||
) -> Optional[VehicleGroup]:
|
||||
"""Generates an early warning radar group.
|
||||
|
||||
:param game: The Game.
|
||||
:param ground_object: The ground object which will own the EWR group.
|
||||
:param faction: Owner faction.
|
||||
:return: The generated group, or None if one could not be generated.
|
||||
"""
|
||||
generators = get_faction_possible_ewrs_generator(faction)
|
||||
if len(generators) > 0:
|
||||
generator_class = random.choice(generators)
|
||||
generator = generator_class(game, ground_object)
|
||||
generator.generate()
|
||||
return generator.get_generated_group()
|
||||
return None
|
||||
@@ -6,7 +6,6 @@ from dcs.vehicles import AirDefence
|
||||
|
||||
from game import Game
|
||||
from game.factions.faction import Faction
|
||||
from game.theater import TheaterGroundObject
|
||||
from game.theater.theatergroundobject import SamGroundObject
|
||||
from gen.sam.aaa_bofors import BoforsGenerator
|
||||
from gen.sam.aaa_flak import FlakGenerator
|
||||
@@ -23,20 +22,7 @@ from gen.sam.cold_war_flak import (
|
||||
ColdWarFlakGenerator,
|
||||
EarlyColdWarFlakGenerator,
|
||||
)
|
||||
from gen.sam.ewrs import (
|
||||
BigBirdGenerator,
|
||||
BoxSpringGenerator,
|
||||
DogEarGenerator,
|
||||
FlatFaceGenerator,
|
||||
HawkEwrGenerator,
|
||||
PatriotEwrGenerator,
|
||||
RolandEwrGenerator,
|
||||
SnowDriftGenerator,
|
||||
StraightFlushGenerator,
|
||||
TallRackGenerator,
|
||||
)
|
||||
from gen.sam.freya_ewr import FreyaGenerator
|
||||
from gen.sam.group_generator import GroupGenerator
|
||||
from gen.sam.sam_avenger import AvengerGenerator
|
||||
from gen.sam.sam_chaparral import ChaparralGenerator
|
||||
from gen.sam.sam_gepard import GepardGenerator
|
||||
@@ -152,19 +138,6 @@ SAM_PRICES = {
|
||||
AirDefence.HQ_7_Self_Propelled_LN: 35,
|
||||
}
|
||||
|
||||
EWR_MAP = {
|
||||
"BoxSpringGenerator": BoxSpringGenerator,
|
||||
"TallRackGenerator": TallRackGenerator,
|
||||
"DogEarGenerator": DogEarGenerator,
|
||||
"RolandEwrGenerator": RolandEwrGenerator,
|
||||
"FlatFaceGenerator": FlatFaceGenerator,
|
||||
"PatriotEwrGenerator": PatriotEwrGenerator,
|
||||
"BigBirdGenerator": BigBirdGenerator,
|
||||
"SnowDriftGenerator": SnowDriftGenerator,
|
||||
"StraightFlushGenerator": StraightFlushGenerator,
|
||||
"HawkEwrGenerator": HawkEwrGenerator,
|
||||
}
|
||||
|
||||
|
||||
def get_faction_possible_sams_generator(
|
||||
faction: Faction,
|
||||
@@ -176,14 +149,6 @@ def get_faction_possible_sams_generator(
|
||||
return [SAM_MAP[s] for s in faction.air_defenses]
|
||||
|
||||
|
||||
def get_faction_possible_ewrs_generator(faction: Faction) -> List[Type[GroupGenerator]]:
|
||||
"""
|
||||
Return the list of possible SAM generator for the given faction
|
||||
:param faction: Faction name to search units for
|
||||
"""
|
||||
return [EWR_MAP[s] for s in faction.ewrs]
|
||||
|
||||
|
||||
def _generate_anti_air_from(
|
||||
generators: Sequence[Type[AirDefenseGroupGenerator]],
|
||||
game: Game,
|
||||
@@ -236,22 +201,3 @@ def generate_anti_air_group(
|
||||
if groups:
|
||||
return groups
|
||||
return []
|
||||
|
||||
|
||||
def generate_ewr_group(
|
||||
game: Game, ground_object: TheaterGroundObject, faction: Faction
|
||||
) -> Optional[VehicleGroup]:
|
||||
"""Generates an early warning radar group.
|
||||
|
||||
:param game: The Game.
|
||||
:param ground_object: The ground object which will own the EWR group.
|
||||
:param faction: Owner faction.
|
||||
:return: The generated group, or None if one could not be generated.
|
||||
"""
|
||||
generators = get_faction_possible_ewrs_generator(faction)
|
||||
if len(generators) > 0:
|
||||
generator_class = random.choice(generators)
|
||||
generator = generator_class(game, ground_object)
|
||||
generator.generate()
|
||||
return generator.get_generated_group()
|
||||
return None
|
||||
|
||||
2
pydcs
2
pydcs
Submodule pydcs updated: d899985e28...cd14f0a049
@@ -103,7 +103,9 @@ class DisplayOptions:
|
||||
waypoint_info = DisplayRule("Waypoint Information", True)
|
||||
culling = DisplayRule("Display Culling Zones", False)
|
||||
actual_frontline_pos = DisplayRule("Display Actual Frontline Location", False)
|
||||
barcap_commit_range = DisplayRule("Display selected BARCAP commit range", False)
|
||||
patrol_engagement_range = DisplayRule(
|
||||
"Display selected patrol engagement range", True
|
||||
)
|
||||
flight_paths = FlightPathOptions()
|
||||
blue_threat_zones = ThreatZoneOptions("Blue")
|
||||
red_threat_zones = ThreatZoneOptions("Red")
|
||||
|
||||
@@ -147,6 +147,8 @@ def load_icons():
|
||||
"./resources/ui/ground_assets/" + category + "_blue.png"
|
||||
)
|
||||
ICONS["destroyed"] = QPixmap("./resources/ui/ground_assets/destroyed.png")
|
||||
ICONS["EWR"] = QPixmap("./resources/ui/ground_assets/ewr.png")
|
||||
ICONS["EWR_blue"] = QPixmap("./resources/ui/ground_assets/ewr_blue.png")
|
||||
ICONS["ship"] = QPixmap("./resources/ui/ground_assets/ship.png")
|
||||
ICONS["ship_blue"] = QPixmap("./resources/ui/ground_assets/ship_blue.png")
|
||||
ICONS["missile"] = QPixmap("./resources/ui/ground_assets/missile.png")
|
||||
|
||||
@@ -7,7 +7,7 @@ from PySide2.QtWidgets import (
|
||||
QLabel,
|
||||
QVBoxLayout,
|
||||
)
|
||||
from dcs.weather import Weather as PydcsWeather
|
||||
from dcs.weather import CloudPreset, Weather as PydcsWeather
|
||||
|
||||
import qt_ui.uiconstants as CONST
|
||||
from game.utils import mps
|
||||
@@ -162,7 +162,7 @@ class QWeatherWidget(QGroupBox):
|
||||
self.turn = turn
|
||||
self.conditions = conditions
|
||||
|
||||
self.updateForecast()
|
||||
self.update_forecast()
|
||||
self.updateWinds()
|
||||
|
||||
def updateWinds(self):
|
||||
@@ -186,55 +186,76 @@ class QWeatherWidget(QGroupBox):
|
||||
self.windFL26SpeedLabel.setText(f"{int(windFL26Speed.knots)}kts")
|
||||
self.windFL26DirLabel.setText(f"{windFL26Dir}º")
|
||||
|
||||
def updateForecast(self):
|
||||
def update_forecast_from_preset(self, preset: CloudPreset) -> None:
|
||||
self.forecastFog.setText("No fog")
|
||||
if "Rain" in preset.name:
|
||||
self.forecastRain.setText("Rain")
|
||||
self.update_forecast_icons("rain")
|
||||
else:
|
||||
self.forecastRain.setText("No rain")
|
||||
self.update_forecast_icons("partly-cloudy")
|
||||
|
||||
# We get a description like the following for the cloud preset.
|
||||
#
|
||||
# 09 ##Two Layer Broken/Scattered \nMETAR:BKN 7.5/10 SCT 20/22 FEW41
|
||||
#
|
||||
# The second line is probably interesting but doesn't fit into the widget
|
||||
# currently, so for now just extract the first line.
|
||||
self.forecastClouds.setText(preset.description.splitlines()[0].split("##")[1])
|
||||
|
||||
def update_forecast(self):
|
||||
"""Updates the Forecast Text and icon with the current conditions wind info."""
|
||||
icon = []
|
||||
if (
|
||||
self.conditions.weather.clouds
|
||||
and self.conditions.weather.clouds.preset is not None
|
||||
):
|
||||
self.update_forecast_from_preset(self.conditions.weather.clouds.preset)
|
||||
return
|
||||
|
||||
if self.conditions.weather.clouds is None:
|
||||
cloudDensity = 0
|
||||
cloud_density = 0
|
||||
precipitation = None
|
||||
else:
|
||||
cloudDensity = self.conditions.weather.clouds.density
|
||||
cloud_density = self.conditions.weather.clouds.density
|
||||
precipitation = self.conditions.weather.clouds.precipitation
|
||||
|
||||
fog = self.conditions.weather.fog or None
|
||||
is_night = self.conditions.time_of_day == TimeOfDay.Night
|
||||
time = "night" if is_night else "day"
|
||||
|
||||
if cloudDensity <= 0:
|
||||
if not cloud_density:
|
||||
self.forecastClouds.setText("Sunny")
|
||||
icon = [time, "clear"]
|
||||
|
||||
if cloudDensity > 0 and cloudDensity < 3:
|
||||
weather_type = "clear"
|
||||
elif cloud_density < 3:
|
||||
self.forecastClouds.setText("Partly Cloudy")
|
||||
icon = [time, "partly-cloudy"]
|
||||
|
||||
if cloudDensity >= 3 and cloudDensity < 5:
|
||||
weather_type = "partly-cloudy"
|
||||
elif cloud_density < 5:
|
||||
self.forecastClouds.setText("Mostly Cloudy")
|
||||
icon = [time, "partly-cloudy"]
|
||||
|
||||
if cloudDensity >= 5:
|
||||
weather_type = "partly-cloudy"
|
||||
else:
|
||||
self.forecastClouds.setText("Totally Cloudy")
|
||||
icon = [time, "partly-cloudy"]
|
||||
weather_type = "partly-cloudy"
|
||||
|
||||
if precipitation == PydcsWeather.Preceptions.Rain:
|
||||
self.forecastRain.setText("Rain")
|
||||
icon = [time, "rain"]
|
||||
|
||||
weather_type = "rain"
|
||||
elif precipitation == PydcsWeather.Preceptions.Thunderstorm:
|
||||
self.forecastRain.setText("Thunderstorm")
|
||||
icon = [time, "thunderstorm"]
|
||||
|
||||
weather_type = "thunderstorm"
|
||||
else:
|
||||
self.forecastRain.setText("No Rain")
|
||||
self.forecastRain.setText("No rain")
|
||||
|
||||
if not fog:
|
||||
if not self.conditions.weather.fog is not None:
|
||||
self.forecastFog.setText("No fog")
|
||||
else:
|
||||
visibility = round(fog.visibility.nautical_miles, 1)
|
||||
visibility = round(self.conditions.weather.fog.visibility.nautical_miles, 1)
|
||||
self.forecastFog.setText(f"Fog vis: {visibility}nm")
|
||||
icon = [time, ("cloudy" if cloudDensity > 1 else None), "fog"]
|
||||
if cloud_density > 1:
|
||||
weather_type = "cloudy-fog"
|
||||
else:
|
||||
weather_type = "fog"
|
||||
|
||||
icon_key = "Weather_{}".format("-".join(filter(None.__ne__, icon)))
|
||||
self.update_forecast_icons(weather_type)
|
||||
|
||||
def update_forecast_icons(self, weather_type: str) -> None:
|
||||
time = "night" if self.conditions.time_of_day == TimeOfDay.Night else "day"
|
||||
icon_key = f"Weather_{time}-{weather_type}"
|
||||
icon = CONST.ICONS.get(icon_key) or CONST.ICONS["Weather_night-partly-cloudy"]
|
||||
self.weather_icon.setPixmap(icon)
|
||||
|
||||
|
||||
@@ -58,6 +58,8 @@ from gen.flights.flightplan import (
|
||||
FlightPlan,
|
||||
FlightPlanBuilder,
|
||||
InvalidObjectiveLocation,
|
||||
PatrollingFlightPlan,
|
||||
TarCapFlightPlan,
|
||||
)
|
||||
from gen.flights.traveltime import TotEstimator
|
||||
from qt_ui.displayoptions import DisplayOptions, ThreatZoneOptions
|
||||
@@ -721,13 +723,11 @@ class QLiberationMap(QGraphicsView):
|
||||
)
|
||||
prev_pos = tuple(new_pos)
|
||||
|
||||
if selected and DisplayOptions.barcap_commit_range:
|
||||
self.draw_barcap_commit_range(scene, flight)
|
||||
if selected and DisplayOptions.patrol_engagement_range:
|
||||
self.draw_patrol_commit_range(scene, flight)
|
||||
|
||||
def draw_barcap_commit_range(self, scene: QGraphicsScene, flight: Flight) -> None:
|
||||
if flight.flight_type is not FlightType.BARCAP:
|
||||
return
|
||||
if not isinstance(flight.flight_plan, BarCapFlightPlan):
|
||||
def draw_patrol_commit_range(self, scene: QGraphicsScene, flight: Flight) -> None:
|
||||
if not isinstance(flight.flight_plan, PatrollingFlightPlan):
|
||||
return
|
||||
start = flight.flight_plan.patrol_start
|
||||
end = flight.flight_plan.patrol_end
|
||||
|
||||
@@ -172,6 +172,8 @@ class QBaseMenu2(QDialog):
|
||||
return "./resources/ui/carrier.png"
|
||||
elif self.cp.cptype == ControlPointType.LHA_GROUP:
|
||||
return "./resources/ui/lha.png"
|
||||
elif self.cp.cptype == ControlPointType.FOB:
|
||||
return "./resources/ui/fob.png"
|
||||
else:
|
||||
return "./resources/ui/airbase.png"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from PySide2.QtWidgets import QTabWidget
|
||||
|
||||
from game.theater import ControlPoint, OffMapSpawn
|
||||
from game.theater import ControlPoint, OffMapSpawn, Fob
|
||||
from qt_ui.models import GameModel
|
||||
from qt_ui.windows.basemenu.airfield.QAirfieldCommand import QAirfieldCommand
|
||||
from qt_ui.windows.basemenu.base_defenses.QBaseDefensesHQ import QBaseDefensesHQ
|
||||
@@ -19,14 +19,26 @@ class QBaseMenuTabs(QTabWidget):
|
||||
self.intel = QIntelInfo(cp, game_model.game)
|
||||
self.addTab(self.intel, "Intel")
|
||||
else:
|
||||
self.airfield_command = QAirfieldCommand(cp, game_model)
|
||||
self.addTab(self.airfield_command, "Airfield Command")
|
||||
|
||||
if cp.is_carrier:
|
||||
self.base_defenses_hq = QBaseDefensesHQ(cp, game_model.game)
|
||||
self.addTab(self.base_defenses_hq, "Fleet")
|
||||
elif not isinstance(cp, OffMapSpawn):
|
||||
self.ground_forces_hq = QGroundForcesHQ(cp, game_model)
|
||||
self.addTab(self.ground_forces_hq, "Ground Forces HQ")
|
||||
self.base_defenses_hq = QBaseDefensesHQ(cp, game_model.game)
|
||||
self.addTab(self.base_defenses_hq, "Base Defenses")
|
||||
if cp:
|
||||
if isinstance(cp, Fob):
|
||||
self.ground_forces_hq = QGroundForcesHQ(cp, game_model)
|
||||
self.addTab(self.ground_forces_hq, "Ground Forces HQ")
|
||||
if cp.helipads:
|
||||
self.airfield_command = QAirfieldCommand(cp, game_model)
|
||||
self.addTab(self.airfield_command, "Heliport")
|
||||
self.base_defenses_hq = QBaseDefensesHQ(cp, game_model.game)
|
||||
self.addTab(self.base_defenses_hq, "Base Defenses")
|
||||
else:
|
||||
|
||||
self.airfield_command = QAirfieldCommand(cp, game_model)
|
||||
self.addTab(self.airfield_command, "Airfield Command")
|
||||
|
||||
if cp.is_carrier:
|
||||
self.base_defenses_hq = QBaseDefensesHQ(cp, game_model.game)
|
||||
self.addTab(self.base_defenses_hq, "Fleet")
|
||||
elif not isinstance(cp, OffMapSpawn):
|
||||
self.ground_forces_hq = QGroundForcesHQ(cp, game_model)
|
||||
self.addTab(self.ground_forces_hq, "Ground Forces HQ")
|
||||
self.base_defenses_hq = QBaseDefensesHQ(cp, game_model.game)
|
||||
self.addTab(self.base_defenses_hq, "Base Defenses")
|
||||
|
||||
@@ -12,11 +12,12 @@ from PySide2.QtWidgets import (
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
)
|
||||
from dcs.helicopters import helicopter_map
|
||||
from dcs.task import CAP, CAS, AWACS
|
||||
from dcs.unittype import FlyingType, UnitType
|
||||
|
||||
from game import db
|
||||
from game.theater import ControlPoint
|
||||
from game.theater import ControlPoint, ControlPointType
|
||||
from qt_ui.models import GameModel
|
||||
from qt_ui.uiconstants import ICONS
|
||||
from qt_ui.windows.basemenu.QRecruitBehaviour import QRecruitBehaviour
|
||||
@@ -63,6 +64,11 @@ class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour):
|
||||
continue
|
||||
if self.cp.is_lha and unit not in db.LHA_CAPABLE:
|
||||
continue
|
||||
if (
|
||||
self.cp.cptype in [ControlPointType.FOB, ControlPointType.FARP]
|
||||
and unit not in helicopter_map.values()
|
||||
):
|
||||
continue
|
||||
unit_types.add(unit)
|
||||
|
||||
sorted_units = sorted(
|
||||
|
||||
@@ -43,4 +43,4 @@ class QLoadoutEditor(QGroupBox):
|
||||
self.flight.use_custom_loadout = self.isChecked()
|
||||
if not self.isChecked():
|
||||
for i in self.findChildren(QPylonEditor):
|
||||
i.default_loadout(i.pylon.number)
|
||||
i.default_loadout()
|
||||
|
||||
@@ -223,7 +223,7 @@ class FactionSelection(QtWidgets.QWizardPage):
|
||||
self.redFactionSelect.activated.connect(self.updateUnitRecap)
|
||||
|
||||
def setDefaultFactions(self, campaign: Campaign):
|
||||
""" Set default faction for selected campaign """
|
||||
"""Set default faction for selected campaign"""
|
||||
|
||||
self.blueFactionSelect.clear()
|
||||
self.redFactionSelect.clear()
|
||||
|
||||
@@ -213,14 +213,14 @@ class QSettingsWindow(QDialog):
|
||||
|
||||
self.player_income = TenthsSpinSlider(
|
||||
"Player income multiplier",
|
||||
1,
|
||||
0,
|
||||
50,
|
||||
int(self.game.settings.player_income_multiplier * 10),
|
||||
)
|
||||
self.player_income.spinner.valueChanged.connect(self.applySettings)
|
||||
self.enemy_income = TenthsSpinSlider(
|
||||
"Enemy income multiplier",
|
||||
1,
|
||||
0,
|
||||
50,
|
||||
int(self.game.settings.enemy_income_multiplier * 10),
|
||||
)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -17,26 +17,22 @@ local unitPayloads = {
|
||||
["num"] = 6,
|
||||
},
|
||||
[4] = {
|
||||
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
|
||||
["CLSID"] = "{AN_ASQ_228}",
|
||||
["num"] = 4,
|
||||
},
|
||||
[5] = {
|
||||
["CLSID"] = "{A111396E-D3E8-4b9c-8AC9-2432489304D5}",
|
||||
["num"] = 5,
|
||||
},
|
||||
[6] = {
|
||||
["CLSID"] = "LAU_117_AGM_65F",
|
||||
["num"] = 3,
|
||||
},
|
||||
[7] = {
|
||||
[6] = {
|
||||
["CLSID"] = "LAU_117_AGM_65F",
|
||||
["num"] = 2,
|
||||
},
|
||||
[8] = {
|
||||
[7] = {
|
||||
["CLSID"] = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}",
|
||||
["num"] = 1,
|
||||
},
|
||||
[9] = {
|
||||
[8] = {
|
||||
["CLSID"] = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}",
|
||||
["num"] = 9,
|
||||
},
|
||||
@@ -61,26 +57,22 @@ local unitPayloads = {
|
||||
["num"] = 6,
|
||||
},
|
||||
[4] = {
|
||||
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
|
||||
["CLSID"] = "{AN_ASQ_228}",
|
||||
["num"] = 4,
|
||||
},
|
||||
[5] = {
|
||||
["CLSID"] = "{A111396E-D3E8-4b9c-8AC9-2432489304D5}",
|
||||
["num"] = 5,
|
||||
},
|
||||
[6] = {
|
||||
["CLSID"] = "{F16A4DE0-116C-4A71-97F0-2CF85B0313EC}",
|
||||
["num"] = 3,
|
||||
},
|
||||
[7] = {
|
||||
[6] = {
|
||||
["CLSID"] = "{F16A4DE0-116C-4A71-97F0-2CF85B0313EC}",
|
||||
["num"] = 2,
|
||||
},
|
||||
[8] = {
|
||||
[7] = {
|
||||
["CLSID"] = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}",
|
||||
["num"] = 1,
|
||||
},
|
||||
[9] = {
|
||||
[8] = {
|
||||
["CLSID"] = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}",
|
||||
["num"] = 9,
|
||||
},
|
||||
@@ -137,38 +129,34 @@ local unitPayloads = {
|
||||
["name"] = "STRIKE",
|
||||
["pylons"] = {
|
||||
[1] = {
|
||||
["CLSID"] = "{BRU55_2*GBU-38}",
|
||||
["num"] = 7,
|
||||
},
|
||||
[2] = {
|
||||
["CLSID"] = "{BRU33_2X_MK-83}",
|
||||
["num"] = 8,
|
||||
},
|
||||
[3] = {
|
||||
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
|
||||
["num"] = 6,
|
||||
},
|
||||
[4] = {
|
||||
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
|
||||
["num"] = 4,
|
||||
},
|
||||
[5] = {
|
||||
["CLSID"] = "{FPU_8A_FUEL_TANK}",
|
||||
["num"] = 5,
|
||||
},
|
||||
[6] = {
|
||||
["CLSID"] = "{BRU55_2*GBU-38}",
|
||||
["num"] = 3,
|
||||
},
|
||||
[7] = {
|
||||
["CLSID"] = "{BRU33_2X_MK-83}",
|
||||
["num"] = 2,
|
||||
},
|
||||
[8] = {
|
||||
["CLSID"] = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}",
|
||||
["num"] = 1,
|
||||
},
|
||||
[9] = {
|
||||
[2] = {
|
||||
["CLSID"] = "{GBU_31_V_2B}",
|
||||
["num"] = 2,
|
||||
},
|
||||
[3] = {
|
||||
["CLSID"] = "{FPU_8A_FUEL_TANK}",
|
||||
["num"] = 3,
|
||||
},
|
||||
[4] = {
|
||||
["CLSID"] = "{AN_ASQ_228}",
|
||||
["num"] = 4,
|
||||
},
|
||||
[5] = {
|
||||
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
|
||||
["num"] = 6,
|
||||
},
|
||||
[6] = {
|
||||
["CLSID"] = "{FPU_8A_FUEL_TANK}",
|
||||
["num"] = 7,
|
||||
},
|
||||
[7] = {
|
||||
["CLSID"] = "{GBU_31_V_2B}",
|
||||
["num"] = 8,
|
||||
},
|
||||
[8] = {
|
||||
["CLSID"] = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}",
|
||||
["num"] = 9,
|
||||
},
|
||||
@@ -189,30 +177,26 @@ local unitPayloads = {
|
||||
["num"] = 7,
|
||||
},
|
||||
[3] = {
|
||||
["CLSID"] = "{A111396E-D3E8-4b9c-8AC9-2432489304D5}",
|
||||
["num"] = 5,
|
||||
},
|
||||
[4] = {
|
||||
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
|
||||
["num"] = 6,
|
||||
},
|
||||
[5] = {
|
||||
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
|
||||
[4] = {
|
||||
["CLSID"] = "{AN_ASQ_228}",
|
||||
["num"] = 4,
|
||||
},
|
||||
[6] = {
|
||||
[5] = {
|
||||
["CLSID"] = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}",
|
||||
["num"] = 1,
|
||||
},
|
||||
[7] = {
|
||||
[6] = {
|
||||
["CLSID"] = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}",
|
||||
["num"] = 9,
|
||||
},
|
||||
[8] = {
|
||||
[7] = {
|
||||
["CLSID"] = "{AGM_84D}",
|
||||
["num"] = 3,
|
||||
},
|
||||
[9] = {
|
||||
[8] = {
|
||||
["CLSID"] = "{AGM_84D}",
|
||||
["num"] = 2,
|
||||
},
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
],
|
||||
"logistics_units": [
|
||||
"Truck_Bedford",
|
||||
"CCKW_353"
|
||||
"Truck_GMC_Jimmy_6x6_Truck"
|
||||
],
|
||||
"infantry_units": [
|
||||
"Infantry_SMLE_No_4_Mk_1",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
],
|
||||
"artillery_units": [
|
||||
"MLRS_9A52_Smerch_HE_300mm",
|
||||
"PLZ_05",
|
||||
"SPH_2S9_Nona_120mm_M"
|
||||
],
|
||||
"logistics_units": [
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
"MBT_M60A3_Patton",
|
||||
"IFV_BMP_1",
|
||||
"SPAAA_ZSU_23_4_Shilka_Gun_Dish",
|
||||
"SPAAA_ZSU_57_2"
|
||||
"SPAAA_ZSU_57_2",
|
||||
"SPAAA_ZU_23_2_Mounted_Ural_375"
|
||||
],
|
||||
"artillery_units": [
|
||||
"MLRS_BM_21_Grad_122mm",
|
||||
@@ -45,7 +46,7 @@
|
||||
"ZSU57Generator",
|
||||
"ZSU23Generator",
|
||||
"ZU23Generator",
|
||||
"ZU23UralGenerator"
|
||||
"ColdWarFlakGenerator"
|
||||
],
|
||||
"ewrs": [
|
||||
"TallRackGenerator"
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
"APC_MTLB",
|
||||
"MBT_T_55",
|
||||
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
||||
"AAA_8_8cm_Flak_18"
|
||||
"AAA_8_8cm_Flak_18",
|
||||
"AAA_S_60_57mm"
|
||||
],
|
||||
"artillery_units": [
|
||||
"MLRS_BM_21_Grad_122mm"
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
"IFV_BMP_1",
|
||||
"MBT_T_55",
|
||||
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
||||
"SPAAA_ZSU_57_2"
|
||||
"SPAAA_ZSU_57_2",
|
||||
"AAA_S_60_57mm"
|
||||
],
|
||||
"artillery_units": [
|
||||
"MLRS_BM_21_Grad_122mm"
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
],
|
||||
"logistics_units": [
|
||||
"Truck_Bedford",
|
||||
"CCKW_353"
|
||||
"Truck_GMC_Jimmy_6x6_Truck"
|
||||
],
|
||||
"infantry_units": [
|
||||
"Infantry_SMLE_No_4_Mk_1"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
],
|
||||
"frontline_units": [
|
||||
"IFV_Sd_Kfz_234_2_Puma",
|
||||
"APC_Sd_Kfz_251_Halftrack_Halftrack",
|
||||
"APC_Sd_Kfz_251_Halftrack",
|
||||
"MT_PzIV_H",
|
||||
"MT_M4_Sherman",
|
||||
"AAA_40mm_Bofors"
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
"MT_PzIV_H",
|
||||
"MBT_T_55",
|
||||
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
||||
"SPAAA_ZSU_57_2"
|
||||
"SPAAA_ZSU_57_2",
|
||||
"AAA_S_60_57mm"
|
||||
],
|
||||
"artillery_units": [
|
||||
"MLRS_BM_21_Grad_122mm"
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
"MT_PzIV_H",
|
||||
"SPG_StuG_III_Ausf__G",
|
||||
"SPG_Jagdpanzer_IV",
|
||||
"SPAAA_ZSU_57_2"
|
||||
"SPAAA_ZSU_57_2",
|
||||
"AAA_S_60_57mm"
|
||||
],
|
||||
"artillery_units": [
|
||||
"MLRS_BM_21_Grad_122mm"
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
"APC_MTLB",
|
||||
"MBT_T_55",
|
||||
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
||||
"SPAAA_ZSU_57_2"
|
||||
"SPAAA_ZSU_57_2",
|
||||
"AAA_S_60_57mm"
|
||||
],
|
||||
"artillery_units": [
|
||||
"MLRS_BM_21_Grad_122mm"
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
"MBT_T_55",
|
||||
"MBT_T_72B",
|
||||
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
||||
"SPAAA_ZSU_57_2"
|
||||
"SPAAA_ZSU_57_2",
|
||||
"AAA_S_60_57mm"
|
||||
],
|
||||
"artillery_units": [
|
||||
"MLRS_BM_21_Grad_122mm"
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
],
|
||||
"logistics_units": [
|
||||
"Truck_Bedford",
|
||||
"CCKW_353"
|
||||
"Truck_GMC_Jimmy_6x6_Truck"
|
||||
],
|
||||
"infantry_units": [
|
||||
"Infantry_SMLE_No_4_Mk_1"
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"SPG_M12_GMC_155mm"
|
||||
],
|
||||
"logistics_units": [
|
||||
"CCKW_353"
|
||||
"Truck_GMC_Jimmy_6x6_Truck"
|
||||
],
|
||||
"infantry_units": [
|
||||
"Infantry_M1_Garand"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"frontline_units": [
|
||||
"MBT_M60A3_Patton",
|
||||
"APC_M113",
|
||||
"APC_M1025_HMMWV",
|
||||
"APC_HMMWV__Scout",
|
||||
"SPAAA_Vulcan_M163"
|
||||
],
|
||||
"artillery_units": [
|
||||
|
||||
58
resources/plugins/lotatc/LotAtcExport-config.lua
Normal file
58
resources/plugins/lotatc/LotAtcExport-config.lua
Normal file
@@ -0,0 +1,58 @@
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- configuration file for the LotATC Export script
|
||||
--
|
||||
-- This configuration is tailored for a mission generated by DCS Liberation
|
||||
-- see https://github.com/Khopa/dcs_liberation
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- LotATC Export plugin - configuration
|
||||
logger:info("DCSLiberation|LotATC Export plugin - configuration")
|
||||
|
||||
local function discoverLotAtcDrawingsPath()
|
||||
-- establish a search pattern into the following modes
|
||||
-- 1. Environment variable LOTATC_DRAWINGS_DIR, to support server exporting with auto load from LotATC
|
||||
-- 2. DCS saved games folder as configured in DCS Liberation
|
||||
|
||||
local drawingEnvDir = os.getenv("LOTATC_DRAWINGS_DIR")
|
||||
if drawingEnvDir then
|
||||
return drawingEnvDir
|
||||
else
|
||||
return lfs.writedir()..[[\Mods\services\LotAtc\userdb\drawings\]]
|
||||
end
|
||||
end
|
||||
|
||||
if dcsLiberation then
|
||||
logger:info("DCSLiberation|LotATC Export plugin - configuration dcsLiberation")
|
||||
|
||||
local exportRedAA = true
|
||||
local exportBlueAA = false
|
||||
local exportSymbols = true
|
||||
|
||||
-- retrieve specific options values
|
||||
if dcsLiberation.plugins then
|
||||
logger:info("DCSLiberation|LotATC Export plugin - configuration dcsLiberation.plugins")
|
||||
|
||||
if dcsLiberation.plugins.lotatc then
|
||||
logger:info("DCSLiberation|LotATC Export plugin - dcsLiberation.plugins.lotatcExport")
|
||||
|
||||
exportRedAA = dcsLiberation.plugins.lotatc.exportRedAA
|
||||
logger:info(string.format("DCSLiberation|LotATC Export plugin - exportRedAA = %s",tostring(exportRedAA)))
|
||||
|
||||
exportBlueAA = dcsLiberation.plugins.lotatc.exportBlueAA
|
||||
logger:info(string.format("DCSLiberation|LotATC Export plugin - exportBlueAA = %s",tostring(exportBlueAA)))
|
||||
|
||||
exportBlueAA = dcsLiberation.plugins.lotatc.exportSymbols
|
||||
logger:info(string.format("DCSLiberation|LotATC Export plugin - exportSymbols = %s",tostring(exportSymbols)))
|
||||
end
|
||||
end
|
||||
|
||||
-- actual configuration code
|
||||
if LotAtcExportConfig then
|
||||
LotAtcExportConfig.exportRedAA = exportRedAA
|
||||
LotAtcExportConfig.exportBlueAA = exportBlueAA
|
||||
LotAtcExportConfig.exportSymbols = exportSymbols
|
||||
LotAtcExportConfig.drawingBasePath = discoverLotAtcDrawingsPath()
|
||||
|
||||
LotatcExport()
|
||||
end
|
||||
end
|
||||
253
resources/plugins/lotatc/LotAtcExport.lua
Normal file
253
resources/plugins/lotatc/LotAtcExport.lua
Normal file
@@ -0,0 +1,253 @@
|
||||
--[[
|
||||
Export script for LotATC drawings
|
||||
|
||||
Allows to export certain DCS Liberation objects as predefined drawing in LotATC.
|
||||
|
||||
This script runs at mission startup and generates a drawing JSON file to be imported
|
||||
in LotATC.
|
||||
]]
|
||||
|
||||
LotAtcExportConfig = {
|
||||
["exportRedAA"] = false,
|
||||
["exportBlueAA"] = false,
|
||||
["exportSymbols"] = false,
|
||||
["exportVersion"] = "2.2.0",
|
||||
["drawingBasePath"] = nil,
|
||||
["redColor"] = "#7FE32000",
|
||||
["blueColor"] = "#7F0084FF"
|
||||
}
|
||||
|
||||
local function factionName(isFriend)
|
||||
if isFriend then
|
||||
return "BLUE"
|
||||
else
|
||||
return "RED"
|
||||
end
|
||||
end
|
||||
|
||||
local function uuid()
|
||||
local random = math.random
|
||||
local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
|
||||
return string.gsub(template, '[xy]', function (c)
|
||||
local v = (c == 'x') and random(0, 0xf) or random(8, 0xb)
|
||||
return string.format('%x', v)
|
||||
end)
|
||||
end
|
||||
|
||||
local function ends_with(str, ending)
|
||||
return ending == "" or str:sub(-#ending) == ending
|
||||
end
|
||||
|
||||
local function combine(path1, path2)
|
||||
if not ends_with(path1, "\\") then
|
||||
path1 = path1 .. "\\"
|
||||
end
|
||||
|
||||
return path1 .. path2
|
||||
end
|
||||
|
||||
local function lotatcExport_get_aa_nato_name(unit, isFriend)
|
||||
if not redIADS or not blueIADS then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- logger:info(string.format("DCSLiberation|LotATC Export plugin - try get NATO name for unit %s", unit.dcsGroupName))
|
||||
|
||||
local iads = redIADS
|
||||
if isFriend then
|
||||
iads = blueIADS
|
||||
end
|
||||
|
||||
local samSite = iads:getSAMSiteByGroupName(unit.dcsGroupName)
|
||||
if samSite and samSite.natoName then
|
||||
-- logger:info(string.format("DCSLiberation|LotATC Export plugin - NATO name is %s", samSite.natoName))
|
||||
return samSite.natoName
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function lotatcExport_get_name(unit, isFriend)
|
||||
local classification = "SAM"
|
||||
|
||||
if string.find(unit.dcsGroupName, "|EWR|", 1, true) then
|
||||
classification = "EWR"
|
||||
elseif string.find(unit.dcsGroupName, "|AA", 1, true) then
|
||||
classification = "AAA"
|
||||
end
|
||||
|
||||
local natoName = lotatcExport_get_aa_nato_name(unit, isFriend)
|
||||
|
||||
local name = nil
|
||||
if not natoName then
|
||||
name = string.format("%s|%s", unit.name, classification)
|
||||
else
|
||||
name = string.format("%s|%s|%s", unit.name, classification, natoName)
|
||||
end
|
||||
|
||||
return name, classification
|
||||
end
|
||||
|
||||
local function lotatc_write_json(filename, json)
|
||||
logger:info(string.format("DCSLiberation|LotATC Export plugin - writing %s", filename))
|
||||
|
||||
local function Write()
|
||||
local fp = io.open(filename, 'w')
|
||||
if fp then
|
||||
fp:write(json)
|
||||
fp:close()
|
||||
end
|
||||
end
|
||||
|
||||
if pcall(Write) then
|
||||
else
|
||||
logger:error("Unable to write LotATC export file to %s", filename)
|
||||
end
|
||||
end
|
||||
|
||||
local function lotatcExport_threat_circles_for_faction(faction, color, isFriend)
|
||||
local drawings = {}
|
||||
|
||||
for _,aa in pairs(faction) do
|
||||
logger:info(string.format("DCSLiberation|LotATC Export plugin - exporting threat circle for %s", aa.dcsGroupName))
|
||||
|
||||
local convLat, convLon = coord.LOtoLL({x = aa.positionX, y = 0, z = aa.positionY})
|
||||
|
||||
local name = lotatcExport_get_name(aa, isFriend)
|
||||
|
||||
table.insert(drawings,
|
||||
{
|
||||
["author"] = "DCSLiberation",
|
||||
["brushStyle"] = 1,
|
||||
["color"] = color,
|
||||
["colorBg"] = "#00000000",
|
||||
["id"] = string.format("{%s}", uuid()),
|
||||
["longitude"] = convLon,
|
||||
["latitude"] = convLat,
|
||||
["radius"] = tonumber(aa.range),
|
||||
["lineWidth"] = 2,
|
||||
["name"] = name,
|
||||
["shared"] = true,
|
||||
["timestamp"] = "",
|
||||
["type"] = "circle",
|
||||
["text"] = name,
|
||||
["font"] = {
|
||||
["color"] = color,
|
||||
["font"] = "Lato"
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
local lotatcData = {
|
||||
["name"] = "Threat Circles " .. factionName(isFriend),
|
||||
["enable"] = "true",
|
||||
["version"] = LotAtcExportConfig.exportVersion,
|
||||
["drawings"] = drawings
|
||||
}
|
||||
|
||||
local drawings_json = json:encode(lotatcData)
|
||||
return drawings_json
|
||||
end
|
||||
|
||||
local function lotatcExport_symbols_for_faction(faction, color, isFriend)
|
||||
local drawings = {}
|
||||
|
||||
for _,aa in pairs(faction) do
|
||||
logger:info(string.format("DCSLiberation|LotATC Export plugin - exporting AA symbol for %s", aa.dcsGroupName))
|
||||
|
||||
local convLat, convLon = coord.LOtoLL({x = aa.positionX, y = 0, z = aa.positionY})
|
||||
|
||||
local name = lotatcExport_get_name(aa, isFriend)
|
||||
|
||||
local classification = "hostile"
|
||||
if isFriend then
|
||||
classification = "friend"
|
||||
end
|
||||
|
||||
local sub_dimension = "none"
|
||||
|
||||
if string.find(aa.dcsGroupName, "|EWR|", 1, true) then
|
||||
sub_dimension = "ew"
|
||||
end
|
||||
|
||||
table.insert(drawings,
|
||||
{
|
||||
["author"] = "DCSLiberation",
|
||||
["brushStyle"] = 1,
|
||||
["classification"] = {
|
||||
["classification"] = classification,
|
||||
["dimension"] = "land_unit",
|
||||
["sub_dimension"] = sub_dimension
|
||||
},
|
||||
["color"] = color,
|
||||
["colorBg"] = "#33FF0000",
|
||||
["font"] = {
|
||||
["color"] = color,
|
||||
["font"] = "Lato"
|
||||
},
|
||||
["id"] = string.format("{%s}", uuid()),
|
||||
["longitude"] = convLon,
|
||||
["latitude"] = convLat,
|
||||
["lineWidth"] = 2,
|
||||
["name"] = name,
|
||||
["shared"] = true,
|
||||
["timestamp"] = "",
|
||||
["type"] = "symbol",
|
||||
["text"] = name
|
||||
})
|
||||
end
|
||||
|
||||
local lotatcData = {
|
||||
["name"] = "Threat Symbols " .. factionName(isFriend),
|
||||
["enable"] = "true",
|
||||
["version"] = LotAtcExportConfig.exportVersion,
|
||||
["drawings"] = drawings,
|
||||
}
|
||||
|
||||
local drawings_json = json:encode(lotatcData)
|
||||
return drawings_json
|
||||
end
|
||||
|
||||
local function lotatc_export_faction(faction, color, factionPath, isFriend)
|
||||
local exportBasePathFaction = combine(LotAtcExportConfig.drawingBasePath, factionPath)
|
||||
lfs.mkdir(exportBasePathFaction)
|
||||
|
||||
local exportFileName = combine(exportBasePathFaction, "threatZones.json")
|
||||
local json = lotatcExport_threat_circles_for_faction(faction, color, isFriend)
|
||||
lotatc_write_json(exportFileName, json)
|
||||
|
||||
if LotAtcExportConfig.exportSymbols then
|
||||
exportFileName = combine(exportBasePathFaction, "threatSymbols.json")
|
||||
json = lotatcExport_symbols_for_faction(faction, color, isFriend);
|
||||
lotatc_write_json(exportFileName, json)
|
||||
end
|
||||
end
|
||||
|
||||
function LotatcExport()
|
||||
|
||||
if not json then
|
||||
local message = "Unable to export LotATC drawings, JSON library is not loaded!"
|
||||
logger:error(message)
|
||||
return
|
||||
end
|
||||
|
||||
if not LotAtcExportConfig.drawingBasePath then
|
||||
local message = "No writable export path for LotATC drawings. Set environment variable LOTATC_DRAWINGS_DIR pointing to your export path."
|
||||
logger:error(message)
|
||||
return
|
||||
end
|
||||
|
||||
local message = "Export LotATC drawings to "..LotAtcExportConfig.drawingBasePath
|
||||
logger:info(message)
|
||||
|
||||
-- The RED AA is exported to the blue folder and vice versa. If a BLUE GCI connects he/she
|
||||
-- wants to see the RED AA.
|
||||
|
||||
if LotAtcExportConfig.exportRedAA then
|
||||
lotatc_export_faction(dcsLiberation.RedAA, LotAtcExportConfig.redColor, [[blue\]], false)
|
||||
end
|
||||
|
||||
if LotAtcExportConfig.exportBlueAA then
|
||||
lotatc_export_faction(dcsLiberation.BlueAA, LotAtcExportConfig.blueColor, [[red\]], true)
|
||||
end
|
||||
end
|
||||
33
resources/plugins/lotatc/plugin.json
Normal file
33
resources/plugins/lotatc/plugin.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"nameInUI": "LotATC Export",
|
||||
"defaultValue": false,
|
||||
"specificOptions": [
|
||||
{
|
||||
"nameInUI": "Export RED AA",
|
||||
"mnemonic": "exportRedAA",
|
||||
"defaultValue": true
|
||||
},
|
||||
{
|
||||
"nameInUI": "Export BLUE AA",
|
||||
"mnemonic": "exportBlueAA",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"nameInUI": "Export AA Symbols",
|
||||
"mnemonic": "exportSymbols",
|
||||
"defaultValue": true
|
||||
}
|
||||
],
|
||||
"scriptsWorkOrders": [
|
||||
{
|
||||
"file": "LotAtcExport.lua",
|
||||
"mnemonic": "LotAtcExport-script"
|
||||
}
|
||||
],
|
||||
"configurationWorkOrders": [
|
||||
{
|
||||
"file": "LotAtcExport-config.lua",
|
||||
"mnemonic": "LotAtcExport-config"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
[
|
||||
"base",
|
||||
"jtacautolase",
|
||||
"skynetiads",
|
||||
"ewrs",
|
||||
"herculescargo",
|
||||
"splashdamage"
|
||||
]
|
||||
[
|
||||
"base",
|
||||
"jtacautolase",
|
||||
"skynetiads",
|
||||
"ewrs",
|
||||
"herculescargo",
|
||||
"splashdamage",
|
||||
"lotatc"
|
||||
]
|
||||
|
||||
BIN
resources/ui/fob.png
Normal file
BIN
resources/ui/fob.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 258 KiB |
BIN
resources/ui/ground_assets/ewr.png
Normal file
BIN
resources/ui/ground_assets/ewr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
resources/ui/ground_assets/ewr_blue.png
Normal file
BIN
resources/ui/ground_assets/ewr_blue.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 881 B |
BIN
resources/ui/units/vehicles/icons/PLZ-05_24.jpg
Normal file
BIN
resources/ui/units/vehicles/icons/PLZ-05_24.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
resources/ui/units/vehicles/icons/S-60_24.jpg
Normal file
BIN
resources/ui/units/vehicles/icons/S-60_24.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1011 B |
Reference in New Issue
Block a user