1 Commits

Author SHA1 Message Date
Raffson
87b630f8d5 Initial attempt 2024-02-26 00:00:27 +01:00
100 changed files with 447 additions and 3058 deletions

View File

@@ -2,7 +2,7 @@
(Github Readme Banner and Splash screen Artwork by Andriy Dankovych, CC BY-SA 4.0)
[![Patreon](https://img.shields.io/badge/patreon-become%20a%20patron-orange?logo=patreon)](https://patreon.com/dcsliberation)
[![Patreon](https://img.shields.io/badge/patreon-become%20a%20patron-orange?logo=patreon)](https://patreon.com/khopa)
[![Download](https://img.shields.io/github/downloads/dcs-retribution/dcs-retribution/total?label=Download)](https://github.com/dcs-retribution/dcs-retribution/releases)

View File

@@ -1,11 +1,3 @@
# Retribution v1.3.1
#### Note: Re-save your missions in DCS' Mission Editor to avoid possible crashes due to datalink (usually the case when F-16C blk50s are used) when hosting missions on a dedicated server.
## Fixes
* **[UX]** Fix save-compatibility issue
* **[UX]** Avoid crash on startup due to incompatible save
# Retribution v1.3.0
#### Note: Re-save your missions in DCS' Mission Editor to avoid possible crashes due to datalink (usually the case when F-16C blk50s are used) when hosting missions on a dedicated server.
@@ -23,8 +15,6 @@
* **[Modding]** Updated support for F-4B/C Phantom mod to 2.8.7.204
* **[Modding]** Updated Community A-4E-C mod version support to 2.2.0 release.
* **[Modding]** Added F/A-18E/F Super Hornet AI Tanker mod support (Chiller Juice Studios SuperBug Tanker AI version 1.4)
* **[Modding]** Added VSN Super Étendard mod support (v2.5.5)
* **[Modding]** Added F9F Panther mod support (version v2.8.7.101)
* **[Modding]** Updated Irondome support to IDF Assets Pack V1.1, adding support for the David's Sling
* **[Radios]** Added HF-FM band for AN/ARC-222
* **[Radios]** Ability to define preset channels for radios on squadron level (for human pilots only)
@@ -51,12 +41,7 @@
* **[Config]** Preference setting to use custom Liberation payloads instead of prioritizing Retribution's default
* **[Config]** Preference setting to configure the server-port on which Retribution's back-end will run
* **[Options]** Made AI jettisoning empty fuel tanks optional (disabled by default)
* **[Options]** Add option (so it can be disabled when fixed in DCS) to force air-starts (except for the slots that work) at Nevatim due to https://forum.dcs.world/topic/335545-29-nevatim-ramp-starts-still-bugged/
* **[Cheat]** Add cheat option to manually manage REDFOR's TGOs
* **[UX]** Buy/Replace TGOs for free before the campaign has started
* **[Data]** Ability to define "cruise" & "combat" altitudes for airplanes
* **[Options]** Option to randomize altitudes for flights with airplanes
* **[Options]** Options to configure/override maximum mission distance for airplanes & helicopters
* **[Options]** Add option (so it can be disabled when fixed in DCS) to force air-starts (except for the slots that work) at Nevatim due to https://forum.dcs.world/topic/335545-29-nevatim-ramp-starts-still-bugged/.
## Fixes
* **[Mission Generation]** Anti-ship strikes should use "group attack" in their attack-task
@@ -282,7 +267,6 @@ BAI/ANTISHIP/DEAD/STRIKE/BARCAP/CAS/OCA/AIR-ASSAULT (main) missions
* **[UI]** Fixed flight properties UI to support F-15E S4+ laser codes.
* **[UI]** Fixed UI bug where altering an "ahead of package" TOT offset would change the offset back to a "behind pacakge" offset.
* **[UI]** Fixed bug where changing TOT offsets could result in flight startup times that are in the past.
* **[UI]** Flight plan paths are now drawn behind all other map elements, fixing rare cases where they could prevent other UI elements from being clickable.
# 8.1.0

View File

@@ -6885,9 +6885,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001596",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001596.tgz",
"integrity": "sha512-zpkZ+kEr6We7w63ORkoJ2pOfBwBkY/bJrG/UZ90qNb45Isblu8wzDgevEOrRL1r9dWayHjYiiyCMEXPn4DweGQ==",
"version": "1.0.30001450",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz",
"integrity": "sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==",
"dev": true,
"funding": [
{
@@ -6897,10 +6897,6 @@
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
]
},
@@ -26058,9 +26054,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30001596",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001596.tgz",
"integrity": "sha512-zpkZ+kEr6We7w63ORkoJ2pOfBwBkY/bJrG/UZ90qNb45Isblu8wzDgevEOrRL1r9dWayHjYiiyCMEXPn4DweGQ==",
"version": "1.0.30001450",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz",
"integrity": "sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==",
"dev": true
},
"case-sensitive-paths-webpack-plugin": {

View File

@@ -1,8 +1,7 @@
import { Flight } from "../../api/liberationApi";
import { useGetCommitBoundaryForFlightQuery } from "../../api/liberationApi";
import WaypointMarker from "../waypointmarker";
import { Polyline as LPolyline } from "leaflet";
import { ReactElement, useEffect, useRef } from "react";
import { ReactElement } from "react";
import { Polyline } from "react-leaflet";
const BLUE_PATH = "#0084ff";
@@ -28,44 +27,16 @@ const pathColor = (props: FlightPlanProps) => {
function FlightPlanPath(props: FlightPlanProps) {
const color = pathColor(props);
const waypoints = props.flight.waypoints;
const polylineRef = useRef<LPolyline | null>(null);
// Flight paths should be drawn under everything else. There seems to be an
// issue where `interactive: false` doesn't do as its told (there's nuance,
// see the bug for details). It looks better if we draw the other elements on
// top of the flight plans anyway, so just push the flight plan to the back.
//
// https://github.com/dcs-liberation/dcs_liberation/issues/3295
//
// It's not possible to z-index a polyline (and leaflet says it never will be,
// because this is a limitation of SVG, not leaflet:
// https://github.com/Leaflet/Leaflet/issues/185), so we need to use
// bringToBack() to push the flight paths to the back of the drawing once
// they've been added to the map. They'll still draw on top of the map, but
// behind everything than was added before them. Anything added after always
// goes on top.
useEffect(() => {
if (props.selected) {
polylineRef.current?.bringToFront();
}
else {
polylineRef.current?.bringToBack();
}
});
if (waypoints == null) {
return <></>;
}
const points = waypoints
.filter((waypoint) => waypoint.include_in_path)
.map((waypoint) => waypoint.position);
return (
<Polyline
positions={points}
pathOptions={{ color: color, interactive: false }}
ref={polylineRef}
/>
);
}

View File

@@ -95,12 +95,8 @@ describe("FlightPlansLayer", () => {
},
},
});
// For some reason passing ref to PolyLine causes it and its group to be
// redrawn, so these numbers don't match what you'd expect from the test.
// It probably needs to be rewritten without mocks.
expect(mockPolyline).toHaveBeenCalledTimes(3);
expect(mockLayerGroup).toBeCalledTimes(2);
expect(mockPolyline).toHaveBeenCalledTimes(2);
expect(mockLayerGroup).toBeCalledTimes(1);
});
it("are not drawn if wrong coalition", () => {
renderWithProviders(<FlightPlansLayer blue={true} />, {

View File

@@ -6,10 +6,10 @@
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = "DCS Retribution"
copyright = "2023, DCS Retribution Team"
author = "DCS Retribution Team"
release = "1.3.1"
project = "DCS Liberation"
copyright = "2023, DCS Liberation Team"
author = "DCS Liberation Team"
release = "7.0.0"
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

View File

@@ -1,6 +1,5 @@
from __future__ import annotations
import random
import uuid
from collections.abc import Iterator
from datetime import datetime, timedelta
@@ -10,7 +9,6 @@ from dcs import Point
from dcs.planes import C_101CC, C_101EB, Su_33, FA_18C_hornet
from game.dcs.aircrafttype import AircraftType
from game.theater import ControlPoint, MissionTarget
from pydcs_extensions.hercules.hercules import Hercules
from .flightmembers import FlightMembers
from .flightroster import FlightRoster
@@ -35,6 +33,7 @@ if TYPE_CHECKING:
from game.sim.gameupdateevents import GameUpdateEvents
from game.sim.simulationresults import SimulationResults
from game.squadrons import Squadron, Pilot
from game.theater import ControlPoint
from game.transfers import TransferOrder
from game.data.weapons import WeaponType
from .flightmember import FlightMember
@@ -124,11 +123,6 @@ class Flight(
)
)
# altitude offset for planes
offset_factor = self.coalition.game.settings.max_plane_altitude_offset
offset_factor = random.randint(0, offset_factor)
self.plane_altitude_offset = 1000 * offset_factor * random.choice([-1, 1])
@property
def available_callsigns(self) -> List[str]:
callsigns = set()
@@ -224,13 +218,6 @@ class Flight(
def points(self) -> List[FlightWaypoint]:
return self.flight_plan.waypoints[1:]
@property
def custom_targets(self) -> List[MissionTarget]:
return [
MissionTarget(wpt.name, wpt.position)
for wpt in self.flight_plan.layout.custom_waypoints
]
def position(self) -> Point:
return self.state.estimate_position()

View File

@@ -6,7 +6,7 @@ from typing import Type
from game.ato.flightplans.ibuilder import IBuilder
from game.ato.flightplans.patrolling import PatrollingFlightPlan, PatrollingLayout
from game.ato.flightplans.waypointbuilder import WaypointBuilder
from game.utils import Distance, Heading, Speed, knots, meters, nautical_miles
from game.utils import Distance, Heading, Speed, feet, knots, meters, nautical_miles
class AewcFlightPlan(PatrollingFlightPlan[PatrollingLayout]):
@@ -70,7 +70,10 @@ class Builder(IBuilder[AewcFlightPlan, PatrollingLayout]):
builder = WaypointBuilder(self.flight)
altitude = builder.get_patrol_altitude
if self.flight.unit_type.patrol_altitude is not None:
altitude = self.flight.unit_type.patrol_altitude
else:
altitude = feet(25000)
racetrack = builder.race_track(racetrack_start, racetrack_end, altitude)
@@ -87,7 +90,6 @@ class Builder(IBuilder[AewcFlightPlan, PatrollingLayout]):
arrival=builder.land(self.flight.arrival),
divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(),
custom_waypoints=list(),
)
def build(self, dump_debug_info: bool = False) -> AewcFlightPlan:

View File

@@ -49,7 +49,6 @@ class AirAssaultLayout(FormationAttackLayout):
if self.divert is not None:
yield self.divert
yield self.bullseye
yield from self.custom_waypoints
class AirAssaultFlightPlan(FormationAttackFlightPlan, UiZoneDisplay):
@@ -112,11 +111,12 @@ class Builder(FormationAttackBuilder[AirAssaultFlightPlan, AirAssaultLayout]):
)
assert self.package.waypoints is not None
builder = WaypointBuilder(self.flight)
altitude = builder.get_cruise_altitude
heli_alt = feet(self.coalition.game.settings.heli_cruise_alt_agl)
altitude = heli_alt if self.flight.is_helo else self.doctrine.ingress_altitude
altitude_is_agl = self.flight.is_helo
builder = WaypointBuilder(self.flight)
if self.flight.is_hercules or self.flight.departure.cptype in [
ControlPointType.AIRCRAFT_CARRIER_GROUP,
ControlPointType.LHA_GROUP,
@@ -133,7 +133,7 @@ class Builder(FormationAttackBuilder[AirAssaultFlightPlan, AirAssaultLayout]):
self._generate_ctld_pickup(),
)
)
pickup.alt = altitude
pickup.alt = heli_alt
pickup_position = pickup.position
ingress = builder.ingress(
@@ -159,6 +159,8 @@ class Builder(FormationAttackBuilder[AirAssaultFlightPlan, AirAssaultLayout]):
drop_pos = tgt.position.point_from_heading(heading, 1200)
drop_off_zone = MissionTarget("Dropoff zone", drop_pos)
dz = builder.dropoff_zone(drop_off_zone) if self.flight.is_helo else None
if dz:
dz.alt = heli_alt
return AirAssaultLayout(
departure=builder.takeoff(self.flight.departure),
@@ -185,7 +187,6 @@ class Builder(FormationAttackBuilder[AirAssaultFlightPlan, AirAssaultLayout]):
join=builder.join(ingress.position),
split=builder.split(self.flight.arrival.position),
refuel=None,
custom_waypoints=list(),
)
def build(self, dump_debug_info: bool = False) -> AirAssaultFlightPlan:

View File

@@ -8,7 +8,7 @@ from typing import Optional
from typing import TYPE_CHECKING, Type
from game.theater.missiontarget import MissionTarget
from game.utils import Distance
from game.utils import feet, Distance
from ._common_ctld import generate_random_ctld_point
from .ibuilder import IBuilder
from .planningerror import PlanningError
@@ -92,7 +92,6 @@ class AirliftLayout(StandardLayout):
if self.divert is not None:
yield self.divert
yield self.bullseye
yield from self.custom_waypoints
class AirliftFlightPlan(StandardFlightPlan[AirliftLayout]):
@@ -133,11 +132,12 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]):
"Cannot plan transport mission for flight with no cargo."
)
builder = WaypointBuilder(self.flight)
altitude = builder.get_cruise_altitude
heli_alt = feet(self.coalition.game.settings.heli_cruise_alt_agl)
altitude = heli_alt if self.flight.is_helo else self.doctrine.ingress_altitude
altitude_is_agl = self.flight.is_helo
builder = WaypointBuilder(self.flight)
pickup_ascent = None
pickup_descent = None
pickup = None
@@ -246,7 +246,6 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]):
arrival=builder.land(self.flight.arrival),
divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(),
custom_waypoints=list(),
)
def build(self, dump_debug_info: bool = False) -> AirliftFlightPlan:

View File

@@ -1,10 +1,11 @@
from __future__ import annotations
import random
from datetime import timedelta
from typing import Type
from game.theater import FrontLine
from game.utils import Distance, Speed
from game.utils import Distance, Speed, feet
from .capbuilder import CapBuilder
from .invalidobjectivelocation import InvalidObjectiveLocation
from .patrolling import PatrollingFlightPlan, PatrollingLayout
@@ -40,9 +41,14 @@ class Builder(CapBuilder[BarCapFlightPlan, PatrollingLayout]):
start_pos, end_pos = self.cap_racetrack_for_objective(location, barcap=True)
builder = WaypointBuilder(self.flight)
patrol_alt = builder.get_patrol_altitude
preferred_alt = self.flight.unit_type.preferred_patrol_altitude
randomized_alt = preferred_alt + feet(random.randint(-2, 1) * 1000)
patrol_alt = max(
self.doctrine.min_patrol_altitude,
min(self.doctrine.max_patrol_altitude, randomized_alt),
)
builder = WaypointBuilder(self.flight)
start, end = builder.race_track(start_pos, end_pos, patrol_alt)
return PatrollingLayout(
@@ -58,7 +64,6 @@ class Builder(CapBuilder[BarCapFlightPlan, PatrollingLayout]):
arrival=builder.land(self.flight.arrival),
divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(),
custom_waypoints=list(),
)
def build(self, dump_debug_info: bool = False) -> BarCapFlightPlan:

View File

@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Type
from game.theater import FrontLine
from game.utils import Distance, Speed, kph, dcs_to_shapely_point
from game.utils import nautical_miles
from game.utils import feet, nautical_miles
from .ibuilder import IBuilder
from .invalidobjectivelocation import InvalidObjectiveLocation
from .patrolling import PatrollingFlightPlan, PatrollingLayout
@@ -37,7 +37,6 @@ class CasLayout(PatrollingLayout):
if self.divert is not None:
yield self.divert
yield self.bullseye
yield from self.custom_waypoints
class CasFlightPlan(PatrollingFlightPlan[CasLayout], UiZoneDisplay):
@@ -105,7 +104,11 @@ class Builder(IBuilder[CasFlightPlan, CasLayout]):
builder = WaypointBuilder(self.flight)
is_helo = self.flight.unit_type.dcs_unit_type.helicopter
ingress_egress_altitude = builder.get_combat_altitude
ingress_egress_altitude = (
self.doctrine.ingress_altitude
if not is_helo
else feet(self.coalition.game.settings.heli_combat_alt_agl)
)
use_agl_patrol_altitude = is_helo
ip_solver = IpSolver(
@@ -164,7 +167,6 @@ class Builder(IBuilder[CasFlightPlan, CasLayout]):
arrival=builder.land(self.flight.arrival),
divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(),
custom_waypoints=list(),
)
def build(self, dump_debug_info: bool = False) -> CasFlightPlan:

