dcs-retribution/game/dcs/unitproperty.py

72 lines
2.4 KiB
Python

from __future__ import annotations
import inspect
import logging
from collections.abc import Iterator
from dataclasses import dataclass
from typing import Any, Generic, Type, TypeVar
from dcs.unittype import FlyingType
from .propertyvalue import PropertyValue
ValueT = TypeVar("ValueT", bool, int)
@dataclass(frozen=True)
class UnitProperty(Generic[ValueT]):
id: str
default: ValueT
values: list[PropertyValue[Any]]
@classmethod
def for_aircraft(
cls, unit_type: Type[FlyingType]
) -> Iterator[UnitProperty[ValueT]]:
try:
props = unit_type.Properties # type: ignore
except AttributeError:
return
if unit_type.property_defaults is None:
raise RuntimeError(f"{unit_type} has Properties but no defaults")
for name, attr in inspect.getmembers(props, inspect.isclass):
if name.startswith("__"):
continue
attr_values = getattr(attr, "Values", None)
if attr_values is None and unit_type.property_defaults[name] is None:
msg = f"Skipping property '{name}' for '{unit_type.id}': default value is None."
logging.warning(msg)
continue
yield cls.property_from(attr, unit_type.property_defaults[name])
@classmethod
def property_from(cls, attr: Type[ValueT], default: ValueT) -> UnitProperty[ValueT]:
prop_id = attr.id # type: ignore
values = getattr(attr, "Values", None)
if values is None:
prop_values = list(cls.default_values_for(prop_id, default))
else:
prop_values = []
for name, value in inspect.getmembers(values):
if name.startswith("__"):
continue
prop_values.append(PropertyValue(name, value))
return UnitProperty(prop_id, default, prop_values)
@classmethod
def default_values_for(
cls, prop_id: str, default: ValueT
) -> Iterator[PropertyValue[ValueT]]:
if isinstance(default, bool):
yield PropertyValue("True", True)
yield PropertyValue("False", False)
elif isinstance(default, int):
for i in range(10):
yield PropertyValue(str(i), i)
else:
raise TypeError(
f"Unexpected property type for {prop_id}: {default.__class__}"
)