diff --git a/changelog.md b/changelog.md index 2235cd6b0..2279041bd 100644 --- a/changelog.md +++ b/changelog.md @@ -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 diff --git a/game/ato/flight.py b/game/ato/flight.py index 96a130723..014c40a41 100644 --- a/game/ato/flight.py +++ b/game/ato/flight.py @@ -275,8 +275,6 @@ def initialize_fuel(self) -> None: 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( diff --git a/game/ato/flightplans/airassault.py b/game/ato/flightplans/airassault.py index 14da523b6..19402b6aa 100644 --- a/game/ato/flightplans/airassault.py +++ b/game/ato/flightplans/airassault.py @@ -75,13 +75,6 @@ def ingress_time(self) -> datetime: ) 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 @@ def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> datetime | None: 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 diff --git a/game/ato/flightplans/armedrecon.py b/game/ato/flightplans/armedrecon.py new file mode 100644 index 000000000..4ccc04399 --- /dev/null +++ b/game/ato/flightplans/armedrecon.py @@ -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()) diff --git a/game/ato/flightplans/cas.py b/game/ato/flightplans/cas.py index 26f7e7c65..6700cb521 100644 --- a/game/ato/flightplans/cas.py +++ b/game/ato/flightplans/cas.py @@ -125,16 +125,12 @@ def layout(self, dump_debug_info: bool) -> 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" diff --git a/game/ato/flightplans/escort.py b/game/ato/flightplans/escort.py index 53385f24d..bdae0ac14 100644 --- a/game/ato/flightplans/escort.py +++ b/game/ato/flightplans/escort.py @@ -11,6 +11,7 @@ ) from .waypointbuilder import WaypointBuilder from .. import FlightType +from ...utils import feet class EscortFlightPlan(FormationAttackFlightPlan): @@ -34,7 +35,7 @@ def layout(self) -> 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 @@ def layout(self) -> 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( diff --git a/game/ato/flightplans/flightplan.py b/game/ato/flightplans/flightplan.py index ed5823531..c1a510853 100644 --- a/game/ato/flightplans/flightplan.py +++ b/game/ato/flightplans/flightplan.py @@ -105,19 +105,6 @@ def best_speed_between_waypoints( # # 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 @@ def startup_time(self) -> datetime: 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) diff --git a/game/ato/flightplans/flightplanbuildertypes.py b/game/ato/flightplans/flightplanbuildertypes.py index 4561eeec9..5ec691d2b 100644 --- a/game/ato/flightplans/flightplanbuildertypes.py +++ b/game/ato/flightplans/flightplanbuildertypes.py @@ -7,6 +7,7 @@ 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 @@ def for_flight(flight: Flight) -> Type[IBuilder[Any, Any]]: 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] diff --git a/game/ato/flightplans/formation.py b/game/ato/flightplans/formation.py index 92d4aba8b..407743e0e 100644 --- a/game/ato/flightplans/formation.py +++ b/game/ato/flightplans/formation.py @@ -64,8 +64,10 @@ def best_flight_formation_speed(self) -> Speed: 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 diff --git a/game/ato/flightplans/formationattack.py b/game/ato/flightplans/formationattack.py index c751a0387..65c72c82e 100644 --- a/game/ato/flightplans/formationattack.py +++ b/game/ato/flightplans/formationattack.py @@ -11,7 +11,7 @@ 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 @@ def speed_between_waypoints(self, a: FlightWaypoint, b: FlightWaypoint) -> Speed 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 @@ def target_area_waypoint(self) -> FlightWaypoint: "TARGET AREA", FlightWaypointType.TARGET_GROUP_LOC, self.package.target.position, - meters(0), + feet(0), "RADIO", ) @@ -192,6 +193,7 @@ def _build( 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 @@ def target_area_waypoint( 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) diff --git a/game/ato/flightplans/waypointbuilder.py b/game/ato/flightplans/waypointbuilder.py index 74b1bc8d6..ebeec5b82 100644 --- a/game/ato/flightplans/waypointbuilder.py +++ b/game/ato/flightplans/waypointbuilder.py @@ -1,5 +1,6 @@ from __future__ import annotations +import math import random from dataclasses import dataclass from typing import ( @@ -252,18 +253,9 @@ def ingress( 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 @@ def ingress( 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 @@ def sead_area(self, target: MissionTarget) -> FlightWaypoint: 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 @@ def _target_area( 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 @@ def cas(self, position: Point) -> FlightWaypoint: 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 @@ def dropoff_zone(self, drop_off: MissionTarget) -> FlightWaypoint: 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 @@ def nav_point_prunable(self, previous: Point, current: Point, nxt: Point) -> boo 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) diff --git a/game/ato/flighttype.py b/game/ato/flighttype.py index b8eeb1c92..7b210e770 100644 --- a/game/ato/flighttype.py +++ b/game/ato/flighttype.py @@ -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 @@ def is_air_to_ground(self) -> bool: FlightType.SEAD_ESCORT, FlightType.AIR_ASSAULT, FlightType.SEAD_SWEEP, + FlightType.ARMED_RECON, } @property @@ -107,6 +107,7 @@ def entity_type(self) -> AirEntity: 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, diff --git a/game/ato/flightwaypointtype.py b/game/ato/flightwaypointtype.py index 8ba5024c3..6a6c691be 100644 --- a/game/ato/flightwaypointtype.py +++ b/game/ato/flightwaypointtype.py @@ -51,3 +51,4 @@ class FlightWaypointType(IntEnum): INGRESS_AIR_ASSAULT = 31 INGRESS_ANTI_SHIP = 32 INGRESS_SEAD_SWEEP = 33 + INGRESS_ARMED_RECON = 34 diff --git a/game/ato/loadouts.py b/game/ato/loadouts.py index 2b3ded303..8b3f692a6 100644 --- a/game/ato/loadouts.py +++ b/game/ato/loadouts.py @@ -208,6 +208,7 @@ def default_loadout_names_for(cls, task: FlightType) -> Iterator[str]: 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]) diff --git a/game/ato/package.py b/game/ato/package.py index 387b03ed3..fb56a46eb 100644 --- a/game/ato/package.py +++ b/game/ato/package.py @@ -54,8 +54,7 @@ def __init__( 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 @@ def formation_speed(self) -> Optional[Speed]: """ 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 @@ def primary_task(self) -> Optional[FlightType]: FlightType.SEAD_SWEEP, FlightType.TARCAP, FlightType.BARCAP, + FlightType.ARMED_RECON, FlightType.AEWC, FlightType.FERRY, FlightType.REFUELING, diff --git a/game/ato/traveltime.py b/game/ato/traveltime.py index 66afa547c..d3784d74c 100644 --- a/game/ato/traveltime.py +++ b/game/ato/traveltime.py @@ -3,7 +3,7 @@ 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 @@ def for_flight(cls, flight: Flight, altitude: Distance) -> Speed: # 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. diff --git a/game/campaignloader/defaultsquadronassigner.py b/game/campaignloader/defaultsquadronassigner.py index 8702496a8..763d31d58 100644 --- a/game/campaignloader/defaultsquadronassigner.py +++ b/game/campaignloader/defaultsquadronassigner.py @@ -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 @@ def find_preferred_squadron( 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 @@ def squadron_compatible_with( 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( diff --git a/game/commander/objectivefinder.py b/game/commander/objectivefinder.py index c789392ed..020398a3c 100644 --- a/game/commander/objectivefinder.py +++ b/game/commander/objectivefinder.py @@ -167,24 +167,6 @@ def vulnerable_control_points(self) -> Iterator[ControlPoint]: 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 diff --git a/game/commander/theaterstate.py b/game/commander/theaterstate.py index 36cc62625..f84735a9b 100644 --- a/game/commander/theaterstate.py +++ b/game/commander/theaterstate.py @@ -6,7 +6,7 @@ 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 @@ def from_game( 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 @@ def from_game( 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 @@ def from_game( 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, ) diff --git a/game/dcs/aircrafttype.py b/game/dcs/aircrafttype.py index b26198fbe..60c8c7a24 100644 --- a/game/dcs/aircrafttype.py +++ b/game/dcs/aircrafttype.py @@ -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 @@ def preferred_patrol_speed(self, altitude: Distance) -> Speed: 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 @@ def _variant_from_dict( 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 @@ def _variant_from_dict( 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] diff --git a/game/factions/faction.py b/game/factions/faction.py index 54fee4c39..a08c8a298 100644 --- a/game/factions/faction.py +++ b/game/factions/faction.py @@ -352,6 +352,45 @@ def apply_mod_settings(self, mod_settings: Optional[ModSettings] = None) -> None 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") diff --git a/game/migrator.py b/game/migrator.py index c96d62a1e..2dbc178ce 100644 --- a/game/migrator.py +++ b/game/migrator.py @@ -11,6 +11,7 @@ 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 @@ def _update_squadrons(self) -> None: 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]() diff --git a/game/missiongenerator/aircraft/aircraftbehavior.py b/game/missiongenerator/aircraft/aircraftbehavior.py index e458305e9..9b129d159 100644 --- a/game/missiongenerator/aircraft/aircraftbehavior.py +++ b/game/missiongenerator/aircraft/aircraftbehavior.py @@ -57,6 +57,8 @@ def apply_to(self, flight: Flight, group: FlyingGroup[Any]) -> None: 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 @@ def configure_cas(self, group: FlyingGroup[Any], flight: Flight) -> None: 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 @@ def configure_task( 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." + ) diff --git a/game/missiongenerator/aircraft/flightgroupspawner.py b/game/missiongenerator/aircraft/flightgroupspawner.py index 7bca76b23..bcdcd1bbf 100644 --- a/game/missiongenerator/aircraft/flightgroupspawner.py +++ b/game/missiongenerator/aircraft/flightgroupspawner.py @@ -497,21 +497,20 @@ def _generate_at_cp_ground_spawn( ) -> Optional[FlyingGroup[Any]]: is_airbase = False is_roadbase = False - - 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)) + ground_spawn = None + + 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 @@ def _generate_at_cp_ground_spawn( 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 ) diff --git a/game/missiongenerator/aircraft/waypoints/armedreconingress.py b/game/missiongenerator/aircraft/waypoints/armedreconingress.py new file mode 100644 index 000000000..094517dcc --- /dev/null +++ b/game/missiongenerator/aircraft/waypoints/armedreconingress.py @@ -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, + ], + ) + ) + ) diff --git a/game/missiongenerator/aircraft/waypoints/holdpoint.py b/game/missiongenerator/aircraft/waypoints/holdpoint.py index 6d2cdd768..72ade67e9 100644 --- a/game/missiongenerator/aircraft/waypoints/holdpoint.py +++ b/game/missiongenerator/aircraft/waypoints/holdpoint.py @@ -17,7 +17,7 @@ def add_tasks(self, waypoint: MovingPoint) -> None: loiter = ControlledTask( OrbitAction( altitude=waypoint.alt, - speed=speed.meters_per_second, + speed=speed.kph, pattern=OrbitAction.OrbitPattern.Circle, ) ) diff --git a/game/missiongenerator/aircraft/waypoints/joinpoint.py b/game/missiongenerator/aircraft/waypoints/joinpoint.py index 19056df9c..034c46be0 100644 --- a/game/missiongenerator/aircraft/waypoints/joinpoint.py +++ b/game/missiongenerator/aircraft/waypoints/joinpoint.py @@ -8,7 +8,6 @@ OptECMUsing, OptFormation, Targets, - OptROE, SetUnlimitedFuelCommand, ) @@ -94,12 +93,6 @@ def configure_escort_tasks( 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]) diff --git a/game/missiongenerator/aircraft/waypoints/ocaaircraftingress.py b/game/missiongenerator/aircraft/waypoints/ocaaircraftingress.py index 8044dabab..3e0fce9ae 100644 --- a/game/missiongenerator/aircraft/waypoints/ocaaircraftingress.py +++ b/game/missiongenerator/aircraft/waypoints/ocaaircraftingress.py @@ -3,7 +3,7 @@ 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__, diff --git a/game/missiongenerator/aircraft/waypoints/seadsweepingress.py b/game/missiongenerator/aircraft/waypoints/seadsweepingress.py index 2e1653530..f06eefd7c 100644 --- a/game/missiongenerator/aircraft/waypoints/seadsweepingress.py +++ b/game/missiongenerator/aircraft/waypoints/seadsweepingress.py @@ -2,9 +2,8 @@ from dcs.task import ( OptECMUsing, ControlledTask, - EngageTargets, Targets, - OptROE, + EngageTargetsInZone, ) from game.utils import nautical_miles @@ -14,16 +13,15 @@ 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 diff --git a/game/missiongenerator/aircraft/waypoints/waypointgenerator.py b/game/missiongenerator/aircraft/waypoints/waypointgenerator.py index 6974d55ed..5a5cb974a 100644 --- a/game/missiongenerator/aircraft/waypoints/waypointgenerator.py +++ b/game/missiongenerator/aircraft/waypoints/waypointgenerator.py @@ -22,6 +22,7 @@ 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 @@ def builder_for_waypoint(self, waypoint: FlightWaypoint) -> PydcsWaypointBuilder 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 @@ def set_takeoff_time(self, waypoint: FlightWaypoint) -> timedelta: 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 diff --git a/game/settings/settings.py b/game/settings/settings.py index 63abc0520..301bddf0f 100644 --- a/game/settings/settings.py +++ b/game/settings/settings.py @@ -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( diff --git a/game/theater/controlpoint.py b/game/theater/controlpoint.py index c5fe88015..04fde9eb2 100644 --- a/game/theater/controlpoint.py +++ b/game/theater/controlpoint.py @@ -1388,6 +1388,11 @@ def mission_types(self, for_player: bool) -> Iterator[FlightType]: 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 @@ def __init__( 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 @@ def mission_types(self, for_player: bool) -> Iterator[FlightType]: 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 diff --git a/game/theater/missiontarget.py b/game/theater/missiontarget.py index 6350a6fba..4637aa443 100644 --- a/game/theater/missiontarget.py +++ b/game/theater/missiontarget.py @@ -39,6 +39,7 @@ def mission_types(self, for_player: bool) -> Iterator[FlightType]: FlightType.TARCAP, FlightType.SEAD_ESCORT, FlightType.SEAD_SWEEP, + FlightType.ARMED_RECON, FlightType.SWEEP, # TODO: FlightType.ELINT, # TODO: FlightType.EWAR, diff --git a/game/theater/start_generator.py b/game/theater/start_generator.py index d1b512b1d..3debeb96f 100644 --- a/game/theater/start_generator.py +++ b/game/theater/start_generator.py @@ -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 diff --git a/pydcs_extensions/__init__.py b/pydcs_extensions/__init__.py index 08731b9cb..869bb13db 100644 --- a/pydcs_extensions/__init__.py +++ b/pydcs_extensions/__init__.py @@ -21,6 +21,8 @@ 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 * diff --git a/pydcs_extensions/oh6/__init__.py b/pydcs_extensions/oh6/__init__.py new file mode 100644 index 000000000..c0df1e5ab --- /dev/null +++ b/pydcs_extensions/oh6/__init__.py @@ -0,0 +1 @@ +from .oh6 import * diff --git a/pydcs_extensions/oh6/oh6.py b/pydcs_extensions/oh6/oh6.py new file mode 100644 index 000000000..2ea6e1a76 --- /dev/null +++ b/pydcs_extensions/oh6/oh6.py @@ -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 + # ERRR + + 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 diff --git a/pydcs_extensions/oh6_vietnamassetpack/__init__.py b/pydcs_extensions/oh6_vietnamassetpack/__init__.py new file mode 100644 index 000000000..c6b04bae3 --- /dev/null +++ b/pydcs_extensions/oh6_vietnamassetpack/__init__.py @@ -0,0 +1 @@ +from .oh6_vietnamassetpack import * diff --git a/pydcs_extensions/oh6_vietnamassetpack/oh6_vietnamassetpack.py b/pydcs_extensions/oh6_vietnamassetpack/oh6_vietnamassetpack.py new file mode 100644 index 000000000..154b43127 --- /dev/null +++ b/pydcs_extensions/oh6_vietnamassetpack/oh6_vietnamassetpack.py @@ -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 diff --git a/qt_ui/main.py b/qt_ui/main.py index 658c75dd4..a3fd8781b 100644 --- a/qt_ui/main.py +++ b/qt_ui/main.py @@ -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, diff --git a/qt_ui/widgets/combos/QSquadronLiverySelector.py b/qt_ui/widgets/combos/QSquadronLiverySelector.py index 8ec8b0dfc..3dd675490 100644 --- a/qt_ui/widgets/combos/QSquadronLiverySelector.py +++ b/qt_ui/widgets/combos/QSquadronLiverySelector.py @@ -33,6 +33,12 @@ def __init__(self, squadron: Squadron) -> None: 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): diff --git a/qt_ui/windows/mission/QAutoCreateDialog.py b/qt_ui/windows/mission/QAutoCreateDialog.py index 3792c6c4b..c1697d062 100644 --- a/qt_ui/windows/mission/QAutoCreateDialog.py +++ b/qt_ui/windows/mission/QAutoCreateDialog.py @@ -130,6 +130,20 @@ def __init__( 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 @@ def __init__( 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: diff --git a/qt_ui/windows/newgame/QNewGameWizard.py b/qt_ui/windows/newgame/QNewGameWizard.py index ce4e45994..8ad07fd50 100644 --- a/qt_ui/windows/newgame/QNewGameWizard.py +++ b/qt_ui/windows/newgame/QNewGameWizard.py @@ -107,6 +107,8 @@ def accept(self): 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"), diff --git a/qt_ui/windows/newgame/WizardPages/QGeneratorSettings.py b/qt_ui/windows/newgame/WizardPages/QGeneratorSettings.py index 43306117b..417a132d8 100644 --- a/qt_ui/windows/newgame/WizardPages/QGeneratorSettings.py +++ b/qt_ui/windows/newgame/WizardPages/QGeneratorSettings.py @@ -98,6 +98,10 @@ def __init__(self, campaign: Campaign, parent=None): 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 @@ def __init__(self, campaign: Campaign, parent=None): ("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), ] diff --git a/resources/campaigns/RetakeTheFalklands.miz b/resources/campaigns/RetakeTheFalklands.miz index aa5be875f..bc292b7e3 100644 Binary files a/resources/campaigns/RetakeTheFalklands.miz and b/resources/campaigns/RetakeTheFalklands.miz differ diff --git a/resources/campaigns/RetakeTheFalklands.yaml b/resources/campaigns/RetakeTheFalklands.yaml index 49d7c3e71..97307e40b 100644 --- a/resources/campaigns/RetakeTheFalklands.yaml +++ b/resources/campaigns/RetakeTheFalklands.yaml @@ -8,6 +8,8 @@ description:

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 diff --git a/resources/campaigns/scenic_merge.miz b/resources/campaigns/scenic_merge.miz index 708ce7328..d02ab4381 100644 Binary files a/resources/campaigns/scenic_merge.miz and b/resources/campaigns/scenic_merge.miz differ diff --git a/resources/campaigns/scenic_merge.yaml b/resources/campaigns/scenic_merge.yaml index c5bab649d..48806d1f6 100644 --- a/resources/campaigns/scenic_merge.yaml +++ b/resources/campaigns/scenic_merge.yaml @@ -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: diff --git a/resources/campaigns/syria_TheLongRoadToH3.miz b/resources/campaigns/syria_TheLongRoadToH3.miz index 160886a1c..ad263770d 100644 Binary files a/resources/campaigns/syria_TheLongRoadToH3.miz and b/resources/campaigns/syria_TheLongRoadToH3.miz differ diff --git a/resources/campaigns/syria_TheLongRoadToH3.yaml b/resources/campaigns/syria_TheLongRoadToH3.yaml index 2f28069d8..af10d9a32 100644 --- a/resources/campaigns/syria_TheLongRoadToH3.yaml +++ b/resources/campaigns/syria_TheLongRoadToH3.yaml @@ -3,7 +3,7 @@ name: Syria - The Long Road to H3 theater: Syria authors: tmz42 (original campaign by Plob) description: -