View File

@@ -17,6 +17,8 @@ if TYPE_CHECKING:
@dataclass
class CustomLayout(Layout):
custom_waypoints: list[FlightWaypoint]
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
yield self.departure
yield from self.custom_waypoints

View File

@@ -11,6 +11,7 @@ from .formationattack import (
)
from .waypointbuilder import WaypointBuilder
from .. import FlightType
from ...utils import feet
class EscortFlightPlan(FormationAttackFlightPlan):
@@ -42,9 +43,12 @@ class Builder(FormationAttackBuilder[EscortFlightPlan, FormationAttackLayout]):
split = builder.split(self._get_split())
ingress_alt = self.doctrine.ingress_altitude
is_helo = builder.flight.is_helo
heli_alt = feet(self.coalition.game.settings.heli_combat_alt_agl)
initial = builder.escort_hold(
target.position if is_helo else self.package.waypoints.initial,
min(heli_alt, ingress_alt) if is_helo else ingress_alt,
)
pf = self.package.primary_flight
@@ -65,6 +69,9 @@ class Builder(FormationAttackBuilder[EscortFlightPlan, FormationAttackLayout]):
if layout.drop_off:
initial = builder.escort_hold(
layout.drop_off.position,
min(feet(200), ingress_alt)
if builder.flight.is_helo
else ingress_alt,
)
refuel = self._build_refuel(builder)
@@ -73,13 +80,13 @@ class Builder(FormationAttackBuilder[EscortFlightPlan, FormationAttackLayout]):
nav_to = builder.nav_path(
hold.position if hold else departure.position,
join.position,
builder.get_cruise_altitude,
self.doctrine.ingress_altitude,
)
nav_from = builder.nav_path(
refuel.position if refuel else split.position,
self.flight.arrival.position,
builder.get_cruise_altitude,
self.doctrine.ingress_altitude,
)
return FormationAttackLayout(
@@ -96,7 +103,6 @@ class Builder(FormationAttackBuilder[EscortFlightPlan, FormationAttackLayout]):
arrival=builder.land(self.flight.arrival),
divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(),
custom_waypoints=list(),
)
def build(self, dump_debug_info: bool = False) -> EscortFlightPlan:

View File

@@ -24,7 +24,6 @@ class FerryLayout(StandardLayout):
if self.divert is not None:
yield self.divert
yield self.bullseye
yield from self.custom_waypoints
class FerryFlightPlan(StandardFlightPlan[FerryLayout]):
@@ -61,14 +60,14 @@ class Builder(IBuilder[FerryFlightPlan, FerryLayout]):
f"{self.flight.departure}"
)
builder = WaypointBuilder(self.flight)
altitude_is_agl = self.flight.is_helo
altitude = (
feet(self.coalition.game.settings.heli_cruise_alt_agl)
if altitude_is_agl
else builder.get_patrol_altitude
else self.flight.unit_type.preferred_patrol_altitude
)
builder = WaypointBuilder(self.flight)
return FerryLayout(
departure=builder.takeoff(self.flight.departure),
nav_to=builder.nav_path(
@@ -81,7 +80,6 @@ class Builder(IBuilder[FerryFlightPlan, FerryLayout]):
divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(),
nav_from=[],
custom_waypoints=list(),
)
def build(self, dump_debug_info: bool = False) -> FerryFlightPlan:

View File

@@ -34,7 +34,6 @@ if TYPE_CHECKING:
@dataclass
class Layout(ABC):
departure: FlightWaypoint
custom_waypoints: list[FlightWaypoint]
@property
def waypoints(self) -> list[FlightWaypoint]:

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 Speed, meters, nautical_miles, feet
from .flightplan import FlightPlan
from .formation import FormationFlightPlan, FormationLayout
from .ibuilder import IBuilder
@@ -157,7 +157,6 @@ class FormationAttackLayout(FormationLayout):
if self.divert is not None:
yield self.divert
yield self.bullseye
yield from self.custom_waypoints
FlightPlanT = TypeVar("FlightPlanT", bound=FlightPlan[FormationAttackLayout])
@@ -210,10 +209,14 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
if self.flight.flight_type == FlightType.STRIKE:
hdg = self.package.target.position.heading_between_point(ingress.position)
pos = ingress.position.point_from_heading(hdg, nautical_miles(10).meters)
lineup = builder.nav(pos, builder.get_combat_altitude)
lineup = builder.nav(pos, self.flight.coalition.doctrine.ingress_altitude)
is_helo = self.flight.is_helo
ingress_egress_altitude = builder.get_combat_altitude
ingress_egress_altitude = (
self.doctrine.ingress_altitude
if not is_helo
else feet(self.coalition.game.settings.heli_combat_alt_agl)
)
use_agl_ingress_egress = is_helo
return FormationAttackLayout(
@@ -241,7 +244,6 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
arrival=builder.land(self.flight.arrival),
divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(),
custom_waypoints=list(),
)
def _build_refuel(self, builder: WaypointBuilder) -> Optional[FlightWaypoint]:

View File

@@ -25,7 +25,6 @@ class IBuilder(ABC, Generic[FlightPlanT, LayoutT]):
def __init__(self, flight: Flight) -> None:
self.flight = flight
self._flight_plan: FlightPlanT | None = None
self.settings = self.flight.coalition.game.settings
def get_or_build(self) -> FlightPlanT:
if self._flight_plan is None:

View File

@@ -5,7 +5,7 @@ from typing import Type
from dcs import Point
from game.utils import Distance, Heading, meters
from game.utils import Distance, Heading, feet, meters
from .ibuilder import IBuilder
from .patrolling import PatrollingLayout
from .refuelingflightplan import RefuelingFlightPlan
@@ -98,7 +98,11 @@ class Builder(IBuilder[PackageRefuelingFlightPlan, PatrollingLayout]):
builder = WaypointBuilder(self.flight)
altitude = builder.get_patrol_altitude
tanker_type = self.flight.unit_type
if tanker_type.patrol_altitude is not None:
altitude = tanker_type.patrol_altitude
else:
altitude = feet(21000)
racetrack = builder.race_track(racetrack_start, racetrack_end, altitude)
@@ -115,7 +119,6 @@ class Builder(IBuilder[PackageRefuelingFlightPlan, PatrollingLayout]):
arrival=builder.land(self.flight.arrival),
divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(),
custom_waypoints=list(),
)
def build(self, dump_debug_info: bool = False) -> PackageRefuelingFlightPlan:

View File

@@ -31,7 +31,6 @@ class PatrollingLayout(StandardLayout):
if self.divert is not None:
yield self.divert
yield self.bullseye
yield from self.custom_waypoints
LayoutT = TypeVar("LayoutT", bound=PatrollingLayout)

View File

@@ -27,7 +27,6 @@ class RtbLayout(StandardLayout):
if self.divert is not None:
yield self.divert
yield self.bullseye
yield from self.custom_waypoints
class RtbFlightPlan(StandardFlightPlan[RtbLayout]):
@@ -66,13 +65,13 @@ class Builder(IBuilder[RtbFlightPlan, RtbLayout]):
current_position = self.flight.state.estimate_position()
current_altitude, altitude_reference = self.flight.state.estimate_altitude()
builder = WaypointBuilder(self.flight)
altitude_is_agl = self.flight.is_helo
altitude = (
feet(self.coalition.game.settings.heli_cruise_alt_agl)
if altitude_is_agl
else builder.get_patrol_altitude
else self.flight.unit_type.preferred_patrol_altitude
)
builder = WaypointBuilder(self.flight)
abort_point = builder.nav(
current_position, current_altitude, altitude_reference == "RADIO"
)
@@ -92,7 +91,6 @@ class Builder(IBuilder[RtbFlightPlan, RtbLayout]):
divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(),
nav_from=[],
custom_waypoints=list(),
)
def build(self, dump_debug_info: bool = False) -> RtbFlightPlan:

View File

@@ -70,9 +70,6 @@ class StandardLayout(Layout, ABC):
elif waypoint in self.nav_from:
self.nav_from.remove(waypoint)
return True
elif waypoint in self.custom_waypoints:
self.custom_waypoints.remove(waypoint)
return True
return False

View File

@@ -11,7 +11,6 @@ from .formationattack import (
from .invalidobjectivelocation import InvalidObjectiveLocation
from .waypointbuilder import StrikeTarget
from ..flightwaypointtype import FlightWaypointType
from ...theater.theatergroup import SceneryUnit
class StrikeFlightPlan(FormationAttackFlightPlan):
@@ -29,10 +28,7 @@ class Builder(FormationAttackBuilder[StrikeFlightPlan, FormationAttackLayout]):
targets: list[StrikeTarget] = []
for idx, unit in enumerate(location.strike_targets):
name = unit.type.id
if isinstance(unit, SceneryUnit):
name = unit.name
targets.append(StrikeTarget(f"{name} #{idx}", unit))
targets.append(StrikeTarget(f"{unit.type.id} #{idx}", unit))
return self._build(FlightWaypointType.INGRESS_STRIKE, targets)

View File

@@ -35,7 +35,6 @@ class SweepLayout(LoiterLayout):
if self.divert is not None:
yield self.divert
yield self.bullseye
yield from self.custom_waypoints
class SweepFlightPlan(LoiterFlightPlan):
@@ -105,27 +104,26 @@ class Builder(IBuilder[SweepFlightPlan, SweepLayout]):
)
builder = WaypointBuilder(self.flight)
altitude = builder.get_patrol_altitude
start, end = builder.sweep(start_pos, target, altitude)
start, end = builder.sweep(start_pos, target, self.doctrine.ingress_altitude)
hold = builder.hold(self._hold_point())
return SweepLayout(
departure=builder.takeoff(self.flight.departure),
hold=hold,
nav_to=builder.nav_path(hold.position, start.position, altitude),
nav_to=builder.nav_path(
hold.position, start.position, self.doctrine.ingress_altitude
),
nav_from=builder.nav_path(
end.position,
self.flight.arrival.position,
altitude,
self.doctrine.ingress_altitude,
),
sweep_start=start,
sweep_end=end,
arrival=builder.land(self.flight.arrival),
divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(),
custom_waypoints=list(),
)
def _hold_point(self) -> Point:

View File

@@ -1,11 +1,12 @@
from __future__ import annotations
import random
from collections.abc import Iterator
from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import TYPE_CHECKING, Type
from game.utils import Distance, Speed
from game.utils import Distance, Speed, feet
from .capbuilder import CapBuilder
from .patrolling import PatrollingFlightPlan, PatrollingLayout
from .waypointbuilder import WaypointBuilder
@@ -30,7 +31,6 @@ class TarCapLayout(PatrollingLayout):
if self.divert is not None:
yield self.divert
yield self.bullseye
yield from self.custom_waypoints
def delete_waypoint(self, waypoint: FlightWaypoint) -> bool:
if waypoint == self.refuel:
@@ -95,9 +95,14 @@ class Builder(CapBuilder[TarCapFlightPlan, TarCapLayout]):
def layout(self) -> TarCapLayout:
location = self.package.target
builder = WaypointBuilder(self.flight)
patrol_alt = builder.get_patrol_altitude
preferred_alt = self.flight.unit_type.preferred_patrol_altitude
randomized_alt = preferred_alt + feet(random.randint(-2, 1) * 1000)
patrol_alt = max(
self.doctrine.min_patrol_altitude,
min(self.doctrine.max_patrol_altitude, randomized_alt),
)
builder = WaypointBuilder(self.flight)
orbit0p, orbit1p = self.cap_racetrack_for_objective(location, barcap=False)
start, end = builder.race_track(orbit0p, orbit1p, patrol_alt)
@@ -123,7 +128,6 @@ class Builder(CapBuilder[TarCapFlightPlan, TarCapLayout]):
arrival=builder.land(self.flight.arrival),
divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(),
custom_waypoints=list(),
)
def build(self, dump_debug_info: bool = False) -> TarCapFlightPlan:

View File

@@ -3,7 +3,7 @@ from __future__ import annotations
from datetime import timedelta
from typing import Type
from game.utils import Heading, meters, nautical_miles
from game.utils import Heading, feet, meters, nautical_miles
from .ibuilder import IBuilder
from .patrolling import PatrollingLayout
from .refuelingflightplan import RefuelingFlightPlan
@@ -58,7 +58,11 @@ class Builder(IBuilder[TheaterRefuelingFlightPlan, PatrollingLayout]):
builder = WaypointBuilder(self.flight)
altitude = builder.get_patrol_altitude
tanker_type = self.flight.unit_type
if tanker_type.patrol_altitude is not None:
altitude = tanker_type.patrol_altitude
else:
altitude = feet(21000)
racetrack = builder.race_track(racetrack_start, racetrack_end, altitude)
@@ -75,7 +79,6 @@ class Builder(IBuilder[TheaterRefuelingFlightPlan, PatrollingLayout]):
arrival=builder.land(self.flight.arrival),
divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(),
custom_waypoints=list(),
)
def build(self, dump_debug_info: bool = False) -> TheaterRefuelingFlightPlan:

View File

