Merge branch 'dev' into pr/204

This commit is contained in:
Raffson 2024-07-28 15:57:38 +02:00
commit 65e529aa55
No known key found for this signature in database
GPG Key ID: B0402B2C9B764D99
100 changed files with 1342 additions and 220 deletions

View File

@ -8,6 +8,7 @@
* **[Squadrons]** Ability to define a livery-set for each squadron from which Retribution will randomly choose during mission generation
* **[Modding]** Updated support for F/A-18E/F/G mod version 2.2.5
* **[Modding]** Added VSN F-106 Delta Dart mod support (v2.9.4.101)
* **[Modding]** Added OH-6 Cayuse (v1.2) mod support, including the Vietnam Asset Pack v1.0
* **[Modding]** Added VSN EA-6B Prowler mod support (v2.9.4.102)
* **[Modding]** Added tripod3 Cold War assets mod support (v1.0)
* **[Campaign Setup]** Allow adjustments to naval TGOs (except carriers) on turn 0
@ -25,6 +26,9 @@
* **[Modding]** Added support for Su-15 Flagon mod (v1.0)
* **[Plugins]** Support for Carsten's Arty Spotter script
* **[Modding]** Added support for SK-60 mod (v1.2.1)
* **[Mission Generation]** Introducing the Armed Recon flight plan, i.e. CAS against any Theater Ground Object
* **[Doctrine]** Ability to customize the startup time allocated to the player
* **[Mission Generation]** Ability to choose whether player flights can spawn on the sixpack or not
## Fixes
* **[UI/UX]** A-10A flights can be edited again

View File

@ -275,8 +275,6 @@ class Flight(
self.fuel = unit_type.fuel_max * 0.5
elif unit_type == Hercules:
self.fuel = unit_type.fuel_max * 0.75
elif self.departure.cptype.name in ["FARP", "FOB"] and not self.is_helo:
self.fuel = unit_type.fuel_max * 0.75
def any_member_has_weapon_of_type(self, weapon_type: WeaponType) -> bool:
return any(

View File

@ -75,13 +75,6 @@ class AirAssaultFlightPlan(FormationAttackFlightPlan, UiZoneDisplay):
)
return tot - travel_time
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> datetime | None:
if waypoint is self.tot_waypoint:
return self.tot
elif waypoint is self.layout.ingress:
return self.ingress_time
return None
def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> datetime | None:
return None
@ -89,10 +82,6 @@ class AirAssaultFlightPlan(FormationAttackFlightPlan, UiZoneDisplay):
def ctld_target_zone_radius(self) -> Distance:
return meters(2500)
@property
def mission_begin_on_station_time(self) -> datetime | None:
return None
@property
def mission_departure_time(self) -> datetime:
return self.package.time_over_target

View File

@ -0,0 +1,34 @@
from __future__ import annotations
from typing import Type
from .formationattack import (
FormationAttackBuilder,
FormationAttackFlightPlan,
FormationAttackLayout,
)
from .uizonedisplay import UiZone, UiZoneDisplay
from ..flightwaypointtype import FlightWaypointType
from ...utils import nautical_miles
class ArmedReconFlightPlan(FormationAttackFlightPlan, UiZoneDisplay):
@staticmethod
def builder_type() -> Type[Builder]:
return Builder
def ui_zone(self) -> UiZone:
return UiZone(
[self.tot_waypoint.position],
nautical_miles(
self.flight.coalition.game.settings.armed_recon_engagement_range_distance
),
)
class Builder(FormationAttackBuilder[ArmedReconFlightPlan, FormationAttackLayout]):
def layout(self) -> FormationAttackLayout:
return self._build(FlightWaypointType.INGRESS_ARMED_RECON)
def build(self, dump_debug_info: bool = False) -> ArmedReconFlightPlan:
return ArmedReconFlightPlan(self.flight, self.layout())

View File

@ -125,16 +125,12 @@ class Builder(IBuilder[CasFlightPlan, CasLayout]):
ingress_point_shapely.x, ingress_point_shapely.y
)
patrol_start_waypoint = builder.nav(
patrol_start, ingress_egress_altitude, use_agl_patrol_altitude
)
patrol_start_waypoint = builder.cas(patrol_start, ingress_egress_altitude)
patrol_start_waypoint.name = "FLOT START"
patrol_start_waypoint.pretty_name = "FLOT start"
patrol_start_waypoint.description = "FLOT boundary"
patrol_end_waypoint = builder.nav(
patrol_end, ingress_egress_altitude, use_agl_patrol_altitude
)
patrol_end_waypoint = builder.cas(patrol_end, ingress_egress_altitude)
patrol_end_waypoint.name = "FLOT END"
patrol_end_waypoint.pretty_name = "FLOT end"
patrol_end_waypoint.description = "FLOT boundary"

View File

@ -11,6 +11,7 @@ from .formationattack import (
)
from .waypointbuilder import WaypointBuilder
from .. import FlightType
from ...utils import feet
class EscortFlightPlan(FormationAttackFlightPlan):
@ -34,7 +35,7 @@ class Builder(FormationAttackBuilder[EscortFlightPlan, FormationAttackLayout]):
hold = builder.hold(self._hold_point())
join_pos = (
self.package.waypoints.ingress
WaypointBuilder.perturb(self.package.waypoints.ingress, feet(500))
if self.flight.is_helo
else self.package.waypoints.join
)
@ -59,8 +60,6 @@ class Builder(FormationAttackBuilder[EscortFlightPlan, FormationAttackLayout]):
join = builder.join(ascent.position)
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)
split = builder.split(layout.arrival.position)
if layout.drop_off:
initial = builder.escort_hold(

View File

@ -105,19 +105,6 @@ class FlightPlan(ABC, Generic[LayoutT]):
#
# Plus, it's a loiter point so there's no reason to hurry.
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.
# 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
@ -268,7 +255,9 @@ class FlightPlan(ABC, Generic[LayoutT]):
def estimate_startup(self) -> timedelta:
if self.flight.start_type is StartType.COLD:
if self.flight.client_count:
return timedelta(minutes=10)
return timedelta(
minutes=self.flight.coalition.game.settings.player_startup_time
)
else:
# The AI doesn't seem to have a real startup procedure.
return timedelta(minutes=2)

View File

@ -7,6 +7,7 @@ from .aewc import AewcFlightPlan
from .airassault import AirAssaultFlightPlan
from .airlift import AirliftFlightPlan
from .antiship import AntiShipFlightPlan
from .armedrecon import ArmedReconFlightPlan
from .bai import BaiFlightPlan
from .barcap import BarCapFlightPlan
from .cas import CasFlightPlan
@ -62,6 +63,7 @@ class FlightPlanBuilderTypes:
FlightType.FERRY: FerryFlightPlan.builder_type(),
FlightType.AIR_ASSAULT: AirAssaultFlightPlan.builder_type(),
FlightType.PRETENSE_CARGO: PretenseCargoFlightPlan.builder_type(),
FlightType.ARMED_RECON: ArmedReconFlightPlan.builder_type(),
}
try:
return builder_dict[flight.flight_type]

View File

@ -64,8 +64,10 @@ class FormationFlightPlan(LoiterFlightPlan, ABC):
return min(speeds)
def speed_between_waypoints(self, a: FlightWaypoint, b: FlightWaypoint) -> Speed:
if self.package.formation_speed and b in self.package_speed_waypoints:
return self.package.formation_speed
if (
speed := self.package.formation_speed(self.flight.is_helo)
) and b in self.package_speed_waypoints:
return speed
return super().speed_between_waypoints(a, b)
@property

View File

@ -11,7 +11,7 @@ from dcs import Point
from game.flightplan import HoldZoneGeometry
from game.theater import MissionTarget
from game.utils import Speed, meters, nautical_miles
from game.utils import nautical_miles, Speed, feet
from .flightplan import FlightPlan
from .formation import FormationFlightPlan, FormationLayout
from .ibuilder import IBuilder
@ -39,8 +39,9 @@ class FormationAttackFlightPlan(FormationFlightPlan, ABC):
if b.waypoint_type == FlightWaypointType.TARGET_GROUP_LOC:
# Should be impossible, as any package with at least one
# FormationFlightPlan flight needs a formation speed.
assert self.package.formation_speed is not None
return self.package.formation_speed
speed = self.package.formation_speed(self.flight.is_helo)
assert speed is not None
return speed
return super().speed_between_waypoints(a, b)
@property
@ -53,7 +54,7 @@ class FormationAttackFlightPlan(FormationFlightPlan, ABC):
"TARGET AREA",
FlightWaypointType.TARGET_GROUP_LOC,
self.package.target.position,
meters(0),
feet(0),
"RADIO",
)
@ -192,6 +193,7 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
join_pos = self.package.waypoints.join
if self.flight.is_helo:
join_pos = self.package.waypoints.ingress
join_pos = WaypointBuilder.perturb(join_pos, feet(500))
join = builder.join(join_pos)
split = builder.split(self._get_split())
refuel = self._build_refuel(builder)
@ -286,6 +288,8 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
return builder.sead_area(location)
elif flight.flight_type == FlightType.OCA_AIRCRAFT:
return builder.oca_strike_area(location)
elif flight.flight_type == FlightType.ARMED_RECON:
return builder.armed_recon_area(location)
else:
return builder.strike_area(location)

View File

