mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Add a CLI tool for viewing default loadouts.
This commit is contained in:
parent
c5efc908de
commit
22c3d4ebc5
@ -2,10 +2,13 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
from typing import Iterator, Mapping, Optional, TYPE_CHECKING
|
from typing import Iterator, Mapping, Optional, TYPE_CHECKING, Type
|
||||||
|
|
||||||
|
from dcs.unittype import FlyingType
|
||||||
|
|
||||||
from game.data.weapons import Pylon, Weapon, WeaponType
|
from game.data.weapons import Pylon, Weapon, WeaponType
|
||||||
from game.dcs.aircrafttype import AircraftType
|
from game.dcs.aircrafttype import AircraftType
|
||||||
|
from .flighttype import FlightType
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .flight import Flight
|
from .flight import Flight
|
||||||
@ -103,6 +106,10 @@ class Loadout:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def iter_for(cls, flight: Flight) -> Iterator[Loadout]:
|
def iter_for(cls, flight: Flight) -> Iterator[Loadout]:
|
||||||
|
return cls.iter_for_aircraft(flight.unit_type)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def iter_for_aircraft(cls, aircraft: AircraftType) -> Iterator[Loadout]:
|
||||||
# Dict of payload ID (numeric) to:
|
# Dict of payload ID (numeric) to:
|
||||||
#
|
#
|
||||||
# {
|
# {
|
||||||
@ -111,7 +118,7 @@ class Loadout:
|
|||||||
# {"CLSID": class ID, "num": pylon number}
|
# {"CLSID": class ID, "num": pylon number}
|
||||||
# "tasks": List (as a dict) of task IDs the payload is used by.
|
# "tasks": List (as a dict) of task IDs the payload is used by.
|
||||||
# }
|
# }
|
||||||
payloads = flight.unit_type.dcs_unit_type.load_payloads()
|
payloads = aircraft.dcs_unit_type.load_payloads()
|
||||||
for payload in payloads.values():
|
for payload in payloads.values():
|
||||||
name = payload["name"]
|
name = payload["name"]
|
||||||
pylons = payload["pylons"]
|
pylons = payload["pylons"]
|
||||||
@ -122,9 +129,7 @@ class Loadout:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def default_loadout_names_for(cls, flight: Flight) -> Iterator[str]:
|
def default_loadout_names_for(cls, task: FlightType) -> Iterator[str]:
|
||||||
from game.ato.flighttype import FlightType
|
|
||||||
|
|
||||||
# This is a list of mappings from the FlightType of a Flight to the type of
|
# This is a list of mappings from the FlightType of a Flight to the type of
|
||||||
# payload defined in the resources/payloads/UNIT_TYPE.lua file. A Flight has no
|
# payload defined in the resources/payloads/UNIT_TYPE.lua file. A Flight has no
|
||||||
# concept of a PyDCS task, so COMMON_OVERRIDE cannot be used here. This is used
|
# concept of a PyDCS task, so COMMON_OVERRIDE cannot be used here. This is used
|
||||||
@ -164,17 +169,25 @@ class Loadout:
|
|||||||
loadout_names[FlightType.DEAD].extend(loadout_names[FlightType.BAI])
|
loadout_names[FlightType.DEAD].extend(loadout_names[FlightType.BAI])
|
||||||
# OCA/Runway falls back to Strike
|
# OCA/Runway falls back to Strike
|
||||||
loadout_names[FlightType.OCA_RUNWAY].extend(loadout_names[FlightType.STRIKE])
|
loadout_names[FlightType.OCA_RUNWAY].extend(loadout_names[FlightType.STRIKE])
|
||||||
yield from loadout_names[flight.flight_type]
|
yield from loadout_names[task]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def default_for(cls, flight: Flight) -> Loadout:
|
def default_for(cls, flight: Flight) -> Loadout:
|
||||||
|
return cls.default_for_task_and_aircraft(
|
||||||
|
flight.flight_type, flight.unit_type.dcs_unit_type
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_for_task_and_aircraft(
|
||||||
|
cls, task: FlightType, dcs_unit_type: Type[FlyingType]
|
||||||
|
) -> Loadout:
|
||||||
# Iterate through each possible payload type for a given aircraft.
|
# Iterate through each possible payload type for a given aircraft.
|
||||||
# Some aircraft have custom loadouts that in aren't the standard set.
|
# Some aircraft have custom loadouts that in aren't the standard set.
|
||||||
for name in cls.default_loadout_names_for(flight):
|
for name in cls.default_loadout_names_for(task):
|
||||||
# This operation is cached, but must be called before load_by_name will
|
# This operation is cached, but must be called before load_by_name will
|
||||||
# work.
|
# work.
|
||||||
flight.unit_type.dcs_unit_type.load_payloads()
|
dcs_unit_type.load_payloads()
|
||||||
payload = flight.unit_type.dcs_unit_type.loadout_by_name(name)
|
payload = dcs_unit_type.loadout_by_name(name)
|
||||||
if payload is not None:
|
if payload is not None:
|
||||||
return Loadout(
|
return Loadout(
|
||||||
name,
|
name,
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import logging
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Iterator, Optional, TYPE_CHECKING, Type, Dict
|
from typing import Any, Dict, Iterator, Optional, TYPE_CHECKING, Type
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from dcs.helicopters import helicopter_map
|
from dcs.helicopters import helicopter_map
|
||||||
@ -315,7 +315,7 @@ class AircraftType(UnitType[Type[FlyingType]]):
|
|||||||
yield unit
|
yield unit
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _each_unit_type() -> Iterator[Type[FlyingType]]:
|
def each_dcs_type() -> Iterator[Type[FlyingType]]:
|
||||||
yield from helicopter_map.values()
|
yield from helicopter_map.values()
|
||||||
yield from plane_map.values()
|
yield from plane_map.values()
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Optional, Type, Iterator
|
from typing import Any, Iterator, Optional, Type
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from dcs.unittype import VehicleType
|
from dcs.unittype import VehicleType
|
||||||
@ -76,7 +76,7 @@ class GroundUnitType(UnitType[Type[VehicleType]]):
|
|||||||
yield unit
|
yield unit
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _each_unit_type() -> Iterator[Type[VehicleType]]:
|
def each_dcs_type() -> Iterator[Type[VehicleType]]:
|
||||||
yield from vehicle_map.values()
|
yield from vehicle_map.values()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Type, Iterator
|
from typing import Iterator, Type
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from dcs.ships import ship_map
|
from dcs.ships import ship_map
|
||||||
@ -32,7 +32,7 @@ class ShipUnitType(UnitType[Type[ShipType]]):
|
|||||||
yield unit
|
yield unit
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _each_unit_type() -> Iterator[Type[ShipType]]:
|
def each_dcs_type() -> Iterator[Type[ShipType]]:
|
||||||
yield from ship_map.values()
|
yield from ship_map.values()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@ -4,7 +4,7 @@ from abc import ABC
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from typing import TypeVar, Generic, Type, ClassVar, Any, Iterator
|
from typing import Any, ClassVar, Generic, Iterator, Type, TypeVar
|
||||||
|
|
||||||
from dcs.unittype import UnitType as DcsUnitType
|
from dcs.unittype import UnitType as DcsUnitType
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ class UnitType(ABC, Generic[DcsUnitTypeT]):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _each_unit_type() -> Iterator[DcsUnitTypeT]:
|
def each_dcs_type() -> Iterator[DcsUnitTypeT]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -61,7 +61,7 @@ class UnitType(ABC, Generic[DcsUnitTypeT]):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _load_all(cls) -> None:
|
def _load_all(cls) -> None:
|
||||||
for unit_type in cls._each_unit_type():
|
for unit_type in cls.each_dcs_type():
|
||||||
for data in cls._each_variant_of(unit_type):
|
for data in cls._each_variant_of(unit_type):
|
||||||
cls.register(data)
|
cls.register(data)
|
||||||
cls._loaded = True
|
cls._loaded = True
|
||||||
|
|||||||
134
resources/tools/loadoutviewer.py
Normal file
134
resources/tools/loadoutviewer.py
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
"""Command-line utility for displaying human readable loadout configurations."""
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
from collections.abc import Iterator
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
|
from dcs.helicopters import helicopter_map
|
||||||
|
from dcs.planes import plane_map
|
||||||
|
from dcs.unittype import FlyingType
|
||||||
|
|
||||||
|
from game import persistency
|
||||||
|
from game.ato import FlightType
|
||||||
|
from game.ato.loadouts import Loadout
|
||||||
|
from game.dcs.aircrafttype import AircraftType
|
||||||
|
|
||||||
|
# TODO: Move this logic out of the UI.
|
||||||
|
from qt_ui import liberation_install
|
||||||
|
from qt_ui.main import inject_custom_payloads
|
||||||
|
|
||||||
|
|
||||||
|
def non_empty_loadouts_for(
|
||||||
|
aircraft: Type[FlyingType],
|
||||||
|
) -> Iterator[tuple[FlightType, Loadout]]:
|
||||||
|
for task in FlightType:
|
||||||
|
try:
|
||||||
|
loadout = Loadout.default_for_task_and_aircraft(task, aircraft)
|
||||||
|
except KeyError:
|
||||||
|
# Not all aircraft have a unitPayloads field. This should maybe be handled
|
||||||
|
# in pydcs, but I'm not sure about the cause. For now, just ignore the field
|
||||||
|
# since we can be less robust in optional tooling.
|
||||||
|
continue
|
||||||
|
|
||||||
|
if loadout.name != "Empty":
|
||||||
|
yield task, loadout
|
||||||
|
|
||||||
|
|
||||||
|
def print_pylons(loadout: Loadout, prefix: str = "\t") -> None:
|
||||||
|
pylons = dict(sorted(loadout.pylons.items()))
|
||||||
|
for pylon_id, weapon in pylons.items():
|
||||||
|
if weapon is not None:
|
||||||
|
print(f"{prefix}{pylon_id}: {weapon.name}")
|
||||||
|
|
||||||
|
|
||||||
|
def show_all_loadouts(aircraft: Type[FlyingType]) -> None:
|
||||||
|
loadouts = list(non_empty_loadouts_for(aircraft))
|
||||||
|
if not loadouts:
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"Loadouts for {aircraft.id}:")
|
||||||
|
for task, loadout in loadouts:
|
||||||
|
print(f"\t{task.value}: {loadout.name}")
|
||||||
|
print_pylons(loadout, prefix="\t\t")
|
||||||
|
|
||||||
|
|
||||||
|
def task_by_name(name: str) -> FlightType:
|
||||||
|
for task in FlightType:
|
||||||
|
if task.value == name:
|
||||||
|
return task
|
||||||
|
raise KeyError(f"No FlightType named {name}")
|
||||||
|
|
||||||
|
|
||||||
|
def show_single_loadout(aircraft: Type[FlyingType], task_name: str) -> None:
|
||||||
|
task = task_by_name(task_name)
|
||||||
|
try:
|
||||||
|
loadout = Loadout.default_for_task_and_aircraft(task, aircraft)
|
||||||
|
except KeyError:
|
||||||
|
# Not all aircraft have a unitPayloads field. This should maybe be handled
|
||||||
|
# in pydcs, but I'm not sure about the cause. For now, just ignore the field
|
||||||
|
# since we can be less robust in optional tooling.
|
||||||
|
return
|
||||||
|
if loadout.pylons:
|
||||||
|
print(f"{aircraft.id} {loadout.name}:")
|
||||||
|
print_pylons(loadout)
|
||||||
|
|
||||||
|
|
||||||
|
def show_loadouts_for(aircraft: Type[FlyingType], task_name: str | None) -> None:
|
||||||
|
if task_name is None:
|
||||||
|
show_all_loadouts(aircraft)
|
||||||
|
else:
|
||||||
|
show_single_loadout(aircraft, task_name)
|
||||||
|
|
||||||
|
|
||||||
|
def show_all_aircraft(task_name: str | None) -> None:
|
||||||
|
for aircraft in AircraftType.each_dcs_type():
|
||||||
|
show_loadouts_for(aircraft, task_name)
|
||||||
|
|
||||||
|
|
||||||
|
def show_single_aircraft(aircraft_id: str, task_name: str | None) -> None:
|
||||||
|
try:
|
||||||
|
aircraft: Type[FlyingType] = plane_map[aircraft_id]
|
||||||
|
except KeyError:
|
||||||
|
aircraft = helicopter_map[aircraft_id]
|
||||||
|
show_loadouts_for(aircraft, task_name)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--aircraft-id",
|
||||||
|
help=(
|
||||||
|
"ID of the aircraft to display loadouts for. By default all aircraft will "
|
||||||
|
+ "be displayed."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--task",
|
||||||
|
help=(
|
||||||
|
"Name of the mission type to display. By default loadouts for all mission "
|
||||||
|
+ "types will be displayed."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
first_start = liberation_install.init()
|
||||||
|
if first_start:
|
||||||
|
sys.exit(
|
||||||
|
"Cannot view payloads without configuring DCS Liberation. Start the UI for "
|
||||||
|
"the first run configuration."
|
||||||
|
)
|
||||||
|
|
||||||
|
inject_custom_payloads(Path(persistency.base_path()))
|
||||||
|
|
||||||
|
if args.aircraft_id is None:
|
||||||
|
show_all_aircraft(args.task)
|
||||||
|
else:
|
||||||
|
show_single_aircraft(args.aircraft_id, args.task)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Loading…
x
Reference in New Issue
Block a user