Merge branch 'pretense-generator' of https://github.com/MetalStormGhost/dcs-retribution into pretense-generator

This commit is contained in:
MetalStormGhost
2024-04-06 13:58:06 +03:00
92 changed files with 5759 additions and 4050 deletions

View File

@@ -1,6 +1,8 @@
# Retribution v1.3.0 # Retribution v1.3.0
#### Note: Re-save your missions in DCS' Mission Editor to avoid possible crashes due to datalink (usually the case when F-16C blk50s are used) when hosting missions on a dedicated server.
## Features/Improvements ## Features/Improvements
* **[Engine]** Support for DCS v2.9.3.51704
* **[Package Planning]** Option to "Auto-Create" package * **[Package Planning]** Option to "Auto-Create" package
* **[Modding]** Custom weapons injection system (definition in aircraft's yaml file) * **[Modding]** Custom weapons injection system (definition in aircraft's yaml file)
* **[Payload Editor]** Ability to save/back-up payloads * **[Payload Editor]** Ability to save/back-up payloads
@@ -30,12 +32,15 @@
* **[COMMs]** Aircraft-specific callsigns will now also be used. * **[COMMs]** Aircraft-specific callsigns will now also be used.
* **[COMMs]** Ability to set a specific callsign to a flight. * **[COMMs]** Ability to set a specific callsign to a flight.
* **[Mission Generator]** Channel terrain fix on exclusion zones, sea zones and inclusion zones * **[Mission Generator]** Channel terrain fix on exclusion zones, sea zones and inclusion zones
* **[Options]** Cheat-option for accessing Air Wing Config Dialog after campaign start * **[Options]** Cheat-option for accessing Air Wing Config Dialog after campaign start (re-initializes turn if applied, thus plan your mission ___after___ making changes)
* **[Options]** Option to enable unlimited fuel for AI (player and non-player flights) * **[Options]** Option to enable unlimited fuel for AI (player and non-player flights)
* **[Mission Generator]** F-15E Strike targets are automatically added as Mission Set 1 * **[Mission Generator]** F-15E Strike targets are automatically added as Mission Set 1
* **[Mission Generator]** Set F-14's IP waypoint according to the flight-plan's ingress point * **[Mission Generator]** Set F-14's IP waypoint according to the flight-plan's ingress point
* **[Mission Generator]** Automatically de-spawn aircraft when arrival/divert is an off-map spawn * **[Mission Generator]** Automatically de-spawn aircraft when arrival/divert is an off-map spawn
* **[Options]** Option to de-spawn AI flights in the air if their start-type was manually set to In-Flight * **[Options]** Option to de-spawn AI flights in the air if their start-type was manually set to In-Flight
* **[Config]** Preference setting to use custom Liberation payloads instead of prioritizing Retribution's default
* **[Config]** Preference setting to configure the server-port on which Retribution's back-end will run
* **[Options]** Made AI jettisoning empty fuel tanks optional (disabled by default)
## Fixes ## Fixes
* **[Mission Generation]** Anti-ship strikes should use "group attack" in their attack-task * **[Mission Generation]** Anti-ship strikes should use "group attack" in their attack-task
@@ -49,6 +54,8 @@
* **[Capture Logic]** Release all parking slots when an airbase is captured * **[Capture Logic]** Release all parking slots when an airbase is captured
* **[Modding]** Swedish Military Assets Pack air defence presets are now correctly removed from the faction when the mod is disabled. * **[Modding]** Swedish Military Assets Pack air defence presets are now correctly removed from the faction when the mod is disabled.
* **[Mission Generation]** Naval aircraft not always returning to carrier * **[Mission Generation]** Naval aircraft not always returning to carrier
* **[Mission Generation]** AI AirLift aircraft crashing into terrain due to insufficient waypoints
* **[Mission Generation]** Fix friendly AI shooting at fires on the front-line
# Retribution v1.2.1 (hotfix) # Retribution v1.2.1 (hotfix)

View File

@@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import random
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from typing import Iterator, TYPE_CHECKING, Type from typing import Iterator, TYPE_CHECKING, Type
@@ -38,6 +39,7 @@ class AirAssaultLayout(FormationAttackLayout):
if self.pickup is not None: if self.pickup is not None:
yield self.pickup yield self.pickup
yield from self.nav_to yield from self.nav_to
yield self.join
yield self.ingress yield self.ingress
if self.drop_off is not None: if self.drop_off is not None:
yield self.drop_off yield self.drop_off
@@ -131,19 +133,34 @@ class Builder(FormationAttackBuilder[AirAssaultFlightPlan, AirAssaultLayout]):
self._generate_ctld_pickup(), self._generate_ctld_pickup(),
) )
) )
pickup.alt = heli_alt
pickup_position = pickup.position pickup_position = pickup.position
ingress = builder.ingress(
FlightWaypointType.INGRESS_AIR_ASSAULT,
self.package.waypoints.ingress,
self.package.target,
)
assault_area = builder.assault_area(self.package.target) assault_area = builder.assault_area(self.package.target)
heading = self.package.target.position.heading_between_point(pickup_position)
if self.flight.is_hercules: if self.flight.is_hercules:
assault_area.only_for_player = False assault_area.only_for_player = False
assault_area.alt = feet(1000) assault_area.alt = feet(1000)
# TODO: define CTLD dropoff zones in campaign miz? tgt = self.package.target
drop_off_zone = MissionTarget( if isinstance(tgt, CTLD) and tgt.ctld_zones:
"Dropoff zone", top3 = sorted(
self.package.target.position.point_from_heading(heading, 1200), tgt.ctld_zones, key=lambda x: ingress.position.distance_to_point(x[0])
) )[:3]
pos, dist = random.choice(top3)
drop_pos = pos.random_point_within(dist)
else:
heading = tgt.position.heading_between_point(ingress.position)
drop_pos = tgt.position.point_from_heading(heading, 1200)
drop_off_zone = MissionTarget("Dropoff zone", drop_pos)
dz = builder.dropoff_zone(drop_off_zone) if self.flight.is_helo else None dz = builder.dropoff_zone(drop_off_zone) if self.flight.is_helo else None
if dz:
dz.alt = heli_alt
return AirAssaultLayout( return AirAssaultLayout(
departure=builder.takeoff(self.flight.departure), departure=builder.takeoff(self.flight.departure),
@@ -154,11 +171,7 @@ class Builder(FormationAttackBuilder[AirAssaultFlightPlan, AirAssaultLayout]):
altitude, altitude,
altitude_is_agl, altitude_is_agl,
), ),
ingress=builder.ingress( ingress=ingress,
FlightWaypointType.INGRESS_AIR_ASSAULT,
self.package.waypoints.ingress,
self.package.target,
),
drop_off=dz, drop_off=dz,
targets=[assault_area], targets=[assault_area],
nav_from=builder.nav_path( nav_from=builder.nav_path(
@@ -171,8 +184,8 @@ class Builder(FormationAttackBuilder[AirAssaultFlightPlan, AirAssaultLayout]):
divert=builder.divert(self.flight.divert), divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(), bullseye=builder.bullseye(),
hold=None, hold=None,
join=builder.join(pickup_position), join=builder.join(ingress.position),
split=builder.split(self.package.waypoints.split), split=builder.split(self.flight.arrival.position),
refuel=None, refuel=None,
) )

View File

@@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import random
from collections.abc import Iterator from collections.abc import Iterator
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime from datetime import datetime
@@ -7,7 +8,7 @@ from typing import Optional
from typing import TYPE_CHECKING, Type from typing import TYPE_CHECKING, Type
from game.theater.missiontarget import MissionTarget from game.theater.missiontarget import MissionTarget
from game.utils import feet from game.utils import feet, Distance
from ._common_ctld import generate_random_ctld_point from ._common_ctld import generate_random_ctld_point
from .ibuilder import IBuilder from .ibuilder import IBuilder
from .planningerror import PlanningError from .planningerror import PlanningError
@@ -23,13 +24,17 @@ if TYPE_CHECKING:
@dataclass @dataclass
class AirliftLayout(StandardLayout): class AirliftLayout(StandardLayout):
pickup_ascent: FlightWaypoint | None
pickup_descent: FlightWaypoint | None
# There will not be a pickup waypoint when the pickup airfield is the departure # There will not be a pickup waypoint when the pickup airfield is the departure
# airfield for cargo planes, as the cargo is pre-loaded. Helicopters will still pick # airfield for cargo planes, as the cargo is pre-loaded. Helicopters will still pick
# up the cargo near the airfield. # up the cargo near the airfield.
pickup: FlightWaypoint | None pickup: FlightWaypoint | None
# pickup_zone will be used for player flights to create the CTLD stuff # pickup_zone will be used for player flights to create the CTLD stuff
ctld_pickup_zone: FlightWaypoint | None ctld_pickup_zone: FlightWaypoint | None
drop_off_ascent: FlightWaypoint | None
nav_to_drop_off: list[FlightWaypoint] nav_to_drop_off: list[FlightWaypoint]
drop_off_descent: FlightWaypoint | None
# There will not be a drop-off waypoint when the drop-off airfield and the arrival # There will not be a drop-off waypoint when the drop-off airfield and the arrival
# airfield is the same for a cargo plane, as planes will land to unload and we don't # airfield is the same for a cargo plane, as planes will land to unload and we don't
# want a double landing. Helicopters will still drop their cargo near the airfield # want a double landing. Helicopters will still drop their cargo near the airfield
@@ -37,6 +42,8 @@ class AirliftLayout(StandardLayout):
drop_off: FlightWaypoint | None drop_off: FlightWaypoint | None
# drop_off_zone will be used for player flights to create the CTLD stuff # drop_off_zone will be used for player flights to create the CTLD stuff
ctld_drop_off_zone: FlightWaypoint | None ctld_drop_off_zone: FlightWaypoint | None
return_ascent: FlightWaypoint
return_descent: FlightWaypoint
def add_waypoint( def add_waypoint(
self, wpt: FlightWaypoint, next_wpt: Optional[FlightWaypoint] self, wpt: FlightWaypoint, next_wpt: Optional[FlightWaypoint]
@@ -60,17 +67,27 @@ class AirliftLayout(StandardLayout):
def iter_waypoints(self) -> Iterator[FlightWaypoint]: def iter_waypoints(self) -> Iterator[FlightWaypoint]:
yield self.departure yield self.departure
if self.pickup_ascent is not None:
yield self.pickup_ascent
yield from self.nav_to yield from self.nav_to
if self.pickup_descent is not None:
yield self.pickup_descent
if self.pickup is not None: if self.pickup is not None:
yield self.pickup yield self.pickup
if self.ctld_pickup_zone is not None: if self.ctld_pickup_zone is not None:
yield self.ctld_pickup_zone yield self.ctld_pickup_zone
if self.drop_off_ascent is not None:
yield self.drop_off_ascent
yield from self.nav_to_drop_off yield from self.nav_to_drop_off
if self.drop_off_descent is not None:
yield self.drop_off_descent
if self.drop_off is not None: if self.drop_off is not None:
yield self.drop_off yield self.drop_off
yield self.return_ascent
if self.ctld_drop_off_zone is not None: if self.ctld_drop_off_zone is not None:
yield self.ctld_drop_off_zone yield self.ctld_drop_off_zone
yield from self.nav_from yield from self.nav_from
yield self.return_descent
yield self.arrival yield self.arrival
if self.divert is not None: if self.divert is not None:
yield self.divert yield self.divert
@@ -121,15 +138,47 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]):
builder = WaypointBuilder(self.flight) builder = WaypointBuilder(self.flight)
pickup_ascent = None
pickup_descent = None
pickup = None pickup = None
drop_off_ascent = None
drop_off_descent = None
drop_off = None drop_off = None
pickup_zone = None pickup_zone = None
drop_off_zone = None drop_off_zone = None
if cargo.origin != self.flight.departure: if cargo.origin != self.flight.departure:
pickup = builder.cargo_stop(cargo.origin) pickup = builder.cargo_stop(cargo.origin)
pickup_ascent = self._create_ascent_or_descent(
builder,
self.flight.departure.position,
cargo.origin.position,
altitude,
altitude_is_agl,
)
pickup_descent = self._create_ascent_or_descent(
builder,
cargo.origin.position,
self.flight.departure.position,
altitude,
altitude_is_agl,
)
if cargo.next_stop != self.flight.arrival: if cargo.next_stop != self.flight.arrival:
drop_off = builder.cargo_stop(cargo.next_stop) drop_off = builder.cargo_stop(cargo.next_stop)
drop_off_ascent = self._create_ascent_or_descent(
builder,
cargo.origin.position,
cargo.next_stop.position,
altitude,
altitude_is_agl,
)
drop_off_descent = self._create_ascent_or_descent(
builder,
cargo.next_stop.position,
cargo.origin.position,
altitude,
altitude_is_agl,
)
if self.flight.is_helo: if self.flight.is_helo:
# Create CTLD Zones for Helo flights # Create CTLD Zones for Helo flights
@@ -150,25 +199,50 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]):
altitude_is_agl, altitude_is_agl,
) )
return_ascent = self._create_ascent_or_descent(
builder,
cargo.next_stop.position
if cargo.next_stop != self.flight.arrival
else cargo.origin.position,
self.flight.arrival.position,
altitude,
altitude_is_agl,
)
return_descent = self._create_ascent_or_descent(
builder,
self.flight.arrival.position,
cargo.next_stop.position
if cargo.next_stop != self.flight.arrival
else cargo.origin.position,
altitude,
altitude_is_agl,
)
return AirliftLayout( return AirliftLayout(
departure=builder.takeoff(self.flight.departure), departure=builder.takeoff(self.flight.departure),
pickup_ascent=pickup_ascent,
nav_to=nav_to_pickup, nav_to=nav_to_pickup,
pickup_descent=pickup_descent,
pickup=pickup, pickup=pickup,
ctld_pickup_zone=pickup_zone, ctld_pickup_zone=pickup_zone,
drop_off_ascent=drop_off_ascent,
nav_to_drop_off=builder.nav_path( nav_to_drop_off=builder.nav_path(
cargo.origin.position, cargo.origin.position,
cargo.next_stop.position, cargo.next_stop.position,
altitude, altitude,
altitude_is_agl, altitude_is_agl,
), ),
drop_off_descent=drop_off_descent,
drop_off=drop_off, drop_off=drop_off,
ctld_drop_off_zone=drop_off_zone, ctld_drop_off_zone=drop_off_zone,
return_ascent=return_ascent,
nav_from=builder.nav_path( nav_from=builder.nav_path(
cargo.origin.position, cargo.origin.position,
self.flight.arrival.position, self.flight.arrival.position,
altitude, altitude,
altitude_is_agl, altitude_is_agl,
), ),
return_descent=return_descent,
arrival=builder.land(self.flight.arrival), arrival=builder.land(self.flight.arrival),
divert=builder.divert(self.flight.divert), divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(), bullseye=builder.bullseye(),
@@ -188,3 +262,18 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]):
if cargo and cargo.transport and isinstance(cargo.transport.destination, CTLD): if cargo and cargo.transport and isinstance(cargo.transport.destination, CTLD):
return generate_random_ctld_point(cargo.transport.destination) return generate_random_ctld_point(cargo.transport.destination)
raise RuntimeError("Could not generate CTLD dropoff") raise RuntimeError("Could not generate CTLD dropoff")
@staticmethod
def _create_ascent_or_descent(
builder: WaypointBuilder,
start: Point,
end: Point,
alt: Distance,
agl: bool,
) -> FlightWaypoint:
distance = start.distance_to_point(end)
rdistance = 1000 if agl else min(distance / 10, 20000)
heading = round(start.heading_between_point(end))
rheading = random.randint(heading - 30, heading + 30) % 360
pos = start.point_from_heading(float(rheading), rdistance)
return builder.nav(pos, alt, agl)

View File

@@ -1,6 +1,5 @@
from __future__ import annotations from __future__ import annotations
from datetime import timedelta, datetime
from typing import Type from typing import Type
from .airassault import AirAssaultLayout from .airassault import AirAssaultLayout
@@ -16,18 +15,6 @@ from ...utils import feet
class EscortFlightPlan(FormationAttackFlightPlan): class EscortFlightPlan(FormationAttackFlightPlan):
@property
def push_time(self) -> datetime:
hold2join_time = (
self.travel_time_between_waypoints(
self.layout.hold,
self.layout.join,
)
if self.layout.hold is not None
else timedelta(0)
)
return self.join_time - hold2join_time
@staticmethod @staticmethod
def builder_type() -> Type[Builder]: def builder_type() -> Type[Builder]:
return Builder return Builder
@@ -44,16 +31,17 @@ class Builder(FormationAttackBuilder[EscortFlightPlan, FormationAttackLayout]):
ingress.only_for_player = True ingress.only_for_player = True
target.only_for_player = True target.only_for_player = True
hold = None hold = None
if not self.primary_flight_is_air_assault: if not self.flight.is_helo:
hold = builder.hold(self._hold_point()) hold = builder.hold(self._hold_point())
elif self.package.primary_flight is not None:
fp = self.package.primary_flight.flight_plan
assert isinstance(fp.layout, AirAssaultLayout)
if fp.layout.pickup:
hold = builder.hold(fp.layout.pickup.position)
join = builder.join(self.package.waypoints.join) join_pos = (
split = builder.split(self.package.waypoints.split) self.package.waypoints.ingress
if self.flight.is_helo
else self.package.waypoints.join
)
join = builder.join(join_pos)
split = builder.split(self._get_split())
ingress_alt = self.doctrine.ingress_altitude ingress_alt = self.doctrine.ingress_altitude
is_helo = builder.flight.is_helo is_helo = builder.flight.is_helo
@@ -70,10 +58,12 @@ class Builder(FormationAttackBuilder[EscortFlightPlan, FormationAttackLayout]):
layout, AirliftLayout layout, AirliftLayout
) )
if isinstance(layout, AirliftLayout): if isinstance(layout, AirliftLayout):
join = builder.join(layout.departure.position) ascent = layout.pickup_ascent or layout.drop_off_ascent
else: assert ascent is not None
join = builder.join(layout.ingress.position) join = builder.join(ascent.position)
if layout.pickup: if layout.pickup and layout.drop_off_ascent:
join = builder.join(layout.drop_off_ascent.position)
elif layout.pickup:
join = builder.join(layout.pickup.position) join = builder.join(layout.pickup.position)
split = builder.split(layout.arrival.position) split = builder.split(layout.arrival.position)
if layout.drop_off: if layout.drop_off:

View File

@@ -71,15 +71,6 @@ class FlightPlan(ABC, Generic[LayoutT]):
"""A list of all waypoints in the flight plan, in order.""" """A list of all waypoints in the flight plan, in order."""
return list(self.iter_waypoints()) return list(self.iter_waypoints())
def get_index_of_wpt_by_type(self, wpt_type: FlightWaypointType) -> int:
index = 0
for wpt in self.waypoints:
if wpt and not wpt.only_for_player:
index += 1
if wpt.waypoint_type == wpt_type:
return index
return -1
def iter_waypoints(self) -> Iterator[FlightWaypoint]: def iter_waypoints(self) -> Iterator[FlightWaypoint]:
"""Iterates over all waypoints in the flight plan, in order.""" """Iterates over all waypoints in the flight plan, in order."""
yield from self.layout.iter_waypoints() yield from self.layout.iter_waypoints()
@@ -113,6 +104,19 @@ class FlightPlan(ABC, Generic[LayoutT]):
# #
# Plus, it's a loiter point so there's no reason to hurry. # Plus, it's a loiter point so there's no reason to hurry.
factor = 0.75 factor = 0.75
elif (
self.flight.is_helo
and (
a.waypoint_type == FlightWaypointType.JOIN
or "INGRESS" in a.waypoint_type.name
or a.waypoint_type == FlightWaypointType.CUSTOM
)
and self.package.primary_flight
and not self.package.primary_flight.flight_plan.is_airassault
):
# Helicopter flights should be slowed down between JOIN & INGRESS
# to allow the escort to keep up while engaging targets along the way.
factor = 0.50
# TODO: Adjust if AGL. # TODO: Adjust if AGL.
# We don't have an exact heightmap, but we should probably be performing # We don't have an exact heightmap, but we should probably be performing
# *some* adjustment for NTTR since the minimum altitude of the map is # *some* adjustment for NTTR since the minimum altitude of the map is

View File

@@ -17,7 +17,7 @@ if TYPE_CHECKING:
@dataclass @dataclass
class FormationLayout(LoiterLayout, ABC): class FormationLayout(LoiterLayout, ABC):
join: Optional[FlightWaypoint] join: FlightWaypoint
split: FlightWaypoint split: FlightWaypoint
refuel: Optional[FlightWaypoint] refuel: Optional[FlightWaypoint]
@@ -92,10 +92,15 @@ class FormationFlightPlan(LoiterFlightPlan, ABC):
@property @property
def push_time(self) -> datetime: def push_time(self) -> datetime:
return self.join_time - self.travel_time_between_waypoints( hold2join_time = (
self.layout.hold, self.travel_time_between_waypoints(
self.layout.join, self.layout.hold,
self.layout.join,
)
if self.layout.hold
else timedelta(0)
) )
return self.join_time - hold2join_time
@property @property
def mission_begin_on_station_time(self) -> datetime | None: def mission_begin_on_station_time(self) -> datetime | None:

View File