@@ -26,8 +26,6 @@ from game.theater import (
)
from game.utils import Distance, meters, nautical_miles, feet
AGL_TRANSITION_ALT = 5000
if TYPE_CHECKING:
from game.transfers import MultiGroupTransport
from game.theater.theatergroup import TheaterGroup
@@ -53,34 +51,11 @@ class WaypointBuilder:
self.navmesh = coalition.nav_mesh
self.targets = targets
self._bullseye = coalition.bullseye
self.settings = self.flight.coalition.game.settings
@property
def is_helo(self) -> bool:
return self.flight.is_helo
@property
def get_patrol_altitude(self) -> Distance:
return self.get_altitude(self.flight.unit_type.preferred_patrol_altitude)
@property
def get_cruise_altitude(self) -> Distance:
return self.get_altitude(self.flight.unit_type.preferred_cruise_altitude)
@property
def get_combat_altitude(self) -> Distance:
return self.get_altitude(self.flight.unit_type.preferred_combat_altitude)
def get_altitude(self, alt: Distance) -> Distance:
randomized_alt = feet(round(alt.feet + self.flight.plane_altitude_offset))
altitude = max(
self.doctrine.min_combat_altitude,
min(self.doctrine.max_combat_altitude, randomized_alt),
)
return (
feet(self.settings.heli_combat_alt_agl) if self.flight.is_helo else altitude
)
def takeoff(self, departure: ControlPoint) -> FlightWaypoint:
"""Create takeoff waypoint for the given arrival airfield or carrier.
@@ -97,7 +72,9 @@ class WaypointBuilder:
"NAV",
FlightWaypointType.NAV,
position,
self.get_cruise_altitude,
feet(self.flight.coalition.game.settings.heli_cruise_alt_agl)
if self.is_helo
else self.doctrine.rendezvous_altitude,
description="Enter theater",
pretty_name="Enter theater",
)
@@ -124,7 +101,9 @@ class WaypointBuilder:
"NAV",
FlightWaypointType.NAV,
position,
self.get_cruise_altitude,
feet(self.flight.coalition.game.settings.heli_cruise_alt_agl)
if self.is_helo
else self.doctrine.rendezvous_altitude,
description="Exit theater",
pretty_name="Exit theater",
)
@@ -150,10 +129,14 @@ class WaypointBuilder:
return None
position = divert.position
altitude_type: AltitudeReference = "BARO"
altitude_type: AltitudeReference
if isinstance(divert, OffMapSpawn):
altitude = self.get_cruise_altitude
altitude_type = "RADIO" if self.is_helo else altitude_type
altitude = (
feet(self.flight.coalition.game.settings.heli_cruise_alt_agl)
if self.is_helo
else self.doctrine.rendezvous_altitude
)
altitude_type = "BARO"
else:
altitude = meters(0)
altitude_type = "RADIO"
@@ -183,15 +166,16 @@ class WaypointBuilder:
def hold(self, position: Point) -> FlightWaypoint:
alt_type: AltitudeReference = "BARO"
if self.is_helo or self.get_combat_altitude.feet <= AGL_TRANSITION_ALT:
if self.is_helo:
alt_type = "RADIO"
return FlightWaypoint(
"HOLD",
FlightWaypointType.LOITER,
position,
# TODO: dedicated altitude setting for holding
self.get_cruise_altitude if self.is_helo else self.get_combat_altitude,
feet(self.flight.coalition.game.settings.heli_cruise_alt_agl)
if self.is_helo
else self.doctrine.ingress_altitude,
alt_type,
description="Wait until push time",
pretty_name="Hold",
@@ -199,14 +183,16 @@ class WaypointBuilder:
def join(self, position: Point) -> FlightWaypoint:
alt_type: AltitudeReference = "BARO"
if self.is_helo or self.get_cruise_altitude.feet <= AGL_TRANSITION_ALT:
if self.is_helo:
alt_type = "RADIO"
return FlightWaypoint(
"JOIN",
FlightWaypointType.JOIN,
position,
self.get_cruise_altitude,
feet(self.flight.coalition.game.settings.heli_cruise_alt_agl)
if self.is_helo
else self.doctrine.ingress_altitude,
alt_type,
description="Rendezvous with package",
pretty_name="Join",
@@ -214,14 +200,16 @@ class WaypointBuilder:
def refuel(self, position: Point) -> FlightWaypoint:
alt_type: AltitudeReference = "BARO"
if self.is_helo or self.get_cruise_altitude.feet <= AGL_TRANSITION_ALT:
if self.is_helo:
alt_type = "RADIO"
return FlightWaypoint(
"REFUEL",
FlightWaypointType.REFUEL,
position,
self.get_cruise_altitude,
feet(self.flight.coalition.game.settings.heli_cruise_alt_agl)
if self.is_helo
else self.doctrine.ingress_altitude,
alt_type,
description="Refuel from tanker",
pretty_name="Refuel",
@@ -229,14 +217,16 @@ class WaypointBuilder:
def split(self, position: Point) -> FlightWaypoint:
alt_type: AltitudeReference = "BARO"
if self.is_helo or self.get_combat_altitude.feet <= AGL_TRANSITION_ALT:
if self.is_helo:
alt_type = "RADIO"
return FlightWaypoint(
"SPLIT",
FlightWaypointType.SPLIT,
position,
self.get_combat_altitude,
feet(self.flight.coalition.game.settings.heli_combat_alt_agl)
if self.is_helo
else self.doctrine.ingress_altitude,
alt_type,
description="Depart from package",
pretty_name="Split",
@@ -248,7 +238,7 @@ class WaypointBuilder:
position: Point,
objective: MissionTarget,
) -> FlightWaypoint:
alt = self.get_combat_altitude
alt = self.doctrine.ingress_altitude
alt_type: AltitudeReference = "BARO"
if self.is_helo or self.flight.is_hercules:
alt_type = "RADIO"
@@ -257,8 +247,6 @@ class WaypointBuilder:
if self.is_helo
else feet(1000)
)
elif alt.feet <= AGL_TRANSITION_ALT:
alt_type = "RADIO"
heading = objective.position.heading_between_point(position)
@@ -277,14 +265,16 @@ class WaypointBuilder:
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:
if self.is_helo:
alt_type = "RADIO"
return FlightWaypoint(
"EGRESS",
FlightWaypointType.EGRESS,
position,
self.get_combat_altitude,
feet(self.flight.coalition.game.settings.heli_combat_alt_agl)
if self.is_helo
else self.doctrine.ingress_altitude,
alt_type,
description=f"EGRESS from {target.name}",
pretty_name=f"EGRESS from {target.name}",
@@ -322,15 +312,11 @@ class WaypointBuilder:
return self._target_area(f"STRIKE {target.name}", target)
def sead_area(self, target: MissionTarget) -> FlightWaypoint:
alt_type: AltitudeReference = "BARO"
if self.get_combat_altitude.feet <= AGL_TRANSITION_ALT:
alt_type = "RADIO"
return self._target_area(
f"SEAD on {target.name}",
target,
altitude=self.get_combat_altitude,
alt_type=alt_type,
altitude=self.doctrine.ingress_altitude,
alt_type="BARO",
)
def dead_area(self, target: MissionTarget) -> FlightWaypoint:
@@ -401,13 +387,11 @@ class WaypointBuilder:
position: Position of the waypoint.
altitude: Altitude of the racetrack.
"""
baro: AltitudeReference = "BARO"
return FlightWaypoint(
"RACETRACK START",
FlightWaypointType.PATROL_TRACK,
position,
altitude,
"RADIO" if altitude.feet <= AGL_TRANSITION_ALT else baro,
description="Orbit between this point and the next point",
pretty_name="Race-track start",
)
@@ -420,13 +404,11 @@ class WaypointBuilder:
position: Position of the waypoint.
altitude: Altitude of the racetrack.
"""
baro: AltitudeReference = "BARO"
return FlightWaypoint(
"RACETRACK END",
FlightWaypointType.PATROL,
position,
altitude,
"RADIO" if altitude.feet <= AGL_TRANSITION_ALT else baro,
description="Orbit between this point and the previous point",
pretty_name="Race-track end",
)
@@ -454,39 +436,42 @@ class WaypointBuilder:
start: Position of the waypoint.
altitude: Altitude of the racetrack.
"""
baro: AltitudeReference = "BARO"
return FlightWaypoint(
"ORBIT",
FlightWaypointType.LOITER,
start,
altitude,
"RADIO" if altitude.feet <= AGL_TRANSITION_ALT else baro,
description="Anchor and hold at this point",
pretty_name="Orbit",
)
def sead_search(self, target: MissionTarget) -> FlightWaypoint:
hold = self._sead_search_point(target)
baro: AltitudeReference = "BARO"
return FlightWaypoint(
"SEAD Search",
FlightWaypointType.NAV,
hold,
self.get_combat_altitude,
"RADIO" if self.get_combat_altitude.feet <= AGL_TRANSITION_ALT else baro,
feet(self.flight.coalition.game.settings.heli_combat_alt_agl)
if self.is_helo
else self.doctrine.ingress_altitude,
alt_type="BARO",
description="Anchor and search from this point",
pretty_name="SEAD Search",
)
def sead_sweep(self, target: MissionTarget) -> FlightWaypoint:
hold = self._sead_search_point(target)
baro: AltitudeReference = "BARO"
return FlightWaypoint(
"SEAD Sweep",
FlightWaypointType.NAV,
hold,
self.get_combat_altitude,
"RADIO" if self.get_combat_altitude.feet <= AGL_TRANSITION_ALT else baro,
feet(self.flight.coalition.game.settings.heli_combat_alt_agl)
if self.is_helo
else self.doctrine.ingress_altitude,
alt_type="BARO",
description="Anchor and search from this point",
pretty_name="SEAD Sweep",
)
@@ -514,16 +499,15 @@ class WaypointBuilder:
)
return hold
def escort_hold(self, start: Point) -> FlightWaypoint:
def escort_hold(self, start: Point, altitude: Distance) -> FlightWaypoint:
"""Creates custom waypoint for escort flights that need to hold.
Args:
start: Position of the waypoint.
altitude: Altitude of the holding pattern.
"""
altitude = self.get_combat_altitude
alt_type: Literal["BARO", "RADIO"] = "BARO"
if self.is_helo or altitude.feet <= AGL_TRANSITION_ALT:
if self.is_helo:
alt_type = "RADIO"
return FlightWaypoint(
@@ -544,13 +528,11 @@ class WaypointBuilder:
position: Position of the waypoint.
altitude: Altitude of the sweep in meters.
"""
baro: AltitudeReference = "BARO"
return FlightWaypoint(
"SWEEP START",
FlightWaypointType.INGRESS_SWEEP,
position,
altitude,
"RADIO" if altitude.feet <= AGL_TRANSITION_ALT else baro,
description="Proceed to the target and engage enemy aircraft",
pretty_name="Sweep start",
)
@@ -563,13 +545,11 @@ class WaypointBuilder:
position: Position of the waypoint.
altitude: Altitude of the sweep in meters.
"""
baro: AltitudeReference = "BARO"
return FlightWaypoint(
"SWEEP END",
FlightWaypointType.EGRESS,
position,
altitude,
"RADIO" if altitude.feet <= AGL_TRANSITION_ALT else baro,
description="End of sweep",
pretty_name="Sweep end",
)
@@ -598,7 +578,7 @@ class WaypointBuilder:
target: The mission target.
"""
alt_type: AltitudeReference = "BARO"
if self.is_helo or self.get_combat_altitude.feet <= AGL_TRANSITION_ALT:
if self.is_helo:
alt_type = "RADIO"
# This would preferably be no points at all, and instead the Escort task
@@ -612,7 +592,9 @@ class WaypointBuilder:
"TARGET",
FlightWaypointType.TARGET_GROUP_LOC,
target.position,
self.get_combat_altitude,
feet(self.flight.coalition.game.settings.heli_combat_alt_agl)
if self.is_helo
else self.doctrine.ingress_altitude,
alt_type,
description="Escort the package",
pretty_name="Target area",
@@ -634,19 +616,17 @@ class WaypointBuilder:
pretty_name="Pick-up zone",
)
def dropoff_zone(self, drop_off: MissionTarget) -> FlightWaypoint:
@staticmethod
def dropoff_zone(drop_off: MissionTarget) -> FlightWaypoint:
"""Creates a dropoff landing zone waypoint
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)
return FlightWaypoint(
"DROPOFFZONE",
FlightWaypointType.DROPOFF_ZONE,
drop_off.position,
altitude,
meters(0),
"RADIO",
description=f"Drop off cargo at {drop_off.name}",
pretty_name="Drop-off zone",
@@ -680,7 +660,7 @@ class WaypointBuilder:
altitude_is_agl: True for altitude is AGL. False if altitude is MSL.
"""
alt_type: AltitudeReference = "BARO"
if altitude_is_agl or altitude.feet <= AGL_TRANSITION_ALT:
if altitude_is_agl:
alt_type = "RADIO"
return FlightWaypoint(

View File

@@ -19,6 +19,7 @@ from game.theater.iadsnetwork.iadsnetwork import IadsNetwork
from game.theater.theaterloader import TheaterLoader
from game.version import CAMPAIGN_FORMAT_VERSION
from .campaignairwingconfig import CampaignAirWingConfig
from .campaigncarrierconfig import CampaignCarrierConfig
from .campaigngroundconfig import TgoConfig
from .mizcampaignloader import MizCampaignLoader
@@ -140,6 +141,13 @@ class Campaign:
return CampaignAirWingConfig({})
return CampaignAirWingConfig.from_campaign_data(squadron_data, theater)
def load_carrier_config(self) -> CampaignCarrierConfig:
try:
carrier_data = self.data["carriers"]
except KeyError:
return CampaignCarrierConfig({})
return CampaignCarrierConfig.from_campaign_data(carrier_data)
def load_ground_forces_config(self) -> TgoConfig:
ground_forces = self.data.get("ground_forces", {})
if not ground_forces:

View File

@@ -0,0 +1,42 @@
from __future__ import annotations
import logging
from collections import defaultdict
from dataclasses import dataclass
from typing import Any, TYPE_CHECKING
from game.dcs.shipunittype import ShipUnitType
if TYPE_CHECKING:
pass
@dataclass(frozen=True)
class CarrierConfig:
preferred_name: str
preferred_type: ShipUnitType
@classmethod
def from_data(cls, data: dict[str, Any]) -> CarrierConfig:
return CarrierConfig(
str(data["preferred_name"]), ShipUnitType.named(data["preferred_type"])
)
@dataclass(frozen=True)
class CampaignCarrierConfig:
by_original_name: dict[str, CarrierConfig]
@classmethod
def from_campaign_data(
cls, data: dict[str, Any]
) -> CampaignCarrierConfig:
by_original_name: dict[str, CarrierConfig] = defaultdict()
for original_name, carrier_config_data in data.items():
try:
carrier_config = CarrierConfig.from_data(carrier_config_data)
by_original_name[original_name] = carrier_config
except KeyError:
logging.warning(f"Skipping invalid carrier config for '{original_name}'")
return CampaignCarrierConfig(by_original_name)

View File

@@ -38,7 +38,7 @@ class PackageBuilder:
self.laser_code_registry = laser_code_registry
self.start_type = start_type
def plan_flight(self, plan: ProposedFlight, ignore_range: bool) -> bool:
def plan_flight(self, plan: ProposedFlight) -> bool:
"""Allocates aircraft for the given flight and adds them to the package.
If no suitable aircraft are available, False is returned. If the failed
@@ -55,7 +55,6 @@ class PackageBuilder:
heli,
this_turn=True,
preferred_type=plan.preferred_type,
ignore_range=ignore_range,
)
if squadron is None:
return False

View File

@@ -81,9 +81,8 @@ class PackageFulfiller:
builder: PackageBuilder,
missing_types: Set[FlightType],
purchase_multiplier: int,
ignore_range: bool = False,
) -> None:
if not builder.plan_flight(flight, ignore_range):
if not builder.plan_flight(flight):
pf = builder.package.primary_flight
heli = pf.is_helo if pf else False
missing_types.add(flight.task)
@@ -139,7 +138,6 @@ class PackageFulfiller:
purchase_multiplier: int,
now: datetime,
tracer: MultiEventTracer,
ignore_range: bool = False,
) -> Optional[Package]:
"""Allocates aircraft for a proposed mission and adds it to the ATO."""
builder = PackageBuilder(
@@ -177,7 +175,6 @@ class PackageFulfiller:
builder,
missing_types,
purchase_multiplier,
ignore_range,
)
if missing_types:

View File

