mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Merge branch 'develop' into helipads
# Conflicts: # game/game.py # resources/campaigns/golan_heights_lite.miz
This commit is contained in:
commit
7667a4f8c0
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,6 +12,7 @@ resources/tools/a.miz
|
||||
# User-specific stuff
|
||||
.idea/
|
||||
.env
|
||||
env/
|
||||
|
||||
/kneeboards
|
||||
/liberation_preferences.json
|
||||
|
||||
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -1,4 +0,0 @@
|
||||
[submodule "pydcs"]
|
||||
path = pydcs
|
||||
url = https://github.com/pydcs/dcs
|
||||
branch = master
|
||||
25
changelog.md
25
changelog.md
@ -1,30 +1,51 @@
|
||||
# 5.0.0
|
||||
|
||||
Saves from 3.x are not compatible with 5.0.
|
||||
|
||||
## Features/Improvements
|
||||
|
||||
## Fixes
|
||||
|
||||
# 4.0.0
|
||||
|
||||
Saves from 3.x are not compatible with 4.0.
|
||||
|
||||
## Features/Improvements
|
||||
|
||||
* **[Campaign]** Squadrons now have a maximum size and killed pilots replenish at a limited rate.
|
||||
* **[Campaign]** Squadrons now (optionally, off by default) have a maximum size and killed pilots replenish at a limited rate.
|
||||
* **[Campaign]** Added an option to disable levelling up of AI pilots.
|
||||
* **[Campaign]** Added Russian Intervention 2015 campaign on Syria, for a small and somewhat realistic Russian COIN scenario.
|
||||
* **[Campaign]** Added Operation Atilla campaign on Syria, for a reasonably large invasion of Cyprus scenario.
|
||||
* **[Campaign AI]** AI will plan Tanker flights.
|
||||
* **[Campaign AI]** Removed max distance for AEW&C auto planning.
|
||||
* **[Economy]** Adjusted prices for aircraft to balance out some price inconsistencies.
|
||||
* **[Factions]** Added more tankers to factions.
|
||||
* **[Flight Planner]** Added ability to plan Tankers.
|
||||
* **[Modding]** Campaign format version is now 7.0 to account for DCS map changes that made scenery strike targets incompatible with existing campaigns.
|
||||
* **[Mods]** Added support for the Gripen mod.
|
||||
* **[Mods]** Removes MB-339PAN support, as the mod is now deprecated and no longer works with DCS 2.7+.
|
||||
* **[Mission Generation]** Added support for "Neutral Dot" label options.
|
||||
* **[New Game Wizard]** Mods are now selected via checkboxes in the new game wizard, not as separate factions.
|
||||
* **[UI]** Ctrl click and shift click now buy or sell 5 or 10 units respectively.
|
||||
* **[UI]** Multiple waypoints can now be deleted simultaneously if multiple waypoints are selected.
|
||||
* **[UI]** Carriers and LHAs now match the colour of airfields, and their destination icons are translucent.
|
||||
* **[UI]** Updated intel box text for first turn.
|
||||
* **[UI]** Base Capture Cheat is now usable at all bases and can also be used to transfer player-owned bases to OPFOR.
|
||||
* **[UI]** Pass Turn button is relabled as "Begin Campaign" on Turn 0.
|
||||
* **[UI]** Added a ruler to the map.
|
||||
* **[UI]** Liberation now saves games to `<DCS user directory>/Liberation/Saves` by default to declutter the main directory.
|
||||
|
||||
## Fixes
|
||||
|
||||
* **[Campaign AI]** Fix procurement for factions that lack some unit types.
|
||||
* **[Campaign AI]** Improved pruning of unplannable missions which should improve turn cycle time and prevent the auto-planner from quitting early.
|
||||
* **[Campaign AI]** Fix auto purchase of aircraft for factions that have no transport aircraft.
|
||||
* **[Campaign AI]** Fix refunding of pending aircraft purchases when a side has no factory available.
|
||||
* **[Mission Generation]** Fixed problem with mission load when control point name contained an apostrophe.
|
||||
* **[Mission Generation]** Fixed EWR group names so they contribute to Skynet again.
|
||||
* **[Mission Generation]** Fixed duplicate name error when generating convoys and cargo ships when creating manual transfers after loading a game.
|
||||
* **[Mission Generation]** Fixed empty convoys not being disbanded when all units are killed/removed.
|
||||
* **[Mission Generation]** Fixed player losing frontline progress when skipping from turn 0 to turn 1.
|
||||
* **[Mission Generation]** Fixed issue where frontline would only search to the right for valid locations.
|
||||
* **[UI]** Made non-interactive map elements less obstructive.
|
||||
* **[UI]** Added support for Neutral Dot difficulty label
|
||||
* **[UI]** Clear skies at night no longer described as "Sunny" by the weather widget.
|
||||
|
||||
@ -1,51 +0,0 @@
|
||||
from dcs.planes import (
|
||||
Bf_109K_4,
|
||||
C_101CC,
|
||||
FW_190A8,
|
||||
FW_190D9,
|
||||
F_5E_3,
|
||||
F_86F_Sabre,
|
||||
I_16,
|
||||
L_39ZA,
|
||||
MiG_15bis,
|
||||
MiG_19P,
|
||||
MiG_21Bis,
|
||||
P_47D_30,
|
||||
P_47D_30bl1,
|
||||
P_47D_40,
|
||||
P_51D,
|
||||
P_51D_30_NA,
|
||||
SpitfireLFMkIX,
|
||||
SpitfireLFMkIXCW,
|
||||
)
|
||||
|
||||
from pydcs_extensions.a4ec.a4ec import A_4E_C
|
||||
|
||||
"""
|
||||
This list contains the aircraft that do not use the guns as the last resort weapons, but as a main weapon
|
||||
They'll RTB when they don't have gun ammo left
|
||||
"""
|
||||
GUNFIGHTERS = [
|
||||
# Cold War
|
||||
MiG_15bis,
|
||||
MiG_19P,
|
||||
MiG_21Bis,
|
||||
F_86F_Sabre,
|
||||
A_4E_C,
|
||||
F_5E_3,
|
||||
# Trainers
|
||||
C_101CC,
|
||||
L_39ZA,
|
||||
# WW2
|
||||
P_51D_30_NA,
|
||||
P_51D,
|
||||
P_47D_30,
|
||||
P_47D_30bl1,
|
||||
P_47D_40,
|
||||
SpitfireLFMkIXCW,
|
||||
SpitfireLFMkIX,
|
||||
Bf_109K_4,
|
||||
FW_190D9,
|
||||
FW_190A8,
|
||||
I_16,
|
||||
]
|
||||
@ -4,7 +4,7 @@ import datetime
|
||||
import inspect
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, Iterator, Optional, Set, Tuple, Union, cast
|
||||
|
||||
from dcs.unitgroup import FlyingGroup
|
||||
@ -21,8 +21,8 @@ class Weapon:
|
||||
"""Wraps a pydcs weapon dict in a hashable type."""
|
||||
|
||||
cls_id: str
|
||||
name: str
|
||||
weight: int
|
||||
name: str = field(compare=False)
|
||||
weight: int = field(compare=False)
|
||||
|
||||
def available_on(self, date: datetime.date) -> bool:
|
||||
introduction_year = WEAPON_INTRODUCTION_YEARS.get(self)
|
||||
|
||||
@ -44,12 +44,10 @@ from pydcs_extensions.a4ec.a4ec import A_4E_C
|
||||
from pydcs_extensions.f22a.f22a import F_22A
|
||||
from pydcs_extensions.hercules.hercules import Hercules
|
||||
from pydcs_extensions.jas39.jas39 import JAS39Gripen, JAS39Gripen_AG
|
||||
from pydcs_extensions.mb339.mb339 import MB_339PAN
|
||||
from pydcs_extensions.su57.su57 import Su_57
|
||||
|
||||
plane_map["A-4E-C"] = A_4E_C
|
||||
plane_map["F-22A"] = F_22A
|
||||
plane_map["MB-339PAN"] = MB_339PAN
|
||||
plane_map["Su-57"] = Su_57
|
||||
plane_map["Hercules"] = Hercules
|
||||
plane_map["JAS39Gripen"] = JAS39Gripen
|
||||
|
||||
@ -29,7 +29,7 @@ from game.radio.channels import (
|
||||
ViggenRadioChannelAllocator,
|
||||
NoOpChannelAllocator,
|
||||
)
|
||||
from game.utils import Speed, kph
|
||||
from game.utils import Distance, Speed, feet, kph, knots
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gen.aircraft import FlightData
|
||||
@ -90,11 +90,34 @@ class RadioConfig:
|
||||
}[config.get("namer", "default")]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PatrolConfig:
|
||||
altitude: Optional[Distance]
|
||||
speed: Optional[Speed]
|
||||
|
||||
@classmethod
|
||||
def from_data(cls, data: dict[str, Any]) -> PatrolConfig:
|
||||
altitude = data.get("altitude", None)
|
||||
speed = data.get("altitude", None)
|
||||
return PatrolConfig(
|
||||
feet(altitude) if altitude is not None else None,
|
||||
knots(speed) if speed is not None else None,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AircraftType(UnitType[FlyingType]):
|
||||
carrier_capable: bool
|
||||
lha_capable: bool
|
||||
always_keeps_gun: bool
|
||||
|
||||
# If true, the aircraft does not use the guns as the last resort weapons, but as a main weapon.
|
||||
# It'll RTB when it doesn't have gun ammo left.
|
||||
gunfighter: bool
|
||||
|
||||
max_group_size: int
|
||||
patrol_altitude: Optional[Distance]
|
||||
patrol_speed: Optional[Speed]
|
||||
intra_flight_radio: Optional[Radio]
|
||||
channel_allocator: Optional[RadioChannelAllocator]
|
||||
channel_namer: Type[ChannelNamer]
|
||||
@ -146,6 +169,12 @@ class AircraftType(UnitType[FlyingType]):
|
||||
def channel_name(self, radio_id: int, channel_id: int) -> str:
|
||||
return self.channel_namer.channel_name(radio_id, channel_id)
|
||||
|
||||
def __setstate__(self, state: dict[str, Any]) -> None:
|
||||
# Update any existing models with new data on load.
|
||||
updated = AircraftType.named(state["name"])
|
||||
state.update(updated.__dict__)
|
||||
self.__dict__.update(state)
|
||||
|
||||
@classmethod
|
||||
def register(cls, aircraft_type: AircraftType) -> None:
|
||||
cls._by_name[aircraft_type.name] = aircraft_type
|
||||
@ -159,6 +188,8 @@ class AircraftType(UnitType[FlyingType]):
|
||||
|
||||
@classmethod
|
||||
def for_dcs_type(cls, dcs_unit_type: Type[FlyingType]) -> Iterator[AircraftType]:
|
||||
if not cls._loaded:
|
||||
cls._load_all()
|
||||
yield from cls._by_unit_type[dcs_unit_type]
|
||||
|
||||
@staticmethod
|
||||
@ -189,6 +220,7 @@ class AircraftType(UnitType[FlyingType]):
|
||||
raise KeyError(f"Missing required price field: {data_path}") from ex
|
||||
|
||||
radio_config = RadioConfig.from_data(data.get("radios", {}))
|
||||
patrol_config = PatrolConfig.from_data(data.get("patrol", {}))
|
||||
|
||||
try:
|
||||
introduction = data["introduced"]
|
||||
@ -210,6 +242,10 @@ class AircraftType(UnitType[FlyingType]):
|
||||
carrier_capable=data.get("carrier_capable", False),
|
||||
lha_capable=data.get("lha_capable", False),
|
||||
always_keeps_gun=data.get("always_keeps_gun", False),
|
||||
gunfighter=data.get("gunfighter", False),
|
||||
max_group_size=data.get("max_group_size", aircraft.group_size_max),
|
||||
patrol_altitude=patrol_config.altitude,
|
||||
patrol_speed=patrol_config.speed,
|
||||
intra_flight_radio=radio_config.intra_flight,
|
||||
channel_allocator=radio_config.channel_allocator,
|
||||
channel_namer=radio_config.channel_namer,
|
||||
|
||||
@ -45,6 +45,8 @@ class GroundUnitType(UnitType[VehicleType]):
|
||||
|
||||
@classmethod
|
||||
def for_dcs_type(cls, dcs_unit_type: Type[VehicleType]) -> Iterator[GroundUnitType]:
|
||||
if not cls._loaded:
|
||||
cls._load_all()
|
||||
yield from cls._by_unit_type[dcs_unit_type]
|
||||
|
||||
@staticmethod
|
||||
|
||||
@ -382,15 +382,21 @@ class PollDebriefingFileThread(threading.Thread):
|
||||
else:
|
||||
last_modified = 0
|
||||
while not self.stopped():
|
||||
if (
|
||||
os.path.isfile("state.json")
|
||||
and os.path.getmtime("state.json") > last_modified
|
||||
):
|
||||
with open("state.json", "r") as json_file:
|
||||
json_data = json.load(json_file)
|
||||
debriefing = Debriefing(json_data, self.game, self.unit_map)
|
||||
self.callback(debriefing)
|
||||
break
|
||||
try:
|
||||
if (
|
||||
os.path.isfile("state.json")
|
||||
and os.path.getmtime("state.json") > last_modified
|
||||
):
|
||||
with open("state.json", "r") as json_file:
|
||||
json_data = json.load(json_file)
|
||||
debriefing = Debriefing(json_data, self.game, self.unit_map)
|
||||
self.callback(debriefing)
|
||||
break
|
||||
except json.JSONDecodeError:
|
||||
logging.exception(
|
||||
"Failed to decode state.json. Probably attempted read while DCS "
|
||||
"was still writing the file. Will retry in 5 seconds."
|
||||
)
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@ class Event:
|
||||
|
||||
@property
|
||||
def is_player_attacking(self) -> bool:
|
||||
return self.attacker_name == self.game.player_name
|
||||
return self.attacker_name == self.game.player_faction.name
|
||||
|
||||
@property
|
||||
def tasks(self) -> List[Type[Task]]:
|
||||
|
||||
@ -257,6 +257,76 @@ class Faction:
|
||||
if unit.unit_class is unit_class:
|
||||
yield unit
|
||||
|
||||
def apply_mod_settings(self, mod_settings) -> Faction:
|
||||
# aircraft
|
||||
if not mod_settings.a4_skyhawk:
|
||||
self.remove_aircraft("A-4E-C")
|
||||
if not mod_settings.hercules:
|
||||
self.remove_aircraft("Hercules")
|
||||
if not mod_settings.f22_raptor:
|
||||
self.remove_aircraft("F-22A")
|
||||
if not mod_settings.jas39_gripen:
|
||||
self.remove_aircraft("JAS39Gripen")
|
||||
self.remove_aircraft("JAS39Gripen_AG")
|
||||
if not mod_settings.su57_felon:
|
||||
self.remove_aircraft("Su-57")
|
||||
# frenchpack
|
||||
if not mod_settings.frenchpack:
|
||||
self.remove_vehicle("AMX10RCR")
|
||||
self.remove_vehicle("SEPAR")
|
||||
self.remove_vehicle("ERC")
|
||||
self.remove_vehicle("M120")
|
||||
self.remove_vehicle("AA20")
|
||||
self.remove_vehicle("TRM2000")
|
||||
self.remove_vehicle("TRM2000_Citerne")
|
||||
self.remove_vehicle("TRM2000_AA20")
|
||||
self.remove_vehicle("TRMMISTRAL")
|
||||
self.remove_vehicle("VABH")
|
||||
self.remove_vehicle("VAB_RADIO")
|
||||
self.remove_vehicle("VAB_50")
|
||||
self.remove_vehicle("VIB_VBR")
|
||||
self.remove_vehicle("VAB_HOT")
|
||||
self.remove_vehicle("VAB_MORTIER")
|
||||
self.remove_vehicle("VBL50")
|
||||
self.remove_vehicle("VBLANF1")
|
||||
self.remove_vehicle("VBL-radio")
|
||||
self.remove_vehicle("VBAE")
|
||||
self.remove_vehicle("VBAE_MMP")
|
||||
self.remove_vehicle("AMX-30B2")
|
||||
self.remove_vehicle("Tracma")
|
||||
self.remove_vehicle("JTACFP")
|
||||
self.remove_vehicle("SHERIDAN")
|
||||
self.remove_vehicle("Leclerc_XXI")
|
||||
self.remove_vehicle("Toyota_bleu")
|
||||
self.remove_vehicle("Toyota_vert")
|
||||
self.remove_vehicle("Toyota_desert")
|
||||
self.remove_vehicle("Kamikaze")
|
||||
# high digit sams
|
||||
if not mod_settings.high_digit_sams:
|
||||
self.remove_air_defenses("SA10BGenerator")
|
||||
self.remove_air_defenses("SA12Generator")
|
||||
self.remove_air_defenses("SA20Generator")
|
||||
self.remove_air_defenses("SA20BGenerator")
|
||||
self.remove_air_defenses("SA23Generator")
|
||||
self.remove_air_defenses("SA17Generator")
|
||||
self.remove_air_defenses("KS19Generator")
|
||||
return self
|
||||
|
||||
def remove_aircraft(self, name):
|
||||
for i in self.aircrafts:
|
||||
if i.dcs_unit_type.id == name:
|
||||
self.aircrafts.remove(i)
|
||||
|
||||
def remove_air_defenses(self, name):
|
||||
for i in self.air_defenses:
|
||||
if i == name:
|
||||
self.air_defenses.remove(i)
|
||||
|
||||
def remove_vehicle(self, name):
|
||||
for i in self.frontline_units:
|
||||
if i.dcs_unit_type.id == name:
|
||||
self.frontline_units.remove(i)
|
||||
|
||||
|
||||
def load_ship(name: str) -> Optional[Type[ShipType]]:
|
||||
if (ship := getattr(dcs.ships, name, None)) is not None:
|
||||
|
||||
82
game/game.py
82
game/game.py
@ -1,23 +1,25 @@
|
||||
from game.dcs.aircrafttype import AircraftType
|
||||
import itertools
|
||||
import logging
|
||||
import random
|
||||
import sys
|
||||
from datetime import date, datetime, timedelta
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List
|
||||
from typing import Any, List
|
||||
|
||||
from dcs.action import Coalition
|
||||
from dcs.countries import Switzerland, UnitedNationsPeacekeepers, USAFAggressors
|
||||
from dcs.mapping import Point
|
||||
from dcs.task import CAP, CAS, PinpointStrike
|
||||
from dcs.vehicles import AirDefence
|
||||
from pydcs_extensions.a4ec.a4ec import A_4E_C
|
||||
from faker import Faker
|
||||
|
||||
from game import db
|
||||
from game.inventory import GlobalAircraftInventory
|
||||
from game.models.game_stats import GameStats
|
||||
from game.plugins import LuaPluginManager
|
||||
from gen import naming
|
||||
from gen import aircraft, naming
|
||||
from gen.ato import AirTaskingOrder
|
||||
from gen.conflictgen import Conflict
|
||||
from gen.flights.ai_flight_planner import CoalitionMissionPlanner
|
||||
@ -87,8 +89,8 @@ class TurnState(Enum):
|
||||
class Game:
|
||||
def __init__(
|
||||
self,
|
||||
player_name: str,
|
||||
enemy_name: str,
|
||||
player_faction: Faction,
|
||||
enemy_faction: Faction,
|
||||
theater: ConflictTheater,
|
||||
start_date: datetime,
|
||||
settings: Settings,
|
||||
@ -98,10 +100,10 @@ class Game:
|
||||
self.settings = settings
|
||||
self.events: List[Event] = []
|
||||
self.theater = theater
|
||||
self.player_name = player_name
|
||||
self.player_country = db.FACTIONS[player_name].country
|
||||
self.enemy_name = enemy_name
|
||||
self.enemy_country = db.FACTIONS[enemy_name].country
|
||||
self.player_faction = player_faction
|
||||
self.player_country = player_faction.country
|
||||
self.enemy_faction = enemy_faction
|
||||
self.enemy_country = enemy_faction.country
|
||||
# pass_turn() will be called when initialization is complete which will
|
||||
# increment this to turn 0 before it reaches the player.
|
||||
self.turn = -1
|
||||
@ -109,7 +111,7 @@ class Game:
|
||||
self.date = date(start_date.year, start_date.month, start_date.day)
|
||||
self.game_stats = GameStats()
|
||||
self.game_stats.update(self)
|
||||
self.ground_planners: Dict[int, GroundPlanner] = {}
|
||||
self.ground_planners: dict[int, GroundPlanner] = {}
|
||||
self.informations = []
|
||||
self.informations.append(Information("Game Start", "-" * 40, 0))
|
||||
# Culling Zones are for areas around points of interest that contain things we may not wish to cull.
|
||||
@ -150,7 +152,7 @@ class Game:
|
||||
|
||||
self.on_load(game_still_initializing=True)
|
||||
|
||||
def __getstate__(self) -> Dict[str, Any]:
|
||||
def __getstate__(self) -> dict[str, Any]:
|
||||
state = self.__dict__.copy()
|
||||
# Avoid persisting any volatile types that can be deterministically
|
||||
# recomputed on load for the sake of save compatibility.
|
||||
@ -162,7 +164,7 @@ class Game:
|
||||
del state["red_faker"]
|
||||
return state
|
||||
|
||||
def __setstate__(self, state: Dict[str, Any]) -> None:
|
||||
def __setstate__(self, state: dict[str, Any]) -> None:
|
||||
self.__dict__.update(state)
|
||||
# Regenerate any state that was not persisted.
|
||||
self.on_load()
|
||||
@ -202,13 +204,49 @@ class Game:
|
||||
else:
|
||||
self.enemy_country = "Russia"
|
||||
|
||||
@property
|
||||
def player_faction(self) -> Faction:
|
||||
return db.FACTIONS[self.player_name]
|
||||
def faction_for(self, player: bool) -> Faction:
|
||||
if player:
|
||||
return self.player_faction
|
||||
return self.enemy_faction
|
||||
|
||||
@property
|
||||
def enemy_faction(self) -> Faction:
|
||||
return db.FACTIONS[self.enemy_name]
|
||||
def faker_for(self, player: bool) -> Faker:
|
||||
if player:
|
||||
return self.blue_faker
|
||||
return self.red_faker
|
||||
|
||||
def air_wing_for(self, player: bool) -> AirWing:
|
||||
if player:
|
||||
return self.blue_air_wing
|
||||
return self.red_air_wing
|
||||
|
||||
def country_for(self, player: bool) -> str:
|
||||
if player:
|
||||
return self.player_country
|
||||
return self.enemy_country
|
||||
|
||||
def bullseye_for(self, player: bool) -> Bullseye:
|
||||
if player:
|
||||
return self.blue_bullseye
|
||||
return self.red_bullseye
|
||||
|
||||
def _roll(self, prob, mult):
|
||||
if self.settings.version == "dev":
|
||||
# always generate all events for dev
|
||||
return 100
|
||||
else:
|
||||
return random.randint(1, 100) <= prob * mult
|
||||
|
||||
def _generate_player_event(self, event_class, player_cp, enemy_cp):
|
||||
self.events.append(
|
||||
event_class(
|
||||
self,
|
||||
player_cp,
|
||||
enemy_cp,
|
||||
enemy_cp.position,
|
||||
self.player_faction.name,
|
||||
self.enemy_faction.name,
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
def neutral_country(self):
|
||||
@ -260,8 +298,8 @@ class Game:
|
||||
player_cp,
|
||||
enemy_cp,
|
||||
enemy_cp.position,
|
||||
self.player_name,
|
||||
self.enemy_name,
|
||||
self.player_faction.name,
|
||||
self.enemy_faction.name,
|
||||
)
|
||||
)
|
||||
|
||||
@ -307,7 +345,7 @@ class Game:
|
||||
return (
|
||||
event
|
||||
and event.attacker_name
|
||||
and event.attacker_name == self.player_name
|
||||
and event.attacker_name == self.player_faction.name
|
||||
)
|
||||
else:
|
||||
raise RuntimeError(f"{event} was passed when an Event type was expected")
|
||||
@ -356,10 +394,10 @@ class Game:
|
||||
self.blue_air_wing.replenish()
|
||||
self.red_air_wing.replenish()
|
||||
|
||||
if not skipped and self.turn > 1:
|
||||
if not skipped:
|
||||
for cp in self.theater.player_points():
|
||||
cp.base.affect_strength(+PLAYER_BASE_STRENGTH_RECOVERY)
|
||||
else:
|
||||
elif self.turn > 1:
|
||||
for cp in self.theater.player_points():
|
||||
if not cp.is_carrier and not cp.is_lha:
|
||||
cp.base.affect_strength(-PLAYER_BASE_STRENGTH_RECOVERY)
|
||||
|
||||
@ -77,8 +77,8 @@ class Operation:
|
||||
yield Conflict(
|
||||
cls.game.theater,
|
||||
frontline,
|
||||
cls.game.player_name,
|
||||
cls.game.enemy_name,
|
||||
cls.game.player_faction.name,
|
||||
cls.game.enemy_faction.name,
|
||||
cls.game.player_country,
|
||||
cls.game.enemy_country,
|
||||
frontline.position,
|
||||
@ -95,8 +95,8 @@ class Operation:
|
||||
return Conflict(
|
||||
cls.game.theater,
|
||||
FrontLine(player_cp, enemy_cp),
|
||||
cls.game.player_name,
|
||||
cls.game.enemy_name,
|
||||
cls.game.player_faction.name,
|
||||
cls.game.enemy_faction.name,
|
||||
cls.game.player_country,
|
||||
cls.game.enemy_country,
|
||||
mid_point,
|
||||
@ -403,8 +403,8 @@ class Operation:
|
||||
player_cp = front_line.blue_cp
|
||||
enemy_cp = front_line.red_cp
|
||||
conflict = Conflict.frontline_cas_conflict(
|
||||
cls.game.player_name,
|
||||
cls.game.enemy_name,
|
||||
cls.game.player_faction.name,
|
||||
cls.game.enemy_faction.name,
|
||||
cls.current_mission.country(cls.game.player_country),
|
||||
cls.current_mission.country(cls.game.enemy_country),
|
||||
front_line,
|
||||
|
||||
@ -2,16 +2,18 @@ import logging
|
||||
import os
|
||||
import pickle
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
|
||||
_dcs_saved_game_folder: Optional[str] = None
|
||||
_file_abs_path = None
|
||||
|
||||
|
||||
def setup(user_folder: str):
|
||||
global _dcs_saved_game_folder
|
||||
_dcs_saved_game_folder = user_folder
|
||||
_file_abs_path = os.path.join(base_path(), "default.liberation")
|
||||
if not save_dir().exists():
|
||||
save_dir().mkdir(parents=True)
|
||||
|
||||
|
||||
def base_path() -> str:
|
||||
@ -20,16 +22,20 @@ def base_path() -> str:
|
||||
return _dcs_saved_game_folder
|
||||
|
||||
|
||||
def save_dir() -> Path:
|
||||
return Path(base_path()) / "Liberation" / "Saves"
|
||||
|
||||
|
||||
def _temporary_save_file() -> str:
|
||||
return os.path.join(base_path(), "tmpsave.liberation")
|
||||
return str(save_dir() / "tmpsave.liberation")
|
||||
|
||||
|
||||
def _autosave_path() -> str:
|
||||
return os.path.join(base_path(), "autosave.liberation")
|
||||
return str(save_dir() / "autosave.liberation")
|
||||
|
||||
|
||||
def mission_path_for(name: str) -> str:
|
||||
return os.path.join(base_path(), "Missions", "{}".format(name))
|
||||
return os.path.join(base_path(), "Missions", name)
|
||||
|
||||
|
||||
def load_game(path):
|
||||
|
||||
@ -34,11 +34,14 @@ class Settings:
|
||||
player_income_multiplier: float = 1.0
|
||||
enemy_income_multiplier: float = 1.0
|
||||
|
||||
#: Feature flag for squadron limits.
|
||||
enable_squadron_pilot_limits: bool = False
|
||||
|
||||
#: The maximum number of pilots a squadron can have at one time. Changing this after
|
||||
#: the campaign has started will have no immediate effect; pilots already in the
|
||||
#: squadron will not be removed if the limit is lowered and pilots will not be
|
||||
#: immediately created if the limit is raised.
|
||||
squadron_pilot_limit: int = 24
|
||||
squadron_pilot_limit: int = 4
|
||||
|
||||
#: The number of pilots a squadron can replace per turn.
|
||||
squadron_replenishment_rate: int = 4
|
||||
|
||||
@ -112,9 +112,19 @@ class Squadron:
|
||||
return self.name
|
||||
return f'{self.name} "{self.nickname}"'
|
||||
|
||||
@property
|
||||
def pilot_limits_enabled(self) -> bool:
|
||||
return self.game.settings.enable_squadron_pilot_limits
|
||||
|
||||
def claim_new_pilot_if_allowed(self) -> Optional[Pilot]:
|
||||
if self.pilot_limits_enabled:
|
||||
return None
|
||||
self._recruit_pilots(1)
|
||||
return self.available_pilots.pop()
|
||||
|
||||
def claim_available_pilot(self) -> Optional[Pilot]:
|
||||
if not self.available_pilots:
|
||||
return None
|
||||
return self.claim_new_pilot_if_allowed()
|
||||
|
||||
# For opfor, so player/AI option is irrelevant.
|
||||
if not self.player:
|
||||
@ -140,7 +150,7 @@ class Squadron:
|
||||
# If they only *prefer* players and we're out of players, just return an AI
|
||||
# pilot.
|
||||
if not prefer_players:
|
||||
return None
|
||||
return self.claim_new_pilot_if_allowed()
|
||||
return self.available_pilots.pop()
|
||||
|
||||
def claim_pilot(self, pilot: Pilot) -> None:
|
||||
@ -169,9 +179,12 @@ class Squadron:
|
||||
self.available_pilots.extend(new_pilots)
|
||||
|
||||
def replenish_lost_pilots(self) -> None:
|
||||
if not self.pilot_limits_enabled:
|
||||
return
|
||||
|
||||
replenish_count = min(
|
||||
self.game.settings.squadron_replenishment_rate,
|
||||
self.number_of_unfilled_pilot_slots,
|
||||
self._number_of_unfilled_pilot_slots,
|
||||
)
|
||||
if replenish_count > 0:
|
||||
self._recruit_pilots(replenish_count)
|
||||
@ -213,20 +226,23 @@ class Squadron:
|
||||
return len(self.current_roster)
|
||||
|
||||
@property
|
||||
def number_of_unfilled_pilot_slots(self) -> int:
|
||||
def _number_of_unfilled_pilot_slots(self) -> int:
|
||||
return self.game.settings.squadron_pilot_limit - len(self.active_pilots)
|
||||
|
||||
@property
|
||||
def number_of_available_pilots(self) -> int:
|
||||
return len(self.available_pilots)
|
||||
|
||||
def can_provide_pilots(self, count: int) -> bool:
|
||||
return not self.pilot_limits_enabled or self.number_of_available_pilots >= count
|
||||
|
||||
@property
|
||||
def has_available_pilots(self) -> bool:
|
||||
return bool(self.available_pilots)
|
||||
return not self.pilot_limits_enabled or bool(self.available_pilots)
|
||||
|
||||
@property
|
||||
def has_unfilled_pilot_slots(self) -> bool:
|
||||
return self.number_of_unfilled_pilot_slots > 0
|
||||
return not self.pilot_limits_enabled or self._number_of_unfilled_pilot_slots > 0
|
||||
|
||||
def can_auto_assign(self, task: FlightType) -> bool:
|
||||
return task in self.auto_assignable_mission_types
|
||||
@ -368,9 +384,16 @@ class AirWing:
|
||||
def squadrons_for(self, aircraft: AircraftType) -> Sequence[Squadron]:
|
||||
return self.squadrons[aircraft]
|
||||
|
||||
def squadrons_for_task(self, task: FlightType) -> Iterator[Squadron]:
|
||||
def can_auto_plan(self, task: FlightType) -> bool:
|
||||
try:
|
||||
next(self.auto_assignable_for_task(task))
|
||||
return True
|
||||
except StopIteration:
|
||||
return False
|
||||
|
||||
def auto_assignable_for_task(self, task: FlightType) -> Iterator[Squadron]:
|
||||
for squadron in self.iter_squadrons():
|
||||
if task in squadron.mission_types:
|
||||
if squadron.can_auto_assign(task):
|
||||
yield squadron
|
||||
|
||||
def auto_assignable_for_task_with_type(
|
||||
|
||||
@ -78,20 +78,33 @@ class GeneratorSettings:
|
||||
no_enemy_navy: bool
|
||||
|
||||
|
||||
@dataclass
|
||||
class ModSettings:
|
||||
a4_skyhawk: bool = False
|
||||
f22_raptor: bool = False
|
||||
hercules: bool = False
|
||||
jas39_gripen: bool = False
|
||||
su57_felon: bool = False
|
||||
frenchpack: bool = False
|
||||
high_digit_sams: bool = False
|
||||
|
||||
|
||||
class GameGenerator:
|
||||
def __init__(
|
||||
self,
|
||||
player: str,
|
||||
enemy: str,
|
||||
player: Faction,
|
||||
enemy: Faction,
|
||||
theater: ConflictTheater,
|
||||
settings: Settings,
|
||||
generator_settings: GeneratorSettings,
|
||||
mod_settings: ModSettings,
|
||||
) -> None:
|
||||
self.player = player
|
||||
self.enemy = enemy
|
||||
self.theater = theater
|
||||
self.settings = settings
|
||||
self.generator_settings = generator_settings
|
||||
self.mod_settings = mod_settings
|
||||
|
||||
def generate(self) -> Game:
|
||||
with logged_duration("TGO population"):
|
||||
@ -99,8 +112,8 @@ class GameGenerator:
|
||||
namegen.reset()
|
||||
self.prepare_theater()
|
||||
game = Game(
|
||||
player_name=self.player,
|
||||
enemy_name=self.enemy,
|
||||
player_faction=self.player.apply_mod_settings(self.mod_settings),
|
||||
enemy_faction=self.enemy.apply_mod_settings(self.mod_settings),
|
||||
theater=self.theater,
|
||||
start_date=self.generator_settings.start_date,
|
||||
settings=self.settings,
|
||||
@ -159,9 +172,9 @@ class ControlPointGroundObjectGenerator:
|
||||
@property
|
||||
def faction_name(self) -> str:
|
||||
if self.control_point.captured:
|
||||
return self.game.player_name
|
||||
return self.game.player_faction.name
|
||||
else:
|
||||
return self.game.enemy_name
|
||||
return self.game.enemy_faction.name
|
||||
|
||||
@property
|
||||
def faction(self) -> Faction:
|
||||
|
||||
@ -1,3 +1,34 @@
|
||||
"""Implements support for ground unit transfers between bases.
|
||||
|
||||
Ground units can be transferred between bases via a number of transport methods, and
|
||||
doing so can take multiple turns.
|
||||
|
||||
There are a few main concepts here:
|
||||
|
||||
* A TransferOrder is a request to move units from one base to another. It is described
|
||||
by its origin, destination, current position, and contents. TransferOrders persist
|
||||
across turns, and if no Transport is available to move the units in a given turn it
|
||||
will have no Transport assigned.
|
||||
* Transports: A Transport is the planned move of a group of units for a leg of the
|
||||
journey *this turn*. A Transport has an assigned mode of transportation and has
|
||||
vehicles assigned to move the units if needed. This might be a Convoy, a CargoShip, or
|
||||
an Airlift.
|
||||
|
||||
The TransportMap (more accurately, it's subtypes) is responsible for managing the
|
||||
transports moving from A to B for the turn. Transfers that are moving between A and B
|
||||
this turn will be added to the TransportMap, which will create a new transport if needed
|
||||
or add the units to an existing transport if one exists. This allows transfers from
|
||||
A->B->C and D->B->C to share a transport between B and C.
|
||||
|
||||
AirLifts do not use TransportMap because no merging will take place between orders. It
|
||||
instead uses AirLiftPlanner to create transport packages.
|
||||
|
||||
PendingTransfers manages all the incomplete transfer orders for the game. New transfer
|
||||
orders are registered with PendingTransfers and it is responsible for allocating
|
||||
transports and processing the turn's transit actions.
|
||||
|
||||
Routing is handled by TransitNetwork.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
@ -6,7 +37,6 @@ from collections import defaultdict
|
||||
from dataclasses import dataclass, field
|
||||
from functools import singledispatchmethod
|
||||
from typing import (
|
||||
Dict,
|
||||
Generic,
|
||||
Iterator,
|
||||
List,
|
||||
@ -29,7 +59,7 @@ from game.theater.transitnetwork import (
|
||||
)
|
||||
from game.utils import meters, nautical_miles
|
||||
from gen.ato import Package
|
||||
from gen.flights.ai_flight_planner_db import TRANSPORT_CAPABLE, aircraft_for_task
|
||||
from gen.flights.ai_flight_planner_db import aircraft_for_task
|
||||
from gen.flights.closestairfields import ObjectiveDistanceCache
|
||||
from gen.flights.flight import Flight, FlightType
|
||||
from gen.flights.flightplan import FlightPlanBuilder
|
||||
@ -72,10 +102,18 @@ class TransferOrder:
|
||||
player: bool = field(init=False)
|
||||
|
||||
#: The units being transferred.
|
||||
units: Dict[GroundUnitType, int]
|
||||
units: dict[GroundUnitType, int]
|
||||
|
||||
transport: Optional[Transport] = field(default=None)
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""Returns the text that should be displayed for the transfer."""
|
||||
count = self.size
|
||||
origin = self.origin.name
|
||||
destination = self.destination.name
|
||||
description = "Transfer" if self.player else "Enemy transfer"
|
||||
return f"{description} of {count} units from {origin} to {destination}"
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
self.position = self.origin
|
||||
self.player = self.origin.is_friendly(to_player=True)
|
||||
@ -91,12 +129,12 @@ class TransferOrder:
|
||||
|
||||
def kill_unit(self, unit_type: GroundUnitType) -> None:
|
||||
if unit_type not in self.units or not self.units[unit_type]:
|
||||
raise KeyError(f"{self.destination} has no {unit_type} remaining")
|
||||
raise KeyError(f"{self} has no {unit_type} remaining")
|
||||
self.units[unit_type] -= 1
|
||||
|
||||
@property
|
||||
def size(self) -> int:
|
||||
return sum(c for c in self.units.values())
|
||||
return sum(self.units.values())
|
||||
|
||||
def iter_units(self) -> Iterator[GroundUnitType]:
|
||||
for unit_type, count in self.units.items():
|
||||
@ -105,7 +143,7 @@ class TransferOrder:
|
||||
|
||||
@property
|
||||
def completed(self) -> bool:
|
||||
return self.destination == self.position or not self.units
|
||||
return self.destination == self.position or not self.size
|
||||
|
||||
def disband_at(self, location: ControlPoint) -> None:
|
||||
logging.info(f"Units halting at {location}.")
|
||||
@ -156,7 +194,7 @@ class Airlift(Transport):
|
||||
self.flight = flight
|
||||
|
||||
@property
|
||||
def units(self) -> Dict[GroundUnitType, int]:
|
||||
def units(self) -> dict[GroundUnitType, int]:
|
||||
return self.transfer.units
|
||||
|
||||
@property
|
||||
@ -261,8 +299,12 @@ class AirliftPlanner:
|
||||
required,
|
||||
available_aircraft,
|
||||
squadron.aircraft.dcs_unit_type.group_size_max,
|
||||
squadron.number_of_available_pilots,
|
||||
)
|
||||
# TODO: Use number_of_available_pilots directly once feature flag is gone.
|
||||
# The number of currently available pilots is not relevant when pilot limits
|
||||
# are disabled.
|
||||
if not squadron.can_provide_pilots(flight_size):
|
||||
flight_size = squadron.number_of_available_pilots
|
||||
capacity = flight_size * capacity_each
|
||||
|
||||
if capacity < self.transfer.size:
|
||||
@ -334,11 +376,11 @@ class MultiGroupTransport(MissionTarget, Transport):
|
||||
|
||||
@property
|
||||
def size(self) -> int:
|
||||
return sum(sum(t.units.values()) for t in self.transfers)
|
||||
return sum(t.size for t in self.transfers)
|
||||
|
||||
@property
|
||||
def units(self) -> dict[GroundUnitType, int]:
|
||||
units: Dict[GroundUnitType, int] = defaultdict(int)
|
||||
units: dict[GroundUnitType, int] = defaultdict(int)
|
||||
for transfer in self.transfers:
|
||||
for unit_type, count in transfer.units.items():
|
||||
units[unit_type] += count
|
||||
@ -414,8 +456,8 @@ TransportType = TypeVar("TransportType", bound=MultiGroupTransport)
|
||||
class TransportMap(Generic[TransportType]):
|
||||
def __init__(self) -> None:
|
||||
# Dict of origin -> destination -> transport.
|
||||
self.transports: Dict[
|
||||
ControlPoint, Dict[ControlPoint, TransportType]
|
||||
self.transports: dict[
|
||||
ControlPoint, dict[ControlPoint, TransportType]
|
||||
] = defaultdict(dict)
|
||||
|
||||
def create_transport(
|
||||
@ -592,7 +634,10 @@ class PendingTransfers:
|
||||
|
||||
def order_airlift_assets(self) -> None:
|
||||
for control_point in self.game.theater.controlpoints:
|
||||
self.order_airlift_assets_at(control_point)
|
||||
if self.game.air_wing_for(control_point.captured).can_auto_plan(
|
||||
FlightType.TRANSPORT
|
||||
):
|
||||
self.order_airlift_assets_at(control_point)
|
||||
|
||||
@staticmethod
|
||||
def desired_airlift_capacity(control_point: ControlPoint) -> int:
|
||||
@ -600,10 +645,10 @@ class PendingTransfers:
|
||||
|
||||
def current_airlift_capacity(self, control_point: ControlPoint) -> int:
|
||||
inventory = self.game.aircraft_inventory.for_control_point(control_point)
|
||||
squadrons = self.game.air_wing_for(control_point.captured).squadrons_for_task(
|
||||
FlightType.TRANSPORT
|
||||
)
|
||||
unit_types = {s.aircraft for s in squadrons}.intersection(TRANSPORT_CAPABLE)
|
||||
squadrons = self.game.air_wing_for(
|
||||
control_point.captured
|
||||
).auto_assignable_for_task(FlightType.TRANSPORT)
|
||||
unit_types = {s.aircraft for s in squadrons}
|
||||
return sum(
|
||||
count
|
||||
for unit_type, count in inventory.all_aircraft
|
||||
|
||||
@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, Optional, TYPE_CHECKING, Any
|
||||
from typing import Optional, TYPE_CHECKING, Any
|
||||
|
||||
from game.theater import ControlPoint
|
||||
from .dcs.groundunittype import GroundUnitType
|
||||
@ -28,16 +28,16 @@ class PendingUnitDeliveries:
|
||||
self.destination = destination
|
||||
|
||||
# Maps unit type to order quantity.
|
||||
self.units: Dict[UnitType, int] = defaultdict(int)
|
||||
self.units: dict[UnitType, int] = defaultdict(int)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Pending delivery to {self.destination}"
|
||||
|
||||
def order(self, units: Dict[UnitType, int]) -> None:
|
||||
def order(self, units: dict[UnitType, int]) -> None:
|
||||
for k, v in units.items():
|
||||
self.units[k] += v
|
||||
|
||||
def sell(self, units: Dict[UnitType, int]) -> None:
|
||||
def sell(self, units: dict[UnitType, int]) -> None:
|
||||
for k, v in units.items():
|
||||
self.units[k] -= v
|
||||
|
||||
@ -45,7 +45,15 @@ class PendingUnitDeliveries:
|
||||
self.refund(game, self.units)
|
||||
self.units = defaultdict(int)
|
||||
|
||||
def refund(self, game: Game, units: Dict[UnitType, int]) -> None:
|
||||
def refund_ground_units(self, game: Game) -> None:
|
||||
ground_units: dict[UnitType[Any], int] = {
|
||||
u: self.units[u] for u in self.units.keys() if isinstance(u, GroundUnitType)
|
||||
}
|
||||
self.refund(game, ground_units)
|
||||
for gu in ground_units.keys():
|
||||
del self.units[gu]
|
||||
|
||||
def refund(self, game: Game, units: dict[UnitType, int]) -> None:
|
||||
for unit_type, count in units.items():
|
||||
logging.info(f"Refunding {count} {unit_type} at {self.destination.name}")
|
||||
game.adjust_budget(
|
||||
@ -69,12 +77,11 @@ class PendingUnitDeliveries:
|
||||
f"{self.destination.name} lost its source for ground unit "
|
||||
"reinforcements. Refunding purchase price."
|
||||
)
|
||||
self.refund_all(game)
|
||||
return
|
||||
self.refund_ground_units(game)
|
||||
|
||||
bought_units: Dict[UnitType, int] = {}
|
||||
units_needing_transfer: Dict[GroundUnitType, int] = {}
|
||||
sold_units: Dict[UnitType, int] = {}
|
||||
bought_units: dict[UnitType, int] = {}
|
||||
units_needing_transfer: dict[GroundUnitType, int] = {}
|
||||
sold_units: dict[UnitType, int] = {}
|
||||
for unit_type, count in self.units.items():
|
||||
coalition = "Ally" if self.destination.captured else "Enemy"
|
||||
d: dict[Any, int]
|
||||
@ -102,11 +109,16 @@ class PendingUnitDeliveries:
|
||||
self.destination.base.commit_losses(sold_units)
|
||||
|
||||
if units_needing_transfer:
|
||||
if ground_unit_source is None:
|
||||
raise RuntimeError(
|
||||
f"ground unit source could not be found for {self.destination} but still tried to "
|
||||
f"transfer units to there"
|
||||
)
|
||||
ground_unit_source.base.commission_units(units_needing_transfer)
|
||||
self.create_transfer(game, ground_unit_source, units_needing_transfer)
|
||||
|
||||
def create_transfer(
|
||||
self, game: Game, source: ControlPoint, units: Dict[GroundUnitType, int]
|
||||
self, game: Game, source: ControlPoint, units: dict[GroundUnitType, int]
|
||||
) -> None:
|
||||
game.transfers.new_transfer(TransferOrder(source, self.destination, units))
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ from pathlib import Path
|
||||
|
||||
|
||||
def _build_version_string() -> str:
|
||||
components = ["4.0.0"]
|
||||
components = ["5.0.0"]
|
||||
build_number_path = Path("resources/buildnumber")
|
||||
if build_number_path.exists():
|
||||
with build_number_path.open("r") as build_number_file:
|
||||
@ -90,4 +90,10 @@ VERSION = _build_version_string()
|
||||
#:
|
||||
#: Version 6.1
|
||||
#: * Support for new Syrian airfields in DCS 2.7.2.7910.1 (Cyprus update).
|
||||
CAMPAIGN_FORMAT_VERSION = (6, 1)
|
||||
#:
|
||||
#: Version 7.0
|
||||
#: * DCS 2.7.2.7910.1 (Cyprus update) changed the IDs of scenery strike targets. Any
|
||||
#: mission using map buildings as strike targets must check and potentially recreate
|
||||
#: all those objectives. This definitely affects all Syria campaigns, other maps are
|
||||
#: not yet verified.
|
||||
CAMPAIGN_FORMAT_VERSION = (7, 0)
|
||||
|
||||
@ -26,7 +26,6 @@ from dcs.planes import (
|
||||
Su_33,
|
||||
Tu_22M3,
|
||||
)
|
||||
from dcs.planes import IL_78M
|
||||
from dcs.point import MovingPoint, PointAction
|
||||
from dcs.task import (
|
||||
AWACS,
|
||||
@ -66,7 +65,6 @@ from dcs.unitgroup import FlyingGroup, ShipGroup, StaticGroup
|
||||
from dcs.unittype import FlyingType
|
||||
|
||||
from game import db
|
||||
from game.data.cap_capabilities_db import GUNFIGHTERS
|
||||
from game.data.weapons import Pylon
|
||||
from game.dcs.aircrafttype import AircraftType
|
||||
from game.factions.faction import Faction
|
||||
@ -866,7 +864,7 @@ class AircraftConflictGenerator:
|
||||
group.task = CAP.name
|
||||
self._setup_group(group, package, flight, dynamic_runways)
|
||||
|
||||
if flight.unit_type not in GUNFIGHTERS:
|
||||
if not flight.unit_type.gunfighter:
|
||||
ammo_type = OptRTBOnOutOfAmmo.Values.AAM
|
||||
else:
|
||||
ammo_type = OptRTBOnOutOfAmmo.Values.Cannon
|
||||
@ -883,7 +881,7 @@ class AircraftConflictGenerator:
|
||||
group.task = FighterSweep.name
|
||||
self._setup_group(group, package, flight, dynamic_runways)
|
||||
|
||||
if flight.unit_type not in GUNFIGHTERS:
|
||||
if not flight.unit_type.gunfighter:
|
||||
ammo_type = OptRTBOnOutOfAmmo.Values.AAM
|
||||
else:
|
||||
ammo_type = OptRTBOnOutOfAmmo.Values.Cannon
|
||||
@ -1208,7 +1206,7 @@ class AircraftConflictGenerator:
|
||||
# under the current flight plans.
|
||||
# TODO: Make this smarter, it currently selects a random unit in the group for target,
|
||||
# this could be updated to make it pick the "best" two targets in the group.
|
||||
if flight.unit_type is AJS37 and flight.client_count:
|
||||
if flight.unit_type.dcs_unit_type is AJS37 and flight.client_count:
|
||||
viggen_target_points = [
|
||||
(idx, point)
|
||||
for idx, point in enumerate(filtered_points)
|
||||
@ -1372,9 +1370,10 @@ class PydcsWaypointBuilder:
|
||||
"""Viggen player aircraft consider any waypoint with a TOT set to be a target ("M") waypoint.
|
||||
If the flight is a player controlled Viggen flight, no TOT should be set on any waypoint except actual target waypoints.
|
||||
"""
|
||||
if (self.flight.client_count > 0 and self.flight.unit_type == AJS37) and (
|
||||
self.waypoint.waypoint_type not in TARGET_WAYPOINTS
|
||||
):
|
||||
if (
|
||||
self.flight.client_count > 0
|
||||
and self.flight.unit_type.dcs_unit_type == AJS37
|
||||
) and (self.waypoint.waypoint_type not in TARGET_WAYPOINTS):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@ -1787,7 +1786,7 @@ class RaceTrackBuilder(PydcsWaypointBuilder):
|
||||
def configure_refueling_actions(self, waypoint: MovingPoint) -> None:
|
||||
waypoint.add_task(Tanker())
|
||||
|
||||
if self.flight.unit_type != IL_78M:
|
||||
if self.flight.unit_type.dcs_unit_type.tacan:
|
||||
tanker_info = self.air_support.tankers[-1]
|
||||
tacan = tanker_info.tacan
|
||||
tacan_callsign = {
|
||||
|
||||
@ -153,6 +153,8 @@ class Conflict:
|
||||
if theater.is_on_land(pos):
|
||||
return pos
|
||||
pos = initial.point_from_heading(opposite_heading(heading), distance)
|
||||
if theater.is_on_land(pos):
|
||||
return pos
|
||||
if coerce:
|
||||
pos = theater.nearest_land_pos(initial)
|
||||
return pos
|
||||
|
||||
@ -178,7 +178,7 @@ class AircraftAllocator:
|
||||
aircraft, task
|
||||
)
|
||||
for squadron in squadrons:
|
||||
if squadron.number_of_available_pilots >= flight.num_aircraft:
|
||||
if squadron.can_provide_pilots(flight.num_aircraft):
|
||||
inventory.remove_aircraft(aircraft, flight.num_aircraft)
|
||||
return airfield, squadron
|
||||
return None
|
||||
@ -604,27 +604,35 @@ class CoalitionMissionPlanner:
|
||||
also possible for the player to exclude mission types from their squadron
|
||||
designs.
|
||||
"""
|
||||
all_compatible = aircraft_for_task(mission_type)
|
||||
for squadron in self.game.air_wing_for(self.is_player).iter_squadrons():
|
||||
if (
|
||||
squadron.aircraft in all_compatible
|
||||
and mission_type in squadron.auto_assignable_mission_types
|
||||
):
|
||||
return True
|
||||
return False
|
||||
return self.game.air_wing_for(self.is_player).can_auto_plan(mission_type)
|
||||
|
||||
@property
|
||||
def oca_aircraft_plannable(self) -> bool:
|
||||
return (
|
||||
self.air_wing_can_plan(FlightType.OCA_AIRCRAFT)
|
||||
and self.game.settings.default_start_type == "Cold"
|
||||
def critical_missions(self) -> Iterator[ProposedMission]:
|
||||
"""Identifies the most important missions to plan this turn.
|
||||
|
||||
Non-critical missions that cannot be fulfilled will create purchase
|
||||
orders for the next turn. Critical missions will create a purchase order
|
||||
unless the mission can be doubly fulfilled. In other words, the AI will
|
||||
attempt to have *double* the aircraft it needs for these missions to
|
||||
ensure that they can be planned again next turn even if all aircraft are
|
||||
eliminated this turn.
|
||||
"""
|
||||
|
||||
# Find farthest, friendly CP for AEWC.
|
||||
yield ProposedMission(
|
||||
self.objective_finder.farthest_friendly_control_point(),
|
||||
[ProposedFlight(FlightType.AEWC, 1, self.MAX_AWEC_RANGE)],
|
||||
# Supports all the early CAP flights, so should be in the air ASAP.
|
||||
asap=True,
|
||||
)
|
||||
|
||||
yield ProposedMission(
|
||||
self.objective_finder.closest_friendly_control_point(),
|
||||
[ProposedFlight(FlightType.REFUELING, 1, self.MAX_TANKER_RANGE)],
|
||||
)
|
||||
|
||||
def propose_barcap(self) -> Iterator[ProposedMission]:
|
||||
# Find friendly CPs within 100 nmi from an enemy airfield, plan CAP.
|
||||
for cp in self.objective_finder.vulnerable_control_points():
|
||||
# Plan CAP in such a way, that it is established during the whole desired
|
||||
# mission length.
|
||||
# Plan CAP in such a way, that it is established during the whole desired mission length
|
||||
for _ in range(
|
||||
0,
|
||||
int(self.game.settings.desired_player_mission_duration.total_seconds()),
|
||||
@ -637,31 +645,36 @@ class CoalitionMissionPlanner:
|
||||
],
|
||||
)
|
||||
|
||||
def propose_cas(self) -> Iterator[ProposedMission]:
|
||||
# Find front lines, plan CAS.
|
||||
for front_line in self.objective_finder.front_lines():
|
||||
flights = [ProposedFlight(FlightType.CAS, 2, self.MAX_CAS_RANGE)]
|
||||
if self.air_wing_can_plan(FlightType.TARCAP):
|
||||
# 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.
|
||||
flights.append(ProposedFlight(FlightType.TARCAP, 2, self.MAX_CAP_RANGE))
|
||||
yield ProposedMission(front_line, flights)
|
||||
yield ProposedMission(
|
||||
front_line,
|
||||
[
|
||||
ProposedFlight(FlightType.CAS, 2, self.MAX_CAS_RANGE),
|
||||
# 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),
|
||||
],
|
||||
)
|
||||
|
||||
def propose_missions(self) -> Iterator[ProposedMission]:
|
||||
"""Identifies and iterates over potential mission in priority order."""
|
||||
yield from self.critical_missions()
|
||||
|
||||
def propose_dead(self) -> Iterator[ProposedMission]:
|
||||
# Find enemy SAM sites with ranges that cover friendly CPs, front lines,
|
||||
# or objects, plan DEAD.
|
||||
# Find enemy SAM sites with ranges that extend to within 50 nmi of
|
||||
@ -686,10 +699,7 @@ class CoalitionMissionPlanner:
|
||||
else:
|
||||
flights.append(
|
||||
ProposedFlight(
|
||||
FlightType.SEAD_ESCORT,
|
||||
2,
|
||||
self.MAX_SEAD_RANGE,
|
||||
EscortType.Sead,
|
||||
FlightType.SEAD_ESCORT, 2, self.MAX_SEAD_RANGE, EscortType.Sead
|
||||
)
|
||||
)
|
||||
# TODO: Max escort range.
|
||||
@ -700,7 +710,6 @@ class CoalitionMissionPlanner:
|
||||
)
|
||||
yield ProposedMission(sam, flights)
|
||||
|
||||
def propose_convoy_interdiction(self) -> Iterator[ProposedMission]:
|
||||
# These will only rarely get planned. When a convoy is travelling multiple legs,
|
||||
# they're targetable after the first leg. The reason for this is that
|
||||
# procurement happens *after* mission planning so that the missions that could
|
||||
@ -729,7 +738,6 @@ class CoalitionMissionPlanner:
|
||||
],
|
||||
)
|
||||
|
||||
def propose_shipping_interdiction(self) -> Iterator[ProposedMission]:
|
||||
for ship in self.objective_finder.cargo_ships():
|
||||
yield ProposedMission(
|
||||
ship,
|
||||
@ -745,7 +753,6 @@ class CoalitionMissionPlanner:
|
||||
],
|
||||
)
|
||||
|
||||
def propose_naval_strikes(self) -> Iterator[ProposedMission]:
|
||||
for group in self.objective_finder.threatening_ships():
|
||||
yield ProposedMission(
|
||||
group,
|
||||
@ -761,7 +768,6 @@ class CoalitionMissionPlanner:
|
||||
],
|
||||
)
|
||||
|
||||
def propose_bai(self) -> Iterator[ProposedMission]:
|
||||
for group in self.objective_finder.threatening_vehicle_groups():
|
||||
yield ProposedMission(
|
||||
group,
|
||||
@ -777,25 +783,16 @@ class CoalitionMissionPlanner:
|
||||
],
|
||||
)
|
||||
|
||||
def propose_oca_strikes(self) -> Iterator[ProposedMission]:
|
||||
for target in self.objective_finder.oca_targets(min_aircraft=20):
|
||||
flights = []
|
||||
if self.air_wing_can_plan(FlightType.OCA_RUNWAY):
|
||||
flights.append(
|
||||
ProposedFlight(FlightType.OCA_RUNWAY, 2, self.MAX_OCA_RANGE)
|
||||
)
|
||||
if self.oca_aircraft_plannable:
|
||||
flights = [
|
||||
ProposedFlight(FlightType.OCA_RUNWAY, 2, self.MAX_OCA_RANGE),
|
||||
]
|
||||
if self.game.settings.default_start_type == "Cold":
|
||||
# Only schedule if the default start type is Cold. If the player
|
||||
# has set anything else there are no targets to hit.
|
||||
flights.append(
|
||||
ProposedFlight(FlightType.OCA_AIRCRAFT, 2, self.MAX_OCA_RANGE)
|
||||
)
|
||||
if not flights:
|
||||
raise RuntimeError(
|
||||
"Attempted planning of OCA strikes but neither OCA/Runway nor "
|
||||
f"OCA/Aircraft are plannable for {self.faction.name} with the "
|
||||
"current game settings."
|
||||
)
|
||||
flights.extend(
|
||||
[
|
||||
# TODO: Max escort range.
|
||||
@ -809,7 +806,7 @@ class CoalitionMissionPlanner:
|
||||
)
|
||||
yield ProposedMission(target, flights)
|
||||
|
||||
def propose_building_strikes(self) -> Iterator[ProposedMission]:
|
||||
# Plan strike missions.
|
||||
for target in self.objective_finder.strike_targets():
|
||||
yield ProposedMission(
|
||||
target,
|
||||
@ -828,48 +825,6 @@ class CoalitionMissionPlanner:
|
||||
],
|
||||
)
|
||||
|
||||
def propose_missions(self) -> Iterator[ProposedMission]:
|
||||
"""Identifies and iterates over potential mission in priority order."""
|
||||
# Find farthest, friendly CP for AEWC.
|
||||
if self.air_wing_can_plan(FlightType.AEWC):
|
||||
yield ProposedMission(
|
||||
self.objective_finder.farthest_friendly_control_point(),
|
||||
[ProposedFlight(FlightType.AEWC, 1, self.MAX_AWEC_RANGE)],
|
||||
# Supports all the early CAP flights, so should be in the air ASAP.
|
||||
asap=True,
|
||||
)
|
||||
|
||||
if self.air_wing_can_plan(FlightType.REFUELING):
|
||||
yield ProposedMission(
|
||||
self.objective_finder.closest_friendly_control_point(),
|
||||
[ProposedFlight(FlightType.REFUELING, 1, self.MAX_TANKER_RANGE)],
|
||||
)
|
||||
|
||||
if self.air_wing_can_plan(FlightType.BARCAP):
|
||||
yield from self.propose_barcap()
|
||||
|
||||
if self.air_wing_can_plan(FlightType.CAS):
|
||||
yield from self.propose_cas()
|
||||
|
||||
if self.air_wing_can_plan(FlightType.DEAD):
|
||||
yield from self.propose_dead()
|
||||
|
||||
if self.air_wing_can_plan(FlightType.BAI):
|
||||
yield from self.propose_convoy_interdiction()
|
||||
|
||||
if self.air_wing_can_plan(FlightType.ANTISHIP):
|
||||
yield from self.propose_shipping_interdiction()
|
||||
yield from self.propose_naval_strikes()
|
||||
|
||||
if self.air_wing_can_plan(FlightType.BAI):
|
||||
yield from self.propose_bai()
|
||||
|
||||
if self.air_wing_can_plan(FlightType.OCA_RUNWAY) or self.oca_aircraft_plannable:
|
||||
yield from self.propose_oca_strikes()
|
||||
|
||||
if self.air_wing_can_plan(FlightType.STRIKE):
|
||||
yield from self.propose_building_strikes()
|
||||
|
||||
def plan_missions(self) -> None:
|
||||
"""Identifies and plans mission for the turn."""
|
||||
player = "Blue" if self.is_player else "Red"
|
||||
@ -878,6 +833,11 @@ class CoalitionMissionPlanner:
|
||||
for proposed_mission in self.propose_missions():
|
||||
self.plan_mission(proposed_mission, tracer)
|
||||
|
||||
with logged_duration(f"{player} reserve mission planning"):
|
||||
with MultiEventTracer() as tracer:
|
||||
for critical_mission in self.critical_missions():
|
||||
self.plan_mission(critical_mission, tracer, reserves=True)
|
||||
|
||||
with logged_duration(f"{player} mission scheduling"):
|
||||
self.stagger_missions()
|
||||
|
||||
|
||||
@ -111,7 +111,6 @@ from pydcs_extensions.a4ec.a4ec import A_4E_C
|
||||
from pydcs_extensions.f22a.f22a import F_22A
|
||||
from pydcs_extensions.hercules.hercules import Hercules
|
||||
from pydcs_extensions.jas39.jas39 import JAS39Gripen, JAS39Gripen_AG
|
||||
from pydcs_extensions.mb339.mb339 import MB_339PAN
|
||||
from pydcs_extensions.su57.su57 import Su_57
|
||||
|
||||
# All aircraft lists are in priority order. Aircraft higher in the list will be
|
||||
@ -219,7 +218,6 @@ CAS_CAPABLE = [
|
||||
F_5E_3,
|
||||
F_86F_Sabre,
|
||||
C_101CC,
|
||||
MB_339PAN,
|
||||
L_39ZA,
|
||||
A_20G,
|
||||
Ju_88A4,
|
||||
@ -332,7 +330,6 @@ STRIKE_CAPABLE = [
|
||||
MiG_15bis,
|
||||
F_5E_3,
|
||||
F_86F_Sabre,
|
||||
MB_339PAN,
|
||||
C_101CC,
|
||||
L_39ZA,
|
||||
B_17G,
|
||||
|
||||
@ -16,17 +16,6 @@ from functools import cached_property
|
||||
from typing import Iterator, List, Optional, Set, TYPE_CHECKING, Tuple
|
||||
|
||||
from dcs.mapping import Point
|
||||
from dcs.planes import (
|
||||
E_3A,
|
||||
E_2C,
|
||||
A_50,
|
||||
IL_78M,
|
||||
KC130,
|
||||
KC135MPRS,
|
||||
KC_135,
|
||||
KJ_2000,
|
||||
S_3B_Tanker,
|
||||
)
|
||||
from dcs.unit import Unit
|
||||
from shapely.geometry import Point as ShapelyPoint
|
||||
|
||||
@ -38,8 +27,9 @@ from game.theater import (
|
||||
MissionTarget,
|
||||
SamGroundObject,
|
||||
TheaterGroundObject,
|
||||
NavalControlPoint,
|
||||
)
|
||||
from game.theater.theatergroundobject import EwrGroundObject
|
||||
from game.theater.theatergroundobject import EwrGroundObject, NavalGroundObject
|
||||
from game.utils import Distance, Speed, feet, meters, nautical_miles, knots
|
||||
from .closestairfields import ObjectiveDistanceCache
|
||||
from .flight import Flight, FlightType, FlightWaypoint, FlightWaypointType
|
||||
@ -1092,15 +1082,8 @@ class FlightPlanBuilder:
|
||||
|
||||
orbit_location = self.aewc_orbit(location)
|
||||
|
||||
# As high as possible to maximize detection and on-station time.
|
||||
if flight.unit_type == E_2C:
|
||||
patrol_alt = feet(30000)
|
||||
elif flight.unit_type == E_3A:
|
||||
patrol_alt = feet(35000)
|
||||
elif flight.unit_type == A_50:
|
||||
patrol_alt = feet(33000)
|
||||
elif flight.unit_type == KJ_2000:
|
||||
patrol_alt = feet(40000)
|
||||
if flight.unit_type.patrol_altitude is not None:
|
||||
patrol_alt = flight.unit_type.patrol_altitude
|
||||
else:
|
||||
patrol_alt = feet(25000)
|
||||
|
||||
@ -1164,12 +1147,9 @@ class FlightPlanBuilder:
|
||||
|
||||
from game.transfers import CargoShip
|
||||
|
||||
if isinstance(location, ControlPoint):
|
||||
if not location.is_fleet:
|
||||
raise InvalidObjectiveLocation(flight.flight_type, location)
|
||||
# The first group generated will be the carrier group itself.
|
||||
targets = self.anti_ship_targets_for_tgo(location.ground_objects[0])
|
||||
elif isinstance(location, TheaterGroundObject):
|
||||
if isinstance(location, NavalControlPoint):
|
||||
targets = self.anti_ship_targets_for_tgo(location.find_main_tgo())
|
||||
elif isinstance(location, NavalGroundObject):
|
||||
targets = self.anti_ship_targets_for_tgo(location)
|
||||
elif isinstance(location, CargoShip):
|
||||
targets = [StrikeTarget(location.name, location)]
|
||||
@ -1680,31 +1660,17 @@ class FlightPlanBuilder:
|
||||
builder = WaypointBuilder(flight, self.game, self.is_player)
|
||||
|
||||
tanker_type = flight.unit_type
|
||||
if tanker_type is KC_135:
|
||||
# ~300 knots IAS.
|
||||
speed = knots(445)
|
||||
altitude = feet(24000)
|
||||
elif tanker_type is KC135MPRS:
|
||||
# ~300 knots IAS.
|
||||
speed = knots(440)
|
||||
altitude = feet(23000)
|
||||
elif tanker_type is KC130:
|
||||
# ~210 knots IAS, roughly the max for the KC-130 at altitude.
|
||||
speed = knots(370)
|
||||
altitude = feet(22000)
|
||||
elif tanker_type is S_3B_Tanker:
|
||||
# ~265 knots IAS.
|
||||
speed = knots(320)
|
||||
altitude = feet(12000)
|
||||
elif tanker_type is IL_78M:
|
||||
# ~280 knots IAS.
|
||||
speed = knots(400)
|
||||
altitude = feet(21000)
|
||||
if tanker_type.patrol_altitude is not None:
|
||||
altitude = tanker_type.patrol_altitude
|
||||
else:
|
||||
# ~280 knots IAS.
|
||||
speed = knots(400)
|
||||
altitude = feet(21000)
|
||||
|
||||
if tanker_type.patrol_speed is not None:
|
||||
speed = tanker_type.patrol_speed
|
||||
else:
|
||||
# ~280 knots IAS at 21000.
|
||||
speed = knots(400)
|
||||
|
||||
racetrack = builder.race_track(racetrack_start, racetrack_end, altitude)
|
||||
|
||||
return RefuelingFlightPlan(
|
||||
|
||||
1
pydcs
1
pydcs
@ -1 +0,0 @@
|
||||
Subproject commit a459d8e1b0bd59bbd7f35390d7ad1bedc0caf76b
|
||||
@ -1,539 +0,0 @@
|
||||
from enum import Enum
|
||||
|
||||
from dcs import task
|
||||
from dcs.planes import PlaneType
|
||||
from dcs.weapons_data import Weapons
|
||||
|
||||
from pydcs_extensions.weapon_injector import inject_weapons
|
||||
|
||||
|
||||
class MB_339PAN_Weapons:
|
||||
ARF8M3_TP = {"clsid": "{ARF8M3_TP}", "name": "ARF8M3 TP", "weight": None}
|
||||
BRD_4_250_4_MK_76_2_ARF_8M3TP_ = {
|
||||
"clsid": "{BRD-4-250}",
|
||||
"name": "BRD-4-250(4*MK.76+2*ARF-8M3TP)",
|
||||
"weight": 137.6,
|
||||
}
|
||||
Color_Oil_Tank = {"clsid": "{COLOR-TANK}", "name": "Color Oil Tank", "weight": 183}
|
||||
Empty_Pylon = {"clsid": "{VOID-PYLON-MB339A}", "name": "Empty Pylon", "weight": 20}
|
||||
Fuel_Tank_330lt = {
|
||||
"clsid": "{FUEL-SUBAL_TANK-330}",
|
||||
"name": "Fuel Tank 330lt",
|
||||
"weight": 315,
|
||||
}
|
||||
GunPod_AN_M3 = {"clsid": "{MB339-AN-M3_L}", "name": "GunPod AN/M3", "weight": 75}
|
||||
GunPod_AN_M3_ = {"clsid": "{MB339-AN-M3_R}", "name": "GunPod AN/M3", "weight": 75}
|
||||
GunPod_DEFA553 = {
|
||||
"clsid": "{MB339-DEFA553_L}",
|
||||
"name": "GunPod DEFA553",
|
||||
"weight": 190,
|
||||
}
|
||||
GunPod_DEFA553_ = {
|
||||
"clsid": "{MB339-DEFA553_R}",
|
||||
"name": "GunPod DEFA553",
|
||||
"weight": 190,
|
||||
}
|
||||
LAU_10___4_ZUNI_MK_71___ = {
|
||||
"clsid": "{LAU-10}",
|
||||
"name": "LAU-10 - 4 ZUNI MK 71",
|
||||
"weight": 308,
|
||||
}
|
||||
LR_25___25_ARF_8M3_API_ = {
|
||||
"clsid": "{LR-25API}",
|
||||
"name": "LR-25 - 25 ARF/8M3(API)",
|
||||
"weight": 141,
|
||||
}
|
||||
LR_25___25_ARF_8M3_HEI_ = {
|
||||
"clsid": "{LR-25HEI}",
|
||||
"name": "LR-25 - 25 ARF/8M3(HEI)",
|
||||
"weight": 161,
|
||||
}
|
||||
MAK79_2_MK_20 = {"clsid": "{MAK79_MK20 2L}", "name": "MAK79 2 MK-20", "weight": 464}
|
||||
MAK79_2_MK_20_ = {
|
||||
"clsid": "{MAK79_MK20 2R}",
|
||||
"name": "MAK79 2 MK-20",
|
||||
"weight": 464,
|
||||
}
|
||||
MAK79_MK_20 = {"clsid": "{MAK79_MK20 1R}", "name": "MAK79 MK-20", "weight": 232}
|
||||
MAK79_MK_20_ = {"clsid": "{MAK79_MK20 1L}", "name": "MAK79 MK-20", "weight": 232}
|
||||
MB339_Black_Smoke = {
|
||||
"clsid": "{SMOKE-BLACK-MB339}",
|
||||
"name": "MB339 Black Smoke",
|
||||
"weight": 1,
|
||||
}
|
||||
MB339_Green_Smoke = {
|
||||
"clsid": "{SMOKE-GREEN-MB339}",
|
||||
"name": "MB339 Green Smoke",
|
||||
"weight": 1,
|
||||
}
|
||||
MB339_ORANGE_Smoke = {
|
||||
"clsid": "{SMOKE-ORANGE-MB339}",
|
||||
"name": "MB339 ORANGE Smoke",
|
||||
"weight": 1,
|
||||
}
|
||||
MB339_Red_Smoke = {
|
||||
"clsid": "{SMOKE-RED-MB339}",
|
||||
"name": "MB339 Red Smoke",
|
||||
"weight": 1,
|
||||
}
|
||||
MB339_White_Smoke = {
|
||||
"clsid": "{SMOKE-WHITE-MB339}",
|
||||
"name": "MB339 White Smoke",
|
||||
"weight": 1,
|
||||
}
|
||||
MB339_YELLOW_Smoke = {
|
||||
"clsid": "{SMOKE-YELLOW-MB339}",
|
||||
"name": "MB339 YELLOW Smoke",
|
||||
"weight": 1,
|
||||
}
|
||||
MK76 = {"clsid": "{MK76}", "name": "MK76", "weight": 11.3}
|
||||
Tip_Fuel_Tank_500lt = {
|
||||
"clsid": "{FUEL-TIP-TANK-500-L}",
|
||||
"name": "Tip Fuel Tank 500lt",
|
||||
"weight": 471,
|
||||
}
|
||||
Tip_Fuel_Tank_500lt_ = {
|
||||
"clsid": "{FUEL-TIP-TANK-500-R}",
|
||||
"name": "Tip Fuel Tank 500lt",
|
||||
"weight": 471,
|
||||
}
|
||||
Tip_Fuel_Tank_Ellittici_320lt = {
|
||||
"clsid": "{FUEL-TIP-ELLITTIC-L}",
|
||||
"name": "Tip Fuel Tank Ellittici 320lt",
|
||||
"weight": 314.2,
|
||||
}
|
||||
Tip_Fuel_Tank_Ellittici_320lt_ = {
|
||||
"clsid": "{FUEL-TIP-ELLITTIC-R}",
|
||||
"name": "Tip Fuel Tank Ellittici 320lt",
|
||||
"weight": 314.2,
|
||||
}
|
||||
|
||||
|
||||
inject_weapons(MB_339PAN_Weapons)
|
||||
|
||||
|
||||
class MB_339PAN(PlaneType):
|
||||
id = "MB-339PAN"
|
||||
flyable = True
|
||||
height = 4.77
|
||||
width = 10.5
|
||||
length = 12.13
|
||||
fuel_max = 626
|
||||
max_speed = 763.2
|
||||
category = "Interceptor" # {78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F}
|
||||
radio_frequency = 124
|
||||
|
||||
panel_radio = {
|
||||
1: {
|
||||
"channels": {
|
||||
1: 225,
|
||||
2: 258,
|
||||
4: 270,
|
||||
8: 257,
|
||||
16: 252,
|
||||
17: 268,
|
||||
9: 253,
|
||||
18: 269,
|
||||
5: 255,
|
||||
10: 263,
|
||||
20: 269,
|
||||
11: 267,
|
||||
3: 260,
|
||||
6: 259,
|
||||
12: 254,
|
||||
13: 264,
|
||||
7: 262,
|
||||
14: 266,
|
||||
19: 268,
|
||||
15: 265,
|
||||
},
|
||||
},
|
||||
2: {
|
||||
"channels": {
|
||||
1: 225,
|
||||
2: 258,
|
||||
4: 270,
|
||||
8: 257,
|
||||
16: 252,
|
||||
17: 268,
|
||||
9: 253,
|
||||
18: 269,
|
||||
5: 255,
|
||||
10: 263,
|
||||
20: 269,
|
||||
30: 263,
|
||||
21: 225,
|
||||
11: 267,
|
||||
22: 258,
|
||||
3: 260,
|
||||
6: 259,
|
||||
12: 254,
|
||||
24: 270,
|
||||
19: 268,
|
||||
25: 255,
|
||||
13: 264,
|
||||
26: 259,
|
||||
27: 262,
|
||||
7: 262,
|
||||
14: 266,
|
||||
28: 257,
|
||||
23: 260,
|
||||
29: 253,
|
||||
15: 265,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
property_defaults = {
|
||||
"SoloFlight": False,
|
||||
"NetCrewControlPriority": 1,
|
||||
}
|
||||
|
||||
class Properties:
|
||||
class SoloFlight:
|
||||
id = "SoloFlight"
|
||||
|
||||
class NetCrewControlPriority:
|
||||
id = "NetCrewControlPriority"
|
||||
|
||||
class Values:
|
||||
Pilot = 0
|
||||
Instructor = 1
|
||||
Ask_Always = -1
|
||||
Equally_Responsible = -2
|
||||
|
||||
class Liveries:
|
||||
class Georgia(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Syria(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Finland(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Australia(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Germany(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class SaudiArabia(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Israel(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Croatia(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class CzechRepublic(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Norway(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Romania(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Spain(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Ukraine(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Belgium(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Slovakia(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Greece(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class UK(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Insurgents(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Hungary(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class France(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Abkhazia(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Russia(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Sweden(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Austria(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Switzerland(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Italy(Enum):
|
||||
MB339PAN__Frecce_Tricolori = "MB339PAN 'Frecce Tricolori'"
|
||||
MB339A__SVBIA____FACTORY = "MB339A 'SVBIA' - FACTORY"
|
||||
MB339A__61BRIGATA____CAMO = "MB339A '61BRIGATA' - CAMO"
|
||||
MB339A__61STORMO____CAMO = "MB339A '61STORMO' - CAMO"
|
||||
MB339A__61STORMO____GREY = "MB339A '61STORMO' - GREY"
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class SouthOssetia(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class SouthKorea(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Iran(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class China(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Pakistan(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Belarus(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class NorthKorea(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Iraq(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Kazakhstan(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Bulgaria(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Serbia(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class India(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class USAFAggressors(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class USA(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Denmark(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Egypt(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Canada(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class TheNetherlands(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Turkey(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Japan(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Poland(Enum):
|
||||
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
|
||||
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
|
||||
MB339__Factory = "MB339 'Factory'"
|
||||
|
||||
class Pylon1:
|
||||
Tip_Fuel_Tank_500lt = (1, MB_339PAN_Weapons.Tip_Fuel_Tank_500lt)
|
||||
Tip_Fuel_Tank_Ellittici_320lt = (
|
||||
1,
|
||||
MB_339PAN_Weapons.Tip_Fuel_Tank_Ellittici_320lt,
|
||||
)
|
||||
|
||||
class Pylon2:
|
||||
Empty_Pylon = (2, MB_339PAN_Weapons.Empty_Pylon)
|
||||
LR_25___25_ARF_8M3_HEI_ = (2, MB_339PAN_Weapons.LR_25___25_ARF_8M3_HEI_)
|
||||
LR_25___25_ARF_8M3_API_ = (2, MB_339PAN_Weapons.LR_25___25_ARF_8M3_API_)
|
||||
Mk_82 = (2, Weapons.Mk_82)
|
||||
Matra_Type_155_Rocket_Pod = (2, Weapons.Matra_Type_155_Rocket_Pod)
|
||||
|
||||
class Pylon3:
|
||||
Fuel_Tank_330lt = (3, MB_339PAN_Weapons.Fuel_Tank_330lt)
|
||||
Empty_Pylon = (3, MB_339PAN_Weapons.Empty_Pylon)
|
||||
LR_25___25_ARF_8M3_HEI_ = (3, MB_339PAN_Weapons.LR_25___25_ARF_8M3_HEI_)
|
||||
LR_25___25_ARF_8M3_API_ = (3, MB_339PAN_Weapons.LR_25___25_ARF_8M3_API_)
|
||||
Mk_82 = (3, Weapons.Mk_82)
|
||||
LAU_10___4_ZUNI_MK_71___ = (3, MB_339PAN_Weapons.LAU_10___4_ZUNI_MK_71___)
|
||||
BRD_4_250_4_MK_76_2_ARF_8M3TP_ = (
|
||||
3,
|
||||
MB_339PAN_Weapons.BRD_4_250_4_MK_76_2_ARF_8M3TP_,
|
||||
)
|
||||
Matra_Type_155_Rocket_Pod = (3, Weapons.Matra_Type_155_Rocket_Pod)
|
||||
|
||||
class Pylon4:
|
||||
Color_Oil_Tank = (4, MB_339PAN_Weapons.Color_Oil_Tank)
|
||||
Empty_Pylon = (4, MB_339PAN_Weapons.Empty_Pylon)
|
||||
GunPod_AN_M3 = (4, MB_339PAN_Weapons.GunPod_AN_M3)
|
||||
GunPod_DEFA553 = (4, MB_339PAN_Weapons.GunPod_DEFA553)
|
||||
LR_25___25_ARF_8M3_HEI_ = (4, MB_339PAN_Weapons.LR_25___25_ARF_8M3_HEI_)
|
||||
LR_25___25_ARF_8M3_API_ = (4, MB_339PAN_Weapons.LR_25___25_ARF_8M3_API_)
|
||||
Mk_82 = (4, Weapons.Mk_82)
|
||||
Matra_Type_155_Rocket_Pod = (4, Weapons.Matra_Type_155_Rocket_Pod)
|
||||
|
||||
class Pylon5:
|
||||
MB339_Red_Smoke = (5, MB_339PAN_Weapons.MB339_Red_Smoke)
|
||||
MB339_Green_Smoke = (5, MB_339PAN_Weapons.MB339_Green_Smoke)
|
||||
MB339_YELLOW_Smoke = (5, MB_339PAN_Weapons.MB339_YELLOW_Smoke)
|
||||
MB339_ORANGE_Smoke = (5, MB_339PAN_Weapons.MB339_ORANGE_Smoke)
|
||||
MB339_Black_Smoke = (5, MB_339PAN_Weapons.MB339_Black_Smoke)
|
||||
|
||||
class Pylon6:
|
||||
MB339_White_Smoke = (6, MB_339PAN_Weapons.MB339_White_Smoke)
|
||||
|
||||
class Pylon7:
|
||||
Color_Oil_Tank = (7, MB_339PAN_Weapons.Color_Oil_Tank)
|
||||
Empty_Pylon = (7, MB_339PAN_Weapons.Empty_Pylon)
|
||||
GunPod_AN_M3_ = (7, MB_339PAN_Weapons.GunPod_AN_M3_)
|
||||
GunPod_DEFA553_ = (7, MB_339PAN_Weapons.GunPod_DEFA553_)
|
||||
LR_25___25_ARF_8M3_HEI_ = (7, MB_339PAN_Weapons.LR_25___25_ARF_8M3_HEI_)
|
||||
LR_25___25_ARF_8M3_API_ = (7, MB_339PAN_Weapons.LR_25___25_ARF_8M3_API_)
|
||||
Mk_82 = (7, Weapons.Mk_82)
|
||||
Matra_Type_155_Rocket_Pod = (7, Weapons.Matra_Type_155_Rocket_Pod)
|
||||
|
||||
class Pylon8:
|
||||
Fuel_Tank_330lt = (8, MB_339PAN_Weapons.Fuel_Tank_330lt)
|
||||
Empty_Pylon = (8, MB_339PAN_Weapons.Empty_Pylon)
|
||||
LR_25___25_ARF_8M3_HEI_ = (8, MB_339PAN_Weapons.LR_25___25_ARF_8M3_HEI_)
|
||||
LR_25___25_ARF_8M3_API_ = (8, MB_339PAN_Weapons.LR_25___25_ARF_8M3_API_)
|
||||
Mk_82 = (8, Weapons.Mk_82)
|
||||
LAU_10___4_ZUNI_MK_71___ = (8, MB_339PAN_Weapons.LAU_10___4_ZUNI_MK_71___)
|
||||
Matra_Type_155_Rocket_Pod = (8, Weapons.Matra_Type_155_Rocket_Pod)
|
||||
BRD_4_250_4_MK_76_2_ARF_8M3TP_ = (
|
||||
8,
|
||||
MB_339PAN_Weapons.BRD_4_250_4_MK_76_2_ARF_8M3TP_,
|
||||
)
|
||||
|
||||
class Pylon9:
|
||||
Empty_Pylon = (9, MB_339PAN_Weapons.Empty_Pylon)
|
||||
LR_25___25_ARF_8M3_HEI_ = (9, MB_339PAN_Weapons.LR_25___25_ARF_8M3_HEI_)
|
||||
LR_25___25_ARF_8M3_API_ = (9, MB_339PAN_Weapons.LR_25___25_ARF_8M3_API_)
|
||||
Mk_82 = (9, Weapons.Mk_82)
|
||||
Matra_Type_155_Rocket_Pod = (9, Weapons.Matra_Type_155_Rocket_Pod)
|
||||
|
||||
class Pylon10:
|
||||
Tip_Fuel_Tank_500lt_ = (10, MB_339PAN_Weapons.Tip_Fuel_Tank_500lt_)
|
||||
Tip_Fuel_Tank_Ellittici_320lt_ = (
|
||||
10,
|
||||
MB_339PAN_Weapons.Tip_Fuel_Tank_Ellittici_320lt_,
|
||||
)
|
||||
|
||||
pylons = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||
|
||||
tasks = [
|
||||
task.GroundAttack,
|
||||
task.RunwayAttack,
|
||||
task.CAS,
|
||||
task.AntishipStrike,
|
||||
task.Reconnaissance,
|
||||
]
|
||||
task_default = task.Nothing
|
||||
@ -3,13 +3,11 @@ from pydcs_extensions.f22a.f22a import F_22A
|
||||
from pydcs_extensions.hercules.hercules import Hercules
|
||||
from pydcs_extensions.highdigitsams import highdigitsams
|
||||
from pydcs_extensions.jas39.jas39 import JAS39Gripen, JAS39Gripen_AG
|
||||
from pydcs_extensions.mb339.mb339 import MB_339PAN
|
||||
from pydcs_extensions.su57.su57 import Su_57
|
||||
import pydcs_extensions.frenchpack.frenchpack as frenchpack
|
||||
|
||||
MODDED_AIRPLANES = [
|
||||
A_4E_C,
|
||||
MB_339PAN,
|
||||
Su_57,
|
||||
F_22A,
|
||||
Hercules,
|
||||
|
||||
@ -18,9 +18,10 @@ from game.data.weapons import (
|
||||
WEAPON_INTRODUCTION_YEARS,
|
||||
Weapon,
|
||||
)
|
||||
from game.db import FACTIONS
|
||||
from game.profiling import logged_duration
|
||||
from game.settings import Settings
|
||||
from game.theater.start_generator import GameGenerator, GeneratorSettings
|
||||
from game.theater.start_generator import GameGenerator, GeneratorSettings, ModSettings
|
||||
from qt_ui import (
|
||||
liberation_install,
|
||||
liberation_theme,
|
||||
@ -199,8 +200,8 @@ def create_game(
|
||||
inject_custom_payloads(Path(persistency.base_path()))
|
||||
campaign = Campaign.from_json(campaign_path)
|
||||
generator = GameGenerator(
|
||||
blue,
|
||||
red,
|
||||
FACTIONS[blue],
|
||||
FACTIONS[red],
|
||||
campaign.load_theater(),
|
||||
Settings(
|
||||
supercarrier=supercarrier,
|
||||
@ -221,6 +222,15 @@ def create_game(
|
||||
no_player_navy=False,
|
||||
no_enemy_navy=False,
|
||||
),
|
||||
ModSettings(
|
||||
a4_skyhawk=False,
|
||||
f22_raptor=False,
|
||||
hercules=False,
|
||||
jas39_gripen=False,
|
||||
su57_felon=False,
|
||||
frenchpack=False,
|
||||
high_digit_sams=False,
|
||||
),
|
||||
)
|
||||
return generator.generate()
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
from typing import Any, Callable, Dict, Iterator, Optional, TypeVar
|
||||
from typing import Any, Callable, Iterator, Optional, TypeVar
|
||||
|
||||
from PySide2.QtCore import (
|
||||
QAbstractListModel,
|
||||
@ -51,7 +51,7 @@ class DeletableChildModelManager:
|
||||
#: The type of model managed by this class.
|
||||
ModelType = TypeVar("ModelType")
|
||||
|
||||
ModelDict = Dict[DataType, ModelType]
|
||||
ModelDict = dict[DataType, ModelType]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -334,11 +334,7 @@ class TransferModel(QAbstractListModel):
|
||||
@staticmethod
|
||||
def text_for_transfer(transfer: TransferOrder) -> str:
|
||||
"""Returns the text that should be displayed for the transfer."""
|
||||
count = sum(transfer.units.values())
|
||||
origin = transfer.origin.name
|
||||
destination = transfer.destination.name
|
||||
description = "Transfer" if transfer.player else "Enemy transfer"
|
||||
return f"{description} of {count} units from {origin} to {destination}"
|
||||
return str(transfer)
|
||||
|
||||
@staticmethod
|
||||
def icon_for_transfer(_transfer: TransferOrder) -> Optional[QIcon]:
|
||||
|
||||
@ -24,8 +24,8 @@ class QFactionsInfos(QGroupBox):
|
||||
|
||||
def setGame(self, game: Game):
|
||||
if game is not None:
|
||||
self.player_name.setText(game.player_name)
|
||||
self.enemy_name.setText(game.enemy_name)
|
||||
self.player_name.setText(game.player_faction.name)
|
||||
self.enemy_name.setText(game.enemy_faction.name)
|
||||
else:
|
||||
self.player_name.setText("")
|
||||
self.enemy_name.setText("")
|
||||
|
||||
@ -46,7 +46,10 @@ class QTopPanel(QFrame):
|
||||
self.conditionsWidget = QConditionsWidget()
|
||||
self.budgetBox = QBudgetBox(self.game)
|
||||
|
||||
self.passTurnButton = QPushButton("Pass Turn")
|
||||
pass_turn_text = "Pass Turn"
|
||||
if not self.game or self.game.turn == 0:
|
||||
pass_turn_text = "Begin Campaign"
|
||||
self.passTurnButton = QPushButton(pass_turn_text)
|
||||
self.passTurnButton.setIcon(CONST.ICONS["PassTurn"])
|
||||
self.passTurnButton.setProperty("style", "btn-primary")
|
||||
self.passTurnButton.clicked.connect(self.passTurn)
|
||||
@ -114,6 +117,8 @@ class QTopPanel(QFrame):
|
||||
self.factionsInfos.setGame(game)
|
||||
|
||||
self.passTurnButton.setEnabled(True)
|
||||
if game and game.turn > 0:
|
||||
self.passTurnButton.setText("Pass Turn")
|
||||
|
||||
if game and game.turn == 0:
|
||||
self.proceedButton.setEnabled(False)
|
||||
@ -267,8 +272,8 @@ class QTopPanel(QFrame):
|
||||
closest_cps[0],
|
||||
closest_cps[1],
|
||||
self.game.theater.controlpoints[0].position,
|
||||
self.game.player_name,
|
||||
self.game.enemy_name,
|
||||
self.game.player_faction.name,
|
||||
self.game.enemy_faction.name,
|
||||
)
|
||||
|
||||
unit_map = self.game.initiate_event(game_event)
|
||||
|
||||
@ -50,7 +50,7 @@ class QLiberationWindow(QMainWindow):
|
||||
self.liberation_map = QLiberationMap(self.game_model, self)
|
||||
|
||||
self.setGeometry(300, 100, 270, 100)
|
||||
self.setWindowTitle(f"DCS Liberation - v{VERSION}")
|
||||
self.updateWindowTitle()
|
||||
self.setWindowIcon(QIcon("./resources/icon.png"))
|
||||
self.statusBar().showMessage("Ready")
|
||||
|
||||
@ -71,6 +71,7 @@ class QLiberationWindow(QMainWindow):
|
||||
logging.info("Loading last saved game : " + str(last_save_file))
|
||||
game = persistency.load_game(last_save_file)
|
||||
self.onGameGenerated(game)
|
||||
self.updateWindowTitle(last_save_file if game else None)
|
||||
except:
|
||||
logging.info("Error loading latest save game")
|
||||
else:
|
||||
@ -236,13 +237,15 @@ class QLiberationWindow(QMainWindow):
|
||||
file = QFileDialog.getOpenFileName(
|
||||
self,
|
||||
"Select game file to open",
|
||||
dir=persistency._dcs_saved_game_folder,
|
||||
dir=self.game.savepath if self.game else persistency._dcs_saved_game_folder,
|
||||
filter="*.liberation",
|
||||
)
|
||||
if file is not None:
|
||||
if file is not None and file[0] != "":
|
||||
game = persistency.load_game(file[0])
|
||||
GameUpdateSignal.get_instance().game_loaded.emit(game)
|
||||
|
||||
self.updateWindowTitle(file[0])
|
||||
|
||||
def saveGame(self):
|
||||
logging.info("Saving game")
|
||||
|
||||
@ -257,7 +260,7 @@ class QLiberationWindow(QMainWindow):
|
||||
file = QFileDialog.getSaveFileName(
|
||||
self,
|
||||
"Save As",
|
||||
dir=persistency._dcs_saved_game_folder,
|
||||
dir=self.game.savepath if self.game else persistency._dcs_saved_game_folder,
|
||||
filter="*.liberation",
|
||||
)
|
||||
if file is not None:
|
||||
@ -266,7 +269,20 @@ class QLiberationWindow(QMainWindow):
|
||||
liberation_install.setup_last_save_file(self.game.savepath)
|
||||
liberation_install.save_config()
|
||||
|
||||
self.updateWindowTitle(file[0])
|
||||
|
||||
def updateWindowTitle(self, save_path: Optional[str] = None) -> None:
|
||||
"""
|
||||
to DCS Liberation - vX.X.X - file_name
|
||||
"""
|
||||
window_title = f"DCS Liberation - v{VERSION}"
|
||||
if save_path: # appending the file name to title as it is updated
|
||||
file_name = save_path.split("/")[-1].split(".liberation")[0]
|
||||
window_title = f"{window_title} - {file_name}"
|
||||
self.setWindowTitle(window_title)
|
||||
|
||||
def onGameGenerated(self, game: Game):
|
||||
self.updateWindowTitle()
|
||||
logging.info("On Game generated")
|
||||
self.game = game
|
||||
GameUpdateSignal.get_instance().game_loaded.emit(self.game)
|
||||
|
||||
@ -33,7 +33,7 @@ class DepartingConvoyInfo(QGroupBox):
|
||||
if unit_type.dcs_id in VEHICLES_ICONS.keys():
|
||||
icon.setPixmap(VEHICLES_ICONS[unit_type.dcs_id])
|
||||
else:
|
||||
icon.setText("<b>" + unit_type.id[:8] + "</b>")
|
||||
icon.setText("<b>" + unit_type.name + "</b>")
|
||||
icon.setProperty("style", "icon-armor")
|
||||
unit_layout.addWidget(icon, idx, 0)
|
||||
unit_layout.addWidget(
|
||||
|
||||
@ -284,7 +284,7 @@ class NewUnitTransferDialog(QDialog):
|
||||
continue
|
||||
|
||||
logging.info(
|
||||
f"Transferring {count} {unit_type.id} from {self.origin} to "
|
||||
f"Transferring {count} {unit_type} from {self.origin} to "
|
||||
f"{destination}"
|
||||
)
|
||||
transfers[unit_type] = count
|
||||
|
||||
@ -118,18 +118,10 @@ class QBaseMenu2(QDialog):
|
||||
|
||||
@property
|
||||
def cheat_capturable(self) -> bool:
|
||||
if not self.game_model.game.settings.enable_base_capture_cheat:
|
||||
return False
|
||||
if self.cp.captured:
|
||||
return False
|
||||
|
||||
for connected in self.cp.connected_points:
|
||||
if connected.captured:
|
||||
return True
|
||||
return False
|
||||
return self.game_model.game.settings.enable_base_capture_cheat
|
||||
|
||||
def cheat_capture(self) -> None:
|
||||
self.cp.capture(self.game_model.game, for_player=True)
|
||||
self.cp.capture(self.game_model.game, for_player=not self.cp.captured)
|
||||
# Reinitialized ground planners and the like. The ATO needs to be reset because
|
||||
# missions planned against the flipped base are no longer valid.
|
||||
self.game_model.game.reset_ato()
|
||||
|
||||
@ -18,7 +18,7 @@ from PySide2.QtWidgets import (
|
||||
from dcs import Point
|
||||
from dcs import vehicles
|
||||
|
||||
from game import Game, db
|
||||
from game import Game
|
||||
from game.data.building_data import FORTIFICATION_BUILDINGS
|
||||
from game.db import REWARDS
|
||||
from game.dcs.groundunittype import GroundUnitType
|
||||
@ -258,20 +258,16 @@ class QGroundObjectMenu(QDialog):
|
||||
self.game.budget = self.game.budget + self.total_value
|
||||
self.ground_object.groups = []
|
||||
self.do_refresh_layout()
|
||||
GameUpdateSignal.get_instance().updateBudget(self.game)
|
||||
GameUpdateSignal.get_instance().updateGame(self.game)
|
||||
|
||||
def buy_group(self):
|
||||
self.subwindow = QBuyGroupForGroundObjectDialog(
|
||||
self, self.ground_object, self.cp, self.game, self.total_value
|
||||
)
|
||||
self.subwindow.changed.connect(self.do_refresh_layout)
|
||||
self.subwindow.show()
|
||||
|
||||
|
||||
class QBuyGroupForGroundObjectDialog(QDialog):
|
||||
|
||||
changed = QtCore.Signal()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
@ -434,10 +430,7 @@ class QBuyGroupForGroundObjectDialog(QDialog):
|
||||
)
|
||||
self.ground_object.groups = [group]
|
||||
|
||||
GameUpdateSignal.get_instance().updateBudget(self.game)
|
||||
|
||||
self.changed.emit()
|
||||
self.close()
|
||||
GameUpdateSignal.get_instance().updateGame(self.game)
|
||||
|
||||
def buySam(self):
|
||||
sam_generator = self.samCombo.itemData(self.samCombo.currentIndex())
|
||||
@ -453,10 +446,7 @@ class QBuyGroupForGroundObjectDialog(QDialog):
|
||||
generator.generate()
|
||||
self.ground_object.groups = list(generator.groups)
|
||||
|
||||
GameUpdateSignal.get_instance().updateBudget(self.game)
|
||||
|
||||
self.changed.emit()
|
||||
self.close()
|
||||
GameUpdateSignal.get_instance().updateGame(self.game)
|
||||
|
||||
def buy_ewr(self):
|
||||
ewr_generator = self.ewr_selector.itemData(self.ewr_selector.currentIndex())
|
||||
@ -471,10 +461,7 @@ class QBuyGroupForGroundObjectDialog(QDialog):
|
||||
generator.generate()
|
||||
self.ground_object.groups = [generator.vg]
|
||||
|
||||
GameUpdateSignal.get_instance().updateBudget(self.game)
|
||||
|
||||
self.changed.emit()
|
||||
self.close()
|
||||
GameUpdateSignal.get_instance().updateGame(self.game)
|
||||
|
||||
def error_money(self):
|
||||
msg = QMessageBox()
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import itertools
|
||||
from typing import Optional
|
||||
|
||||
from PySide2.QtWidgets import (
|
||||
QCheckBox,
|
||||
@ -63,10 +64,11 @@ class IntelTableLayout(QGridLayout):
|
||||
0,
|
||||
)
|
||||
|
||||
def add_row(self, text: str, count: int) -> None:
|
||||
def add_row(self, text: str, count: Optional[int] = None) -> None:
|
||||
row = next(self.row)
|
||||
self.addWidget(QLabel(text), row, 0)
|
||||
self.addWidget(QLabel(str(count)), row, 1)
|
||||
if count is not None: # optional count for blank rows
|
||||
self.addWidget(QLabel(str(count)), row, 1)
|
||||
|
||||
|
||||
class AircraftIntelLayout(IntelTableLayout):
|
||||
@ -80,14 +82,16 @@ class AircraftIntelLayout(IntelTableLayout):
|
||||
if not base.total_aircraft:
|
||||
continue
|
||||
|
||||
self.add_header(control_point.name)
|
||||
for airframe, count in base.aircraft.items():
|
||||
self.add_header(f"{control_point.name} ({base.total_aircraft})")
|
||||
for airframe in sorted(base.aircraft, key=lambda k: k.name):
|
||||
count = base.aircraft[airframe]
|
||||
if not count:
|
||||
continue
|
||||
self.add_row(airframe.name, count)
|
||||
self.add_row(f" {airframe.name}", count)
|
||||
self.add_row("")
|
||||
|
||||
self.add_spacer()
|
||||
self.add_row("<b>Total</b>", total)
|
||||
self.add_spacer()
|
||||
|
||||
|
||||
class AircraftIntelTab(ScrollingFrame):
|
||||
@ -107,14 +111,16 @@ class ArmyIntelLayout(IntelTableLayout):
|
||||
if not base.total_armor:
|
||||
continue
|
||||
|
||||
self.add_header(control_point.name)
|
||||
for vehicle, count in base.armor.items():
|
||||
self.add_header(f"{control_point.name} ({base.total_armor})")
|
||||
for vehicle in sorted(base.armor, key=lambda k: k.name):
|
||||
count = base.armor[vehicle]
|
||||
if not count:
|
||||
continue
|
||||
self.add_row(vehicle.name, count)
|
||||
self.add_row(f" {vehicle.name}", count)
|
||||
self.add_row("")
|
||||
|
||||
self.add_spacer()
|
||||
self.add_row("<b>Total</b>", total)
|
||||
self.add_spacer()
|
||||
|
||||
|
||||
class ArmyIntelTab(ScrollingFrame):
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import logging
|
||||
from typing import Optional, Type
|
||||
|
||||
from PySide2.QtCore import Qt, Signal
|
||||
@ -148,6 +147,11 @@ class QFlightCreator(QDialog):
|
||||
|
||||
self.on_departure_changed(self.departure.currentIndex())
|
||||
|
||||
def reject(self) -> None:
|
||||
super().reject()
|
||||
# Clear the roster to return pilots to the pool.
|
||||
self.roster_editor.replace(None)
|
||||
|
||||
def set_custom_name_text(self, text: str):
|
||||
self.custom_name_text = text
|
||||
|
||||
@ -217,7 +221,7 @@ class QFlightCreator(QDialog):
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
self.created.emit(flight)
|
||||
self.close()
|
||||
self.accept()
|
||||
|
||||
def on_aircraft_changed(self, index: int) -> None:
|
||||
new_aircraft = self.aircraft_selector.itemData(index)
|
||||
@ -260,7 +264,12 @@ class QFlightCreator(QDialog):
|
||||
)
|
||||
|
||||
def update_max_size(self, available: int) -> None:
|
||||
self.flight_size_spinner.setMaximum(min(available, 4))
|
||||
aircraft = self.aircraft_selector.currentData()
|
||||
if aircraft is None:
|
||||
self.flight_size_spinner.setMaximum(0)
|
||||
return
|
||||
|
||||
self.flight_size_spinner.setMaximum(min(available, aircraft.max_group_size))
|
||||
|
||||
if self.flight_size_spinner.maximum() >= 2:
|
||||
if self.flight_size_spinner.value() < 2:
|
||||
self.flight_size_spinner.setValue(2)
|
||||
self.flight_size_spinner.setValue(2)
|
||||
|
||||
@ -11,7 +11,8 @@ from jinja2 import Environment, FileSystemLoader, select_autoescape
|
||||
|
||||
from game import db
|
||||
from game.settings import Settings
|
||||
from game.theater.start_generator import GameGenerator, GeneratorSettings
|
||||
from game.theater.start_generator import GameGenerator, GeneratorSettings, ModSettings
|
||||
from game.factions.faction import Faction
|
||||
from qt_ui.widgets.QLiberationCalendar import QLiberationCalendar
|
||||
from qt_ui.widgets.spinsliders import TenthsSpinSlider, TimeInputs, CurrencySpinner
|
||||
from qt_ui.windows.newgame.QCampaignList import (
|
||||
@ -102,15 +103,25 @@ class NewGameWizard(QtWidgets.QWizard):
|
||||
no_player_navy=self.field("no_player_navy"),
|
||||
no_enemy_navy=self.field("no_enemy_navy"),
|
||||
)
|
||||
mod_settings = ModSettings(
|
||||
a4_skyhawk=self.field("a4_skyhawk"),
|
||||
f22_raptor=self.field("f22_raptor"),
|
||||
hercules=self.field("hercules"),
|
||||
jas39_gripen=self.field("jas39_gripen"),
|
||||
su57_felon=self.field("su57_felon"),
|
||||
frenchpack=self.field("frenchpack"),
|
||||
high_digit_sams=self.field("high_digit_sams"),
|
||||
)
|
||||
|
||||
blue_faction = [c for c in db.FACTIONS][self.field("blueFaction")]
|
||||
red_faction = [c for c in db.FACTIONS][self.field("redFaction")]
|
||||
blue_faction = self.faction_selection_page.selected_blue_faction
|
||||
red_faction = self.faction_selection_page.selected_red_faction
|
||||
generator = GameGenerator(
|
||||
blue_faction,
|
||||
red_faction,
|
||||
campaign.load_theater(),
|
||||
settings,
|
||||
generator_settings,
|
||||
mod_settings,
|
||||
)
|
||||
self.generatedGame = generator.generate()
|
||||
|
||||
@ -196,14 +207,6 @@ class FactionSelection(QtWidgets.QWizardPage):
|
||||
self.factionsGroupLayout.addLayout(self.redGroupLayout)
|
||||
self.factionsGroup.setLayout(self.factionsGroupLayout)
|
||||
|
||||
# Create required mod layout
|
||||
self.requiredModsGroup = QtWidgets.QGroupBox("Required Mods")
|
||||
self.requiredModsGroupLayout = QtWidgets.QHBoxLayout()
|
||||
self.requiredMods = QtWidgets.QLabel("<ul><li>None</li></ul>")
|
||||
self.requiredMods.setOpenExternalLinks(True)
|
||||
self.requiredModsGroupLayout.addWidget(self.requiredMods)
|
||||
self.requiredModsGroup.setLayout(self.requiredModsGroupLayout)
|
||||
|
||||
# Docs Link
|
||||
docsText = QtWidgets.QLabel(
|
||||
'<a href="https://github.com/dcs-liberation/dcs_liberation/wiki/Custom-Factions"><span style="color:#FFFFFF;">How to create your own faction</span></a>'
|
||||
@ -218,7 +221,6 @@ class FactionSelection(QtWidgets.QWizardPage):
|
||||
# Build layout
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
layout.addWidget(self.factionsGroup)
|
||||
layout.addWidget(self.requiredModsGroup)
|
||||
layout.addWidget(docsText)
|
||||
self.setLayout(layout)
|
||||
self.updateUnitRecap()
|
||||
@ -257,42 +259,13 @@ class FactionSelection(QtWidgets.QWizardPage):
|
||||
self.blueFactionDescription.setText(blue_faction_txt)
|
||||
self.redFactionDescription.setText(red_faction_txt)
|
||||
|
||||
# Compute mod requirements txt
|
||||
self.requiredMods.setText("<ul>")
|
||||
has_mod = False
|
||||
if len(red_faction.requirements.keys()) > 0:
|
||||
has_mod = True
|
||||
for mod in red_faction.requirements.keys():
|
||||
self.requiredMods.setText(
|
||||
self.requiredMods.text()
|
||||
+ "\n<li>"
|
||||
+ mod
|
||||
+ ': <a href="'
|
||||
+ red_faction.requirements[mod]
|
||||
+ '">'
|
||||
+ red_faction.requirements[mod]
|
||||
+ "</a></li>"
|
||||
)
|
||||
@property
|
||||
def selected_blue_faction(self) -> Faction:
|
||||
return db.FACTIONS[self.blueFactionSelect.currentText()]
|
||||
|
||||
if len(blue_faction.requirements.keys()) > 0:
|
||||
has_mod = True
|
||||
for mod in blue_faction.requirements.keys():
|
||||
if mod not in red_faction.requirements.keys():
|
||||
self.requiredMods.setText(
|
||||
self.requiredMods.text()
|
||||
+ "\n<li>"
|
||||
+ mod
|
||||
+ ': <a href="'
|
||||
+ blue_faction.requirements[mod]
|
||||
+ '">'
|
||||
+ blue_faction.requirements[mod]
|
||||
+ "</a></li>"
|
||||
)
|
||||
|
||||
if has_mod:
|
||||
self.requiredMods.setText(self.requiredMods.text() + "</ul>\n\n")
|
||||
else:
|
||||
self.requiredMods.setText(self.requiredMods.text() + "<li>None</li></ul>\n")
|
||||
@property
|
||||
def selected_red_faction(self) -> Faction:
|
||||
return db.FACTIONS[self.redFactionSelect.currentText()]
|
||||
|
||||
|
||||
class TheaterConfiguration(QtWidgets.QWizardPage):
|
||||
@ -561,8 +534,48 @@ class GeneratorOptions(QtWidgets.QWizardPage):
|
||||
generatorLayout.addLayout(desired_player_mission_duration, 6, 0)
|
||||
generatorSettingsGroup.setLayout(generatorLayout)
|
||||
|
||||
modSettingsGroup = QtWidgets.QGroupBox("Mod Settings")
|
||||
a4_skyhawk = QtWidgets.QCheckBox()
|
||||
self.registerField("a4_skyhawk", a4_skyhawk)
|
||||
hercules = QtWidgets.QCheckBox()
|
||||
self.registerField("hercules", hercules)
|
||||
f22_raptor = QtWidgets.QCheckBox()
|
||||
self.registerField("f22_raptor", f22_raptor)
|
||||
jas39_gripen = QtWidgets.QCheckBox()
|
||||
self.registerField("jas39_gripen", jas39_gripen)
|
||||
su57_felon = QtWidgets.QCheckBox()
|
||||
self.registerField("su57_felon", su57_felon)
|
||||
frenchpack = QtWidgets.QCheckBox()
|
||||
self.registerField("frenchpack", frenchpack)
|
||||
high_digit_sams = QtWidgets.QCheckBox()
|
||||
self.registerField("high_digit_sams", high_digit_sams)
|
||||
|
||||
modHelpText = QtWidgets.QLabel(
|
||||
"<p>Select the mods you have installed. If your chosen factions support them, you'll be able to use these mods in your campaign.</p>"
|
||||
)
|
||||
modHelpText.setAlignment(Qt.AlignCenter)
|
||||
|
||||
modLayout = QtWidgets.QGridLayout()
|
||||
modLayout.addWidget(QtWidgets.QLabel("A-4E Skyhawk"), 1, 0)
|
||||
modLayout.addWidget(a4_skyhawk, 1, 1)
|
||||
modLayout.addWidget(QtWidgets.QLabel("F-22A Raptor"), 2, 0)
|
||||
modLayout.addWidget(f22_raptor, 2, 1)
|
||||
modLayout.addWidget(QtWidgets.QLabel("C-130J-30 Super Hercules"), 3, 0)
|
||||
modLayout.addWidget(hercules, 3, 1)
|
||||
modLayout.addWidget(QtWidgets.QLabel("JAS 39 Gripen"), 4, 0)
|
||||
modLayout.addWidget(jas39_gripen, 4, 1)
|
||||
modLayout.addWidget(QtWidgets.QLabel("Su-57 Felon"), 5, 0)
|
||||
modLayout.addWidget(su57_felon, 5, 1)
|
||||
modLayout.addWidget(QtWidgets.QLabel("Frenchpack"), 6, 0)
|
||||
modLayout.addWidget(frenchpack, 6, 1)
|
||||
modLayout.addWidget(QtWidgets.QLabel("High Digit SAMs"), 7, 0)
|
||||
modLayout.addWidget(high_digit_sams, 7, 1)
|
||||
modSettingsGroup.setLayout(modLayout)
|
||||
|
||||
mlayout = QVBoxLayout()
|
||||
mlayout.addWidget(generatorSettingsGroup)
|
||||
mlayout.addWidget(modSettingsGroup)
|
||||
mlayout.addWidget(modHelpText)
|
||||
self.setLayout(mlayout)
|
||||
|
||||
|
||||
|
||||
@ -179,6 +179,116 @@ class HqAutomationSettingsBox(QGroupBox):
|
||||
self.game.settings.auto_ato_player_missions_asap = value
|
||||
|
||||
|
||||
class PilotSettingsBox(QGroupBox):
|
||||
def __init__(self, game: Game) -> None:
|
||||
super().__init__("Pilots and Squadrons")
|
||||
self.game = game
|
||||
|
||||
layout = QGridLayout()
|
||||
self.setLayout(layout)
|
||||
|
||||
self.ai_pilot_levelling = QCheckBox()
|
||||
self.ai_pilot_levelling.setChecked(self.game.settings.ai_pilot_levelling)
|
||||
self.ai_pilot_levelling.toggled.connect(self.set_ai_pilot_leveling)
|
||||
|
||||
ai_pilot_levelling_info = (
|
||||
"Set whether or not AI pilots will level up after completing a number of"
|
||||
" sorties. Since pilot level affects the AI skill, you may wish to disable"
|
||||
" this, lest you face an Ace!"
|
||||
)
|
||||
|
||||
self.ai_pilot_levelling.setToolTip(ai_pilot_levelling_info)
|
||||
ai_pilot_levelling_label = QLabel("Allow AI pilot levelling")
|
||||
ai_pilot_levelling_label.setToolTip(ai_pilot_levelling_info)
|
||||
|
||||
layout.addWidget(ai_pilot_levelling_label, 0, 0)
|
||||
layout.addWidget(self.ai_pilot_levelling, 0, 1, Qt.AlignRight)
|
||||
|
||||
enable_squadron_pilot_limits_info = (
|
||||
"If set, squadrons will be limited to a maximum number of pilots and dead "
|
||||
"pilots will replenish at a fixed rate, each defined with the settings"
|
||||
"below. Auto-purchase may buy aircraft for which there are no pilots"
|
||||
"available, so this feature is still a work-in-progress."
|
||||
)
|
||||
|
||||
enable_squadron_pilot_limits_label = QLabel(
|
||||
"Enable per-squadron pilot limtits (WIP)"
|
||||
)
|
||||
enable_squadron_pilot_limits_label.setToolTip(enable_squadron_pilot_limits_info)
|
||||
enable_squadron_pilot_limits = QCheckBox()
|
||||
enable_squadron_pilot_limits.setToolTip(enable_squadron_pilot_limits_info)
|
||||
enable_squadron_pilot_limits.setChecked(
|
||||
self.game.settings.enable_squadron_pilot_limits
|
||||
)
|
||||
enable_squadron_pilot_limits.toggled.connect(
|
||||
self.set_enable_squadron_pilot_limits
|
||||
)
|
||||
|
||||
layout.addWidget(enable_squadron_pilot_limits_label, 1, 0)
|
||||
layout.addWidget(enable_squadron_pilot_limits, 1, 1, Qt.AlignRight)
|
||||
|
||||
self.pilot_limit = QSpinBox()
|
||||
self.pilot_limit.setMinimum(12)
|
||||
self.pilot_limit.setMaximum(72)
|
||||
self.pilot_limit.setValue(self.game.settings.squadron_pilot_limit)
|
||||
self.pilot_limit.setEnabled(self.game.settings.enable_squadron_pilot_limits)
|
||||
self.pilot_limit.valueChanged.connect(self.set_squadron_pilot_limit)
|
||||
|
||||
pilot_limit_info = (
|
||||
"Sets the maximum number of pilots a squadron may have active. "
|
||||
"Changing this value will not have an immediate effect, but will alter "
|
||||
"replenishment for future turns."
|
||||
)
|
||||
|
||||
self.pilot_limit.setToolTip(pilot_limit_info)
|
||||
pilot_limit_label = QLabel("Maximum number of pilots per squadron")
|
||||
pilot_limit_label.setToolTip(pilot_limit_info)
|
||||
|
||||
layout.addWidget(pilot_limit_label, 2, 0)
|
||||
layout.addWidget(self.pilot_limit, 2, 1, Qt.AlignRight)
|
||||
|
||||
self.squadron_replenishment_rate = QSpinBox()
|
||||
self.squadron_replenishment_rate.setMinimum(1)
|
||||
self.squadron_replenishment_rate.setMaximum(20)
|
||||
self.squadron_replenishment_rate.setValue(
|
||||
self.game.settings.squadron_replenishment_rate
|
||||
)
|
||||
self.squadron_replenishment_rate.setEnabled(
|
||||
self.game.settings.enable_squadron_pilot_limits
|
||||
)
|
||||
self.squadron_replenishment_rate.valueChanged.connect(
|
||||
self.set_squadron_replenishment_rate
|
||||
)
|
||||
|
||||
squadron_replenishment_rate_info = (
|
||||
"Sets the maximum number of pilots that will be recruited to each squadron "
|
||||
"at the end of each turn. Squadrons will not recruit new pilots beyond the "
|
||||
"pilot limit, but each squadron with room for more pilots will recruit "
|
||||
"this many pilots each turn up to the limit."
|
||||
)
|
||||
|
||||
self.squadron_replenishment_rate.setToolTip(squadron_replenishment_rate_info)
|
||||
squadron_replenishment_rate_label = QLabel("Squadron pilot replenishment rate")
|
||||
squadron_replenishment_rate_label.setToolTip(squadron_replenishment_rate_info)
|
||||
|
||||
layout.addWidget(squadron_replenishment_rate_label, 3, 0)
|
||||
layout.addWidget(self.squadron_replenishment_rate, 3, 1, Qt.AlignRight)
|
||||
|
||||
def set_enable_squadron_pilot_limits(self, checked: bool) -> None:
|
||||
self.game.settings.enable_squadron_pilot_limits = checked
|
||||
self.pilot_limit.setEnabled(checked)
|
||||
self.squadron_replenishment_rate.setEnabled(checked)
|
||||
|
||||
def set_squadron_pilot_limit(self, value: int) -> None:
|
||||
self.game.settings.squadron_pilot_limit = value
|
||||
|
||||
def set_squadron_replenishment_rate(self, value: int) -> None:
|
||||
self.game.settings.squadron_replenishment_rate = value
|
||||
|
||||
def set_ai_pilot_leveling(self, checked: bool) -> None:
|
||||
self.game.settings.ai_pilot_levelling = checked
|
||||
|
||||
|
||||
START_TYPE_TOOLTIP = "Selects the start type used for AI aircraft."
|
||||
|
||||
|
||||
@ -516,72 +626,7 @@ class QSettingsWindow(QDialog):
|
||||
general_layout.addWidget(old_tanker_label, 2, 0)
|
||||
general_layout.addWidget(old_tanker, 2, 1, Qt.AlignRight)
|
||||
|
||||
def set_squadron_pilot_limit(value: int) -> None:
|
||||
self.game.settings.squadron_pilot_limit = value
|
||||
|
||||
pilot_limit = QSpinBox()
|
||||
pilot_limit.setMinimum(12)
|
||||
pilot_limit.setMaximum(72)
|
||||
pilot_limit.setValue(self.game.settings.squadron_pilot_limit)
|
||||
pilot_limit.valueChanged.connect(set_squadron_pilot_limit)
|
||||
|
||||
pilot_limit_info = (
|
||||
"Sets the maximum number of pilots a squadron may have active. "
|
||||
"Changing this value will not have an immediate effect, but will alter "
|
||||
"replenishment for future turns."
|
||||
)
|
||||
|
||||
pilot_limit.setToolTip(pilot_limit_info)
|
||||
pilot_limit_label = QLabel("Maximum number of pilots per squadron")
|
||||
pilot_limit_label.setToolTip(pilot_limit_info)
|
||||
|
||||
general_layout.addWidget(pilot_limit_label, 3, 0)
|
||||
general_layout.addWidget(pilot_limit, 3, 1, Qt.AlignRight)
|
||||
|
||||
def set_squadron_replenishment_rate(value: int) -> None:
|
||||
self.game.settings.squadron_replenishment_rate = value
|
||||
|
||||
squadron_replenishment_rate = QSpinBox()
|
||||
squadron_replenishment_rate.setMinimum(1)
|
||||
squadron_replenishment_rate.setMaximum(20)
|
||||
squadron_replenishment_rate.setValue(
|
||||
self.game.settings.squadron_replenishment_rate
|
||||
)
|
||||
squadron_replenishment_rate.valueChanged.connect(
|
||||
set_squadron_replenishment_rate
|
||||
)
|
||||
|
||||
squadron_replenishment_rate_info = (
|
||||
"Sets the maximum number of pilots that will be recruited to each squadron "
|
||||
"at the end of each turn. Squadrons will not recruit new pilots beyond the "
|
||||
"pilot limit, but each squadron with room for more pilots will recruit "
|
||||
"this many pilots each turn up to the limit."
|
||||
)
|
||||
|
||||
squadron_replenishment_rate.setToolTip(squadron_replenishment_rate_info)
|
||||
squadron_replenishment_rate_label = QLabel("Squadron pilot replenishment rate")
|
||||
squadron_replenishment_rate_label.setToolTip(squadron_replenishment_rate_info)
|
||||
|
||||
general_layout.addWidget(squadron_replenishment_rate_label, 4, 0)
|
||||
general_layout.addWidget(squadron_replenishment_rate, 4, 1, Qt.AlignRight)
|
||||
|
||||
ai_pilot_levelling = QCheckBox()
|
||||
ai_pilot_levelling.setChecked(self.game.settings.ai_pilot_levelling)
|
||||
ai_pilot_levelling.toggled.connect(self.applySettings)
|
||||
|
||||
ai_pilot_levelling_info = (
|
||||
"Set whether or not AI pilots will level up after completing a number of"
|
||||
" sorties. Since pilot level affects the AI skill, you may wish to disable"
|
||||
" this, lest you face an Ace!"
|
||||
)
|
||||
|
||||
ai_pilot_levelling.setToolTip(ai_pilot_levelling_info)
|
||||
ai_pilot_levelling_label = QLabel("Allow AI pilot levelling")
|
||||
ai_pilot_levelling_label.setToolTip(ai_pilot_levelling_info)
|
||||
|
||||
general_layout.addWidget(ai_pilot_levelling_label, 5, 0)
|
||||
general_layout.addWidget(ai_pilot_levelling, 5, 1, Qt.AlignRight)
|
||||
|
||||
campaign_layout.addWidget(PilotSettingsBox(self.game))
|
||||
campaign_layout.addWidget(HqAutomationSettingsBox(self.game))
|
||||
|
||||
def initGeneratorLayout(self):
|
||||
|
||||
@ -19,6 +19,7 @@ pathspec==0.8.1
|
||||
pefile==2019.4.18
|
||||
Pillow==8.2.0
|
||||
pre-commit==2.10.1
|
||||
-e git://github.com/pydcs/dcs@7dea4f516d943c1f48454a46043b5f38d42a35f0#egg=pydcs
|
||||
pyinstaller==4.3
|
||||
pyinstaller-hooks-contrib==2021.1
|
||||
pyparsing==2.4.7
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"recommended_player_faction": "Bluefor Modern",
|
||||
"recommended_enemy_faction": "Iran 2015",
|
||||
"description": "<p>Following the Battle of Abu Dhabi, Iran's invasion of the UAE has been halted approximately 20 miles Northeast of Liwa Airbase by coalition forces.</p><p>After weeks of stalemate, coalition forces have consolidated their position and are ready to launch their counterattack to push Iranian forces off the peninsula.</p>",
|
||||
"version": "6.0",
|
||||
"version": "7.0",
|
||||
"miz": "Battle_for_the_UAE_v3.0.2.miz",
|
||||
"performance": 2
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"recommended_player_faction": "Bluefor Modern",
|
||||
"recommended_enemy_faction": "Russia 2010",
|
||||
"description": "<p>This is a complete map of every airbase in the Caucasus Region, all bases are fully defended by Air, Land and/or Sea. The player starts by invading southern Georgia and works their way through Russia. The Strike and SAM targets are limited for performance reasons. If this Scenario is too taxing for your computer you may use the Multi-Part Scenarios. They are copied from this Campaign and are catered toward less powerful machines.</p>",
|
||||
"version": "6.0",
|
||||
"version": "7.0",
|
||||
"miz": "Caucasus_Multi_Full.miz",
|
||||
"performance": 3
|
||||
}
|
||||
@ -5,7 +5,7 @@
|
||||
"recommended_player_faction": "Bluefor Modern",
|
||||
"recommended_enemy_faction": "Georgia 2008",
|
||||
"description": "<p>This is Part 1 of the Caucasus Multi-Part Campaign. This is the invasion of Georgia starting from the southwest (Batumi) and ending in both Gudauta and Tiblisi. This is a straightforward campaign that is smaller and simpler than most. However, it acts great as either a stand alone campaign for beginners, or as a lead into the Caucasus Multi-Part Russia campaign.</p>",
|
||||
"version": "6.0",
|
||||
"version": "7.0",
|
||||
"miz": "Caucasus_Multi_Georgia.miz",
|
||||
"performance": 1
|
||||
}
|
||||
@ -5,7 +5,7 @@
|
||||
"recommended_player_faction": "Bluefor Modern",
|
||||
"recommended_enemy_faction": "Russia 2010",
|
||||
"description": "<p>This is part 2 of the Caucasus Multi-part campaign. After completing Multi-Part Georgia, play this campaign to invade Russia and finish the theater. As this is now Russia the recommended enemy faction has changed. To simulate still owning Georgia the player income has been supplemented through an increased number of blue strike targets at the starting bases. This is a more difficult scenario with a higher concentration of Redfor SAMs and Strike targets than usual.</p>",
|
||||
"version": "6.0",
|
||||
"version": "7.0",
|
||||
"miz": "Caucasus_Multi_Russia.miz",
|
||||
"performance": 2
|
||||
}
|
||||
@ -6,6 +6,6 @@
|
||||
"recommended_enemy_faction": "Syria 1982",
|
||||
"description": "<p> 1100HRS, 06 June 1982: H-hour for Operation Peace for Galilee. </p><p>Objective: Push North towards Beirut and into the Bekaa Valley, eliminating or displacing any PLO and Syrian resistance. Airbases and their surrounding infrastructure in Syria are not the main objective but are still viable strategic targets.</p> <p>Background: Years of PLO encroachment into the UN neutral zone and their resulting terror attacks against Israelis have pushed tension along the border to a breaking point. On June 3, the attempted assassination of Israeli Ambassador, Shlomo Argov by gunmen with ties to the PLO have finally pushed the Israelis to action.</p><p>Recommended Starting Budget:</p><p>$1500m for recommended factions, $$2000m for modern scenarios</p><p>Income Multiplier:</p><p>Blue: 1.0x</p><p>Red: 0.7x-1.0x</p>",
|
||||
"miz": "First_Lebanon_War_v3.0.2.miz",
|
||||
"version": "6.0",
|
||||
"version": "7.0",
|
||||
"performance": 2
|
||||
}
|
||||
|
||||
11
resources/campaigns/Operation_Atilla.json
Normal file
11
resources/campaigns/Operation_Atilla.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "Syria - Operation Atilla",
|
||||
"theater": "Syria",
|
||||
"authors": "Malakhit",
|
||||
"recommended_player_faction": "Turkey 2005",
|
||||
"recommended_enemy_faction": "Greece 2005",
|
||||
"description": "<p>This is based on the Turkish invasion of Cyprus, and the Greek defense of the island. You must make sure to keep your beachhead at all costs, otherwise reclaiming it will be tricky. It is recommended to reduce the per-turn income rate for both factions in this scenario due to the large oil depots both sides have - setting it to around 15-20% is probably reasonable.</p>",
|
||||
"version": "6.0",
|
||||
"miz": "Operation_Atilla.miz",
|
||||
"performance": 2
|
||||
}
|
||||
BIN
resources/campaigns/Operation_Atilla.miz
Normal file
BIN
resources/campaigns/Operation_Atilla.miz
Normal file
Binary file not shown.
@ -5,7 +5,7 @@
|
||||
"recommended_player_faction": "Bluefor Modern",
|
||||
"recommended_enemy_faction": "Syria 2011",
|
||||
"description": "<p>In a scenario reminescent of the First Lebanon War, hostile Syrian-backed forces have flooded into the Bekaa Valley.</p><p>The objective of this operation is twofold: drive the enemy out of the Bekaa Valley and push past the Golan Heights into Syrian territory to capture Tiyas Airbase.</p>",
|
||||
"version": "6.0",
|
||||
"version": "7.0",
|
||||
"miz": "Operation_Mole_Cricket_2010_v3.0.2.miz",
|
||||
"performance": 2
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
"theater": "Persian Gulf",
|
||||
"authors": "Doc_of_Mur",
|
||||
"description": "<p>Small beginner friendly map</p><p><strong>Note:</strong> This scenario is based around Iran invading the UAE and you are trying to take it back. It is small and beginner friendly.</p>",
|
||||
"version": "6.0",
|
||||
"version": "7.0",
|
||||
"recommended_player_faction": "USA 2005",
|
||||
"recommended_enemy_faction": "Iran 2015",
|
||||
"miz": "Road_to_Dubai.miz",
|
||||
|
||||
11
resources/campaigns/Russian_Intervention_2015.json
Normal file
11
resources/campaigns/Russian_Intervention_2015.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "Syria - Russian Intervention 2015",
|
||||
"theater": "Syria",
|
||||
"authors": "Malakhit",
|
||||
"recommended_player_faction": "Russia 2010",
|
||||
"recommended_enemy_faction": "Insurgents (Hard)",
|
||||
"description": "<p>This short campaign is loosely based on the 2015 Russian military intervention in Syria. It should be perfect for COIN operations, especially with the Hind.</p>",
|
||||
"version": "6.0",
|
||||
"miz": "Russian_Intervention_2015.miz",
|
||||
"performance": 1
|
||||
}
|
||||
BIN
resources/campaigns/Russian_Intervention_2015.miz
Normal file
BIN
resources/campaigns/Russian_Intervention_2015.miz
Normal file
Binary file not shown.
11
resources/campaigns/around_the_mountain.json
Normal file
11
resources/campaigns/around_the_mountain.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "Caucasus - Around The Mountain",
|
||||
"theater": "Caucasus",
|
||||
"authors": "Dillie",
|
||||
"recommended_player_faction": "Russia 2010",
|
||||
"recommended_enemy_faction": "USA 1990",
|
||||
"description": "<p>Scenario from Russia to Georgia in two Frontlines.</p>",
|
||||
"version": "7.0",
|
||||
"miz": "around_the_mountain.miz",
|
||||
"performance": 2
|
||||
}
|
||||
BIN
resources/campaigns/around_the_mountain.miz
Normal file
BIN
resources/campaigns/around_the_mountain.miz
Normal file
Binary file not shown.
@ -7,5 +7,5 @@
|
||||
"description": "<p>You have managed to establish a foothold at Khasab. Continue pushing south.</p>",
|
||||
"miz": "battle_of_abu_dhabi.miz",
|
||||
"performance": 2,
|
||||
"version": "6.0"
|
||||
"version": "7.0"
|
||||
}
|
||||
@ -5,5 +5,5 @@
|
||||
"description": "<p>A medium sized theater with bases along the coast of the Black Sea.</p>",
|
||||
"miz": "black_sea.miz",
|
||||
"performance": 2,
|
||||
"version": "6.0"
|
||||
"version": "7.0"
|
||||
}
|
||||
@ -7,5 +7,5 @@
|
||||
"description": "<p>This is a light scenario on the Normandy map.</p><p>August 1944, allied forces are pushing from Caen/Carpiquet to the cities of Lisieux and Evreux.<p>Lisieux is an important logistic hub for the Werhmacht, and Evreux airbase is hosting most of the Luftwaffe forces in the region.</p>",
|
||||
"miz": "caen_to_evreux.miz",
|
||||
"performance": 1,
|
||||
"version": "6.0"
|
||||
"version": "7.0"
|
||||
}
|
||||
|
||||
Binary file not shown.
@ -5,7 +5,7 @@
|
||||
"recommended_player_faction": "Bluefor Modern",
|
||||
"recommended_enemy_faction": "Redfor (China) 2010",
|
||||
"description": "<p>This is an asymmetrical Red Flag Exercise scenario for the NTTR comprising 4 control points. You start off in control of the two Tonopah airports, and will push south to capture Groom Lake and Nellis AFBs. Taking down Nellis AFB's IADS and striking their resource sites ASAP once Groom Lake has been captured is recommended to offset their resource advantage.</p>",
|
||||
"version": "6.0",
|
||||
"version": "7.0",
|
||||
"miz": "exercise_vegas_nerve.miz",
|
||||
"performance": 0
|
||||
}
|
||||
@ -7,5 +7,5 @@
|
||||
"description": "<p>In this scenario, you start in Israel and the conflict is focused around the golan heights, an historically disputed territory.<br/><br/>This scenario is designed to be performance friendly.</p>",
|
||||
"miz": "golan_heights_lite.miz",
|
||||
"performance": 1,
|
||||
"version": "6.0"
|
||||
"version": "7.0"
|
||||
}
|
||||
|
||||
Binary file not shown.
11
resources/campaigns/mozdok_to_maykop.json
Normal file
11
resources/campaigns/mozdok_to_maykop.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "Caucasus - Mozdok to Maykop",
|
||||
"theater": "Caucasus",
|
||||
"authors": "Khopa",
|
||||
"recommended_player_faction": "Russia 2010",
|
||||
"recommended_enemy_faction": "USA 1990",
|
||||
"description": "<p>A small theater in Russia, progress from Mozdok to Maykop.</p><p>This scenario is pretty simple, and is ideal if you want to run a short campaign to try liberation. If your PC is not powerful, this is also the less performance heavy scenario.</p>",
|
||||
"miz": "mozdok_to_maykop.miz",
|
||||
"performance": 0,
|
||||
"version": "7.0"
|
||||
}
|
||||
BIN
resources/campaigns/mozdok_to_maykop.miz
Normal file
BIN
resources/campaigns/mozdok_to_maykop.miz
Normal file
Binary file not shown.
11
resources/campaigns/nevada_Limited_Air.json
Normal file
11
resources/campaigns/nevada_Limited_Air.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "Nevada - Limited Air",
|
||||
"theater": "Nevada",
|
||||
"authors": "Doc_of_Mur",
|
||||
"recommended_player_faction": "USA 2005",
|
||||
"recommended_enemy_faction": "Russia 1975",
|
||||
"description": "<p>This campaign is designed to be beginner friendly in that the number of aircraft slot have been limited. Other than the starting point and the 'boss' base the max slots in each of the airbases have a mere 3-5 slots.</p><p>This should prevent the airpower rush escperienced in most of the other larger campaign.</p>",
|
||||
"version": "7.0",
|
||||
"miz": "nevada_limited_air.miz",
|
||||
"performance": 1
|
||||
}
|
||||
BIN
resources/campaigns/nevada_Limited_Air.miz
Normal file
BIN
resources/campaigns/nevada_Limited_Air.miz
Normal file
Binary file not shown.
@ -2,10 +2,10 @@
|
||||
"name": "Caucasus - Northern Russia",
|
||||
"theater": "Caucasus",
|
||||
"authors": "Plob",
|
||||
"recommended_player_faction": "USA 2005",
|
||||
"recommended_enemy_faction": "Russia 1990",
|
||||
"description": "<p>A medium campaign through the north eastern part of the Caucasus map.</p><p>Russia has invaded Georgia through the eastern mountains. Mount a counter offense and push them back!",
|
||||
"recommended_player_faction": "Bluefor Modern",
|
||||
"recommended_enemy_faction": "Russia 1975",
|
||||
"description": "<p>A medium campaign through the north eastern part of the Caucasus map. Play vs 1975 Russia for an low-medium difficulty campaign, play vs russia 1990 for a hard difficulty campaign. </p><p>Russia has invaded Georgia through the eastern mountains. Mount a counter offense and push them back!",
|
||||
"miz": "northern_russia.miz",
|
||||
"performance": 2,
|
||||
"version": "6.0"
|
||||
"version": "7.0"
|
||||
}
|
||||
Binary file not shown.
@ -2,8 +2,10 @@
|
||||
"name": "Syria - Operation Allied Sword",
|
||||
"theater": "Syria",
|
||||
"authors": "Fuzzle",
|
||||
"recommended_player_faction": "Israel-USN 2005 (Allied Sword)",
|
||||
"recommended_enemy_faction": "Syria-Lebanon 2005 (Allied Sword)",
|
||||
"description": "<p>In this fictional scenario, a US/Israeli coalition must push north from the Israeli border, through Syria and Lebanon to Aleppo.</p><p><strong>Backstory:</strong> A Syrian-Lebanese joint force (with Russian materiel support) has attacked Israel, attmepting to cross the northern border. With the arrival of a US carrier group, Israel prepares its counterattack. The US Navy will handle the Beirut region's coastal arena, while the IAF will push through Damascus and the inland mountain ranges.</p>",
|
||||
"version": "6.0",
|
||||
"version": "7.0",
|
||||
"miz": "operation_allied_sword.miz",
|
||||
"performance": 2
|
||||
}
|
||||
Binary file not shown.
11
resources/campaigns/operation_blackball.json
Normal file
11
resources/campaigns/operation_blackball.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "Syria - Operation Blackball",
|
||||
"theater": "Syria",
|
||||
"authors": "Fuzzle",
|
||||
"recommended_player_faction": "US Navy 2005",
|
||||
"recommended_enemy_faction": "Russia 2010",
|
||||
"description": "<p><strong>Warning: This campaign will not work if the attacking faction does not have a carrier.</strong></p><p>A lightweight, fictional showcase of Cyprus for the Syria terrain. A US Navy force must deploy from a FOB and carrier group to push from the north-east down through the island.</p><p><strong>Backstory:</strong> The world is at war. With the help of her eastern allies, Russia has taken the Suez Canal and deployed a large naval force to the Mediterranean, trapping a US carrier group near the Turkish-Syrian border. Now, they must break out by taking Cyprus back.</p>",
|
||||
"version": "7.0",
|
||||
"miz": "operation_blackball.miz",
|
||||
"performance": 1
|
||||
}
|
||||
BIN
resources/campaigns/operation_blackball.miz
Normal file
BIN
resources/campaigns/operation_blackball.miz
Normal file
Binary file not shown.
11
resources/campaigns/operation_dynamo.json
Normal file
11
resources/campaigns/operation_dynamo.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "The Channel - Operation Dynamo",
|
||||
"theater": "The Channel",
|
||||
"authors": "Khopa",
|
||||
"recommended_player_faction": "Allies 1940",
|
||||
"recommended_enemy_faction": "Germany 1940",
|
||||
"description": "<p>The Battle of Dunkirk (French: Bataille de Dunkerque) was fought around the French port of Dunkirk (Dunkerque) during the Second World War, between the Allies and Nazi Germany. As the Allies were losing the Battle of France on the Western Front, the Battle of Dunkirk was the defence and evacuation of British and other Allied forces to Britain from 26 May to 4 June 1940..</p>",
|
||||
"version": 7.0,
|
||||
"miz": "operation_dynamo.miz",
|
||||
"performance": 1
|
||||
}
|
||||
BIN
resources/campaigns/operation_dynamo.miz
Normal file
BIN
resources/campaigns/operation_dynamo.miz
Normal file
Binary file not shown.
@ -5,7 +5,7 @@
|
||||
"recommended_player_faction": "Bluefor Modern",
|
||||
"recommended_enemy_faction": "Turkey 2005",
|
||||
"description": "<p>This is a semi-fictional what-if scenario for Operation Peace Spring, during which Turkish forces that crossed into Syria on an offensive against Kurdish militias were emboldened by early successes to continue pushing further southward. Attempts to broker a ceasefire have failed. Members of Operation Inherent Resolve have gathered at Ramat David Airbase in Israel to launch a counter-offensive. Campaign inversion is available if you wish to play as Turkey.</p>",
|
||||
"version": "6.0",
|
||||
"version": "7.0",
|
||||
"miz": "operation_peace_spring.miz",
|
||||
"performance": 1
|
||||
}
|
||||
Binary file not shown.
@ -5,7 +5,7 @@
|
||||
"recommended_player_faction": "USA 2005",
|
||||
"recommended_enemy_faction": "Russia 1990",
|
||||
"description": "<p>United Nations Observer Mission in Georgia (UNOMIG) observers stationed in Georgia to monitor the ceasefire between Georgia and Abkhazia have been cut off from friendly forces by Russian troops backing the separatist state. The UNOMIG HQ at Sukhumi has been taken, and a small contingent of observers and troops at the Zugdidi Sector HQ will have to make a run for the coast, supported by offshore US naval aircraft. The contingent is aware that their best shot at survival is to swiftly retake Sukhumi before Russian forces have a chance to dig in, so that friendly ground forces can land and reinforce them.<br/></p><p><strong>Note:</strong> Ground unit purchase will not be available past Turn 0 until Sukhumi is retaken, so it is imperative you reach Sukhumi with at least one surviving ground unit to capture it. The player can either play the first leg of the scenario as an evacuation with a couple of light vehicles (e.g. Humvees) set on breakthrough (modifying waypoints in the mission editor so they are not charging head-on into enemy ground forces is suggested), or purchase heavier ground units if they wish to experience a more traditional ground war.</p>",
|
||||
"version": "6.0",
|
||||
"version": "7.0",
|
||||
"miz": "operation_vectrons_claw.miz",
|
||||
"performance": 1
|
||||
}
|
||||
Binary file not shown.
@ -1,11 +0,0 @@
|
||||
{
|
||||
"name": "Caucasus - Russia Small",
|
||||
"theater": "Caucasus",
|
||||
"authors": "Khopa",
|
||||
"recommended_player_faction": "Russia 2010",
|
||||
"recommended_enemy_faction": "USA 1990",
|
||||
"description": "<p>A small theater in Russia, progress from Mozdok to Maykop.</p><p>This scenario is pretty simple, it is ideal if you want to run a short campaign. If your PC is not powerful, this is also the less performance heavy scenario.</p>",
|
||||
"miz": "russia_small.miz",
|
||||
"performance": 0,
|
||||
"version": "6.0"
|
||||
}
|
||||
Binary file not shown.
11
resources/campaigns/scenic_route.json
Normal file
11
resources/campaigns/scenic_route.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "Persian Gulf - Scenic Route",
|
||||
"theater": "Persian Gulf",
|
||||
"authors": "Fuzzle",
|
||||
"recommended_player_faction": "US Navy 2005",
|
||||
"recommended_enemy_faction": "Iran 2015",
|
||||
"description": "<p>A lightweight naval campaign involving a US Navy carrier group pushing across the coast of Iran. <strong>Note that the ground units purchased on turn zero must sustain you until you've taken the first hostile FOB. The starting point does not have a factory to simulate a Marine Expeditionary Force deploying from the carrier group.</strong></p><p><strong>Backstory:</strong> Iran has declared war on all US forces in the Gulf, resulting in all local allies withdrawing their support for American troops. A lone carrier group must pacify the southern coast of Iran and hold out until backup can arrive, lest the US and her interests be ejected from the region permanently.</p>",
|
||||
"version": "7.0",
|
||||
"miz": "scenic_route.miz",
|
||||
"performance": 1
|
||||
}
|
||||
BIN
resources/campaigns/scenic_route.miz
Normal file
BIN
resources/campaigns/scenic_route.miz
Normal file
Binary file not shown.
@ -4,8 +4,8 @@
|
||||
"authors": "Plob",
|
||||
"recommended_player_faction": "Bluefor Modern",
|
||||
"recommended_enemy_faction": "Syria 2011",
|
||||
"description": "<p>Syria Full map, designed for groups of 4-12 players.</p>",
|
||||
"description": "<p>A long campaign across the syria map, strap in.</p></p>Each turn after planning missions, enable culling to ensure correct culling behaviour.</p>",
|
||||
"miz": "syria_full_map.miz",
|
||||
"performance": 3,
|
||||
"version": "6.0"
|
||||
"version": "7.0"
|
||||
}
|
||||
Binary file not shown.
@ -1,164 +0,0 @@
|
||||
local unitPayloads = {
|
||||
["name"] = "MB-339PAN",
|
||||
["payloads"] = {
|
||||
[1] = {
|
||||
["name"] = "CAP",
|
||||
["pylons"] = {
|
||||
[1] = {
|
||||
["CLSID"] = "{MB339-DEFA553_R}",
|
||||
["num"] = 7,
|
||||
},
|
||||
[2] = {
|
||||
["CLSID"] = "{MB339-DEFA553_L}",
|
||||
["num"] = 4,
|
||||
},
|
||||
[3] = {
|
||||
["CLSID"] = "{FUEL-TIP-ELLITTIC-L}",
|
||||
["num"] = 1,
|
||||
},
|
||||
[4] = {
|
||||
["CLSID"] = "{FUEL-TIP-ELLITTIC-R}",
|
||||
["num"] = 10,
|
||||
},
|
||||
},
|
||||
["tasks"] = {
|
||||
},
|
||||
},
|
||||
[2] = {
|
||||
["name"] = "CAS",
|
||||
["pylons"] = {
|
||||
[1] = {
|
||||
["CLSID"] = "{FUEL-TIP-TANK-500-R}",
|
||||
["num"] = 10,
|
||||
},
|
||||
[2] = {
|
||||
["CLSID"] = "{FUEL-TIP-TANK-500-L}",
|
||||
["num"] = 1,
|
||||
},
|
||||
[3] = {
|
||||
["CLSID"] = "{LAU-10}",
|
||||
["num"] = 3,
|
||||
},
|
||||
[4] = {
|
||||
["CLSID"] = "{LAU-10}",
|
||||
["num"] = 8,
|
||||
},
|
||||
[5] = {
|
||||
["CLSID"] = "{MB339-DEFA553_L}",
|
||||
["num"] = 4,
|
||||
},
|
||||
[6] = {
|
||||
["CLSID"] = "{MB339-DEFA553_R}",
|
||||
["num"] = 7,
|
||||
},
|
||||
},
|
||||
["tasks"] = {
|
||||
},
|
||||
},
|
||||
[3] = {
|
||||
["name"] = "ANTISHIP",
|
||||
["pylons"] = {
|
||||
[1] = {
|
||||
["CLSID"] = "{FUEL-TIP-TANK-500-R}",
|
||||
["num"] = 10,
|
||||
},
|
||||
[2] = {
|
||||
["CLSID"] = "{FUEL-TIP-TANK-500-L}",
|
||||
["num"] = 1,
|
||||
},
|
||||
[3] = {
|
||||
["CLSID"] = "{LAU-10}",
|
||||
["num"] = 3,
|
||||
},
|
||||
[4] = {
|
||||
["CLSID"] = "{LAU-10}",
|
||||
["num"] = 8,
|
||||
},
|
||||
[5] = {
|
||||
["CLSID"] = "{MB339-DEFA553_L}",
|
||||
["num"] = 4,
|
||||
},
|
||||
[6] = {
|
||||
["CLSID"] = "{MB339-DEFA553_R}",
|
||||
["num"] = 7,
|
||||
},
|
||||
},
|
||||
["tasks"] = {
|
||||
},
|
||||
},
|
||||
[4] = {
|
||||
["name"] = "STRIKE",
|
||||
["pylons"] = {
|
||||
[1] = {
|
||||
["CLSID"] = "{FUEL-TIP-TANK-500-R}",
|
||||
["num"] = 10,
|
||||
},
|
||||
[2] = {
|
||||
["CLSID"] = "{FUEL-TIP-TANK-500-L}",
|
||||
["num"] = 1,
|
||||
},
|
||||
[3] = {
|
||||
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
|
||||
["num"] = 3,
|
||||
},
|
||||
[4] = {
|
||||
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
|
||||
["num"] = 8,
|
||||
},
|
||||
[5] = {
|
||||
["CLSID"] = "{MB339-AN-M3_L}",
|
||||
["num"] = 4,
|
||||
},
|
||||
[6] = {
|
||||
["CLSID"] = "{MB339-AN-M3_R}",
|
||||
["num"] = 7,
|
||||
},
|
||||
[7] = {
|
||||
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
|
||||
["num"] = 9,
|
||||
},
|
||||
[8] = {
|
||||
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
|
||||
["num"] = 2,
|
||||
},
|
||||
},
|
||||
["tasks"] = {
|
||||
},
|
||||
},
|
||||
[5] = {
|
||||
["name"] = "SEAD",
|
||||
["pylons"] = {
|
||||
[1] = {
|
||||
["CLSID"] = "{FUEL-TIP-TANK-500-R}",
|
||||
["num"] = 10,
|
||||
},
|
||||
[2] = {
|
||||
["CLSID"] = "{FUEL-TIP-TANK-500-L}",
|
||||
["num"] = 1,
|
||||
},
|
||||
[3] = {
|
||||
["CLSID"] = "{LAU-10}",
|
||||
["num"] = 3,
|
||||
},
|
||||
[4] = {
|
||||
["CLSID"] = "{LAU-10}",
|
||||
["num"] = 8,
|
||||
},
|
||||
[5] = {
|
||||
["CLSID"] = "{MB339-AN-M3_L}",
|
||||
["num"] = 4,
|
||||
},
|
||||
[6] = {
|
||||
["CLSID"] = "{MB339-AN-M3_R}",
|
||||
["num"] = 7,
|
||||
},
|
||||
},
|
||||
["tasks"] = {
|
||||
},
|
||||
},
|
||||
},
|
||||
["tasks"] = {
|
||||
},
|
||||
["unitType"] = "MB-339PAN",
|
||||
}
|
||||
return unitPayloads
|
||||
@ -1,49 +1,39 @@
|
||||
{
|
||||
"country": "USA",
|
||||
"name": "USA 2005 Modded",
|
||||
"authors": "Khopa",
|
||||
"description": "<p>USA 2005 with the Raptor mod, with the F-22 mod by Grinelli Designs.</p>",
|
||||
"country": "Israel",
|
||||
"name": "Israel-USN 2005 (Allied Sword)",
|
||||
"authors": "Fuzzle",
|
||||
"description": "<p>A joint US Navy/Israeli modern faction for use with the Operation Allied Sword scenario.</p>",
|
||||
"locales": [
|
||||
"en_US"
|
||||
],
|
||||
"aircrafts": [
|
||||
"A-10C Thunderbolt II (Suite 3)",
|
||||
"A-10C Thunderbolt II (Suite 7)",
|
||||
"AH-64D Apache Longbow",
|
||||
"AV-8B Harrier II Night Attack",
|
||||
"B-1B Lancer",
|
||||
"B-52H Stratofortress",
|
||||
"F-117A Nighthawk",
|
||||
"F-14B Tomcat",
|
||||
"F-4E Phantom II",
|
||||
"F-15C Eagle",
|
||||
"F-15E Strike Eagle",
|
||||
"F-16CM Fighting Falcon (Block 50)",
|
||||
"F-22A Raptor",
|
||||
"F-14B Tomcat",
|
||||
"F/A-18C Hornet (Lot 20)",
|
||||
"AV-8B Harrier II Night Attack",
|
||||
"AH-1W SuperCobra",
|
||||
"AH-64A Apache",
|
||||
"S-3B Viking",
|
||||
"SH-60B Seahawk",
|
||||
"UH-1H Iroquois"
|
||||
],
|
||||
"awacs": [
|
||||
"E-2C Hawkeye",
|
||||
"E-3A"
|
||||
"E-2C Hawkeye"
|
||||
],
|
||||
"tankers": [
|
||||
"KC-130",
|
||||
"KC-135 Stratotanker",
|
||||
"KC-135 Stratotanker MPRS",
|
||||
"KC-130",
|
||||
"S-3B Tanker"
|
||||
],
|
||||
"frontline_units": [
|
||||
"LAV-25",
|
||||
"M113",
|
||||
"M1043 HMMWV (M2 HMG)",
|
||||
"M1045 HMMWV (BGM-71 TOW)",
|
||||
"M1097 Heavy HMMWV Avenger",
|
||||
"M1126 Stryker ICV (M2 HMG)",
|
||||
"M1134 Stryker ATGM (BGM-71 TOW)",
|
||||
"M1A2 Abrams",
|
||||
"M2A2 Bradley",
|
||||
"M6 Linebacker"
|
||||
"Merkava Mk IV",
|
||||
"M163 Vulcan Air Defense System"
|
||||
],
|
||||
"artillery_units": [
|
||||
"M109A6 Paladin",
|
||||
@ -53,19 +43,18 @@
|
||||
"Truck M818 6x6"
|
||||
],
|
||||
"infantry_units": [
|
||||
"Infantry M249",
|
||||
"Infantry M4",
|
||||
"MANPADS Stinger",
|
||||
"Mortar 2B11 120mm"
|
||||
"Infantry M249",
|
||||
"MANPADS Stinger"
|
||||
],
|
||||
"air_defenses": [
|
||||
"AvengerGenerator",
|
||||
"ChaparralGenerator",
|
||||
"HawkGenerator",
|
||||
"LinebackerGenerator",
|
||||
"VulcanGenerator",
|
||||
"PatriotGenerator"
|
||||
],
|
||||
"ewrs": [
|
||||
"PatriotEwrGenerator"
|
||||
"HawkEwrGenerator"
|
||||
],
|
||||
"aircraft_carrier": [
|
||||
"Stennis"
|
||||
@ -74,16 +63,20 @@
|
||||
"LHA_Tarawa"
|
||||
],
|
||||
"destroyers": [
|
||||
"USS_Arleigh_Burke_IIa"
|
||||
"USS_Arleigh_Burke_IIa",
|
||||
"PERRY"
|
||||
],
|
||||
"cruisers": [
|
||||
"TICONDEROG"
|
||||
],
|
||||
"requirements": {
|
||||
},
|
||||
"carrier_names": [
|
||||
"CVN-71 Theodore Roosevelt",
|
||||
"CVN-72 Abraham Lincoln",
|
||||
"CVN-73 George Washington",
|
||||
"CVN-74 John C. Stennis"
|
||||
"CVN-74 John C. Stennis",
|
||||
"CVN-75 Harry S. Truman"
|
||||
],
|
||||
"helicopter_carrier_names": [
|
||||
"LHA-1 Tarawa",
|
||||
@ -96,29 +89,24 @@
|
||||
"ArleighBurkeGroupGenerator",
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
],
|
||||
"requirements": {
|
||||
"F-22A mod by Grinnelli Designs": "https://bestf22modever.com"
|
||||
},
|
||||
"has_jtac": true,
|
||||
"jtac_unit": "MQ-9 Reaper",
|
||||
"doctrine": "modern",
|
||||
"liveries_overrides": {
|
||||
"F-14B Tomcat": [
|
||||
"VF-142 Ghostriders"
|
||||
],
|
||||
"F/A-18C Hornet (Lot 20)": [
|
||||
"VFA-106",
|
||||
"VFA-113",
|
||||
"VFA-122",
|
||||
"VFA-131",
|
||||
"VFA-192",
|
||||
"VFA-34",
|
||||
"VFA-37",
|
||||
"VFA-83",
|
||||
"VFA-87",
|
||||
"VFA-97",
|
||||
"VMFA-122",
|
||||
"VMFA-132",
|
||||
"VMFA-251",
|
||||
"VMFA-312",
|
||||
"VMFA-314",
|
||||
"VMFA-323"
|
||||
"VMFA-251 high visibility"
|
||||
],
|
||||
"AV-8B Harrier II Night Attack": [
|
||||
"VMAT-542"
|
||||
],
|
||||
"AH-1W SuperCobra": [
|
||||
"Marines"
|
||||
],
|
||||
"UH-1H Iroquois": [
|
||||
"US NAVY"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,8 @@
|
||||
"AV-8B Harrier II Night Attack",
|
||||
"B-1B Lancer",
|
||||
"B-52H Stratofortress",
|
||||
"C-130",
|
||||
"C-130J-30 Super Hercules",
|
||||
"F-117A Nighthawk",
|
||||
"F-14A Tomcat (Block 135-GR Late)",
|
||||
"F-14B Tomcat",
|
||||
|
||||
102
resources/factions/Syria-Lebanon_2005_Allied_Sword.json
Normal file
102
resources/factions/Syria-Lebanon_2005_Allied_Sword.json
Normal file
@ -0,0 +1,102 @@
|
||||
{
|
||||
"country": "Combined Joint Task Forces Red",
|
||||
"name": "Syria-Lebanon 2005 (Allied Sword)",
|
||||
"authors": "Fuzzle",
|
||||
"description": "<p>Syria-Lebanon alliance in a modern setting with several imported Russian assets. Designed for use with the Allied Sword scenario.</p>",
|
||||
"aircrafts": [
|
||||
"MiG-23ML Flogger-G",
|
||||
"MiG-25RBT Foxbat-B",
|
||||
"MiG-29A Fulcrum-A",
|
||||
"Su-17M4 Fitter-K",
|
||||
"Su-24M Fencer-D",
|
||||
"Su-30 Flanker-C",
|
||||
"Su-34 Fullback",
|
||||
"L-39ZA Albatros",
|
||||
"Tu-22M3 Backfire-C",
|
||||
"Mi-24V Hind-E",
|
||||
"Mi-8MTV2 Hip",
|
||||
"SA 342M Gazelle",
|
||||
"SA 342L Gazelle"
|
||||
],
|
||||
"awacs": [
|
||||
"A-50"
|
||||
],
|
||||
"tankers": [
|
||||
"IL-78M"
|
||||
],
|
||||
"frontline_units": [
|
||||
"BMP-1",
|
||||
"BMP-2",
|
||||
"BTR-80",
|
||||
"BRDM-2",
|
||||
"MT-LB",
|
||||
"T-55A",
|
||||
"T-72B with Kontakt-1 ERA",
|
||||
"T-90A",
|
||||
"ZSU-57-2 'Sparka'"
|
||||
],
|
||||
"artillery_units": [
|
||||
"BM-21 Grad",
|
||||
"2S1 Gvozdika"
|
||||
],
|
||||
"logistics_units": [
|
||||
"Truck Ural-375",
|
||||
"LUV UAZ-469 Jeep"
|
||||
],
|
||||
"infantry_units": [
|
||||
"Paratrooper AKS",
|
||||
"Infantry AK-74 Rus",
|
||||
"Paratrooper RPG-16",
|
||||
"MANPADS SA-18 Igla-S \"Grouse\""
|
||||
],
|
||||
"air_defenses": [
|
||||
"ColdWarFlakGenerator",
|
||||
"SA2Generator",
|
||||
"SA3Generator",
|
||||
"SA6Generator",
|
||||
"SA8Generator",
|
||||
"SA9Generator",
|
||||
"SA10Generator",
|
||||
"SA11Generator",
|
||||
"SA13Generator",
|
||||
"SA19Generator",
|
||||
"ZSU23Generator",
|
||||
"ZU23Generator",
|
||||
"ZU23UralGenerator",
|
||||
"ZSU57Generator"
|
||||
],
|
||||
"ewrs": [
|
||||
"BoxSpringGenerator",
|
||||
"TallRackGenerator"
|
||||
],
|
||||
"missiles": [
|
||||
"ScudGenerator"
|
||||
],
|
||||
"missiles_group_count": 2,
|
||||
"coastal_defenses": [
|
||||
"SilkwormGenerator"
|
||||
],
|
||||
"coastal_group_count": 4,
|
||||
"aircraft_carrier": [
|
||||
],
|
||||
"helicopter_carrier": [
|
||||
],
|
||||
"helicopter_carrier_names": [
|
||||
],
|
||||
"destroyers": [
|
||||
"REZKY",
|
||||
"MOLNIYA"
|
||||
],
|
||||
"cruisers": [
|
||||
],
|
||||
"requirements": {},
|
||||
"carrier_names": [
|
||||
],
|
||||
"coastal_group_count": 8,
|
||||
"navy_generators": [
|
||||
"GrishaGroupGenerator",
|
||||
"MolniyaGroupGenerator",
|
||||
"RussianNavyGroupGenerator",
|
||||
"LaCombattanteIIGroupGenerator"
|
||||
]
|
||||
}
|
||||
@ -1,10 +1,11 @@
|
||||
{
|
||||
"country": "Australia",
|
||||
"name": "Australia 2005",
|
||||
"authors": "Khopa",
|
||||
"authors": "Khopa, SpaceEnthusiast",
|
||||
"description": "<p>The Australian army in 2005.</p><p>Some units might not be accurate, but were picked to represent at best this army.</p>",
|
||||
"aircrafts": [
|
||||
"AH-1W SuperCobra",
|
||||
"C-130J-30 Super Hercules",
|
||||
"F/A-18C Hornet (Lot 20)",
|
||||
"SH-60B Seahawk",
|
||||
"UH-1H Iroquois"
|
||||
@ -47,7 +48,9 @@
|
||||
"USS_Arleigh_Burke_IIa"
|
||||
],
|
||||
"cruisers": [],
|
||||
"requirements": {},
|
||||
"requirements": {
|
||||
"C-130J-30 Super Hercules Mod by Anubis": "https://forums.eagle.ru/topic/252075-dcs-super-hercules-mod-by-anubis/"
|
||||
},
|
||||
"carrier_names": [],
|
||||
"helicopter_carrier_names": [
|
||||
"HMAS Canberra",
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
{
|
||||
"country": "Australia",
|
||||
"name": "Australia 2005 (With C-130)",
|
||||
"authors": "Khopa, SpaceEnthusiast",
|
||||
"description": "<p>The Australian army in 2005.</p><p>Some units might not be accurate, but were picked to represent at best this army.</p>",
|
||||
"aircrafts": [
|
||||
"AH-1W SuperCobra",
|
||||
"C-130J-30 Super Hercules",
|
||||
"F/A-18C Hornet (Lot 20)",
|
||||
"SH-60B Seahawk",
|
||||
"UH-1H Iroquois"
|
||||
],
|
||||
"awacs": [
|
||||
"E-3A"
|
||||
],
|
||||
"tankers": [
|
||||
"KC-130",
|
||||
"KC-135 Stratotanker"
|
||||
],
|
||||
"frontline_units": [
|
||||
"FV510 Warrior",
|
||||
"LAV-25",
|
||||
"Leopard 1A3",
|
||||
"M113",
|
||||
"M1A2 Abrams"
|
||||
],
|
||||
"artillery_units": [],
|
||||
"logistics_units": [
|
||||
"Truck M818 6x6"
|
||||
],
|
||||
"infantry_units": [
|
||||
"Infantry M249",
|
||||
"Infantry M4",
|
||||
"MANPADS Stinger"
|
||||
],
|
||||
"air_defenses": [
|
||||
"HawkGenerator",
|
||||
"RapierGenerator"
|
||||
],
|
||||
"ewrs": [
|
||||
"HawkEwrGenerator"
|
||||
],
|
||||
"aircraft_carrier": [],
|
||||
"helicopter_carrier": [
|
||||
"LHA_Tarawa"
|
||||
],
|
||||
"destroyers": [
|
||||
"USS_Arleigh_Burke_IIa"
|
||||
],
|
||||
"cruisers": [],
|
||||
"requirements": {
|
||||
"C-130J-30 Super Hercules Mod by Anubis": "https://forums.eagle.ru/topic/252075-dcs-super-hercules-mod-by-anubis/"
|
||||
},
|
||||
"carrier_names": [],
|
||||
"helicopter_carrier_names": [
|
||||
"HMAS Canberra",
|
||||
"HMAS Adelaide"
|
||||
],
|
||||
"navy_generators": [
|
||||
"ArleighBurkeGroupGenerator"
|
||||
],
|
||||
"has_jtac": true,
|
||||
"jtac_unit": "MQ-9 Reaper",
|
||||
"liveries_overrides": {
|
||||
"F/A-18C Hornet (Lot 20)": [
|
||||
"Australian 75th Squadron",
|
||||
"Australian 77th Squadron"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -2,11 +2,14 @@
|
||||
"country": "Combined Joint Task Forces Blue",
|
||||
"name": "Bluefor Coldwar",
|
||||
"authors": "Khopa",
|
||||
"description": "<p>A generic bluefor coldwar faction.</p>",
|
||||
"description": "<p>A generic bluefor coldwar faction. (With the A-4E-C mod)</p>",
|
||||
"aircrafts": [
|
||||
"A-10A Thunderbolt II",
|
||||
"A-4E Skyhawk",
|
||||
"AJS-37 Viggen",
|
||||
"B-52H Stratofortress",
|
||||
"C-130",
|
||||
"C-130J-30 Super Hercules",
|
||||
"F-14A Tomcat (Block 135-GR Late)",
|
||||
"F-14B Tomcat",
|
||||
"F-4E Phantom II",
|
||||
@ -16,7 +19,6 @@
|
||||
"UH-1H Iroquois"
|
||||
],
|
||||
"awacs": [
|
||||
"C-130",
|
||||
"E-2C Hawkeye",
|
||||
"E-3A"
|
||||
],
|
||||
@ -60,7 +62,9 @@
|
||||
"cruisers": [
|
||||
"TICONDEROG"
|
||||
],
|
||||
"requirements": {},
|
||||
"requirements": {
|
||||
"Community A-4E": "https://heclak.github.io/community-a4e-c/"
|
||||
},
|
||||
"carrier_names": [
|
||||
"CVN-71 Theodore Roosevelt",
|
||||
"CVN-72 Abraham Lincoln",
|
||||
|
||||
@ -1,86 +0,0 @@
|
||||
{
|
||||
"country": "Combined Joint Task Forces Blue",
|
||||
"name": "Bluefor Coldwar (With A4)",
|
||||
"authors": "Khopa",
|
||||
"description": "<p>A generic bluefor coldwar faction. (With the A-4E-C mod)</p>",
|
||||
"aircrafts": [
|
||||
"A-10A Thunderbolt II",
|
||||
"A-4E Skyhawk",
|
||||
"AJS-37 Viggen",
|
||||
"B-52H Stratofortress",
|
||||
"F-14A Tomcat (Block 135-GR Late)",
|
||||
"F-14B Tomcat",
|
||||
"F-4E Phantom II",
|
||||
"F-5E Tiger II",
|
||||
"SA 342L Gazelle",
|
||||
"SA 342M Gazelle",
|
||||
"UH-1H Iroquois"
|
||||
],
|
||||
"awacs": [
|
||||
"C-130",
|
||||
"E-2C Hawkeye",
|
||||
"E-3A"
|
||||
],
|
||||
"tankers": [
|
||||
"KC-130",
|
||||
"KC-135 Stratotanker"
|
||||
],
|
||||
"frontline_units": [
|
||||
"M113",
|
||||
"M48 Chaparral",
|
||||
"M60A3 \"Patton\""
|
||||
],
|
||||
"artillery_units": [
|
||||
"M109A6 Paladin"
|
||||
],
|
||||
"logistics_units": [
|
||||
"Truck M818 6x6"
|
||||
],
|
||||
"infantry_units": [
|
||||
"Infantry M249",
|
||||
"Infantry M4"
|
||||
],
|
||||
"air_defenses": [
|
||||
"ChaparralGenerator",
|
||||
"EarlyColdWarFlakGenerator",
|
||||
"HawkGenerator",
|
||||
"VulcanGenerator"
|
||||
],
|
||||
"ewrs": [
|
||||
"HawkEwrGenerator"
|
||||
],
|
||||
"aircraft_carrier": [
|
||||
"Stennis"
|
||||
],
|
||||
"helicopter_carrier": [
|
||||
"LHA_Tarawa"
|
||||
],
|
||||
"destroyers": [
|
||||
"USS_Arleigh_Burke_IIa"
|
||||
],
|
||||
"cruisers": [
|
||||
"TICONDEROG"
|
||||
],
|
||||
"requirements": {
|
||||
"Community A-4E": "https://heclak.github.io/community-a4e-c/"
|
||||
},
|
||||
"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": [
|
||||
"ArleighBurkeGroupGenerator"
|
||||
],
|
||||
"has_jtac": true,
|
||||
"jtac_unit": "MQ-9 Reaper",
|
||||
"doctrine": "coldwar"
|
||||
}
|
||||
@ -1,88 +0,0 @@
|
||||
{
|
||||
"country": "Combined Joint Task Forces Blue",
|
||||
"name": "Bluefor Coldwar (With A4 & MB339)",
|
||||
"authors": "Khopa",
|
||||
"description": "<p>A generic bluefor coldwar faction. (With the A-4E-C and the MB-339 mods)</p>",
|
||||
"aircrafts": [
|
||||
"A-10A Thunderbolt II",
|
||||
"A-4E Skyhawk",
|
||||
"AJS-37 Viggen",
|
||||
"B-52H Stratofortress",
|
||||
"F-14A Tomcat (Block 135-GR Late)",
|
||||
"F-14B Tomcat",
|
||||
"F-4E Phantom II",
|
||||
"F-5E Tiger II",
|
||||
"MB-339PAN",
|
||||
"SA 342L Gazelle",
|
||||
"SA 342M Gazelle",
|
||||
"UH-1H Iroquois"
|
||||
],
|
||||
"awacs": [
|
||||
"C-130",
|
||||
"E-2C Hawkeye",
|
||||
"E-3A"
|
||||
],
|
||||
"tankers": [
|
||||
"KC-130",
|
||||
"KC-135 Stratotanker"
|
||||
],
|
||||
"frontline_units": [
|
||||
"M113",
|
||||
"M48 Chaparral",
|
||||
"M60A3 \"Patton\""
|
||||
],
|
||||
"artillery_units": [
|
||||
"M109A6 Paladin"
|
||||
],
|
||||
"logistics_units": [
|
||||
"Truck M818 6x6"
|
||||
],
|
||||
"infantry_units": [
|
||||
"Infantry M249",
|
||||
"Infantry M4"
|
||||
],
|
||||
"air_defenses": [
|
||||
"ChaparralGenerator",
|
||||
"EarlyColdWarFlakGenerator",
|
||||
"HawkGenerator",
|
||||
"VulcanGenerator"
|
||||
],
|
||||
"ewrs": [
|
||||
"HawkEwrGenerator"
|
||||
],
|
||||
"aircraft_carrier": [
|
||||
"Stennis"
|
||||
],
|
||||
"helicopter_carrier": [
|
||||
"LHA_Tarawa"
|
||||
],
|
||||
"destroyers": [
|
||||
"USS_Arleigh_Burke_IIa"
|
||||
],
|
||||
"cruisers": [
|
||||
"TICONDEROG"
|
||||
],
|
||||
"requirements": {
|
||||
"MB-339A/PAN by Frecce Tricolori Virtuali": "http://www.freccetricolorivirtuali.net/",
|
||||
"Community A-4E": "https://heclak.github.io/community-a4e-c/"
|
||||
},
|
||||
"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": [
|
||||
"ArleighBurkeGroupGenerator"
|
||||
],
|
||||
"has_jtac": true,
|
||||
"jtac_unit": "MQ-9 Reaper",
|
||||
"doctrine": "coldwar"
|
||||
}
|
||||
@ -12,15 +12,20 @@
|
||||
"AV-8B Harrier II Night Attack",
|
||||
"B-1B Lancer",
|
||||
"B-52H Stratofortress",
|
||||
"C-130",
|
||||
"C-130J-30 Super Hercules",
|
||||
"F-14B Tomcat",
|
||||
"F-15C Eagle",
|
||||
"F-15E Strike Eagle",
|
||||
"F-16CM Fighting Falcon (Block 50)",
|
||||
"F-22A Raptor",
|
||||
"F-5E Tiger II",
|
||||
"F/A-18C Hornet (Lot 20)",
|
||||
"JF-17 Thunder",
|
||||
"Ka-50 Hokum",
|
||||
"Mirage 2000C",
|
||||
"Mi-24P Hind-F",
|
||||
"Mi-8MTV2 Hip",
|
||||
"SA 342L Gazelle",
|
||||
"SA 342M Gazelle",
|
||||
"Su-25T Frogfoot",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"country": "Canada",
|
||||
"name": "Canada 2005",
|
||||
"authors": "Khopa",
|
||||
"authors": "Khopa, SpaceEnthusiast",
|
||||
"description": "<p>Canada in the 2000s, an F/A-18C Hornet focused faction.</p>",
|
||||
"locales": [
|
||||
"en_US",
|
||||
@ -9,6 +9,7 @@
|
||||
],
|
||||
"aircrafts": [
|
||||
"AH-1W SuperCobra",
|
||||
"C-130J-30 Super Hercules",
|
||||
"CF-188 Hornet",
|
||||
"UH-1H Iroquois"
|
||||
],
|
||||
@ -52,7 +53,9 @@
|
||||
"cruisers": [
|
||||
"TICONDEROG"
|
||||
],
|
||||
"requirements": {},
|
||||
"requirements": {
|
||||
"C-130J-30 Super Hercules Mod by Anubis": "https://forums.eagle.ru/topic/252075-dcs-super-hercules-mod-by-anubis/"
|
||||
},
|
||||
"carrier_names": [],
|
||||
"helicopter_carrier_names": [],
|
||||
"navy_generators": [
|
||||
@ -64,6 +67,9 @@
|
||||
"CF-188 Hornet": [
|
||||
"Canada 409th Squadron",
|
||||
"Canada 425th Squadron"
|
||||
],
|
||||
"C-130J-30 Super Hercules": [
|
||||
"Royal Canadian AF CC-130J"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,75 +0,0 @@
|
||||
{
|
||||
"country": "Canada",
|
||||
"name": "Canada 2005 (With C-130)",
|
||||
"authors": "Khopa, SpaceEnthusiast",
|
||||
"description": "<p>Canada in the 2000s, an F/A-18C Hornet focused faction.</p>",
|
||||
"locales": [
|
||||
"en_US",
|
||||
"fr_CA"
|
||||
],
|
||||
"aircrafts": [
|
||||
"AH-1W SuperCobra",
|
||||
"C-130J-30 Super Hercules",
|
||||
"CF-188 Hornet",
|
||||
"UH-1H Iroquois"
|
||||
],
|
||||
"awacs": [
|
||||
"E-3A"
|
||||
],
|
||||
"tankers": [
|
||||
"KC-130",
|
||||
"KC-135 Stratotanker"
|
||||
],
|
||||
"frontline_units": [
|
||||
"FV510 Warrior",
|
||||
"LAV-25",
|
||||
"Leopard 1A3",
|
||||
"Leopard 2",
|
||||
"Leopard 2A4",
|
||||
"M1097 Heavy HMMWV Avenger",
|
||||
"M113"
|
||||
],
|
||||
"artillery_units": [],
|
||||
"logistics_units": [
|
||||
"Truck M818 6x6"
|
||||
],
|
||||
"infantry_units": [
|
||||
"Infantry M249",
|
||||
"Infantry M4",
|
||||
"MANPADS Stinger"
|
||||
],
|
||||
"air_defenses": [
|
||||
"AvengerGenerator",
|
||||
"HawkGenerator"
|
||||
],
|
||||
"ewrs": [
|
||||
"HawkEwrGenerator"
|
||||
],
|
||||
"aircraft_carrier": [],
|
||||
"helicopter_carrier": [],
|
||||
"destroyers": [
|
||||
"USS_Arleigh_Burke_IIa"
|
||||
],
|
||||
"cruisers": [
|
||||
"TICONDEROG"
|
||||
],
|
||||
"requirements": {
|
||||
"C-130J-30 Super Hercules Mod by Anubis": "https://forums.eagle.ru/topic/252075-dcs-super-hercules-mod-by-anubis/"
|
||||
},
|
||||
"carrier_names": [],
|
||||
"helicopter_carrier_names": [],
|
||||
"navy_generators": [
|
||||
"ArleighBurkeGroupGenerator"
|
||||
],
|
||||
"has_jtac": true,
|
||||
"jtac_unit": "MQ-9 Reaper",
|
||||
"liveries_overrides": {
|
||||
"CF-188 Hornet": [
|
||||
"Canada 409th Squadron",
|
||||
"Canada 425th Squadron"
|
||||
],
|
||||
"C-130J-30 Super Hercules": [
|
||||
"Royal Canadian AF CC-130J"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -8,10 +8,11 @@
|
||||
],
|
||||
"aircrafts": [
|
||||
"FC-1 Fierce Dragon",
|
||||
"IL-76MD",
|
||||
"J-11A Flanker-L",
|
||||
"J-15 Flanker X-2",
|
||||
"J-7B",
|
||||
"Mi-28N Havoc",
|
||||
"Mi-24P Hind-F",
|
||||
"Mi-8MTV2 Hip",
|
||||
"Su-30MKK Flanker-G"
|
||||
],
|
||||
@ -51,6 +52,7 @@
|
||||
"SA10Generator",
|
||||
"SA11Generator",
|
||||
"SA13Generator",
|
||||
"SA20BGenerator",
|
||||
"Tier2SA10Generator",
|
||||
"ZSU23Generator",
|
||||
"ZSU57Generator",
|
||||
|
||||
81
resources/factions/france_1985.json
Normal file
81
resources/factions/france_1985.json
Normal file
@ -0,0 +1,81 @@
|
||||
{
|
||||
"country": "France",
|
||||
"name": "France 1985",
|
||||
"authors": "Colonel Panic",
|
||||
"description": "<p>1980s French equipment using FrenchPack.</p>",
|
||||
"locales": [
|
||||
"fr_FR"
|
||||
],
|
||||
"doctrine": "coldwar",
|
||||
"aircrafts": [
|
||||
"C-130",
|
||||
"Mirage 2000C",
|
||||
"SA 342L Gazelle",
|
||||
"SA 342M Gazelle",
|
||||
"SA 342M Gazelle Mistral"
|
||||
],
|
||||
"awacs": [
|
||||
"E-3A"
|
||||
],
|
||||
"tankers": [
|
||||
"KC-130",
|
||||
"KC-135 Stratotanker"
|
||||
],
|
||||
"frontline_units": [
|
||||
"AMX.30B2",
|
||||
"Leclerc S\u00e9ries 2",
|
||||
"Leclerc S\u00e9ries 2",
|
||||
"Pamela",
|
||||
"Panhard",
|
||||
"Roland 2 (Marder Chassis)",
|
||||
"VAB .50",
|
||||
"VAB Mephisto",
|
||||
"VAB T20/13",
|
||||
"VBL .50",
|
||||
"VBL AANF1"
|
||||
],
|
||||
"artillery_units": [
|
||||
"M109A6 Paladin",
|
||||
"M270 Multiple Launch Rocket System"
|
||||
],
|
||||
"logistics_units": [
|
||||
"Truck M818 6x6"
|
||||
],
|
||||
"infantry_units": [
|
||||
"Infantry M249",
|
||||
"Infantry M4",
|
||||
"MANPADS Stinger"
|
||||
],
|
||||
"air_defenses": [
|
||||
"RolandGenerator",
|
||||
"HawkGenerator"
|
||||
],
|
||||
"aircraft_carrier": [],
|
||||
"helicopter_carrier": [
|
||||
"LHA_Tarawa"
|
||||
],
|
||||
"destroyers": [
|
||||
"USS_Arleigh_Burke_IIa"
|
||||
],
|
||||
"cruisers": [
|
||||
"TICONDEROG"
|
||||
],
|
||||
"requirements": {
|
||||
"frenchpack V3.5": "https://forums.eagle.ru/showthread.php?t=279974"
|
||||
},
|
||||
"carrier_names": [
|
||||
"R91 Charles de Gaulle"
|
||||
],
|
||||
"helicopter_carrier_names": [
|
||||
"R97 Jeanne d'Arc",
|
||||
"L9013 Mistral",
|
||||
"L9014 Tonerre",
|
||||
"L9015 Dixmude"
|
||||
],
|
||||
"navy_generators": [
|
||||
"ArleighBurkeGroupGenerator"
|
||||
],
|
||||
"has_jtac": true,
|
||||
"jtac_unit": "MQ-9 Reaper"
|
||||
}
|
||||
|
||||
@ -1,79 +0,0 @@
|
||||
{
|
||||
"country": "France",
|
||||
"name": "France 1985 (Frenchpack)",
|
||||
"authors": "Colonel Panic",
|
||||
"description": "<p>1980s French equipment using FrenchPack.</p>",
|
||||
"locales": [
|
||||
"fr_FR"
|
||||
],
|
||||
"doctrine": "coldwar",
|
||||
"aircrafts": [
|
||||
"Mirage 2000C",
|
||||
"SA 342L Gazelle",
|
||||
"SA 342M Gazelle",
|
||||
"SA 342M Gazelle Mistral"
|
||||
],
|
||||
"awacs": [
|
||||
"E-3A"
|
||||
],
|
||||
"tankers": [
|
||||
"KC-130",
|
||||
"KC-135 Stratotanker"
|
||||
],
|
||||
"frontline_units": [
|
||||
"AMX.30B2",
|
||||
"Leclerc S\u00e9ries 2",
|
||||
"Leclerc S\u00e9ries 2",
|
||||
"Pamela",
|
||||
"Panhard",
|
||||
"Roland 2 (Marder Chassis)",
|
||||
"VAB .50",
|
||||
"VAB Mephisto",
|
||||
"VAB T20/13",
|
||||
"VBL .50",
|
||||
"VBL AANF1"
|
||||
],
|
||||
"artillery_units": [
|
||||
"M109A6 Paladin",
|
||||
"M270 Multiple Launch Rocket System"
|
||||
],
|
||||
"logistics_units": [
|
||||
"Truck M818 6x6"
|
||||
],
|
||||
"infantry_units": [
|
||||
"Infantry M249",
|
||||
"Infantry M4",
|
||||
"MANPADS Stinger"
|
||||
],
|
||||
"air_defenses": [
|
||||
"RolandGenerator",
|
||||
"HawkGenerator"
|
||||
],
|
||||
"aircraft_carrier": [],
|
||||
"helicopter_carrier": [
|
||||
"LHA_Tarawa"
|
||||
],
|
||||
"destroyers": [
|
||||
"USS_Arleigh_Burke_IIa"
|
||||
],
|
||||
"cruisers": [
|
||||
"TICONDEROG"
|
||||
],
|
||||
"requirements": {
|
||||
"frenchpack V3.5": "https://forums.eagle.ru/showthread.php?t=279974"
|
||||
},
|
||||
"carrier_names": [
|
||||
"R91 Charles de Gaulle"
|
||||
],
|
||||
"helicopter_carrier_names": [
|
||||
"R97 Jeanne d'Arc",
|
||||
"L9013 Mistral",
|
||||
"L9014 Tonerre",
|
||||
"L9015 Dixmude"
|
||||
],
|
||||
"navy_generators": [
|
||||
"ArleighBurkeGroupGenerator"
|
||||
],
|
||||
"has_jtac": true,
|
||||
"jtac_unit": "SA 342L Gazelle"
|
||||
}
|
||||
@ -7,6 +7,7 @@
|
||||
"fr_FR"
|
||||
],
|
||||
"aircrafts": [
|
||||
"C-130",
|
||||
"Mirage 2000-5",
|
||||
"Mirage 2000C",
|
||||
"SA 342L Gazelle",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user