@@ -142,8 +142,7 @@ class FormationAttackLayout(FormationLayout):
if self.hold: if self.hold:
yield self.hold yield self.hold
yield from self.nav_to yield from self.nav_to
if self.join: yield self.join
yield self.join
if self.lineup: if self.lineup:
yield self.lineup yield self.lineup
yield self.ingress yield self.ingress
@@ -187,11 +186,13 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
) )
hold = None hold = None
join = None if not self.flight.is_helo:
if not self.primary_flight_is_air_assault:
hold = builder.hold(self._hold_point()) hold = builder.hold(self._hold_point())
join = builder.join(self.package.waypoints.join) join_pos = self.package.waypoints.join
split = builder.split(self.package.waypoints.split) if self.flight.is_helo:
join_pos = self.package.waypoints.ingress
join = builder.join(join_pos)
split = builder.split(self._get_split())
refuel = self._build_refuel(builder) refuel = self._build_refuel(builder)
ingress = builder.ingress( ingress = builder.ingress(
@@ -255,6 +256,9 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
@property @property
def primary_flight_is_air_assault(self) -> bool: def primary_flight_is_air_assault(self) -> bool:
if self.flight is self.package.primary_flight: if self.flight is self.package.primary_flight:
# Can't call self.package.primary_flight.flight_plan here
# because the flight-plan wasn't created yet.
# Calling the fligh_plan property would result in infinite recursion
return self.flight.flight_type == FlightType.AIR_ASSAULT return self.flight.flight_type == FlightType.AIR_ASSAULT
else: else:
assert self.package.primary_flight is not None assert self.package.primary_flight is not None
@@ -296,3 +300,13 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
return HoldZoneGeometry( return HoldZoneGeometry(
target, origin, ip, join, self.coalition, self.theater target, origin, ip, join, self.coalition, self.theater
).find_best_hold_point() ).find_best_hold_point()
def _get_split(self) -> Point:
assert self.package.waypoints is not None
assert self.package.primary_flight is not None
split_pos = (
self.package.primary_flight.arrival.position
if self.package.primary_flight.is_helo
else self.package.waypoints.split
)
return split_pos

View File

@@ -15,7 +15,6 @@ from typing import (
from dcs.mapping import Point, Vector2 from dcs.mapping import Point, Vector2
from game.ato.flightplans._common_ctld import generate_random_ctld_point
from game.ato.flightwaypoint import AltitudeReference, FlightWaypoint from game.ato.flightwaypoint import AltitudeReference, FlightWaypoint
from game.ato.flightwaypointtype import FlightWaypointType from game.ato.flightwaypointtype import FlightWaypointType
from game.theater import ( from game.theater import (
@@ -25,7 +24,6 @@ from game.theater import (
TheaterGroundObject, TheaterGroundObject,
TheaterUnit, TheaterUnit,
) )
from game.theater.interfaces.CTLD import CTLD
from game.utils import Distance, meters, nautical_miles, feet from game.utils import Distance, meters, nautical_miles, feet
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -250,10 +248,14 @@ class WaypointBuilder:
else feet(1000) else feet(1000)
) )
heading = objective.position.heading_between_point(position)
return FlightWaypoint( return FlightWaypoint(
"INGRESS", "INGRESS",
ingress_type, ingress_type,
position, objective.position.point_from_heading(heading, nautical_miles(5).meters)
if self.is_helo
else position,
alt, alt,
alt_type, alt_type,
description=f"INGRESS on {objective.name}", description=f"INGRESS on {objective.name}",
@@ -635,14 +637,10 @@ class WaypointBuilder:
"""Creates a cargo stop waypoint. """Creates a cargo stop waypoint.
This waypoint is used by AirLift as a landing and stopover waypoint This waypoint is used by AirLift as a landing and stopover waypoint
""" """
if isinstance(control_point, CTLD) and control_point.ctld_zones:
pos = generate_random_ctld_point(control_point)
else:
pos = control_point.position
return FlightWaypoint( return FlightWaypoint(
"CARGOSTOP", "CARGOSTOP",
FlightWaypointType.CARGO_STOP, FlightWaypointType.CARGO_STOP,
pos, control_point.position,
meters(0), meters(0),
"RADIO", "RADIO",
description=f"Stop for cargo at {control_point.name}", description=f"Stop for cargo at {control_point.name}",

View File

@@ -11,6 +11,7 @@ from dcs.unittype import FlyingType
from game.data.weapons import Pylon, Weapon, WeaponType from game.data.weapons import Pylon, Weapon, WeaponType
from game.dcs.aircrafttype import AircraftType from game.dcs.aircrafttype import AircraftType
from .flighttype import FlightType from .flighttype import FlightType
from ..persistency import prefer_liberation_payloads
if TYPE_CHECKING: if TYPE_CHECKING:
from .flight import Flight from .flight import Flight
@@ -161,7 +162,10 @@ class Loadout:
# last - the first element in the tuple will be tried first, then the second, # last - the first element in the tuple will be tried first, then the second,
# etc. # etc.
loadout_names = { loadout_names = {
t: [f"Retribution {t.value}", f"Liberation {t.value}"] for t in FlightType t: [f"Liberation {t.value}", f"Retribution {t.value}"]
if prefer_liberation_payloads()
else [f"Retribution {t.value}", f"Liberation {t.value}"]
for t in FlightType
} }
legacy_names = { legacy_names = {
FlightType.TARCAP: ( FlightType.TARCAP: (

View File

@@ -3,6 +3,7 @@ from enum import Enum, auto
from typing import Optional from typing import Optional
from game.ato.flighttype import FlightType from game.ato.flighttype import FlightType
from game.dcs.aircrafttype import AircraftType
from game.theater import MissionTarget from game.theater import MissionTarget
@@ -33,6 +34,8 @@ class ProposedFlight:
#: field is None. #: field is None.
escort_type: Optional[EscortType] = field(default=None) escort_type: Optional[EscortType] = field(default=None)
preferred_type: Optional[AircraftType] = field(default=None)
def __str__(self) -> str: def __str__(self) -> str:
return f"{self.task} {self.num_aircraft} ship" return f"{self.task} {self.num_aircraft} ship"

View File

@@ -140,7 +140,7 @@ class ObjectiveFinder:
def vulnerable_control_points(self) -> Iterator[ControlPoint]: def vulnerable_control_points(self) -> Iterator[ControlPoint]:
"""Iterates over friendly CPs that are vulnerable to enemy CPs. """Iterates over friendly CPs that are vulnerable to enemy CPs.
Vulnerability is defined as any enemy CP within threat range of of the Vulnerability is defined as any enemy CP within threat range of the
CP. CP.
""" """
for cp in self.friendly_control_points(): for cp in self.friendly_control_points():

View File

@@ -49,7 +49,12 @@ class PackageBuilder:
pf = self.package.primary_flight pf = self.package.primary_flight
heli = pf.is_helo if pf else False heli = pf.is_helo if pf else False
squadron = self.air_wing.best_squadron_for( squadron = self.air_wing.best_squadron_for(
self.package.target, plan.task, plan.num_aircraft, heli, this_turn=True self.package.target,
plan.task,
plan.num_aircraft,
heli,
this_turn=True,
preferred_type=plan.preferred_type,
) )
if squadron is None: if squadron is None:
return False return False

View File

@@ -0,0 +1,15 @@
from collections.abc import Iterator
from game.commander.tasks.primitive.antiship import PlanAntiShip
from game.commander.theaterstate import TheaterState
from game.htn import CompoundTask, Method
class AttackShips(CompoundTask[TheaterState]):
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
for ship in state.enemy_ships:
# Ammo depots are targeted based on the needs of the front line by
# ReduceEnemyFrontLineCapacity. No reason to target them before that front
# line is active.
if ship.has_live_radar_sam:
yield [PlanAntiShip(ship)]

View File

@@ -6,6 +6,7 @@ from game.commander.tasks.compound.attackairinfrastructure import (
) )
from game.commander.tasks.compound.attackbattlepositions import AttackBattlePositions from game.commander.tasks.compound.attackbattlepositions import AttackBattlePositions
from game.commander.tasks.compound.attackbuildings import AttackBuildings from game.commander.tasks.compound.attackbuildings import AttackBuildings
from game.commander.tasks.compound.attackships import AttackShips
from game.commander.tasks.compound.capturebases import CaptureBases from game.commander.tasks.compound.capturebases import CaptureBases
from game.commander.tasks.compound.defendbases import DefendBases from game.commander.tasks.compound.defendbases import DefendBases
from game.commander.tasks.compound.degradeiads import DegradeIads from game.commander.tasks.compound.degradeiads import DegradeIads
@@ -25,10 +26,11 @@ class PlanNextAction(CompoundTask[TheaterState]):
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]: def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
yield [TheaterSupport()] yield [TheaterSupport()]
yield [ProtectAirSpace()] yield [ProtectAirSpace()]
yield [CaptureBases()]
yield [DefendBases()] yield [DefendBases()]
yield [InterdictReinforcements()] yield [InterdictReinforcements()]
yield [DegradeIads()]
yield [AttackBattlePositions()] yield [AttackBattlePositions()]
yield [CaptureBases()]
yield [AttackAirInfrastructure(self.aircraft_cold_start)] yield [AttackAirInfrastructure(self.aircraft_cold_start)]
yield [AttackBuildings()] yield [AttackBuildings()]
yield [DegradeIads()] yield [AttackShips()]

View File

@@ -372,10 +372,10 @@ class Game:
persistency.autosave(self) persistency.autosave(self)
def check_win_loss(self) -> TurnState: def check_win_loss(self) -> TurnState:
if not self.theater.player_points(): if not self.theater.player_points(state_check=True):
return TurnState.LOSS return TurnState.LOSS
if not self.theater.enemy_points(): if not self.theater.enemy_points(state_check=True):
return TurnState.WIN return TurnState.WIN
return TurnState.CONTINUE return TurnState.CONTINUE

View File

@@ -7,6 +7,8 @@ from typing import TYPE_CHECKING, Any
from dcs.countries import countries_by_name from dcs.countries import countries_by_name
from game.ato import FlightType from game.ato import FlightType
from game.ato.flightplans.formation import FormationLayout
from game.ato.flightplans.waypointbuilder import WaypointBuilder
from game.ato.packagewaypoints import PackageWaypoints from game.ato.packagewaypoints import PackageWaypoints
from game.data.doctrine import MODERN_DOCTRINE, COLDWAR_DOCTRINE, WWII_DOCTRINE from game.data.doctrine import MODERN_DOCTRINE, COLDWAR_DOCTRINE, WWII_DOCTRINE
from game.theater import ParkingType, SeasonalConditions from game.theater import ParkingType, SeasonalConditions
@@ -107,6 +109,10 @@ class Migrator:
try_set_attr(layout, "nav_from", []) try_set_attr(layout, "nav_from", [])
if f.flight_type == FlightType.CAS: if f.flight_type == FlightType.CAS:
try_set_attr(layout, "ingress", None) try_set_attr(layout, "ingress", None)
if isinstance(layout, FormationLayout):
if not layout.join and f.package.waypoints:
builder = WaypointBuilder(f, [])
layout.join = builder.join(f.package.waypoints.join)
def _update_flights(self) -> None: def _update_flights(self) -> None:
to_remove = [] to_remove = []

View File

@@ -1,5 +1,5 @@
import logging import logging
from typing import Any, Optional, Type from typing import Any, Optional, Type, List
from dcs.task import ( from dcs.task import (
AWACS, AWACS,
@@ -124,7 +124,8 @@ class AircraftBehavior:
unit.gun = 0 unit.gun = 0
group.points[0].tasks.append(OptRTBOnBingoFuel(rtb_on_bingo)) group.points[0].tasks.append(OptRTBOnBingoFuel(rtb_on_bingo))
group.points[0].tasks.append(OptJettisonEmptyTanks()) if flight.coalition.game.settings.ai_jettison_empty_tanks:
group.points[0].tasks.append(OptJettisonEmptyTanks())
# Do not restrict afterburner. # Do not restrict afterburner.
# https://forums.eagle.ru/forum/english/digital-combat-simulator/dcs-world-2-5/bugs-and-problems-ai/ai-ad/7121294-ai-stuck-at-high-aoa-after-making-sharp-turn-if-afterburner-is-restricted # https://forums.eagle.ru/forum/english/digital-combat-simulator/dcs-world-2-5/bugs-and-problems-ai/ai-ad/7121294-ai-stuck-at-high-aoa-after-making-sharp-turn-if-afterburner-is-restricted
@@ -152,7 +153,7 @@ class AircraftBehavior:
self.configure_behavior(flight, group, rtb_winchester=ammo_type) self.configure_behavior(flight, group, rtb_winchester=ammo_type)
def configure_cas(self, group: FlyingGroup[Any], flight: Flight) -> None: def configure_cas(self, group: FlyingGroup[Any], flight: Flight) -> None:
self.configure_task(flight, group, CAS, AFAC) self.configure_task(flight, group, CAS, [AFAC, AntishipStrike])
self.configure_behavior( self.configure_behavior(
flight, flight,
group, group,
@@ -170,7 +171,7 @@ class AircraftBehavior:
# Note that the only effect that the DCS task type has is in determining which # Note that the only effect that the DCS task type has is in determining which
# waypoint actions the group may perform. # waypoint actions the group may perform.
self.configure_task(flight, group, SEAD, CAS) self.configure_task(flight, group, SEAD, [CAS, AFAC, AntishipStrike])
self.configure_behavior( self.configure_behavior(
flight, flight,
group, group,
@@ -186,7 +187,7 @@ class AircraftBehavior:
# available aircraft, and F-14s are not able to be SEAD despite having TALDs. # available aircraft, and F-14s are not able to be SEAD despite having TALDs.
# https://forums.eagle.ru/topic/272112-cannot-assign-f-14-to-sead/ # https://forums.eagle.ru/topic/272112-cannot-assign-f-14-to-sead/
self.configure_task(flight, group, SEAD, CAS) self.configure_task(flight, group, SEAD, [CAS, AFAC, AntishipStrike])
self.configure_behavior( self.configure_behavior(
flight, flight,
group, group,
@@ -200,7 +201,7 @@ class AircraftBehavior:
) )
def configure_strike(self, group: FlyingGroup[Any], flight: Flight) -> None: def configure_strike(self, group: FlyingGroup[Any], flight: Flight) -> None:
self.configure_task(flight, group, GroundAttack, PinpointStrike) self.configure_task(flight, group, GroundAttack, [PinpointStrike, AFAC])
self.configure_behavior( self.configure_behavior(
flight, flight,
group, group,
@@ -211,7 +212,7 @@ class AircraftBehavior:
) )
def configure_anti_ship(self, group: FlyingGroup[Any], flight: Flight) -> None: def configure_anti_ship(self, group: FlyingGroup[Any], flight: Flight) -> None:
self.configure_task(flight, group, AntishipStrike, CAS) self.configure_task(flight, group, AntishipStrike, [CAS, AFAC, SEAD])
self.configure_behavior( self.configure_behavior(
flight, flight,
group, group,
@@ -233,7 +234,7 @@ class AircraftBehavior:
) )
def configure_oca_strike(self, group: FlyingGroup[Any], flight: Flight) -> None: def configure_oca_strike(self, group: FlyingGroup[Any], flight: Flight) -> None:
self.configure_task(flight, group, CAS) self.configure_task(flight, group, CAS, [AFAC, SEAD])
self.configure_behavior( self.configure_behavior(
flight, flight,
group, group,
@@ -345,7 +346,7 @@ class AircraftBehavior:
flight: Flight, flight: Flight,
group: FlyingGroup[Any], group: FlyingGroup[Any],
preferred_task: Type[MainTask], preferred_task: Type[MainTask],
fallback_task: Optional[Type[MainTask]] = None, fallback_tasks: Optional[List[Type[MainTask]]] = None,
) -> None: ) -> None:
ac_type = flight.unit_type.dcs_unit_type.id ac_type = flight.unit_type.dcs_unit_type.id
@@ -358,15 +359,22 @@ class AircraftBehavior:
if preferred_task in flight.unit_type.dcs_unit_type.tasks: if preferred_task in flight.unit_type.dcs_unit_type.tasks:
group.task = preferred_task.name group.task = preferred_task.name
elif fallback_task and fallback_task in flight.unit_type.dcs_unit_type.tasks: elif fallback_tasks:
group.task = fallback_task.name for task in fallback_tasks:
if task in flight.unit_type.dcs_unit_type.tasks:
group.task = task.name
return
elif flight.unit_type.dcs_unit_type.task_default and preferred_task == Nothing: elif flight.unit_type.dcs_unit_type.task_default and preferred_task == Nothing:
group.task = flight.unit_type.dcs_unit_type.task_default.name group.task = flight.unit_type.dcs_unit_type.task_default.name
logging.warning( logging.warning(
f"{ac_type} is not capable of 'Nothing', using default task '{group.task}'" f"{ac_type} is not capable of 'Nothing', using default task '{group.task}'"
) )
else: else:
fallback_part = f" nor {fallback_task.name}" if fallback_task else "" fallback_part = (
f" nor any of the following fall-back tasks: {[task.name for task in fallback_tasks]}"
if fallback_tasks
else ""
)
raise RuntimeError( raise RuntimeError(
f"{ac_type} is neither capable of {preferred_task.name}" f"{ac_type} is neither capable of {preferred_task.name}"
f"{fallback_part}. Can't generate {flight.flight_type} flight." f"{fallback_part}. Can't generate {flight.flight_type} flight."

View File

@@ -243,6 +243,8 @@ class FlightGroupSpawner:
speed=speed.kph, speed=speed.kph,
maintask=None, maintask=None,
group_size=self.flight.count, group_size=self.flight.count,
callsign_name=self.flight.callsign.name if self.flight.callsign else None,
callsign_nr=self.flight.callsign.nr if self.flight.callsign else None,
) )
group.points[0].alt_type = alt_type group.points[0].alt_type = alt_type

View File

@@ -15,9 +15,9 @@ class AntiShipIngressBuilder(PydcsWaypointBuilder):
target = self.package.target target = self.package.target
if isinstance(target, NavalControlPoint): if isinstance(target, NavalControlPoint):
carrier_name = target.get_carrier_group_name() carrier_tgo = target.ground_objects[0]
if carrier_name: for g in carrier_tgo.groups:
group_names.append(carrier_name) group_names.append(g.group_name)
elif isinstance(target, TheaterGroundObject): elif isinstance(target, TheaterGroundObject):
for group in target.groups: for group in target.groups:
group_names.append(group.group_name) group_names.append(group.group_name)

View File

@@ -11,7 +11,8 @@ from .pydcswaypointbuilder import PydcsWaypointBuilder
class BaiIngressBuilder(PydcsWaypointBuilder): class BaiIngressBuilder(PydcsWaypointBuilder):
def add_tasks(self, waypoint: MovingPoint) -> None: def add_tasks(self, waypoint: MovingPoint) -> None:
self.register_special_ingress_points() self.register_special_ingress_points()
waypoint.tasks.append(OptFormation.trail_open()) if not self.flight.is_helo:
waypoint.tasks.append(OptFormation.trail_open())
# TODO: Add common "UnitGroupTarget" base type. # TODO: Add common "UnitGroupTarget" base type.
group_names = [] group_names = []
target = self.package.target target = self.package.target

View File

@@ -94,7 +94,11 @@ class JoinPointBuilder(PydcsWaypointBuilder):
max_dist: float = 30.0, max_dist: float = 30.0,
vertical_spacing: float = 2000.0, vertical_spacing: float = 2000.0,
) -> None: ) -> None:
waypoint.tasks.append(OptROE(value=OptROE.Values.OpenFire)) if self.flight.is_helo:
# Make helicopters a bit more aggressive
waypoint.tasks.append(OptROE(value=OptROE.Values.OpenFireWeaponFree))
else:
waypoint.tasks.append(OptROE(value=OptROE.Values.OpenFire))
rx = (random.random() + 0.1) * 333 rx = (random.random() + 0.1) * 333
ry = feet(vertical_spacing).meters ry = feet(vertical_spacing).meters

View File

@@ -4,6 +4,7 @@ from dcs.task import (
ControlledTask, ControlledTask,
EngageTargets, EngageTargets,
Targets, Targets,
OptROE,
) )
from game.utils import nautical_miles from game.utils import nautical_miles
@@ -13,6 +14,7 @@ from .pydcswaypointbuilder import PydcsWaypointBuilder
class SeadSweepIngressBuilder(PydcsWaypointBuilder): class SeadSweepIngressBuilder(PydcsWaypointBuilder):
def add_tasks(self, waypoint: MovingPoint) -> None: def add_tasks(self, waypoint: MovingPoint) -> None:
self.register_special_ingress_points() self.register_special_ingress_points()
waypoint.tasks.append(OptROE(value=OptROE.Values.OpenFireWeaponFree))
# Preemptively use ECM to better avoid getting swatted. # Preemptively use ECM to better avoid getting swatted.
ecm_option = OptECMUsing(value=OptECMUsing.Values.UseIfDetectedLockByRadar) ecm_option = OptECMUsing(value=OptECMUsing.Values.UseIfDetectedLockByRadar)
waypoint.tasks.append(ecm_option) waypoint.tasks.append(ecm_option)
@@ -26,7 +28,7 @@ class SeadSweepIngressBuilder(PydcsWaypointBuilder):
self.flight.coalition.game.settings.sead_sweep_engagement_range_distance self.flight.coalition.game.settings.sead_sweep_engagement_range_distance
).meters ).meters
), ),
targets=[Targets.All.GroundUnits.AirDefence.AAA.SAMRelated], targets=[Targets.All.GroundUnits.AirDefence],
) )
) )
) )

View File

@@ -7,6 +7,7 @@ from dcs.task import (
SwitchWaypoint, SwitchWaypoint,
) )
from game.utils import knots
from .pydcswaypointbuilder import PydcsWaypointBuilder from .pydcswaypointbuilder import PydcsWaypointBuilder
@@ -29,10 +30,12 @@ class SplitPointBuilder(PydcsWaypointBuilder):
waypoint.tasks.append(OptFormation.rotary_wedge()) waypoint.tasks.append(OptFormation.rotary_wedge())
else: else:
waypoint.tasks.append(OptFormation.finger_four_close()) waypoint.tasks.append(OptFormation.finger_four_close())
if not self.flight.is_helo: waypoint.speed_locked = True
waypoint.speed_locked = True waypoint.ETA_locked = False
if self.flight.is_helo:
waypoint.speed = knots(100).meters_per_second
else:
waypoint.speed = self.flight.coalition.doctrine.rtb_speed.meters_per_second waypoint.speed = self.flight.coalition.doctrine.rtb_speed.meters_per_second
waypoint.ETA_locked = False
if self.flight is self.package.primary_flight: if self.flight is self.package.primary_flight:
script = RunScript( script = RunScript(
f'trigger.action.setUserFlag("split-{id(self.package)}", true)' f'trigger.action.setUserFlag("split-{id(self.package)}", true)'

View File

@@ -1,7 +1,7 @@
import logging import logging
from dcs.point import MovingPoint from dcs.point import MovingPoint
from dcs.task import EngageTargets, OptFormation, Targets from dcs.task import EngageTargets, OptFormation, Targets, OptROE
from game.ato.flightplans.sweep import SweepFlightPlan from game.ato.flightplans.sweep import SweepFlightPlan
from game.utils import nautical_miles from game.utils import nautical_miles
@@ -16,6 +16,8 @@ class SweepIngressBuilder(PydcsWaypointBuilder):
else: else:
waypoint.tasks.append(OptFormation.spread_four_open()) waypoint.tasks.append(OptFormation.spread_four_open())
waypoint.tasks.append(OptROE(value=OptROE.Values.OpenFireWeaponFree))
if not isinstance(self.flight.flight_plan, SweepFlightPlan): if not isinstance(self.flight.flight_plan, SweepFlightPlan):
flight_plan_type = self.flight.flight_plan.__class__.__name__ flight_plan_type = self.flight.flight_plan.__class__.__name__
logging.error( logging.error(

View File

@@ -73,7 +73,7 @@ class VisualsGenerator:
self.game = game self.game = game
def _generate_frontline_smokes(self) -> None: def _generate_frontline_smokes(self) -> None:
country = self.mission.country(self.game.red.faction.country.name) country = list(self.mission.coalition["neutrals"].countries.values())[0]
for front_line in self.game.theater.conflicts(): for front_line in self.game.theater.conflicts():
from_cp = front_line.blue_cp from_cp = front_line.blue_cp
to_cp = front_line.red_cp to_cp = front_line.red_cp

View File

@@ -16,6 +16,8 @@ if TYPE_CHECKING:
from game import Game from game import Game
_dcs_saved_game_folder: Optional[str] = None _dcs_saved_game_folder: Optional[str] = None
_prefer_liberation_payloads: bool = False
_server_port: int = 16880
# fmt: off # fmt: off
@@ -61,6 +63,8 @@ class MigrationUnpickler(pickle.Unpickler):
return dcs.terrain.falklands.airports.Hipico_Flying_Club return dcs.terrain.falklands.airports.Hipico_Flying_Club
if name in ["SaveManager", "SaveGameBundle"]: if name in ["SaveManager", "SaveGameBundle"]:
return DummyObject return DummyObject
if name == "CaletaTortel":
return dcs.terrain.falklands.airports.Caleta_Tortel_Airport
if module == "pydcs_extensions.f4b.f4b": if module == "pydcs_extensions.f4b.f4b":
return pydcs_extensions.f4 return pydcs_extensions.f4
if module == "pydcs_extensions.irondome.irondome": if module == "pydcs_extensions.irondome.irondome":
@@ -74,9 +78,13 @@ class MigrationUnpickler(pickle.Unpickler):
# fmt: on # fmt: on
def setup(user_folder: str) -> None: def setup(user_folder: str, prefer_liberation_payloads: bool, port: int) -> None:
global _dcs_saved_game_folder global _dcs_saved_game_folder
global _prefer_liberation_payloads
global _server_port
_dcs_saved_game_folder = user_folder _dcs_saved_game_folder = user_folder
_prefer_liberation_payloads = prefer_liberation_payloads
_server_port = port
if not save_dir().exists(): if not save_dir().exists():
save_dir().mkdir(parents=True) save_dir().mkdir(parents=True)
@@ -110,6 +118,11 @@ def payloads_dir(backup: bool = False) -> Path:
return payloads return payloads
def prefer_liberation_payloads() -> bool:
global _prefer_liberation_payloads
return _prefer_liberation_payloads
def user_custom_weapon_injections_dir() -> Path: def user_custom_weapon_injections_dir() -> Path:
return base_path() / "Retribution" / "WeaponInjections" return base_path() / "Retribution" / "WeaponInjections"
@@ -118,6 +131,11 @@ def save_dir() -> Path:
return base_path() / "Retribution" / "Saves" return base_path() / "Retribution" / "Saves"
def server_port() -> int:
global _server_port
return _server_port
def _temporary_save_file() -> str: def _temporary_save_file() -> str:
return str(save_dir() / "tmpsave.retribution") return str(save_dir() / "tmpsave.retribution")

View File

@@ -281,9 +281,45 @@ class PretenseAircraftGenerator:
num_of_bai = 0 num_of_bai = 0
num_of_strike = 0 num_of_strike = 0
num_of_cap = 0 num_of_cap = 0
sead_tasks = [FlightType.SEAD, FlightType.SEAD_SWEEP, FlightType.SEAD_ESCORT]
strike_tasks = [
FlightType.STRIKE,
FlightType.OCA_RUNWAY,
FlightType.OCA_AIRCRAFT,
]
patrol_tasks = [
FlightType.BARCAP,
FlightType.TARCAP,
FlightType.ESCORT,
FlightType.INTERCEPTION,
]
sead_capable_cp = False
cas_capable_cp = False
bai_capable_cp = False
strike_capable_cp = False
patrol_capable_cp = False
# First check what are the capabilities of the squadrons on this CP
for squadron in cp.squadrons:
for task in sead_tasks:
if task in squadron.auto_assignable_mission_types:
sead_capable_cp = True
for task in strike_tasks:
if task in squadron.auto_assignable_mission_types:
if not squadron.aircraft.helicopter:
strike_capable_cp = True
for task in patrol_tasks:
if task in squadron.auto_assignable_mission_types:
if not squadron.aircraft.helicopter:
patrol_capable_cp = True
if FlightType.CAS in squadron.auto_assignable_mission_types:
cas_capable_cp = True
if FlightType.BAI in squadron.auto_assignable_mission_types:
bai_capable_cp = True
random_squadron_list = list(cp.squadrons) random_squadron_list = list(cp.squadrons)
random.shuffle(random_squadron_list) random.shuffle(random_squadron_list)
# Then plan transports, AEWC and tankers
for squadron in random_squadron_list: for squadron in random_squadron_list:
# Intentionally don't spawn anything at OffMapSpawns in Pretense # Intentionally don't spawn anything at OffMapSpawns in Pretense
if isinstance(squadron.location, OffMapSpawn): if isinstance(squadron.location, OffMapSpawn):
@@ -291,14 +327,6 @@ class PretenseAircraftGenerator:
if cp.coalition != squadron.coalition: if cp.coalition != squadron.coalition:
continue continue
squadron.owned_aircraft += (
self.game.settings.pretense_ai_aircraft_per_flight
)
squadron.untasked_aircraft += (
self.game.settings.pretense_ai_aircraft_per_flight
)
squadron.populate_for_turn_0(False)
package = Package(cp, squadron.flight_db, auto_asap=False)
mission_types = squadron.auto_assignable_mission_types mission_types = squadron.auto_assignable_mission_types
aircraft_per_flight = 1 aircraft_per_flight = 1
if squadron.aircraft.helicopter and ( if squadron.aircraft.helicopter and (
@@ -311,52 +339,6 @@ class PretenseAircraftGenerator:
or FlightType.AIR_ASSAULT in mission_types or FlightType.AIR_ASSAULT in mission_types
): ):
flight_type = FlightType.TRANSPORT flight_type = FlightType.TRANSPORT
elif (
FlightType.SEAD in mission_types
or FlightType.SEAD_SWEEP in mission_types
or FlightType.SEAD_ESCORT in mission_types
) and num_of_sead < self.game.settings.pretense_sead_flights_per_cp:
flight_type = FlightType.SEAD
num_of_sead += 1
aircraft_per_flight = self.game.settings.pretense_ai_aircraft_per_flight
elif (
FlightType.DEAD in mission_types
and num_of_sead < self.game.settings.pretense_sead_flights_per_cp
):
flight_type = FlightType.DEAD
num_of_sead += 1
aircraft_per_flight = self.game.settings.pretense_ai_aircraft_per_flight
elif (
(squadron.aircraft.helicopter and (FlightType.ESCORT in mission_types))
or (FlightType.CAS in mission_types)
and num_of_cas < self.game.settings.pretense_cas_flights_per_cp
):
flight_type = FlightType.CAS
num_of_cas += 1
aircraft_per_flight = self.game.settings.pretense_ai_aircraft_per_flight
elif (
FlightType.BAI in mission_types
) and num_of_bai < self.game.settings.pretense_bai_flights_per_cp:
flight_type = FlightType.BAI
num_of_bai += 1
aircraft_per_flight = self.game.settings.pretense_ai_aircraft_per_flight
elif (
FlightType.STRIKE in mission_types
or FlightType.OCA_RUNWAY in mission_types
or FlightType.OCA_AIRCRAFT in mission_types
) and num_of_strike < self.game.settings.pretense_strike_flights_per_cp:
flight_type = FlightType.STRIKE
num_of_strike += 1
aircraft_per_flight = self.game.settings.pretense_ai_aircraft_per_flight
elif (
FlightType.BARCAP in mission_types
or FlightType.TARCAP in mission_types
or FlightType.ESCORT in mission_types
or FlightType.INTERCEPTION in mission_types
) and num_of_cap < self.game.settings.pretense_barcap_flights_per_cp:
flight_type = FlightType.BARCAP
num_of_cap += 1
aircraft_per_flight = self.game.settings.pretense_ai_aircraft_per_flight
elif FlightType.AEWC in mission_types: elif FlightType.AEWC in mission_types:
flight_type = FlightType.AEWC flight_type = FlightType.AEWC
aircraft_per_flight = PRETENSE_AI_AWACS_PER_FLIGHT aircraft_per_flight = PRETENSE_AI_AWACS_PER_FLIGHT
@@ -366,37 +348,181 @@ class PretenseAircraftGenerator:
else: else:
continue continue
if flight_type == FlightType.TRANSPORT: self.generate_pretense_flight(
flight = Flight( ato, cp, squadron, aircraft_per_flight, flight_type
package,
squadron,
aircraft_per_flight,
FlightType.PRETENSE_CARGO,
StartType.IN_FLIGHT,
divert=cp,
)
package.add_flight(flight)
flight.state = Navigating(flight, self.game.settings, waypoint_index=1)
else:
flight = Flight(
package,
squadron,
aircraft_per_flight,
flight_type,
StartType.COLD,
divert=cp,
)
package.add_flight(flight)
flight.state = WaitingForStart(
flight, self.game.settings, self.game.conditions.start_time
)
print(
f"Generated flight for {flight_type} flying {squadron.aircraft.display_name} at {squadron.location.name}"
) )
ato.add_package(package) # Then plan SEAD and DEAD, if capable
if sead_capable_cp:
while num_of_sead < self.game.settings.pretense_sead_flights_per_cp:
for squadron in random_squadron_list:
# Intentionally don't spawn anything at OffMapSpawns in Pretense
if isinstance(squadron.location, OffMapSpawn):
continue
if cp.coalition != squadron.coalition:
continue
mission_types = squadron.auto_assignable_mission_types
if (
(
FlightType.SEAD in mission_types
or FlightType.SEAD_SWEEP in mission_types
or FlightType.SEAD_ESCORT in mission_types
)
and num_of_sead
< self.game.settings.pretense_sead_flights_per_cp
):
flight_type = FlightType.SEAD
num_of_sead += 1
aircraft_per_flight = (
self.game.settings.pretense_ai_aircraft_per_flight
)
elif (
FlightType.DEAD in mission_types
and num_of_sead
< self.game.settings.pretense_sead_flights_per_cp
):
flight_type = FlightType.DEAD
num_of_sead += 1
aircraft_per_flight = (
self.game.settings.pretense_ai_aircraft_per_flight
)
else:
continue
self.generate_pretense_flight(
ato, cp, squadron, aircraft_per_flight, flight_type
)
# Then plan Strike, if capable
if strike_capable_cp:
while num_of_strike < self.game.settings.pretense_strike_flights_per_cp:
for squadron in random_squadron_list:
# Intentionally don't spawn anything at OffMapSpawns in Pretense
if isinstance(squadron.location, OffMapSpawn):
continue
if cp.coalition != squadron.coalition:
continue
mission_types = squadron.auto_assignable_mission_types
for task in strike_tasks:
if task in mission_types and not squadron.aircraft.helicopter:
flight_type = FlightType.STRIKE
num_of_strike += 1
aircraft_per_flight = (
self.game.settings.pretense_ai_aircraft_per_flight
)
self.generate_pretense_flight(
ato, cp, squadron, aircraft_per_flight, flight_type
)
break
# Then plan air-to-air, if capable
if patrol_capable_cp:
while num_of_cap < self.game.settings.pretense_barcap_flights_per_cp:
for squadron in random_squadron_list:
# Intentionally don't spawn anything at OffMapSpawns in Pretense
if isinstance(squadron.location, OffMapSpawn):
continue
if cp.coalition != squadron.coalition:
continue
mission_types = squadron.auto_assignable_mission_types
for task in patrol_tasks:
if task in mission_types and not squadron.aircraft.helicopter:
flight_type = FlightType.BARCAP
num_of_cap += 1
aircraft_per_flight = (
self.game.settings.pretense_ai_aircraft_per_flight
)
self.generate_pretense_flight(
ato, cp, squadron, aircraft_per_flight, flight_type
)
break
# Then plan CAS, if capable
if cas_capable_cp:
while num_of_cas < self.game.settings.pretense_cas_flights_per_cp:
for squadron in random_squadron_list:
# Intentionally don't spawn anything at OffMapSpawns in Pretense
if isinstance(squadron.location, OffMapSpawn):
continue
if cp.coalition != squadron.coalition:
continue
mission_types = squadron.auto_assignable_mission_types
if (
squadron.aircraft.helicopter
and (FlightType.ESCORT in mission_types)
) or (FlightType.CAS in mission_types):
flight_type = FlightType.CAS
num_of_cas += 1
aircraft_per_flight = (
self.game.settings.pretense_ai_aircraft_per_flight
)
self.generate_pretense_flight(
ato, cp, squadron, aircraft_per_flight, flight_type
)
# And finally, plan BAI, if capable
if bai_capable_cp:
while num_of_bai < self.game.settings.pretense_bai_flights_per_cp:
for squadron in random_squadron_list:
# Intentionally don't spawn anything at OffMapSpawns in Pretense
if isinstance(squadron.location, OffMapSpawn):
continue
if cp.coalition != squadron.coalition:
continue
mission_types = squadron.auto_assignable_mission_types
if FlightType.BAI in mission_types:
flight_type = FlightType.BAI
num_of_bai += 1
aircraft_per_flight = (
self.game.settings.pretense_ai_aircraft_per_flight
)
self.generate_pretense_flight(
ato, cp, squadron, aircraft_per_flight, flight_type
)
return return
def generate_pretense_flight(
self,
ato: AirTaskingOrder,
cp: ControlPoint,
squadron: Squadron,
aircraft_per_flight: int,
flight_type: FlightType,
) -> None:
squadron.owned_aircraft += self.game.settings.pretense_ai_aircraft_per_flight
squadron.untasked_aircraft += self.game.settings.pretense_ai_aircraft_per_flight
squadron.populate_for_turn_0(False)
package = Package(cp, squadron.flight_db, auto_asap=False)
if flight_type == FlightType.TRANSPORT:
flight = Flight(
package,
squadron,
aircraft_per_flight,
FlightType.PRETENSE_CARGO,
StartType.IN_FLIGHT,
divert=cp,
)
package.add_flight(flight)
flight.state = Navigating(flight, self.game.settings, waypoint_index=1)
else:
flight = Flight(
package,
squadron,
aircraft_per_flight,
flight_type,
StartType.COLD,
divert=cp,
)
package.add_flight(flight)
flight.state = WaitingForStart(
flight, self.game.settings, self.game.conditions.start_time
)
print(
f"Generated flight for {flight_type} flying {squadron.aircraft.display_name} at {squadron.location.name}"
)
ato.add_package(package)
def generate_pretense_aircraft_for_other_side( def generate_pretense_aircraft_for_other_side(
self, cp: ControlPoint, coalition: Coalition, ato: AirTaskingOrder self, cp: ControlPoint, coalition: Coalition, ato: AirTaskingOrder
) -> None: ) -> None:

View File

@@ -219,13 +219,6 @@ class ProcurementAi:
budget, fulfilled = self.fulfill_aircraft_request( budget, fulfilled = self.fulfill_aircraft_request(
squadrons, request.number, budget squadrons, request.number, budget
) )
if not fulfilled:
# The request was not fulfilled because we could not afford any suitable
# aircraft. Rather than continuing, which could proceed to buy tons of
# cheap escorts that will never allow us to plan a strike package, stop
# buying so we can save the budget until a turn where we *can* afford to
# fill the package.
break
return budget return budget
@property @property

View File

@@ -2,6 +2,7 @@ import time
from collections.abc import Iterator from collections.abc import Iterator
from contextlib import contextmanager from contextlib import contextmanager
from threading import Thread from threading import Thread
from typing import Optional
import uvicorn import uvicorn
from uvicorn import Config from uvicorn import Config
@@ -13,12 +14,13 @@ from game.sim import GameUpdateEvents
class Server(uvicorn.Server): class Server(uvicorn.Server):
def __init__(self) -> None: def __init__(self, port: Optional[int]) -> None:
settings = ServerSettings.get(port)
super().__init__( super().__init__(
Config( Config(
app=app, app=app,
host=ServerSettings.get().server_bind_address, host=settings.server_bind_address,
port=ServerSettings.get().server_port, port=settings.server_port,
# Configured explicitly with default_logging.yaml or logging.yaml. # Configured explicitly with default_logging.yaml or logging.yaml.
log_config=None, log_config=None,
) )

View File

@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
from functools import lru_cache from functools import lru_cache
from typing import Optional
from pydantic_settings import BaseSettings from pydantic_settings import BaseSettings
@@ -30,5 +31,5 @@ class ServerSettings(BaseSettings):
@classmethod @classmethod
@lru_cache @lru_cache
def get(cls) -> ServerSettings: def get(cls, port: Optional[int] = server_port) -> ServerSettings:
return cls() return cls(server_port=port)

View File

@@ -193,11 +193,12 @@ class Settings:
default=False, default=False,
detail=( detail=(
"If checked, squadrons with a primary task matching the mission will be " "If checked, squadrons with a primary task matching the mission will be "
"preferred even if there is a closer squadron capable of the mission as a" "preferred even if there is a closer squadron capable of the mission as a "
"secondary task. Expect longer flights, but squadrons will be more often " "secondary task. Expect longer flights, but squadrons will be more often "
"assigned to their primary task." "assigned to their primary task."
), ),
) )
# CAMPAIGN DOCTRINE
autoplan_tankers_for_strike: bool = boolean_option( autoplan_tankers_for_strike: bool = boolean_option(
"Auto-planner plans refueling flights for Strike packages", "Auto-planner plans refueling flights for Strike packages",
page=CAMPAIGN_DOCTRINE_PAGE, page=CAMPAIGN_DOCTRINE_PAGE,
@@ -283,6 +284,23 @@ class Settings:
"setting to avoid the AI flying into the terrain." "setting to avoid the AI flying into the terrain."
), ),
) )
atflir_autoswap: bool = boolean_option(
"Auto-swap ATFLIR to LITENING",
page=CAMPAIGN_DOCTRINE_PAGE,
section=GENERAL_SECTION,
default=True,
detail=(
"Automatically swaps ATFLIR to LITENING pod for newly generated land-based F-18 flights "
"without having to change the payload. <u>Takes effect after current turn!</u>"
),
)
ai_jettison_empty_tanks: bool = boolean_option(
"Enable AI empty fuel tank jettison",
page=CAMPAIGN_DOCTRINE_PAGE,
section=GENERAL_SECTION,
default=False,
detail=("AI will jettison their fuel tanks as soon as they're empty."),
)
airbase_threat_range: int = bounded_int_option( airbase_threat_range: int = bounded_int_option(
"Airbase threat range (nmi)", "Airbase threat range (nmi)",
page=CAMPAIGN_DOCTRINE_PAGE, page=CAMPAIGN_DOCTRINE_PAGE,
@@ -291,7 +309,7 @@ class Settings:
min=0, min=0,
max=300, max=300,
detail=( detail=(
"Will impact both defensive (BARCAP) and offensive flights. Also has a performance impact," "Will impact both defensive (BARCAP) and offensive flights. Also has a performance impact, "
"lower threat range generally means less BARCAPs are planned." "lower threat range generally means less BARCAPs are planned."
), ),
) )
@@ -339,7 +357,7 @@ class Settings:
min=0, min=0,
max=300, max=300,
detail=( detail=(
"How far, at minimum, will AEW&C racetracks be planned" "How far, at minimum, will AEW&C racetracks be planned "
"to known threat zones." "to known threat zones."
), ),
) )
@@ -375,7 +393,7 @@ class Settings:
default=True, default=True,
detail=( detail=(
"If set, squadrons will be limited to a maximum number of pilots and dead " "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" "pilots will replenish at a fixed rate, each defined with the settings "
"below. Auto-purchase may buy aircraft for which there are no pilots" "below. Auto-purchase may buy aircraft for which there are no pilots"
"available, so this feature is still a work-in-progress." "available, so this feature is still a work-in-progress."
), ),
@@ -573,7 +591,7 @@ class Settings:
default=35, default=35,
min=0, min=0,
max=100, max=100,
detail="See 2-ship weight factor (WF2)", detail="See 2-ship weight factor (WF3)",
) )
fpa_4ship_weight: int = bounded_int_option( fpa_4ship_weight: int = bounded_int_option(
"4-ship weight factor (WF4)", "4-ship weight factor (WF4)",
@@ -582,7 +600,7 @@ class Settings:
default=15, default=15,
min=0, min=0,
max=100, max=100,
detail="See 2-ship weight factor (WF2)", detail="See 2-ship weight factor (WF4)",
) )
# Mission Generator # Mission Generator
@@ -675,16 +693,6 @@ class Settings:
"targets available for OCA/Aircraft missions." "targets available for OCA/Aircraft missions."
), ),
) )
atflir_autoswap: bool = boolean_option(
"Auto-swap ATFLIR to LITENING",
MISSION_GENERATOR_PAGE,
GAMEPLAY_SECTION,
default=True,
detail=(
"Automatically swaps ATFLIR to LITENING pod for newly generated land-based F-18 flights "
"without having to change the payload. <u>Takes effect after current turn!</u>"
),
)
default_start_type: StartType = choices_option( default_start_type: StartType = choices_option(
"Default start type for AI aircraft", "Default start type for AI aircraft",
page=MISSION_GENERATOR_PAGE, page=MISSION_GENERATOR_PAGE,
@@ -777,7 +785,7 @@ class Settings:
GAMEPLAY_SECTION, GAMEPLAY_SECTION,
default=False, default=False,
detail=( detail=(
"If enabled, AI can use roadbases or airbases which only have ground spawns." "If enabled, AI can use roadbases or airbases which only have ground spawns. "
"AI will always air-start from these bases (due to DCS limitation)." "AI will always air-start from these bases (due to DCS limitation)."
), ),
) )
@@ -787,7 +795,7 @@ class Settings:
GAMEPLAY_SECTION, GAMEPLAY_SECTION,
default=True, default=True,
detail=( detail=(
"Can be used to remove lightposts and other obstacles from roadbase runways." "Can be used to remove lightposts and other obstacles from roadbase runways. "
"Might not work in DCS multiplayer." "Might not work in DCS multiplayer."
), ),
) )