@@ -26,6 +26,8 @@ class Doctrine:
strike: bool
antiship: bool
rendezvous_altitude: Distance
#: The minimum distance between the departure airfield and the hold point.
hold_distance: Distance
@@ -44,14 +46,11 @@ class Doctrine:
#: target.
min_ingress_distance: Distance
ingress_altitude: Distance
min_patrol_altitude: Distance
max_patrol_altitude: Distance
min_cruise_altitude: Distance
max_cruise_altitude: Distance
min_combat_altitude: Distance
max_combat_altitude: Distance
pattern_altitude: Distance
#: The duration that CAP flights will remain on-station.
cap_duration: timedelta
@@ -98,17 +97,16 @@ MODERN_DOCTRINE = Doctrine(
sead=True,
strike=True,
antiship=True,
rendezvous_altitude=feet(25000),
hold_distance=nautical_miles(25),
push_distance=nautical_miles(20),
join_distance=nautical_miles(20),
max_ingress_distance=nautical_miles(45),
min_ingress_distance=nautical_miles(10),
ingress_altitude=feet(20000),
min_patrol_altitude=feet(15000),
max_patrol_altitude=feet(33000),
min_cruise_altitude=feet(10000),
max_cruise_altitude=feet(40000),
min_combat_altitude=feet(1000),
max_combat_altitude=feet(35000),
pattern_altitude=feet(5000),
cap_duration=timedelta(minutes=30),
cap_min_track_length=nautical_miles(15),
cap_max_track_length=nautical_miles(40),
@@ -142,17 +140,16 @@ COLDWAR_DOCTRINE = Doctrine(
sead=True,
strike=True,
antiship=True,
rendezvous_altitude=feet(22000),
hold_distance=nautical_miles(15),
push_distance=nautical_miles(10),
join_distance=nautical_miles(10),
max_ingress_distance=nautical_miles(30),
min_ingress_distance=nautical_miles(10),
ingress_altitude=feet(18000),
min_patrol_altitude=feet(10000),
max_patrol_altitude=feet(24000),
min_cruise_altitude=feet(10000),
max_cruise_altitude=feet(30000),
min_combat_altitude=feet(1000),
max_combat_altitude=feet(25000),
pattern_altitude=feet(5000),
cap_duration=timedelta(minutes=30),
cap_min_track_length=nautical_miles(12),
cap_max_track_length=nautical_miles(24),
@@ -189,14 +186,13 @@ WWII_DOCTRINE = Doctrine(
hold_distance=nautical_miles(10),
push_distance=nautical_miles(5),
join_distance=nautical_miles(5),
rendezvous_altitude=feet(10000),
max_ingress_distance=nautical_miles(7),
min_ingress_distance=nautical_miles(5),
ingress_altitude=feet(8000),
min_patrol_altitude=feet(4000),
max_patrol_altitude=feet(15000),
min_cruise_altitude=feet(5000),
max_cruise_altitude=feet(30000),
min_combat_altitude=feet(1000),
max_combat_altitude=feet(10000),
pattern_altitude=feet(5000),
cap_duration=timedelta(minutes=30),
cap_min_track_length=nautical_miles(8),
cap_max_track_length=nautical_miles(18),

View File

@@ -132,21 +132,6 @@ class PatrolConfig:
)
@dataclass(frozen=True)
class AltitudesConfig:
cruise: Optional[Distance]
combat: Optional[Distance]
@classmethod
def from_data(cls, data: dict[str, Any]) -> AltitudesConfig:
cruise = data.get("cruise", None)
combat = data.get("combat", None)
return AltitudesConfig(
feet(cruise) if cruise is not None else None,
feet(combat) if combat is not None else None,
)
@dataclass(frozen=True)
class FuelConsumption:
#: The estimated taxi fuel requirement, in pounds.
@@ -197,9 +182,6 @@ class AircraftType(UnitType[Type[FlyingType]]):
patrol_altitude: Optional[Distance]
patrol_speed: Optional[Speed]
cruise_altitude: Optional[Distance]
combat_altitude: Optional[Distance]
#: The maximum range between the origin airfield and the target for which the auto-
#: planner will consider this aircraft usable for a mission.
max_mission_range: Distance
@@ -263,13 +245,33 @@ class AircraftType(UnitType[Type[FlyingType]]):
def max_speed(self) -> Speed:
return kph(self.dcs_unit_type.max_speed)
@cached_property
@property
def preferred_patrol_altitude(self) -> Distance:
if self.patrol_altitude:
if self.patrol_altitude is not None:
return self.patrol_altitude
else:
# TODO: somehow make the upper and lower limit configurable
return self.preferred_altitude(10, 33, "patrol")
# Estimate based on max speed.
# Aircaft with max speed 600 kph will prefer patrol at 10 000 ft
# Aircraft with max speed 2800 kph will prefer pratrol at 33 000 ft
altitude_for_lowest_speed = feet(10 * 1000)
altitude_for_highest_speed = feet(33 * 1000)
lowest_speed = kph(600)
highest_speed = kph(2800)
factor = (self.max_speed - lowest_speed).kph / (
highest_speed - lowest_speed
).kph
altitude = (
altitude_for_lowest_speed
+ (altitude_for_highest_speed - altitude_for_lowest_speed) * factor
)
logging.debug(
f"Preferred patrol altitude for {self.dcs_unit_type.id}: {altitude.feet}"
)
rounded_altitude = feet(round(1000 * round(altitude.feet / 1000)))
return max(
altitude_for_lowest_speed,
min(altitude_for_highest_speed, rounded_altitude),
)
def preferred_patrol_speed(self, altitude: Distance) -> Speed:
"""Preferred true airspeed when patrolling"""
@@ -307,46 +309,6 @@ class AircraftType(UnitType[Type[FlyingType]]):
)
return min(Speed.from_mach(0.35, altitude), max_speed * 0.5)
@cached_property
def preferred_cruise_altitude(self) -> Distance:
if self.cruise_altitude:
return self.cruise_altitude
else:
# TODO: somehow make the upper and lower limit configurable
return self.preferred_altitude(20, 20, "cruise")
@cached_property
def preferred_combat_altitude(self) -> Distance:
if self.combat_altitude:
return self.combat_altitude
else:
# TODO: somehow make the upper and lower limit configurable
return self.preferred_altitude(20, 20, "combat")
def preferred_altitude(self, low: int, high: int, type: str) -> Distance:
# Estimate based on max speed.
# Aircraft with max speed 600 kph will prefer low
# Aircraft with max speed 2800 kph will prefer high
altitude_for_lowest_speed = feet(low * 1000)
altitude_for_highest_speed = feet(high * 1000)
lowest_speed = kph(600)
highest_speed = kph(2800)
factor = (self.max_speed - lowest_speed).kph / (
highest_speed - lowest_speed
).kph
altitude = (
altitude_for_lowest_speed
+ (altitude_for_highest_speed - altitude_for_lowest_speed) * factor
)
logging.debug(
f"Preferred {type} altitude for {self.dcs_unit_type.id}: {altitude.feet}"
)
rounded_altitude = feet(round(1000 * round(altitude.feet / 1000)))
return max(
altitude_for_lowest_speed,
min(altitude_for_highest_speed, rounded_altitude),
)
def alloc_flight_radio(self, radio_registry: RadioRegistry) -> RadioFrequency:
from game.radio.radios import ChannelInUseError, kHz
@@ -480,7 +442,6 @@ class AircraftType(UnitType[Type[FlyingType]]):
radio_config = RadioConfig.from_data(data.get("radios", {}))
patrol_config = PatrolConfig.from_data(data.get("patrol", {}))
altitudes_config = AltitudesConfig.from_data(data.get("altitudes", {}))
try:
mission_range = nautical_miles(int(data["max_range"]))
@@ -549,8 +510,6 @@ class AircraftType(UnitType[Type[FlyingType]]):
max_group_size=data.get("max_group_size", aircraft.group_size_max),
patrol_altitude=patrol_config.altitude,
patrol_speed=patrol_config.speed,
cruise_altitude=altitudes_config.cruise,
combat_altitude=altitudes_config.combat,
max_mission_range=mission_range,
fuel_consumption=fuel_consumption,
default_livery=data.get("default_livery"),

View File

@@ -2,6 +2,7 @@ from __future__ import annotations
import itertools
import logging
from collections import defaultdict
from dataclasses import dataclass, field
from functools import cached_property
from typing import Optional, Dict, Type, List, Any, Iterator, TYPE_CHECKING, Set
@@ -93,8 +94,8 @@ class Faction:
# Required mods or asset packs
requirements: Dict[str, str] = field(default_factory=dict)
# Possible carrier names
carrier_names: Set[str] = field(default_factory=set)
# Possible aircraft carrier units
carriers: Dict[ShipUnitType, Set[str]] = field(default_factory=dict)
# Possible helicopter carrier names
helicopter_carrier_names: Set[str] = field(default_factory=set)
@@ -241,8 +242,29 @@ class Faction:
faction.requirements = json.get("requirements", {})
faction.carrier_names = json.get("carrier_names", [])
faction.helicopter_carrier_names = json.get("helicopter_carrier_names", [])
# First try to load the carriers in the new format which
# specifies different names for different carrier types
loaded_carriers = load_carriers(json)
carriers: List[ShipUnitType] = [
unit
for unit in faction.naval_units
if unit.unit_class
in [
UnitClass.AIRCRAFT_CARRIER,
UnitClass.HELICOPTER_CARRIER,
]
]
carrier_names = json.get("carrier_names", [])
for c in carriers:
if c.variant_id not in loaded_carriers:
if c.unit_class == UnitClass.AIRCRAFT_CARRIER:
loaded_carriers[c] = carrier_names
elif c.unit_class == UnitClass.HELICOPTER_CARRIER:
loaded_carriers[c] = faction.helicopter_carrier_names
faction.carriers = loaded_carriers
faction.naval_units.union(faction.carriers.keys())
faction.has_jtac = json.get("has_jtac", False)
jtac_name = json.get("jtac_unit", None)
@@ -340,8 +362,6 @@ class Faction:
if not mod_settings.f4bc_phantom:
self.remove_aircraft("VSN_F4B")
self.remove_aircraft("VSN_F4C")
if not mod_settings.f9f_panther:
self.remove_aircraft("VSN_F9F")
if not mod_settings.f15d_baz:
self.remove_aircraft("F-15D")
if not mod_settings.f_15_idf:
@@ -383,8 +403,6 @@ class Faction:
self.remove_aircraft("JAS39Gripen")
self.remove_aircraft("JAS39Gripen_BVR")
self.remove_aircraft("JAS39Gripen_AG")
if not mod_settings.super_etendard:
self.remove_aircraft("VSN_SEM")
if not mod_settings.su30_flanker_h:
self.remove_aircraft("Su-30MKA")
self.remove_aircraft("Su-30MKI")
@@ -596,3 +614,14 @@ def load_all_ships(data: list[str]) -> List[Type[ShipType]]:
if item is not None:
items.append(item)
return items
def load_carriers(json: Dict[str, Any]) -> Dict[ShipUnitType, Set[str]]:
# Load carriers
items: Dict[ShipUnitType, Set[str]] = defaultdict(Set[str])
carriers = json.get("carriers", {})
for carrier_shiptype, shipnames in carriers.items():
shiptype = ShipUnitType.named(carrier_shiptype)
if shiptype is not None:
items[shiptype] = shipnames
return items

View File

@@ -52,7 +52,7 @@ class Migrator:
continue
found = False
for d in doctrines:
if c.faction.doctrine.max_patrol_altitude == d.max_patrol_altitude:
if c.faction.doctrine.rendezvous_altitude == d.rendezvous_altitude:
c.faction.doctrine = d
found = True
break
@@ -78,7 +78,6 @@ class Migrator:
def _update_control_points(self) -> None:
is_sinai = self.game.theater.terrain.name == "SinaiMap"
for cp in self.game.theater.controlpoints:
cp.release_parking_slots()
is_carrier = cp.is_carrier
is_lha = cp.is_lha
is_fob = cp.category == "fob"
@@ -108,7 +107,6 @@ class Migrator:
layout = f.flight_plan.layout
try_set_attr(layout, "nav_to", [])
try_set_attr(layout, "nav_from", [])
try_set_attr(layout, "custom_waypoints", [])
if f.flight_type == FlightType.CAS:
try_set_attr(layout, "ingress", None)
if isinstance(layout, FormationLayout):
@@ -123,7 +121,6 @@ class Migrator:
try_set_attr(f, "tacan")
try_set_attr(f, "tcn_name")
try_set_attr(f, "fuel", f.unit_type.max_fuel)
try_set_attr(f, "plane_altitude_offset", 0)
if f.package in f.squadron.coalition.ato.packages:
self._update_flight_plan(f)
else:
@@ -204,8 +201,6 @@ class Migrator:
c.faction.air_defense_units = set(c.faction.air_defense_units)
if isinstance(c.faction.missiles, list):
c.faction.missiles = set(c.faction.missiles)
if isinstance(c.faction.carrier_names, list):
c.faction.carrier_names = set(c.faction.carrier_names)
if isinstance(c.faction.helicopter_carrier_names, list):
c.faction.helicopter_carrier_names = set(
c.faction.helicopter_carrier_names

View File

@@ -44,16 +44,7 @@ class DeadIngressBuilder(PydcsWaypointBuilder):
task = AttackGroup(
miz_group.id,
weapon_type=WeaponType.ASM,
expend=Expend.All,
altitude=waypoint.alt,
group_attack=True,
)
waypoint.tasks.append(task)
task = AttackGroup(
miz_group.id,
weapon_type=WeaponType.GuidedBombs,
weapon_type=WeaponType.Guided,
altitude=waypoint.alt,
)
waypoint.tasks.append(task)

View File

@@ -40,4 +40,4 @@ class HoldPointBuilder(PydcsWaypointBuilder):
if self.flight.is_helo:
waypoint.add_task(OptFormation.rotary_column())
else:
waypoint.add_task(OptFormation.finger_four_open())
waypoint.add_task(OptFormation.finger_four_close())

View File

@@ -8,7 +8,9 @@ class LandingPointBuilder(PydcsWaypointBuilder):
def build(self) -> MovingPoint:
waypoint = super().build()
if self.ai_despawn(waypoint):
waypoint.alt = round(self.flight.unit_type.preferred_patrol_altitude.meters)
waypoint.alt = round(
self.flight.coalition.doctrine.max_patrol_altitude.meters
)
waypoint.alt_type = "BARO"
else:
waypoint.type = "Land"

View File

@@ -124,9 +124,7 @@ class PydcsWaypointBuilder:
return False
def register_special_strike_points(
self,
targets: Iterable[Union[MissionTarget, TheaterUnit]],
start: int = 1,
self, targets: Iterable[Union[MissionTarget, TheaterUnit]]
) -> None:
"""Create special strike waypoints for various aircraft"""
for i, t in enumerate(targets):
@@ -137,7 +135,7 @@ class PydcsWaypointBuilder:
# Add F-15E mission target points as mission 1 (for JDAM for instance)
if self.group.units[0].unit_type == F_15ESE:
self.group.add_nav_target_point(
t.position, f"M{(i//8)+start}.{i%8+1}\nH-1\nA0\nV0"
t.position, f"M{(i//8)+1}.{i%8+1}" f"\nH-1" f"\nA0" f"\nV0"
)
def register_special_ingress_points(self) -> None:

View File

@@ -29,7 +29,7 @@ class SplitPointBuilder(PydcsWaypointBuilder):
if self.flight.is_helo:
waypoint.tasks.append(OptFormation.rotary_wedge())
else:
waypoint.tasks.append(OptFormation.finger_four_open())
waypoint.tasks.append(OptFormation.finger_four_close())
waypoint.speed_locked = True
waypoint.ETA_locked = False
if self.flight.is_helo:

View File

@@ -2,7 +2,7 @@ import copy
from typing import Union
from dcs import Point
from dcs.planes import B_17G, B_52H, Tu_22M3, B_1B, F_15ESE
from dcs.planes import B_17G, B_52H, Tu_22M3, B_1B
from dcs.point import MovingPoint
from dcs.task import Bombing, Expend, OptFormation, WeaponType, CarpetBombing
@@ -67,6 +67,8 @@ class StrikeIngressBuilder(PydcsWaypointBuilder):
# If there is only one target, drop all ordnance in one pass.
if len(self.waypoint.targets) == 1:
bombing.params["expend"] = Expend.All.value
elif target.is_static:
bombing.params["expend"] = Expend.Half.value
waypoint.tasks.append(bombing)
waypoint.speed = mach(0.85, meters(waypoint.alt)).meters_per_second
@@ -74,6 +76,4 @@ class StrikeIngressBuilder(PydcsWaypointBuilder):
# Register special waypoints
if not self._special_wpts_injected:
self.register_special_strike_points(self.waypoint.targets)
if self.flight.unit_type.dcs_unit_type == F_15ESE:
self.register_special_strike_points(self.flight.custom_targets, 2)
self._special_wpts_injected = True

View File

@@ -39,13 +39,7 @@ from dcs.task import (
OptAlarmState,
)
from dcs.translation import String
from dcs.triggers import (
Event,
TriggerOnce,
TriggerStart,
TriggerZone,
TriggerZoneQuadPoint,
)
from dcs.triggers import Event, TriggerOnce, TriggerStart, TriggerZone
from dcs.unit import Unit, InvisibleFARP, BaseFARP, SingleHeliPad, FARP
from dcs.unitgroup import MovingGroup, ShipGroup, StaticGroup, VehicleGroup
from dcs.unittype import ShipType, VehicleType
@@ -411,24 +405,14 @@ class GroundObjectGenerator:
# is minimized. As long as the triggerzone is over the scenery object, we're ok.
smallest_valid_radius = feet(16).meters
if isinstance(scenery.zone, TriggerZoneQuadPoint):
trigger_zone: TriggerZone = self.m.triggers.add_triggerzone_quad(
scenery.zone.position,
scenery.zone.verticies,
scenery.zone.hidden,
scenery.zone.name,
color,
scenery.zone.properties,
)
else:
trigger_zone = self.m.triggers.add_triggerzone(
scenery.zone.position,
smallest_valid_radius,
scenery.zone.hidden,
scenery.zone.name,
color,
scenery.zone.properties,
)
trigger_zone = self.m.triggers.add_triggerzone(
scenery.zone.position,
smallest_valid_radius,
scenery.zone.hidden,
scenery.zone.name,
color,
scenery.zone.properties,
)
# DCS only visually shows a scenery object is dead when
# this trigger rule is applied. Otherwise you can kill a
# structure twice.

View File

@@ -297,18 +297,8 @@ class Settings:
page=CAMPAIGN_DOCTRINE_PAGE,
section=GENERAL_SECTION,
default=False,
detail="AI will jettison their fuel tanks as soon as they're empty.",
detail=("AI will jettison their fuel tanks as soon as they're empty."),
)
max_plane_altitude_offset: int = bounded_int_option(
"Maximum randomized altitude offset (x1000 ft) for airplanes.",
page=CAMPAIGN_DOCTRINE_PAGE,
section=GENERAL_SECTION,
min=0,
max=5,
default=2,
detail="Creates a randomized altitude offset for airplanes.",
)
# Doctrine Distances Section
airbase_threat_range: int = bounded_int_option(
"Airbase threat range (nmi)",
page=CAMPAIGN_DOCTRINE_PAGE,
@@ -381,32 +371,6 @@ class Settings:
"planned to known threat zones."
),
)
max_mission_range_planes: int = bounded_int_option(
"Auto-planner maximum mission range for airplanes (NM)",
page=CAMPAIGN_DOCTRINE_PAGE,
section=DOCTRINE_DISTANCES_SECTION,
default=150,
min=150,
max=1000,
detail=(
"The maximum mission distance that's used by the auto-planner for airplanes. "
"This setting won't take effect when a larger "
"range is defined in the airplane's yaml specification."
),
)
max_mission_range_helicopters: int = bounded_int_option(
"Auto-planner maximum mission range for helicopters (NM)",
page=CAMPAIGN_DOCTRINE_PAGE,
section=DOCTRINE_DISTANCES_SECTION,
default=100,
min=50,
max=1000,
detail=(
"The maximum mission distance that's used by the auto-planner for helicopters. "
"This setting won't take effect when a larger "
"range is defined in the helicopter's yaml specification."
),
)
# Pilots and Squadrons
ai_pilot_levelling: bool = boolean_option(
"Allow AI pilot leveling",
@@ -1034,7 +998,6 @@ class Settings:
enable_transfer_cheat: bool = False
enable_runway_state_cheat: bool = False
enable_air_wing_adjustments: bool = False
enable_enemy_buy_sell: bool = False
# LUA Plugins system
plugins: Dict[str, bool] = field(default_factory=dict)

View File

@@ -52,7 +52,6 @@ class AirWing:
heli: bool,
this_turn: bool,
preferred_type: Optional[AircraftType] = None,
ignore_range: bool = False,
) -> list[Squadron]:
airfield_cache = ObjectiveDistanceCache.get_closest_airfields(location)
best_aircraft = AircraftType.priority_list_for_task(task)
@@ -69,7 +68,7 @@ class AirWing:
]
for squadron in squadrons:
if squadron.can_auto_assign_mission(
location, task, size, heli, this_turn, ignore_range
location, task, size, heli, this_turn
):
capable_at_base.append(squadron)
if squadron.aircraft not in best_aircraft:
@@ -101,10 +100,9 @@ class AirWing:
heli: bool,
this_turn: bool,
preferred_type: Optional[AircraftType] = None,
ignore_range: bool = False,
) -> Optional[Squadron]:
for squadron in self.best_squadrons_for(
location, task, size, heli, this_turn, preferred_type, ignore_range
location, task, size, heli, this_turn, preferred_type
):
return squadron
return None