@ -1,5 +1,6 @@
from __future__ import annotations
import math
import random
from dataclasses import dataclass
from typing import (
@ -252,18 +253,9 @@ class WaypointBuilder:
if ingress_type in [
FlightWaypointType.INGRESS_CAS,
FlightWaypointType.INGRESS_OCA_AIRCRAFT,
FlightWaypointType.INGRESS_ARMED_RECON,
]:
weather = self.flight.coalition.game.conditions.weather
max_alt = feet(30000)
if weather.clouds and (
weather.clouds.preset
and "overcast" in weather.clouds.preset.description.lower()
or weather.clouds.density > 5
):
max_alt = meters(
max(feet(500).meters, weather.clouds.base - feet(500).meters)
)
alt = min(alt, max_alt)
alt = self._adjust_altitude_for_clouds(alt)
alt_type: AltitudeReference = "BARO"
if self.is_helo or self.flight.is_hercules:
@ -291,6 +283,19 @@ class WaypointBuilder:
targets=objective.strike_targets,
)
def _adjust_altitude_for_clouds(self, alt: Distance) -> Distance:
weather = self.flight.coalition.game.conditions.weather
max_alt = feet(math.inf)
if weather.clouds and (
weather.clouds.preset
and "overcast" in weather.clouds.preset.description.lower()
or weather.clouds.density > 5
):
max_alt = meters(
max(feet(500).meters, weather.clouds.base - feet(500).meters)
)
return min(alt, max_alt)
def egress(self, position: Point, target: MissionTarget) -> FlightWaypoint:
alt_type: AltitudeReference = "BARO"
if self.is_helo or self.get_combat_altitude.feet <= AGL_TRANSITION_ALT:
@ -354,6 +359,21 @@ class WaypointBuilder:
def dead_area(self, target: MissionTarget) -> FlightWaypoint:
return self._target_area(f"DEAD on {target.name}", target)
def armed_recon_area(self, target: MissionTarget) -> FlightWaypoint:
# Force AI aircraft to fly towards target area
alt = self.get_combat_altitude
alt = self._adjust_altitude_for_clouds(alt)
alt_type: AltitudeReference = "BARO"
if self.is_helo or alt.feet <= AGL_TRANSITION_ALT:
alt_type = "RADIO"
return self._target_area(
f"ARMED RECON {target.name}",
target,
altitude=alt,
alt_type=alt_type,
flyover=True,
)
def oca_strike_area(self, target: MissionTarget) -> FlightWaypoint:
return self._target_area(f"ATTACK {target.name}", target, flyover=True)
@ -398,15 +418,14 @@ class WaypointBuilder:
waypoint.only_for_player = True
return waypoint
def cas(self, position: Point) -> FlightWaypoint:
def cas(self, position: Point, altitude: Distance) -> FlightWaypoint:
weather = self.flight.coalition.game.conditions.weather
max_alt = feet(30000)
if weather.clouds and (
weather.clouds.preset
and "overcast" in weather.clouds.preset.description.lower()
or weather.clouds.density > 5
):
max_alt = meters(
altitude = meters(
max(feet(500).meters, weather.clouds.base - feet(500).meters)
)
return FlightWaypoint(
@ -415,7 +434,7 @@ class WaypointBuilder:
position,
feet(self.flight.coalition.game.settings.heli_combat_alt_agl)
if self.is_helo
else min(meters(1000), max_alt),
else max(meters(1000), altitude),
"RADIO",
description="Provide CAS",
pretty_name="CAS",
@ -667,14 +686,13 @@ class WaypointBuilder:
This waypoint is used to generate the Trigger Zone used for AirAssault and
AirLift using the CTLD plugin (see LogisticsGenerator)
"""
heli_alt = feet(self.flight.coalition.game.settings.heli_cruise_alt_agl)
altitude = heli_alt if self.flight.is_helo else meters(0)
alt = self.get_combat_altitude if self.flight.is_helo else meters(0)
return FlightWaypoint(
"DROPOFFZONE",
FlightWaypointType.DROPOFF_ZONE,
drop_off.position,
altitude,
alt,
"RADIO",
description=f"Drop off cargo at {drop_off.name}",
pretty_name="Drop-off zone",
@ -772,8 +790,7 @@ class WaypointBuilder:
return previous_threatened and next_threatened
@staticmethod
def perturb(point: Point) -> Point:
deviation = nautical_miles(1)
def perturb(point: Point, deviation: Distance = nautical_miles(1)) -> Point:
x_adj = random.randint(int(-deviation.meters), int(deviation.meters))
y_adj = random.randint(int(-deviation.meters), int(deviation.meters))
return point + Vector2(x_adj, y_adj)

View File

@ -58,9 +58,8 @@ class FlightType(Enum):
FERRY = "Ferry"
AIR_ASSAULT = "Air Assault"
SEAD_SWEEP = "SEAD Sweep" # Reintroduce legacy "engage-whatever-you-can-find" SEAD
PRETENSE_CARGO = (
"Cargo Transport" # Flight type for Pretense campaign AI cargo planes
)
PRETENSE_CARGO = "Cargo Transport" # For Pretense campaign AI cargo planes
ARMED_RECON = "Armed Recon"
def __str__(self) -> str:
return self.value
@ -96,6 +95,7 @@ class FlightType(Enum):
FlightType.SEAD_ESCORT,
FlightType.AIR_ASSAULT,
FlightType.SEAD_SWEEP,
FlightType.ARMED_RECON,
}
@property
@ -107,6 +107,7 @@ class FlightType(Enum):
return {
FlightType.AEWC: AirEntity.AIRBORNE_EARLY_WARNING,
FlightType.ANTISHIP: AirEntity.ANTISURFACE_WARFARE,
FlightType.ARMED_RECON: AirEntity.ATTACK_STRIKE,
FlightType.BAI: AirEntity.ATTACK_STRIKE,
FlightType.BARCAP: AirEntity.FIGHTER,
FlightType.CAS: AirEntity.ATTACK_STRIKE,

View File

@ -51,3 +51,4 @@ class FlightWaypointType(IntEnum):
INGRESS_AIR_ASSAULT = 31
INGRESS_ANTI_SHIP = 32
INGRESS_SEAD_SWEEP = 33
INGRESS_ARMED_RECON = 34

View File

@ -208,6 +208,7 @@ class Loadout:
loadout_names[FlightType.INTERCEPTION].extend(loadout_names[FlightType.BARCAP])
# OCA/Aircraft falls back to BAI, which falls back to CAS.
loadout_names[FlightType.BAI].extend(loadout_names[FlightType.CAS])
loadout_names[FlightType.ARMED_RECON].extend(loadout_names[FlightType.CAS])
loadout_names[FlightType.OCA_AIRCRAFT].extend(loadout_names[FlightType.BAI])
# DEAD also falls back to BAI.
loadout_names[FlightType.DEAD].extend(loadout_names[FlightType.BAI])

View File

@ -54,8 +54,7 @@ class Package(RadioFrequencyContainer):
def has_players(self) -> bool:
return any(flight.client_count for flight in self.flights)
@property
def formation_speed(self) -> Optional[Speed]:
def formation_speed(self, is_helo: bool) -> Optional[Speed]:
"""The speed of the package when in formation.
If none of the flights in the package will join a formation, this
@ -66,7 +65,10 @@ class Package(RadioFrequencyContainer):
"""
speeds = []
for flight in self.flights:
if isinstance(flight.flight_plan, FormationFlightPlan):
if (
isinstance(flight.flight_plan, FormationFlightPlan)
and flight.is_helo == is_helo
):
speeds.append(flight.flight_plan.best_flight_formation_speed)
if not speeds:
return None
@ -183,6 +185,7 @@ class Package(RadioFrequencyContainer):
FlightType.SEAD_SWEEP,
FlightType.TARCAP,
FlightType.BARCAP,
FlightType.ARMED_RECON,
FlightType.AEWC,
FlightType.FERRY,
FlightType.REFUELING,

View File

@ -3,7 +3,7 @@ from __future__ import annotations
from datetime import datetime
from typing import TYPE_CHECKING
from game.utils import Distance, SPEED_OF_SOUND_AT_SEA_LEVEL, Speed, mach, meters
from game.utils import Distance, SPEED_OF_SOUND_AT_SEA_LEVEL, Speed, mach
if TYPE_CHECKING:
from .flight import Flight
@ -30,8 +30,8 @@ class GroundSpeed:
# as it can at sea level. This probably isn't great assumption, but
# might. be sufficient given the wiggle room. We can come up with
# another heuristic if needed.
cruise_mach = max_speed.mach() * (0.60 if flight.is_helo else 0.85)
return mach(cruise_mach, altitude if not flight.is_helo else meters(0))
cruise_mach = max_speed.mach() * (0.7 if flight.is_helo else 0.85)
return mach(cruise_mach, altitude)
# TODO: Most if not all of this should move into FlightPlan.

View File

@ -1,6 +1,7 @@
from __future__ import annotations
import logging
import random
from typing import Optional, TYPE_CHECKING
from game.squadrons import Squadron
@ -101,9 +102,10 @@ class DefaultSquadronAssigner:
if aircraft not in self.coalition.faction.all_aircrafts:
return None
lo = self.coalition.faction.liveries_overrides
squadron_def = self.find_squadron_for_airframe(aircraft, task, control_point)
if squadron_def is not None and lo.get(aircraft) is None:
if squadron_def is not None and (
squadron_def.livery is not None or squadron_def.livery_set is not None
):
return squadron_def
# No premade squadron available for this aircraft that meets the requirements,
@ -124,11 +126,14 @@ class DefaultSquadronAssigner:
def find_squadron_for_airframe(
self, aircraft: AircraftType, task: FlightType, control_point: ControlPoint
) -> Optional[SquadronDef]:
choices = []
for squadron in self.air_wing.squadron_defs[aircraft]:
if not squadron.claimed and self.squadron_compatible_with(
squadron, task, control_point
):
return squadron
choices.append(squadron)
if choices:
return random.choice(choices)
return None
def find_squadron_by_name(

View File

@ -167,24 +167,6 @@ class ObjectiveFinder:
yield cp
break
def vulnerable_enemy_control_points(self) -> Iterator[ControlPoint]:
"""Iterates over enemy CPs that are vulnerable to Air Assault.
Vulnerability is defined as any unit being alive in the CP's "blocking_capture" groups.
"""
for cp in self.enemy_control_points():
include = True
for tgo in cp.connected_objectives:
if tgo.distance_to(cp) > cp.CAPTURE_DISTANCE.meters:
continue
for u in tgo.units:
if u.is_vehicle and u.alive:
include = False
break
if not include:
break
if include:
yield cp
def oca_targets(self, min_aircraft: int) -> Iterator[ControlPoint]:
parking_type = ParkingType()
parking_type.include_rotary_wing = True

View File

@ -6,7 +6,7 @@ import math
from collections.abc import Iterator
from dataclasses import dataclass
from datetime import datetime
from typing import Optional, TYPE_CHECKING, Union
from typing import Optional, TYPE_CHECKING, Union, Dict
from game.commander.battlepositions import BattlePositions
from game.commander.objectivefinder import ObjectiveFinder
@ -163,6 +163,15 @@ class TheaterState(WorldState["TheaterState"]):
barcap_duration = coalition.doctrine.cap_duration.total_seconds()
barcap_rounds = math.ceil(mission_duration / barcap_duration)
battle_postitions: Dict[ControlPoint, BattlePositions] = {
cp: BattlePositions.for_control_point(cp)
for cp in ordered_capturable_points
}
vulnerable_control_points = [
cp for cp, bp in battle_postitions.items() if not bp.blocking_capture
]
return TheaterState(
context=context,
barcaps_needed={
@ -179,10 +188,7 @@ class TheaterState(WorldState["TheaterState"]):
enemy_convoys=list(finder.convoys()),
enemy_shipping=list(finder.cargo_ships()),
enemy_ships=list(finder.enemy_ships()),
enemy_battle_positions={
cp: BattlePositions.for_control_point(cp)
for cp in ordered_capturable_points
},
enemy_battle_positions=battle_postitions,
oca_targets=list(
finder.oca_targets(
min_aircraft=game.settings.oca_target_autoplanner_min_aircraft_count
@ -191,5 +197,5 @@ class TheaterState(WorldState["TheaterState"]):
strike_targets=list(finder.strike_targets()),
enemy_barcaps=list(game.theater.control_points_for(not player)),
threat_zones=game.threat_zone_for(not player),
vulnerable_control_points=list(finder.vulnerable_enemy_control_points()),
vulnerable_control_points=vulnerable_control_points,
)

View File

@ -241,9 +241,18 @@ class AircraftType(UnitType[Type[FlyingType]]):
def __post_init__(self) -> None:
enrich = {}
for t in self.task_priorities:
if t == FlightType.SEAD:
enrich[FlightType.SEAD_SWEEP] = self.task_priorities[t]
if FlightType.SEAD_SWEEP not in self.task_priorities:
if (value := self.task_priorities.get(FlightType.SEAD)) or (
value := self.task_priorities.get(FlightType.SEAD_ESCORT)
):
enrich[FlightType.SEAD_SWEEP] = value
if FlightType.ARMED_RECON not in self.task_priorities:
if (value := self.task_priorities.get(FlightType.CAS)) or (
value := self.task_priorities.get(FlightType.BAI)
):
enrich[FlightType.ARMED_RECON] = value
self.task_priorities.update(enrich)
@classmethod
@ -299,17 +308,24 @@ class AircraftType(UnitType[Type[FlyingType]]):
elif max_speed > SPEED_OF_SOUND_AT_SEA_LEVEL * 0.7:
# Semi-fast like airliners or similar
return (
Speed.from_mach(0.5, altitude)
Speed.from_mach(0.6, altitude)
if altitude.feet > 20000
else Speed.from_mach(0.4, altitude)
else Speed.from_mach(0.5, altitude)
)
elif self.helicopter:
return max_speed * 0.4
else:
# Slow like warbirds or helicopters
# Use whichever is slowest - mach 0.35 or 50% of max speed
logging.debug(
f"{self.display_name} max_speed * 0.5 is {max_speed * 0.5}"
# Slow like warbirds or attack planes
# return 50% of max speed + 5% per 2k above 10k to maintain momentum
return max_speed * min(
1.0,
0.5
+ (
(((altitude.feet - 10000) / 2000) * 0.05)
if altitude.feet > 10000
else 0
),
)
return min(Speed.from_mach(0.35, altitude), max_speed * 0.5)
@cached_property
def preferred_cruise_altitude(self) -> Distance:
@ -526,17 +542,7 @@ class AircraftType(UnitType[Type[FlyingType]]):
if prop_overrides is not None:
cls._set_props_overrides(prop_overrides, aircraft)
from game.ato.flighttype import FlightType
task_priorities: dict[FlightType, int] = {}
for task_name, priority in data.get("tasks", {}).items():
task_priorities[FlightType(task_name)] = priority
if (
FlightType.SEAD_SWEEP not in task_priorities
and FlightType.SEAD in task_priorities
):
task_priorities[FlightType.SEAD_SWEEP] = task_priorities[FlightType.SEAD]
task_priorities = cls.get_task_priorities(data)
cls._custom_weapon_injections(aircraft, data)
cls._user_weapon_injections(aircraft)
@ -583,6 +589,27 @@ class AircraftType(UnitType[Type[FlyingType]]):
use_f15e_waypoint_names=data.get("use_f15e_waypoint_names", False),
)
@classmethod
def get_task_priorities(cls, data: dict[str, Any]) -> dict[FlightType, int]:
task_priorities: dict[FlightType, int] = {}
for task_name, priority in data.get("tasks", {}).items():
task_priorities[FlightType(task_name)] = priority
if (
FlightType.SEAD_SWEEP not in task_priorities
and FlightType.SEAD in task_priorities
):
task_priorities[FlightType.SEAD_SWEEP] = task_priorities[FlightType.SEAD]
if FlightType.ARMED_RECON not in task_priorities:
if FlightType.CAS in task_priorities:
task_priorities[FlightType.ARMED_RECON] = task_priorities[
FlightType.CAS
]
elif FlightType.BAI in task_priorities:
task_priorities[FlightType.ARMED_RECON] = task_priorities[
FlightType.BAI
]
return task_priorities
@staticmethod
def _custom_weapon_injections(
aircraft: Type[FlyingType], data: Dict[str, Any]

View File

@ -352,6 +352,45 @@ class Faction:
self.remove_aircraft("A-4E-C")
if not mod_settings.hercules:
self.remove_aircraft("Hercules")
if not mod_settings.oh_6:
self.remove_aircraft("OH-6A")
if not mod_settings.oh_6_vietnamassetpack:
self.remove_vehicle("vap_mutt_gun")
self.remove_vehicle("vap_type63_mlrs")
self.remove_vehicle("vap_vc_bicycle_mortar")
self.remove_vehicle("vap_zis_150_aa")
self.remove_vehicle("vap_us_hooch_LP")
self.remove_vehicle("vap_ammo_50cal_line")
self.remove_vehicle("vap_ammo_50cal_pack")
self.remove_vehicle("vap_barrels_line")
self.remove_vehicle("vap_barrels")
self.remove_vehicle("vap_ammo_box_pile")
self.remove_vehicle("vap_ammo_box_wood_long")
self.remove_vehicle("vap_ammo_box_wood_small")
self.remove_vehicle("vap_barrel_red")
self.remove_vehicle("vap_barrel_green")
self.remove_vehicle("vap_mre_boxes")
self.remove_vehicle("vap_mixed_cargo_1")
self.remove_vehicle("vap_mixed_cargo_2")
self.remove_vehicle("vap_watchtower")
self.remove_vehicle("vap_house_high")
self.remove_vehicle("vap_house_long")
self.remove_vehicle("vap_house_small")
self.remove_vehicle("vap_house_T")
self.remove_vehicle("vap_house_tiny")
self.remove_vehicle("vap_house1")
self.remove_vehicle("vap_us_hooch_radio")
self.remove_vehicle("vap_us_hooch_closed")
self.remove_vehicle("vap_vc_bunker_single")
self.remove_vehicle("vap_vc_mg_nest")
self.remove_vehicle("vap_mule")
self.remove_vehicle("vap_mutt")
self.remove_vehicle("vap_m35_truck")
self.remove_vehicle("vap_vc_zis")
self.remove_vehicle("vap_vc_bicycle")
self.remove_vehicle("vap_vc_zil")
self.remove_vehicle("vap_vc_bicycle_ak")
self.remove_ship("vap_us_seafloat")
if not mod_settings.uh_60l:
self.remove_aircraft("UH-60L")
self.remove_aircraft("KC130J")

View File

@ -11,6 +11,7 @@ from game.ato.flightplans.formation import FormationLayout
from game.ato.flightplans.waypointbuilder import WaypointBuilder
from game.ato.packagewaypoints import PackageWaypoints
from game.data.doctrine import MODERN_DOCTRINE, COLDWAR_DOCTRINE, WWII_DOCTRINE
from game.dcs.aircrafttype import AircraftType
from game.theater import ParkingType, SeasonalConditions
if TYPE_CHECKING:
@ -164,6 +165,7 @@ class Migrator:
try_set_attr(s, "max_size", 12)
try_set_attr(s, "radio_presets", {})
try_set_attr(s, "livery_set", [])
s.aircraft = AircraftType.named(s.aircraft.variant_id)
if isinstance(s.country, str):
c = country_dict.get(s.country, s.country)
s.country = countries_by_name[c]()

View File

@ -57,6 +57,8 @@ class AircraftBehavior:
self.configure_refueling(group, flight)
elif self.task in [FlightType.CAS, FlightType.BAI]:
self.configure_cas(group, flight)
elif self.task == FlightType.ARMED_RECON:
self.configure_armed_recon(group, flight)
elif self.task == FlightType.DEAD:
self.configure_dead(group, flight)
elif self.task in [FlightType.SEAD, FlightType.SEAD_SWEEP]:
@ -183,6 +185,17 @@ class AircraftBehavior:
restrict_jettison=True,
)
def configure_armed_recon(self, group: FlyingGroup[Any], flight: Flight) -> None:
self.configure_task(flight, group, CAS, [AFAC, AntishipStrike])
self.configure_behavior(
flight,
group,
react_on_threat=OptReactOnThreat.Values.EvadeFire,
roe=OptROE.Values.OpenFire,
rtb_winchester=OptRTBOnOutOfAmmo.Values.All,
restrict_jettison=True,
)
def configure_dead(self, group: FlyingGroup[Any], flight: Flight) -> None:
# Only CAS and SEAD are capable of the Attack Group task. SEAD is arguably more
# appropriate but it has an extremely limited list of capable aircraft, whereas
@ -379,23 +392,35 @@ class AircraftBehavior:
if preferred_task in flight.unit_type.dcs_unit_type.tasks:
group.task = preferred_task.name
elif fallback_tasks:
return
if fallback_tasks:
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:
if flight.unit_type.dcs_unit_type.task_default and preferred_task == Nothing:
group.task = flight.unit_type.dcs_unit_type.task_default.name
logging.warning(
f"{ac_type} is not capable of 'Nothing', using default task '{group.task}'"
)
else:
fallback_part = (
f" nor any of the following fall-back tasks: {[task.name for task in fallback_tasks]}"
if fallback_tasks
else ""
return
if flight.roster.members and flight.roster.members[0].is_player:
group.task = (
flight.unit_type.dcs_unit_type.task_default.name
if flight.unit_type.dcs_unit_type.task_default
else group.task # even if this is incompatible, if it's a client we don't really care...
)
raise RuntimeError(
f"{ac_type} is neither capable of {preferred_task.name}"
f"{fallback_part}. Can't generate {flight.flight_type} flight."
logging.warning(
f"Client override: {ac_type} is not capable of '{preferred_task}', using default task '{group.task}'"
)
return
fallback_part = (
f" nor any of the following fall-back tasks: {[task.name for task in fallback_tasks]}"
if fallback_tasks
else ""
)
raise RuntimeError(
f"{ac_type} is neither capable of {preferred_task.name}"
f"{fallback_part}. Can't generate {flight.flight_type} flight."
)

View File

@ -497,21 +497,20 @@ class FlightGroupSpawner:
) -> Optional[FlyingGroup[Any]]:
is_airbase = False
is_roadbase = False
ground_spawn = None
try:
if is_large:
if len(self.ground_spawns_large[cp]) > 0:
ground_spawn = self.ground_spawns_large[cp].pop()
is_airbase = True
else:
if len(self.ground_spawns_roadbase[cp]) > 0:
ground_spawn = self.ground_spawns_roadbase[cp].pop()
is_roadbase = True
if len(self.ground_spawns[cp]) > 0:
ground_spawn = self.ground_spawns[cp].pop()
is_airbase = True
except IndexError as ex:
logging.warning("Not enough ground spawn slots available at " + str(ex))
if not is_large and len(self.ground_spawns_roadbase[cp]) > 0:
ground_spawn = self.ground_spawns_roadbase[cp].pop()
is_roadbase = True
elif not is_large and len(self.ground_spawns[cp]) > 0:
ground_spawn = self.ground_spawns[cp].pop()
is_airbase = True
elif len(self.ground_spawns_large[cp]) > 0:
ground_spawn = self.ground_spawns_large[cp].pop()
is_airbase = True
if ground_spawn is None:
logging.warning("Not enough ground spawn slots available at " + cp.name)
return None
group = self._generate_at_group(name, ground_spawn[0])
@ -581,14 +580,12 @@ class FlightGroupSpawner:
for i in range(self.flight.count - 1):
try:
terrain = cp.coalition.game.theater.terrain
if is_large:
if len(self.ground_spawns_large[cp]) > 0:
ground_spawn = self.ground_spawns_large[cp].pop()
else:
if len(self.ground_spawns_roadbase[cp]) > 0:
ground_spawn = self.ground_spawns_roadbase[cp].pop()
else:
ground_spawn = self.ground_spawns[cp].pop()
if not is_large and len(self.ground_spawns_roadbase[cp]) > 0:
ground_spawn = self.ground_spawns_roadbase[cp].pop()
elif not is_large and len(self.ground_spawns[cp]) > 0:
ground_spawn = self.ground_spawns[cp].pop()
elif len(self.ground_spawns_large[cp]) > 0:
ground_spawn = self.ground_spawns_large[cp].pop()
group.units[1 + i].position = Point(
ground_spawn[0].x, ground_spawn[0].y, terrain=terrain
)

View File

@ -0,0 +1,35 @@
from dcs.point import MovingPoint
from dcs.task import (
OptECMUsing,
ControlledTask,
Targets,
EngageTargetsInZone,
)
from game.utils import nautical_miles
from .pydcswaypointbuilder import PydcsWaypointBuilder
class ArmedReconIngressBuilder(PydcsWaypointBuilder):
def add_tasks(self, waypoint: MovingPoint) -> None:
self.register_special_ingress_points()
# Preemptively use ECM to better avoid getting swatted.
ecm_option = OptECMUsing(value=OptECMUsing.Values.UseIfDetectedLockByRadar)
waypoint.tasks.append(ecm_option)
waypoint.add_task(
ControlledTask(
EngageTargetsInZone(
position=self.flight.flight_plan.tot_waypoint.position,
radius=int(
nautical_miles(
self.flight.coalition.game.settings.armed_recon_engagement_range_distance
).meters
),
targets=[
Targets.All.GroundUnits,
Targets.All.Air.Helicopters,
],
)
)
)

View File

@ -17,7 +17,7 @@ class HoldPointBuilder(PydcsWaypointBuilder):
loiter = ControlledTask(
OrbitAction(
altitude=waypoint.alt,
speed=speed.meters_per_second,
speed=speed.kph,
pattern=OrbitAction.OrbitPattern.Circle,
)
)

View File

@ -8,7 +8,6 @@ from dcs.task import (
OptECMUsing,
OptFormation,
Targets,
OptROE,
SetUnlimitedFuelCommand,
)
@ -94,12 +93,6 @@ class JoinPointBuilder(PydcsWaypointBuilder):
max_dist: float = 30.0,
vertical_spacing: float = 2000.0,
) -> None:
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
ry = feet(vertical_spacing).meters
rz = (random.random() + 0.1) * 166 * random.choice([-1, 1])

View File

@ -3,7 +3,7 @@ import logging
from dcs.point import MovingPoint
from dcs.task import EngageTargetsInZone, Targets
from game.theater import Airfield
from game.theater import Airfield, Fob
from game.utils import nautical_miles
from .pydcswaypointbuilder import PydcsWaypointBuilder
@ -12,7 +12,7 @@ class OcaAircraftIngressBuilder(PydcsWaypointBuilder):
def add_tasks(self, waypoint: MovingPoint) -> None:
target = self.package.target
self.register_special_ingress_points()
if not isinstance(target, Airfield):
if not (isinstance(target, Airfield) or isinstance(target, Fob)):
logging.error(
"Unexpected target type for OCA Strike mission: %s",
target.__class__.__name__,

View File

@ -2,9 +2,8 @@ from dcs.point import MovingPoint
from dcs.task import (
OptECMUsing,
ControlledTask,
EngageTargets,
Targets,
OptROE,
EngageTargetsInZone,
)
from game.utils import nautical_miles
@ -14,16 +13,15 @@ from .pydcswaypointbuilder import PydcsWaypointBuilder
class SeadSweepIngressBuilder(PydcsWaypointBuilder):
def add_tasks(self, waypoint: MovingPoint) -> None:
self.register_special_ingress_points()
waypoint.tasks.append(OptROE(value=OptROE.Values.OpenFireWeaponFree))
# Preemptively use ECM to better avoid getting swatted.
ecm_option = OptECMUsing(value=OptECMUsing.Values.UseIfDetectedLockByRadar)
waypoint.tasks.append(ecm_option)
waypoint.add_task(
ControlledTask(
EngageTargets(
# TODO: From doctrine.
max_distance=int(
EngageTargetsInZone(
position=self.flight.flight_plan.tot_waypoint.position,
radius=int(
nautical_miles(
self.flight.coalition.game.settings.sead_sweep_engagement_range_distance
).meters

View File

@ -22,6 +22,7 @@ from game.settings import Settings
from game.utils import pairwise
from .airassaultingress import AirAssaultIngressBuilder
from .antishipingress import AntiShipIngressBuilder
from .armedreconingress import ArmedReconIngressBuilder
from .baiingress import BaiIngressBuilder
from .casingress import CasIngressBuilder
from .deadingress import DeadIngressBuilder
@ -136,6 +137,7 @@ class WaypointGenerator:
FlightWaypointType.DROPOFF_ZONE: LandingZoneBuilder,
FlightWaypointType.INGRESS_AIR_ASSAULT: AirAssaultIngressBuilder,
FlightWaypointType.INGRESS_ANTI_SHIP: AntiShipIngressBuilder,
FlightWaypointType.INGRESS_ARMED_RECON: ArmedReconIngressBuilder,
FlightWaypointType.INGRESS_BAI: BaiIngressBuilder,
FlightWaypointType.INGRESS_CAS: CasIngressBuilder,
FlightWaypointType.INGRESS_DEAD: DeadIngressBuilder,
@ -203,7 +205,10 @@ class WaypointGenerator:
not self.flight.state.in_flight
and self.flight.state.spawn_type is not StartType.RUNWAY
and self.flight.departure.is_fleet
and not self.flight.client_count
and not (
self.flight.client_count
and self.flight.coalition.game.settings.player_flights_sixpack
)
):
# https://github.com/dcs-liberation/dcs_liberation/issues/1309
# Without a delay, AI aircraft will be spawned on the sixpack, which other

View File

@ -325,6 +325,20 @@ class Settings:
default=2,
detail="Creates a randomized altitude offset for airplanes.",
)
player_startup_time: int = bounded_int_option(
"Player startup time",
page=CAMPAIGN_DOCTRINE_PAGE,
section=GENERAL_SECTION,
default=10,
min=0,
max=100,
detail=(
"The startup time allocated to player flights (default : 10 minutes, AI is 2 minutes). "
"Packages have to be planned again for this to take effect. "
),
)
# Doctrine Distances Section
airbase_threat_range: int = bounded_int_option(
"Airbase threat range (NM)",
@ -346,6 +360,14 @@ class Settings:
min=0,
max=100,
)
armed_recon_engagement_range_distance: int = bounded_int_option(
"Armed Recon engagement range (NM)",
page=CAMPAIGN_DOCTRINE_PAGE,
section=DOCTRINE_DISTANCES_SECTION,
default=5,
min=0,
max=25,
)
sead_sweep_engagement_range_distance: int = bounded_int_option(
"SEAD Sweep engagement range (NM)",
page=CAMPAIGN_DOCTRINE_PAGE,
@ -424,6 +446,7 @@ class Settings:
"range is defined in the helicopter's yaml specification."
),
)
# Pilots and Squadrons
ai_pilot_levelling: bool = boolean_option(
"Allow AI pilot leveling",
@ -944,6 +967,12 @@ class Settings:
default=True,
detail=("Enables dynamic cargo for airfields, ships, FARPs & warehouses."),
)
player_flights_sixpack: bool = boolean_option(
"Player flights can spawn on the sixpack",
MISSION_GENERATOR_PAGE,
GAMEPLAY_SECTION,
default=True,
)
# Performance
perf_smoke_gen: bool = boolean_option(

View File

@ -1388,6 +1388,11 @@ class NavalControlPoint(
FlightType.SEAD_ESCORT,
]
yield from super().mission_types(for_player)
if self.is_friendly(for_player):
yield from [
FlightType.AEWC,
FlightType.REFUELING,
]
@property
def heading(self) -> Heading:
@ -1486,16 +1491,6 @@ class Carrier(NavalControlPoint):
def symbol_set_and_entity(self) -> tuple[SymbolSet, Entity]:
return SymbolSet.SEA_SURFACE, SeaSurfaceEntity.CARRIER
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
from game.ato.flighttype import FlightType
yield from super().mission_types(for_player)
if self.is_friendly(for_player):
yield from [
FlightType.AEWC,
FlightType.REFUELING,
]
def capture(self, game: Game, events: GameUpdateEvents, for_player: bool) -> None:
raise RuntimeError("Carriers cannot be captured")
@ -1661,7 +1656,6 @@ class Fob(ControlPoint, RadioFrequencyContainer, CTLD):
from game.ato import FlightType
if not self.is_friendly(for_player):
yield FlightType.STRIKE
yield FlightType.AIR_ASSAULT
if self.total_aircraft_parking(ParkingType(True, True, True)):
yield FlightType.OCA_AIRCRAFT

View File

@ -39,6 +39,7 @@ class MissionTarget:
FlightType.TARCAP,
FlightType.SEAD_ESCORT,
FlightType.SEAD_SWEEP,
FlightType.ARMED_RECON,
FlightType.SWEEP,
# TODO: FlightType.ELINT,
# TODO: FlightType.EWAR,

View File

@ -84,6 +84,8 @@ class ModSettings:
f106_deltadart: bool = False
hercules: bool = False
irondome: bool = False
oh_6: bool = False
oh_6_vietnamassetpack: bool = False
uh_60l: bool = False
jas39_gripen: bool = False
sk_60: bool = False

View File

@ -21,6 +21,8 @@ from .hercules import *
from .highdigitsams import *
from .irondome import *
from .jas39 import *
from .oh6 import *
from .oh6_vietnamassetpack import *
from .ov10a import *
from .spanishnavypack import *
from .super_etendard import *

View File

@ -0,0 +1 @@
from .oh6 import *

161
pydcs_extensions/oh6/oh6.py Normal file
View File

@ -0,0 +1,161 @@
from typing import Set
from dcs import task
from dcs.helicopters import HelicopterType
from game.modsupport import helicoptermod
from pydcs_extensions.weapon_injector import inject_weapons
class WeaponsOH6:
Camrig = {"clsid": "{OH-6_CAMRIG}", "name": "Camrig", "weight": 70}
Frag_Grenade = {"clsid": "{OH6_FRAG}", "name": "Frag Grenade", "weight": 0}
M134_Door_Minigun = {
"clsid": "{OH-6_M134_Door}",
"name": "M134 Door Minigun",
"weight": 110,
}
M134_Minigun_ = {
"clsid": "{OH-6_M134_Minigun}",
"name": "M134 Minigun",
"weight": 39,
}
M60_Doorgun = {"clsid": "{OH-6_M60_Door}", "name": "M60 Doorgun", "weight": 110}
Searchlight = {"clsid": "{OH-6_Searchlight}", "name": "Searchlight", "weight": 70}
SMOKE_Grenade_Blue = {
"clsid": "{OH6_SMOKE_BLUE}",
"name": "SMOKE Grenade Blue",
"weight": 0,
}
SMOKE_Grenade_Green = {
"clsid": "{OH6_SMOKE_GREEN}",
"name": "SMOKE Grenade Green",
"weight": 0,
}
SMOKE_Grenade_RED = {
"clsid": "{OH6_SMOKE_RED}",
"name": "SMOKE Grenade RED",
"weight": 0,
}
SMOKE_Grenade_yellow = {
"clsid": "{OH6_SMOKE_YELLOW}",
"name": "SMOKE Grenade yellow",
"weight": 0,
}
XM158_Weapon_System__4_ = {
"clsid": "{OH6_XM158_4}",
"name": "XM158 Weapon System (4)",
"weight": 76.8,
}
XM158_Weapon_System__7_ = {
"clsid": "{OH6_XM158}",
"name": "XM158 Weapon System (7)",
"weight": 99.9,
}
inject_weapons(WeaponsOH6)
@helicoptermod
class OH_6A(HelicopterType):
id = "OH-6A"
flyable = True
height = 3
width = 8.33
length = 10
fuel_max = 181
max_speed = 217
category = "Air" # Helicopter
radio_frequency = 262
panel_radio = {
1: {
"channels": {
1: 264,
2: 265,
4: 254,
8: 258,
16: 267,
17: 251,
9: 262,
18: 253,
5: 250,
10: 259,
20: 252,
11: 268,
3: 256,
6: 270,
12: 269,
13: 260,
7: 257,
14: 263,
19: 266,
15: 261,
},
},
}
property_defaults = {
"CableCutterEnables": False,
}
class Properties:
class CableCutterEnables:
id = "CableCutterEnables"
livery_name = "OH-6A" # from type
class Pylon1:
SMOKE_Grenade_RED = (1, WeaponsOH6.SMOKE_Grenade_RED)
SMOKE_Grenade_Green = (1, WeaponsOH6.SMOKE_Grenade_Green)
SMOKE_Grenade_Blue = (1, WeaponsOH6.SMOKE_Grenade_Blue)
SMOKE_Grenade_yellow = (1, WeaponsOH6.SMOKE_Grenade_yellow)
class Pylon2:
SMOKE_Grenade_RED = (2, WeaponsOH6.SMOKE_Grenade_RED)
SMOKE_Grenade_Green = (2, WeaponsOH6.SMOKE_Grenade_Green)
SMOKE_Grenade_Blue = (2, WeaponsOH6.SMOKE_Grenade_Blue)
SMOKE_Grenade_yellow = (2, WeaponsOH6.SMOKE_Grenade_yellow)
class Pylon3:
SMOKE_Grenade_RED = (3, WeaponsOH6.SMOKE_Grenade_RED)
SMOKE_Grenade_Green = (3, WeaponsOH6.SMOKE_Grenade_Green)
SMOKE_Grenade_Blue = (3, WeaponsOH6.SMOKE_Grenade_Blue)
SMOKE_Grenade_yellow = (3, WeaponsOH6.SMOKE_Grenade_yellow)
class Pylon4:
SMOKE_Grenade_RED = (4, WeaponsOH6.SMOKE_Grenade_RED)
SMOKE_Grenade_Green = (4, WeaponsOH6.SMOKE_Grenade_Green)
SMOKE_Grenade_Blue = (4, WeaponsOH6.SMOKE_Grenade_Blue)
SMOKE_Grenade_yellow = (4, WeaponsOH6.SMOKE_Grenade_yellow)
class Pylon5:
Frag_Grenade = (5, WeaponsOH6.Frag_Grenade)
# ERRR <CLEAN>
# ERRR <CLEAN>
class Pylon8:
M134_Minigun_ = (8, WeaponsOH6.M134_Minigun_)
class Pylon9:
XM158_Weapon_System__7_ = (9, WeaponsOH6.XM158_Weapon_System__7_)
XM158_Weapon_System__4_ = (9, WeaponsOH6.XM158_Weapon_System__4_)
class Pylon10:
XM158_Weapon_System__7_ = (10, WeaponsOH6.XM158_Weapon_System__7_)
XM158_Weapon_System__4_ = (10, WeaponsOH6.XM158_Weapon_System__4_)
class Pylon11:
M60_Doorgun = (11, WeaponsOH6.M60_Doorgun)
M134_Door_Minigun = (11, WeaponsOH6.M134_Door_Minigun)
class Pylon12:
Camrig = (12, WeaponsOH6.Camrig)
Searchlight = (12, WeaponsOH6.Searchlight)
pylons: Set[int] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
tasks = [task.Transport, task.Reconnaissance]
task_default = task.Reconnaissance

View File

@ -0,0 +1 @@
from .oh6_vietnamassetpack import *

View File

@ -0,0 +1,336 @@
# Requires OH-6 Vietnam Asset Pack:
# https://github.com/tobi-be/DCS-OH-6A
#
from typing import Set
from dcs import unittype, task
from dcs.helicopters import HelicopterType
from game.modsupport import vehiclemod, shipmod, helicoptermod
@vehiclemod
class Vap_mutt_gun(unittype.VehicleType):
id = "vap_mutt_gun"
name = "VAP US MUTT Gun"
detection_range = 0
threat_range = 5000
air_weapon_dist = 5000
@vehiclemod
class Vap_type63_mlrs(unittype.VehicleType):
id = "vap_type63_mlrs"
name = "VAP VC Type63 107mm MLRS"
detection_range = 5000
threat_range = 5000
air_weapon_dist = 5000
@vehiclemod
class Vap_vc_bicycle_mortar(unittype.VehicleType):
id = "vap_vc_bicycle_mortar"
name = "VAP VC Bicycle Mortar"
detection_range = 0
threat_range = 7000
air_weapon_dist = 7000
@vehiclemod
class Vap_zis_150_aa(unittype.VehicleType):
id = "vap_zis_150_aa"
name = "VAP VC Zis 150 AAA"
detection_range = 5000
threat_range = 7000
air_weapon_dist = 7000
@vehiclemod
class Vap_us_hooch_LP(unittype.VehicleType):
id = "vap_us_hooch_LP"
name = "VAP US Hooch Low Poly"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_ammo_50cal_line(unittype.VehicleType):
id = "vap_ammo_50cal_line"
name = "VAP US Ammo 50Cal Line"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_ammo_50cal_pack(unittype.VehicleType):
id = "vap_ammo_50cal_pack"
name = "VAP US Ammo 50Cal Pack"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_barrels_line(unittype.VehicleType):
id = "vap_barrels_line"
name = "VAP Barrels Line"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_barrels(unittype.VehicleType):
id = "vap_barrels"
name = "VAP Barrels Pack"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_ammo_box_pile(unittype.VehicleType):
id = "vap_ammo_box_pile"
name = "VAP Ammo Box Pile"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_ammo_box_wood_long(unittype.VehicleType):
id = "vap_ammo_box_wood_long"
name = "VAP Ammo Box Long"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_ammo_box_wood_small(unittype.VehicleType):
id = "vap_ammo_box_wood_small"
name = "VAP Ammo Box Small"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_barrel_red(unittype.VehicleType):
id = "vap_barrel_red"
name = "VAP Barrel Red"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_barrel_green(unittype.VehicleType):
id = "vap_barrel_green"
name = "VAP Barrel Green"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_mre_boxes(unittype.VehicleType):
id = "vap_mre_boxes"
name = "VAP US MRE Boxes"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_mixed_cargo_1(unittype.VehicleType):
id = "vap_mixed_cargo_1"
name = "VAP US Mixed Cargo 1"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_mixed_cargo_2(unittype.VehicleType):
id = "vap_mixed_cargo_2"
name = "VAP US Mixed Cargo 2"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_watchtower(unittype.VehicleType):
id = "vap_watchtower"
name = "VAP Vietcong Watchtower"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_house_high(unittype.VehicleType):
id = "vap_house_high"
name = "VAP Bamboo House High"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_house_long(unittype.VehicleType):
id = "vap_house_long"
name = "VAP Bamboo House Long"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_house_small(unittype.VehicleType):
id = "vap_house_small"
name = "VAP Bamboo House Small"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_house_T(unittype.VehicleType):
id = "vap_house_T"
name = "VAP Bamboo House T-Shape"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_house_tiny(unittype.VehicleType):
id = "vap_house_tiny"
name = "VAP Bamboo House Tiny"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_house1(unittype.VehicleType):
id = "vap_house1"
name = "VAP Bamboo House"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_us_hooch_radio(unittype.VehicleType):
id = "vap_us_hooch_radio"
name = "VAP US Hooch Radio"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_us_hooch_closed(unittype.VehicleType):
id = "vap_us_hooch_closed"
name = "VAP US Hooch"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_vc_bunker_single(unittype.VehicleType):
id = "vap_vc_bunker_single"
name = "VAP VC Bunker"
detection_range = 0
threat_range = 800
air_weapon_dist = 800
@vehiclemod
class Vap_vc_mg_nest(unittype.VehicleType):
id = "vap_vc_mg_nest"
name = "VAP VC MG Nest"
detection_range = 1000
threat_range = 500
air_weapon_dist = 500
@vehiclemod
class Vap_mule(unittype.VehicleType):
id = "vap_mule"
name = "VAP US Mule"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_mutt(unittype.VehicleType):
id = "vap_mutt"
name = "VAP US MUTT"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_m35_truck(unittype.VehicleType):
id = "vap_m35_truck"
name = "VAP US M35 Truck"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_vc_zis(unittype.VehicleType):
id = "vap_vc_zis"
name = "VAP VC Zis 150"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_vc_bicycle(unittype.VehicleType):
id = "vap_vc_bicycle"
name = "VAP VC Bicycle"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
@vehiclemod
class Vap_vc_zil(unittype.VehicleType):
id = "vap_vc_zil"
name = "VAP VC Zil 130"
detection_range = 5000
threat_range = 500
air_weapon_dist = 500
@vehiclemod
class Vap_vc_bicycle_ak(unittype.VehicleType):
id = "vap_vc_bicycle_ak"
name = "VAP VC Bicycle AK"
detection_range = 5000
threat_range = 500
air_weapon_dist = 500
@shipmod
class Vap_us_seafloat(unittype.ShipType):
id = "vap_us_seafloat"
name = "VAP - US Sea Float Barge"
helicopter_num = 4
parking = 4
detection_range = 0
threat_range = 0
air_weapon_dist = 0

View File

@ -335,6 +335,9 @@ def create_game(
f104_starfighter=False,
f105_thunderchief=False,
hercules=False,
oh_6=False,
oh_6_vietnamassetpack=False,
uh_60l=False,
jas39_gripen=False,
sk60_saab105=False,
su15_flagon=False,

View File

@ -33,6 +33,12 @@ class SquadronLiverySelector(QComboBox):
for x in faction.liveries_overrides.get(self.aircraft_type, [])
if x in [y.id.lower() for y in liveries]
]
if selected_livery is None and squadron.livery_set:
self.addItem("Using livery-set from squadron's yaml", userData=None)
self.setEnabled(False)
return
if selected_livery is None and squadron.aircraft.default_livery:
selected_livery = squadron.aircraft.default_livery
if len(overrides) > 0:
self.addItem("Use livery overrides", userData=None)
for livery in sorted(liveries):

View File

@ -130,6 +130,20 @@ class QAutoCreateDialog(QDialog):
self.sead_sweep_type,
)
hbox = QHBoxLayout()
self.armed_recon = self._create_checkbox("Armed Recon")
self.armed_recon_count = _spinbox_template()
hbox.addWidget(self.armed_recon)
hbox.addWidget(self.armed_recon_count)
self.armed_recon_type = self._create_type_selector(FlightType.ARMED_RECON)
hbox.addWidget(self.armed_recon_type)
self.layout.addLayout(hbox)
self.checkboxes[self.armed_recon] = (
FlightType.ARMED_RECON,
self.armed_recon_count,
self.armed_recon_type,
)
hbox = QHBoxLayout()
self.refueling = self._create_checkbox("Refueling")
self.refueling_count = _spinbox_template()
@ -161,6 +175,8 @@ class QAutoCreateDialog(QDialog):
FlightType.ANTISHIP,
FlightType.BAI,
FlightType.CAS,
FlightType.ARMED_RECON,
FlightType.AIR_ASSAULT,
}
for mt in self.package.target.mission_types(self.is_ownfor):
if mt in primary_tasks:

View File

@ -107,6 +107,8 @@ class NewGameWizard(QtWidgets.QWizard):
f106_deltadart=self.field("f106_deltadart"),
hercules=self.field("hercules"),
irondome=self.field("irondome"),
oh_6=self.field("oh_6"),
oh_6_vietnamassetpack=self.field("oh_6_vietnamassetpack"),
uh_60l=self.field("uh_60l"),
jas39_gripen=self.field("jas39_gripen"),
super_etendard=self.field("super_etendard"),

View File

@ -98,6 +98,10 @@ class GeneratorOptions(QtWidgets.QWizardPage):
self.registerField("ea6b_prowler", self.ea6b_prowler)
self.hercules = QtWidgets.QCheckBox()
self.registerField("hercules", self.hercules)
self.oh_6 = QtWidgets.QCheckBox()
self.registerField("oh_6", self.oh_6)
self.oh_6_vietnamassetpack = QtWidgets.QCheckBox()
self.registerField("oh_6_vietnamassetpack", self.oh_6_vietnamassetpack)
self.uh_60l = QtWidgets.QCheckBox()
self.registerField("uh_60l", self.uh_60l)
self.f4bc_phantom = QtWidgets.QCheckBox()
@ -196,6 +200,8 @@ class GeneratorOptions(QtWidgets.QWizardPage):
("Su-57 Felon (build-04)", self.su57_felon),
("Super Étendard (v2.5.5)", self.super_etendard),
("Swedish Military Assets pack (1.10)", self.swedishmilitaryassetspack),
("OH-6 Cayuse (v1.2)", self.oh_6),
("OH-6 Vietnam Asset Pack (v1.0)", self.oh_6_vietnamassetpack),
("UH-60L Black Hawk (v1.3.1)", self.uh_60l),
]

View File

@ -8,6 +8,8 @@ description: <p>Argentina and China have taken the Falklands by surprise. Curren
miz: RetakeTheFalklands.miz
performance: 2
recommended_start_date: 2002-12-21
settings:
squadron_start_full: true
version: "10.7"
squadrons:
# Off-map spawn
@ -22,6 +24,12 @@ squadrons:
aircraft:
- F-15E Strike Eagle (Suite 4+)
size: 12
# Gamblers
- primary: DEAD
secondary: any
aircraft:
- 77th FS
size: 12
- primary: Strike
secondary: air-to-ground
aircraft:
@ -88,14 +96,6 @@ squadrons:
size: 12
aircraft:
- J-15 Flanker X-2
# Blue - San Julian
11:
# Gamblers
- primary: DEAD
secondary: any
aircraft:
- 77th FS
size: 9
# RED - Punta Arenas
9:
- primary: CAS

Binary file not shown.

View File

@ -16,14 +16,25 @@ recommended_player_income_multiplier: 1.5
recommended_enemy_income_multiplier: 0.7
settings:
airbase_threat_range: 300
# Set mission duration to 45 minutes
desired_player_mission_duration: 45
# Set max frontline width to 30 km
max_frontline_width: 30
squadron_start_full: true
squadrons:
#BLUFOR CVN
Naval-3:
# Pukin' Dogs F-14
# The Swordsmen F-14
- primary: BARCAP
secondary: air-to-air
aircraft:
- VF-143
- VF-32
size: 14
- primary: Strike
secondary: air-to-air
aircraft:
- VF-213
- F-14B Tomcat
size: 14
# Golden Dragons F/A-18
- primary: SEAD
@ -52,13 +63,18 @@ squadrons:
- primary: BAI
secondary: air-to-ground
aircraft:
- VMA-223
size: 12
- primary: Transport
- AV-8B Harrier II Night Attack
size: 10
- primary: Air Assault
secondary: air-to-ground
aircraft:
- HMLA-169 (UH-1H)
size: 8
- UH-1H Iroquois
size: 4
- primary: BAI
secondary: air-to-ground
aircraft:
- AH-1W SuperCobra
size: 6
# Al-Dhafra AFB
4:
#USAF F-16C Gamblers
@ -92,12 +108,6 @@ squadrons:
#USAF F-15C
# Al-Minhab AFB
12:
- primary: Transport
secondary: air-to-ground
aircraft:
- 101st Combat Aviation Brigade
#US Army UH-60
size: 6
- primary: SEAD
secondary: any
aircraft:
@ -115,8 +125,30 @@ squadrons:
#USAF A-10CI
- primary: Transport
aircraft:
- C-130J-30 Super Hercules
- C-130
size: 8
# Khasab / 12
10:
- primary: Transport
secondary: air-to-ground
aircraft:
- 101st Combat Aviation Brigade
#US Army UH-60
size: 4
- primary: BAI
size: 4
secondary: air-to-ground
aircraft:
- A-10C Thunderbolt II (Suite 7)
#USAF A-10CI
- primary: CAS
secondary: air-to-ground
aircraft:
- Wolfpack, 1-82 ARB
#US Army Apache AH-64D
size: 4
### OPFOR
# Kish Intl
24:
- primary: AEW&C
@ -156,7 +188,12 @@ squadrons:
secondary: air-to-ground
aircraft:
- MiG-29A Fulcrum-A
size: 6
size: 12
- primary: BARCAP
secondary: air-to-ground
aircraft:
- Mirage-F1EQ
size: 12
- primary: Strike
secondary: air-to-ground
aircraft:

View File

@ -3,7 +3,7 @@ name: Syria - The Long Road to H3
theater: Syria
authors: tmz42 (original campaign by Plob)
description:
<p>From Incirlik to H-3, repel the alliance between Iraq and Syria.</p>
<p>From Incirlik to H-3, repel the alliance between Syria and Iraq.</p>
<p>This campaign is based on and meant as a lighter alternative to Syria - Full Map.</p>
recommended_player_faction: NATO OIF
recommended_enemy_faction:
@ -25,7 +25,6 @@ recommended_enemy_faction:
- SA 342L Gazelle
- Su-22M4 Fitter-K
- Su-24M Fencer-D
- Tu-22M3 Backfire-C
- Su-25 Frogfoot
- Mirage-F1EQ
- H-6J Badger
@ -42,10 +41,9 @@ recommended_enemy_faction:
- MT-LB
- T-55A
- T-72B with Kontakt-1 ERA
- T-90A
- ZSU-57-2 'Sparka'
- ZSU-23-4 Shilka
artillery_units:
- BM-21 Grad
- 2S1 Gvozdika
- 2S9 Nona-S
logistics_units:
@ -65,19 +63,16 @@ recommended_enemy_faction:
- SA-6
- SA-11
- SA-10/S-300PS
- SA-12/S-300V
- Cold-War-Flak
- Silkworm
- KS-19/SON-9
naval_units:
- Corvette 1124.4 Grish
- Corvette 1241.1 Molniya
- FAC La Combattante IIa
- Frigate 1135M Rezky
- LS Ropucha
air_defense_units:
- SAM P19 "Flat Face" SR (SA-2/3)
- EWR 1L13
- EWR 55G6
- SAM SA-8 Osa "Gecko" TEL
- SA-9 Strela
- SA-13 Gopher (9K35 Strela-10M3)
@ -102,6 +97,7 @@ settings:
desired_player_mission_duration: 45
# Set max frontline width to 30 km
max_frontline_width: 30
squadron_start_full: true
squadrons:
####################### BLUEFOR
Blue CV:

View File

@ -120,7 +120,7 @@ local unitPayloads = {
["name"] = "Retribution Escort",
["pylons"] = {
[1] = {
["CLSID"] = "{88D18A5E-99C8-4B04-B40B-1C02F2018B6E}",
["CLSID"] = "{M299_4xAGM_114L}",
["num"] = 3,
},
[2] = {
@ -128,7 +128,7 @@ local unitPayloads = {
["num"] = 4,
},
[3] = {
["CLSID"] = "{88D18A5E-99C8-4B04-B40B-1C02F2018B6E}",
["CLSID"] = "{M299_4xAGM_114L}",
["num"] = 2,
},
[4] = {

View File

@ -0,0 +1,153 @@
local unitPayloads = {
["name"] = "OH-6A",
["payloads"] = {
[1] = {
["displayName"] = "Retribution BAI",
["name"] = "Retribution BAI",
["pylons"] = {
[1] = {
["CLSID"] = "{OH-6_M60_Door}",
["num"] = 11,
},
[2] = {
["CLSID"] = "<CLEAN>",
["num"] = 6,
},
[3] = {
["CLSID"] = "<CLEAN>",
["num"] = 7,
},
[4] = {
["CLSID"] = "{OH-6_M134_Minigun}",
["num"] = 8,
},
[5] = {
["CLSID"] = "{OH6_FRAG}",
["num"] = 5,
},
[6] = {
["CLSID"] = "{OH6_SMOKE_BLUE}",
["num"] = 4,
},
[7] = {
["CLSID"] = "{OH6_SMOKE_GREEN}",
["num"] = 3,
},
[8] = {
["CLSID"] = "{OH6_SMOKE_RED}",
["num"] = 2,
},
[9] = {
["CLSID"] = "{OH6_SMOKE_YELLOW}",
["num"] = 1,
},
},
["tasks"] = {
[1] = 17,
[2] = 35,
},
},
[2] = {
["displayName"] = "Retribution OCA/Aircraft",
["name"] = "Retribution OCA/Aircraft",
["pylons"] = {
[1] = {
["CLSID"] = "{OH6_XM158}",
["num"] = 10,
},
[2] = {
["CLSID"] = "{OH6_XM158}",
["num"] = 9,
},
[3] = {
["CLSID"] = "{OH6_FRAG}",
["num"] = 5,
},
[4] = {
["CLSID"] = "{OH6_SMOKE_BLUE}",
["num"] = 4,
},
[5] = {
["CLSID"] = "<CLEAN>",
["num"] = 6,
},
[6] = {
["CLSID"] = "<CLEAN>",
["num"] = 7,
},
[7] = {
["CLSID"] = "{OH6_SMOKE_GREEN}",
["num"] = 3,
},
[8] = {
["CLSID"] = "{OH6_SMOKE_RED}",
["num"] = 2,
},
[9] = {
["CLSID"] = "{OH6_SMOKE_YELLOW}",
["num"] = 1,
},
},
["tasks"] = {
[1] = 35,
[2] = 17,
[3] = 31,
[4] = 15,
},
},
[3] = {
["displayName"] = "Retribution CAS",
["name"] = "Retribution CAS",
["pylons"] = {
[1] = {
["CLSID"] = "{OH-6 M60 Door}",
["num"] = 11,
},
[2] = {
["CLSID"] = "<CLEAN>",
["num"] = 6,
},
[3] = {
["CLSID"] = "<CLEAN>",
["num"] = 7,
},
[4] = {
["CLSID"] = "{OH-6 M134 Minigun}",
["num"] = 8,
},
[5] = {
["CLSID"] = "{OH6_FRAG}",
["num"] = 5,
},
[6] = {
["CLSID"] = "{OH6_SMOKE_BLUE}",
["num"] = 4,
},
[7] = {
["CLSID"] = "{OH6_SMOKE_GREEN}",
["num"] = 3,
},
[8] = {
["CLSID"] = "{OH6_SMOKE_RED}",
["num"] = 2,
},
[9] = {
["CLSID"] = "{OH6_SMOKE_YELLOW}",
["num"] = 1,
},
},
["tasks"] = {
[1] = 17,
[2] = 35,
},
},
},
["tasks"] = {
[1] = 35,
[2] = 17,
[3] = 31,
[4] = 15,
},
["unitType"] = "OH-6A",
}
return unitPayloads

View File

@ -7,6 +7,7 @@
"aircrafts": [
"CH-47D",
"CH-53E",
"OH-6A Cayuse",
"UH-1H Iroquois",
"AH-1W SuperCobra",
"OV-10A Bronco",
@ -38,7 +39,8 @@
"frontline_units": [
"M113",
"M163 Vulcan Air Defense System",
"M60A3 \"Patton\""
"M60A3 \"Patton\"",
"M151A1C MUTT w/ 106mm Recoilless Rifle"
],
"artillery_units": [
"M109A6 Paladin"
@ -48,7 +50,10 @@
"Infantry M4 Georgia"
],
"logistics_units": [
"Truck M818 6x6"
"Truck M818 6x6",
"VAP US M35 Truck",
"M151 1/4-ton 4x4 utility truck",
"M274 1/2-ton 4x4 utility truck"
],
"air_defense_units": [
"SAM Hawk SR (AN/MPQ-50)",

View File

@ -7,6 +7,7 @@
"aircrafts": [
"CH-47D",
"CH-53E",
"OH-6A Cayuse",
"UH-1H Iroquois",
"AH-1W SuperCobra",
"OV-10A Bronco",
@ -36,7 +37,8 @@
"frontline_units": [
"M113",
"M163 Vulcan Air Defense System",
"M60A3 \"Patton\""
"M60A3 \"Patton\"",
"M151A1C MUTT w/ 106mm Recoilless Rifle"
],
"artillery_units": [
"M109A6 Paladin"
@ -46,7 +48,10 @@
"Infantry M4 Georgia"
],
"logistics_units": [
"Truck M818 6x6"
"Truck M818 6x6",
"VAP US M35 Truck",
"M151 1/4-ton 4x4 utility truck",
"M274 1/2-ton 4x4 utility truck"
],
"air_defense_units": [
"SAM Hawk SR (AN/MPQ-50)",

View File

@ -29,11 +29,14 @@
"F-16D Fighting Falcon (Block 50+)",
"F-16D Fighting Falcon (Block 50)",
"F-22A Raptor",
"F-4E-45MC Phantom II",
"F-5E Tiger II",
"F-5E Tiger II (FC)",
"F/A-18C Hornet (Lot 20)",
"F/A-18E Super Hornet",
"F/A-18F Super Hornet",
"EA-18G Growler",
"J-11A Flanker-L",
"JF-17 Thunder",
"Ka-50 Hokum",
"Ka-50 Hokum III",
@ -52,6 +55,7 @@
"OH-58D(R) Kiowa Warrior",
"SA 342L Gazelle",
"SA 342M Gazelle",
"Su-24M Fencer-D",
"Su-25T Frogfoot",
"Su-27 Flanker-B",
"Su-30MKA Flanker-H",
@ -148,10 +152,14 @@
"jtac_unit": "MQ-9 Reaper",
"unrestricted_satnav": true,
"liveries_overrides": {
"Ka-50 Hokum": ["georgia camo"],
"J-11A Flanker-L": ["USN Aggressor VFC-13 'Ferris' (Fictional)"],
"JF-17 Thunder": ["'Splinter' Camo for Blue Side (Fictional)"],
"Ka-50 Hokum": ["georgia camo"],
"Ka-50 Hokum III": ["Ka-50_black_neutral"],
"Mi-8MTV2 Hip": ["Ukraine"],
"Mi-24P Hind-F": ["Ukrainian Army Aviation"],
"MiG-29S Fulcrum-C": ["Air Force Ukraine Standard"],
"Su-24M Fencer-D": ["Ukrainian Air Force Standard"],
"Su-25T Frogfoot": ["af standard 101"],
"Su-27 Flanker-B": ["Air Force Ukraine Standard"]
}

View File

@ -23,6 +23,7 @@
"F-105G Thunderchief",
"F-106A Delta Dart",
"F-106B Delta Dart",
"OH-6A Cayuse",
"UH-1H Iroquois"
],
"awacs": [

View File

@ -20,6 +20,7 @@
"C-47 Skytrain",
"C-130",
"C-130J-30 Super Hercules",
"OH-6A Cayuse",
"UH-1H Iroquois",
"AH-1W SuperCobra",
"OH-58D(R) Kiowa Warrior",

View File

@ -27,6 +27,7 @@
"F-106B Delta Dart",
"S-3B Viking",
"OV-10A Bronco",
"OH-6A Cayuse",
"UH-1H Iroquois"
],
"awacs": [

View File

@ -0,0 +1,54 @@
{
"country": "Vietnam",
"name": "Viet Cong 1970",
"authors": "Ghosti",
"description": "<p>National Liberation Front of South Vietnam during the Vietnam War from 1965 to 1975. Includes air units of the PAVN/VPAF. Requires the OH-6 Vietnam Asset Pack.</p>",
"locales": [
"vi_Vn"
],
"aircrafts": [
"Mi-8MTV2 Hip",
"MiG-19P Farmer-B",
"MiG-21bis Fishbed-N"
],
"awacs": [],
"tankers": [],
"frontline_units": [
"PT-76",
"VAP VC Zil 130 Armed",
"VAP VC Bicycle AK",
"VAP VC MG Nest",
"VAP VC Bunker"
],
"artillery_units": [
"VAP VC Type63 107mm MLRS"
],
"logistics_units": [
"VAP VC Zis 150",
"VAP VC Bicycle"
],
"infantry_units": [
"Infantry AK-74 Rus",
"VAP VC Bicycle Mortar"
],
"missiles": [],
"preset_groups": [
"KS-19/SON-9"
],
"naval_units": [
"Boat Armed Hi-speed",
"Boat Schnellboot type S130"
],
"air_defense_units": [
"VAP VC Zis 150 AAA",
"S-60 57mm",
"ZSU-57-2 'Sparka'",
"AAA ZU-23 Emplacement",
"ZU-23 on Ural-375",
"ZSU-23-4 Shilka"
],
"requirements": {
"OH-6 Vietnam Asset Pack": "https://github.com/tobi-be/DCS-OH-6A"
},
"doctrine": "coldwar"
}

View File

@ -6,6 +6,9 @@ country: USA
role: Strike Fighter
aircraft: F/A-18C Hornet (Lot 20)
livery: VMFA-122 high visibility
bases:
shore: true
carrier: true
mission_types:
- Anti-ship
- BAI

View File

@ -6,6 +6,9 @@ country: USA
role: Strike Fighter
aircraft: F/A-18C Hornet (Lot 20)
livery: VMFA-251 high visibility
bases:
shore: true
carrier: true
mission_types:
- Anti-ship
- BAI

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -16,6 +16,9 @@ price: 12
role: Close Air Support/Attack
variants:
A-10A Thunderbolt II: {}
altitudes:
cruise: 15000
combat: 10000
tasks:
BAI: 680
CAS: 680

View File

@ -14,6 +14,9 @@ price: 18
role: Close Air Support/Attack
variants:
A-10C Thunderbolt II (Suite 3): {}
altitudes:
cruise: 15000
combat: 10000
radios:
intra_flight: AN/ARC-186(V) AM
inter_flight: AN/ARC-164

View File

@ -14,6 +14,9 @@ price: 20
role: Close Air Support/Attack
variants:
A-10C Thunderbolt II (Suite 7): {}
altitudes:
cruise: 15000
combat: 10000
radios:
# DCS will clobber channel 1 of the AN/ARC-164 with the flight's assigned
# frequency, so we may as well use that one for intra-flight.

View File

@ -0,0 +1,20 @@
class: Helicopter
carrier_capable: false
description:
The Hughes OH-6 Cayuse is a single-engine light helicopter designed and
produced by the American aerospace company Hughes Helicopters.
introduced: 1966
lha_capable: false
can_carry_crates: false
manufacturer: Hughes Helicopters
origin: USA
price: 3
role: Light Observation/Light Attack
variants:
OH-6A Cayuse: {}
tasks:
BAI: 0
CAS: 0
OCA/Aircraft: 0
Air Assault: 50
Transport: 50

View File

@ -1,6 +1,6 @@
class: Tank
description:
introduced: 1992
description: Abrams M1A2 SEPv3 is a modernised configuration of the Abrams main battle tank (MBT) in service with the US Army.The new version offers enhanced protection and survivability, as well as higher lethality than its predecessors. The tank also features various advancements in technology, including improved armour, communications, reliability, sustainment, and fuel efficiency.
introduced: 2017
manufacturer: General Dynamics Land Systems
origin: USA
price: 25

View File

@ -1,6 +1,6 @@
class: APC
description: 2007
introduced:
description: The International M1224 MaxxPro MRAP (Mine-Resistant Ambush Protected) is an armored fighting vehicle designed by American company Navistar International's subsidiary Navistar Defense along with the Israeli Plasan Sasa, who designed and manufactures the vehicle's armor. The vehicle was designed to take part in the US military's Mine Resistant Ambush Protected vehicle program, led by the US Marine Corps, as well as a similar US Army-led Medium Mine Protected Vehicle program.
introduced: 2007
manufacturer: Navistar/Plasan
origin: USA
price: 8

View File

@ -0,0 +1,14 @@
class: ATGM
description: The M551 Sheridan AR/AAV (Armored Reconnaissance/Airborne Assault Vehicle)
is a light tank developed by the United States and named after General Philip Sheridan,
of American Civil War fame. It was designed to be landed by parachute and to swim across
rivers. It is armed with the technically advanced M81/M81 Modified/M81E1 152 mm
gun/launcher, which fires both conventional ammunition and the MGM-51 Shillelagh guided
anti-tank missile.
introduced: 1966
manufacturer: General Motors, Cadillac Motor Company
origin: USA
price: 16
role: Light Tank
variants:
M551 "Sheridan": {}

View File

@ -0,0 +1,14 @@
class: Tank
description: The T-62 is a Soviet main battle tank that was first introduced in 1961.
As a further development of the T-55 series, the T-62 retained many similar design
elements of its predecessor including low profile and thick turret armour. In
contrast with previous tanks, which were armed with rifled tank guns, the T-62 was
the first production tank armed with a smoothbore tank gun that could fire APFSDS
rounds at higher velocities.
introduced: 1961
manufacturer: UralVagonZavod
origin: USSR/Russia
price: 19
role: Main Battle Tank
variants:
T-62: {}

View File

@ -0,0 +1,17 @@
class: Tank
description: The T-64 is a Soviet second-generation main battle tank, designed by
Kharkiv Morozov Machine Building Design Bureau in Ukraine, introduced in the early
1960s. It was a more advanced counterpart to the T-62; the T-64 served in tank
divisions, while the T-62 supported infantry in motorized rifle divisions. It
introduced a number of advanced features including composite armour, a compact
engine and transmission, and a smoothbore 125-mm gun equipped with an autoloader
to allow the crew to be reduced to three so the tank could be smaller and lighter.
In spite of being armed and armoured like a heavy tank, the T-64 weighed only 38
tonnes (42 short tons; 37 long tons).
introduced: 1966
manufacturer: Kharkiv
origin: USSR/Russia
price: 19
role: Main Battle Tank
variants:
T-64BV: {}

View File

@ -0,0 +1,4 @@
class: Logistics
price: 3
variants:
VAP US M35 Truck: null

View File

@ -0,0 +1,5 @@
class: Logistics
price: 3
variants:
VAP US Mule: {}
M274 1/2-ton 4x4 utility truck: {}

View File

@ -0,0 +1,5 @@
class: Logistics
price: 3
variants:
VAP US MUTT: {}
M151 1/4-ton 4x4 utility truck: {}

View File

@ -0,0 +1,6 @@
class: ATGM
price: 10
role: Self-Propelled Recoilless Rifle
variants:
VAP US MUTT Gun: {}
M151A1C MUTT w/ 106mm Recoilless Rifle: {}

View File

@ -0,0 +1,5 @@
class: Artillery
price: 10
role: Multiple-Launch Rocket System
variants:
VAP VC Type63 107mm MLRS: {}

View File

@ -0,0 +1,4 @@
class: Logistics
price: 2
variants:
VAP VC Bicycle: null

View File

@ -0,0 +1,5 @@
class: Recon
price: 2
role: Recon
variants:
VAP VC Bicycle AK: {}

View File

@ -0,0 +1,5 @@
class: Infantry
price: 0
spawn_weight: 1
variants:
VAP VC Bicycle Mortar: null

View File

@ -0,0 +1,4 @@
class: IFV
price: 4
variants:
VAP VC Bunker: null

View File

@ -0,0 +1,4 @@
class: APC
price: 3
variants:
VAP VC MG Nest: null

View File

@ -0,0 +1,6 @@
class: ATGM
description:
price: 7
role: Armed Truck
variants:
VAP VC Zil 130 Armed: {}

View File

@ -0,0 +1,4 @@
class: Logistics
price: 3
variants:
VAP VC Zis 150: null

View File

@ -0,0 +1,6 @@
class: AAA
description:
price: 7
role: Self-Propelled Anti-Aircraft Gun
variants:
VAP VC Zis 150 AAA: {}

View File

@ -0,0 +1,4 @@
class: Boat
price: 0
variants:
VAP - US Sea Float Barge: null