View File

@@ -51,6 +51,7 @@ class AirWing:
size: int, size: int,
heli: bool, heli: bool,
this_turn: bool, this_turn: bool,
preferred_type: Optional[AircraftType] = None,
) -> list[Squadron]: ) -> list[Squadron]:
airfield_cache = ObjectiveDistanceCache.get_closest_airfields(location) airfield_cache = ObjectiveDistanceCache.get_closest_airfields(location)
best_aircraft = AircraftType.priority_list_for_task(task) best_aircraft = AircraftType.priority_list_for_task(task)
@@ -59,7 +60,13 @@ class AirWing:
if control_point.captured != self.player: if control_point.captured != self.player:
continue continue
capable_at_base = [] capable_at_base = []
for squadron in control_point.squadrons: squadrons = [
s
for s in control_point.squadrons
if not preferred_type
or s.aircraft.variant_id == preferred_type.variant_id
]
for squadron in squadrons:
if squadron.can_auto_assign_mission( if squadron.can_auto_assign_mission(
location, task, size, heli, this_turn location, task, size, heli, this_turn
): ):
@@ -92,8 +99,11 @@ class AirWing:
size: int, size: int,
heli: bool, heli: bool,
this_turn: bool, this_turn: bool,
preferred_type: Optional[AircraftType] = None,
) -> Optional[Squadron]: ) -> Optional[Squadron]:
for squadron in self.best_squadrons_for(location, task, size, heli, this_turn): for squadron in self.best_squadrons_for(
location, task, size, heli, this_turn, preferred_type
):
return squadron return squadron
return None return None

View File

@@ -120,20 +120,27 @@ class ConflictTheater:
) )
return new_point return new_point
def control_points_for(self, player: bool) -> Iterator[ControlPoint]: def control_points_for(
self, player: bool, state_check: bool = False
) -> Iterator[ControlPoint]:
for point in self.controlpoints: for point in self.controlpoints:
if point.captured == player: if point.captured == player:
yield point if not state_check:
yield point
elif point.is_carrier and point.runway_is_operational():
yield point
elif not point.is_carrier:
yield point
def player_points(self) -> List[ControlPoint]: def player_points(self, state_check: bool = False) -> List[ControlPoint]:
return list(self.control_points_for(player=True)) return list(self.control_points_for(player=True, state_check=state_check))
def conflicts(self) -> Iterator[FrontLine]: def conflicts(self) -> Iterator[FrontLine]:
for cp in self.player_points(): for cp in self.player_points():
yield from cp.front_lines.values() yield from cp.front_lines.values()
def enemy_points(self) -> List[ControlPoint]: def enemy_points(self, state_check: bool = False) -> List[ControlPoint]:
return list(self.control_points_for(player=False)) return list(self.control_points_for(player=False, state_check=state_check))
def closest_control_point( def closest_control_point(
self, point: Point, allow_naval: bool = False self, point: Point, allow_naval: bool = False

View File

@@ -321,6 +321,13 @@ class AirliftPlanner:
if self.package.flights: if self.package.flights:
self.package.set_tot_asap(now) self.package.set_tot_asap(now)
self.game.ato_for(self.for_player).add_package(self.package) self.game.ato_for(self.for_player).add_package(self.package)
from game.server import EventStream
from game.sim import GameUpdateEvents
events = GameUpdateEvents()
for f in self.package.flights:
events = events.new_flight(f)
EventStream.put_nowait(events)
def create_airlift_flight(self, squadron: Squadron) -> int: def create_airlift_flight(self, squadron: Squadron) -> int:
available_aircraft = squadron.untasked_aircraft available_aircraft = squadron.untasked_aircraft
@@ -606,6 +613,8 @@ class PendingTransfers:
== TransitConnection.Shipping == TransitConnection.Shipping
): ):
return self.cargo_ships.add(transfer, next_stop) return self.cargo_ships.add(transfer, next_stop)
else:
next_stop = transfer.destination
AirliftPlanner(self.game, transfer, next_stop).create_package_for_airlift(now) AirliftPlanner(self.game, transfer, next_stop).create_package_for_airlift(now)
def new_transfer(self, transfer: TransferOrder, now: datetime) -> None: def new_transfer(self, transfer: TransferOrder, now: datetime) -> None:

View File

@@ -11,6 +11,8 @@ from game import persistency
global __dcs_saved_game_directory global __dcs_saved_game_directory
global __dcs_installation_directory global __dcs_installation_directory
global __last_save_file global __last_save_file
global __prefer_liberation_payloads
global __server_port
USER_PATH = Path(os.environ["LOCALAPPDATA"]) / "DCSRetribution" USER_PATH = Path(os.environ["LOCALAPPDATA"]) / "DCSRetribution"
@@ -23,6 +25,8 @@ def init():
global __dcs_installation_directory global __dcs_installation_directory
global __last_save_file global __last_save_file
global __ignore_empty_install_directory global __ignore_empty_install_directory
global __prefer_liberation_payloads
global __server_port
if PREFERENCES_PATH.exists(): if PREFERENCES_PATH.exists():
try: try:
@@ -35,16 +39,24 @@ def init():
__ignore_empty_install_directory = pref_data.get( __ignore_empty_install_directory = pref_data.get(
"ignore_empty_install_directory", False "ignore_empty_install_directory", False
) )
__prefer_liberation_payloads = pref_data.get(
"prefer_liberation_payloads", False
)
__server_port = pref_data.get("server_port", 16880)
is_first_start = False is_first_start = False
except (KeyError, json.JSONDecodeError): except (KeyError, json.JSONDecodeError):
__dcs_saved_game_directory = "" __dcs_saved_game_directory = ""
__dcs_installation_directory = "" __dcs_installation_directory = ""
__last_save_file = "" __last_save_file = ""
__ignore_empty_install_directory = False __ignore_empty_install_directory = False
__prefer_liberation_payloads = False
__server_port = 16880
is_first_start = True is_first_start = True
else: else:
__last_save_file = "" __last_save_file = ""
__ignore_empty_install_directory = False __ignore_empty_install_directory = False
__prefer_liberation_payloads = False
__server_port = 16880
try: try:
__dcs_saved_game_directory = ( __dcs_saved_game_directory = (
dcs.installation.get_dcs_saved_games_directory() dcs.installation.get_dcs_saved_games_directory()
@@ -61,16 +73,22 @@ def init():
__dcs_installation_directory = "" __dcs_installation_directory = ""
is_first_start = True is_first_start = True
persistency.setup(__dcs_saved_game_directory) persistency.setup(
__dcs_saved_game_directory, __prefer_liberation_payloads, __server_port
)
return is_first_start return is_first_start
def setup(saved_game_dir, install_dir): def setup(saved_game_dir, install_dir, prefer_liberation_payloads, port):
global __dcs_saved_game_directory global __dcs_saved_game_directory
global __dcs_installation_directory global __dcs_installation_directory
global __prefer_liberation_payloads
global __server_port
__dcs_saved_game_directory = saved_game_dir __dcs_saved_game_directory = saved_game_dir
__dcs_installation_directory = install_dir __dcs_installation_directory = install_dir
persistency.setup(__dcs_saved_game_directory) __prefer_liberation_payloads = prefer_liberation_payloads
__server_port = port
persistency.setup(saved_game_dir, prefer_liberation_payloads, port)
def setup_last_save_file(last_save_file): def setup_last_save_file(last_save_file):
@@ -83,11 +101,14 @@ def save_config():
global __dcs_installation_directory global __dcs_installation_directory
global __last_save_file global __last_save_file
global __ignore_empty_install_directory global __ignore_empty_install_directory
global __server_port
pref_data = { pref_data = {
"saved_game_dir": __dcs_saved_game_directory, "saved_game_dir": __dcs_saved_game_directory,
"dcs_install_dir": __dcs_installation_directory, "dcs_install_dir": __dcs_installation_directory,
"last_save_file": __last_save_file, "last_save_file": __last_save_file,
"ignore_empty_install_directory": __ignore_empty_install_directory, "ignore_empty_install_directory": __ignore_empty_install_directory,
"prefer_liberation_payloads": __prefer_liberation_payloads,
"server_port": __server_port,
} }
PREFERENCES_PATH.parent.mkdir(exist_ok=True, parents=True) PREFERENCES_PATH.parent.mkdir(exist_ok=True, parents=True)
with PREFERENCES_PATH.open("w") as prefs: with PREFERENCES_PATH.open("w") as prefs:
@@ -104,6 +125,16 @@ def get_saved_game_dir():
return __dcs_saved_game_directory return __dcs_saved_game_directory
def prefer_liberation_payloads():
global __prefer_liberation_payloads
return __prefer_liberation_payloads
def server_port():
global __server_port
return __server_port
def ignore_empty_install_directory(): def ignore_empty_install_directory():
global __ignore_empty_install_directory global __ignore_empty_install_directory
return __ignore_empty_install_directory return __ignore_empty_install_directory

View File

@@ -61,18 +61,6 @@ def inject_custom_payloads(user_path: Path) -> None:
PayloadDirectories.set_preferred(user_path / "MissionEditor" / "UnitPayloads") PayloadDirectories.set_preferred(user_path / "MissionEditor" / "UnitPayloads")
def inject_mod_payloads(mod_path: Path) -> None:
if mod_path.exists():
payloads = mod_path
else:
raise RuntimeError(
f"Could not find mod payloads at {mod_path}."
f"Aircraft will have no payloads."
)
# We configure these as preferred so the mod's loadouts override the stock ones.
PayloadDirectories.set_preferred(payloads)
def on_game_load(game: Optional[Game]) -> None: def on_game_load(game: Optional[Game]) -> None:
EventStream.drain() EventStream.drain()
EventStream.put_nowait(GameUpdateEvents().game_loaded(game)) EventStream.put_nowait(GameUpdateEvents().game_loaded(game))
@@ -454,7 +442,8 @@ def main():
dump_task_priorities() dump_task_priorities()
return return
with Server().run_in_thread(): liberation_install.init()
with Server(liberation_install.server_port()).run_in_thread():
run_ui(game, UiFlags(args.dev, args.show_sim_speed_controls)) run_ui(game, UiFlags(args.dev, args.show_sim_speed_controls))

View File

@@ -166,6 +166,8 @@ class QTopPanel(QFrame):
with logged_duration("Skipping turn"): with logged_duration("Skipping turn"):
self.game.pass_turn(no_action=True) self.game.pass_turn(no_action=True)
GameUpdateSignal.get_instance().updateGame(self.game) GameUpdateSignal.get_instance().updateGame(self.game)
state = self.game_model.game.check_win_loss()
GameUpdateSignal.get_instance().gameStateChanged(state)
self.proceedButton.setEnabled(True) self.proceedButton.setEnabled(True)
def negative_start_packages(self, now: datetime) -> List[Package]: def negative_start_packages(self, now: datetime) -> List[Package]:

View File

@@ -8,6 +8,7 @@ from PySide6.QtWebEngineCore import QWebEnginePage, QWebEngineSettings
from PySide6.QtWebEngineWidgets import QWebEngineView from PySide6.QtWebEngineWidgets import QWebEngineView
from game.server.settings import ServerSettings from game.server.settings import ServerSettings
from qt_ui.liberation_install import server_port
from qt_ui.models import GameModel from qt_ui.models import GameModel
@@ -44,7 +45,7 @@ class QLiberationMap(QWebEngineView):
url = QUrl("http://localhost:3000") url = QUrl("http://localhost:3000")
else: else:
url = QUrl.fromLocalFile(str(Path("client/build/index.html").resolve())) url = QUrl.fromLocalFile(str(Path("client/build/index.html").resolve()))
server_settings = ServerSettings.get() server_settings = ServerSettings.get(server_port())
host = server_settings.server_bind_address host = server_settings.server_bind_address
if host.startswith("::"): if host.startswith("::"):
host = f"[{host}]" host = f"[{host}]"

View File

@@ -19,6 +19,8 @@ from PySide6.QtWidgets import (
) )
from game.ato.flight import Flight from game.ato.flight import Flight
from game.server import EventStream
from game.sim import GameUpdateEvents
from game.squadrons import Squadron from game.squadrons import Squadron
from game.theater import ConflictTheater from game.theater import ConflictTheater
from qt_ui.delegates import TwoColumnRowDelegate from qt_ui.delegates import TwoColumnRowDelegate
@@ -258,6 +260,9 @@ class AirWingTabs(QTabWidget):
def open_awcd(self, gm: GameModel): def open_awcd(self, gm: GameModel):
AirWingConfigurationDialog(gm.game, True, self).exec_() AirWingConfigurationDialog(gm.game, True, self).exec_()
events = GameUpdateEvents().begin_new_turn()
EventStream.put_nowait(events)
gm.ato_model.on_sim_update(events)
class AirWingDialog(QDialog): class AirWingDialog(QDialog):

View File

@@ -569,7 +569,7 @@ class QLiberationWindow(QMainWindow):
LAYOUTS.import_templates() LAYOUTS.import_templates()
def showLogsDialog(self): def showLogsDialog(self):
self.dialog = QLogsWindow() self.dialog = QLogsWindow(self)
self.dialog.show() self.dialog.show()
def onDebriefing(self, debrief: Debriefing): def onDebriefing(self, debrief: Debriefing):

View File

@@ -8,6 +8,7 @@ from PySide6.QtWidgets import (
QPlainTextEdit, QPlainTextEdit,
QVBoxLayout, QVBoxLayout,
QPushButton, QPushButton,
QWidget,
) )
from qt_ui.logging_handler import HookableInMemoryHandler from qt_ui.logging_handler import HookableInMemoryHandler
@@ -21,8 +22,8 @@ class QLogsWindow(QDialog):
clear_button: QPushButton clear_button: QPushButton
_logging_handler: typing.Optional[HookableInMemoryHandler] _logging_handler: typing.Optional[HookableInMemoryHandler]
def __init__(self): def __init__(self, parent: QWidget):
super().__init__() super().__init__(parent)
self.setWindowTitle("Logs") self.setWindowTitle("Logs")
self.setMinimumSize(400, 100) self.setMinimumSize(400, 100)