From Incirlik to H-3, repel the alliance between Iraq and Syria.

+

From Incirlik to H-3, repel the alliance between Syria and Iraq.

This campaign is based on and meant as a lighter alternative to Syria - Full Map.

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: diff --git a/resources/customized_payloads/AH-64D_BLK_II.lua b/resources/customized_payloads/AH-64D_BLK_II.lua index 62b6f0b80..8bf5595cb 100644 --- a/resources/customized_payloads/AH-64D_BLK_II.lua +++ b/resources/customized_payloads/AH-64D_BLK_II.lua @@ -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] = { diff --git a/resources/customized_payloads/OH-6A.lua b/resources/customized_payloads/OH-6A.lua new file mode 100644 index 000000000..556789783 --- /dev/null +++ b/resources/customized_payloads/OH-6A.lua @@ -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"] = "", + ["num"] = 6, + }, + [3] = { + ["CLSID"] = "", + ["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"] = "", + ["num"] = 6, + }, + [6] = { + ["CLSID"] = "", + ["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"] = "", + ["num"] = 6, + }, + [3] = { + ["CLSID"] = "", + ["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 diff --git a/resources/factions/USA 1970 Vietnam War.json b/resources/factions/USA 1970 Vietnam War.json index ad949c552..1b2a43bad 100644 --- a/resources/factions/USA 1970 Vietnam War.json +++ b/resources/factions/USA 1970 Vietnam War.json @@ -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)", diff --git a/resources/factions/USA 1971 Vietnam War.json b/resources/factions/USA 1971 Vietnam War.json index a6fb177c9..7a812755e 100644 --- a/resources/factions/USA 1971 Vietnam War.json +++ b/resources/factions/USA 1971 Vietnam War.json @@ -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)", diff --git a/resources/factions/bluefor_modern.json b/resources/factions/bluefor_modern.json index dafabdf03..c06bcd769 100644 --- a/resources/factions/bluefor_modern.json +++ b/resources/factions/bluefor_modern.json @@ -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"] } diff --git a/resources/factions/usa_1965.json b/resources/factions/usa_1965.json index 57cb6d7c0..1e17ecfb2 100644 --- a/resources/factions/usa_1965.json +++ b/resources/factions/usa_1965.json @@ -23,6 +23,7 @@ "F-105G Thunderchief", "F-106A Delta Dart", "F-106B Delta Dart", + "OH-6A Cayuse", "UH-1H Iroquois" ], "awacs": [ diff --git a/resources/factions/usa_1970.json b/resources/factions/usa_1970.json index b5af28948..c8fcc7dbe 100644 --- a/resources/factions/usa_1970.json +++ b/resources/factions/usa_1970.json @@ -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", diff --git a/resources/factions/usa_1975.json b/resources/factions/usa_1975.json index d60fb74a4..2e1b4c822 100644 --- a/resources/factions/usa_1975.json +++ b/resources/factions/usa_1975.json @@ -27,6 +27,7 @@ "F-106B Delta Dart", "S-3B Viking", "OV-10A Bronco", + "OH-6A Cayuse", "UH-1H Iroquois" ], "awacs": [ diff --git a/resources/factions/vietcong_1970.json b/resources/factions/vietcong_1970.json new file mode 100644 index 000000000..d8c23d110 --- /dev/null +++ b/resources/factions/vietcong_1970.json @@ -0,0 +1,54 @@ +{ + "country": "Vietnam", + "name": "Viet Cong 1970", + "authors": "Ghosti", + "description": "

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.

", + "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" +} \ No newline at end of file diff --git a/resources/squadrons/hornet/VMFA-122.yaml b/resources/squadrons/hornet/VMFA-122.yaml index 79fdb66c3..ad92e4c52 100644 --- a/resources/squadrons/hornet/VMFA-122.yaml +++ b/resources/squadrons/hornet/VMFA-122.yaml @@ -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 diff --git a/resources/squadrons/hornet/VMFA-251.yaml b/resources/squadrons/hornet/VMFA-251.yaml index 82344eb4e..28d11a1e5 100644 --- a/resources/squadrons/hornet/VMFA-251.yaml +++ b/resources/squadrons/hornet/VMFA-251.yaml @@ -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 diff --git a/resources/ui/units/aircrafts/banners/OH-6A.jpg b/resources/ui/units/aircrafts/banners/OH-6A.jpg new file mode 100644 index 000000000..b64d26480 Binary files /dev/null and b/resources/ui/units/aircrafts/banners/OH-6A.jpg differ diff --git a/resources/ui/units/aircrafts/icons/OH-6A_24.jpg b/resources/ui/units/aircrafts/icons/OH-6A_24.jpg new file mode 100644 index 000000000..fd4db2cb8 Binary files /dev/null and b/resources/ui/units/aircrafts/icons/OH-6A_24.jpg differ diff --git a/resources/ui/units/vehicles/icons/PT_76_24.jpg b/resources/ui/units/vehicles/icons/PT_76_24.jpg new file mode 100644 index 000000000..fd092155d Binary files /dev/null and b/resources/ui/units/vehicles/icons/PT_76_24.jpg differ diff --git a/resources/ui/units/vehicles/icons/vap_m35_truck_24.jpg b/resources/ui/units/vehicles/icons/vap_m35_truck_24.jpg new file mode 100644 index 000000000..799e44647 Binary files /dev/null and b/resources/ui/units/vehicles/icons/vap_m35_truck_24.jpg differ diff --git a/resources/ui/units/vehicles/icons/vap_mule_24.jpg b/resources/ui/units/vehicles/icons/vap_mule_24.jpg new file mode 100644 index 000000000..340e3bb26 Binary files /dev/null and b/resources/ui/units/vehicles/icons/vap_mule_24.jpg differ diff --git a/resources/ui/units/vehicles/icons/vap_mutt_24.jpg b/resources/ui/units/vehicles/icons/vap_mutt_24.jpg new file mode 100644 index 000000000..a80c05a64 Binary files /dev/null and b/resources/ui/units/vehicles/icons/vap_mutt_24.jpg differ diff --git a/resources/ui/units/vehicles/icons/vap_mutt_gun_24.jpg b/resources/ui/units/vehicles/icons/vap_mutt_gun_24.jpg new file mode 100644 index 000000000..51bc3ae66 Binary files /dev/null and b/resources/ui/units/vehicles/icons/vap_mutt_gun_24.jpg differ diff --git a/resources/ui/units/vehicles/icons/vap_type63_mlrs_24.jpg b/resources/ui/units/vehicles/icons/vap_type63_mlrs_24.jpg new file mode 100644 index 000000000..a3f1291ee Binary files /dev/null and b/resources/ui/units/vehicles/icons/vap_type63_mlrs_24.jpg differ diff --git a/resources/ui/units/vehicles/icons/vap_vc_bicycle_24.jpg b/resources/ui/units/vehicles/icons/vap_vc_bicycle_24.jpg new file mode 100644 index 000000000..46854ccf5 Binary files /dev/null and b/resources/ui/units/vehicles/icons/vap_vc_bicycle_24.jpg differ diff --git a/resources/ui/units/vehicles/icons/vap_vc_bicycle_ak_24.jpg b/resources/ui/units/vehicles/icons/vap_vc_bicycle_ak_24.jpg new file mode 100644 index 000000000..3d5bad2bb Binary files /dev/null and b/resources/ui/units/vehicles/icons/vap_vc_bicycle_ak_24.jpg differ diff --git a/resources/ui/units/vehicles/icons/vap_vc_bicycle_mortar_24.jpg b/resources/ui/units/vehicles/icons/vap_vc_bicycle_mortar_24.jpg new file mode 100644 index 000000000..a8738fd6d Binary files /dev/null and b/resources/ui/units/vehicles/icons/vap_vc_bicycle_mortar_24.jpg differ diff --git a/resources/ui/units/vehicles/icons/vap_vc_bunker_single_24.jpg b/resources/ui/units/vehicles/icons/vap_vc_bunker_single_24.jpg new file mode 100644 index 000000000..74b3444c0 Binary files /dev/null and b/resources/ui/units/vehicles/icons/vap_vc_bunker_single_24.jpg differ diff --git a/resources/ui/units/vehicles/icons/vap_vc_mg_nest_24.jpg b/resources/ui/units/vehicles/icons/vap_vc_mg_nest_24.jpg new file mode 100644 index 000000000..cefb69ffe Binary files /dev/null and b/resources/ui/units/vehicles/icons/vap_vc_mg_nest_24.jpg differ diff --git a/resources/ui/units/vehicles/icons/vap_vc_zil_24.jpg b/resources/ui/units/vehicles/icons/vap_vc_zil_24.jpg new file mode 100644 index 000000000..f3b2ef970 Binary files /dev/null and b/resources/ui/units/vehicles/icons/vap_vc_zil_24.jpg differ diff --git a/resources/ui/units/vehicles/icons/vap_vc_zis_24.jpg b/resources/ui/units/vehicles/icons/vap_vc_zis_24.jpg new file mode 100644 index 000000000..38409d5ab Binary files /dev/null and b/resources/ui/units/vehicles/icons/vap_vc_zis_24.jpg differ diff --git a/resources/ui/units/vehicles/icons/vap_zis_150_aa_24.jpg b/resources/ui/units/vehicles/icons/vap_zis_150_aa_24.jpg new file mode 100644 index 000000000..38409d5ab Binary files /dev/null and b/resources/ui/units/vehicles/icons/vap_zis_150_aa_24.jpg differ diff --git a/resources/units/aircraft/A-10A.yaml b/resources/units/aircraft/A-10A.yaml index 898de9b16..84fdbde54 100644 --- a/resources/units/aircraft/A-10A.yaml +++ b/resources/units/aircraft/A-10A.yaml @@ -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 diff --git a/resources/units/aircraft/A-10C.yaml b/resources/units/aircraft/A-10C.yaml index dfb5082d9..d753dacb6 100644 --- a/resources/units/aircraft/A-10C.yaml +++ b/resources/units/aircraft/A-10C.yaml @@ -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 diff --git a/resources/units/aircraft/A-10C_2.yaml b/resources/units/aircraft/A-10C_2.yaml index 0f3d785c2..bc99188d1 100644 --- a/resources/units/aircraft/A-10C_2.yaml +++ b/resources/units/aircraft/A-10C_2.yaml @@ -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. diff --git a/resources/units/aircraft/OH-6A.yaml b/resources/units/aircraft/OH-6A.yaml new file mode 100644 index 000000000..3c8e16c96 --- /dev/null +++ b/resources/units/aircraft/OH-6A.yaml @@ -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 diff --git a/resources/units/ground_units/M1A2C_SEP_V3.yaml b/resources/units/ground_units/M1A2C_SEP_V3.yaml index 19597a372..bbd923796 100644 --- a/resources/units/ground_units/M1A2C_SEP_V3.yaml +++ b/resources/units/ground_units/M1A2C_SEP_V3.yaml @@ -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 diff --git a/resources/units/ground_units/MaxxPro_MRAP.yaml b/resources/units/ground_units/MaxxPro_MRAP.yaml index 14af8adb2..14357381f 100644 --- a/resources/units/ground_units/MaxxPro_MRAP.yaml +++ b/resources/units/ground_units/MaxxPro_MRAP.yaml @@ -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 diff --git a/resources/units/ground_units/SHERIDAN.yaml b/resources/units/ground_units/SHERIDAN.yaml new file mode 100644 index 000000000..919289531 --- /dev/null +++ b/resources/units/ground_units/SHERIDAN.yaml @@ -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": {} diff --git a/resources/units/ground_units/T62.yaml b/resources/units/ground_units/T62.yaml new file mode 100644 index 000000000..adf26fa56 --- /dev/null +++ b/resources/units/ground_units/T62.yaml @@ -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: {} diff --git a/resources/units/ground_units/T64BV.yaml b/resources/units/ground_units/T64BV.yaml new file mode 100644 index 000000000..8c148eea3 --- /dev/null +++ b/resources/units/ground_units/T64BV.yaml @@ -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: {} diff --git a/resources/units/ground_units/vap_m35_truck.yaml b/resources/units/ground_units/vap_m35_truck.yaml new file mode 100644 index 000000000..3f747493b --- /dev/null +++ b/resources/units/ground_units/vap_m35_truck.yaml @@ -0,0 +1,4 @@ +class: Logistics +price: 3 +variants: + VAP US M35 Truck: null diff --git a/resources/units/ground_units/vap_mule.yaml b/resources/units/ground_units/vap_mule.yaml new file mode 100644 index 000000000..5db17c489 --- /dev/null +++ b/resources/units/ground_units/vap_mule.yaml @@ -0,0 +1,5 @@ +class: Logistics +price: 3 +variants: + VAP US Mule: {} + M274 1/2-ton 4x4 utility truck: {} diff --git a/resources/units/ground_units/vap_mutt.yaml b/resources/units/ground_units/vap_mutt.yaml new file mode 100644 index 000000000..95feade93 --- /dev/null +++ b/resources/units/ground_units/vap_mutt.yaml @@ -0,0 +1,5 @@ +class: Logistics +price: 3 +variants: + VAP US MUTT: {} + M151 1/4-ton 4x4 utility truck: {} diff --git a/resources/units/ground_units/vap_mutt_gun.yaml b/resources/units/ground_units/vap_mutt_gun.yaml new file mode 100644 index 000000000..65be3a939 --- /dev/null +++ b/resources/units/ground_units/vap_mutt_gun.yaml @@ -0,0 +1,6 @@ +class: ATGM +price: 10 +role: Self-Propelled Recoilless Rifle +variants: + VAP US MUTT Gun: {} + M151A1C MUTT w/ 106mm Recoilless Rifle: {} diff --git a/resources/units/ground_units/vap_type63_mlrs.yaml b/resources/units/ground_units/vap_type63_mlrs.yaml new file mode 100644 index 000000000..31103149c --- /dev/null +++ b/resources/units/ground_units/vap_type63_mlrs.yaml @@ -0,0 +1,5 @@ +class: Artillery +price: 10 +role: Multiple-Launch Rocket System +variants: + VAP VC Type63 107mm MLRS: {} diff --git a/resources/units/ground_units/vap_vc_bicycle.yaml b/resources/units/ground_units/vap_vc_bicycle.yaml new file mode 100644 index 000000000..c4e61801b --- /dev/null +++ b/resources/units/ground_units/vap_vc_bicycle.yaml @@ -0,0 +1,4 @@ +class: Logistics +price: 2 +variants: + VAP VC Bicycle: null diff --git a/resources/units/ground_units/vap_vc_bicycle_ak.yaml b/resources/units/ground_units/vap_vc_bicycle_ak.yaml new file mode 100644 index 000000000..a8d6d319d --- /dev/null +++ b/resources/units/ground_units/vap_vc_bicycle_ak.yaml @@ -0,0 +1,5 @@ +class: Recon +price: 2 +role: Recon +variants: + VAP VC Bicycle AK: {} diff --git a/resources/units/ground_units/vap_vc_bicycle_mortar.yaml b/resources/units/ground_units/vap_vc_bicycle_mortar.yaml new file mode 100644 index 000000000..76a9660cc --- /dev/null +++ b/resources/units/ground_units/vap_vc_bicycle_mortar.yaml @@ -0,0 +1,5 @@ +class: Infantry +price: 0 +spawn_weight: 1 +variants: + VAP VC Bicycle Mortar: null diff --git a/resources/units/ground_units/vap_vc_bunker_single.yaml b/resources/units/ground_units/vap_vc_bunker_single.yaml new file mode 100644 index 000000000..8f91b683a --- /dev/null +++ b/resources/units/ground_units/vap_vc_bunker_single.yaml @@ -0,0 +1,4 @@ +class: IFV +price: 4 +variants: + VAP VC Bunker: null diff --git a/resources/units/ground_units/vap_vc_mg_nest.yaml b/resources/units/ground_units/vap_vc_mg_nest.yaml new file mode 100644 index 000000000..5e8265964 --- /dev/null +++ b/resources/units/ground_units/vap_vc_mg_nest.yaml @@ -0,0 +1,4 @@ +class: APC +price: 3 +variants: + VAP VC MG Nest: null diff --git a/resources/units/ground_units/vap_vc_zil.yaml b/resources/units/ground_units/vap_vc_zil.yaml new file mode 100644 index 000000000..1cde6af3f --- /dev/null +++ b/resources/units/ground_units/vap_vc_zil.yaml @@ -0,0 +1,6 @@ +class: ATGM +description: +price: 7 +role: Armed Truck +variants: + VAP VC Zil 130 Armed: {} diff --git a/resources/units/ground_units/vap_vc_zis.yaml b/resources/units/ground_units/vap_vc_zis.yaml new file mode 100644 index 000000000..3550e0e39 --- /dev/null +++ b/resources/units/ground_units/vap_vc_zis.yaml @@ -0,0 +1,4 @@ +class: Logistics +price: 3 +variants: + VAP VC Zis 150: null diff --git a/resources/units/ground_units/vap_zis_150_aa.yaml b/resources/units/ground_units/vap_zis_150_aa.yaml new file mode 100644 index 000000000..9c119068a --- /dev/null +++ b/resources/units/ground_units/vap_zis_150_aa.yaml @@ -0,0 +1,6 @@ +class: AAA +description: +price: 7 +role: Self-Propelled Anti-Aircraft Gun +variants: + VAP VC Zis 150 AAA: {} diff --git a/resources/units/ships/vap_us_seafloat.yaml b/resources/units/ships/vap_us_seafloat.yaml new file mode 100644 index 000000000..6cffc0b5f --- /dev/null +++ b/resources/units/ships/vap_us_seafloat.yaml @@ -0,0 +1,4 @@ +class: Boat +price: 0 +variants: + VAP - US Sea Float Barge: null \ No newline at end of file