View File

@@ -18,7 +18,7 @@ from game.theater import ParkingType
from .pilot import Pilot, PilotStatus
from ..db.database import Database
from ..radio.radios import RadioFrequency
from ..utils import meters, nautical_miles
from ..utils import meters
if TYPE_CHECKING:
from game import Game
@@ -281,7 +281,6 @@ class Squadron:
size: int,
heli: bool,
this_turn: bool,
ignore_range: bool = False,
) -> bool:
if (
self.location.cptype.name in ["FOB", "FARP"]
@@ -305,23 +304,8 @@ class Squadron:
if heli and task == FlightType.REFUELING:
return False
if ignore_range:
return True
distance_to_target = meters(location.distance_to(self.location))
max_plane_dist = nautical_miles(
self.coalition.game.settings.max_mission_range_planes
)
max_heli_dist = nautical_miles(
self.coalition.game.settings.max_mission_range_helicopters
)
if self.aircraft.helicopter:
return distance_to_target <= max(
self.aircraft.max_mission_range, max_heli_dist
)
return distance_to_target <= max(
self.aircraft.max_mission_range, max_plane_dist
)
return distance_to_target <= self.aircraft.max_mission_range
def operates_from(self, control_point: ControlPoint) -> bool:
if not control_point.can_operate(self.aircraft):

View File

@@ -1021,8 +1021,6 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
# clear the ATO and replan the airlifts with the correct time.
self.ground_unit_orders.process(game, game.conditions.start_time)
self.release_parking_slots()
runway_status = self.runway_status
if runway_status is not None:
runway_status.process_turn()

View File

@@ -12,7 +12,7 @@ from game import Game
from game.factions.faction import Faction
from game.naming import namegen
from game.scenery_group import SceneryGroup
from game.theater import PointWithHeading, PresetLocation
from game.theater import PointWithHeading, PresetLocation, NavalControlPoint
from game.theater.theatergroundobject import (
BuildingGroundObject,
IadsBuildingGroundObject,
@@ -30,8 +30,10 @@ from .theatergroup import IadsGroundGroup, IadsRole, SceneryUnit, TheaterGroup
from ..armedforces.armedforces import ArmedForces
from ..armedforces.forcegroup import ForceGroup
from ..campaignloader.campaignairwingconfig import CampaignAirWingConfig
from ..campaignloader.campaigncarrierconfig import CampaignCarrierConfig
from ..campaignloader.campaigngroundconfig import TgoConfig
from ..data.groups import GroupTask
from ..dcs.shipunittype import ShipUnitType
from ..profiling import logged_duration
from ..settings import Settings
@@ -49,6 +51,7 @@ class GeneratorSettings:
no_player_navy: bool
no_enemy_navy: bool
tgo_config: TgoConfig
carrier_config: CampaignCarrierConfig
squadrons_start_full: bool
@@ -58,7 +61,6 @@ class ModSettings:
a6a_intruder: bool = False
a7e_corsair2: bool = False
f4bc_phantom: bool = False
f9f_panther: bool = False
f15d_baz: bool = False
f_15_idf: bool = False
f_16_idf: bool = False
@@ -73,7 +75,6 @@ class ModSettings:
irondome: bool = False
uh_60l: bool = False
jas39_gripen: bool = False
super_etendard: bool = False
su30_flanker_h: bool = False
su57_felon: bool = False
frenchpack: bool = False
@@ -127,7 +128,7 @@ class GameGenerator:
def should_remove_carrier(self, player: bool) -> bool:
faction = self.player if player else self.enemy
return self.generator_settings.no_carrier or not faction.carrier_names
return self.generator_settings.no_carrier or not faction.carriers
def should_remove_lha(self, player: bool) -> bool:
faction = self.player if player else self.enemy
@@ -223,14 +224,49 @@ class GenericCarrierGroundObjectGenerator(ControlPointGroundObjectGenerator):
carrier = next(self.control_point.ground_objects[-1].units)
carrier.name = carrier_name
def apply_carrier_config(self) -> None:
assert isinstance(self.control_point, NavalControlPoint)
# If the campaign designer has specified a preferred name, use that
# Note that the preferred name needs to exist in the faction, so we
# don't end up with Kuznetsov carriers called CV-59 Forrestal
preferred_name = None
preferred_type = None
carrier_map = self.generator_settings.carrier_config.by_original_name
if ccfg := carrier_map.get(self.control_point.name):
preferred_name = ccfg.preferred_name
preferred_type = ccfg.preferred_type
carrier_unit = self.control_point.ground_objects[0].groups[0].units[0]
carrier_type = preferred_type if preferred_type else carrier_unit.unit_type
assert isinstance(carrier_type, ShipUnitType)
if preferred_type and self.faction.has_access_to_dcs_type(preferred_type.dcs_unit_type):
carrier_unit.type = carrier_type.dcs_unit_type
if (
preferred_name
):
self.control_point.name = preferred_name
else:
# Otherwise pick randomly from the names specified for that particular carrier type
carrier_names = self.faction.carriers.get(carrier_type)
if carrier_names:
self.control_point.name = random.choice(list(carrier_names))
else:
self.control_point.name = carrier_type.display_name
# Prevents duplicate carrier or LHA names in campaigns with more that one of either.
for carrier_type_key in self.faction.carriers:
for carrier_name in self.faction.carriers[carrier_type_key]:
if carrier_name == self.control_point.name:
self.faction.carriers[carrier_type_key].remove(
self.control_point.name
)
class CarrierGroundObjectGenerator(GenericCarrierGroundObjectGenerator):
def generate(self) -> bool:
if not super().generate():
return False
carrier_names = self.faction.carrier_names
if not carrier_names:
carriers = self.faction.carriers
if not carriers:
logging.info(
f"Skipping generation of {self.control_point.name} because "
f"{self.faction_name} has no carriers"
@@ -241,6 +277,7 @@ class CarrierGroundObjectGenerator(GenericCarrierGroundObjectGenerator):
if not unit_group:
logging.error(f"{self.faction_name} has no access to AircraftCarrier")
return False
self.generate_ground_object_from_group(
unit_group,
PresetLocation(
@@ -250,7 +287,7 @@ class CarrierGroundObjectGenerator(GenericCarrierGroundObjectGenerator):
),
GroupTask.AIRCRAFT_CARRIER,
)
self.update_carrier_name(random.choice(list(carrier_names)))
self.apply_carrier_config()
return True
@@ -282,7 +319,7 @@ class LhaGroundObjectGenerator(GenericCarrierGroundObjectGenerator):
),
GroupTask.HELICOPTER_CARRIER,
)
self.update_carrier_name(random.choice(list(lha_names)))
self.apply_carrier_config()
return True

View File

@@ -3,7 +3,7 @@ from pathlib import Path
MAJOR_VERSION = 1
MINOR_VERSION = 3
MICRO_VERSION = 1
MICRO_VERSION = 0
def _optional_build_id_component(path: Path) -> str | None:

View File

@@ -2,7 +2,6 @@ from .SWPack import *
from .a4ec import *
from .a7e import *
from .a6a import *
from .f9f import *
from .f100 import *
from .f104 import *
from .f105 import *
@@ -21,7 +20,6 @@ from .irondome import *
from .jas39 import *
from .ov10a import *
from .spanishnavypack import *
from .super_etendard import *
from .su30 import *
from .su57 import *
from .swedishmilitaryassetspack import *

View File

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

View File

@@ -1,699 +0,0 @@
from typing import Set
from dcs import task
from dcs.planes import PlaneType
from dcs.weapons_data import Weapons
from game.modsupport import planemod
@planemod
class VSN_F9F(PlaneType):
id = "VSN_F9F"
flyable = True
height = 3.73
width = 11.58
length = 11.84
fuel_max = 2310
max_speed = 961.2
category = "Interceptor" # {78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F}
radio_frequency = 127.5
livery_name = "VSN_F9F" # from type
class Pylon1:
Smoke_Generator___red_ = (1, Weapons.Smoke_Generator___red_)
Smoke_Generator___green_ = (1, Weapons.Smoke_Generator___green_)
Smoke_Generator___blue_ = (1, Weapons.Smoke_Generator___blue_)
Smoke_Generator___white_ = (1, Weapons.Smoke_Generator___white_)
Smoke_Generator___yellow_ = (1, Weapons.Smoke_Generator___yellow_)
Smoke_Generator___orange_ = (1, Weapons.Smoke_Generator___orange_)
class Pylon2:
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice = (
2,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT = (
2,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice = (
2,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
2,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos = (
2,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice = (
2,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum = (
2,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk = (
2,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice = (
2,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT = (
2,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice = (
2,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
2,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos = (
2,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice = (
2,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum = (
2,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk = (
2,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk,
)
AN_M64___500lb_GP_Bomb_LD_ = (2, Weapons.AN_M64___500lb_GP_Bomb_LD_)
Mk_82___500lb_GP_Bomb_LD = (2, Weapons.Mk_82___500lb_GP_Bomb_LD)
Mk_81___250lb_GP_Bomb_LD = (2, Weapons.Mk_81___250lb_GP_Bomb_LD)
HVAR__UnGd_Rkt = (2, Weapons.HVAR__UnGd_Rkt)
LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk1__HE = (
2,
Weapons.LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk1__HE,
)
LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk5__HEAT = (
2,
Weapons.LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk5__HEAT,
)
# ERRR <CLEAN>
class Pylon3:
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice = (
3,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT = (
3,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice = (
3,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
3,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos = (
3,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice = (
3,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum = (
3,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk = (
3,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice = (
3,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT = (
3,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice = (
3,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
3,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos = (
3,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice = (
3,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum = (
3,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk = (
3,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk,
)
AN_M64___500lb_GP_Bomb_LD_ = (3, Weapons.AN_M64___500lb_GP_Bomb_LD_)
Mk_82___500lb_GP_Bomb_LD = (3, Weapons.Mk_82___500lb_GP_Bomb_LD)
Mk_81___250lb_GP_Bomb_LD = (3, Weapons.Mk_81___250lb_GP_Bomb_LD)
HVAR__UnGd_Rkt = (3, Weapons.HVAR__UnGd_Rkt)
LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk1__HE = (
3,
Weapons.LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk1__HE,
)
LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk5__HEAT = (
3,
Weapons.LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk5__HEAT,
)
# ERRR <CLEAN>
class Pylon4:
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice = (
4,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT = (
4,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice = (
4,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
4,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos = (
4,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice = (
4,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum = (
4,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk = (
4,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice = (
4,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT = (
4,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice = (
4,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
4,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos = (
4,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice = (
4,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum = (
4,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk = (
4,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk,
)
AN_M64___500lb_GP_Bomb_LD_ = (4, Weapons.AN_M64___500lb_GP_Bomb_LD_)
Mk_82___500lb_GP_Bomb_LD = (4, Weapons.Mk_82___500lb_GP_Bomb_LD)
Mk_81___250lb_GP_Bomb_LD = (4, Weapons.Mk_81___250lb_GP_Bomb_LD)
HVAR__UnGd_Rkt = (4, Weapons.HVAR__UnGd_Rkt)
LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk1__HE = (
4,
Weapons.LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk1__HE,
)
LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk5__HEAT = (
4,
Weapons.LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk5__HEAT,
)
# ERRR <CLEAN>
class Pylon5:
AIM_9B_Sidewinder_IR_AAM = (5, Weapons.AIM_9B_Sidewinder_IR_AAM)
Mk_83___1000lb_GP_Bomb_LD = (5, Weapons.Mk_83___1000lb_GP_Bomb_LD)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice = (
5,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT = (
5,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice = (
5,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
5,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos = (
5,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice = (
5,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum = (
5,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk = (
5,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice = (
5,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT = (
5,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice = (
5,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
5,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos = (
5,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice = (
5,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum = (
5,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk = (
5,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk,
)
M117___750lb_GP_Bomb_LD = (5, Weapons.M117___750lb_GP_Bomb_LD)
AN_M64___500lb_GP_Bomb_LD_ = (5, Weapons.AN_M64___500lb_GP_Bomb_LD_)
Mk_82___500lb_GP_Bomb_LD = (5, Weapons.Mk_82___500lb_GP_Bomb_LD)
Mk_81___250lb_GP_Bomb_LD = (5, Weapons.Mk_81___250lb_GP_Bomb_LD)
HVAR__UnGd_Rkt = (5, Weapons.HVAR__UnGd_Rkt)
# ERRR <CLEAN>
Smoke_Generator___red_ = (5, Weapons.Smoke_Generator___red_)
Smoke_Generator___green_ = (5, Weapons.Smoke_Generator___green_)
Smoke_Generator___blue_ = (5, Weapons.Smoke_Generator___blue_)
Smoke_Generator___white_ = (5, Weapons.Smoke_Generator___white_)
Smoke_Generator___yellow_ = (5, Weapons.Smoke_Generator___yellow_)
Smoke_Generator___orange_ = (5, Weapons.Smoke_Generator___orange_)
class Pylon6:
Smoke_Generator___red_ = (6, Weapons.Smoke_Generator___red_)
Smoke_Generator___green_ = (6, Weapons.Smoke_Generator___green_)
Smoke_Generator___blue_ = (6, Weapons.Smoke_Generator___blue_)
Smoke_Generator___white_ = (6, Weapons.Smoke_Generator___white_)
Smoke_Generator___yellow_ = (6, Weapons.Smoke_Generator___yellow_)
Smoke_Generator___orange_ = (6, Weapons.Smoke_Generator___orange_)
class Pylon7:
AIM_9B_Sidewinder_IR_AAM = (7, Weapons.AIM_9B_Sidewinder_IR_AAM)
Mk_83___1000lb_GP_Bomb_LD = (7, Weapons.Mk_83___1000lb_GP_Bomb_LD)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice = (
7,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT = (
7,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice = (
7,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
7,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos = (
7,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice = (
7,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum = (
7,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk = (
7,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice = (
7,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT = (
7,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice = (
7,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
7,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos = (
7,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice = (
7,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum = (
7,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk = (
7,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk,
)
M117___750lb_GP_Bomb_LD = (7, Weapons.M117___750lb_GP_Bomb_LD)
AN_M64___500lb_GP_Bomb_LD_ = (7, Weapons.AN_M64___500lb_GP_Bomb_LD_)
Mk_82___500lb_GP_Bomb_LD = (7, Weapons.Mk_82___500lb_GP_Bomb_LD)
Mk_81___250lb_GP_Bomb_LD = (7, Weapons.Mk_81___250lb_GP_Bomb_LD)
HVAR__UnGd_Rkt = (7, Weapons.HVAR__UnGd_Rkt)
# ERRR <CLEAN>
Smoke_Generator___red_ = (7, Weapons.Smoke_Generator___red_)
Smoke_Generator___green_ = (7, Weapons.Smoke_Generator___green_)
Smoke_Generator___blue_ = (7, Weapons.Smoke_Generator___blue_)
Smoke_Generator___white_ = (7, Weapons.Smoke_Generator___white_)
Smoke_Generator___yellow_ = (7, Weapons.Smoke_Generator___yellow_)
Smoke_Generator___orange_ = (7, Weapons.Smoke_Generator___orange_)
class Pylon8:
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice = (
8,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT = (
8,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice = (
8,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
8,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos = (
8,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice = (
8,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum = (
8,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk = (
8,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice = (
8,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT = (
8,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice = (
8,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
8,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos = (
8,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice = (
8,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum = (
8,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk = (
8,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk,
)
AN_M64___500lb_GP_Bomb_LD_ = (8, Weapons.AN_M64___500lb_GP_Bomb_LD_)
Mk_82___500lb_GP_Bomb_LD = (8, Weapons.Mk_82___500lb_GP_Bomb_LD)
Mk_81___250lb_GP_Bomb_LD = (8, Weapons.Mk_81___250lb_GP_Bomb_LD)
HVAR__UnGd_Rkt = (8, Weapons.HVAR__UnGd_Rkt)
LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk1__HE = (
8,
Weapons.LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk1__HE,
)
LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk5__HEAT = (
8,
Weapons.LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk5__HEAT,
)
# ERRR <CLEAN>
class Pylon9:
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice = (
9,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT = (
9,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice = (
9,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
9,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos = (
9,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice = (
9,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum = (
9,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk = (
9,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice = (
9,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT = (
9,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice = (
9,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
9,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos = (
9,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice = (
9,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum = (
9,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk = (
9,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk,
)
AN_M64___500lb_GP_Bomb_LD_ = (9, Weapons.AN_M64___500lb_GP_Bomb_LD_)
Mk_82___500lb_GP_Bomb_LD = (9, Weapons.Mk_82___500lb_GP_Bomb_LD)
Mk_81___250lb_GP_Bomb_LD = (9, Weapons.Mk_81___250lb_GP_Bomb_LD)
HVAR__UnGd_Rkt = (9, Weapons.HVAR__UnGd_Rkt)
LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk1__HE = (
9,
Weapons.LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk1__HE,
)
LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk5__HEAT = (
9,
Weapons.LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk5__HEAT,
)
# ERRR <CLEAN>
class Pylon10:
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice = (
10,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT = (
10,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice = (
10,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
10,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos = (
10,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice = (
10,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum = (
10,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum,
)
LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk = (
10,
Weapons.LAU_68_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice = (
10,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk1__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT = (
10,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk5__HEAT,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice = (
10,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_Mk61__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
10,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos = (
10,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M156__Wht_Phos,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice = (
10,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_WTU_1_B__Practice,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum = (
10,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M257__Para_Illum,
)
LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk = (
10,
Weapons.LAU_131_pod___7_x_2_75_Hydra__UnGd_Rkts_M274__Practice_Smk,
)
AN_M64___500lb_GP_Bomb_LD_ = (10, Weapons.AN_M64___500lb_GP_Bomb_LD_)
Mk_82___500lb_GP_Bomb_LD = (10, Weapons.Mk_82___500lb_GP_Bomb_LD)
Mk_81___250lb_GP_Bomb_LD = (10, Weapons.Mk_81___250lb_GP_Bomb_LD)
HVAR__UnGd_Rkt = (10, Weapons.HVAR__UnGd_Rkt)
LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk1__HE = (
10,
Weapons.LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk1__HE,
)
LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk5__HEAT = (
10,
Weapons.LAU_3_pod___19_x_2_75_FFAR__UnGd_Rkts_Mk5__HEAT,
)
# ERRR <CLEAN>
class Pylon11:
Smoke_Generator___red_ = (11, Weapons.Smoke_Generator___red_)
Smoke_Generator___green_ = (11, Weapons.Smoke_Generator___green_)
Smoke_Generator___blue_ = (11, Weapons.Smoke_Generator___blue_)
Smoke_Generator___white_ = (11, Weapons.Smoke_Generator___white_)
Smoke_Generator___yellow_ = (11, Weapons.Smoke_Generator___yellow_)
Smoke_Generator___orange_ = (11, Weapons.Smoke_Generator___orange_)
pylons: Set[int] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
tasks = [
task.CAP,
task.CAS,
task.Escort,
task.FighterSweep,
task.GroundAttack,
task.Intercept,
task.AntishipStrike,
]
task_default = task.GroundAttack

View File

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

View File

@@ -1,144 +0,0 @@
from dcs import task
from dcs.planes import PlaneType
from dcs.weapons_data import Weapons
from game.modsupport import planemod
from pydcs_extensions.weapon_injector import inject_weapons
class WeaponsSEM:
Fuel_Tank_1100_Liter = {
"clsid": "{SEM1100_PTB}",
"name": "Fuel Tank 1100 Liter",
"weight": 1150,
}
Fuel_Tank_625_Liter = {
"clsid": "{SEM625_PTB}",
"name": "Fuel Tank 625 Liter",
"weight": 1150,
}
inject_weapons(WeaponsSEM)
@planemod
class VSN_SEM(PlaneType):
id = "VSN_SEM"
flyable = True
height = 4
width = 13.05
length = 18
fuel_max = 6103
max_speed = 2649.996
chaff = 90
flare = 45
charge_total = 180
chaff_charge_size = 1
flare_charge_size = 2
category = "Interceptor" # {78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F}
radio_frequency = 127.5
livery_name = "VSN_SEM" # from type
class Pylon1:
Smokewinder___red = (1, Weapons.Smokewinder___red)
Smokewinder___green = (1, Weapons.Smokewinder___green)
Smokewinder___blue = (1, Weapons.Smokewinder___blue)
Smokewinder___white = (1, Weapons.Smokewinder___white)
Smokewinder___yellow = (1, Weapons.Smokewinder___yellow)
AN_ASQ_T50_TCTS_Pod___ACMI_Pod = (1, Weapons.AN_ASQ_T50_TCTS_Pod___ACMI_Pod)
Mk_81___250lb_GP_Bomb_LD = (1, Weapons.Mk_81___250lb_GP_Bomb_LD)
Mk_82___500lb_GP_Bomb_LD = (1, Weapons.Mk_82___500lb_GP_Bomb_LD)
R550_Magic_2_IR_AAM = (1, Weapons.R550_Magic_2_IR_AAM)
LAU_61_pod___19_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
1,
Weapons.LAU_61_pod___19_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
ALQ_131___ECM_Pod = (1, Weapons.ALQ_131___ECM_Pod)
AIM_9M_Sidewinder_IR_AAM = (1, Weapons.AIM_9M_Sidewinder_IR_AAM)
AIM_9P_Sidewinder_IR_AAM = (1, Weapons.AIM_9P_Sidewinder_IR_AAM)
class Pylon2:
AGM_65K___Maverick_K__CCD_Imp_ASM_ = (
2,
Weapons.AGM_65K___Maverick_K__CCD_Imp_ASM_,
)
GBU_12___500lb_Laser_Guided_Bomb = (2, Weapons.GBU_12___500lb_Laser_Guided_Bomb)
LAU_61_pod___19_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
2,
Weapons.LAU_61_pod___19_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
Mk_81___250lb_GP_Bomb_LD = (2, Weapons.Mk_81___250lb_GP_Bomb_LD)
Mk_82___500lb_GP_Bomb_LD = (2, Weapons.Mk_82___500lb_GP_Bomb_LD)
Fuel_Tank_1100_Liter = (2, WeaponsSEM.Fuel_Tank_1100_Liter)
Fuel_Tank_625_Liter = (2, WeaponsSEM.Fuel_Tank_625_Liter)
# ERRR <CLEAN>
class Pylon5:
L_081_Fantasmagoria_ELINT_pod = (5, Weapons.L_081_Fantasmagoria_ELINT_pod)
class Pylon6:
MER2_with_2_x_Mk_82___500lb_GP_Bombs_LD = (
6,
Weapons.MER2_with_2_x_Mk_82___500lb_GP_Bombs_LD,
)
AGM_65K___Maverick_K__CCD_Imp_ASM_ = (
6,
Weapons.AGM_65K___Maverick_K__CCD_Imp_ASM_,
)
# ERRR <CLEAN>
class Pylon10:
AGM_65K___Maverick_K__CCD_Imp_ASM_ = (
10,
Weapons.AGM_65K___Maverick_K__CCD_Imp_ASM_,
)
GBU_12___500lb_Laser_Guided_Bomb = (
10,
Weapons.GBU_12___500lb_Laser_Guided_Bomb,
)
LAU_61_pod___19_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
10,
Weapons.LAU_61_pod___19_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
Mk_81___250lb_GP_Bomb_LD = (10, Weapons.Mk_81___250lb_GP_Bomb_LD)
Mk_82___500lb_GP_Bomb_LD = (10, Weapons.Mk_82___500lb_GP_Bomb_LD)
Fuel_Tank_1100_Liter = (10, WeaponsSEM.Fuel_Tank_1100_Liter)
Fuel_Tank_625_Liter = (10, WeaponsSEM.Fuel_Tank_625_Liter)
class Pylon11:
Smokewinder___red = (11, Weapons.Smokewinder___red)
Smokewinder___green = (11, Weapons.Smokewinder___green)
Smokewinder___blue = (11, Weapons.Smokewinder___blue)
Smokewinder___white = (11, Weapons.Smokewinder___white)
Smokewinder___yellow = (11, Weapons.Smokewinder___yellow)
AN_ASQ_T50_TCTS_Pod___ACMI_Pod = (11, Weapons.AN_ASQ_T50_TCTS_Pod___ACMI_Pod)
Mk_81___250lb_GP_Bomb_LD = (11, Weapons.Mk_81___250lb_GP_Bomb_LD)
Mk_82___500lb_GP_Bomb_LD = (11, Weapons.Mk_82___500lb_GP_Bomb_LD)
R550_Magic_2_IR_AAM = (11, Weapons.R550_Magic_2_IR_AAM)
LAU_61_pod___19_x_2_75_Hydra__UnGd_Rkts_M151__HE = (
11,
Weapons.LAU_61_pod___19_x_2_75_Hydra__UnGd_Rkts_M151__HE,
)
ALQ_131___ECM_Pod = (11, Weapons.ALQ_131___ECM_Pod)
AIM_9M_Sidewinder_IR_AAM = (11, Weapons.AIM_9M_Sidewinder_IR_AAM)
AIM_9P_Sidewinder_IR_AAM = (11, Weapons.AIM_9P_Sidewinder_IR_AAM)
pylons = {1, 2, 3, 5, 6, 9, 10, 11}
tasks = [
task.CAP,
task.Escort,
task.FighterSweep,
task.Intercept,
task.Reconnaissance,
task.GroundAttack,
task.CAS,
task.AFAC,
task.RunwayAttack,
]
task_default = task.FighterSweep

View File

@@ -317,6 +317,7 @@ def create_game(
no_player_navy=False,
no_enemy_navy=False,
tgo_config=campaign.load_ground_forces_config(),
carrier_config=campaign.load_carrier_config(),
),
ModSettings(
a4_skyhawk=False,
@@ -325,7 +326,6 @@ def create_game(
fa_18efg=False,
fa18ef_tanker=False,
f4bc_phantom=False,
f9f_panther=False,
f22_raptor=False,
f84g_thunderjet=False,
f100_supersabre=False,

View File

@@ -141,7 +141,6 @@ class QFlightList(QListView):
)
return
self.package_model.add_flight(clone)
EventStream.put_nowait(GameUpdateEvents().new_flight(clone))
def cancel_or_abort_flight(self, index: QModelIndex) -> None:
self.package_model.cancel_or_abort_flight_at_index(index)
@@ -339,10 +338,6 @@ class QPackageList(QListView):
)
return
self.ato_model.add_package(clone)
events = GameUpdateEvents()
for f in clone.flights:
events.new_flight(f)
EventStream.put_nowait(events)
def delete_package(self, index: QModelIndex) -> None:
self.ato_model.cancel_or_abort_package_at_index(index)

View File

@@ -97,7 +97,6 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
wpt.description = f"Friendly unit: {target.name}"
else:
wpt.description = f"Enemy unit: {target.name}"
wpt.waypoint_type = FlightWaypointType.TARGET_POINT
i = add_model_item(i, model, wpt.pretty_name, wpt)
if self.include_airbases:

View File

@@ -108,7 +108,7 @@ class QLiberationWindow(QMainWindow):
if last_save_file:
logging.info("Loading last saved game : " + str(last_save_file))
game = persistency.load_game(last_save_file)
game = self.migrate_game(game, last_save_file)
self.migrate_game(game, last_save_file)
self.onGameGenerated(game)
self.updateWindowTitle(last_save_file if game else None)
else:
@@ -316,7 +316,7 @@ class QLiberationWindow(QMainWindow):
)
if file is not None and file[0] != "":
game = persistency.load_game(file[0])
game = self.migrate_game(game, file[0])
self.migrate_game(game, file[0])
GameUpdateSignal.get_instance().game_loaded.emit(game)
self.updateWindowTitle(file[0])
@@ -324,23 +324,15 @@ class QLiberationWindow(QMainWindow):
def migrate_game(self, game, path):
if game:
is_liberation = ".liberation" in path
try:
Migrator(game, is_liberation)
return game
except Exception:
self.incompatible_save_popup(path)
Migrator(game, is_liberation)
else:
self.incompatible_save_popup(path)
return None
def incompatible_save_popup(self, path):
relative_path = Path(path)
QMessageBox.critical(
self,
"Incompatible save",
"Incompatible save file detected, please report the issue on GitHub or Discord.\n"
f"Make sure to include the campaign that fails to load, i.e.:\n\n{relative_path}",
)
relative_path = Path(path)
QMessageBox.critical(
self,
"Incompatible save",
"Incompatible save file detected, please report the issue on GitHub or Discord.\n"
f"Make sure to include the campaign that fails to load, i.e.:\n\n{relative_path}",
)
def saveGame(self):
logging.info("Saving game")

View File

@@ -239,7 +239,5 @@ class QWaitingForMissionResultWindow(QDialog):
self.sim_controller.set_game(self.game)
for _, f in self.game.db.flights.objects.items():
f.state.reinitialize(self.game.conditions.start_time)
for cp in self.game.theater.controlpoints:
cp.release_parking_slots()
GameUpdateSignal.get_instance().updateGame(self.game)
self.close()

View File

@@ -188,8 +188,7 @@ class QGroundObjectTemplateLayout(QGroupBox):
@property
def affordable(self) -> bool:
coalition = self.ground_object.coalition
return self.cost <= coalition.budget or self.game.turn == 0
return self.cost <= self.game.blue.budget
def add_theater_group(
self, group_name: str, force_group: ForceGroup, groups: list[TgoLayoutUnitGroup]
@@ -227,8 +226,7 @@ class QGroundObjectTemplateLayout(QGroupBox):
self.game.theater.heading_to_conflict_from(self.ground_object.position)
or self.ground_object.heading
)
coalition = self.ground_object.coalition
coalition.budget -= self.cost if self.game.turn else 0
self.game.blue.budget -= self.cost
self.ground_object.groups = []
for group_name, groups in self.layout_model.groups.items():
for group in groups:
@@ -284,8 +282,7 @@ class QGroundObjectBuyMenu(QDialog):
if not tasks:
tasks = role.tasks
coalition = ground_object.coalition
for group in coalition.armed_forces.groups_for_tasks(tasks):
for group in game.blue.armed_forces.groups_for_tasks(tasks):
self.force_group_selector.addItem(group.name, userData=group)
self.force_group_selector.setEnabled(self.force_group_selector.count() > 1)
self.force_group_selector.adjustSize()

View File

@@ -109,16 +109,10 @@ class QGroundObjectMenu(QDialog):
self.actionLayout.addWidget(self.sell_all_button)
self.actionLayout.addWidget(self.buy_replace)
if self.show_buy_sell_actions and self.ground_object.purchasable:
if self.cp.captured and self.ground_object.purchasable:
self.mainLayout.addLayout(self.actionLayout)
self.setLayout(self.mainLayout)
@property
def show_buy_sell_actions(self) -> bool:
buysell_allowed = self.game.settings.enable_enemy_buy_sell
buysell_allowed |= self.cp.captured
return buysell_allowed
def doLayout(self):
self.update_total_value()
self.intelBox = QGroupBox("Units :")
@@ -246,7 +240,7 @@ class QGroundObjectMenu(QDialog):
self.actionLayout.addWidget(self.sell_all_button)
self.actionLayout.addWidget(self.buy_replace)
if self.show_buy_sell_actions and self.ground_object.purchasable:
if self.cp.captured and self.ground_object.purchasable:
self.mainLayout.addLayout(self.actionLayout)
except Exception as e:
logging.exception(e)
@@ -282,8 +276,7 @@ class QGroundObjectMenu(QDialog):
def sell_all(self):
self.update_total_value()
coalition = self.ground_object.coalition
coalition.budget += self.total_value
self.game.blue.budget += self.total_value
self.ground_object.groups = []
self.update_game()
@@ -303,10 +296,7 @@ class QGroundObjectMenu(QDialog):
for package in self.game.ato_for(player=False).packages
):
# Replan if the tgo was a target of the redfor
coalition = self.ground_object.coalition
self.game.initialize_turn(
events, for_red=coalition.player, for_blue=not coalition.player
)
self.game.initialize_turn(events, for_red=True, for_blue=False)
EventStream.put_nowait(events)
GameUpdateSignal.get_instance().updateGame(self.game)
# Refresh the dialog

View File

@@ -223,7 +223,7 @@ class QAutoCreateDialog(QDialog):
self.game.settings,
)
now = self.package_model.game_model.sim_controller.current_time_in_sim
package = pff.plan_mission(pm, 1, now, tracer, ignore_range=True)
package = pff.plan_mission(pm, 1, now, tracer)
if package:
package.set_tot_asap(now)
self.package = package

View File

@@ -86,7 +86,7 @@ class QFlightWaypointList(QTableView):
self.model.setItem(row, 0, QWaypointItem(waypoint, row))
altitude = round(waypoint.alt.feet)
altitude = int(waypoint.alt.feet)
altitude_item = QStandardItem(f"{altitude}")
altitude_item.setEditable(True)
self.model.setItem(row, 1, altitude_item)

View File

@@ -162,9 +162,9 @@ class QFlightWaypointTab(QFrame):
assert isinstance(self.flight.flight_plan, CustomFlightPlan)
self.flight.flight_plan.layout.custom_waypoints.remove(waypoint)
def confirm_degrade(self, parent: Optional[QWidget] = None) -> bool:
def confirm_degrade(self) -> bool:
result = QMessageBox.warning(
parent if parent else self,
self,
"Degrade flight-plan?",
"Deleting the selected waypoint(s) will require degradation to a custom flight-plan. "
"A custom flight-plan will no longer respect the TOTs of the package.<br><br>"
@@ -184,6 +184,12 @@ class QFlightWaypointTab(QFrame):
def on_waypoints_added(self, waypoints: Iterable[FlightWaypoint]) -> None:
if not waypoints:
return
if not self.flight.flight_plan.is_custom:
confirmed = self.confirm_degrade()
if not confirmed:
return
self.degrade_to_custom_flight_plan()
assert isinstance(self.flight.flight_plan, CustomFlightPlan)
self.flight.flight_plan.layout.custom_waypoints.extend(waypoints)
self.add_rows(len(list(waypoints)))

View File

@@ -84,10 +84,10 @@ class NewGameWizard(QtWidgets.QWizard):
no_player_navy=self.field("no_player_navy"),
no_enemy_navy=self.field("no_enemy_navy"),
tgo_config=campaign.load_ground_forces_config(),
carrier_config=campaign.load_carrier_config(),
squadrons_start_full=self.field("squadrons_start_full"),
)
mod_settings = ModSettings(
f9f_panther=self.field("f9f_panther"),
a4_skyhawk=self.field("a4_skyhawk"),
a6a_intruder=self.field("a6a_intruder"),
a7e_corsair2=self.field("a7e_corsair2"),
@@ -106,7 +106,6 @@ class NewGameWizard(QtWidgets.QWizard):
irondome=self.field("irondome"),
uh_60l=self.field("uh_60l"),
jas39_gripen=self.field("jas39_gripen"),
super_etendard=self.field("super_etendard"),
su30_flanker_h=self.field("su30_flanker_h"),
su57_felon=self.field("su57_felon"),
ov10a_bronco=self.field("ov10a_bronco"),

View File

@@ -100,8 +100,6 @@ class GeneratorOptions(QtWidgets.QWizardPage):
self.registerField("uh_60l", self.uh_60l)
self.f4bc_phantom = QtWidgets.QCheckBox()
self.registerField("f4bc_phantom", self.f4bc_phantom)
self.f9f_panther = QtWidgets.QCheckBox()
self.registerField("f9f_panther", self.f9f_panther)
self.f15d_baz = QtWidgets.QCheckBox()
self.registerField("f15d_baz", self.f15d_baz)
self.f_15_idf = QtWidgets.QCheckBox()
@@ -124,8 +122,6 @@ class GeneratorOptions(QtWidgets.QWizardPage):
self.registerField("f105_thunderchief", self.f105_thunderchief)
self.jas39_gripen = QtWidgets.QCheckBox()
self.registerField("jas39_gripen", self.jas39_gripen)
self.super_etendard = QtWidgets.QCheckBox()
self.registerField("super_etendard", self.super_etendard)
self.su30_flanker_h = QtWidgets.QCheckBox()
self.registerField("su30_flanker_h", self.su30_flanker_h)
self.su57_felon = QtWidgets.QCheckBox()
@@ -154,7 +150,6 @@ class GeneratorOptions(QtWidgets.QWizardPage):
modLayout_row = 1
mod_pairs = [
("F9F Panther (v2.8.7.101)", self.f9f_panther),
("A-4E Skyhawk (v2.2.0)", self.a4_skyhawk),
("A-6A Intruder (v2.7.5.01)", self.a6a_intruder),
("A-7E Corsair II", self.a7e_corsair2),
@@ -175,7 +170,6 @@ class GeneratorOptions(QtWidgets.QWizardPage):
("Swedish Military Assets pack (1.10)", self.swedishmilitaryassetspack),
("JAS 39 Gripen (v1.8.5-beta)", self.jas39_gripen),
("OV-10A Bronco", self.ov10a_bronco),
("Super Étendard (v2.5.5)", self.super_etendard),
("Su-30 Flanker-H (V2.7.3 beta)", self.su30_flanker_h),
("Su-57 Felon (build-04)", self.su57_felon),
("UH-60L Black Hawk (v1.3.1)", self.uh_60l),
@@ -218,7 +212,6 @@ class GeneratorOptions(QtWidgets.QWizardPage):
self.no_enemy_navy.setChecked(s.get("no_enemy_navy", False))
self.squadrons_start_full.setChecked(s.get("squadron_start_full", False))
self.f9f_panther.setChecked(s.get("f9f_panther", False))
self.a4_skyhawk.setChecked(s.get("a4_skyhawk", False))
self.a6a_intruder.setChecked(s.get("a6a_intruder", False))
self.a7e_corsair2.setChecked(s.get("a7e_corsair2", False))
@@ -235,7 +228,6 @@ class GeneratorOptions(QtWidgets.QWizardPage):
self.f104_starfighter.setChecked(s.get("f104_starfighter", False))
self.f105_thunderchief.setChecked(s.get("f105_thunderchief", False))
self.jas39_gripen.setChecked(s.get("jas39_gripen", False))
self.super_etendard.setChecked(s.get("super_etendard", False))
self.su30_flanker_h.setChecked(s.get("su30_flanker_h", False))
self.su57_felon.setChecked(s.get("su57_felon", False))
self.ov10a_bronco.setChecked(s.get("ov10a_bronco", False))

View File

@@ -113,15 +113,6 @@ class CheatSettingsBox(QGroupBox):
)
self.main_layout.addLayout(self.air_wing_cheat)
# Buy/Sell actions for OPFOR
self.opfor_buysell_checkbox = QCheckBox()
self.opfor_buysell_checkbox.setChecked(sc.settings.enable_enemy_buy_sell)
self.opfor_buysell_checkbox.toggled.connect(apply_settings)
self.redfor_buysell_cheat = QLabeledWidget(
"Enable OPFOR Buy/Sell actions Cheat:", self.opfor_buysell_checkbox
)
self.main_layout.addLayout(self.redfor_buysell_cheat)
@property
def show_red_ato(self) -> bool:
return self.red_ato_checkbox.isChecked()
@@ -146,10 +137,6 @@ class CheatSettingsBox(QGroupBox):
def enable_air_wing_cheats(self) -> bool:
return self.air_wing_adjustments_checkbox.isChecked()
@property
def enable_redfor_buysell(self) -> bool:
return self.opfor_buysell_checkbox.isChecked()
class AutoSettingsLayout(QGridLayout):
def __init__(
@@ -508,7 +495,6 @@ class QSettingsWidget(QtWidgets.QWizardPage, SettingsContainer):
self.settings.enable_air_wing_adjustments = (
self.cheat_options.enable_air_wing_cheats
)
self.settings.enable_enemy_buy_sell = self.cheat_options.enable_redfor_buysell
if self.game:
events = GameUpdateEvents()
@@ -535,15 +521,6 @@ class QSettingsWidget(QtWidgets.QWizardPage, SettingsContainer):
self.cheat_options.transfer_cheat_checkbox.setChecked(
self.settings.enable_transfer_cheat
)
self.cheat_options.base_runway_state_cheat_checkbox.setChecked(
self.settings.enable_runway_state_cheat
)
self.cheat_options.air_wing_adjustments_checkbox.setChecked(
self.settings.enable_air_wing_adjustments
)
self.cheat_options.opfor_buysell_checkbox.setChecked(
self.settings.enable_enemy_buy_sell
)
self.pluginsPage.update_from_settings()
self.pluginsOptionsPage.update_from_settings()

View File

@@ -81,6 +81,14 @@ settings:
#default="Full"
carriers:
Naval-1:
preferred_name: CVN-74
preferred_type: CVN-74 John C. Stennis
Naval-2:
preferred_name: The Harry
preferred_type: CVN-75 Harry S. Truman
#Squadrons and order of battle settings
squadrons:
@@ -156,7 +164,7 @@ squadrons:
aircraft:
- CH-53E
size: 2
#CV-59 Forrestal
#CV-59 Forrestal
Naval-2:
- primary: AEW&C
aircraft:

View File

@@ -38,12 +38,12 @@ squadrons:
#Port Stanley
1:
- primary: DEAD
secondary: any
secondary: air-to-ground
aircraft:
- AH-64D Apache Longbow
size: 6
- primary: BAI
secondary: any
secondary: air-to-ground
aircraft:
- AH-64D Apache Longbow
size: 6
@@ -56,15 +56,14 @@ squadrons:
#San Carlos FOB
3:
- primary: BAI
secondary: any
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
size: 6
#Goose Green
24:
- primary: DEAD
secondary: any
secondary: air-to-ground
aircraft:
- Ka-50 Hokum III
- Ka-50 Hokum (Blackshark 3)
size: 6

View File

@@ -95,11 +95,6 @@ squadrons:
aircraft:
- CH-47D
size: 20
- primary: BAI
secondary: any
aircraft:
- AH-64D Apache Longbow
size: 8
- primary: Air Assault
secondary: any
aircraft:
@@ -120,9 +115,8 @@ squadrons:
# Melez (30)
5:
- primary: CAS
secondary: any
secondary: air-to-ground
aircraft:
- Ka-50 Hokum III
- Ka-50 Hokum (Blackshark 3)
size: 4
- primary: BAI

View File

@@ -53,7 +53,7 @@ squadrons:
- A-10C Thunderbolt II (Suite 7)
size: 8
- primary: CAS
secondary: any
secondary: air-to-ground
aircraft:
- AH-64D Apache Longbow
size: 10
@@ -88,13 +88,12 @@ squadrons:
# Creech
1:
- primary: CAS
secondary: any
secondary: air-to-ground
aircraft:
- Ka-50 Hokum III
- Ka-50 Hokum (Blackshark 3)
size: 8
- primary: Air Assault
secondary: any
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
size: 4

View File

@@ -35,11 +35,6 @@ squadrons:
aircraft:
- F-15C Eagle
size: 8
- primary: CAS
secondary: any
aircraft:
- AH-64D Apache Longbow
size: 8
- primary: Refueling
aircraft:
- KC-135 Stratotanker
@@ -147,15 +142,14 @@ squadrons:
#Ushuaia
7:
- primary: CAS
secondary: any
secondary: air-to-ground
aircraft:
- Ka-50 Hokum III
- Ka-50 Hokum (Blackshark 3)
size: 8
#Ushuaia Helo Port
8:
- primary: Air Assault
secondary: any
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
size: 8
size: 8

View File

@@ -48,7 +48,7 @@ squadrons:
#Tarawa Class LHA
Blue-LHA:
- primary: DEAD
secondary: any
secondary: air-to-ground
aircraft:
- AH-64D Apache Longbow
size: 12
@@ -72,7 +72,7 @@ squadrons:
#Akrotiri
44:
- primary: BAI
secondary: any
secondary: air-to-ground
aircraft:
- AH-1W SuperCobra
size: 4
@@ -81,8 +81,8 @@ squadrons:
aircraft:
- OH-58D Kiowa Warrior
size: 4
- primary: Transport
secondary: any
- primary: Air Assault
secondary: air-to-ground
aircraft:
- UH-60A
size: 4
@@ -95,7 +95,6 @@ squadrons:
#Ercan
49:
- primary: Transport
secondary: any
aircraft:
- CH-47D
size: 6

View File

@@ -100,7 +100,7 @@ squadrons:
#Qeshm Island (12)
13:
- primary: Air Assault
secondary: any
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
size: 12

View File

@@ -80,7 +80,7 @@ squadrons:
- F-16CM Fighting Falcon (Block 50)
size: 28
- primary: CAS
secondary: any
secondary: air-to-ground
aircraft:
- AH-64D Apache Longbow
size: 4
@@ -93,13 +93,13 @@ squadrons:
- H-6J Badger
size: 16
- primary: BAI
secondary: any
secondary: air-to-ground
aircraft:
- AH-1W SuperCobra
- Su-25 Frogfoot
size: 16
- primary: CAS
secondary: any
secondary: air-to-ground
aircraft:
- OH-58D Kiowa Warrior
- Mi-24P Hind-F

View File

@@ -57,7 +57,7 @@ squadrons:
size: 20
Squadrons from Incirlik:
- primary: CAS
secondary: any
secondary: air-to-ground
aircraft:
- AH-64D Apache Longbow
size: 4

View File

@@ -1,227 +0,0 @@
local unitPayloads = {
["name"] = "VSN_F9F",
["payloads"] = {
[1] = {
["name"] = "STRIKE",
["pylons"] = {
[1] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 2,
},
[2] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 3,
},
[3] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 4,
},
[4] = {
["CLSID"] = "{7A44FF09-527C-4B7E-B42B-3F111CFE50FB}",
["num"] = 5,
},
[5] = {
["CLSID"] = "{7A44FF09-527C-4B7E-B42B-3F111CFE50FB}",
["num"] = 7,
},
[6] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 8,
},
[7] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 9,
},
[8] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 10,
},
},
["tasks"] = {
[1] = 31,
},
},
[2] = {
["name"] = "CAP",
["pylons"] = {
[1] = {
["CLSID"] = "{AIM-9B}",
["num"] = 5,
},
[2] = {
["CLSID"] = "{AIM-9B}",
["num"] = 7,
},
},
["tasks"] = {
[1] = 31,
},
},
[3] = {
["name"] = "CAS",
["pylons"] = {
[1] = {
["CLSID"] = "{319293F2-392C-4617-8315-7C88C22AF7C4}",
["num"] = 2,
},
[2] = {
["CLSID"] = "{319293F2-392C-4617-8315-7C88C22AF7C4}",
["num"] = 3,
},
[3] = {
["CLSID"] = "{LAU3_FFAR_MK5HEAT}",
["num"] = 4,
},
[4] = {
["CLSID"] = "{319293F2-392C-4617-8315-7C88C22AF7C4}",
["num"] = 5,
},
[5] = {
["CLSID"] = "{319293F2-392C-4617-8315-7C88C22AF7C4}",
["num"] = 7,
},
[6] = {
["CLSID"] = "{LAU3_FFAR_MK5HEAT}",
["num"] = 8,
},
[7] = {
["CLSID"] = "{319293F2-392C-4617-8315-7C88C22AF7C4}",
["num"] = 9,
},
[8] = {
["CLSID"] = "{319293F2-392C-4617-8315-7C88C22AF7C4}",
["num"] = 10,
},
},
["tasks"] = {
[1] = 31,
},
},
[4] = {
["name"] = "SEAD",
["pylons"] = {
[1] = {
["CLSID"] = "{HVAR}",
["num"] = 2,
},
[2] = {
["CLSID"] = "{HVAR}",
["num"] = 3,
},
[3] = {
["CLSID"] = "{HVAR}",
["num"] = 4,
},
[4] = {
["CLSID"] = "{HVAR}",
["num"] = 5,
},
[5] = {
["CLSID"] = "{HVAR}",
["num"] = 7,
},
[6] = {
["CLSID"] = "{HVAR}",
["num"] = 8,
},
[7] = {
["CLSID"] = "{HVAR}",
["num"] = 9,
},
[8] = {
["CLSID"] = "{HVAR}",
["num"] = 10,
},
},
["tasks"] = {
[1] = 31,
},
},
[5] = {
["name"] = "ANTISHIP",
["pylons"] = {
[1] = {
["CLSID"] = "{HVAR}",
["num"] = 2,
},
[2] = {
["CLSID"] = "{HVAR}",
["num"] = 3,
},
[3] = {
["CLSID"] = "{HVAR}",
["num"] = 4,
},
[4] = {
["CLSID"] = "{HVAR}",
["num"] = 5,
},
[5] = {
["CLSID"] = "{HVAR}",
["num"] = 7,
},
[6] = {
["CLSID"] = "{HVAR}",
["num"] = 8,
},
[7] = {
["CLSID"] = "{HVAR}",
["num"] = 9,
},
[8] = {
["CLSID"] = "{HVAR}",
["num"] = 10,
},
},
["tasks"] = {
[1] = 31,
},
},
[6] = {
["displayName"] = "BAI",
["name"] = "BAI",
["pylons"] = {
[1] = {
["CLSID"] = "{HVAR}",
["num"] = 2,
},
[2] = {
["CLSID"] = "{HVAR}",
["num"] = 3,
},
[3] = {
["CLSID"] = "{HVAR}",
["num"] = 4,
},
[4] = {
["CLSID"] = "{HVAR}",
["num"] = 5,
},
[5] = {
["CLSID"] = "{HVAR}",
["num"] = 7,
},
[6] = {
["CLSID"] = "{HVAR}",
["num"] = 8,
},
[7] = {
["CLSID"] = "{HVAR}",
["num"] = 9,
},
[8] = {
["CLSID"] = "{HVAR}",
["num"] = 10,
},
},
["tasks"] = {
[1] = 31,
},
},
},
["tasks"] = {
},
["unitType"] = "VSN_F9F",
}
return unitPayloads

View File

@@ -1,146 +0,0 @@
local unitPayloads = {
["name"] = "VSN_SEM",
["payloads"] = {
[1] = {
["name"] = "CAS",
["pylons"] = {
[1] = {
["CLSID"] = "{FC23864E-3B80-48E3-9C03-4DA8B1D7497B}",
["num"] = 1,
},
[2] = {
["CLSID"] = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B65}",
["num"] = 2,
},
[3] = {
["CLSID"] = "{FC23864E-3B80-48E3-9C03-4DA8B1D7497B}",
["num"] = 11,
},
[4] = {
["CLSID"] = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B65}",
["num"] = 10,
},
[5] = {
["CLSID"] = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B65}",
["num"] = 6,
},
},
["tasks"] = {
[1] = 32,
[2] = 31,
},
},
[2] = {
["name"] = "CAP",
["pylons"] = {
[1] = {
["CLSID"] = "{FC23864E-3B80-48E3-9C03-4DA8B1D7497B}",
["num"] = 1,
},
[2] = {
["CLSID"] = "{SEM1100_PTB}",
["num"] = 2,
},
[3] = {
["CLSID"] = "{FC23864E-3B80-48E3-9C03-4DA8B1D7497B}",
["num"] = 11,
},
[4] = {
["CLSID"] = "{SEM1100_PTB}",
["num"] = 10,
},
},
["tasks"] = {
[1] = 11,
},
},
[3] = {
["name"] = "ANTISHIP",
["pylons"] = {
[1] = {
["CLSID"] = "{FC23864E-3B80-48E3-9C03-4DA8B1D7497B}",
["num"] = 1,
},
[2] = {
["CLSID"] = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B65}",
["num"] = 2,
},
[3] = {
["CLSID"] = "{FC23864E-3B80-48E3-9C03-4DA8B1D7497B}",
["num"] = 11,
},
[4] = {
["CLSID"] = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B65}",
["num"] = 10,
},
[5] = {
["CLSID"] = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B65}",
["num"] = 6,
},
},
["tasks"] = {
[1] = 30,
},
},
[4] = {
["name"] = "STRIKE",
["pylons"] = {
[1] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 1,
},
[2] = {
["CLSID"] = "{SEM1100_PTB}",
["num"] = 2,
},
[3] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 11,
},
[4] = {
["CLSID"] = "{SEM1100_PTB}",
["num"] = 10,
},
[5] = {
["CLSID"] = "{D5D51E24-348C-4702-96AF-97A714E72697}",
["num"] = 6,
},
},
["tasks"] = {
[1] = 32,
},
},
[5] = {
["name"] = "DEAD",
["pylons"] = {
[1] = {
["CLSID"] = "{6D21ECEA-F85B-4E8D-9D51-31DC9B8AA4EF}",
["num"] = 1,
},
[2] = {
["CLSID"] = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B65}",
["num"] = 2,
},
[3] = {
["CLSID"] = "{6D21ECEA-F85B-4E8D-9D51-31DC9B8AA4EF}",
["num"] = 11,
},
[4] = {
["CLSID"] = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B65}",
["num"] = 10,
},
[5] = {
["CLSID"] = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B65}",
["num"] = 6,
},
},
["tasks"] = {
[1] = 32,
},
},
},
["tasks"] = {
},
["unitType"] = "VSN_SEM",
}
return unitPayloads

File diff suppressed because it is too large Load Diff

View File

@@ -90,13 +90,6 @@
"hertz": 343000,
"channel": null
},
"world_13": {
"name": "GHI",
"callsign": "GHI",
"beacon_type": 5,
"hertz": 113800000,
"channel": 85
},
"airfield2_0": {
"name": "",
"callsign": "IADA",
@@ -153,20 +146,6 @@
"hertz": 365000,
"channel": null
},
"airfield67_0": {
"name": "",
"callsign": "IAMN",
"beacon_type": 13,
"hertz": 109500000,
"channel": null
},
"airfield67_1": {
"name": "",
"callsign": "IAMN",
"beacon_type": 14,
"hertz": 109500000,
"channel": null
},
"airfield6_0": {
"name": "KALDE",
"callsign": "KAD",
@@ -254,8 +233,8 @@
"airfield7_4": {
"name": "DAMASCUS",
"callsign": "DAL",
"beacon_type": 9,
"hertz": 342000,
"beacon_type": 10,
"hertz": 342000000,
"channel": null
},
"airfield7_5": {
@@ -338,8 +317,8 @@
"airfield41_1": {
"name": "ALANYA/GAZIPASA",
"callsign": "GZP",
"beacon_type": 3,
"hertz": 114200000,
"beacon_type": 2,
"hertz": 0,
"channel": 89
},
"airfield41_2": {
@@ -426,20 +405,6 @@
"hertz": 111700000,
"channel": null
},
"airfield65_0": {
"name": "",
"callsign": "",
"beacon_type": 14,
"hertz": 109100000,
"channel": null
},
"airfield65_1": {
"name": "",
"callsign": "",
"beacon_type": 13,
"hertz": 109100000,
"channel": null
},
"airfield47_0": {
"name": "",
"callsign": "ILC",
@@ -503,34 +468,6 @@
"hertz": 358000,
"channel": null
},
"airfield68_0": {
"name": "",
"callsign": "",
"beacon_type": 13,
"hertz": 112910000,
"channel": null
},
"airfield68_1": {
"name": "",
"callsign": "",
"beacon_type": 13,
"hertz": 112900000,
"channel": null
},
"airfield68_2": {
"name": "",
"callsign": "",
"beacon_type": 14,
"hertz": 112910000,
"channel": null
},
"airfield68_3": {
"name": "",
"callsign": "",
"beacon_type": 14,
"hertz": 112900000,
"channel": null
},
"airfield27_0": {
"name": "ALEPPO",
"callsign": "ALE",
@@ -608,27 +545,6 @@
"hertz": null,
"channel": 79
},
"airfield64_0": {
"name": "PrinceHussein",
"callsign": "ABC",
"beacon_type": 4,
"hertz": 0,
"channel": 106
},
"airfield64_1": {
"name": "",
"callsign": "ABC",
"beacon_type": 13,
"hertz": 111400000,
"channel": null
},
"airfield64_2": {
"name": "",
"callsign": "ABC",
"beacon_type": 14,
"hertz": 111400000,
"channel": null
},
"airfield30_0": {
"name": "RAMATDAVID",
"callsign": "RMD",
@@ -664,20 +580,6 @@
"hertz": 115300000,
"channel": null
},
"airfield58_0": {
"name": "Sanliurfa",
"callsign": "GAP",
"beacon_type": 3,
"hertz": 113200000,
"channel": 79
},
"airfield58_1": {
"name": "Sanliurfa",
"callsign": "GAP",
"beacon_type": 9,
"hertz": 391000,
"channel": null
},
"airfield40_0": {
"name": "Cheka",
"callsign": "CAK",

View File

@@ -13,7 +13,6 @@
"Mirage-F1B",
"Mirage-F1CE",
"Mirage-F1C-200",
"Super Étendard",
"SA 342L Gazelle",
"SA 342M Gazelle",
"SA 342M Gazelle Mistral"

View File

@@ -14,7 +14,6 @@
"Mirage-F1CE",
"Mirage-F1C-200",
"Mirage-F1CT",
"Super Étendard",
"SA 342L Gazelle",
"SA 342M Gazelle",
"SA 342M Gazelle Mistral"

View File

@@ -14,7 +14,6 @@
"Mirage-F1CE",
"Mirage-F1C-200",
"Mirage-F1CT",
"Super Étendard",
"SA 342L Gazelle",
"SA 342M Gazelle",
"SA 342M Gazelle Mistral"

View File

@@ -1,53 +0,0 @@
{
"country": "Russia",
"name": "Private Military Company - Russian (Hard)",
"authors": "Starfire",
"description": "<p>A well-equipped private military company using a mix of modern and older Russian hardware.</p>",
"locales": [
"ru_RU"
],
"aircrafts": [
"Ka-50 Hokum",
"Ka-50 Hokum (Blackshark 3)",
"L-39ZA Albatros",
"Mi-24V Hind-E",
"Mi-24P Hind-F",
"Mi-8MTV2 Hip"
],
"frontline_units": [
"BMP-1",
"BRDM-2",
"BTR-80",
"BTR-D",
"Cobra",
"MT-LB",
"PT-76",
"T-55A",
"ZSU-57-2 'Sparka'"
],
"artillery_units": [
"2S1 Gvozdika",
"2S19 Msta-S",
"BM-27 Uragan"
],
"logistics_units": [
"LUV UAZ-469 Jeep",
"Truck Ural-375"
],
"infantry_units": [
"Infantry AK-74 Rus",
"MANPADS SA-18 Igla-S \"Grouse\"",
"Mortar 2B11 120mm",
"Paratrooper AKS",
"Paratrooper RPG-16"
],
"air_defense_units": [
"SA-9 Strela",
"SA-13 Gopher (9K35 Strela-10M3)",
"ZSU-57-2 'Sparka'",
"ZSU-23-4 Shilka"
],
"preset_groups": [],
"naval_units": [],
"missiles": []
}

View File

@@ -2,7 +2,7 @@
"country": "USA",
"name": "USA 1955",
"authors": "Khopa",
"description": "<p>US armed forces in the 50s, circa Korean War.</p>",
"description": "<p>US army in the 50s, circa Korean War.</p>",
"locales": [
"en_US"
],
@@ -13,8 +13,7 @@
"F-86F Sabre",
"F-100 Super Sabre",
"P-51D-25-NA Mustang",
"P-51D-30-NA Mustang",
"F9F Panther"
"P-51D-30-NA Mustang"
],
"frontline_units": [
"Bofors 40 mm Gun",

View File

@@ -46,14 +46,14 @@
],
"preset_groups": [
"Hawk",
"Patriot"
"Patriot",
"Carrier Strike Group 8"
],
"naval_units": [
"FFG Oliver Hazard Perry",
"DDG Arleigh Burke IIa",
"CG Ticonderoga",
"LHA-1 Tarawa",
"CVN-74 John C. Stennis"
"LHA-1 Tarawa"
],
"missiles": [],
"air_defense_units": [

View File

@@ -2,7 +2,11 @@ name: Carrier Strike Group 8
tasks:
- Navy
units:
- CVN-71 Theodore Roosevelt
- CVN-72 Abraham Lincoln
- CVN-73 George Washington
- CVN-74 John C. Stennis
- CVN-75 Harry S. Truman
- DDG Arleigh Burke IIa
- CG Ticonderoga
layouts:

View File

@@ -80,14 +80,6 @@ def beacons_from_terrain(dcs_path: Path, path: Path) -> Iterable[tuple[str, Beac
)
)
lua.execute(
textwrap.dedent(
"""
function math.pow(x,y) return x^y end
"""
)
)
bind_gettext = lua.eval(
textwrap.dedent(
"""\

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

View File

@@ -1,28 +0,0 @@
carrier_capable: true
description:
The Grumman F9F Panther is an early carrier-based jet fighter designed and produced
by the American aircraft manufacturer Grumman. It was the first jet-powered fighter
aircraft operated by the United States Navy as well as being Grummans first jet fighter.
The F9F is a single-engined, straight-winged day fighter, armed with four 20 mm (0.79 in)
cannons and could also carry a wide assortment of air-to-ground munitions.
The Panther was used extensively by the U.S. Navy and Marine Corps in the Korean War.
introduced: 1947
manufacturer: Grumman
origin: USA
price: 6
role: Carrier-based Attack/Light Fighter
gunfighter: true
variants:
F9F Panther: {}
tasks:
BAI: 650
BARCAP: 150
CAS: 650
DEAD: 370
Escort: 150
Fighter sweep: 150
Intercept: 150
OCA/Aircraft: 650
OCA/Runway: 350
Strike: 350
TARCAP: 150

View File

@@ -1,25 +0,0 @@
description:
The Dassault-Breguet Super Étendard is a French carrier-borne strike fighter aircraft
designed for service with the French Navy aircraft carriers.
The aircraft is an advanced development of the Étendard IVM, which it replaced.
Super Étendards have seen combat in Kosovo, Afghanistan, Libya, Falklands and
in the Iraq-Iran war.
introduced: 1978
manufacturer: Dassault-Breguet
origin: France
price: 10
role: Carrier-based Strike Fighter
carrier_capable: true
gunfighter: true
max_range: 200
variants:
Super Étendard: {}
tasks:
Anti-ship: 150
BAI: 250
CAS: 250
DEAD: 250
OCA/Aircraft: 250
OCA/Runway: 230
Strike: 230

View File

@@ -0,0 +1,4 @@
class: AircraftCarrier
price: 0
variants:
CVN-71 Theodore Roosevelt: null

View File

@@ -0,0 +1,4 @@
class: AircraftCarrier
price: 0
variants:
CVN-72 Abraham Lincoln: null

View File

@@ -0,0 +1,4 @@
class: AircraftCarrier
price: 0
variants:
CVN-73 George Washington: null

View File

@@ -0,0 +1,4 @@
class: AircraftCarrier
price: 0
variants:
CVN-75 Harry S. Truman: null

View File

@@ -0,0 +1,4 @@
class: AircraftCarrier
price: 0
variants:
CV 1143.5 Admiral Kuznetsov(2017): null

View File

@@ -92,12 +92,13 @@ class TestFactionLoader(unittest.TestCase):
self.assertIn(Infantry.Soldier_M249, faction.infantry_units)
self.assertIn(Stennis.name, faction.naval_units)
self.assertIn(Stennis, faction.carriers.keys())
self.assertIn(LHA_Tarawa.name, faction.naval_units)
self.assertIn("mod", faction.requirements.keys())
self.assertIn("Some mod is required", faction.requirements.values())
self.assertEqual(4, len(faction.carrier_names))
self.assertEqual(4, len(faction.carriers))
self.assertEqual(5, len(faction.helicopter_carrier_names))
@pytest.mark.skip(reason="Faction unit names in the json files are outdated")