View File

@@ -47,76 +47,100 @@ class QAutoCreateDialog(QDialog):
hbox = QHBoxLayout() hbox = QHBoxLayout()
self.primary_combobox = QComboBox() self.primary_combobox = QComboBox()
self.primary_combobox.setFixedWidth(100)
self.primary_count = _spinbox_template() self.primary_count = _spinbox_template()
self.primary_type = QComboBox()
nr_targets = len(self.package.target.strike_targets) nr_targets = len(self.package.target.strike_targets)
count = max(1, min(4, nr_targets // 2) + nr_targets % 1) if nr_targets else 4 count = max(1, min(4, nr_targets // 2) + nr_targets % 1) if nr_targets else 4
self.primary_count.setValue(count) self.primary_count.setValue(count)
hbox.addWidget(self.primary_combobox) hbox.addWidget(self.primary_combobox)
hbox.addWidget(self.primary_count) hbox.addWidget(self.primary_count)
hbox.addWidget(self.primary_type)
self.layout.addLayout(hbox) self.layout.addLayout(hbox)
self.checkboxes = {} self.checkboxes = {}
hbox = QHBoxLayout() hbox = QHBoxLayout()
self.tarcap = QCheckBox() self.tarcap = self._create_checkbox("TARCAP")
self.tarcap.setText("TARCAP")
self.tarcap_count = _spinbox_template() self.tarcap_count = _spinbox_template()
hbox.addWidget(self.tarcap) hbox.addWidget(self.tarcap)
hbox.addWidget(self.tarcap_count) hbox.addWidget(self.tarcap_count)
self.tarcap_type = self._create_type_selector(FlightType.TARCAP)
hbox.addWidget(self.tarcap_type)
self.layout.addLayout(hbox) self.layout.addLayout(hbox)
self.checkboxes[self.tarcap] = (FlightType.TARCAP, self.tarcap_count) self.checkboxes[self.tarcap] = (
FlightType.TARCAP,
self.tarcap_count,
self.tarcap_type,
)
hbox = QHBoxLayout() hbox = QHBoxLayout()
self.escort = QCheckBox() self.escort = self._create_checkbox("Escort")
self.escort.setText("Escort")
self.escort_count = _spinbox_template() self.escort_count = _spinbox_template()
hbox.addWidget(self.escort) hbox.addWidget(self.escort)
hbox.addWidget(self.escort_count) hbox.addWidget(self.escort_count)
self.escort_type = self._create_type_selector(FlightType.ESCORT)
hbox.addWidget(self.escort_type)
self.layout.addLayout(hbox) self.layout.addLayout(hbox)
self.checkboxes[self.escort] = (FlightType.ESCORT, self.escort_count) self.checkboxes[self.escort] = (
FlightType.ESCORT,
self.escort_count,
self.escort_type,
)
hbox = QHBoxLayout() hbox = QHBoxLayout()
self.sead_escort = QCheckBox() self.sead_escort = self._create_checkbox("SEAD Escort")
self.sead_escort.setText("SEAD Escort")
self.sead_escort_count = _spinbox_template() self.sead_escort_count = _spinbox_template()
hbox.addWidget(self.sead_escort) hbox.addWidget(self.sead_escort)
hbox.addWidget(self.sead_escort_count) hbox.addWidget(self.sead_escort_count)
self.sead_escort_type = self._create_type_selector(FlightType.SEAD_ESCORT)
hbox.addWidget(self.sead_escort_type)
self.layout.addLayout(hbox) self.layout.addLayout(hbox)
self.checkboxes[self.sead_escort] = ( self.checkboxes[self.sead_escort] = (
FlightType.SEAD_ESCORT, FlightType.SEAD_ESCORT,
self.sead_escort_count, self.sead_escort_count,
self.sead_escort_type,
) )
hbox = QHBoxLayout() hbox = QHBoxLayout()
self.sead = QCheckBox() self.sead = self._create_checkbox("SEAD")
self.sead.setText("SEAD")
self.sead_count = _spinbox_template() self.sead_count = _spinbox_template()
hbox.addWidget(self.sead) hbox.addWidget(self.sead)
hbox.addWidget(self.sead_count) hbox.addWidget(self.sead_count)
self.sead_type = self._create_type_selector(FlightType.SEAD)
hbox.addWidget(self.sead_type)
self.layout.addLayout(hbox) self.layout.addLayout(hbox)
self.checkboxes[self.sead] = (FlightType.SEAD, self.sead_count) self.checkboxes[self.sead] = (FlightType.SEAD, self.sead_count, self.sead_type)
hbox = QHBoxLayout() hbox = QHBoxLayout()
self.sead_sweep = QCheckBox() self.sead_sweep = self._create_checkbox("SEAD Sweep")
self.sead_sweep.setText("SEAD Sweep")
self.sead_sweep_count = _spinbox_template() self.sead_sweep_count = _spinbox_template()
hbox.addWidget(self.sead_sweep) hbox.addWidget(self.sead_sweep)
hbox.addWidget(self.sead_sweep_count) hbox.addWidget(self.sead_sweep_count)
self.sead_sweep_type = self._create_type_selector(FlightType.SEAD_SWEEP)
hbox.addWidget(self.sead_sweep_type)
self.layout.addLayout(hbox) self.layout.addLayout(hbox)
self.checkboxes[self.sead_sweep] = ( self.checkboxes[self.sead_sweep] = (
FlightType.SEAD_SWEEP, FlightType.SEAD_SWEEP,
self.sead_sweep_count, self.sead_sweep_count,
self.sead_sweep_type,
) )
hbox = QHBoxLayout() hbox = QHBoxLayout()
self.refueling = QCheckBox() self.refueling = self._create_checkbox("Refueling")
self.refueling.setText("Refueling")
self.refueling_count = _spinbox_template() self.refueling_count = _spinbox_template()
self.refueling_count.setValue(1) self.refueling_count.setValue(1)
hbox.addWidget(self.refueling) hbox.addWidget(self.refueling)
hbox.addWidget(self.refueling_count) hbox.addWidget(self.refueling_count)
self.refueling_type = self._create_type_selector(FlightType.REFUELING)
hbox.addWidget(self.refueling_type, 1)
self.layout.addLayout(hbox) self.layout.addLayout(hbox)
self.checkboxes[self.refueling] = (FlightType.REFUELING, self.refueling_count) self.checkboxes[self.refueling] = (
FlightType.REFUELING,
self.refueling_count,
self.refueling_type,
)
self.create_button = QPushButton("Create") self.create_button = QPushButton("Create")
self.create_button.setProperty("style", "start-button") self.create_button.setProperty("style", "start-button")
@@ -139,6 +163,28 @@ class QAutoCreateDialog(QDialog):
if mt in primary_tasks: if mt in primary_tasks:
self.primary_combobox.addItem(mt.value, mt) self.primary_combobox.addItem(mt.value, mt)
self.primary_combobox.setCurrentIndex(0) self.primary_combobox.setCurrentIndex(0)
self._load_aircraft_types()
@staticmethod
def _create_checkbox(label: str) -> QCheckBox:
cb = QCheckBox(label)
cb.setFixedWidth(100)
return cb
def _create_type_selector(self, flight_type: FlightType) -> QComboBox:
airwing = self.game.blue.air_wing
cb = QComboBox()
for ac in airwing.best_available_aircrafts_for(flight_type):
cb.addItem(ac.variant_id, ac)
return cb
def _load_aircraft_types(self):
self.primary_type.clear()
for ac in self.game.blue.air_wing.best_available_aircrafts_for(
self.primary_combobox.currentData()
):
self.primary_type.addItem(ac.variant_id, ac)
self.primary_type.setCurrentIndex(0)
def on_primary_task_changed(self) -> None: def on_primary_task_changed(self) -> None:
disable = self.primary_combobox.currentData() == FlightType.CAS disable = self.primary_combobox.currentData() == FlightType.CAS
@@ -147,15 +193,26 @@ class QAutoCreateDialog(QDialog):
if disable: if disable:
cb.setChecked(False) cb.setChecked(False)
cb.setDisabled(disable) cb.setDisabled(disable)
self._load_aircraft_types()
def on_create_clicked(self) -> None: def on_create_clicked(self) -> None:
pf: List[ProposedFlight] = [] pf: List[ProposedFlight] = []
count = self.primary_count.value() count = self.primary_count.value()
pf.append(ProposedFlight(self.primary_combobox.currentData(), count)) pf.append(
ProposedFlight(
self.primary_combobox.currentData(),
count,
preferred_type=self.primary_type.currentData(),
)
)
for cb in self.checkboxes: for cb in self.checkboxes:
if cb.isChecked(): if cb.isChecked():
type, spinner = self.checkboxes[cb] type, spinner, ac_box = self.checkboxes[cb]
pf.append(ProposedFlight(type, spinner.value())) pf.append(
ProposedFlight(
type, spinner.value(), preferred_type=ac_box.currentData()
)
)
with MultiEventTracer() as tracer: with MultiEventTracer() as tracer:
with tracer.trace(f"Auto-plan package"): with tracer.trace(f"Auto-plan package"):
pm = ProposedMission(self.package.target, pf, asap=True) pm = ProposedMission(self.package.target, pf, asap=True)

View File

@@ -237,11 +237,12 @@ class QPackageDialog(QDialog):
auto_create_dialog = QAutoCreateDialog( auto_create_dialog = QAutoCreateDialog(
self.game, self.package_model, parent=self.window() self.game, self.package_model, parent=self.window()
) )
auto_create_dialog.exec_() if auto_create_dialog.exec_() == QDialog.DialogCode.Accepted:
for f in self.package_model.package.flights: for f in self.package_model.package.flights:
EventStream.put_nowait(GameUpdateEvents().new_flight(f)) EventStream.put_nowait(GameUpdateEvents().new_flight(f))
self.package_model.update_tot() self.package_model.update_tot()
self.package_changed.emit() self.package_changed.emit()
self.auto_create_button.setDisabled(True)
def on_change_name(self) -> None: def on_change_name(self) -> None:
self.package_model.package.custom_name = self.package_name_text.text() self.package_model.package.custom_name = self.package_name_text.text()

View File

@@ -11,6 +11,8 @@ from PySide6.QtWidgets import (
QMessageBox, QMessageBox,
QPushButton, QPushButton,
QVBoxLayout, QVBoxLayout,
QCheckBox,
QSpinBox,
) )
from qt_ui import liberation_install, liberation_theme from qt_ui import liberation_install, liberation_theme
@@ -40,6 +42,14 @@ class QLiberationPreferences(QFrame):
self.themeSelect = QComboBox() self.themeSelect = QComboBox()
[self.themeSelect.addItem(y["themeName"]) for x, y in THEMES.items()] [self.themeSelect.addItem(y["themeName"]) for x, y in THEMES.items()]
preference = liberation_install.prefer_liberation_payloads()
self.prefer_liberation_payloads = preference if preference else False
self.payloads_cb = QCheckBox()
self.payloads_cb.setChecked(self.prefer_liberation_payloads)
self.port = liberation_install.server_port()
self.port_input = QSpinBox()
self.initUi() self.initUi()
def initUi(self): def initUi(self):
@@ -73,6 +83,25 @@ class QLiberationPreferences(QFrame):
layout.addWidget(self.themeSelect, 4, 1, alignment=Qt.AlignmentFlag.AlignRight) layout.addWidget(self.themeSelect, 4, 1, alignment=Qt.AlignmentFlag.AlignRight)
self.themeSelect.setCurrentIndex(get_theme_index()) self.themeSelect.setCurrentIndex(get_theme_index())
layout.addWidget(
QLabel("<strong>Prefer custom Liberation payloads:</strong>"),
5,
0,
alignment=Qt.AlignmentFlag.AlignLeft,
)
layout.addWidget(self.payloads_cb, 5, 1, alignment=Qt.AlignmentFlag.AlignRight)
layout.addWidget(
QLabel("<strong>Server port (restart required):</strong>"),
6,
0,
alignment=Qt.AlignmentFlag.AlignLeft,
)
layout.addWidget(self.port_input, 6, 1, alignment=Qt.AlignmentFlag.AlignRight)
self.port_input.setRange(1, 2**16 - 1)
self.port_input.setValue(self.port)
self.port_input.setStyleSheet("QSpinBox{ width: 50 }")
main_layout.addLayout(layout) main_layout.addLayout(layout)
main_layout.addStretch() main_layout.addStretch()
@@ -98,6 +127,8 @@ class QLiberationPreferences(QFrame):
print("Applying changes") print("Applying changes")
self.saved_game_dir = self.edit_saved_game_dir.text() self.saved_game_dir = self.edit_saved_game_dir.text()
self.dcs_install_dir = self.edit_dcs_install_dir.text() self.dcs_install_dir = self.edit_dcs_install_dir.text()
self.prefer_liberation_payloads = self.payloads_cb.isChecked()
self.port = self.port_input.value()
set_theme_index(self.themeSelect.currentIndex()) set_theme_index(self.themeSelect.currentIndex())
if not os.path.isdir(self.saved_game_dir): if not os.path.isdir(self.saved_game_dir):
@@ -153,7 +184,12 @@ class QLiberationPreferences(QFrame):
error_dialog.exec_() error_dialog.exec_()
return False return False
liberation_install.setup(self.saved_game_dir, self.dcs_install_dir) liberation_install.setup(
self.saved_game_dir,
self.dcs_install_dir,
self.prefer_liberation_payloads,
self.port,
)
liberation_install.save_config() liberation_install.save_config()
liberation_theme.save_theme_config() liberation_theme.save_theme_config()
return True return True

View File

@@ -10,7 +10,7 @@ click==8.1.7
colorama==0.4.6 colorama==0.4.6
distlib==0.3.8 distlib==0.3.8
Faker==22.6.0 Faker==22.6.0
fastapi==0.109.1 fastapi==0.109.2
filelock==3.13.1 filelock==3.13.1
h11==0.14.0 h11==0.14.0
httptools==0.6.1 httptools==0.6.1
@@ -32,15 +32,15 @@ pluggy==1.4.0
pre-commit==3.6.0 pre-commit==3.6.0
pydantic==2.6.0 pydantic==2.6.0
pydantic-settings==2.1.0 pydantic-settings==2.1.0
-e git+https://github.com/dcs-retribution/pydcs@0bcdeecb4bd9032878b4dbf6fedac5a951429e26#egg=pydcs -e git+https://github.com/dcs-retribution/pydcs@353f5b177dd406122a83e8572fd6ca54adf84389#egg=pydcs
pyinstaller==5.13.2 pyinstaller==5.13.2
pyinstaller-hooks-contrib==2024.0 pyinstaller-hooks-contrib==2024.0
pyparsing==3.1.1 pyparsing==3.1.1
pyproj==3.6.1 pyproj==3.6.1
pyshp==2.3.1 pyshp==2.3.1
PySide6==6.6.1 PySide6==6.4.3
PySide6-Addons==6.6.1 PySide6-Addons==6.4.3
PySide6-Essentials==6.6.1 PySide6-Essentials==6.4.3
pytest==8.0.0 pytest==8.0.0
pytest-cov==4.1.0 pytest-cov==4.1.0
python-dateutil==2.8.2 python-dateutil==2.8.2
@@ -48,10 +48,10 @@ python-dotenv==1.0.1
pywin32-ctypes==0.2.2 pywin32-ctypes==0.2.2
PyYAML==6.0.1 PyYAML==6.0.1
Shapely==2.0.2 Shapely==2.0.2
shiboken6==6.6.1 shiboken6==6.4.3
six==1.16.0 six==1.16.0
sniffio==1.3.0 sniffio==1.3.0
starlette==0.35.1 starlette==0.36.3
suntime==1.2.5 suntime==1.2.5
tabulate==0.9.0 tabulate==0.9.0
text-unidecode==1.3 text-unidecode==1.3

View File

@@ -7,7 +7,7 @@ recommended_enemy_faction: China/Argentina Falklands Occupation Force
description: <p>Argentina and China have taken the Falklands by surprise. Currently on the East Coast of the United States for training, an Invicible-class carrier is steaming down to the South Atlantic to retake the islands, with the help of the United States Carrier Group. A beachhead has been established on the small air base of San Julian where the USAF has parked a small number of F-15Es and F-16 to assist. <p>Retake the Falklands, and proceed inland to neutralize the enemy.</p></p><p><b>Author's notes</b><br/>For this campaign, you can use UK Harrier and Apache skins, and Argentine JF-17 and Mirage Skins. Original campaign Gran Polvorin by Fuzzle.</p> description: <p>Argentina and China have taken the Falklands by surprise. Currently on the East Coast of the United States for training, an Invicible-class carrier is steaming down to the South Atlantic to retake the islands, with the help of the United States Carrier Group. A beachhead has been established on the small air base of San Julian where the USAF has parked a small number of F-15Es and F-16 to assist. <p>Retake the Falklands, and proceed inland to neutralize the enemy.</p></p><p><b>Author's notes</b><br/>For this campaign, you can use UK Harrier and Apache skins, and Argentine JF-17 and Mirage Skins. Original campaign Gran Polvorin by Fuzzle.</p>
miz: RetakeTheFalklands.miz miz: RetakeTheFalklands.miz
performance: 2 performance: 2
recommended_start_date: 2005-06-15 recommended_start_date: 2002-12-21
version: "10.7" version: "10.7"
squadrons: squadrons:
# Off-map spawn # Off-map spawn

View File

@@ -1,11 +1,11 @@
--- ---
name: Syria - WRL - Assault on Damascus (v1.1) #1.0 - Original Release / #1.1 - Player F15e added name: Syria - WRL - Assault on Damascus (v1.2) #1.0 - Original Release / #1.1 - Player F15e added / #1.2 - IADS ADDED & Massive Overhaul, Added C130 spawn support at main airbase
theater: Syria theater: Syria
authors: HolyOrangeJuice authors: HolyOrangeJuice
recommended_player_faction: WRL - Task Force Blue recommended_player_faction: WRL - Task Force Blue
recommended_enemy_faction: WRL - Task Force Red recommended_enemy_faction: WRL - Task Force Red
recommended_start_date: 2022-06-04 recommended_start_date: 2022-06-04
description: "<p>This mission was modified by HolyOrangeJuice [OscarJuliet] for the WRL Group. Join us for weekly PVE events. <a style=\"color: white\" href=\"https://werunliberation.com\">werunliberation.com</a></p><p>Original Battle for Golan Heights by Khopa.</p><p>This modified campaign does not represent any historical time period or events. Attack from Ramat-David and eliminate the enemy around Damascus.</p><p>All WRL Campaigns use high budget and high income to focus on the PVE goals to eliminate the enemy. Use lower starting money and income for increased difficulty.</p>" description: "<p>This campaign is the gold standard of WRL campaigns. This campaign will always have the latest retribution additions. Consider this a feature complete campaign as it progresses through updates. Play Battle 4 Gerogia as a beginner mission. Play this one for an advanced experience</p><p>This mission was heavily modified by HolyOrangeJuice [OscarJuliet] for the WRL Group. Join us for weekly PVE events. <a style=\"color: white\" href=\"https://werunliberation.com\">werunliberation.com</a></p><p>Original Battle for Golan Heights by Khopa.</p><p>This modified campaign does not represent any historical time period or events. Attack from Ramat-David and eliminate the enemy around Damascus.</p><p>All WRL Campaigns use high budget and high income to focus on the PVE goals to eliminate the enemy. Use lower starting money and income for increased difficulty.</p>"
miz: WRL_AssaultonDamascus.miz miz: WRL_AssaultonDamascus.miz
performance: 2 performance: 2
version: "10.7" #CTLD Zones Added version: "10.7" #CTLD Zones Added
@@ -13,9 +13,102 @@ recommended_player_money: 5000
recommended_enemy_money: 2000 recommended_enemy_money: 2000
recommended_player_income_multiplier: 5.0 recommended_player_income_multiplier: 5.0
recommended_enemy_income_multiplier: 1.0 recommended_enemy_income_multiplier: 1.0
advanced_iads: false
settings: settings:
max_frontline_width: 30 max_frontline_width: 30
advanced_iads: true # Campaign has connection_nodes / power_sources / command_centers
iads_config:
#REDFOR DEFENSE MARJ AIRBASE AREA
- M-COMMAND-1:
- M-POWER-1
- M-POWER-2
- M-TOWER-1
- M-TOWER-2
- M-EWR-1:
- M-POWER-1
- M-TOWER-1
- M-EWR-2:
- M-POWER-1
- M-TOWER-1
- M-EWR-3:
- M-POWER-2
- M-TOWER-2
- M-SAM-1:
- M-POWER-1
- M-TOWER-1
- M-SAM-2:
- M-POWER-1
- M-TOWER-1
- M-SAM-3:
- M-POWER-2
- M-TOWER-2
- M-SAM-4:
- M-POWER-2
- M-TOWER-2
- M-SAM-5:
- M-POWER-2
- M-TOWER-2
- M-SAM-6:
- M-POWER-2
- M-TOWER-2
#REDFOR DEFENSE DAMASCUS AIRBASE AREA
- D-COMMAND-1:
- D-POWER-1
- D-POWER-2
- D-TOWER-1
- D-TOWER-2
- D-EWR-1:
- D-POWER-1
- D-TOWER-1
- D-EWR-2:
- D-POWER-2
- D-TOWER-2
- D-EWR-3:
- D-POWER-2
- D-TOWER-2
- D-SAM-1:
- D-POWER-1
- D-TOWER-1
- D-SAM-2:
- D-POWER-2
- D-TOWER-2
- D-SAM-3:
- D-POWER-1
- D-TOWER-1
- D-SAM-4:
- D-POWER-2
- D-TOWER-2
#REDFOR DEFENSE AL-DUMAYR AIRBASE AREA
- A-COMMAND-1:
- A-POWER-1
- A-TOWER-1
- A-EWR-1:
- A-POWER-1
- A-TOWER-1
- A-EWR-2:
- A-POWER-1
- A-TOWER-1
- A-SAM-1:
- A-POWER-1
- A-TOWER-1
- A-SAM-2:
- A-POWER-1
- A-TOWER-1
#BLUEFOR RAMAT DEFENSE
- R-COMMAND-1:
- R-POWER-1
- R-TOWER-1
- RAMAT1:
- R-POWER-1
- R-TOWER-1
- RAMAT2:
- R-POWER-1
- R-TOWER-1
- RAMAT3:
- R-POWER-1
- R-TOWER-1
- RAMAT4:
- R-POWER-1
- R-TOWER-1
squadrons: squadrons:
CVN-74 John Stennis: CVN-74 John Stennis:
- primary: BARCAP - primary: BARCAP
@@ -75,7 +168,12 @@ squadrons:
secondary: any secondary: any
aircraft: aircraft:
- F-117A Nighthawk - F-117A Nighthawk
size: 5 size: 8
- primary: BARCAP
secondary: air-to-air
aircraft:
- F-15C Eagle
size: 16
# Ramat-David # Ramat-David
30: 30:
- primary: BAI - primary: BAI
@@ -108,88 +206,103 @@ squadrons:
aircraft: aircraft:
- AV-8B Harrier II Night Attack - AV-8B Harrier II Night Attack
size: 16 size: 16
# Golan South
# Golan South:
# Golan North
Golan North: Golan North:
- primary: BAI - primary: BAI
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- A Company, 1-211th ARB #AH-64D Apache Longbow - A Company, 1-211th ARB #AH-64D Apache Longbow
size: 10 size: 8
- primary: BAI - primary: BAI
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- Mi-24P Hind-F - Mi-24P Hind-F
size: 10 size: 8
- primary: BAI - primary: BAI
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- Ka-50 Hokum III - Ka-50 Hokum III
size: 10 size: 8
- primary: Transport
secondary: air-to-ground
aircraft:
- HMLA-169 (UH-1H) #UH-1H Iroquois
size: 8
# Golan South
Golan South:
- primary: BAI
secondary: air-to-ground
aircraft:
- AH-1W SuperCobra
size: 4
- primary: BAI - primary: BAI
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- SA 342M Gazelle - SA 342M Gazelle
size: 4 size: 8
- primary: Transport - primary: Transport
secondary: any secondary: air-to-ground
aircraft:
- HMLA-169 (UH-1H) #UH-1H Iroquois
size: 4
- primary: Transport
secondary: any
aircraft: aircraft:
- Mi-8MTV2 Hip - Mi-8MTV2 Hip
size: 4 size: 8
# Marj Ruhayyil
23:
- primary: BARCAP
secondary: air-to-air
aircraft:
- MiG-21bis Fishbed-N
size: 16
# Al-Dumayr
9:
- primary: BARCAP
secondary: any
aircraft:
- Su-27 Flanker-B
size: 16
# Khalkhalah
18:
- primary: BARCAP
secondary: any
aircraft:
- MiG-23MLD Flogger-K
size: 16
# Damascus # Damascus
7: 7:
- primary: BARCAP - primary: BARCAP
secondary: any secondary: air-to-air
aircraft: aircraft:
- MiG-29S Fulcrum-C - MiG-29S Fulcrum-C
size: 16 size: 12
- primary: SEAD
secondary: air-to-ground
aircraft:
- Su-34 Fullback
size: 12
- primary: Strike - primary: Strike
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- Su-24M Fencer-D - Su-24M Fencer-D
size: 16 size: 12
- primary: SEAD # Marj Ruhayyil
secondary: air-to-ground # 23:
aircraft: # Al-Dumayr
- Su-25T Frogfoot # 9:
size: 16 # Khalkhalah
# 18:
# MEZZEH
# 25:
# FOB ALPHA
# FOB ALPHA:
# FOB BRAVO
# FOB BRAVO:
# MARJ AS SULTAN SOUTH
# 8:
# MARJ AS SULTAN NORTH
# 22:
# QABR AS SITT
# 29:
ground_forces: ground_forces:
KHALK1: SA-11 M-SAM-1: SA-11
KHALK2: SA-2/S-75 M-SAM-2: SA-11
DAMASCUS1: SA-11 M-SAM-3: SA-2/S-75
DUMAYR1: SA-2/S-75 M-SAM-4: SA-3/S-125
DUMAYR2: SA-2/S-75 M-SAM-5: SA-3/S-125
DUMAYR3: SA-10/S-300PS M-SAM-6: SA-3/S-125
M-SHORAD-1: SA-15 SHORAD
# M-AAA-1:
D-SAM-1: SA-2/S-75
D-SAM-2: SA-2/S-75
D-SAM-3: SA-6
D-SAM-4: SA-3/S-125
D-SHORAD-1: SA-15 SHORAD
# D-AAA-1:
K-SHORAD-1: SA-15 SHORAD
# K-AAA-1:
FOB-SHORAD-1: SA-15 SHORAD
# FOB-AAA-1:
A-SHORAD-1: SA-15 SHORAD
RAMAT1: Patriot RAMAT1: Patriot
RAMAT2: Hawk RAMAT2: Patriot
RAMAT3: Patriot RAMAT3: NASAMS AIM-120C
RAMAT4: NASAMS AIM-120C
GSOUTH1: NASAMS AIM-120C GSOUTH1: NASAMS AIM-120C
GSOUTH2: Hawk GSOUTH2: NASAMS AIM-120C
GNORTH1: NASAMS AIM-120C GNORTH1: NASAMS AIM-120C

View File

@@ -1,28 +1,25 @@
--- ---
name: Syria - WRL - Battle For Syria North (1.0) #1.0 Release with Player F15e name: Syria - WRL - Battle For Syria North (v1.2) #1.2 Complete Mission Overhaul - Frontlines Removed - CTLD or C130s are expected for base capture / Consider victory by destroying all airbases.
theater: Syria theater: Syria
authors: HolyOrangeJuice authors: HolyOrangeJuice
description: "<p>This mission was modified by HolyOrangeJuice [OscarJuliet] for the WRL Group. Original Mission by Sith1144. Join us for weekly PVE events. <a style=\"color: white\" href=\"https://werunliberation.com\">werunliberation.com</a></p><p>This modification of the mission is not meant to represent historical accuracy. An exciting battle on the Northern part of the Syria map.</p><p>All WRL Campaigns use high budget and high income to focus on the PVE goals to eliminate the enemy. Use lower starting money and income for increased difficulty</p>" description: "<p>This mission was heavily modified by HolyOrangeJuice [OscarJuliet] for the WRL Group. Original Mission by Sith1144. Join us for weekly PVE events. <a style=\"color: white\" href=\"https://werunliberation.com\">werunliberation.com</a></p><p>This modification of the mission is not meant to represent historical accuracy. An exciting battle on the Northern part of the Syria map.</p><p>All WRL Campaigns use high budget and high income to focus on the PVE goals to eliminate the enemy. Use lower starting money and income for increased difficulty</p>"
recommended_player_faction: WRL - Task Force Blue recommended_player_faction: WRL - Task Force Blue
recommended_enemy_faction: WRL - Task Force Red recommended_enemy_faction: WRL - Task Force Red
recommended_start_date: 2019-06-04 recommended_start_date: 2019-06-04
miz: WRL_Battle4SyriaNorth.miz miz: WRL_Battle4SyriaNorth.miz
performance: 2 performance: 2
version: "10.7" #No CTLD zones, campaign is too large for CTLD focus version: "10.7" #CTLD ZONES ADDED
recommended_player_money: 5000 recommended_player_money: 5000
recommended_enemy_money: 2000 recommended_enemy_money: 2000
recommended_player_income_multiplier: 5.0 recommended_player_income_multiplier: 5.0
recommended_enemy_income_multiplier: 1.0 recommended_enemy_income_multiplier: 1.0
advanced_iads: true # Campaign has connection_nodes / power_sources / command_centers advanced_iads: true
#IADS: EWR and C2 get power generators. batteries have their own generators.
iads_config: iads_config:
# NATO IADS # NATO IADS
#EWRs
- NATO EWR-1: - NATO EWR-1:
- NATO IADS Command - NATO IADS Command
- NATO EWR: - NATO EWR:
- NATO IADS Command - NATO IADS Command
# SAMs
- NATO SAM: - NATO SAM:
- NATO IADS Command - NATO IADS Command
- NATO SAM-1: - NATO SAM-1:
@@ -39,202 +36,129 @@ iads_config:
- NATO IADS Command - NATO IADS Command
- NATO SAM-7: - NATO SAM-7:
- NATO IADS Command - NATO IADS Command
# Yellow defense zone (Gaziantep-Minakh) #BLUE DEFENSE AREA - HINT: Go after the power stations
#EWRs - B-COMMAND-1:
- YellowEWRN: #mountainrange (north) - B-POWER-1
- YellowPPW - B-TOWER-1
- YellowControlN - B-POWER-2
- YellowEWRS: #mountainrange (center) - B-TOWER-2
- YellowPPW - B-SAM-1:
- YellowControlW - B-POWER-1
- YellowEWRC: # internal - B-TOWER-1
- HamidiyeControl - B-SAM-2:
- GaziantepControl - B-POWER-1
- GaziantepPP - B-TOWER-1
- GaziantepAirControl: - B-SAM-3:
- GaziantepControl - B-POWER-1
- GaziantepPP - B-TOWER-1
# The air defense barrier behind the mountains - B-SAM-4:
- YellowBarrierN: - B-POWER-2
- YellowControlN - B-TOWER-2
- YellowBarrierC1: - B-SAM-5: #SA10
- YellowControlW - B-POWER-2
- YellowBarrierC2: - B-TOWER-2
- YellowControlW - B-EWR-1:
- YellowBarrierS: - B-POWER-1
- YellowControlW - B-TOWER-1
- YellowBarrierAAA: - B-EWR-2:
- YellowControlW - B-POWER-1
# the central long range SAM and its point defenses - B-TOWER-1
- YellowLongRangeSAMPointDefense: #RED DEFENSE AREA - HINT: Go after the power stations
- HamidiyeControl - R-COMMAND-1:
- YellowLongRangeSAM: - R-POWER-1
- HamidiyeControl - R-TOWER-1
- YellowLongRangeSAMAAA: - R-POWER-2
- HamidiyeControl - R-TOWER-2
# the defenses around Gaziantep airfield - R-SAM-1:
- GAZSAMN: - R-POWER-1
- GaziantepControl - R-TOWER-1
- GAZSAMW: - R-SAM-2:
- GaziantepControl - R-POWER-1
# the defenses around Minakh airfield - R-TOWER-1
- MinakhSAM1: - R-SAM-3:
- MinakhControl - R-POWER-1
- MinakhSAM2: - R-TOWER-1
- MinakhControl - R-SAM-4:
#C2 links - R-POWER-1
- YellowControlN: - R-TOWER-1
- HamidiyeControl - R-SAM-5:
- YellowControlW: - R-POWER-1
- HamidiyeControl - R-TOWER-1
- YellowControlN - R-SAM-6:
- YellowZoneCommand: - R-POWER-1
- GaziantepControl - R-TOWER-1
- MinakhControl - R-SAM-7:
- HamidiyeControl - R-POWER-1
- GaziantepPP - R-TOWER-1
- GaziantepAirControl - R-SAM-8:
# Green Defense Zone (HATAY) - R-POWER-2
# EWRs - R-TOWER-2
- CoastalEWRN: - R-SAM-9: #SA-10
- HatayControl - R-POWER-2
- AntakyaPower - R-TOWER-2
- CoastalEWRS: #YELLOW DEFNESE AREA - HINT: Go after the command bunkers
- SamandagControl - Y-COMMAND-1:
- AntakyaPower - Y-POWER-1
# The air defense barrier behind the mountains - Y-TOWER-1
- GreenBarrierSAM: - Y-POWER-2
- SamandagControl - Y-TOWER-2
- GreenBarrierSAM-1: - Y-TOWER-3
- SamandagControl - Y-COMMAND-2:
- GreenBarrierSAM-2: - Y-POWER-1
- HatayControl - Y-TOWER-1
- GreenBarrierSAM-3: - Y-POWER-2
- HatayControl - Y-TOWER-2
- GreenBarrierSAM-4: - Y-TOWER-3
- HatayControl - Y-SAM-1:
# Coastal Defenses - Y-POWER-1
- GreenSAM-2: - Y-TOWER-1
- SamandagControl - Y-POWER-2
- GreenSAM-3: - Y-TOWER-2
- SamandagControl - Y-SAM-2:
- GreenSAM-4: - Y-POWER-1
- SamandagControl - Y-TOWER-1
# Hatay Airfield - Y-POWER-2
- HataySAM: - Y-TOWER-2
- HatayControl - Y-SAM-3:
- HataySAM-1: - Y-POWER-1
- HatayControl - Y-TOWER-1
- HatayAAA: - Y-POWER-2
- HatayControl - Y-TOWER-2
# Industrial/Command zone - Y-SAM-4:
- GreenSAM: - Y-POWER-1
- IdlibControl - Y-TOWER-1
- GreenZoneCommand - Y-POWER-2
- GreenSAM-2: - Y-TOWER-2
- IdlibControl - Y-SAM-5:
- GreenZoneCommand - Y-POWER-1
# long range battery and defenses - Y-TOWER-1
- GreenZoneSAM: - Y-POWER-2
- ReyhanliControl - Y-TOWER-2
- GreenZoneSAMDefense: - Y-SAM-6:
- ReyhanliControl - Y-POWER-1
# C2 links - Y-TOWER-1
- GreenZoneCommand: - Y-POWER-2
- IdlibControl - Y-TOWER-2
- IdlibPower - Y-SAM-7:
- SamandagControl - Y-POWER-2
- HatayControl: - Y-TOWER-2
- ReyhanliControl - Y-TOWER-3
- SamandagControl - Y-SAM-8:
- AntakyaPower - Y-POWER-2
- SamandagControl: - Y-TOWER-2
- AntakyaPower - Y-TOWER-3
- IdlibControl: - Y-SAM-9:
- ReyhanliControl - Y-POWER-2
- SamandagControl - Y-TOWER-2
- IdlibPower - Y-TOWER-3
- ReyhanliControl: - Y-SAM-10:
- IdlibPower - Y-POWER-2
# Pink zone (Aleppo) - Y-TOWER-2
# EWR - Y-TOWER-3
- Abu al-Duhur Air Control: - Y-SAM-11: #SA-5
- Al Safira Power - Y-POWER-2
- Aleppo Air Control: - Y-TOWER-2
- Aleppo Power - Y-TOWER-3
- Kuweires Air Control:
- Al Safira Power
- Aleppo Power
- Jirah Air Control:
- Al Safira Power
# Aleppo city defenses
- AleppoSAM:
- AleppoCommand
- Aleppo Control
- AleppoSAM-1:
- AleppoCommand
- Aleppo Control
- AleppoSAM-2:
- AleppoCommand
- Aleppo Control
- AleppoSAM-3:
- AleppoCommand
- Aleppo Control
- AleppoSAM-4:
- AleppoCommand
- Aleppo Control
- AleppoSAM-5:
- AleppoCommand
- Aleppo Control
- AleppoSAM-6:
- AleppoCommand
- Aleppo Control
- AleppoSAM-7:
- AleppoCommand
- Aleppo Control
# Long Range SAM
- Pink SAM:
- Al Safira Power
- Aleppo Control
- AlSafiraCommand
- AlSafiraPD:
- Aleppo Control
- AlSafiraCommand
# Abu al-Duhur
- AbuSAM:
- Abu al-Duhur control
- AbuSAM-1:
- Abu al-Duhur control
# Kuweires
- KuweiresSAM:
- KuweiresControl
# Jirah
- JirahSAM:
- JirahControl
- JirahSAM-1:
- JirahControl
- JirahSAM-2:
- JirahControl
# C2 links
- AleppoCommand:
- Aleppo Power
- Al Safira Power
- Aleppo Control
- AlSafiraCommand:
- Al Safira Power
- Aleppo Power
- Aleppo Control
- KuweiresControl:
- Al Safira Power
- Aleppo Control
- JirahControl:
- Al Safira Power
- KuweiresControl
- Abu al-Duhur control:
- Aleppo Power
- Aleppo Control
- Aleppo Control:
- Aleppo Power
squadrons: squadrons:
#Incirlik #Incirlik
16: 16:
@@ -340,8 +264,8 @@ squadrons:
aircraft: aircraft:
- Mi-8MTV2 Hip - Mi-8MTV2 Hip
size: 4 size: 4
#carrier #NATO CARRIERS
Blue Carrier: NATO CVN:
- primary: BARCAP - primary: BARCAP
secondary: any secondary: any
aircraft: aircraft:
@@ -365,47 +289,59 @@ squadrons:
aircraft: aircraft:
- S-3B Viking - S-3B Viking
size: 4 size: 4
#Abu Al-Duhur NATO LHA:
1: - primary: BAI
- primary: BARCAP
secondary: any
aircraft:
- MiG-29S Fulcrum-C
size: 16
#Hatay
15:
- primary: SEAD
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- Su-25T Frogfoot - AV-8B Harrier II Night Attack
size: 12 #Abu Al-Duhur
#Minakh 1:
26:
- primary: BAI - primary: BAI
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- Su-34 Fullback - Su-34 Fullback
size: 12 size: 12
#Hatay
15:
- primary: BAI
secondary: air-to-ground
aircraft:
- Su-24M Fencer-D
size: 12
#Minakh
26:
- primary: BARCAP
secondary: air-to-air
aircraft:
- MiG-21bis Fishbed-N
size: 12
#Aleppo #Aleppo
27: 27:
- primary: BARCAP - primary: BARCAP
secondary: air-to-air secondary: air-to-air
aircraft: aircraft:
- MiG-29S Fulcrum-C - MiG-23MLD Flogger-K
size: 16 size: 12
#Jirah #Jirah
17: 17:
- primary: BARCAP - primary: BARCAP
secondary: air-to-air secondary: air-to-air
aircraft: aircraft:
- MiG-31 Foxhound - MiG-23MLD Flogger-K
size: 16 size: 12
#Gaziantep #Gaziantep
11: 11:
- primary: BAI - primary: BARCAP
secondary: air-to-ground secondary: air-to-air
aircraft: aircraft:
- Su-30 Flanker-C - MiG-21bis Fishbed-N
size: 12
#Kuweires
31:
- primary: BARCAP
secondary: air-to-air
aircraft:
- MiG-29S Fulcrum-C
size: 12 size: 12
ground_forces: ground_forces:
NATO SAM: Hawk NATO SAM: Hawk
@@ -413,38 +349,28 @@ ground_forces:
NATO SAM-2: Hawk NATO SAM-2: Hawk
NATO SAM-3: Hawk NATO SAM-3: Hawk
NATO SAM-4: Hawk NATO SAM-4: Hawk
YellowBarrierN: SA-2/S-75 with ZSU-23/57 B-SAM-1: SA-2/S-75
YellowBarrierC1: SA-6 B-SAM-2: SA-11
YellowBarrierC2: SA-11 B-SAM-3: SA-2/S-75
YellowBarrierS: SA-2/S-75 with ZSU-23/57 B-SAM-4: SA-2/S-75
YellowLongRangeSAM: SA-2/S-75 with ZSU-23/57 B-SAM-5: SA-10/S-300PS
GAZSAMN: SA-11 R-SAM-1: SA-2/S-75
GreenBarrierSAM-4: SA-2/S-75 with ZSU-23/57 R-SAM-2: SA-2/S-75
GreenBarrierSAM-3: SA-2/S-75 with ZSU-23/57 R-SAM-3: SA-11
GreenZoneSAMDefense: SA-11 R-SAM-4: SA-11
GreenZoneSAM: SA-2/S-75 with ZSU-23/57 R-SAM-5: SA-6
HataySAM: SA-11 R-SAM-6: SA-3/S-125
HataySAM-1: SA-2/S-75 with ZSU-23/57 R-SAM-7: SA-6
GreenSAM-2: SA-3/S-125 R-SAM-8: SA-11
GreenSAM-3: SA-6 R-SAM-9: SA-10/S-300PS
GreenSAM-1: SA-2/S-75 with ZSU-23/57 Y-SAM-1: SA-6
JirahSAM: SA-10/S-300PS Y-SAM-2: SA-3/S-125
JirahSAM-1: SA-11 Y-SAM-3: SA-6
JirahSAM-2: SA-2/S-75 with ZSU-23/57 Y-SAM-4: SA-3/S-125
KuweiresSAM: SA-6 Y-SAM-5: SA-6
Pink SAM: SA-11 Y-SAM-6: SA-3/S-125
AlSafiraPD: SA-15 SHORAD Y-SAM-7: SA-11
AleppoSAM: SA-3/S-125 Y-SAM-8: SA-2/S-75
AleppoSAM-1: SA-3/S-125 Y-SAM-9: SA-2/S-75
AleppoSAM-2: SA-3/S-125 Y-SAM-10: SA-2/S-75
AleppoSAM-3: SA-3/S-125 Y-SAM-11: SA-10/S-300PS
AleppoSAM-4: SA-3/S-125
AleppoSAM-5: SA-6
AleppoSAM-6: SA-6
AleppoSAM-7: SA-11
MinakhSAM1: SA-3/S-125
MinakhSAM2: SA-3/S-125
GAZSAMN: SA-11
GAZSAMW: SA-3/S-125
AbuSAM: SA-2/S-75 with ZSU-23/57
AbuSAM-1: SA-15 SHORAD

Binary file not shown.

View File

@@ -0,0 +1,69 @@
---
name: Falklands - Battle for No Man's Land
theater: Falklands
authors: Starfire
recommended_player_faction: USA 2005
recommended_enemy_faction: Private Military Company - Russian (Hard)
description:
<p><strong>Note:</strong> This campaign was designed for helicopters.</p><p>
Set against the rugged and windswept backdrop of the Falkland Islands,
this fictional campaign scenario unfolds with a dramatic dawn sneak attack
on RAF Mount Pleasant Airbase. Orchestrated by a Russia-backed private
military company, the deadly offensive with helicopter gunships and ground troops
has left the airbase's runways in ruins and its defences obliterated. This brutal
incursion resulted in significant casualties among the RAF personnel, with many
killed or wounded in the unexpected onslaught. The carrier HMS Queen Elizabeth and
its task force are on their way to evacuate the survivors and retake Mount Pleasant.
However, they are eight days away at full steam.</p><p>
Amidst this chaos, a beacon of hope emerges in the heart of the Falklands. At Port
Stanley, a small detachment of US military personnel, including helicopter pilots
and armor units, find themselves inadvertently thrust into the fray. Originally at
Port Stanley for some R&R following a training exercise, these soldiers now face
an unexpected and urgent call to action. Their mission is daunting but clear - to
prevent the capture of Port Stanley and liberate East Falkland from the clutches
of the PMC forces.</p><p>
This small group must strategically destroy the PMC forces deployed around the treacherous
valley lying between Wickham Heights and the Onion Ranges, an area ominously known
as No Man's Land. Their plan involves a daring assault to destroy the enemy's
helicopter gunships stationed at San Carlos FOB. Following this, they aim to force
the PMC ground forces into a strategic retreat southward, along the 1.6 mile wide
isthmus into Lafonia. This offensive is designed to create a defensible position
at Goose Green on the narrow isthmus, which can be held against a numerically
superior force until the arrival of Big Lizzie.</p>
miz: battle_for_no_mans_land.miz
performance: 1
recommended_start_date: 2001-11-10
version: "10.7"
squadrons:
#Port Stanley
1:
- primary: DEAD
secondary: air-to-ground
aircraft:
- AH-64D Apache Longbow
size: 6
- primary: BAI
secondary: air-to-ground
aircraft:
- AH-64D Apache Longbow
size: 6
- primary: Air Assault
secondary: any
aircraft:
- UH-60L
- UH-60A
size: 4
#San Carlos FOB
3:
- primary: BAI
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
size: 6
#Goose Green
24:
- primary: DEAD
secondary: air-to-ground
aircraft:
- Ka-50 Hokum (Blackshark 3)
size: 6

View File

@@ -3,8 +3,29 @@ name: Sinai - Exercise Bright Star
theater: Sinai theater: Sinai
authors: Starfire authors: Starfire
recommended_player_faction: Bluefor Modern recommended_player_faction: Bluefor Modern
recommended_enemy_faction: Egypt 2010's recommended_enemy_faction: Egypt 2000
description: <p>For over 4 decades, the United States and Egypt have run a series of biannual joint military exercises called Bright Star. Over the years, the number of participating countries has grown substantially. Exercise Bright Star 2025 boasts 8 participant nations and 14 observer nations. The United States and a portion of the exercise coalition will play the part of a fictional hostile nation dubbed Orangeland, staging a mock invasion against Cairo. Israel, having for the first time accepted the invitation to observe, is hosting the aggressor faction of the exercise coalition at its airfields.</p> description:
<p>For over four decades, the United States and Egypt have conducted a series
of biannual joint military exercises known as Bright Star. As the
geopolitical landscape has transformed, so too has the scope and scale of
Exercise Bright Star. The exercise has grown over the years to incorporate
a wide array of international participants. The 2025 iteration of
Exercise Bright Star features eight participating nations alongside
fourteen observer nations.</p><p>
For the 2025 exercises, the United States, along with a select contingent
from the exercise coalition, will take on the role of a hypothetical
adversarial nation, dubbed Orangeland. This scenario is designed to
simulate a mock invasion against Cairo, and presents a valuable
opportunity for participating nations to refine their joint operational
capabilities and improve logistical and tactical interoperability</p><p>
A historic addition to Exercise Bright Star 2025 is the participation of
Israel as an observer nation. This marks a significant milestone, given
the complex historical relations in the region, and symbolises a step
forward in regional collaboration and military diplomacy. Israel's role,
hosting the aggressor faction of the exercise coalition at its airfields,
not only demonstrates the broadening scope of the exercise but also highlights
the value of fostering an environment of mutual cooperation and shared
security objectives.</p>
miz: exercise_bright_star.miz miz: exercise_bright_star.miz
performance: 1 performance: 1
recommended_start_date: 2025-09-01 recommended_start_date: 2025-09-01
@@ -18,21 +39,27 @@ squadrons:
size: 24 size: 24
- primary: AEW&C - primary: AEW&C
aircraft: aircraft:
- E-2C Hawkeye - E-2D Advanced Hawkeye
size: 2 size: 2
- primary: Refueling - primary: Refueling
aircraft: aircraft:
- S-3B Tanker - S-3B Tanker
size: 2 size: 4
- primary: Air Assault Bombers from RAF Fairford:
secondary: any - primary: Anti-ship
secondary: air-to-ground
aircraft: aircraft:
- SH-60B Seahawk - B-52H Stratofortress
size: 2 size: 8
# Hatzerim (141) - primary: Strike
secondary: air-to-ground
aircraft:
- B-1B Lancer
size: 8
# Hatzerim (141)
7: 7:
- primary: Escort - primary: Escort
secondary: air-to-air secondary: any
aircraft: aircraft:
- F-15C Eagle - F-15C Eagle
size: 20 size: 20
@@ -40,12 +67,7 @@ squadrons:
secondary: any secondary: any
aircraft: aircraft:
- F-15E Strike Eagle (Suite 4+) - F-15E Strike Eagle (Suite 4+)
size: 8 size: 16
- primary: Strike
secondary: air-to-ground
aircraft:
- F-15E Strike Eagle
size: 8
- primary: DEAD - primary: DEAD
secondary: any secondary: any
aircraft: aircraft:
@@ -60,19 +82,21 @@ squadrons:
secondary: any secondary: any
aircraft: aircraft:
- Mirage 2000C - Mirage 2000C
size: 16 size: 12
# Kedem # Kedem
12: 12:
- primary: Transport - primary: Transport
secondary: any
aircraft: aircraft:
- CH-47D - CH-47D
size: 20 size: 20
- primary: Air Assault - primary: Air Assault
secondary: air-to-ground secondary: any
aircraft: aircraft:
- UH-1H Iroquois - UH-60L
- UH-60A
size: 4 size: 4
# Nevatim (106) # Nevatim (106)
8: 8:
- primary: AEW&C - primary: AEW&C
aircraft: aircraft:
@@ -81,34 +105,30 @@ squadrons:
- primary: Refueling - primary: Refueling
aircraft: aircraft:
- KC-135 Stratotanker - KC-135 Stratotanker
size: 1 size: 2
- primary: Refueling
aircraft:
- KC-135 Stratotanker MPRS
size: 1
- primary: CAS - primary: CAS
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- A-10C Thunderbolt II (Suite 7) - A-10C Thunderbolt II (Suite 7)
size: 8 size: 8
# Melez (30) # Melez (30)
5: 5:
- primary: CAS - primary: CAS
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- Ka-50 Hokum (Blackshark 3) - Ka-50 Hokum (Blackshark 3)
size: 4 size: 4
- primary: TARCAP - primary: BAI
secondary: air-to-air secondary: any
aircraft: aircraft:
- Mirage 2000C - Mirage 2000C
size: 12 size: 12
- primary: Strike - primary: Escort
secondary: air-to-ground secondary: any
aircraft: aircraft:
- Mirage 2000C - MiG-21bis Fishbed-N
size: 12 size: 12
# Wadi al Jandali (72) # Wadi al Jandali (72)
13: 13:
- primary: AEW&C - primary: AEW&C
aircraft: aircraft:
@@ -117,7 +137,7 @@ squadrons:
- primary: SEAD - primary: SEAD
secondary: any secondary: any
aircraft: aircraft:
- F-16CM Fighting Falcon (Block 50) - F-4E Phantom II
size: 20 size: 20
- primary: DEAD - primary: DEAD
secondary: any secondary: any
@@ -125,28 +145,23 @@ squadrons:
- F-16CM Fighting Falcon (Block 50) - F-16CM Fighting Falcon (Block 50)
size: 20 size: 20
- primary: Air Assault - primary: Air Assault
secondary: air-to-ground secondary: any
aircraft: aircraft:
- Mi-24P Hind-F - Mi-24P Hind-F
size: 4 size: 4
- primary: OCA/Aircraft - primary: OCA/Aircraft
secondary: air-to-ground secondary: any
aircraft: aircraft:
- SA 342L Gazelle - SA 342L Gazelle
size: 4 size: 4
# Cairo West (95) # Cairo West (95)
18: 18:
- primary: Transport - primary: Transport
aircraft: aircraft:
- C-130 - C-130
size: 8 size: 8
- primary: Escort - primary: BARCAP
secondary: air-to-air secondary: air-to-air
aircraft: aircraft:
- MiG-29S Fulcrum-C - MiG-29S Fulcrum-C
size: 20 size: 20
- primary: BARCAP
secondary: any
aircraft:
- J-7B
size: 20

View File

@@ -4,30 +4,48 @@ theater: Nevada
authors: Starfire authors: Starfire
recommended_player_faction: USA 2005 recommended_player_faction: USA 2005
recommended_enemy_faction: Redfor (China) 2010 recommended_enemy_faction: Redfor (China) 2010
description: <p>Welcome to Vegas Nerve, an asymmetrical Red Flag Exercise scenario. You are starting off in control of the two Tonopah airports, and will push south from there. For the duration of this exercise, Creech AFB has been cleared of all fixed wing aircraft and will function as a FARP for rotor ops. OPFOR has a substantial resource advantage and an extensive IADS. Reducing that resource advantage while degrading their IADS will be vital to a successful completion of this exercise. Good luck, Commander.</p> description:
<p>Welcome to Vegas Nerve, an asymmetrical Red Flag Exercise scenario. You are
starting off in control of the two Tonopah airports, and will push south from
there. For the duration of this exercise, Creech AFB has been cleared of all
fixed wing aircraft and will function as a FARP for rotor ops. OPFOR has a
substantial resource advantage and an extensive IADS. Reducing that resource
advantage while degrading their IADS will be vital to a successful completion
of this exercise. Good luck, Commander.</p>
miz: exercise_vegas_nerve.miz miz: exercise_vegas_nerve.miz
performance: 1 performance: 1
recommended_start_date: 2011-02-24 recommended_start_date: 2011-02-24
version: "10.7" version: "10.7"
squadrons: squadrons:
# Tonopah Airport Bombers from Minot AFB:
- primary: Strike
secondary: air-to-ground
aircraft:
- B-52H Stratofortress
size: 4
Bombers from Ellsworth AFB:
- primary: OCA/Runway
secondary: air-to-ground
aircraft:
- B-1B Lancer
size: 4
# Tonopah Airport
17: 17:
- primary: BARCAP - primary: TARCAP
secondary: air-to-air secondary: any
aircraft: aircraft:
- F-15C Eagle - F-15C Eagle
size: 12 size: 12
- primary: Strike - primary: Strike
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- F-15E Strike Eagle (AI)
- F-15E Strike Eagle (Suite 4+) - F-15E Strike Eagle (Suite 4+)
size: 12 size: 12
- primary: AEW&C - primary: AEW&C
aircraft: aircraft:
- E-3A - E-3A
size: 1 size: 1
# Tonopah Test Range # Tonopah Test Range
18: 18:
- primary: BAI - primary: BAI
secondary: air-to-ground secondary: air-to-ground
@@ -52,9 +70,10 @@ squadrons:
- primary: Air Assault - primary: Air Assault
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- UH-1H Iroquois - UH-60L
- UH-60A
size: 2 size: 2
# Groom Lake # Groom Lake
2: 2:
- primary: Escort - primary: Escort
secondary: air-to-air secondary: air-to-air
@@ -66,8 +85,8 @@ squadrons:
aircraft: aircraft:
- Su-25T Frogfoot - Su-25T Frogfoot
size: 20 size: 20
# Creech # Creech
Creech FARP: 1:
- primary: CAS - primary: CAS
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
@@ -78,7 +97,7 @@ squadrons:
aircraft: aircraft:
- Mi-24P Hind-F - Mi-24P Hind-F
size: 4 size: 4
# Nellis AFB # Nellis AFB
4: 4:
- primary: Strike - primary: Strike
secondary: air-to-ground secondary: air-to-ground
@@ -99,7 +118,7 @@ squadrons:
aircraft: aircraft:
- Su-34 Fullback - Su-34 Fullback
size: 20 size: 20
# Boulder City Airport # Boulder City Airport
6: 6:
- primary: SEAD Escort - primary: SEAD Escort
secondary: any secondary: any

View File

@@ -5,7 +5,18 @@ authors: Starfire
recommended_player_faction: D-Day Allied Forces 1944 and 1990 recommended_player_faction: D-Day Allied Forces 1944 and 1990
recommended_enemy_faction: Germany 1944 recommended_enemy_faction: Germany 1944
description: description:
<p>While enroute to the Persian Gulf for Operation Desert Shield, the USS Theodore Roosevelt and its carrier strike group are engufled by an electrical vortex and transported through time and space to the English channel on the morning of the Normandy Landings - June 6th 1944. Seeking to reduce the cost in lives to the Allied Forces about to storm the beaches, the captain of the Roosevelt has elected to provide air support for the landings.</p><p><strong>Note:</strong> This campaign has a custom faction that combines modern US naval forces with WW2 Allied forces. To play it as intended, you should carefully ration your use of modern aircraft and not replenish them if shot down (as you cannot get new Tomcats and Hornets in 1944). You can also choose to play it as a purely WW2 campaign by switching to one of the WW2 Ally factions.</p> <p>While enroute to the Persian Gulf for Operation Desert Shield, the USS
Theodore Roosevelt and its carrier strike group are engufled by an electrical
vortex and transported through time and space to the English channel on the
morning of the Normandy Landings - June 6th 1944. Seeking to reduce the cost
in lives to the Allied Forces about to storm the beaches, the captain of the
Roosevelt has elected to provide air support for the
landings.</p><p><strong>Note:</strong> This campaign has a custom faction that
combines modern US naval forces with WW2 Allied forces. To play it as
intended, you should carefully ration your use of modern aircraft and not
replenish them if shot down (as you cannot get new Tomcats and Hornets in
1944). You can also choose to play it as a purely WW2 campaign by switching to
one of the WW2 Ally factions.</p>
miz: final_countdown_2.miz miz: final_countdown_2.miz
performance: 2 performance: 2
recommended_start_date: 1944-06-06 recommended_start_date: 1944-06-06
@@ -23,6 +34,11 @@ squadrons:
aircraft: aircraft:
- F/A-18C Hornet (Lot 20) - F/A-18C Hornet (Lot 20)
size: 24 size: 24
- primary: CAS
secondary: air-to-ground
aircraft:
- S-3B Viking
size: 12
- primary: AEW&C - primary: AEW&C
aircraft: aircraft:
- E-2C Hawkeye - E-2C Hawkeye
@@ -34,8 +50,9 @@ squadrons:
- primary: Air Assault - primary: Air Assault
secondary: any secondary: any
aircraft: aircraft:
- SH-60B Seahawk - UH-60L
size: 4 - UH-60A
size: 6
#Stoney Cross (39) #Stoney Cross (39)
58: 58:
- primary: OCA/Runway - primary: OCA/Runway
@@ -57,7 +74,7 @@ squadrons:
- MosquitoFBMkVI - MosquitoFBMkVI
size: 20 size: 20
#RAF Grafton Underwood (1000) #RAF Grafton Underwood (1000)
From RAF Grafton Underwood: Bombers from RAF Grafton Underwood:
- primary: Strike - primary: Strike
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
@@ -96,7 +113,7 @@ squadrons:
- primary: Escort - primary: Escort
secondary: any secondary: any
aircraft: aircraft:
- Bf 109 K-4 Kurfürst - Bf 109 K-4 Kurfürst
size: 24 size: 24
#Saint-Andre-de-lEure (30) #Saint-Andre-de-lEure (30)
70: 70:
@@ -115,5 +132,5 @@ squadrons:
- primary: BARCAP - primary: BARCAP
secondary: any secondary: any
aircraft: aircraft:
- Fw 190 A-8 Anton - Fw 190 A-8 Anton
size: 20 size: 20

View File

@@ -7,12 +7,12 @@ recommended_enemy_faction: Russia 2010
description: description:
<p>An Argentinean extremist group has contracted the Sons of Warvan (SoW), an <p>An Argentinean extremist group has contracted the Sons of Warvan (SoW), an
unusually well-equipped PMC with close ties to the Russian government, to unusually well-equipped PMC with close ties to the Russian government, to
construct a beryllium bomb at the secret Omega 13 production facility in Ushaia construct a beryllium bomb at the secret Omega 13 production facility in
for use in its ongoing conflict with Chile. United States military forces have Ushuaia for use in its ongoing conflict with Chile. United States military
established a foothold at San Julian. While the SoW are distracted up north, it forces have established a foothold at San Julian. While the SoW are distracted
is up to the Marines to launch an assault upon Ushaia from an LHA in order to up north, it is up to the Marines to launch an assault upon Ushuaia from an LHA
disable the bomb production facility. Fortunately, Ushaia is lightly defended as in order to disable the bomb production facility. Fortunately, Ushuaia is
the SoW are trying to avoid unwanted attention.</p> lightly defended as the SoW are trying to avoid unwanted attention.</p>
miz: grabthars_hammer.miz miz: grabthars_hammer.miz
performance: 2 performance: 2
recommended_start_date: 1999-12-25 recommended_start_date: 1999-12-25
@@ -24,34 +24,28 @@ squadrons:
secondary: any secondary: any
aircraft: aircraft:
- F-16CM Fighting Falcon (Block 50) - F-16CM Fighting Falcon (Block 50)
size: 16 size: 8
- primary: CAS - primary: CAS
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- A-10C Thunderbolt II (Suite 7) - A-10C Thunderbolt II (Suite 7)
size: 8 size: 8
- primary: Strike - primary: Escort
secondary: air-to-ground secondary: any
aircraft: aircraft:
- B-1B Lancer - F-15C Eagle
size: 12 size: 8
- primary: Refueling - primary: Refueling
aircraft: aircraft:
- KC-135 Stratotanker - KC-135 Stratotanker
size: 1 size: 2
#San Julian #San Julian
11: 11:
- primary: BAI - primary: BAI
secondary: air-to-ground
aircraft:
- F-15E Strike Eagle (AI)
- F-15E Strike Eagle (Suite 4+)
size: 8
- primary: Air Assault
secondary: any secondary: any
aircraft: aircraft:
- UH-1H Iroquois - F-15E Strike Eagle (Suite 4+)
size: 1 size: 8
#Blue CV #Blue CV
Blue-CV: Blue-CV:
- primary: BARCAP - primary: BARCAP
@@ -63,25 +57,26 @@ squadrons:
secondary: any secondary: any
aircraft: aircraft:
- F/A-18C Hornet (Lot 20) - F/A-18C Hornet (Lot 20)
size: 32 size: 40
- primary: Escort - primary: DEAD
secondary: any secondary: air-to-ground
aircraft: aircraft:
- F/A-18C Hornet (Lot 20) - S-3B Viking
size: 32 size: 20
- primary: AEW&C - primary: AEW&C
aircraft: aircraft:
- E-2C Hawkeye - E-2C Hawkeye
size: 1 size: 2
- primary: Refueling - primary: Refueling
aircraft: aircraft:
- S-3B Tanker - S-3B Tanker
size: 3 size: 4
- primary: Air Assault - primary: Air Assault
secondary: any secondary: any
aircraft: aircraft:
- SH-60B Seahawk - UH-60L
size: 2 - UH-60A
size: 4
# Blue LHA # Blue LHA
Blue-LHA: Blue-LHA:
- primary: DEAD - primary: DEAD
@@ -90,10 +85,21 @@ squadrons:
- AV-8B Harrier II Night Attack - AV-8B Harrier II Night Attack
size: 18 size: 18
- primary: Air Assault - primary: Air Assault
secondary: air-to-ground secondary: any
aircraft: aircraft:
- UH-1H Iroquois - UH-1H Iroquois
size: 2 size: 2
Bombers from Edwards AFB:
- primary: Strike
secondary: air-to-ground
aircraft:
- B-52H Stratofortress
size: 12
- primary: OCA/Runway
secondary: air-to-ground
aircraft:
- B-1B Lancer
size: 12
#El Calafate #El Calafate
14: 14:
- primary: Transport - primary: Transport
@@ -102,7 +108,7 @@ squadrons:
size: 10 size: 10
#Rio Gallegros #Rio Gallegros
5: 5:
- primary: BARCAP - primary: Escort
secondary: air-to-air secondary: air-to-air
aircraft: aircraft:
- Su-27 Flanker-B - Su-27 Flanker-B

Binary file not shown.

View File

@@ -0,0 +1,100 @@
---
name: Syria - Operation Aegean Aegis
theater: Syria
authors: Starfire
recommended_player_faction: USA 2005
recommended_enemy_faction: Turkey 2005
description:
<p><strong>Note:</strong> This fictional campaign was designed for the Apache
and Harrier. It requires manual flight planning. While enemy aircraft are present
at airfields, there will be no enemy flights as their aircraft are grounded.</p>
<p>
In a sudden and alarming escalation of tensions in Cyprus, the Anatolian Order,
a North Cypriot insurgent faction, has carried out a bold night-time assault on
three crucial airfields in the Republic of Cyprus; Paphos, Akrotiri, and Larnaca.
The insurgents, equipped with stolen surplus Turkish military hardware, used
chemical weapons in their attack, forcing the evacuation of all three airfields.
Notably, the capture of Akrotiri, a British Overseas Territory hosting a Royal
Air Force base, has drawn significant international attention and concern.</p>
<p>
The EU has strongly condemned this unprovoked attack against one of its member
states. Turkey, despite its historical connections with North Cyprus, has also
denounced the Anatolian Order's actions and is investigating how its aircraft,
ground vehicles, and weaponry held in storage ended up in insurgent hands.</p>
<p>
Amidst the crisis, a lone US Navy LHA, strategically positioned in the Aegean Sea,
is preparing a response to the crisis. Its mission is to deploy Apache helicopters
to neutralise the hastily erected air defenses around the captured airfields, before
Harrier jumpjets neutralise the Anatolian Order's aircraft. These aircraft, a
selection of mothballed Turkish F-4s and helicopters, are currently grounded due to
lack of suitable fuel and spare parts. It is imperative that they are dealt with
swiftly before ground troops are air-lifted in to reclaim the airfields.</p>
<p>
The operation's final phase involves targeting North Cyprus's only airport at Ercan.
The plan is to bomb its runway, preventing any further airborne reinforcements by the
insurgents. However, due to the air defenses established along the northern edge of
the Green Line (the UN-patrolled demilitarised zone) there are strict advisories against
overflying North Cyprus unless absolutely necessary, to minimise the risk of losses.
</p>
miz: operation_aegean_aegis.miz
performance: 1
recommended_start_date: 2017-04-20
recommended_player_money: 1000
recommended_enemy_money: 0
recommended_player_income_multiplier: 1.0
recommended_enemy_income_multiplier: 0.0
version: "10.7"
squadrons:
#Tarawa Class LHA
Blue-LHA:
- primary: DEAD
secondary: air-to-ground
aircraft:
- AH-64D Apache Longbow
size: 12
- primary: DEAD
secondary: air-to-ground
aircraft:
- AV-8B Harrier II Night Attack
size: 6
- primary: Air Assault
secondary: any
aircraft:
- UH-1H Iroquois
size: 2
#Paphos
46:
- primary: DEAD
secondary: air-to-ground
aircraft:
- F-4E Phantom II
size: 12
#Akrotiri
44:
- primary: BAI
secondary: air-to-ground
aircraft:
- AH-1W SuperCobra
size: 4
- primary: CAS
secondary: air-to-ground
aircraft:
- OH-58D Kiowa Warrior
size: 4
- primary: Air Assault
secondary: air-to-ground
aircraft:
- UH-60A
size: 4
#Larnaca
47:
- primary: Transport
aircraft:
- C-130
size: 6
#Ercan
49:
- primary: Transport
aircraft:
- CH-47D
size: 6

View File

@@ -30,7 +30,7 @@ recommended_player_money: 0
recommended_enemy_money: 0 recommended_enemy_money: 0
recommended_player_income_multiplier: 0.2 recommended_player_income_multiplier: 0.2
recommended_enemy_income_multiplier: 0.2 recommended_enemy_income_multiplier: 0.2
version: "10.9" version: "10.7"
squadrons: squadrons:
Blue CV-1: Blue CV-1:
- primary: SEAD - primary: SEAD
@@ -40,7 +40,7 @@ squadrons:
size: 40 size: 40
- primary: AEW&C - primary: AEW&C
aircraft: aircraft:
- E-2C Hawkeye - E-2D Advanced Hawkeye
size: 2 size: 2
- primary: Refueling - primary: Refueling
aircraft: aircraft:
@@ -49,7 +49,8 @@ squadrons:
- primary: Air Assault - primary: Air Assault
secondary: any secondary: any
aircraft: aircraft:
- SH-60B Seahawk - UH-60L
- UH-60A
size: 4 size: 4
#Al Minhad AFB (61) #Al Minhad AFB (61)
12: 12:
@@ -78,12 +79,12 @@ squadrons:
secondary: any secondary: any
aircraft: aircraft:
- F-15E Strike Eagle (Suite 4+) - F-15E Strike Eagle (Suite 4+)
size: 8 size: 20
- primary: BAI - primary: DEAD
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- F-15E Strike Eagle - AV-8B Harrier II Night Attack
size: 12 size: 20
#Bandar Abbas Intl (51) #Bandar Abbas Intl (51)
2: 2:
- primary: SEAD - primary: SEAD

View File

@@ -4,129 +4,157 @@ theater: Syria
authors: Starfire authors: Starfire
recommended_player_faction: Bluefor Modern recommended_player_faction: Bluefor Modern
recommended_enemy_faction: Iraq 1991 recommended_enemy_faction: Iraq 1991
description: <p>This is a semi-fictional what-if scenario for Operation Peace Spring, during which Turkish forces that crossed into Syria on an offensive against Kurdish militias were emboldened by early successes to continue pushing further southward. Attempts to broker a ceasefire have failed. Members of Operation Inherent Resolve have gathered at Ramat David Airbase in Israel to launch a counter-offensive.</p><p><strong>Note:</strong> The default faction is set as Iraq 1991 in order to provide an opponent with a wider variety of units. While Turkey 2005 would be the historical faction (and has preset squadrons included), they only have two jets available (F-4 and F-16).</p> description:
<p>This is a semi-fictional what-if scenario for Operation Peace Spring,
during which Turkish forces that crossed into Syria on an offensive against
Kurdish militias were emboldened by early successes to continue pushing
further southward. Attempts to broker a ceasefire have failed. Members of
Operation Inherent Resolve have gathered at Ramat David Airbase in Israel to
launch a counter-offensive.</p><p><strong>Note:</strong> The default faction
is set as Iraq 1991 in order to provide an opponent with a wider variety of
units. While Turkey 2005 would be the historical faction (and has preset
squadrons included), they only have two jets available (F-4 and F-16).</p>
miz: operation_peace_spring.miz miz: operation_peace_spring.miz
performance: 1 performance: 2
recommended_start_date: 2019-12-23 recommended_start_date: 2019-12-23
version: "10.7" version: "10.7"
squadrons: squadrons:
# Ramat David Blue CV-1:
30:
- primary: CAS
secondary: air-to-ground
aircraft:
- A-10C Thunderbolt II (Suite 7)
size: 8
- primary: SEAD - primary: SEAD
secondary: any secondary: any
aircraft: aircraft:
- F/A-18C Hornet (Lot 20) - F/A-18C Hornet (Lot 20)
size: 40
- primary: BAI
secondary: air-to-ground
aircraft:
- S-3B Viking
size: 20 size: 20
- primary: AEW&C
aircraft:
- E-2D Advanced Hawkeye
size: 4
- primary: Refueling
aircraft:
- S-3B Tanker
size: 4
Bombers from RAF Fairford:
- primary: DEAD
secondary: air-to-ground
aircraft:
- B-52H Stratofortress
size: 8
# Akrotiri
44:
- primary: Strike - primary: Strike
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- F-15E Strike Eagle (AI) - B-1B Lancer
size: 8
- primary: OCA/Runway
secondary: any
aircraft:
- F-15E Strike Eagle (Suite 4+) - F-15E Strike Eagle (Suite 4+)
size: 12 size: 12
- primary: AEW&C - primary: TARCAP
secondary: air-to-air
aircraft: aircraft:
- E-3A - F-15C Eagle
size: 2 size: 20
# King Hussein Air College - primary: Air Assault
19: secondary: any
aircraft:
- UH-60L
- UH-60A
size: 4
# Ramat David
30:
- primary: BAI
secondary: air-to-ground
aircraft:
- A-10C Thunderbolt II (Suite 7)
size: 8
- primary: DEAD - primary: DEAD
secondary: any secondary: any
aircraft: aircraft:
- F-16CM Fighting Falcon (Block 50) - F-16CM Fighting Falcon (Block 50)
size: 20 size: 28
- primary: BAI
secondary: air-to-ground
aircraft:
- AV-8B Harrier II Night Attack
size: 8
- primary: BARCAP
secondary: air-to-air
aircraft:
- F-15C Eagle
size: 12
- primary: CAS - primary: CAS
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- AH-64D Apache Longbow - AH-64D Apache Longbow
size: 8 size: 4
- primary: Refueling # Damascus
aircraft: 7:
- KC-135 Stratotanker - primary: Strike
size: 2
- primary: Air Assault
secondary: air-to-ground secondary: air-to-ground
aircraft:
- UH-1H Iroquois
size: 2
# Damascus
7:
- primary: TARCAP
secondary: air-to-air
aircraft: aircraft:
- F-4E Phantom II - F-4E Phantom II
- Mirage-F1EQ - H-6J Badger
size: 12 size: 16
- primary: BAI - primary: BAI
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- AH-1W SuperCobra - AH-1W SuperCobra
- Su-25 Frogfoot - Su-25 Frogfoot
size: 12 size: 16
- primary: CAS - primary: CAS
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- OH-58D Kiowa Warrior - OH-58D Kiowa Warrior
- Mi-24P Hind-F - Mi-24P Hind-F
size: 8 size: 4
# Tiyas # Tiyas
39: 39:
- primary: SEAD - primary: SEAD
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- F-16CM Fighting Falcon (Block 50) - F-16CM Fighting Falcon (Block 50)
- Su-24M Fencer-D - Su-24M Fencer-D
size: 20 size: 16
# Abu Al Duhur - primary: Escort
1: secondary: air-to-air
- primary: Strike
secondary: air-to-ground
aircraft: aircraft:
- F-4E Phantom II - F-4E Phantom II
- H-6J Badger - Mirage-F1EQ
size: 20 size: 16
# Gaziantep # Abu Al Duhur
1:
- primary: Fighter sweep
secondary: air-to-air
aircraft:
- F-4E Phantom II
- MiG-23ML Flogger-G
size: 8
# Gaziantep
11: 11:
- primary: BARCAP - primary: BARCAP
secondary: any secondary: any
aircraft: aircraft:
- F-16CM Fighting Falcon (Block 50) - F-16CM Fighting Falcon (Block 50)
- MiG-29A Fulcrum-A - MiG-29A Fulcrum-A
size: 12 size: 8
# Incirlik # Incirlik
16: 16:
- primary: Strike - primary: Strike
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- F-4E Phantom II - F-4E Phantom II
- Tu-22M3 Backfire-C - Tu-22M3 Backfire-C
size: 20 size: 16
- primary: AEW&C - primary: AEW&C
aircraft: aircraft:
- E-3A - E-3A
- A-50 - A-50
size: 2 size: 1
- primary: Refueling - primary: Refueling
aircraft: aircraft:
- KC-135 Stratotanker - KC-135 Stratotanker
- IL-78M - IL-78M
size: 2 size: 1
- primary: Transport - primary: Escort
secondary: any
aircraft: aircraft:
- C-130 - F-4E Phantom II
- IL-76MD - MiG-21bis Fishbed-N
size: 4 size: 16

View File

@@ -4,40 +4,50 @@ theater: Caucasus
authors: Starfire authors: Starfire
recommended_player_faction: USA 2005 recommended_player_faction: USA 2005
recommended_enemy_faction: Russia 2010 recommended_enemy_faction: Russia 2010
description: <p>United Nations Observer Mission in Georgia (UNOMIG) observers stationed in Georgia to monitor the ceasefire between Georgia and Abkhazia have been cut off from friendly forces by Russian troops backing the separatist state. The UNOMIG HQ at Sukhumi has been taken, and a small contingent of observers and troops at the Zugdidi Sector HQ will have to make a run for the coast, supported by offshore US naval aircraft. The contingent is aware that their best shot at survival is to swiftly retake Sukhumi before Russian forces have a chance to dig in, so that friendly ground forces can land and reinforce them.</p><p><strong>Note:</strong> Ground unit purchase will not be available past Turn 0 until Sukhumi is retaken, so it is imperative you reach Sukhumi with at least one surviving ground unit to capture it. Two Hueys are available at Zugdidi for some close air support. 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 frontline ground war. Once Sukhumi has been captured, squadrons based in Incirlik Turkey can be ferried in via the "From Incirlik" off-map spawn point.</p> description:
<p><strong>Background</strong> - The United Nations Observer Mission in Georgia (UNOMIG) has been actively monitoring the ceasefire agreement between Georgia and its breakaway region of Abkhazia. Recent developments have escalated tensions in the area, leading to a precarious situation for UN observers and affiliated personnel.
</p><p><strong>Current Situation</strong> - UNOMIG observers, along with a contingent of international troops, have found themselves isolated due to the intervention of Russian forces supporting the separatist ambitions of Abkhazia. The UNOMIG headquarters located in Sukhumi has fallen into enemy hands. A smaller group based at the Zugdidi Sector Headquarters now faces the daunting task of navigating to safety with the aid of offshore naval air support.
</p><p><strong>Objective</strong> - The immediate goal is to orchestrate a strategic withdrawal of UN forces to the coastline, leveraging support from US naval aircraft positioned offshore. The critical mission objective is the rapid recapture of Sukhumi. This action is essential to enable the landing of friendly ground forces and the ferrying in of land-based sqwuadrons from Incirlik.
</p><p><strong>Operational Constraints</strong> - It is crucial to note that reinforcement of ground units will not be possible until Sukhumi is successfully recaptured. This recapture can either be performed using existing UN personnel and ground vehicles, or via heliborne assault troop insertion.
</p><p><strong>Assets & Support</strong> - Available assets include two Huey helicopters for close air support. Commanders may opt to initiate the operation with light vehicles, such as Humvees, employing breakthrough tactics to avoid direct confrontations with enemy forces. Alternatively, the use of heavier ground units is an option for commanders seeking a more conventional combat engagement. Upon the recapture of Sukhumi, additional squadrons from Incirlik, Turkey, will become operational.
</p><p><strong>Secondary Objective</strong> - Consider prioritising the capture of the Batumi airfield, located to the south, for its strategic value as a forward operating base. Commanders should be aware of the inherent risks, as the airfield is relatively small and lacks air defence systems, posing a significant threat to any stationed aircraft.</p>
miz: operation_vectrons_claw.miz miz: operation_vectrons_claw.miz
performance: 1 performance: 1
recommended_start_date: 2008-08-08 recommended_start_date: 2008-08-08
version: "10.7" version: "10.7"
control_points:
Squadrons from Incirlik:
ferry_only: true
squadrons: squadrons:
Blue CV-1: Blue CV-1:
- primary: BARCAP - primary: BARCAP
secondary: any secondary: any
aircraft: aircraft:
- F-14B Tomcat - F-14B Tomcat
size: 20 size: 16
- primary: SEAD - primary: SEAD
secondary: any secondary: any
aircraft: aircraft:
- F/A-18C Hornet (Lot 20) - F/A-18C Hornet (Lot 20)
size: 32 size: 60
- primary: Escort - primary: CAS
secondary: any secondary: air-to-ground
aircraft: aircraft:
- F/A-18C Hornet (Lot 20) - S-3B Viking
size: 32 size: 8
- primary: AEW&C - primary: AEW&C
aircraft: aircraft:
- E-2C Hawkeye - E-2C Hawkeye
size: 1 size: 2
- primary: Refueling - primary: Refueling
aircraft: aircraft:
- S-3B Tanker - S-3B Tanker
size: 3 size: 2
- primary: Air Assault - primary: Air Assault
secondary: any secondary: any
aircraft: aircraft:
- SH-60B Seahawk - UH-60L
- UH-60A
size: 2 size: 2
Blue LHA: Blue LHA:
- primary: BAI - primary: BAI
@@ -45,12 +55,12 @@ squadrons:
aircraft: aircraft:
- AV-8B Harrier II Night Attack - AV-8B Harrier II Night Attack
size: 20 size: 20
From Incirlik: Squadrons from Incirlik:
- primary: CAS - primary: CAS
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- AH-64D Apache Longbow - AH-64D Apache Longbow
size: 8 size: 4
- primary: CAS - primary: CAS
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
@@ -61,32 +71,28 @@ squadrons:
aircraft: aircraft:
- F-16CM Fighting Falcon (Block 50) - F-16CM Fighting Falcon (Block 50)
size: 16 size: 16
- primary: DEAD - primary: BAI
secondary: any secondary: any
aircraft: aircraft:
- F-16CM Fighting Falcon (Block 50)
size: 16
- primary: BAI
secondary: air-to-ground
aircraft:
- F-15E Strike Eagle (AI)
- F-15E Strike Eagle (Suite 4+) - F-15E Strike Eagle (Suite 4+)
size: 12
- primary: Strike
secondary: air-to-ground
aircraft:
- B-1B Lancer
size: 8
- primary: Strike
secondary: air-to-ground
aircraft:
- B-52H Stratofortress
size: 8 size: 8
- primary: Refueling - primary: Refueling
aircraft: aircraft:
- KC-135 Stratotanker - KC-135 Stratotanker
size: 1 size: 1
#FARPs Bombers from RAF Fairford:
- primary: Anti-ship
secondary: air-to-ground
aircraft:
- B-52H Stratofortress
size: 4
Bombers from Base Aérea de Morón:
- primary: OCA/Runway
secondary: air-to-ground
aircraft:
- B-1B Lancer
size: 4
#FARPs
UNOMIG Sector HQ: UNOMIG Sector HQ:
- primary: Transport - primary: Transport
secondary: any secondary: any
@@ -98,8 +104,8 @@ squadrons:
secondary: any secondary: any
aircraft: aircraft:
- Mi-24P Hind-F - Mi-24P Hind-F
size: 8 size: 4
#Sukhumi-Babushara #Sukhumi-Babushara
20: 20:
- primary: TARCAP - primary: TARCAP
secondary: air-to-air secondary: air-to-air
@@ -111,7 +117,7 @@ squadrons:
aircraft: aircraft:
- Su-25T Frogfoot - Su-25T Frogfoot
size: 12 size: 12
#Sochi-Adler #Sochi-Adler
18: 18:
- primary: Escort - primary: Escort
secondary: air-to-air secondary: air-to-air
@@ -128,7 +134,14 @@ squadrons:
aircraft: aircraft:
- Su-34 Fullback - Su-34 Fullback
size: 20 size: 20
#Anapa-Vityazevo #Maykop-Khanskaya
16:
- primary: Strike
secondary: air-to-ground
aircraft:
- Tu-22M3 Backfire-C
size: 20
#Anapa-Vityazevo
12: 12:
- primary: Strike - primary: Strike
secondary: air-to-ground secondary: air-to-ground
@@ -140,9 +153,4 @@ squadrons:
secondary: any secondary: any
aircraft: aircraft:
- SU-33 Flanker-D - SU-33 Flanker-D
size: 18 size: 16
#I am aware there is no Russian LHA. This is just for campaign inversion.
Red LHA:
- primary: BAI
secondary: air-to-ground
size: 20

Binary file not shown.

View File

@@ -0,0 +1,99 @@
---
name: Marianas - Operation Velvet Thunder
theater: MarianaIslands
authors: Starfire
recommended_player_faction: USA 1970
recommended_enemy_faction: NVA 1970
description:
<p>Operation Velvet Thunder is a high-intensity training exercise designed to
prepare fresh troops for the challenges they will face in Vietnam. The dense
jungle and rugged terrain of the Mariana Islands will provide a realistic
backdrop, allowing our forces to hone essential skills in jungle warfare,
unconventional tactics, and counterinsurgency operations. There are multiple
checkpoints scattered across the area of operations that will have to be
captured by Air Assault. Due to the limited size and availability of LZs, it
is vital to pay close attention to where you designate troop drop off zones.
</p><p><strong>Note:</strong> This campaign is intended to be played with the
A-4 Skyhawk and OV-10a aircraft mods active. The C-101CC has also been included
as a stand-in for the Cessna A-37 Dragonfly in order to provide a CAS platform
of roughly equivalent combat capability. This campaign will be updated to use
Heatblur's F-4 Phantom II once it is in early access.</p>
miz: operation_velvet_thunder.miz
performance: 1
recommended_start_date: 1970-11-29
version: "10.7"
settings:
a4_skyhawk: true
f4bc_phantom: true
ov10a_bronco: true
squadrons:
#Andersen AFB
6:
- primary: Escort
secondary: any
aircraft:
- F-5E Tiger II
size: 8
- primary: BAI
secondary: any
aircraft:
- C-101CC Aviojet
size: 8
- primary: CAS
secondary: any
aircraft:
- OV-10A Bronco
size: 8
- primary: SEAD
secondary: any
aircraft:
- F-4C Phantom II
- F-4E Phantom II
size: 8
- primary: Strike
secondary: any
aircraft:
- B-52H Stratofortress
size: 4
- primary: Air Assault
secondary: any
aircraft:
- UH-1H Iroquois
size: 4
- primary: Transport
secondary: any
aircraft:
- CH-47D
size: 4
#Blue CV
Blue-CV:
- primary: DEAD
secondary: any
aircraft:
- A-4E Skyhawk
size: 16
- primary: AEW&C
aircraft:
- E-2C Hawkeye
size: 2
#Rota Intl
1:
- primary: TARCAP
secondary: any
aircraft:
- MiG-19P Farmer-B
size: 8
#Tinian Intl
3:
- primary: Air Assault
secondary: any
aircraft:
- Mi-8MTV2 Hip
size: 4
#Saipan Intl
2:
- primary: BAI
secondary: any
aircraft:
- MiG-21bis Fishbed-N
size: 8

View File

@@ -14,6 +14,8 @@ recommended_player_money: 2000
recommended_enemy_money: 1000 recommended_enemy_money: 1000
recommended_player_income_multiplier: 1.5 recommended_player_income_multiplier: 1.5
recommended_enemy_income_multiplier: 0.7 recommended_enemy_income_multiplier: 0.7
settings:
airbase_threat_range: 300
squadrons: squadrons:
#BLUFOR CVN #BLUFOR CVN
Naval-3: Naval-3:

View File

@@ -2,30 +2,6 @@ local unitPayloads = {
["name"] = "AH-64D_BLK_II", ["name"] = "AH-64D_BLK_II",
["payloads"] = { ["payloads"] = {
[1] = { [1] = {
["name"] = "Retribution CAS",
["pylons"] = {
[1] = {
["CLSID"] = "{88D18A5E-99C8-4B04-B40B-1C02F2018B6E}",
["num"] = 3,
},
[2] = {
["CLSID"] = "{88D18A5E-99C8-4B04-B40B-1C02F2018B6E}",
["num"] = 4,
},
[3] = {
["CLSID"] = "{88D18A5E-99C8-4B04-B40B-1C02F2018B6E}",
["num"] = 2,
},
[4] = {
["CLSID"] = "{88D18A5E-99C8-4B04-B40B-1C02F2018B6E}",
["num"] = 1,
},
},
["tasks"] = {
[1] = 31,
},
},
[2] = {
["displayName"] = "Retribution BAI", ["displayName"] = "Retribution BAI",
["name"] = "Retribution BAI", ["name"] = "Retribution BAI",
["pylons"] = { ["pylons"] = {
@@ -45,12 +21,16 @@ local unitPayloads = {
["CLSID"] = "{M299_4xAGM_114L}", ["CLSID"] = "{M299_4xAGM_114L}",
["num"] = 1, ["num"] = 1,
}, },
[5] = {
["CLSID"] = "{AN_APG_78}",
["num"] = 6,
},
}, },
["tasks"] = { ["tasks"] = {
[1] = 31, [1] = 31,
}, },
}, },
[3] = { [2] = {
["name"] = "Retribution OCA/Aircraft", ["name"] = "Retribution OCA/Aircraft",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
@@ -69,14 +49,46 @@ local unitPayloads = {
["CLSID"] = "{M261_M229}", ["CLSID"] = "{M261_M229}",
["num"] = 1, ["num"] = 1,
}, },
[5] = {
["CLSID"] = "{AN_APG_78}",
["num"] = 6,
},
},
["tasks"] = {
[1] = 31,
},
},
[3] = {
["name"] = "Retribution CAS",
["pylons"] = {
[1] = {
["CLSID"] = "{88D18A5E-99C8-4B04-B40B-1C02F2018B6E}",
["num"] = 3,
},
[2] = {
["CLSID"] = "{M299_4xAGM_114L}",
["num"] = 4,
},
[3] = {
["CLSID"] = "{88D18A5E-99C8-4B04-B40B-1C02F2018B6E}",
["num"] = 2,
},
[4] = {
["CLSID"] = "{M299_4xAGM_114L}",
["num"] = 1,
},
[5] = {
["CLSID"] = "{AN_APG_78}",
["num"] = 6,
},
}, },
["tasks"] = { ["tasks"] = {
[1] = 31, [1] = 31,
}, },
}, },
[4] = { [4] = {
["displayName"] = "Retribution Escort", ["displayName"] = "Retribution DEAD",
["name"] = "Retribution Escort", ["name"] = "Retribution DEAD",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{M299_4xAGM_114L}", ["CLSID"] = "{M299_4xAGM_114L}",
@@ -94,6 +106,39 @@ local unitPayloads = {
["CLSID"] = "{M299_4xAGM_114L}", ["CLSID"] = "{M299_4xAGM_114L}",
["num"] = 1, ["num"] = 1,
}, },
[5] = {
["CLSID"] = "{AN_APG_78}",
["num"] = 6,
},
},
["tasks"] = {
[1] = 31,
},
},
[5] = {
["displayName"] = "Retribution Escort",
["name"] = "Retribution Escort",
["pylons"] = {
[1] = {
["CLSID"] = "{88D18A5E-99C8-4B04-B40B-1C02F2018B6E}",
["num"] = 3,
},
[2] = {
["CLSID"] = "{M299_4xAGM_114L}",
["num"] = 4,
},
[3] = {
["CLSID"] = "{88D18A5E-99C8-4B04-B40B-1C02F2018B6E}",
["num"] = 2,
},
[4] = {
["CLSID"] = "{M299_4xAGM_114L}",
["num"] = 1,
},
[5] = {
["CLSID"] = "{AN_APG_78}",
["num"] = 6,
},
}, },
["tasks"] = { ["tasks"] = {
[1] = 31, [1] = 31,

View File

@@ -2,84 +2,81 @@ local unitPayloads = {
["name"] = "B-52H", ["name"] = "B-52H",
["payloads"] = { ["payloads"] = {
[1] = { [1] = {
["name"] = "ANTISHIP",
["pylons"] = {
[1] = {
["CLSID"] = "{46ACDCF8-5451-4E26-BDDB-E78D5830E93C}",
["num"] = 2,
},
},
["tasks"] = {
[1] = 30,
},
},
[2] = {
["name"] = "STRIKE",
["pylons"] = {
[1] = {
["CLSID"] = "{696CFFC4-0BDE-42A8-BE4B-0BE3D9DD723C}",
["num"] = 1,
},
[2] = {
["CLSID"] = "{696CFFC4-0BDE-42A8-BE4B-0BE3D9DD723C}",
["num"] = 3,
},
},
["tasks"] = {
[1] = 32,
[2] = 34,
},
},
[3] = {
["name"] = "SEAD",
["pylons"] = {
[1] = {
["CLSID"] = "{8DCAF3A3-7FCF-41B8-BB88-58DEDA878EDE}",
["num"] = 2,
},
},
["tasks"] = {
[1] = 33,
},
},
[4] = {
["name"] = "CAP",
["pylons"] = {
},
["tasks"] = {
},
},
[5] = {
["name"] = "CAS",
["pylons"] = {
[1] = {
["CLSID"] = "{696CFFC4-0BDE-42A8-BE4B-0BE3D9DD723C}",
["num"] = 3,
},
[2] = {
["CLSID"] = "{696CFFC4-0BDE-42A8-BE4B-0BE3D9DD723C}",
["num"] = 1,
},
},
["tasks"] = {
},
},
[6] = {
["displayName"] = "Retribution OCA/Runway",
["name"] = "Retribution OCA/Runway", ["name"] = "Retribution OCA/Runway",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{696CFFC4-0BDE-42A8-BE4B-0BE3D9DD723C}", ["CLSID"] = "{585D626E-7F42-4073-AB70-41E728C333E2}",
["num"] = 1, ["num"] = 3,
}, },
[2] = { [2] = {
["CLSID"] = "{696CFFC4-0BDE-42A8-BE4B-0BE3D9DD723C}", ["CLSID"] = "{6C47D097-83FF-4FB2-9496-EAB36DDF0B05}",
["num"] = 3, ["num"] = 2,
},
[3] = {
["CLSID"] = "{585D626E-7F42-4073-AB70-41E728C333E2}",
["num"] = 1,
},
},
["tasks"] = {
[1] = 32,
},
},
[2] = {
["name"] = "Retribution DEAD",
["pylons"] = {
[1] = {
["CLSID"] = "{45447F82-01B5-4029-A572-9AAD28AF0275}",
["num"] = 3,
},
[2] = {
["CLSID"] = "{8DCAF3A3-7FCF-41B8-BB88-58DEDA878EDE}",
["num"] = 2,
},
[3] = {
["CLSID"] = "{45447F82-01B5-4029-A572-9AAD28AF0275}",
["num"] = 1,
},
},
["tasks"] = {
[1] = 32,
},
},
[3] = {
["displayName"] = "Retribution Anti-ship",
["name"] = "Retribution Anti-ship",
["pylons"] = {
[1] = {
["CLSID"] = "{HSAB-6xAGM-84}",
["num"] = 3,
},
[2] = {
["CLSID"] = "{HSAB-6xAGM-84}",
["num"] = 1,
},
},
["tasks"] = {
[1] = 32,
},
},
[4] = {
["displayName"] = "Retribution Strike",
["name"] = "Retribution Strike",
["pylons"] = {
[1] = {
["CLSID"] = "{585D626E-7F42-4073-AB70-41E728C333E2}",
["num"] = 3,
},
[2] = {
["CLSID"] = "{6C47D097-83FF-4FB2-9496-EAB36DDF0B05}",
["num"] = 2,
},
[3] = {
["CLSID"] = "{585D626E-7F42-4073-AB70-41E728C333E2}",
["num"] = 1,
}, },
}, },
["tasks"] = { ["tasks"] = {
[1] = 32, [1] = 32,
[2] = 34,
}, },
}, },
}, },

