diff --git a/changelog.md b/changelog.md
index 23eb1f62..fc2d62b6 100644
--- a/changelog.md
+++ b/changelog.md
@@ -135,6 +135,7 @@ Saves from 6.x are not compatible with 7.0.
* **[Mission Generation]** Both A-10C modules now use separate radios for inter- and intra-flight comms (similar to other modern aircraft).
* **[Modding]** Updated Community A-4E-C mod version support to 2.1.0 release.
* **[Modding]** Add support for VSN F-4B and F-4C mod.
+* **[Modding]** Aircraft task capabilities and preferred aircraft for each task are now moddable in the aircraft unit yaml files. Each aircraft has a weight per task. Higher weights are given higher preference.
## Fixes
diff --git a/game/ato/ai_flight_planner_db.py b/game/ato/ai_flight_planner_db.py
deleted file mode 100644
index 5eb888e7..00000000
--- a/game/ato/ai_flight_planner_db.py
+++ /dev/null
@@ -1,805 +0,0 @@
-import logging
-from collections.abc import Sequence
-from typing import Type
-
-from dcs.helicopters import (
- AH_1W,
- AH_64A,
- AH_64D,
- AH_64D_BLK_II,
- CH_47D,
- CH_53E,
- Ka_50,
- Ka_50_3,
- Mi_24P,
- Mi_24V,
- Mi_26,
- Mi_28N,
- Mi_8MT,
- OH_58D,
- SA342L,
- SA342M,
- SH_60B,
- UH_1H,
- UH_60A,
-)
-from dcs.planes import (
- AJS37,
- AV8BNA,
- A_10A,
- A_10C,
- A_10C_2,
- A_20G,
- A_50,
- An_26B,
- B_17G,
- B_1B,
- B_52H,
- Bf_109K_4,
- C_101CC,
- C_130,
- C_17A,
- C_47,
- E_2C,
- E_3A,
- FA_18C_hornet,
- FW_190A8,
- FW_190D9,
- F_117A,
- F_14A,
- F_14A_135_GR,
- F_14B,
- F_15C,
- F_15E,
- F_16A,
- F_16A_MLU,
- F_16C_50,
- F_4E,
- F_5E_3,
- F_86F_Sabre,
- H_6J,
- IL_76MD,
- IL_78M,
- I_16,
- JF_17,
- J_11A,
- Ju_88A4,
- KC130,
- KC135MPRS,
- KC_135,
- KJ_2000,
- L_39ZA,
- MQ_9_Reaper,
- M_2000C,
- MiG_15bis,
- MiG_19P,
- MiG_21Bis,
- MiG_23MLD,
- MiG_25PD,
- MiG_27K,
- MiG_29A,
- MiG_29G,
- MiG_29S,
- MiG_31,
- Mirage_2000_5,
- Mirage_F1B,
- Mirage_F1BE,
- Mirage_F1CE,
- Mirage_F1EE,
- Mirage_F1EQ,
- Mirage_F1M_CE,
- Mirage_F1M_EE,
- Mirage_F1C_200,
- Mirage_F1CT,
- MosquitoFBMkVI,
- P_47D_30,
- P_47D_30bl1,
- P_47D_40,
- P_51D,
- P_51D_30_NA,
- RQ_1A_Predator,
- S_3B,
- S_3B_Tanker,
- SpitfireLFMkIX,
- SpitfireLFMkIXCW,
- Su_17M4,
- Su_24M,
- Su_25,
- Su_25T,
- Su_25TM,
- Su_27,
- Su_30,
- Su_33,
- Su_34,
- Tornado_GR4,
- Tornado_IDS,
- Tu_142,
- Tu_160,
- Tu_22M3,
- Tu_95MS,
- WingLoong_I,
- Yak_40,
- MB_339A,
-)
-from dcs.unittype import FlyingType
-
-from game.dcs.aircrafttype import AircraftType
-from pydcs_extensions.a4ec.a4ec import A_4E_C
-from pydcs_extensions.a6a.a6a import VSN_A6A
-from pydcs_extensions.a7e.a7e import A_7E
-from pydcs_extensions.SWPack.SWPack import (
- TIE,
- HUNTER,
- TIE_INTER,
- AWING,
- XWING,
- YWING,
- CORVETTE,
- tie_bomber_2,
- naboo_starfighter,
-)
-from pydcs_extensions.f100.f100 import VSN_F100
-from pydcs_extensions.f104.f104 import VSN_F104C, VSN_F104G, VSN_F104S, VSN_F104S_AG
-from pydcs_extensions.f105.f105 import VSN_F105D, VSN_F105G
-from pydcs_extensions.f15d.f15d import F_15D
-from pydcs_extensions.f16i_idf.f16i_idf import (
- F_16D_50,
- F_16D_52,
- F_16D_50_NS,
- F_16D_52_NS,
- F_16D_Barak_30,
- F_16D_Barak_40,
- F_16I,
-)
-from pydcs_extensions.f22a.f22a import F_22A
-from pydcs_extensions.f4b.f4b import VSN_F4B, VSN_F4C
-from pydcs_extensions.f84g.f84g import VSN_F84G
-from pydcs_extensions.fa18efg.fa18efg import FA_18E, FA_18F, EA_18G
-from pydcs_extensions.hercules.hercules import Hercules
-from pydcs_extensions.jas39.jas39 import JAS39Gripen, JAS39Gripen_BVR, JAS39Gripen_AG
-from pydcs_extensions.su30.su30 import Su_30MKA, Su_30MKI, Su_30MKM, Su_30SM
-from pydcs_extensions.su57.su57 import Su_57
-from pydcs_extensions.ov10a.ov10a import Bronco_OV_10A
-from pydcs_extensions.uh60l.uh60l import KC130J, UH_60L
-from .flighttype import FlightType
-
-# All aircraft lists are in priority order. Aircraft higher in the list will be
-# preferred over those lower in the list.
-# TODO: These lists really ought to be era (faction) dependent.
-# Factions which have F-5s, F-86s, and A-4s will should prefer F-5s for CAP, but
-# factions that also have F-4s should not.
-
-# Used for CAP, Escort, and intercept if there is not a specialised aircraft available
-ESCORT_CAPABLE = [
- TIE,
- HUNTER,
- XWING,
- AWING,
- CORVETTE,
- TIE_INTER,
- naboo_starfighter,
- Su_57,
- F_22A,
- F_15C,
- F_15D,
- F_14B,
- F_14A_135_GR,
- F_14A,
- Su_33,
- J_11A,
- Su_30,
- Su_30MKA,
- Su_30MKI,
- Su_30MKM,
- Su_30SM,
- Su_27,
- MiG_29S,
- F_16C_50,
- F_16I,
- F_16D_Barak_40,
- F_16D_Barak_30,
- F_16D_50,
- F_16D_50_NS,
- F_16D_52,
- F_16D_52_NS,
- FA_18E,
- FA_18F,
- FA_18C_hornet,
- JF_17,
- JAS39Gripen_BVR,
- JAS39Gripen,
- F_16A_MLU,
- F_16A,
- F_4E,
- VSN_F4C,
- VSN_F4B,
- MiG_31,
- MiG_25PD,
- MiG_29G,
- MiG_29A,
- MiG_23MLD,
- MiG_21Bis,
- Mirage_2000_5,
- Mirage_F1B,
- Mirage_F1BE,
- Mirage_F1CE,
- Mirage_F1EE,
- Mirage_F1EQ,
- Mirage_F1M_CE,
- Mirage_F1M_EE,
- Mirage_F1C_200,
- Mirage_F1CT,
- F_15E,
- M_2000C,
- F_5E_3,
- VSN_F104S,
- VSN_F104G,
- VSN_F104C,
- MiG_19P,
- VSN_F100,
- A_4E_C,
- F_86F_Sabre,
- MiG_15bis,
- C_101CC,
- VSN_F84G,
- P_51D_30_NA,
- P_51D,
- SpitfireLFMkIXCW,
- SpitfireLFMkIX,
- MosquitoFBMkVI,
- Bf_109K_4,
- FW_190D9,
- FW_190A8,
- P_47D_30,
- P_47D_30bl1,
- P_47D_40,
- I_16,
-]
-
-# Types to be skipped for escorts
-CAP_CAPABLE = ESCORT_CAPABLE + [
- L_39ZA,
-]
-
-
-# Used for CAS (Close air support) and BAI (Battlefield Interdiction)
-CAS_CAPABLE = [
- tie_bomber_2,
- YWING,
- A_10C_2,
- A_10C,
- Hercules,
- Su_34,
- Su_25TM,
- Su_25T,
- Su_25,
- F_15D,
- F_15E,
- F_16C_50,
- F_16I,
- F_16D_Barak_40,
- F_16D_Barak_30,
- F_16D_50,
- F_16D_50_NS,
- F_16D_52,
- F_16D_52_NS,
- FA_18E,
- FA_18F,
- FA_18C_hornet,
- Tornado_GR4,
- Tornado_IDS,
- JAS39Gripen_AG,
- JF_17,
- AV8BNA,
- A_10A,
- F_16A_MLU,
- F_16A,
- B_1B,
- A_7E,
- A_4E_C,
- F_14B,
- F_14A_135_GR,
- AJS37,
- Su_24M,
- Su_17M4,
- Su_33,
- F_4E,
- S_3B,
- Su_30,
- Su_30MKA,
- Su_30MKI,
- Su_30MKM,
- Su_30SM,
- MiG_29S,
- MiG_27K,
- MiG_29A,
- MiG_21Bis,
- AH_64D_BLK_II,
- AH_64D,
- AH_64A,
- AH_1W,
- OH_58D,
- SA342M,
- SA342L,
- Ka_50,
- Ka_50_3,
- Mi_28N,
- Mi_24P,
- Mi_24V,
- Mi_8MT,
- H_6J,
- MiG_19P,
- MiG_15bis,
- M_2000C,
- Mirage_F1B,
- Mirage_F1BE,
- Mirage_F1CE,
- Mirage_F1EE,
- Mirage_F1EQ,
- Mirage_F1M_CE,
- Mirage_F1M_EE,
- Mirage_F1CT,
- F_5E_3,
- F_86F_Sabre,
- C_101CC,
- L_39ZA,
- MB_339A,
- Bronco_OV_10A,
- UH_1H,
- A_20G,
- Ju_88A4,
- P_47D_40,
- P_47D_30bl1,
- P_47D_30,
- P_51D_30_NA,
- P_51D,
- SpitfireLFMkIXCW,
- SpitfireLFMkIX,
- MosquitoFBMkVI,
- I_16,
- Bf_109K_4,
- FW_190D9,
- FW_190A8,
- WingLoong_I,
- MQ_9_Reaper,
- RQ_1A_Predator,
- VSN_A6A,
- VSN_F4C,
- VSN_F4B,
- VSN_F100,
- VSN_F105G,
- VSN_F105D,
- VSN_F104S_AG,
- VSN_F104G,
- VSN_F104C,
- VSN_F84G,
-]
-
-
-# Aircraft used for SEAD and SEAD Escort tasks. Must be capable of the CAS DCS task.
-SEAD_ESCORT_CAPABLE = [
- JF_17,
- F_16C_50,
- F_16I,
- F_16D_Barak_40,
- F_16D_Barak_30,
- F_16D_50,
- F_16D_50_NS,
- F_16D_52,
- F_16D_52_NS,
- EA_18G,
- FA_18E,
- FA_18F,
- FA_18C_hornet,
- Tornado_IDS,
- Su_25T,
- Su_25TM,
- F_4E,
- A_7E,
- A_4E_C,
- JAS39Gripen_AG,
- AV8BNA,
- Su_24M,
- Su_17M4,
- Su_34,
- Su_30,
- Su_30MKA,
- Su_30MKI,
- Su_30MKM,
- Su_30SM,
- MiG_27K,
- Tornado_GR4,
- VSN_F105G,
- VSN_F100,
-]
-
-
-SEAD_CAPABLE = SEAD_ESCORT_CAPABLE + [
- F_14B,
- F_14A_135_GR,
-]
-
-# Aircraft used for DEAD tasks. Must be capable of the CAS DCS task.
-DEAD_CAPABLE = SEAD_CAPABLE + [
- YWING,
- tie_bomber_2,
- AJS37,
- F_16A_MLU,
- F_16A,
- F_15E,
- JAS39Gripen_AG,
- B_1B,
- B_52H,
- Tu_160,
- Tu_95MS,
- H_6J,
- A_20G,
- Ju_88A4,
- P_47D_40,
- P_47D_30bl1,
- P_47D_30,
- P_51D_30_NA,
- P_51D,
- Bronco_OV_10A,
- SpitfireLFMkIXCW,
- SpitfireLFMkIX,
- MosquitoFBMkVI,
- Bf_109K_4,
- FW_190D9,
- FW_190A8,
- VSN_A6A,
- VSN_F105D,
- VSN_F104S_AG,
- VSN_F104G,
- VSN_F104C,
- VSN_F100,
- VSN_F84G,
-]
-
-
-# Aircraft used for Strike mission
-STRIKE_CAPABLE = [
- YWING,
- tie_bomber_2,
- F_117A,
- B_1B,
- B_52H,
- Tu_160,
- Tu_95MS,
- Tu_22M3,
- H_6J,
- F_15D,
- F_15E,
- AJS37,
- Tornado_GR4,
- F_16C_50,
- F_16I,
- F_16D_Barak_40,
- F_16D_Barak_30,
- F_16D_50,
- F_16D_50_NS,
- F_16D_52,
- F_16D_52_NS,
- FA_18E,
- FA_18F,
- FA_18C_hornet,
- AV8BNA,
- JF_17,
- F_16A_MLU,
- F_16A,
- F_14B,
- F_14A_135_GR,
- JAS39Gripen_AG,
- Tornado_IDS,
- Su_17M4,
- Su_24M,
- Su_25TM,
- Su_25T,
- Su_25,
- Su_34,
- Su_33,
- Su_30,
- Su_30MKA,
- Su_30MKI,
- Su_30MKM,
- Su_30SM,
- Su_27,
- MiG_29S,
- MiG_29G,
- MiG_29A,
- F_4E,
- A_7E,
- A_10C_2,
- A_10C,
- VSN_F4C,
- VSN_F4B,
- S_3B,
- A_4E_C,
- Bronco_OV_10A,
- M_2000C,
- Mirage_F1B,
- Mirage_F1BE,
- Mirage_F1CE,
- Mirage_F1EE,
- Mirage_F1EQ,
- Mirage_F1M_CE,
- Mirage_F1M_EE,
- Mirage_F1CT,
- MiG_27K,
- MiG_21Bis,
- MiG_15bis,
- F_5E_3,
- F_86F_Sabre,
- C_101CC,
- L_39ZA,
- MB_339A,
- B_17G,
- A_20G,
- Ju_88A4,
- P_47D_40,
- P_47D_30bl1,
- P_47D_30,
- P_51D_30_NA,
- P_51D,
- SpitfireLFMkIXCW,
- SpitfireLFMkIX,
- MosquitoFBMkVI,
- Bf_109K_4,
- FW_190D9,
- FW_190A8,
- VSN_A6A,
- VSN_F100,
- VSN_F104S_AG,
- VSN_F104G,
- VSN_F104C,
- VSN_F105G,
- VSN_F105D,
- VSN_F84G,
-]
-
-
-ANTISHIP_CAPABLE = [
- AJS37,
- Tu_142,
- Tu_22M3,
- H_6J,
- FA_18E,
- FA_18F,
- FA_18C_hornet,
- JAS39Gripen_AG,
- F_16A_MLU,
- F_16A,
- Su_24M,
- Su_17M4,
- JF_17,
- Su_34,
- Su_30,
- Su_30MKA,
- Su_30MKI,
- Su_30MKM,
- Su_30SM,
- Tornado_IDS,
- Tornado_GR4,
- AV8BNA,
- S_3B,
- A_7E,
- A_20G,
- Ju_88A4,
- MosquitoFBMkVI,
- C_101CC,
- SH_60B,
-]
-
-
-# This list does not "inherit" from the strike list because some strike aircraft can
-# only carry guided weapons, and the AI cannot do runway attack with dguided weapons.
-# https://github.com/dcs-liberation/dcs_liberation/issues/1703
-RUNWAY_ATTACK_CAPABLE = [
- JF_17,
- Tornado_IDS,
- M_2000C,
- Mirage_F1B,
- Mirage_F1BE,
- Mirage_F1CE,
- Mirage_F1EE,
- Mirage_F1EQ,
- Mirage_F1M_CE,
- Mirage_F1M_EE,
- Mirage_F1CT,
- H_6J,
- B_1B,
- B_52H,
- Tu_22M3,
- H_6J,
- F_15E,
- AJS37,
- F_16C_50,
- F_16I,
- F_16D_Barak_40,
- F_16D_Barak_30,
- F_16D_50,
- F_16D_50_NS,
- F_16D_52,
- F_16D_52_NS,
- FA_18E,
- FA_18F,
- FA_18C_hornet,
- AV8BNA,
- JF_17,
- F_16A_MLU,
- F_16A,
- F_14B,
- F_14A_135_GR,
- JAS39Gripen_AG,
- Tornado_IDS,
- Su_17M4,
- Su_24M,
- Su_25TM,
- Su_25T,
- Su_25,
- Su_34,
- Su_33,
- Su_30,
- Su_30MKA,
- Su_30MKI,
- Su_30MKM,
- Su_30SM,
- Su_27,
- MiG_29S,
- MiG_29G,
- MiG_29A,
- F_4E,
- A_10C_2,
- A_10C,
- VSN_F4C,
- VSN_F4B,
- S_3B,
- A_7E,
- A_4E_C,
- Bronco_OV_10A,
- M_2000C,
- MiG_27K,
- MiG_21Bis,
- MiG_15bis,
- F_5E_3,
- F_86F_Sabre,
- C_101CC,
- L_39ZA,
- MB_339A,
- B_17G,
- A_20G,
- Ju_88A4,
- P_47D_40,
- P_47D_30bl1,
- P_47D_30,
- P_51D_30_NA,
- P_51D,
- SpitfireLFMkIXCW,
- SpitfireLFMkIX,
- MosquitoFBMkVI,
- Bf_109K_4,
- FW_190D9,
- FW_190A8,
- VSN_A6A,
- VSN_F105G,
- VSN_F105D,
- VSN_F104S_AG,
- VSN_F104G,
- VSN_F104C,
- VSN_F100,
-]
-
-# For any aircraft that isn't necessarily directly involved in strike
-# missions in a direct combat sense, but can transport objects and infantry.
-TRANSPORT_CAPABLE = [
- C_17A,
- Hercules,
- C_130,
- C_47,
- IL_76MD,
- An_26B,
- Yak_40,
- CH_53E,
- CH_47D,
- UH_60L,
- SH_60B,
- UH_60A,
- UH_1H,
- Mi_8MT,
- Mi_8MT,
- Mi_26,
-]
-
-AIR_ASSAULT_CAPABLE = [
- CH_53E,
- CH_47D,
- UH_60L,
- SH_60B,
- UH_60A,
- UH_1H,
- Mi_8MT,
- Mi_26,
- Mi_24P,
- Mi_24V,
- Hercules,
-]
-
-DRONES = [MQ_9_Reaper, RQ_1A_Predator, WingLoong_I]
-
-AEWC_CAPABLE = [
- E_3A,
- E_2C,
- A_50,
- KJ_2000,
-]
-
-# Priority is given to the tankers that can carry the most fuel.
-REFUELING_CAPABALE = [
- KC_135,
- KC135MPRS,
- IL_78M,
- KC130J,
- KC130,
- S_3B_Tanker,
-]
-
-
-def dcs_types_for_task(task: FlightType) -> Sequence[Type[FlyingType]]:
- cap_missions = (
- FlightType.BARCAP,
- FlightType.INTERCEPTION,
- FlightType.SWEEP,
- FlightType.TARCAP,
- )
- if task in cap_missions:
- return CAP_CAPABLE
- elif task == FlightType.ANTISHIP:
- return ANTISHIP_CAPABLE
- elif task == FlightType.BAI:
- return CAS_CAPABLE
- elif task == FlightType.CAS:
- return CAS_CAPABLE
- elif task == FlightType.SEAD:
- return SEAD_CAPABLE
- elif task == FlightType.SEAD_ESCORT:
- return SEAD_ESCORT_CAPABLE
- elif task == FlightType.DEAD:
- return DEAD_CAPABLE
- elif task == FlightType.OCA_AIRCRAFT:
- return CAS_CAPABLE
- elif task == FlightType.OCA_RUNWAY:
- return RUNWAY_ATTACK_CAPABLE
- elif task == FlightType.STRIKE:
- return STRIKE_CAPABLE
- elif task == FlightType.ESCORT:
- return ESCORT_CAPABLE
- elif task == FlightType.AEWC:
- return AEWC_CAPABLE
- elif task == FlightType.REFUELING:
- return REFUELING_CAPABALE
- elif task == FlightType.TRANSPORT:
- return TRANSPORT_CAPABLE
- elif task == FlightType.AIR_ASSAULT:
- return AIR_ASSAULT_CAPABLE
- else:
- logging.error(f"Unplannable flight type: {task}")
- return []
-
-
-def aircraft_for_task(task: FlightType) -> list[AircraftType]:
- dcs_types = dcs_types_for_task(task)
- types: list[AircraftType] = []
- for dcs_type in dcs_types:
- types.extend(AircraftType.for_dcs_type(dcs_type))
- return types
-
-
-def tasks_for_aircraft(aircraft: AircraftType) -> list[FlightType]:
- tasks: list[FlightType] = []
- for task in FlightType:
- if task is FlightType.FERRY:
- # Not a plannable task, so skip it.
- continue
- if aircraft in aircraft_for_task(task):
- tasks.append(task)
- return tasks
diff --git a/game/ato/flighttype.py b/game/ato/flighttype.py
index 9660f3f2..18216ea3 100644
--- a/game/ato/flighttype.py
+++ b/game/ato/flighttype.py
@@ -24,8 +24,8 @@ class FlightType(Enum):
* Implementations of MissionTarget.mission_types: A mission type can only be planned
against compatible targets. The mission_types method of each target class defines
which missions may target it.
- * ai_flight_planner_db.py: Add the new mission type to aircraft_for_task that
- returns the list of compatible aircraft in order of preference.
+ * resources/units/aircraft/*.yaml: Assign aircraft weight for the new task type in
+ the `tasks` dict for all capable aircraft.
You may also need to update:
diff --git a/game/campaignloader/squadrondefgenerator.py b/game/campaignloader/squadrondefgenerator.py
index 0bf87ea7..52eb6726 100644
--- a/game/campaignloader/squadrondefgenerator.py
+++ b/game/campaignloader/squadrondefgenerator.py
@@ -4,7 +4,6 @@ import itertools
import random
from typing import Optional, TYPE_CHECKING
-from game.ato.ai_flight_planner_db import aircraft_for_task, tasks_for_aircraft
from game.ato.flighttype import FlightType
from game.dcs.aircrafttype import AircraftType
from game.squadrons.operatingbases import OperatingBases
@@ -25,7 +24,7 @@ class SquadronDefGenerator:
self, task: FlightType, control_point: ControlPoint
) -> Optional[SquadronDef]:
aircraft_choice: Optional[AircraftType] = None
- for aircraft in aircraft_for_task(task):
+ for aircraft in AircraftType.priority_list_for_task(task):
if aircraft not in self.faction.aircrafts:
continue
if not control_point.can_operate(aircraft):
@@ -48,7 +47,7 @@ class SquadronDefGenerator:
role="Flying Squadron",
aircraft=aircraft,
livery=None,
- mission_types=tuple(tasks_for_aircraft(aircraft)),
+ auto_assignable_mission_types=set(aircraft.iter_task_capabilities()),
operating_bases=OperatingBases.default_for_aircraft(aircraft),
female_pilot_percentage=6,
pilot_pool=[],
diff --git a/game/dcs/aircrafttype.py b/game/dcs/aircrafttype.py
index 20db8f2e..78fc8cf0 100644
--- a/game/dcs/aircrafttype.py
+++ b/game/dcs/aircrafttype.py
@@ -364,6 +364,9 @@ class AircraftType(UnitType[Type[FlyingType]]):
capable.append(aircraft)
return list(reversed(sorted(capable, key=lambda a: a.task_priority(task))))
+ def iter_task_capabilities(self) -> Iterator[FlightType]:
+ yield from self.task_priorities
+
@staticmethod
def each_dcs_type() -> Iterator[Type[FlyingType]]:
yield from helicopter_map.values()
diff --git a/game/squadrons/airwing.py b/game/squadrons/airwing.py
index de035b36..8aa9c560 100644
--- a/game/squadrons/airwing.py
+++ b/game/squadrons/airwing.py
@@ -4,7 +4,6 @@ import itertools
from collections import defaultdict
from typing import Sequence, Iterator, TYPE_CHECKING, Optional
-from game.ato.ai_flight_planner_db import aircraft_for_task
from game.ato.closestairfields import ObjectiveDistanceCache
from game.dcs.aircrafttype import AircraftType
from .squadrondefloader import SquadronDefLoader
@@ -48,7 +47,7 @@ class AirWing:
self, location: MissionTarget, task: FlightType, size: int, this_turn: bool
) -> list[Squadron]:
airfield_cache = ObjectiveDistanceCache.get_closest_airfields(location)
- best_aircraft = aircraft_for_task(task)
+ best_aircraft = AircraftType.priority_list_for_task(task)
ordered: list[Squadron] = []
for control_point in airfield_cache.operational_airfields:
if control_point.captured != self.player:
@@ -79,7 +78,7 @@ class AirWing:
def best_available_aircrafts_for(self, task: FlightType) -> list[AircraftType]:
"""Returns an ordered list of available aircrafts for the given task"""
aircrafts = []
- best_aircraft_for_task = aircraft_for_task(task)
+ best_aircraft_for_task = AircraftType.priority_list_for_task(task)
for aircraft, squadrons in self.squadrons.items():
for squadron in squadrons:
if squadron.untasked_aircraft and task in squadron.mission_types:
diff --git a/game/squadrons/squadron.py b/game/squadrons/squadron.py
index 9fdc42c0..d5f163a1 100644
--- a/game/squadrons/squadron.py
+++ b/game/squadrons/squadron.py
@@ -256,6 +256,14 @@ class Squadron:
def has_unfilled_pilot_slots(self) -> bool:
return not self.pilot_limits_enabled or self._number_of_unfilled_pilot_slots > 0
+ def capable_of(self, task: FlightType) -> bool:
+ """Returns True if the squadron is capable of performing the given task.
+
+ A squadron may be capable of performing a task even if it will not be
+ automatically assigned to it.
+ """
+ return self.aircraft.capable_of(task)
+
def can_auto_assign(self, task: FlightType) -> bool:
return task in self.auto_assignable_mission_types
diff --git a/game/squadrons/squadrondef.py b/game/squadrons/squadrondef.py
index 712dd892..94abec92 100644
--- a/game/squadrons/squadrondef.py
+++ b/game/squadrons/squadrondef.py
@@ -25,16 +25,12 @@ class SquadronDef:
role: str
aircraft: AircraftType
livery: Optional[str]
- mission_types: tuple[FlightType, ...]
+ auto_assignable_mission_types: set[FlightType]
operating_bases: OperatingBases
female_pilot_percentage: int
pilot_pool: list[Pilot]
claimed: bool = False
- auto_assignable_mission_types: set[FlightType] = field(
- init=False, hash=False, compare=False
- )
-
def __post_init__(self) -> None:
self.auto_assignable_mission_types = set(self.mission_types)
@@ -48,7 +44,11 @@ class SquadronDef:
self.auto_assignable_mission_types.intersection_update(self.mission_types)
def can_auto_assign(self, task: FlightType) -> bool:
- return task in self.auto_assignable_mission_types
+ """
+ A squadron may be capable of performing a task even if it will not be
+ automatically assigned to it.
+ """
+ return self.aircraft.capable_of(task)
def operates_from(self, control_point: ControlPoint) -> bool:
if not control_point.can_operate(self.aircraft):
@@ -95,7 +95,7 @@ class SquadronDef:
role=data["role"],
aircraft=unit_type,
livery=data.get("livery"),
- mission_types=tuple(mission_types),
+ auto_assignable_mission_types=set(unit_type.iter_task_capabilities()),
operating_bases=OperatingBases.from_yaml(unit_type, data.get("bases", {})),
female_pilot_percentage=female_pilot_percentage,
pilot_pool=pilots,
diff --git a/game/transfers.py b/game/transfers.py
index 7e1860d2..81a33a3c 100644
--- a/game/transfers.py
+++ b/game/transfers.py
@@ -40,7 +40,6 @@ from typing import Generic, Iterator, List, Optional, Sequence, TYPE_CHECKING, T
from dcs.mapping import Point
-from game.ato.ai_flight_planner_db import aircraft_for_task
from game.ato.closestairfields import ObjectiveDistanceCache
from game.ato.flight import Flight
from game.ato.flighttype import FlightType
@@ -270,7 +269,7 @@ class AirliftPlanner:
def compatible_with_mission(
self, unit_type: AircraftType, airfield: ControlPoint
) -> bool:
- if unit_type not in aircraft_for_task(FlightType.TRANSPORT):
+ if not unit_type.capable_of(FlightType.TRANSPORT):
return False
if not self.transfer.origin.can_operate(unit_type):
return False
diff --git a/qt_ui/windows/QUnitInfoWindow.py b/qt_ui/windows/QUnitInfoWindow.py
index d59f3bed..50765faa 100644
--- a/qt_ui/windows/QUnitInfoWindow.py
+++ b/qt_ui/windows/QUnitInfoWindow.py
@@ -10,8 +10,6 @@ from PySide2.QtWidgets import (
QFrame,
)
-import game.ato.ai_flight_planner_db
-from game.ato.flighttype import FlightType
from game.dcs.aircrafttype import AircraftType
from game.dcs.groundunittype import GroundUnitType
from game.dcs.unittype import UnitType
@@ -84,9 +82,8 @@ class QUnitInfoWindow(QDialog):
# If it's an aircraft, include the task list.
if isinstance(unit_type, AircraftType):
- self.tasks_box = QLabel(
- f"In-Game Tasks: {self.generateAircraftTasks()}"
- )
+ tasks = ", ".join(str(t) for t in unit_type.iter_task_capabilities())
+ self.tasks_box = QLabel(f"In-Game Tasks: {tasks}")
self.tasks_box.setProperty("style", "info-element")
self.gridLayout.addWidget(self.tasks_box, 2, 0)
@@ -101,30 +98,3 @@ class QUnitInfoWindow(QDialog):
self.layout.addLayout(self.gridLayout, 1, 0)
self.setLayout(self.layout)
-
- def generateAircraftTasks(self) -> str:
- aircraft_tasks = ""
- unit_type = self.unit_type.dcs_unit_type
- if unit_type in game.ato.ai_flight_planner_db.CAP_CAPABLE:
- aircraft_tasks = (
- aircraft_tasks
- + f"{FlightType.BARCAP}, {FlightType.ESCORT}, {FlightType.INTERCEPTION}, {FlightType.SWEEP}, {FlightType.TARCAP}, "
- )
- if unit_type in game.ato.ai_flight_planner_db.CAS_CAPABLE:
- aircraft_tasks = (
- aircraft_tasks
- + f"{FlightType.CAS}, {FlightType.BAI}, {FlightType.OCA_AIRCRAFT}, "
- )
- if unit_type in game.ato.ai_flight_planner_db.SEAD_CAPABLE:
- aircraft_tasks = aircraft_tasks + f"{FlightType.SEAD}, "
- if unit_type in game.ato.ai_flight_planner_db.DEAD_CAPABLE:
- aircraft_tasks = aircraft_tasks + f"{FlightType.DEAD}, "
- if unit_type in game.ato.ai_flight_planner_db.ANTISHIP_CAPABLE:
- aircraft_tasks = aircraft_tasks + f"{FlightType.ANTISHIP}, "
- if unit_type in game.ato.ai_flight_planner_db.RUNWAY_ATTACK_CAPABLE:
- aircraft_tasks = aircraft_tasks + f"{FlightType.OCA_RUNWAY}, "
- if unit_type in game.ato.ai_flight_planner_db.STRIKE_CAPABLE:
- aircraft_tasks = aircraft_tasks + f"{FlightType.STRIKE}, "
- if unit_type in game.ato.ai_flight_planner_db.REFUELING_CAPABALE:
- aircraft_tasks = aircraft_tasks + f"{FlightType.REFUELING}, "
- return aircraft_tasks[:-2]