diff --git a/.gitignore b/.gitignore index 5e953c36..b9205033 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ resources/tools/a.miz # User-specific stuff .idea/ .env +env/ /kneeboards /liberation_preferences.json diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index d8db9cf5..00000000 --- a/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "pydcs"] - path = pydcs - url = https://github.com/pydcs/dcs - branch = master diff --git a/changelog.md b/changelog.md index ede6521d..06115f0d 100644 --- a/changelog.md +++ b/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 `/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. diff --git a/game/data/cap_capabilities_db.py b/game/data/cap_capabilities_db.py deleted file mode 100644 index 160faede..00000000 --- a/game/data/cap_capabilities_db.py +++ /dev/null @@ -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, -] diff --git a/game/data/weapons.py b/game/data/weapons.py index 668cc028..22aa53b9 100644 --- a/game/data/weapons.py +++ b/game/data/weapons.py @@ -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) diff --git a/game/db.py b/game/db.py index 2561e6cd..6f95a1d6 100644 --- a/game/db.py +++ b/game/db.py @@ -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 diff --git a/game/dcs/aircrafttype.py b/game/dcs/aircrafttype.py index 7c884220..05b3d557 100644 --- a/game/dcs/aircrafttype.py +++ b/game/dcs/aircrafttype.py @@ -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, diff --git a/game/dcs/groundunittype.py b/game/dcs/groundunittype.py index ff03702d..7cf92fcf 100644 --- a/game/dcs/groundunittype.py +++ b/game/dcs/groundunittype.py @@ -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 diff --git a/game/debriefing.py b/game/debriefing.py index 59c795db..212ea7a4 100644 --- a/game/debriefing.py +++ b/game/debriefing.py @@ -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) diff --git a/game/event/event.py b/game/event/event.py index 1fb0230f..1d554fca 100644 --- a/game/event/event.py +++ b/game/event/event.py @@ -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]]: diff --git a/game/factions/faction.py b/game/factions/faction.py index 0a4b2548..452fb9a7 100644 --- a/game/factions/faction.py +++ b/game/factions/faction.py @@ -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: diff --git a/game/game.py b/game/game.py index 88adc369..e3d08939 100644 --- a/game/game.py +++ b/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) diff --git a/game/operation/operation.py b/game/operation/operation.py index c63c61f2..6b44cb9f 100644 --- a/game/operation/operation.py +++ b/game/operation/operation.py @@ -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, diff --git a/game/persistency.py b/game/persistency.py index 8e15ece3..d9d9d135 100644 --- a/game/persistency.py +++ b/game/persistency.py @@ -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): diff --git a/game/settings.py b/game/settings.py index 622ebde0..0e056d2a 100644 --- a/game/settings.py +++ b/game/settings.py @@ -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 diff --git a/game/squadrons.py b/game/squadrons.py index 0f6097a2..9b13eed3 100644 --- a/game/squadrons.py +++ b/game/squadrons.py @@ -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( diff --git a/game/theater/start_generator.py b/game/theater/start_generator.py index 2f31fecf..4ec827ec 100644 --- a/game/theater/start_generator.py +++ b/game/theater/start_generator.py @@ -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: diff --git a/game/transfers.py b/game/transfers.py index 5543d572..c3d337da 100644 --- a/game/transfers.py +++ b/game/transfers.py @@ -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 diff --git a/game/unitdelivery.py b/game/unitdelivery.py index 4de3addf..a6de6a71 100644 --- a/game/unitdelivery.py +++ b/game/unitdelivery.py @@ -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)) diff --git a/game/version.py b/game/version.py index cd2f1ffa..bcb8b8cf 100644 --- a/game/version.py +++ b/game/version.py @@ -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) diff --git a/gen/aircraft.py b/gen/aircraft.py index 62246a88..a5eaeb4b 100644 --- a/gen/aircraft.py +++ b/gen/aircraft.py @@ -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 = { diff --git a/gen/conflictgen.py b/gen/conflictgen.py index d4b145d4..eabf4e4e 100644 --- a/gen/conflictgen.py +++ b/gen/conflictgen.py @@ -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 diff --git a/gen/flights/ai_flight_planner.py b/gen/flights/ai_flight_planner.py index 6f9b47aa..7bbee129 100644 --- a/gen/flights/ai_flight_planner.py +++ b/gen/flights/ai_flight_planner.py @@ -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() diff --git a/gen/flights/ai_flight_planner_db.py b/gen/flights/ai_flight_planner_db.py index 50bff5a0..6ed26bd4 100644 --- a/gen/flights/ai_flight_planner_db.py +++ b/gen/flights/ai_flight_planner_db.py @@ -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, diff --git a/gen/flights/flightplan.py b/gen/flights/flightplan.py index f64e6ac5..80a7f6e1 100644 --- a/gen/flights/flightplan.py +++ b/gen/flights/flightplan.py @@ -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( diff --git a/pydcs b/pydcs deleted file mode 160000 index a459d8e1..00000000 --- a/pydcs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a459d8e1b0bd59bbd7f35390d7ad1bedc0caf76b diff --git a/pydcs_extensions/mb339/mb339.py b/pydcs_extensions/mb339/mb339.py deleted file mode 100644 index 77fe9691..00000000 --- a/pydcs_extensions/mb339/mb339.py +++ /dev/null @@ -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 diff --git a/pydcs_extensions/mod_units.py b/pydcs_extensions/mod_units.py index 31c26829..d923c4c5 100644 --- a/pydcs_extensions/mod_units.py +++ b/pydcs_extensions/mod_units.py @@ -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, diff --git a/qt_ui/main.py b/qt_ui/main.py index 46b7fc1c..744f90de 100644 --- a/qt_ui/main.py +++ b/qt_ui/main.py @@ -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() diff --git a/qt_ui/models.py b/qt_ui/models.py index dd214e84..4e5f46b3 100644 --- a/qt_ui/models.py +++ b/qt_ui/models.py @@ -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]: diff --git a/qt_ui/widgets/QFactionsInfos.py b/qt_ui/widgets/QFactionsInfos.py index 40369356..935f4af9 100644 --- a/qt_ui/widgets/QFactionsInfos.py +++ b/qt_ui/widgets/QFactionsInfos.py @@ -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("") diff --git a/qt_ui/widgets/QTopPanel.py b/qt_ui/widgets/QTopPanel.py index 7292cec4..3fa91736 100644 --- a/qt_ui/widgets/QTopPanel.py +++ b/qt_ui/widgets/QTopPanel.py @@ -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) diff --git a/qt_ui/windows/QLiberationWindow.py b/qt_ui/windows/QLiberationWindow.py index c2a96a4d..d431266d 100644 --- a/qt_ui/windows/QLiberationWindow.py +++ b/qt_ui/windows/QLiberationWindow.py @@ -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) diff --git a/qt_ui/windows/basemenu/DepartingConvoysMenu.py b/qt_ui/windows/basemenu/DepartingConvoysMenu.py index 1dd4b46c..d858539e 100644 --- a/qt_ui/windows/basemenu/DepartingConvoysMenu.py +++ b/qt_ui/windows/basemenu/DepartingConvoysMenu.py @@ -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("" + unit_type.id[:8] + "") + icon.setText("" + unit_type.name + "") icon.setProperty("style", "icon-armor") unit_layout.addWidget(icon, idx, 0) unit_layout.addWidget( diff --git a/qt_ui/windows/basemenu/NewUnitTransferDialog.py b/qt_ui/windows/basemenu/NewUnitTransferDialog.py index 9689c9fd..21a70159 100644 --- a/qt_ui/windows/basemenu/NewUnitTransferDialog.py +++ b/qt_ui/windows/basemenu/NewUnitTransferDialog.py @@ -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 diff --git a/qt_ui/windows/basemenu/QBaseMenu2.py b/qt_ui/windows/basemenu/QBaseMenu2.py index 4b8265c8..8a913280 100644 --- a/qt_ui/windows/basemenu/QBaseMenu2.py +++ b/qt_ui/windows/basemenu/QBaseMenu2.py @@ -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() diff --git a/qt_ui/windows/groundobject/QGroundObjectMenu.py b/qt_ui/windows/groundobject/QGroundObjectMenu.py index 7bb20c0f..b04750ee 100644 --- a/qt_ui/windows/groundobject/QGroundObjectMenu.py +++ b/qt_ui/windows/groundobject/QGroundObjectMenu.py @@ -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() diff --git a/qt_ui/windows/intel.py b/qt_ui/windows/intel.py index 619938cf..64b539d0 100644 --- a/qt_ui/windows/intel.py +++ b/qt_ui/windows/intel.py @@ -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("Total", 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("Total", total) + self.add_spacer() class ArmyIntelTab(ScrollingFrame): diff --git a/qt_ui/windows/mission/flight/QFlightCreator.py b/qt_ui/windows/mission/flight/QFlightCreator.py index 0e8293a1..7f5c6cc4 100644 --- a/qt_ui/windows/mission/flight/QFlightCreator.py +++ b/qt_ui/windows/mission/flight/QFlightCreator.py @@ -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) diff --git a/qt_ui/windows/newgame/QNewGameWizard.py b/qt_ui/windows/newgame/QNewGameWizard.py index f2e2e0c4..264f73cf 100644 --- a/qt_ui/windows/newgame/QNewGameWizard.py +++ b/qt_ui/windows/newgame/QNewGameWizard.py @@ -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("") - self.requiredMods.setOpenExternalLinks(True) - self.requiredModsGroupLayout.addWidget(self.requiredMods) - self.requiredModsGroup.setLayout(self.requiredModsGroupLayout) - # Docs Link docsText = QtWidgets.QLabel( 'How to create your own faction' @@ -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("\n\n") - else: - self.requiredMods.setText(self.requiredMods.text() + "
  • None
  • \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( + "

    Select the mods you have installed. If your chosen factions support them, you'll be able to use these mods in your campaign.

    " + ) + 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) diff --git a/qt_ui/windows/settings/QSettingsWindow.py b/qt_ui/windows/settings/QSettingsWindow.py index dc1392c4..b60920ba 100644 --- a/qt_ui/windows/settings/QSettingsWindow.py +++ b/qt_ui/windows/settings/QSettingsWindow.py @@ -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): diff --git a/requirements.txt b/requirements.txt index f7360083..75bf846a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 diff --git a/resources/campaigns/Battle_for_the_UAE.json b/resources/campaigns/Battle_for_the_UAE.json index 87d1aa15..0c5086e2 100644 --- a/resources/campaigns/Battle_for_the_UAE.json +++ b/resources/campaigns/Battle_for_the_UAE.json @@ -5,7 +5,7 @@ "recommended_player_faction": "Bluefor Modern", "recommended_enemy_faction": "Iran 2015", "description": "

    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.

    After weeks of stalemate, coalition forces have consolidated their position and are ready to launch their counterattack to push Iranian forces off the peninsula.

    ", - "version": "6.0", + "version": "7.0", "miz": "Battle_for_the_UAE_v3.0.2.miz", "performance": 2 } diff --git a/resources/campaigns/Caucasus_Multi_Full.json b/resources/campaigns/Caucasus_Multi_Full.json index 0ef642a3..cf41d395 100644 --- a/resources/campaigns/Caucasus_Multi_Full.json +++ b/resources/campaigns/Caucasus_Multi_Full.json @@ -5,7 +5,7 @@ "recommended_player_faction": "Bluefor Modern", "recommended_enemy_faction": "Russia 2010", "description": "

    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.

    ", - "version": "6.0", + "version": "7.0", "miz": "Caucasus_Multi_Full.miz", "performance": 3 } \ No newline at end of file diff --git a/resources/campaigns/Caucasus_Multi_Georgia.json b/resources/campaigns/Caucasus_Multi_Georgia.json index c10a6e9c..63c1dba0 100644 --- a/resources/campaigns/Caucasus_Multi_Georgia.json +++ b/resources/campaigns/Caucasus_Multi_Georgia.json @@ -5,7 +5,7 @@ "recommended_player_faction": "Bluefor Modern", "recommended_enemy_faction": "Georgia 2008", "description": "

    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.

    ", - "version": "6.0", + "version": "7.0", "miz": "Caucasus_Multi_Georgia.miz", "performance": 1 } \ No newline at end of file diff --git a/resources/campaigns/Caucasus_Multi_Russia.json b/resources/campaigns/Caucasus_Multi_Russia.json index 09904d35..e9df1997 100644 --- a/resources/campaigns/Caucasus_Multi_Russia.json +++ b/resources/campaigns/Caucasus_Multi_Russia.json @@ -5,7 +5,7 @@ "recommended_player_faction": "Bluefor Modern", "recommended_enemy_faction": "Russia 2010", "description": "

    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.

    ", - "version": "6.0", + "version": "7.0", "miz": "Caucasus_Multi_Russia.miz", "performance": 2 } \ No newline at end of file diff --git a/resources/campaigns/First_Lebanon_War.json b/resources/campaigns/First_Lebanon_War.json index 07fde1a9..ecb56691 100644 --- a/resources/campaigns/First_Lebanon_War.json +++ b/resources/campaigns/First_Lebanon_War.json @@ -6,6 +6,6 @@ "recommended_enemy_faction": "Syria 1982", "description": "

    1100HRS, 06 June 1982: H-hour for Operation Peace for Galilee.

    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.

    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.

    Recommended Starting Budget:

    $1500m for recommended factions, $$2000m for modern scenarios

    Income Multiplier:

    Blue: 1.0x

    Red: 0.7x-1.0x

    ", "miz": "First_Lebanon_War_v3.0.2.miz", - "version": "6.0", + "version": "7.0", "performance": 2 } diff --git a/resources/campaigns/Operation_Atilla.json b/resources/campaigns/Operation_Atilla.json new file mode 100644 index 00000000..ea75acdf --- /dev/null +++ b/resources/campaigns/Operation_Atilla.json @@ -0,0 +1,11 @@ +{ + "name": "Syria - Operation Atilla", + "theater": "Syria", + "authors": "Malakhit", + "recommended_player_faction": "Turkey 2005", + "recommended_enemy_faction": "Greece 2005", + "description": "

    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.

    ", + "version": "6.0", + "miz": "Operation_Atilla.miz", + "performance": 2 +} \ No newline at end of file diff --git a/resources/campaigns/Operation_Atilla.miz b/resources/campaigns/Operation_Atilla.miz new file mode 100644 index 00000000..e68aa418 Binary files /dev/null and b/resources/campaigns/Operation_Atilla.miz differ diff --git a/resources/campaigns/Operation_Mole_Cricket_2010.json b/resources/campaigns/Operation_Mole_Cricket_2010.json index cf50a1b0..fad2810b 100644 --- a/resources/campaigns/Operation_Mole_Cricket_2010.json +++ b/resources/campaigns/Operation_Mole_Cricket_2010.json @@ -5,7 +5,7 @@ "recommended_player_faction": "Bluefor Modern", "recommended_enemy_faction": "Syria 2011", "description": "

    In a scenario reminescent of the First Lebanon War, hostile Syrian-backed forces have flooded into the Bekaa Valley.

    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.

    ", - "version": "6.0", + "version": "7.0", "miz": "Operation_Mole_Cricket_2010_v3.0.2.miz", "performance": 2 } diff --git a/resources/campaigns/Road_to_Dubai.json b/resources/campaigns/Road_to_Dubai.json index d0d7e9e8..4d90403d 100644 --- a/resources/campaigns/Road_to_Dubai.json +++ b/resources/campaigns/Road_to_Dubai.json @@ -3,7 +3,7 @@ "theater": "Persian Gulf", "authors": "Doc_of_Mur", "description": "

    Small beginner friendly map

    Note: This scenario is based around Iran invading the UAE and you are trying to take it back. It is small and beginner friendly.

    ", - "version": "6.0", + "version": "7.0", "recommended_player_faction": "USA 2005", "recommended_enemy_faction": "Iran 2015", "miz": "Road_to_Dubai.miz", diff --git a/resources/campaigns/Russian_Intervention_2015.json b/resources/campaigns/Russian_Intervention_2015.json new file mode 100644 index 00000000..17b53a19 --- /dev/null +++ b/resources/campaigns/Russian_Intervention_2015.json @@ -0,0 +1,11 @@ +{ + "name": "Syria - Russian Intervention 2015", + "theater": "Syria", + "authors": "Malakhit", + "recommended_player_faction": "Russia 2010", + "recommended_enemy_faction": "Insurgents (Hard)", + "description": "

    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.

    ", + "version": "6.0", + "miz": "Russian_Intervention_2015.miz", + "performance": 1 +} \ No newline at end of file diff --git a/resources/campaigns/Russian_Intervention_2015.miz b/resources/campaigns/Russian_Intervention_2015.miz new file mode 100644 index 00000000..e1797983 Binary files /dev/null and b/resources/campaigns/Russian_Intervention_2015.miz differ diff --git a/resources/campaigns/around_the_mountain.json b/resources/campaigns/around_the_mountain.json new file mode 100644 index 00000000..422cee26 --- /dev/null +++ b/resources/campaigns/around_the_mountain.json @@ -0,0 +1,11 @@ +{ + "name": "Caucasus - Around The Mountain", + "theater": "Caucasus", + "authors": "Dillie", + "recommended_player_faction": "Russia 2010", + "recommended_enemy_faction": "USA 1990", + "description": "

    Scenario from Russia to Georgia in two Frontlines.

    ", + "version": "7.0", + "miz": "around_the_mountain.miz", + "performance": 2 +} \ No newline at end of file diff --git a/resources/campaigns/around_the_mountain.miz b/resources/campaigns/around_the_mountain.miz new file mode 100644 index 00000000..f1adec86 Binary files /dev/null and b/resources/campaigns/around_the_mountain.miz differ diff --git a/resources/campaigns/battle_of_abu_dhabi.json b/resources/campaigns/battle_of_abu_dhabi.json index a551607b..9cfa5476 100644 --- a/resources/campaigns/battle_of_abu_dhabi.json +++ b/resources/campaigns/battle_of_abu_dhabi.json @@ -7,5 +7,5 @@ "description": "

    You have managed to establish a foothold at Khasab. Continue pushing south.

    ", "miz": "battle_of_abu_dhabi.miz", "performance": 2, - "version": "6.0" + "version": "7.0" } \ No newline at end of file diff --git a/resources/campaigns/black_sea.json b/resources/campaigns/black_sea.json index 29217f5c..02f4ddbe 100644 --- a/resources/campaigns/black_sea.json +++ b/resources/campaigns/black_sea.json @@ -5,5 +5,5 @@ "description": "

    A medium sized theater with bases along the coast of the Black Sea.

    ", "miz": "black_sea.miz", "performance": 2, - "version": "6.0" + "version": "7.0" } \ No newline at end of file diff --git a/resources/campaigns/caen_to_evreux.json b/resources/campaigns/caen_to_evreux.json index 5776979e..d630767e 100644 --- a/resources/campaigns/caen_to_evreux.json +++ b/resources/campaigns/caen_to_evreux.json @@ -7,5 +7,5 @@ "description": "

    This is a light scenario on the Normandy map.

    August 1944, allied forces are pushing from Caen/Carpiquet to the cities of Lisieux and Evreux.

    Lisieux is an important logistic hub for the Werhmacht, and Evreux airbase is hosting most of the Luftwaffe forces in the region.

    ", "miz": "caen_to_evreux.miz", "performance": 1, - "version": "6.0" + "version": "7.0" } diff --git a/resources/campaigns/caen_to_evreux.miz b/resources/campaigns/caen_to_evreux.miz index a0f321ab..bd119430 100644 Binary files a/resources/campaigns/caen_to_evreux.miz and b/resources/campaigns/caen_to_evreux.miz differ diff --git a/resources/campaigns/exercise_vegas_nerve.json b/resources/campaigns/exercise_vegas_nerve.json index 0179641f..2925c5a9 100644 --- a/resources/campaigns/exercise_vegas_nerve.json +++ b/resources/campaigns/exercise_vegas_nerve.json @@ -5,7 +5,7 @@ "recommended_player_faction": "Bluefor Modern", "recommended_enemy_faction": "Redfor (China) 2010", "description": "

    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.

    ", - "version": "6.0", + "version": "7.0", "miz": "exercise_vegas_nerve.miz", "performance": 0 } \ No newline at end of file diff --git a/resources/campaigns/golan_heights_lite.json b/resources/campaigns/golan_heights_lite.json index 45d3c728..051205da 100644 --- a/resources/campaigns/golan_heights_lite.json +++ b/resources/campaigns/golan_heights_lite.json @@ -7,5 +7,5 @@ "description": "

    In this scenario, you start in Israel and the conflict is focused around the golan heights, an historically disputed territory.

    This scenario is designed to be performance friendly.

    ", "miz": "golan_heights_lite.miz", "performance": 1, - "version": "6.0" + "version": "7.0" } diff --git a/resources/campaigns/golan_heights_lite.miz b/resources/campaigns/golan_heights_lite.miz index 29da46ee..7b9e408a 100644 Binary files a/resources/campaigns/golan_heights_lite.miz and b/resources/campaigns/golan_heights_lite.miz differ diff --git a/resources/campaigns/mozdok_to_maykop.json b/resources/campaigns/mozdok_to_maykop.json new file mode 100644 index 00000000..5cb9c642 --- /dev/null +++ b/resources/campaigns/mozdok_to_maykop.json @@ -0,0 +1,11 @@ +{ + "name": "Caucasus - Mozdok to Maykop", + "theater": "Caucasus", + "authors": "Khopa", + "recommended_player_faction": "Russia 2010", + "recommended_enemy_faction": "USA 1990", + "description": "

    A small theater in Russia, progress from Mozdok to Maykop.

    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.

    ", + "miz": "mozdok_to_maykop.miz", + "performance": 0, + "version": "7.0" +} diff --git a/resources/campaigns/mozdok_to_maykop.miz b/resources/campaigns/mozdok_to_maykop.miz new file mode 100644 index 00000000..2e5663b2 Binary files /dev/null and b/resources/campaigns/mozdok_to_maykop.miz differ diff --git a/resources/campaigns/nevada_Limited_Air.json b/resources/campaigns/nevada_Limited_Air.json new file mode 100644 index 00000000..d042461d --- /dev/null +++ b/resources/campaigns/nevada_Limited_Air.json @@ -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": "

    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.

    This should prevent the airpower rush escperienced in most of the other larger campaign.

    ", + "version": "7.0", + "miz": "nevada_limited_air.miz", + "performance": 1 +} \ No newline at end of file diff --git a/resources/campaigns/nevada_Limited_Air.miz b/resources/campaigns/nevada_Limited_Air.miz new file mode 100644 index 00000000..7939d53b Binary files /dev/null and b/resources/campaigns/nevada_Limited_Air.miz differ diff --git a/resources/campaigns/northern_russia.json b/resources/campaigns/northern_russia.json index 96f8a9c4..f76276c1 100644 --- a/resources/campaigns/northern_russia.json +++ b/resources/campaigns/northern_russia.json @@ -2,10 +2,10 @@ "name": "Caucasus - Northern Russia", "theater": "Caucasus", "authors": "Plob", - "recommended_player_faction": "USA 2005", - "recommended_enemy_faction": "Russia 1990", - "description": "

    A medium campaign through the north eastern part of the Caucasus map.

    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": "

    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.

    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" } \ No newline at end of file diff --git a/resources/campaigns/northern_russia.miz b/resources/campaigns/northern_russia.miz index aa21fbe4..158a7053 100644 Binary files a/resources/campaigns/northern_russia.miz and b/resources/campaigns/northern_russia.miz differ diff --git a/resources/campaigns/operation_allied_sword.json b/resources/campaigns/operation_allied_sword.json index 2da3badc..3f26c95a 100644 --- a/resources/campaigns/operation_allied_sword.json +++ b/resources/campaigns/operation_allied_sword.json @@ -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": "

    In this fictional scenario, a US/Israeli coalition must push north from the Israeli border, through Syria and Lebanon to Aleppo.

    Backstory: 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.

    ", - "version": "6.0", + "version": "7.0", "miz": "operation_allied_sword.miz", "performance": 2 } \ No newline at end of file diff --git a/resources/campaigns/operation_allied_sword.miz b/resources/campaigns/operation_allied_sword.miz index 1fbb0bb3..89b46fda 100644 Binary files a/resources/campaigns/operation_allied_sword.miz and b/resources/campaigns/operation_allied_sword.miz differ diff --git a/resources/campaigns/operation_blackball.json b/resources/campaigns/operation_blackball.json new file mode 100644 index 00000000..14ee2863 --- /dev/null +++ b/resources/campaigns/operation_blackball.json @@ -0,0 +1,11 @@ +{ + "name": "Syria - Operation Blackball", + "theater": "Syria", + "authors": "Fuzzle", + "recommended_player_faction": "US Navy 2005", + "recommended_enemy_faction": "Russia 2010", + "description": "

    Warning: This campaign will not work if the attacking faction does not have a carrier.

    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.

    Backstory: 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.

    ", + "version": "7.0", + "miz": "operation_blackball.miz", + "performance": 1 +} \ No newline at end of file diff --git a/resources/campaigns/operation_blackball.miz b/resources/campaigns/operation_blackball.miz new file mode 100644 index 00000000..9c16abd2 Binary files /dev/null and b/resources/campaigns/operation_blackball.miz differ diff --git a/resources/campaigns/operation_dynamo.json b/resources/campaigns/operation_dynamo.json new file mode 100644 index 00000000..63c147a3 --- /dev/null +++ b/resources/campaigns/operation_dynamo.json @@ -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": "

    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..

    ", + "version": 7.0, + "miz": "operation_dynamo.miz", + "performance": 1 +} diff --git a/resources/campaigns/operation_dynamo.miz b/resources/campaigns/operation_dynamo.miz new file mode 100644 index 00000000..1aadc86f Binary files /dev/null and b/resources/campaigns/operation_dynamo.miz differ diff --git a/resources/campaigns/operation_peace_spring.json b/resources/campaigns/operation_peace_spring.json index eb028ac5..2e6eb3b5 100644 --- a/resources/campaigns/operation_peace_spring.json +++ b/resources/campaigns/operation_peace_spring.json @@ -5,7 +5,7 @@ "recommended_player_faction": "Bluefor Modern", "recommended_enemy_faction": "Turkey 2005", "description": "

    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.

    ", - "version": "6.0", + "version": "7.0", "miz": "operation_peace_spring.miz", "performance": 1 } \ No newline at end of file diff --git a/resources/campaigns/operation_peace_spring.miz b/resources/campaigns/operation_peace_spring.miz index 8fdd5687..7d1b5c7c 100644 Binary files a/resources/campaigns/operation_peace_spring.miz and b/resources/campaigns/operation_peace_spring.miz differ diff --git a/resources/campaigns/operation_vectrons_claw.json b/resources/campaigns/operation_vectrons_claw.json index 1b4e7ae9..ac0e176f 100644 --- a/resources/campaigns/operation_vectrons_claw.json +++ b/resources/campaigns/operation_vectrons_claw.json @@ -5,7 +5,7 @@ "recommended_player_faction": "USA 2005", "recommended_enemy_faction": "Russia 1990", "description": "

    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.

    Note: 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.

    ", - "version": "6.0", + "version": "7.0", "miz": "operation_vectrons_claw.miz", "performance": 1 } \ No newline at end of file diff --git a/resources/campaigns/operation_vectrons_claw.miz b/resources/campaigns/operation_vectrons_claw.miz index e500b794..5862883f 100644 Binary files a/resources/campaigns/operation_vectrons_claw.miz and b/resources/campaigns/operation_vectrons_claw.miz differ diff --git a/resources/campaigns/russia_small.json b/resources/campaigns/russia_small.json deleted file mode 100644 index 49a4faab..00000000 --- a/resources/campaigns/russia_small.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "Caucasus - Russia Small", - "theater": "Caucasus", - "authors": "Khopa", - "recommended_player_faction": "Russia 2010", - "recommended_enemy_faction": "USA 1990", - "description": "

    A small theater in Russia, progress from Mozdok to Maykop.

    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.

    ", - "miz": "russia_small.miz", - "performance": 0, - "version": "6.0" -} diff --git a/resources/campaigns/russia_small.miz b/resources/campaigns/russia_small.miz deleted file mode 100644 index 1441ec8a..00000000 Binary files a/resources/campaigns/russia_small.miz and /dev/null differ diff --git a/resources/campaigns/scenic_route.json b/resources/campaigns/scenic_route.json new file mode 100644 index 00000000..d79ae493 --- /dev/null +++ b/resources/campaigns/scenic_route.json @@ -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": "

    A lightweight naval campaign involving a US Navy carrier group pushing across the coast of Iran. 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.

    Backstory: 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.

    ", + "version": "7.0", + "miz": "scenic_route.miz", + "performance": 1 +} \ No newline at end of file diff --git a/resources/campaigns/scenic_route.miz b/resources/campaigns/scenic_route.miz new file mode 100644 index 00000000..4ace24cb Binary files /dev/null and b/resources/campaigns/scenic_route.miz differ diff --git a/resources/campaigns/syria_full_map.json b/resources/campaigns/syria_full_map.json index 99adafde..f0393d24 100644 --- a/resources/campaigns/syria_full_map.json +++ b/resources/campaigns/syria_full_map.json @@ -4,8 +4,8 @@ "authors": "Plob", "recommended_player_faction": "Bluefor Modern", "recommended_enemy_faction": "Syria 2011", - "description": "

    Syria Full map, designed for groups of 4-12 players.

    ", + "description": "

    A long campaign across the syria map, strap in.

    Each turn after planning missions, enable culling to ensure correct culling behaviour.

    ", "miz": "syria_full_map.miz", "performance": 3, - "version": "6.0" + "version": "7.0" } \ No newline at end of file diff --git a/resources/campaigns/syria_full_map.miz b/resources/campaigns/syria_full_map.miz index 01345dd1..f36953ab 100644 Binary files a/resources/campaigns/syria_full_map.miz and b/resources/campaigns/syria_full_map.miz differ diff --git a/resources/customized_payloads/MB-339PAN.lua b/resources/customized_payloads/MB-339PAN.lua deleted file mode 100644 index c3d04b5f..00000000 --- a/resources/customized_payloads/MB-339PAN.lua +++ /dev/null @@ -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 diff --git a/resources/factions/usa_2005_modded.json b/resources/factions/Israel-USN_2005_Allied_Sword.json similarity index 55% rename from resources/factions/usa_2005_modded.json rename to resources/factions/Israel-USN_2005_Allied_Sword.json index 1ba82e75..55d9a662 100644 --- a/resources/factions/usa_2005_modded.json +++ b/resources/factions/Israel-USN_2005_Allied_Sword.json @@ -1,49 +1,39 @@ { - "country": "USA", - "name": "USA 2005 Modded", - "authors": "Khopa", - "description": "

    USA 2005 with the Raptor mod, with the F-22 mod by Grinelli Designs.

    ", + "country": "Israel", + "name": "Israel-USN 2005 (Allied Sword)", + "authors": "Fuzzle", + "description": "

    A joint US Navy/Israeli modern faction for use with the Operation Allied Sword scenario.

    ", "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" ] } -} \ No newline at end of file +} diff --git a/resources/factions/NATO_Desert_Storm.json b/resources/factions/NATO_Desert_Storm.json index 60edc2c5..604786f0 100644 --- a/resources/factions/NATO_Desert_Storm.json +++ b/resources/factions/NATO_Desert_Storm.json @@ -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", diff --git a/resources/factions/Syria-Lebanon_2005_Allied_Sword.json b/resources/factions/Syria-Lebanon_2005_Allied_Sword.json new file mode 100644 index 00000000..71cb66e5 --- /dev/null +++ b/resources/factions/Syria-Lebanon_2005_Allied_Sword.json @@ -0,0 +1,102 @@ +{ + "country": "Combined Joint Task Forces Red", + "name": "Syria-Lebanon 2005 (Allied Sword)", + "authors": "Fuzzle", + "description": "

    Syria-Lebanon alliance in a modern setting with several imported Russian assets. Designed for use with the Allied Sword scenario.

    ", + "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" + ] +} diff --git a/resources/factions/australia_2005.json b/resources/factions/australia_2005.json index fbb7a648..572e710d 100644 --- a/resources/factions/australia_2005.json +++ b/resources/factions/australia_2005.json @@ -1,10 +1,11 @@ { "country": "Australia", "name": "Australia 2005", - "authors": "Khopa", + "authors": "Khopa, SpaceEnthusiast", "description": "

    The Australian army in 2005.

    Some units might not be accurate, but were picked to represent at best this army.

    ", "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", diff --git a/resources/factions/australia_2005_c130.json b/resources/factions/australia_2005_c130.json deleted file mode 100644 index 868f6696..00000000 --- a/resources/factions/australia_2005_c130.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "country": "Australia", - "name": "Australia 2005 (With C-130)", - "authors": "Khopa, SpaceEnthusiast", - "description": "

    The Australian army in 2005.

    Some units might not be accurate, but were picked to represent at best this army.

    ", - "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" - ] - } -} \ No newline at end of file diff --git a/resources/factions/bluefor_coldwar.json b/resources/factions/bluefor_coldwar.json index 7d9392dc..50e97b5d 100644 --- a/resources/factions/bluefor_coldwar.json +++ b/resources/factions/bluefor_coldwar.json @@ -2,11 +2,14 @@ "country": "Combined Joint Task Forces Blue", "name": "Bluefor Coldwar", "authors": "Khopa", - "description": "

    A generic bluefor coldwar faction.

    ", + "description": "

    A generic bluefor coldwar faction. (With the A-4E-C mod)

    ", "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", diff --git a/resources/factions/bluefor_coldwar_a4.json b/resources/factions/bluefor_coldwar_a4.json deleted file mode 100644 index 07612aae..00000000 --- a/resources/factions/bluefor_coldwar_a4.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "country": "Combined Joint Task Forces Blue", - "name": "Bluefor Coldwar (With A4)", - "authors": "Khopa", - "description": "

    A generic bluefor coldwar faction. (With the A-4E-C mod)

    ", - "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" -} \ No newline at end of file diff --git a/resources/factions/bluefor_coldwar_a4_mb339.json b/resources/factions/bluefor_coldwar_a4_mb339.json deleted file mode 100644 index 23893280..00000000 --- a/resources/factions/bluefor_coldwar_a4_mb339.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "country": "Combined Joint Task Forces Blue", - "name": "Bluefor Coldwar (With A4 & MB339)", - "authors": "Khopa", - "description": "

    A generic bluefor coldwar faction. (With the A-4E-C and the MB-339 mods)

    ", - "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" -} \ No newline at end of file diff --git a/resources/factions/bluefor_modern.json b/resources/factions/bluefor_modern.json index 9a5089ab..72e5dafb 100644 --- a/resources/factions/bluefor_modern.json +++ b/resources/factions/bluefor_modern.json @@ -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", diff --git a/resources/factions/canada_2005.json b/resources/factions/canada_2005.json index 44761af1..6fb106fd 100644 --- a/resources/factions/canada_2005.json +++ b/resources/factions/canada_2005.json @@ -1,7 +1,7 @@ { "country": "Canada", "name": "Canada 2005", - "authors": "Khopa", + "authors": "Khopa, SpaceEnthusiast", "description": "

    Canada in the 2000s, an F/A-18C Hornet focused faction.

    ", "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" ] } } \ No newline at end of file diff --git a/resources/factions/canada_2005_c130.json b/resources/factions/canada_2005_c130.json deleted file mode 100644 index 872fb5a2..00000000 --- a/resources/factions/canada_2005_c130.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "country": "Canada", - "name": "Canada 2005 (With C-130)", - "authors": "Khopa, SpaceEnthusiast", - "description": "

    Canada in the 2000s, an F/A-18C Hornet focused faction.

    ", - "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" - ] - } -} \ No newline at end of file diff --git a/resources/factions/china_2010.json b/resources/factions/china_2010.json index fa91b93e..ef1ff28e 100644 --- a/resources/factions/china_2010.json +++ b/resources/factions/china_2010.json @@ -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", diff --git a/resources/factions/france_1985.json b/resources/factions/france_1985.json new file mode 100644 index 00000000..a23613d0 --- /dev/null +++ b/resources/factions/france_1985.json @@ -0,0 +1,81 @@ +{ + "country": "France", + "name": "France 1985", + "authors": "Colonel Panic", + "description": "

    1980s French equipment using FrenchPack.

    ", + "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" + } + diff --git a/resources/factions/france_1985_frenchpack.json b/resources/factions/france_1985_frenchpack.json deleted file mode 100644 index f443f1c4..00000000 --- a/resources/factions/france_1985_frenchpack.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "country": "France", - "name": "France 1985 (Frenchpack)", - "authors": "Colonel Panic", - "description": "

    1980s French equipment using FrenchPack.

    ", - "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" -} \ No newline at end of file diff --git a/resources/factions/france_1995.json b/resources/factions/france_1995.json index 87c73a8a..1615dd24 100644 --- a/resources/factions/france_1995.json +++ b/resources/factions/france_1995.json @@ -7,6 +7,7 @@ "fr_FR" ], "aircrafts": [ + "C-130", "Mirage 2000-5", "Mirage 2000C", "SA 342L Gazelle", diff --git a/resources/factions/france_2005.json b/resources/factions/france_2005.json new file mode 100644 index 00000000..3b460ed4 --- /dev/null +++ b/resources/factions/france_2005.json @@ -0,0 +1,83 @@ +{ + "country": "France", + "name": "France 2005", + "authors": "HerrTom", + "description": "

    French equipment using the Frenchpack, but without the Rafale mod.

    ", + "locales": [ + "fr_FR" + ], + "aircrafts": [ + "C-130", + "Mirage 2000-5", + "Mirage 2000C", + "SA 342L Gazelle", + "SA 342M Gazelle", + "SA 342M Gazelle Mistral" + ], + "awacs": [ + "E-2C Hawkeye", + "E-3A" + ], + "tankers": [ + "KC-130", + "KC-135 Stratotanker" + ], + "frontline_units": [ + "AMX.30B2", + "Leclerc S\u00e9ries 2", + "Leclerc_XXI", + "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": [ + "Stennis" + ], + "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" + } diff --git a/resources/factions/france_2005_frenchpack.json b/resources/factions/france_2005_frenchpack.json deleted file mode 100644 index eee15cb0..00000000 --- a/resources/factions/france_2005_frenchpack.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "country": "France", - "name": "France 2005 (Frenchpack)", - "authors": "HerrTom", - "description": "

    French equipment using the Frenchpack, but without the Rafale mod.

    ", - "locales": [ - "fr_FR" - ], - "aircrafts": [ - "Mirage 2000-5", - "Mirage 2000C", - "SA 342L Gazelle", - "SA 342M Gazelle", - "SA 342M Gazelle Mistral" - ], - "awacs": [ - "E-2C Hawkeye", - "E-3A" - ], - "tankers": [ - "KC-130", - "KC-135 Stratotanker" - ], - "frontline_units": [ - "AMX.30B2", - "Leclerc S\u00e9ries 2", - "Leclerc S\u00e9ries 2", - "Leclerc_XXI", - "Pamela", - "Panhard", - "Roland 2 (Marder Chassis)", - "VAB .50", - "VAB Mephisto", - "VAB T20/13", - "VAB T20/13", - "VBAE CRAB", - "VBAE CRAB MMP", - "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": [ - "Stennis" - ], - "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" -} \ No newline at end of file diff --git a/resources/factions/germany_1990.json b/resources/factions/germany_1990.json index 588a4a13..e239c83b 100644 --- a/resources/factions/germany_1990.json +++ b/resources/factions/germany_1990.json @@ -7,6 +7,7 @@ "de_DE" ], "aircrafts": [ + "C-130", "F-4F Phantom II", "MiG-29G Fulcrum-A", "SA 342L Gazelle", diff --git a/resources/factions/greece_2005.json b/resources/factions/greece_2005.json index 9a4dc897..59651dfc 100644 --- a/resources/factions/greece_2005.json +++ b/resources/factions/greece_2005.json @@ -9,6 +9,7 @@ "aircrafts": [ "AH-64A Apache", "C-130", + "C-130J-30 Super Hercules", "F-16CM Fighting Falcon (Block 50)", "F-4E Phantom II", "Mirage 2000-5", @@ -48,6 +49,7 @@ "SA8Generator", "SA10Generator", "SA15Generator", + "SA20Generator", "ZU23Generator" ], "ewrs": [ diff --git a/resources/factions/india_2010.json b/resources/factions/india_2010.json index 9ef3630d..5deef839 100644 --- a/resources/factions/india_2010.json +++ b/resources/factions/india_2010.json @@ -8,6 +8,7 @@ ], "aircrafts": [ "AH-64A Apache", + "IL-76MD", "Mi-8MTV2 Hip", "MiG-21bis Fishbed-N", "MiG-27K Flogger-J2", diff --git a/resources/factions/insurgents.json b/resources/factions/insurgents.json index 540d73d9..d2663f8c 100644 --- a/resources/factions/insurgents.json +++ b/resources/factions/insurgents.json @@ -5,6 +5,10 @@ "description": "

    Insurgents faction.

    ", "aircrafts": [], "frontline_units": [ + "DIM' KAMIKAZE", + "DIM' TOYOTA BLUE", + "DIM' TOYOTA DESERT", + "DIM' TOYOTA GREEN", "BRDM-2", "Cobra", "MT-LB", diff --git a/resources/factions/insurgents_hard.json b/resources/factions/insurgents_hard.json index 11aa5e5f..cf378a1c 100644 --- a/resources/factions/insurgents_hard.json +++ b/resources/factions/insurgents_hard.json @@ -5,6 +5,10 @@ "description": "

    Insurgents faction.

    ", "aircrafts": [], "frontline_units": [ + "DIM' KAMIKAZE", + "DIM' TOYOTA BLUE", + "DIM' TOYOTA DESERT", + "DIM' TOYOTA GREEN", "BMP-1", "BRDM-2", "BTR-80", @@ -35,5 +39,8 @@ "ZSU57Generator", "ZU23Generator", "ZU23UralInsurgentGenerator" + ], + "missiles": [ + "ScudGenerator" ] } \ No newline at end of file diff --git a/resources/factions/insurgents_modded.json b/resources/factions/insurgents_modded.json deleted file mode 100644 index 4e39c517..00000000 --- a/resources/factions/insurgents_modded.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "country": "Insurgents", - "name": "Insurgents (Modded)", - "authors": "Khopa", - "description": "

    Insurgents faction using the modded insurgents units from the frenchpack mods.

    ", - "aircrafts": [], - "frontline_units": [ - "DIM' KAMIKAZE", - "DIM' TOYOTA BLUE", - "DIM' TOYOTA DESERT", - "DIM' TOYOTA GREEN", - "ZU-23 on Ural-375" - ], - "artillery_units": [ - "2S19 Msta-S", - "BM-21 Grad" - ], - "logistics_units": [ - "LUV UAZ-469 Jeep", - "Truck Ural-375" - ], - "infantry_units": [ - "Infantry RPG", - "Insurgent AK-74", - "MANPADS SA-18 Igla \"Grouse\"" - ], - "air_defenses": [ - "SA9Generator", - "ZU23Generator", - "ZSU23Generator" - ], - "requirements": { - "frenchpack V3.5": "https://forums.eagle.ru/showthread.php?t=279974" - } -} \ No newline at end of file diff --git a/resources/factions/iran_1988.json b/resources/factions/iran_1988.json index 3fd5e69b..67edce5b 100644 --- a/resources/factions/iran_1988.json +++ b/resources/factions/iran_1988.json @@ -79,5 +79,6 @@ "MolniyaGroupGenerator" ], "has_jtac": true, - "jtac_unit": "MQ-9 Reaper" + "jtac_unit": "MQ-9 Reaper", + "doctrine": "coldwar" } \ No newline at end of file diff --git a/resources/factions/iran_2015.json b/resources/factions/iran_2015.json index 31837abc..76f1d23a 100644 --- a/resources/factions/iran_2015.json +++ b/resources/factions/iran_2015.json @@ -10,6 +10,7 @@ "F-14A Tomcat (Block 135-GR Late)", "F-4E Phantom II", "F-5E Tiger II", + "IL-76MD", "Mi-24V Hind-E", "Mi-24P Hind-F", "Mi-28N Havoc", @@ -57,6 +58,7 @@ "SA6Generator", "SA11Generator", "SA15Generator", + "SA17Generator", "VulcanGenerator", "ZSU23Generator", "ZU23Generator", diff --git a/resources/factions/iraq_1991.json b/resources/factions/iraq_1991.json index 01f2acc6..ed735f13 100644 --- a/resources/factions/iraq_1991.json +++ b/resources/factions/iraq_1991.json @@ -4,6 +4,7 @@ "authors": "Hawkmoon", "description": "

    Iraq forces during desert Storm

    ", "aircrafts": [ + "IL-76MD", "L-39ZA Albatros", "Mi-24V Hind-E", "Mi-24P Hind-F", @@ -80,5 +81,6 @@ "carrier_names": [], "navy_generators": [ "GrishaGroupGenerator" - ] + ], + "doctrine": "coldwar" } \ No newline at end of file diff --git a/resources/factions/israel_2000.json b/resources/factions/israel_2000.json index 565a9e96..5cf66e92 100644 --- a/resources/factions/israel_2000.json +++ b/resources/factions/israel_2000.json @@ -9,6 +9,8 @@ "aircrafts": [ "AH-1W SuperCobra", "AH-64D Apache Longbow", + "C-130", + "C-130J-30 Super Hercules", "F-15C Eagle", "F-15E Strike Eagle", "F-16CM Fighting Falcon (Block 50)", diff --git a/resources/factions/israel_2012.json b/resources/factions/israel_2012.json index e9656488..18810d0e 100644 --- a/resources/factions/israel_2012.json +++ b/resources/factions/israel_2012.json @@ -9,6 +9,8 @@ "aircrafts": [ "A-10C Thunderbolt II (Suite 3)", "AH-64D Apache Longbow", + "C-130", + "C-130J-30 Super Hercules", "F-15C Eagle", "F-15E Strike Eagle", "F-16CM Fighting Falcon (Block 50)", diff --git a/resources/factions/italy_1990.json b/resources/factions/italy_1990.json index f528a81b..6af584cb 100644 --- a/resources/factions/italy_1990.json +++ b/resources/factions/italy_1990.json @@ -9,6 +9,7 @@ "aircrafts": [ "AH-1W SuperCobra", "AV-8B Harrier II Night Attack", + "C-130", "Tornado IDS", "UH-1H Iroquois" ], diff --git a/resources/factions/italy_1990_mb339.json b/resources/factions/italy_1990_mb339.json deleted file mode 100644 index 8ffb80ac..00000000 --- a/resources/factions/italy_1990_mb339.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "country": "Italy", - "name": "Italy 1990 (With MB339)", - "authors": "Khopa", - "description": "

    Italy in the 90s, with the MB339 mod.

    ", - "locales": [ - "it_IT" - ], - "aircrafts": [ - "AH-1W SuperCobra", - "AV-8B Harrier II Night Attack", - "MB-339PAN", - "Tornado IDS", - "UH-1H Iroquois" - ], - "awacs": [ - "E-3A" - ], - "tankers": [ - "KC-130", - "KC-135 Stratotanker" - ], - "frontline_units": [ - "Leopard 1A3", - "M1097 Heavy HMMWV Avenger", - "M113" - ], - "artillery_units": [ - "M109A6 Paladin" - ], - "logistics_units": [ - "Truck M818 6x6" - ], - "infantry_units": [ - "Infantry M249", - "Infantry M4", - "MANPADS Stinger" - ], - "air_defenses": [ - "AvengerGenerator", - "HawkGenerator" - ], - "ewrs": [ - "HawkEwrGenerator" - ], - "aircraft_carrier": [], - "helicopter_carrier": [ - "LHA_Tarawa" - ], - "destroyers": [ - "PERRY" - ], - "cruisers": [ - "TICONDEROG" - ], - "requirements": { - "MB-339A/PAN by Frecce Tricolori Virtuali": "http://www.freccetricolorivirtuali.net/" - }, - "carrier_names": [], - "helicopter_carrier_names": [ - "Giuseppe Garibaldi", - "Cavour" - ], - "navy_generators": [ - "OliverHazardPerryGroupGenerator" - ], - "has_jtac": true, - "jtac_unit": "MQ-9 Reaper" -} \ No newline at end of file diff --git a/resources/factions/japan_2005.json b/resources/factions/japan_2005.json index a3bd9fcc..ddc82208 100644 --- a/resources/factions/japan_2005.json +++ b/resources/factions/japan_2005.json @@ -9,6 +9,8 @@ "aircrafts": [ "AH-1W SuperCobra", "AH-64D Apache Longbow", + "C-130", + "C-130J-30 Super Hercules", "F-15J Eagle", "F-2A", "F-4EJ Kai Phantom II" diff --git a/resources/factions/libya_2011.json b/resources/factions/libya_2011.json index 029541ea..58454e54 100644 --- a/resources/factions/libya_2011.json +++ b/resources/factions/libya_2011.json @@ -4,8 +4,10 @@ "authors": "Khopa", "description": "

    Gaddafi's Lybian forces during the 2011 international intervention

    ", "aircrafts": [ + "IL-76MD", "Mi-24V Hind-E", "Mi-24P Hind-F", + "Mi-8MTV2 Hip", "MiG-21bis Fishbed-N", "MiG-23MLD Flogger-K", "Su-17M4 Fitter-K", diff --git a/resources/factions/netherlands_1990.json b/resources/factions/netherlands_1990.json index 7ef783eb..71ad9553 100644 --- a/resources/factions/netherlands_1990.json +++ b/resources/factions/netherlands_1990.json @@ -8,8 +8,10 @@ ], "aircrafts": [ "AH-64A Apache", + "C-130", "F-16CM Fighting Falcon (Block 50)", - "F-5E Tiger II" + "F-5E Tiger II", + "UH-1H Iroquois" ], "awacs": [ "E-3A" diff --git a/resources/factions/pmc_us.json b/resources/factions/pmc_us.json index e3802ef6..30f87336 100644 --- a/resources/factions/pmc_us.json +++ b/resources/factions/pmc_us.json @@ -4,6 +4,7 @@ "authors": "Khopa", "description": "

    A private military company using western units.

    ", "aircrafts": [ + "A-4E Skyhawk", "C-101CC Aviojet", "Mi-8MTV2 Hip", "SA 342M Gazelle", diff --git a/resources/factions/pmc_us_with_mb339.json b/resources/factions/pmc_us_with_mb339.json deleted file mode 100644 index 6de74108..00000000 --- a/resources/factions/pmc_us_with_mb339.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "country": "USA", - "name": "Private Military Company - USA (MB339)", - "authors": "Khopa", - "description": "

    A private military company using western units (And using the MB339 mod).

    ", - "aircrafts": [ - "C-101CC Aviojet", - "MB-339PAN", - "Mi-8MTV2 Hip", - "SA 342M Gazelle", - "UH-1H Iroquois" - ], - "frontline_units": [ - "FV510 Warrior", - "LAV-25", - "M1043 HMMWV (M2 HMG)", - "M1097 Heavy HMMWV Avenger" - ], - "artillery_units": [], - "logistics_units": [ - "Truck M818 6x6" - ], - "infantry_units": [ - "Infantry M249", - "Infantry M4", - "MANPADS Stinger" - ], - "air_defenses": [ - "AvengerGenerator" - ], - "requirements": { - "MB-339A/PAN by Frecce Tricolori Virtuali": "http://www.freccetricolorivirtuali.net/" - }, - "has_jtac": true, - "jtac_unit": "MQ-9 Reaper" -} \ No newline at end of file diff --git a/resources/factions/redfor_china_2010.json b/resources/factions/redfor_china_2010.json index b8cc7c7d..c635c89c 100644 --- a/resources/factions/redfor_china_2010.json +++ b/resources/factions/redfor_china_2010.json @@ -5,6 +5,7 @@ "description": "

    Combined Redfor under China's leadership in the late 2000s, early 2010s.

    ", "aircrafts": [ "FC-1 Fierce Dragon", + "IL-76MD", "J-11A Flanker-L", "J-15 Flanker X-2", "J-7B", @@ -72,11 +73,16 @@ "SA11Generator", "SA13Generator", "SA15Generator", + "SA17Generator", "SA19Generator", "Tier2SA10Generator", "Tier3SA10Generator", + "SA10BGenerator", + "SA12Generator", + "SA20Generator", + "SA20BGenerator", + "SA23Generator", "ZSU23Generator", - "ZSU57Generator", "ZU23Generator", "ZU23UralGenerator" ], diff --git a/resources/factions/redfor_russia_2010.json b/resources/factions/redfor_russia_2010.json index 9041f92a..0c36b9b7 100644 --- a/resources/factions/redfor_russia_2010.json +++ b/resources/factions/redfor_russia_2010.json @@ -7,6 +7,7 @@ "ru_RU" ], "aircrafts": [ + "IL-76MD", "J-11A Flanker-L", "JF-17 Thunder", "Ka-50 Hokum", @@ -75,11 +76,16 @@ "SA11Generator", "SA13Generator", "SA15Generator", + "SA17Generator", "SA19Generator", "Tier2SA10Generator", "Tier3SA10Generator", + "SA10BGenerator", + "SA12Generator", + "SA20Generator", + "SA20BGenerator", + "SA23Generator", "ZSU23Generator", - "ZSU57Generator", "ZU23Generator", "ZU23UralGenerator" ], diff --git a/resources/factions/russia_1955.json b/resources/factions/russia_1955.json index 0416a069..a08f2a3a 100644 --- a/resources/factions/russia_1955.json +++ b/resources/factions/russia_1955.json @@ -7,7 +7,8 @@ "ru_RU" ], "aircrafts": [ - "MiG-15bis Fagot" + "MiG-15bis Fagot", + "Mi-8MTV2 Hip" ], "awacs": [ "A-50" diff --git a/resources/factions/russia_1975 (Mi-24P).json b/resources/factions/russia_1975 (Mi-24P).json new file mode 100644 index 00000000..5294604c --- /dev/null +++ b/resources/factions/russia_1975 (Mi-24P).json @@ -0,0 +1,84 @@ +{ + "country": "Russia", + "name": "Russia 1975 (Mi-24P)", + "authors": "Khopa", + "description": "

    Soviet army in the late 70s, using their prototype Mig-29A and Mi-24P.

    ", + "locales": [ + "ru_RU" + ], + "aircrafts": [ + "Mi-24V Hind-E", + "Mi-24P Hind-F", + "Mi-8MTV2 Hip", + "MiG-21bis Fishbed-N", + "MiG-23MLD Flogger-K", + "MiG-25PD Foxbat-E", + "MiG-29A Fulcrum-A", + "Su-17M4 Fitter-K", + "Su-24M Fencer-D", + "Su-25 Frogfoot", + "Tu-142 Bear-F", + "Tu-95MS Bear-H" + ], + "awacs": [ + "A-50" + ], + "tankers": [ + "IL-78M" + ], + "frontline_units": [ + "BMD-1", + "BMP-1", + "BRDM-2", + "BTR-80", + "PT-76", + "SAM SA-8 Osa \"Gecko\" TEL", + "T-55A" + ], + "artillery_units": [ + "2S1 Gvozdika", + "2S9 Nona-S", + "BM-21 Grad" + ], + "logistics_units": [ + "LUV UAZ-469 Jeep", + "Truck Ural-375" + ], + "infantry_units": [ + "Infantry AK-74 Rus", + "Infantry RPG", + "Mortar 2B11 120mm" + ], + "air_defenses": [ + "ColdWarFlakGenerator", + "SA2Generator", + "SA3Generator", + "SA6Generator", + "SA8Generator", + "SA9Generator", + "SA13Generator", + "ZSU23Generator", + "ZSU57Generator", + "ZU23Generator", + "ZU23UralGenerator" + ], + "ewrs": [ + "FlatFaceGenerator" + ], + "aircraft_carrier": [], + "helicopter_carrier": [], + "helicopter_carrier_names": [], + "destroyers": [], + "cruisers": [], + "missiles": [ + "ScudGenerator" + ], + "missiles_group_count": 1, + "requirements": {}, + "carrier_names": [], + "navy_generators": [ + "MolniyaGroupGenerator" + ], + "has_jtac": false, + "doctrine": "coldwar" +} diff --git a/resources/factions/russia_1975.json b/resources/factions/russia_1975.json index 1f456259..9c4dbd3a 100644 --- a/resources/factions/russia_1975.json +++ b/resources/factions/russia_1975.json @@ -7,8 +7,8 @@ "ru_RU" ], "aircrafts": [ + "IL-76MD", "Mi-24V Hind-E", - "Mi-24P Hind-F", "Mi-8MTV2 Hip", "MiG-21bis Fishbed-N", "MiG-23MLD Flogger-K", diff --git a/resources/factions/russia_1990.json b/resources/factions/russia_1990.json index 22afa47f..a833f71b 100644 --- a/resources/factions/russia_1990.json +++ b/resources/factions/russia_1990.json @@ -7,6 +7,7 @@ "ru_RU" ], "aircrafts": [ + "IL-76MD", "Ka-50 Hokum", "Mi-24V Hind-E", "Mi-24P Hind-F", diff --git a/resources/factions/russia_2010.json b/resources/factions/russia_2010.json index 791e2b67..1d7a519d 100644 --- a/resources/factions/russia_2010.json +++ b/resources/factions/russia_2010.json @@ -7,6 +7,7 @@ "ru_RU" ], "aircrafts": [ + "IL-76MD", "Ka-50 Hokum", "L-39ZA Albatros", "Mi-24V Hind-E", @@ -66,9 +67,15 @@ "SA11Generator", "SA13Generator", "SA15Generator", + "SA17Generator", "SA19Generator", "Tier2SA10Generator", "Tier3SA10Generator", + "SA10BGenerator", + "SA12Generator", + "SA20Generator", + "SA20BGenerator", + "SA23Generator", "ZSU23Generator", "ZU23Generator", "ZU23UralGenerator" diff --git a/resources/factions/russia_2010_hds.json b/resources/factions/russia_2010_hds.json deleted file mode 100644 index f61612b4..00000000 --- a/resources/factions/russia_2010_hds.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "country": "Russia", - "name": "Russia 2010 (High Digit SAMs)", - "authors": "Khopa", - "description": "

    Russian army in the early 2010s, featuring the High Digit SAMs mod units.

    ", - "locales": [ - "ru_RU" - ], - "aircrafts": [ - "Ka-50 Hokum", - "L-39ZA Albatros", - "Mi-24V Hind-E", - "Mi-24P Hind-F", - "Mi-28N Havoc", - "Mi-8MTV2 Hip", - "MiG-29S Fulcrum-C", - "MiG-31 Foxhound", - "Su-24M Fencer-D", - "Su-25 Frogfoot", - "Su-25T Frogfoot", - "Su-27 Flanker-B", - "Su-30 Flanker-C", - "Su-33 Flanker-D", - "Su-34 Fullback", - "Tu-142 Bear-F", - "Tu-160 Blackjack", - "Tu-22M3 Backfire-C", - "Tu-95MS Bear-H" - ], - "awacs": [ - "A-50" - ], - "tankers": [ - "IL-78M" - ], - "frontline_units": [ - "BMP-1", - "BMP-2", - "BMP-3", - "BTR-80", - "BTR-82A", - "SA-19 Grison (2K22 Tunguska)", - "T-72B3 model 2011", - "T-80UD", - "T-90A" - ], - "artillery_units": [ - "2S19 Msta-S", - "BM-27 Uragan" - ], - "logistics_units": [ - "LUV UAZ-469 Jeep", - "Truck Ural-375" - ], - "infantry_units": [ - "Infantry AK-74 Rus", - "MANPADS SA-18 Igla \"Grouse\"", - "Mortar 2B11 120mm", - "Paratrooper AKS", - "Paratrooper RPG-16" - ], - "air_defenses": [ - "SA17Generator", - "SA13Generator", - "SA15Generator", - "SA19Generator", - "SA10BGenerator", - "SA12Generator", - "SA20Generator", - "SA20BGenerator", - "SA23Generator" - ], - "ewrs": [ - "BoxSpringGenerator", - "TallRackGenerator" - ], - "aircraft_carrier": [ - "KUZNECOW" - ], - "helicopter_carrier": [], - "helicopter_carrier_names": [], - "destroyers": [ - "REZKY" - ], - "cruisers": [ - "MOLNIYA" - ], - "requirements": { - "High Digit SAMs": "https://github.com/Auranis/HighDigitSAMs/releases" - }, - "carrier_names": [ - "Admiral Kuznetsov" - ], - "navy_generators": [ - "RussianNavyGroupGenerator" - ], - "has_jtac": true, - "jtac_unit": "MQ-9 Reaper" -} \ No newline at end of file diff --git a/resources/factions/russia_2020.json b/resources/factions/russia_2020.json index d20fa37f..49026f60 100644 --- a/resources/factions/russia_2020.json +++ b/resources/factions/russia_2020.json @@ -1,12 +1,13 @@ { "country": "Russia", - "name": "Russia 2020 (Modded)", + "name": "Russia 2020", "authors": "Khopa", "description": "

    Russia in 2020, using the Su-57 mod by Cubanace.

    ", "locales": [ "ru_RU" ], "aircrafts": [ + "IL-76MD", "Ka-50 Hokum", "L-39ZA Albatros", "Mi-24V Hind-E", @@ -63,9 +64,16 @@ "SA11Generator", "SA13Generator", "SA15Generator", + "SA17Generator", "SA19Generator", "Tier2SA10Generator", "Tier3SA10Generator", + "SA10BGenerator", + "SA12Generator", + "SA20Generator", + "SA20BGenerator", + "SA23Generator", + "ZSU23Generator", "ZU23Generator", "ZU23UralGenerator" ], diff --git a/resources/factions/spain_1990.json b/resources/factions/spain_1990.json index 13ed6812..6031faab 100644 --- a/resources/factions/spain_1990.json +++ b/resources/factions/spain_1990.json @@ -8,6 +8,7 @@ ], "aircrafts": [ "AV-8B Harrier II Night Attack", + "C-130", "C-101CC Aviojet", "EF-18A+ Hornet", "F-5E Tiger II", diff --git a/resources/factions/sweden_2002.json b/resources/factions/sweden_2002.json new file mode 100644 index 00000000..a3efd379 --- /dev/null +++ b/resources/factions/sweden_2002.json @@ -0,0 +1,52 @@ +{ + "country": "Sweden", + "name": "Sweden 2002", + "authors": "Khopa (updated with Gripen by bgreman)", + "description": "

    Sweden in 2002 after the addition of the Gripen-C.

    ", + "locales": [ + "sv_SE" + ], + "aircrafts": [ + "AJS-37 Viggen", + "JAS 39 Gripen", + "JAS 39 Gripen A/G", + "UH-1H Iroquois" + ], + "awacs": [ + "E-3A" + ], + "tankers": [ + "KC-130", + "KC-135 Stratotanker" + ], + "frontline_units": [ + "FV510 Warrior", + "Leopard 2A4", + "M1097 Heavy HMMWV Avenger", + "M1126 Stryker ICV (M2 HMG)" + ], + "artillery_units": [], + "logistics_units": [ + "Truck M818 6x6" + ], + "infantry_units": [ + "Infantry M249", + "Infantry M4", + "MANPADS Stinger" + ], + "air_defenses": [ + "AvengerGenerator", + "HawkGenerator" + ], + "ewrs": [ + "HawkEwrGenerator" + ], + "navy_generators": [ + "OliverHazardPerryGroupGenerator" + ], + "requirements": { + "JAS39 Gripen Mod by Community": "https://github.com/whisky-actual/Community-JAS-39-C" + }, + "has_jtac": true, + "jtac_unit": "MQ-9 Reaper" + } \ No newline at end of file diff --git a/resources/factions/sweden_2002_with_gripen.json b/resources/factions/sweden_2002_with_gripen.json deleted file mode 100644 index a4a406b7..00000000 --- a/resources/factions/sweden_2002_with_gripen.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "country": "Sweden", - "name": "Sweden 2002", - "authors": "Khopa (updated with Gripen by bgreman)", - "description": "

    Sweden in 2002 after the addition of the Gripen-C.

    ", - "locales": [ - "sv_SE" - ], - "aircrafts": [ - "AJS-37 Viggen", - "JAS 39 Gripen", - "JAS 39 Gripen A/G", - "UH-1H Iroquois" - ], - "awacs": [ - "E-3A" - ], - "tankers": [ - "KC-130", - "KC-135 Stratotanker" - ], - "frontline_units": [ - "FV510 Warrior", - "Leopard 2A4", - "M1097 Heavy HMMWV Avenger", - "M1126 Stryker ICV (M2 HMG)" - ], - "artillery_units": [], - "logistics_units": [ - "Truck M818 6x6" - ], - "infantry_units": [ - "Infantry M249", - "Infantry M4", - "MANPADS Stinger" - ], - "air_defenses": [ - "AvengerGenerator", - "HawkGenerator" - ], - "ewrs": [ - "HawkEwrGenerator" - ], - "navy_generators": [ - "OliverHazardPerryGroupGenerator" - ], - "requirements": { - "JAS39 Gripen Mod by Community": "https://github.com/whisky-actual/Community-JAS-39-C" - }, - "has_jtac": true, - "jtac_unit": "MQ-9 Reaper" -} \ No newline at end of file diff --git a/resources/factions/syria_2011.json b/resources/factions/syria_2011.json index 4e3d46fd..07e77af1 100644 --- a/resources/factions/syria_2011.json +++ b/resources/factions/syria_2011.json @@ -4,6 +4,7 @@ "authors": "Khopa", "description": "

    Syrian Arab Army at the start of the Syrian Civil War.

    ", "aircrafts": [ + "IL-76MD", "L-39ZA Albatros", "Mi-24V Hind-E", "Mi-24P Hind-F", @@ -63,6 +64,7 @@ "SA10Generator", "SA11Generator", "SA13Generator", + "SA17Generator", "SA19Generator", "ZSU23Generator", "ZU23Generator", diff --git a/resources/factions/syria_2012.json b/resources/factions/syria_2012.json index 21750851..1b8c4719 100644 --- a/resources/factions/syria_2012.json +++ b/resources/factions/syria_2012.json @@ -4,6 +4,7 @@ "authors": "Headiii", "description": "

    Syrian Army with more modern Imports and supported by a Russian Expeditionary Force.

    ", "aircrafts": [ + "IL-76MD", "L-39ZA Albatros", "Mi-24V Hind-E", "Mi-24P Hind-F", @@ -64,6 +65,7 @@ "SA10Generator", "SA11Generator", "SA13Generator", + "SA17Generator", "SA19Generator", "ZSU23Generator", "ZU23Generator", diff --git a/resources/factions/uae_2005.json b/resources/factions/uae_2005.json index b4b725b0..ecc41eb3 100644 --- a/resources/factions/uae_2005.json +++ b/resources/factions/uae_2005.json @@ -5,6 +5,7 @@ "description": "

    UAR army in the 2000s.

    ", "aircrafts": [ "AH-64D Apache Longbow", + "C-130", "F-16CM Fighting Falcon (Block 50)", "Mirage 2000-5", "Mirage 2000C" diff --git a/resources/factions/uae_2015.json b/resources/factions/uae_2015.json index 58568c21..7786ab95 100644 --- a/resources/factions/uae_2015.json +++ b/resources/factions/uae_2015.json @@ -6,6 +6,7 @@ "aircrafts": [ "AH-64D Apache Longbow", "C-130", + "C-130J-30 Super Hercules", "C-17A", "CH-47D", "F-16CM Fighting Falcon (Block 50)", diff --git a/resources/factions/uk_1990.json b/resources/factions/uk_1990.json index 38ae53d2..b7780622 100644 --- a/resources/factions/uk_1990.json +++ b/resources/factions/uk_1990.json @@ -9,6 +9,8 @@ "aircrafts": [ "AH-64A Apache", "AV-8B Harrier II Night Attack", + "C-130", + "C-130J-30 Super Hercules", "Gazelle AH.1", "Phantom F.3", "Tornado GR4" diff --git a/resources/factions/ukraine_2010.json b/resources/factions/ukraine_2010.json index e379cdcc..56e6baf2 100644 --- a/resources/factions/ukraine_2010.json +++ b/resources/factions/ukraine_2010.json @@ -4,6 +4,7 @@ "authors": "Khopa", "description": "

    Ukrainian army in the 2010s.

    ", "aircrafts": [ + "IL-76MD", "L-39ZA Albatros", "Mi-24V Hind-E", "Mi-24P Hind-F", @@ -49,7 +50,9 @@ "SA11Generator", "SA13Generator", "SA15Generator", + "SA17Generator", "SA19Generator", + "SA20Generator", "Tier2SA10Generator", "Tier3SA10Generator", "ZSU23Generator", diff --git a/resources/factions/us_aggressors.json b/resources/factions/us_aggressors.json index 6ddb89a6..83a98be8 100644 --- a/resources/factions/us_aggressors.json +++ b/resources/factions/us_aggressors.json @@ -7,12 +7,15 @@ "en_US" ], "aircrafts": [ + "A-4E Skyhawk", "A-10A Thunderbolt II", "A-10C Thunderbolt II (Suite 3)", "AH-64D Apache Longbow", "AV-8B Harrier II Night Attack", "B-1B Lancer", "B-52H Stratofortress", + "C-130", + "C-130J-30 Super Hercules", "F-117A Nighthawk", "F-14B Tomcat", "F-15C Eagle", @@ -20,6 +23,7 @@ "F-16CM Fighting Falcon (Block 50)", "F/A-18C Hornet (Lot 20)", "Ka-50 Hokum", + "Mi-24P Hind-F", "SH-60B Seahawk", "Su-27 Flanker-B", "UH-1H Iroquois" diff --git a/resources/factions/usa_1990.json b/resources/factions/usa_1990.json index 0519f175..deda3324 100644 --- a/resources/factions/usa_1990.json +++ b/resources/factions/usa_1990.json @@ -13,6 +13,7 @@ "B-1B Lancer", "B-52H Stratofortress", "C-130", + "C-130J-30 Super Hercules", "CH-47D", "CH-53E", "F-117A Nighthawk", @@ -24,7 +25,6 @@ "F/A-18C Hornet (Lot 20)", "S-3B Viking", "SH-60B Seahawk", - "SH-60B Seahawk", "UH-1H Iroquois", "UH-60A" ], diff --git a/resources/factions/usa_2005.json b/resources/factions/usa_2005.json index b2afc54c..a05ede7a 100644 --- a/resources/factions/usa_2005.json +++ b/resources/factions/usa_2005.json @@ -14,6 +14,7 @@ "B-1B Lancer", "B-52H Stratofortress", "C-130", + "C-130J-30 Super Hercules", "C-17A", "CH-47D", "CH-53E", @@ -22,6 +23,7 @@ "F-15C Eagle", "F-15E Strike Eagle", "F-16CM Fighting Falcon (Block 50)", + "F-22A Raptor", "F/A-18C Hornet (Lot 20)", "S-3B Viking", "SH-60B Seahawk", diff --git a/resources/factions/usn_1985.json b/resources/factions/usn_1985.json index 05ccf915..435c9567 100644 --- a/resources/factions/usn_1985.json +++ b/resources/factions/usn_1985.json @@ -8,6 +8,7 @@ ], "aircrafts": [ "AH-1W SuperCobra", + "A-4E Skyhawk", "F-14A Tomcat (Block 135-GR Late)", "F-14B Tomcat", "F-4E Phantom II", diff --git a/resources/factions/usa_2005_c130.json b/resources/factions/usn_2005.json similarity index 51% rename from resources/factions/usa_2005_c130.json rename to resources/factions/usn_2005.json index d59e133e..1ba50c00 100644 --- a/resources/factions/usa_2005_c130.json +++ b/resources/factions/usn_2005.json @@ -1,75 +1,53 @@ { "country": "USA", - "name": "USA 2005 (With C-130)", - "authors": "Khopa", - "description": "

    USA in the 2000s.

    ", + "name": "US Navy 2005", + "authors": "Fuzzle", + "description": "

    A modern representation of the US Navy/Marine Corps.

    ", "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", - "C-130", - "C-130J-30 Super Hercules", - "C-17A", - "CH-47D", - "CH-53E", - "F-117A Nighthawk", "F-14B Tomcat", - "F-15C Eagle", - "F-15E Strike Eagle", - "F-16CM Fighting Falcon (Block 50)", "F/A-18C Hornet (Lot 20)", + "AV-8B Harrier II Night Attack", + "AH-1W SuperCobra", "S-3B Viking", "SH-60B Seahawk", - "UH-1H Iroquois", - "UH-60A" + "UH-1H Iroquois" ], "awacs": [ - "E-2C Hawkeye", - "E-3A" + "E-2C Hawkeye" ], "tankers": [ - "KC-130", - "KC-135 Stratotanker", - "KC-135 Stratotanker MPRS", "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" + "LAV-25", + "M163 Vulcan Air Defense System" ], "artillery_units": [ - "M109A6 Paladin", "M270 Multiple Launch Rocket System" ], "logistics_units": [ "Truck M818 6x6" ], "infantry_units": [ - "Infantry M249", "Infantry M4", - "MANPADS Stinger", - "Mortar 2B11 120mm" + "Infantry M249", + "MANPADS Stinger" ], "air_defenses": [ - "AvengerGenerator", - "LinebackerGenerator", + "ChaparralGenerator", + "HawkGenerator", + "VulcanGenerator", "PatriotGenerator" ], "ewrs": [ - "PatriotEwrGenerator" + "HawkEwrGenerator" ], "aircraft_carrier": [ "Stennis" @@ -84,7 +62,6 @@ "TICONDEROG" ], "requirements": { - "C-130J-30 Super Hercules Mod by Anubis": "https://forums.eagle.ru/topic/252075-dcs-super-hercules-mod-by-anubis/" }, "carrier_names": [ "CVN-71 Theodore Roosevelt", @@ -106,24 +83,22 @@ ], "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" ] } -} \ No newline at end of file +} diff --git a/resources/squadrons/Eagle/IAF 106th Sqn.yaml b/resources/squadrons/Eagle/IAF 106th Sqn.yaml new file mode 100644 index 00000000..9e7924b9 --- /dev/null +++ b/resources/squadrons/Eagle/IAF 106th Sqn.yaml @@ -0,0 +1,14 @@ +--- +name: 106th Squadron +nickname: Spearhead +country: Israel +role: Air Superiority Fighter +aircraft: F-15C Eagle +livery: 106th SQN (8th Airbase) +mission_types: + - BARCAP + - Escort + - Intercept + - Fighter sweep + - TARCAP + diff --git a/resources/squadrons/Strike Eagle/IAF 69th Sqn.yaml b/resources/squadrons/Strike Eagle/IAF 69th Sqn.yaml new file mode 100644 index 00000000..57affc9d --- /dev/null +++ b/resources/squadrons/Strike Eagle/IAF 69th Sqn.yaml @@ -0,0 +1,15 @@ +--- +name: 69th Squadron +nickname: Hammers +country: Israel +role: Strike Fighter +aircraft: F-15E Strike Eagle +livery: IDF No 69 Hammers Squadron +mission_types: + - BAI + - CAS + - DEAD + - OCA/Aircraft + - OCA/Runway + - Strike + diff --git a/resources/squadrons/viper/IAF 101st Sqn.yaml b/resources/squadrons/viper/IAF 101st Sqn.yaml new file mode 100644 index 00000000..844e80d1 --- /dev/null +++ b/resources/squadrons/viper/IAF 101st Sqn.yaml @@ -0,0 +1,21 @@ +--- +name: 101st Squadron +nickname: First Fighter +country: Israel +role: Strike Fighter +aircraft: F-16CM Fighting Falcon (Block 50) +livery: IAF_101st_squadron +mission_types: + - BAI + - BARCAP + - CAS + - DEAD + - Escort + - Intercept + - OCA/Aircraft + - OCA/Runway + - SEAD + - SEAD Escort + - Strike + - Fighter sweep + - TARCAP diff --git a/resources/squadrons/viper/IAF 110th Sqn.yaml b/resources/squadrons/viper/IAF 110th Sqn.yaml new file mode 100644 index 00000000..e968326e --- /dev/null +++ b/resources/squadrons/viper/IAF 110th Sqn.yaml @@ -0,0 +1,21 @@ +--- +name: 110th Squadron +nickname: Knights of the North +country: Israel +role: Strike Fighter +aircraft: F-16CM Fighting Falcon (Block 50) +livery: IAF_110th_Squadron +mission_types: + - BAI + - BARCAP + - CAS + - DEAD + - Escort + - Intercept + - OCA/Aircraft + - OCA/Runway + - SEAD + - SEAD Escort + - Strike + - Fighter sweep + - TARCAP diff --git a/resources/squadrons/viper/IAF 117th Sqn.yaml b/resources/squadrons/viper/IAF 117th Sqn.yaml new file mode 100644 index 00000000..f7b45064 --- /dev/null +++ b/resources/squadrons/viper/IAF 117th Sqn.yaml @@ -0,0 +1,21 @@ +--- +name: 117th Squadron +nickname: First Jet +country: Israel +role: Strike Fighter +aircraft: F-16CM Fighting Falcon (Block 50) +livery: IAF_117th_Squadron +mission_types: + - BAI + - BARCAP + - CAS + - DEAD + - Escort + - Intercept + - OCA/Aircraft + - OCA/Runway + - SEAD + - SEAD Escort + - Strike + - Fighter sweep + - TARCAP diff --git a/resources/ui/map/canvas.html b/resources/ui/map/canvas.html index 42830fe8..ff99669a 100644 --- a/resources/ui/map/canvas.html +++ b/resources/ui/map/canvas.html @@ -24,6 +24,13 @@ integrity="sha384-XAr1poM2RCR9/QQFki7ylrGSdmvYE0NuHghuRuxb/k9zJQA53y6qR5te5jJRZlcL" crossorigin=""> + +