View File

@@ -2,133 +2,127 @@ local unitPayloads = {
["name"] = "Ka-50", ["name"] = "Ka-50",
["payloads"] = { ["payloads"] = {
[1] = { [1] = {
["name"] = "CAS", ["name"] = "Retribution CAS",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}", ["CLSID"] = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}",
["num"] = 1, ["num"] = 4,
}, },
[2] = { [2] = {
["CLSID"] = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}", ["CLSID"] = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}",
["num"] = 2, ["num"] = 1,
}, },
[3] = { [3] = {
["CLSID"] = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}", ["CLSID"] = "B_8V20A_OFP2",
["num"] = 3, ["num"] = 3,
}, },
[4] = { [4] = {
["CLSID"] = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}", ["CLSID"] = "B_8V20A_OFP2",
["num"] = 4, ["num"] = 2,
}, },
}, },
["tasks"] = { ["tasks"] = {
[1] = 31, [1] = 31,
[2] = 32,
[3] = 18,
}, },
}, },
[2] = { [2] = {
["name"] = "CAP", ["displayName"] = "Retribution BAI",
["name"] = "Retribution BAI",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}", ["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
["num"] = 1, ["num"] = 4,
}, },
[2] = { [2] = {
["CLSID"] = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}", ["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
["num"] = 2, ["num"] = 1,
}, },
[3] = { [3] = {
["CLSID"] = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}", ["CLSID"] = "B_8V20A_OFP2",
["num"] = 3, ["num"] = 3,
}, },
[4] = { [4] = {
["CLSID"] = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}", ["CLSID"] = "B_8V20A_OFP2",
["num"] = 4, ["num"] = 2,
}, },
}, },
["tasks"] = { ["tasks"] = {
[1] = 31, [1] = 31,
[2] = 32,
[3] = 18,
}, },
}, },
[3] = { [3] = {
["name"] = "SEAD", ["displayName"] = "Retribution DEAD",
["name"] = "Retribution DEAD",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}", ["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
["num"] = 1, ["num"] = 4,
}, },
[2] = { [2] = {
["CLSID"] = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}", ["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
["num"] = 2, ["num"] = 1,
}, },
[3] = { [3] = {
["CLSID"] = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}", ["CLSID"] = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}",
["num"] = 3, ["num"] = 3,
}, },
[4] = { [4] = {
["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}", ["CLSID"] = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}",
["num"] = 4, ["num"] = 2,
}, },
}, },
["tasks"] = { ["tasks"] = {
[1] = 31, [1] = 31,
[2] = 32,
[3] = 18,
}, },
}, },
[4] = { [4] = {
["name"] = "ANTISHIP", ["displayName"] = "Retribution OCA/Aircraft",
["name"] = "Retribution OCA/Aircraft",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}", ["CLSID"] = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}",
["num"] = 1, ["num"] = 4,
}, },
[2] = { [2] = {
["CLSID"] = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}", ["CLSID"] = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}",
["num"] = 2, ["num"] = 1,
}, },
[3] = { [3] = {
["CLSID"] = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}", ["CLSID"] = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}",
["num"] = 3, ["num"] = 3,
}, },
[4] = { [4] = {
["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}", ["CLSID"] = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}",
["num"] = 4, ["num"] = 2,
}, },
}, },
["tasks"] = { ["tasks"] = {
[1] = 31, [1] = 31,
[2] = 32,
[3] = 18,
}, },
}, },
[5] = { [5] = {
["name"] = "STRIKE", ["displayName"] = "Retribution Escort",
["name"] = "Retribution Escort",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}", ["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
["num"] = 1, ["num"] = 4,
}, },
[2] = { [2] = {
["CLSID"] = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}", ["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
["num"] = 2, ["num"] = 1,
}, },
[3] = { [3] = {
["CLSID"] = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}", ["CLSID"] = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}",
["num"] = 3, ["num"] = 3,
}, },
[4] = { [4] = {
["CLSID"] = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}", ["CLSID"] = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}",
["num"] = 4, ["num"] = 2,
}, },
}, },
["tasks"] = { ["tasks"] = {
[1] = 31, [1] = 31,
[2] = 32,
[3] = 18,
}, },
}, },
}, },

View File

@@ -2,7 +2,7 @@ local unitPayloads = {
["name"] = "Ka-50_3", ["name"] = "Ka-50_3",
["payloads"] = { ["payloads"] = {
[1] = { [1] = {
["name"] = "CAS", ["name"] = "Retribution CAS",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{9S846_2xIGLA}", ["CLSID"] = "{9S846_2xIGLA}",
@@ -34,8 +34,8 @@ local unitPayloads = {
}, },
}, },
[2] = { [2] = {
["displayName"] = "STRIKE", ["displayName"] = "Retribution BAI",
["name"] = "STRIKE", ["name"] = "Retribution BAI",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{9S846_2xIGLA}", ["CLSID"] = "{9S846_2xIGLA}",
@@ -54,20 +54,86 @@ local unitPayloads = {
["num"] = 1, ["num"] = 1,
}, },
[5] = { [5] = {
["CLSID"] = "{37DCC01E-9E02-432F-B61D-10C166CA2798}", ["CLSID"] = "B_8V20A_OFP2",
["num"] = 3, ["num"] = 3,
}, },
[6] = { [6] = {
["CLSID"] = "{37DCC01E-9E02-432F-B61D-10C166CA2798}", ["CLSID"] = "B_8V20A_OFP2",
["num"] = 2, ["num"] = 2,
}, },
}, },
["tasks"] = { ["tasks"] = {
[1] = 31, [1] = 31,
[2] = 32,
}, },
}, },
[3] = { [3] = {
["displayName"] = "Retribution OCA/Aircraft",
["name"] = "Retribution OCA/Aircraft",
["pylons"] = {
[1] = {
["CLSID"] = "{9S846_2xIGLA}",
["num"] = 6,
},
[2] = {
["CLSID"] = "{9S846_2xIGLA}",
["num"] = 5,
},
[3] = {
["CLSID"] = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}",
["num"] = 4,
},
[4] = {
["CLSID"] = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}",
["num"] = 1,
},
[5] = {
["CLSID"] = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}",
["num"] = 3,
},
[6] = {
["CLSID"] = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}",
["num"] = 2,
},
},
["tasks"] = {
[1] = 31,
},
},
[4] = {
["displayName"] = "Retribution DEAD",
["name"] = "Retribution DEAD",
["pylons"] = {
[1] = {
["CLSID"] = "{9S846_2xIGLA}",
["num"] = 6,
},
[2] = {
["CLSID"] = "{9S846_2xIGLA}",
["num"] = 5,
},
[3] = {
["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
["num"] = 4,
},
[4] = {
["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
["num"] = 1,
},
[5] = {
["CLSID"] = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}",
["num"] = 3,
},
[6] = {
["CLSID"] = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}",
["num"] = 2,
},
},
["tasks"] = {
[1] = 31,
},
},
[5] = {
["displayName"] = "Retribution Escort",
["name"] = "Retribution Escort", ["name"] = "Retribution Escort",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
@@ -79,19 +145,19 @@ local unitPayloads = {
["num"] = 5, ["num"] = 5,
}, },
[3] = { [3] = {
["CLSID"] = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}", ["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
["num"] = 4, ["num"] = 4,
}, },
[4] = { [4] = {
["CLSID"] = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}", ["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
["num"] = 1, ["num"] = 1,
}, },
[5] = { [5] = {
["CLSID"] = "B_8V20A_OFP2", ["CLSID"] = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}",
["num"] = 3, ["num"] = 3,
}, },
[6] = { [6] = {
["CLSID"] = "B_8V20A_OFP2", ["CLSID"] = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}",
["num"] = 2, ["num"] = 2,
}, },
}, },

View File

@@ -2,63 +2,49 @@ local unitPayloads = {
["name"] = "S-3B", ["name"] = "S-3B",
["payloads"] = { ["payloads"] = {
[1] = { [1] = {
["name"] = "SEAD", ["displayName"] = "Retribution OCA/Aircraft",
["name"] = "Retribution OCA/Aircraft",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{AF42E6DF-9A60-46D8-A9A0-1708B241AADB}", ["CLSID"] = "{B83CB620-5BBE-4BEA-910C-EB605A327EF9}",
["num"] = 1, ["num"] = 6,
}, },
[2] = { [2] = {
["CLSID"] = "{ADD3FAE1-EBF6-4EF9-8EFC-B36B5DDF1E6B}", ["CLSID"] = "{B83CB620-5BBE-4BEA-910C-EB605A327EF9}",
["num"] = 2, ["num"] = 1,
}, },
[3] = { [3] = {
["CLSID"] = "{ADD3FAE1-EBF6-4EF9-8EFC-B36B5DDF1E6B}", ["CLSID"] = "{ADD3FAE1-EBF6-4EF9-8EFC-B36B5DDF1E6B}",
["num"] = 3, ["num"] = 2,
}, },
[4] = { [4] = {
["CLSID"] = "{ADD3FAE1-EBF6-4EF9-8EFC-B36B5DDF1E6B}", ["CLSID"] = "{ADD3FAE1-EBF6-4EF9-8EFC-B36B5DDF1E6B}",
["num"] = 4, ["num"] = 3,
}, },
[5] = { [5] = {
["CLSID"] = "{ADD3FAE1-EBF6-4EF9-8EFC-B36B5DDF1E6B}", ["CLSID"] = "{ADD3FAE1-EBF6-4EF9-8EFC-B36B5DDF1E6B}",
["num"] = 5, ["num"] = 4,
}, },
[6] = { [6] = {
["CLSID"] = "{AF42E6DF-9A60-46D8-A9A0-1708B241AADB}", ["CLSID"] = "{ADD3FAE1-EBF6-4EF9-8EFC-B36B5DDF1E6B}",
["num"] = 6, ["num"] = 5,
}, },
}, },
["tasks"] = { ["tasks"] = {
[1] = 29, [1] = 30,
}, },
}, },
[2] = { [2] = {
["name"] = "ANTISHIP", ["displayName"] = "Retribution DEAD",
["name"] = "Retribution DEAD",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{8B7CADF9-4954-46B3-8CFB-93F2F5B90B03}", ["CLSID"] = "{AF42E6DF-9A60-46D8-A9A0-1708B241AADB}",
["num"] = 1, ["num"] = 6,
}, },
[2] = { [2] = {
["CLSID"] = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}", ["CLSID"] = "{AF42E6DF-9A60-46D8-A9A0-1708B241AADB}",
["num"] = 2, ["num"] = 1,
},
[3] = {
["CLSID"] = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}",
["num"] = 3,
},
[4] = {
["CLSID"] = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}",
["num"] = 4,
},
[5] = {
["CLSID"] = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}",
["num"] = 5,
},
[6] = {
["CLSID"] = "{8B7CADF9-4954-46B3-8CFB-93F2F5B90B03}",
["num"] = 6,
}, },
}, },
["tasks"] = { ["tasks"] = {
@@ -66,52 +52,102 @@ local unitPayloads = {
}, },
}, },
[3] = { [3] = {
["name"] = "STRIKE", ["displayName"] = "Retribution Anti-ship",
["name"] = "Retribution Anti-ship",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B66}", ["CLSID"] = "{8B7CADF9-4954-46B3-8CFB-93F2F5B90B03}",
["num"] = 1, ["num"] = 6,
}, },
[2] = { [2] = {
["CLSID"] = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}", ["CLSID"] = "{8B7CADF9-4954-46B3-8CFB-93F2F5B90B03}",
["num"] = 2, ["num"] = 1,
},
[3] = {
["CLSID"] = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}",
["num"] = 3,
},
[4] = {
["CLSID"] = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}",
["num"] = 4,
},
[5] = {
["CLSID"] = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}",
["num"] = 5,
},
[6] = {
["CLSID"] = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B66}",
["num"] = 6,
}, },
}, },
["tasks"] = { ["tasks"] = {
[1] = 32, [1] = 30,
[1] = 33,
}, },
}, },
[4] = { [4] = {
["name"] = "CAS", ["name"] = "Retribution CAS",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{A76344EB-32D2-4532-8FA2-0C1BDC00747E}", ["CLSID"] = "{444BA8AE-82A7-4345-842E-76154EFCCA46}",
["num"] = 1, ["num"] = 6,
}, },
[2] = { [2] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}", ["CLSID"] = "{444BA8AE-82A7-4345-842E-76154EFCCA46}",
["num"] = 1,
},
[3] = {
["CLSID"] = "{ADD3FAE1-EBF6-4EF9-8EFC-B36B5DDF1E6B}",
["num"] = 2, ["num"] = 2,
}, },
[4] = {
["CLSID"] = "{ADD3FAE1-EBF6-4EF9-8EFC-B36B5DDF1E6B}",
["num"] = 3,
},
[5] = {
["CLSID"] = "{ADD3FAE1-EBF6-4EF9-8EFC-B36B5DDF1E6B}",
["num"] = 4,
},
[6] = {
["CLSID"] = "{ADD3FAE1-EBF6-4EF9-8EFC-B36B5DDF1E6B}",
["num"] = 5,
},
},
["tasks"] = {
[1] = 30,
},
},
[5] = {
["displayName"] = "Retribution BAI",
["name"] = "Retribution BAI",
["pylons"] = {
[1] = {
["CLSID"] = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B66}",
["num"] = 6,
},
[2] = {
["CLSID"] = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B66}",
["num"] = 1,
},
[3] = {
["CLSID"] = "{ADD3FAE1-EBF6-4EF9-8EFC-B36B5DDF1E6B}",
["num"] = 5,
},
[4] = {
["CLSID"] = "{ADD3FAE1-EBF6-4EF9-8EFC-B36B5DDF1E6B}",
["num"] = 4,
},
[5] = {
["CLSID"] = "{ADD3FAE1-EBF6-4EF9-8EFC-B36B5DDF1E6B}",
["num"] = 3,
},
[6] = {
["CLSID"] = "{ADD3FAE1-EBF6-4EF9-8EFC-B36B5DDF1E6B}",
["num"] = 2,
},
},
["tasks"] = {
[1] = 30,
},
},
[6] = {
["displayName"] = "Retribution Strike",
["name"] = "Retribution Strike",
["pylons"] = {
[1] = {
["CLSID"] = "{60CC734F-0AFA-4E2E-82B8-93B941AB11CF}",
["num"] = 6,
},
[2] = {
["CLSID"] = "{60CC734F-0AFA-4E2E-82B8-93B941AB11CF}",
["num"] = 1,
},
[3] = { [3] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}", ["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 3, ["num"] = 5,
}, },
[4] = { [4] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}", ["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
@@ -119,49 +155,19 @@ local unitPayloads = {
}, },
[5] = { [5] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}", ["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 5, ["num"] = 3,
}, },
[6] = { [6] = {
["CLSID"] = "{A76344EB-32D2-4532-8FA2-0C1BDC00747E}", ["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 6, ["num"] = 2,
}, },
}, },
["tasks"] = { ["tasks"] = {
[1] = 31, [1] = 30,
}, },
}, },
}, },
[5] = { ["tasks"] = {
["name"] = "RUNWAY_ATTACK",
["pylons"] = {
[1] = {
["CLSID"] = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}",
["num"] = 1,
},
[2] = {
["CLSID"] = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}",
["num"] = 2,
},
[3] = {
["CLSID"] = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}",
["num"] = 3,
},
[4] = {
["CLSID"] = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}",
["num"] = 4,
},
[5] = {
["CLSID"] = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}",
["num"] = 5,
},
[6] = {
["CLSID"] = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}",
["num"] = 6,
},
},
["tasks"] = {
[1] = 34,
},
}, },
["unitType"] = "S-3B", ["unitType"] = "S-3B",
} }

View File

@@ -21,6 +21,7 @@
"S-3B Viking", "S-3B Viking",
"F/A-18C Hornet (Lot 20)", "F/A-18C Hornet (Lot 20)",
"F-14B Tomcat", "F-14B Tomcat",
"F-14A Tomcat (Block 135-GR Late)",
"UH-1H Iroquois", "UH-1H Iroquois",
"B-1B Lancer" "B-1B Lancer"

View File

@@ -0,0 +1,96 @@
{
"country": "Egypt",
"name": "Egypt 2000",
"authors": "Starfire",
"description": "<p>Egyptian military in the 21st century.</p>",
"locales": [
"ar_SA"
],
"aircrafts": [
"MiG-29S Fulcrum-C",
"MiG-21bis Fishbed-N",
"Mirage 2000C",
"F-4E Phantom II",
"F-16CM Fighting Falcon (Block 50)",
"IL-76MD",
"C-130",
"C-130J-30 Super Hercules",
"AH-64D Apache Longbow",
"AH-64D Apache Longbow (AI)",
"SA 342L Gazelle",
"SA 342M Gazelle",
"CH-47D",
"Ka-50 Hokum",
"Ka-50 Hokum (Blackshark 3)",
"Mi-24V Hind-E",
"Mi-24P Hind-F",
"Mi-8MTV2 Hip"
],
"awacs": [
"E-2C Hawkeye"
],
"frontline_units": [
"M1A2 Abrams",
"M60A3 \"Patton\"",
"T-90A",
"T-55A",
"BMP-1",
"M113",
"BTR-80",
"M1043 HMMWV (M2 HMG)",
"M1045 HMMWV (BGM-71 TOW)",
"M1097 Heavy HMMWV Avenger",
"ZSU-23-4 Shilka",
"M163 Vulcan Air Defense System"
],
"artillery_units": [
"M109A6 Paladin",
"M270 Multiple Launch Rocket System"
],
"logistics_units": [
"Truck Ural-375",
"Truck Ural-4320T",
"Truck GAZ-66"
],
"infantry_units": [
"Infantry RPG",
"Infantry AK-74 Rus",
"MANPADS SA-18 Igla \"Grouse\"",
"MANPADS Stinger",
"Mortar 2B11 120mm",
"Paratrooper AKS",
"Paratrooper RPG-16"
],
"preset_groups": [
"SA-2/S-75",
"SA-6",
"SA-17",
"SA-10/S-300PS",
"SA-23/S-300VM",
"Patriot",
"Hawk"
],
"naval_units": [
"FFG Oliver Hazard Perry",
"Corvette 1241.1 Molniya",
"FAC La Combattante IIa"
],
"missiles": [
"SSM SS-1C Scud-B"
],
"air_defense_units": [
"EWR AN/FPS-117 Radar",
"EWR 55G6",
"M1097 Heavy HMMWV Avenger",
"M48 Chaparral",
"M163 Vulcan Air Defense System",
"SA-9 Strela",
"SA-15 Tor",
"ZSU-23-4 Shilka",
"AAA ZU-23 Closed Emplacement",
"ZU-23 on Ural-375",
"ZSU-57-2 'Sparka'"
],
"has_jtac": true,
"jtac_unit": "MQ-9 Reaper"
}

View File

@@ -3,6 +3,9 @@
"name": "D-Day Allied Forces 1944 and 1990", "name": "D-Day Allied Forces 1944 and 1990",
"authors": "Starfire", "authors": "Starfire",
"description": "<p>Faction for Final Countdown II</p>", "description": "<p>Faction for Final Countdown II</p>",
"locales": [
"en_US"
],
"aircrafts": [ "aircrafts": [
"Boston Mk.III", "Boston Mk.III",
"Fortress Mk.III", "Fortress Mk.III",
@@ -12,7 +15,9 @@
"MosquitoFBMkVI", "MosquitoFBMkVI",
"F-14B Tomcat", "F-14B Tomcat",
"F/A-18C Hornet (Lot 20)", "F/A-18C Hornet (Lot 20)",
"SH-60B Seahawk" "S-3B Viking",
"UH-60L",
"UH-60A"
], ],
"awacs": [ "awacs": [
"E-2C Hawkeye" "E-2C Hawkeye"
@@ -39,24 +44,19 @@
"Truck GMC \"Jimmy\" 6x6 Truck" "Truck GMC \"Jimmy\" 6x6 Truck"
], ],
"infantry_units": [ "infantry_units": [
"Infantry M1 Garand", "Infantry M1 Garand"
"Infantry SMLE No.4 Mk-1" ],
"naval_units": [
"DDG Arleigh Burke IIa",
"CG Ticonderoga",
"CVN-74 John C. Stennis"
], ],
"missiles": [], "missiles": [],
"air_defense_units": [ "air_defense_units": [
"Bofors 40 mm Gun" "Bofors 40 mm Gun"
], ],
"preset_groups": [ "preset_groups": [
"Ally Flak", "Ally Flak"
"WW2LST"
],
"locales": [
"en_US"
],
"naval_units": [
"DDG Arleigh Burke IIa",
"CG Ticonderoga",
"CVN-74 John C. Stennis"
], ],
"requirements": { "requirements": {
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/" "WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/"
@@ -64,10 +64,10 @@
"carrier_names": [ "carrier_names": [
"CVN-71 Theodore Roosevelt" "CVN-71 Theodore Roosevelt"
], ],
"helicopter_carrier_names": [],
"has_jtac": true, "has_jtac": true,
"doctrine": "ww2", "jtac_unit": "MQ-9 Reaper",
"unrestricted_satnav": true, "unrestricted_satnav": true,
"doctrine": "ww2",
"building_set": "ww2ally", "building_set": "ww2ally",
"cargo_ship": "LST Mk.II" "cargo_ship": "LST Mk.II"
} }

View File

@@ -0,0 +1,55 @@
{
"country": "Vietnam",
"name": "NVA 1970",
"authors": "Starfire",
"description": "<p>North Vietnamese Army during the Vietnam War from 1965 to 1975</p>",
"locales": [
"vi_Vn"
],
"aircrafts": [
"Mi-8MTV2 Hip",
"MiG-19P Farmer-B",
"MiG-21bis Fishbed-N"
],
"awacs": [],
"tankers": [],
"frontline_units": [
"T-55A",
"PT-76",
"M2A1 Half-Track",
"Grad MRL FDDM (FC)"
],
"artillery_units": [
"BM-21 Grad"
],
"logistics_units": [
"LUV UAZ-469 Jeep",
"Truck Ural-375"
],
"infantry_units": [
"Infantry AK-74 Rus",
"Infantry RPG"
],
"missiles": [],
"preset_groups": [
"SA-2/S-75",
"SA-3/S-125",
"KS-19/SON-9",
"Cold-War-Flak"
],
"naval_units": [
"Boat Armed Hi-speed",
"Boat Schnellboot type S130"
],
"air_defense_units": [
"SAM P19 \"Flat Face\" SR (SA-2/3)",
"S-60 57mm",
"8.8 cm Flak 18",
"ZSU-57-2 'Sparka'",
"AAA ZU-23 Emplacement",
"ZU-23 on Ural-375",
"ZSU-23-4 Shilka"
],
"requirements": {},
"doctrine": "coldwar"
}

View File

@@ -10,6 +10,7 @@
"J-15 Flanker X-2", "J-15 Flanker X-2",
"J-7B", "J-7B",
"Ka-50 Hokum", "Ka-50 Hokum",
"Ka-50 Hokum (Blackshark 3)",
"L-39ZA Albatros", "L-39ZA Albatros",
"Mi-24V Hind-E", "Mi-24V Hind-E",
"Mi-24P Hind-F", "Mi-24P Hind-F",

View File

@@ -0,0 +1,71 @@
{
"country": "USA",
"name": "USA 1970",
"authors": "Starfire",
"description": "<p>US military during the Vietnam War from 1965 to 1975</p>",
"locales": [
"en_US"
],
"aircrafts": [
"F-14A Tomcat (Block 135-GR Late)",
"F-4C Phantom II",
"F-4B Phantom II",
"F-4E Phantom II",
"F-5E Tiger II",
"A-4E Skyhawk",
"OV-10A Bronco",
"C-101CC Aviojet",
"B-52H Stratofortress",
"C-47 Skytrain",
"C-130",
"UH-1H Iroquois",
"AH-1W SuperCobra",
"OH-58D Kiowa Warrior",
"CH-47D",
"CH-53E"
],
"awacs": [
"E-2C Hawkeye"
],
"tankers": [
"KC-130",
"KC-135 Stratotanker"
],
"frontline_units": [
"M113",
"M163 Vulcan Air Defense System",
"M60A3 \"Patton\""
],
"artillery_units": [
"M109A6 Paladin"
],
"logistics_units": [
"Truck M818 6x6",
"LARC-V"
],
"infantry_units": [
"Infantry M249",
"Infantry M4"
],
"missiles": [],
"preset_groups": [
"Hawk"
],
"naval_units": [
"FFG Oliver Hazard Perry",
"CV-59 Forrestal"
],
"air_defense_units": [
"SAM Hawk SR (AN/MPQ-50)",
"M163 Vulcan Air Defense System",
"M48 Chaparral"
],
"carrier_names": [
"CV-59 Forrestal",
"CV-60 Saratoga",
"CV-61 Ranger",
"CV-62 Independence"
],
"requirements": {},
"doctrine": "coldwar"
}

View File

@@ -106,7 +106,6 @@
"UH-1H Iroquois" "UH-1H Iroquois"
], ],
"awacs": [ "awacs": [
"E-2C Hawkeye",
"E-2D Advanced Hawkeye", "E-2D Advanced Hawkeye",
"E-3A" "E-3A"
], ],

View File

@@ -1,5 +1,5 @@
{ {
"nameInUI": "DCS Dismounts", "nameInUI": "DCS Dismounts (FPS-killer)",
"defaultValue": false, "defaultValue": false,
"specificOptions": [ "specificOptions": [
{ {

File diff suppressed because it is too large Load Diff

View File

@@ -574,10 +574,12 @@ function onWpnEvent(event)
end end
local player = event.initiator local player = event.initiator
targetName = event.target:getTypeName() local targetName = event.target:getTypeName()
local impactPoint = event.target:getPosition().p local impactPoint = event.target:getPosition().p
env.info(weapon.." hit "..targetName) if weapon and targetName then
debugMsg(weapon.." hit "..targetName) env.info(weapon.." hit "..targetName)
debugMsg(weapon.." hit "..targetName)
end
--env.info('Impact point was at: X: ' .. impactPoint.x .. ' Y: ' .. impactPoint.y .. ' Z: ' .. impactPoint.z) --env.info('Impact point was at: X: ' .. impactPoint.x .. ' Y: ' .. impactPoint.y .. ' Z: ' .. impactPoint.z)
if clusterWeaps[weapon] then if clusterWeaps[weapon] then
local ordnance = event.weapon local ordnance = event.weapon

View File

@@ -37,5 +37,6 @@ radios:
tasks: tasks:
BAI: 510 BAI: 510
CAS: 510 CAS: 510
DEAD: 115
Escort: 100 Escort: 100
OCA/Aircraft: 510 OCA/Aircraft: 510

View File

@@ -27,5 +27,6 @@ kneeboard_units: "metric"
tasks: tasks:
BAI: 430 BAI: 430
CAS: 430 CAS: 430
DEAD: 113
Escort: 90 Escort: 90
OCA/Aircraft: 430 OCA/Aircraft: 430

View File

@@ -28,5 +28,6 @@ kneeboard_units: "metric"
tasks: tasks:
BAI: 440 BAI: 440
CAS: 440 CAS: 440
DEAD: 113
Escort: 100 Escort: 100
OCA/Aircraft: 440 OCA/Aircraft: 440

View File

@@ -20,5 +20,8 @@ variants:
S-3B Viking: {} S-3B Viking: {}
tasks: tasks:
Anti-ship: 350 Anti-ship: 350
BAI: 570
CAS: 570
DEAD: 200
OCA/Aircraft: 330 OCA/Aircraft: 330
Strike: 370 Strike: 370