Implement Template System for ground objects

- Factored out the current generators to use a better approach with Templates build from the dcs mission editor.
- This information is extended with a template-mapping in a json file which allows to logically group together multiple dcs groups and even statics to one template
- The combination of mapping and miz will be serialized to a template.json which is only used for loading.
- Factions now load templates during initialization and hold all the templates they can really use. This is based around the faction file.
- Implemented a template randomizer which allows to add some randomization to templates
- Each Template Group can have 1 randomizer which randomizes unit_type and size based on the mapping definition. Larger groups need to be devided in more fine detailed groups as we can now handle them better due to the change from dcs group types to our own classes.
- Rewritten the ArmorGroup, Naval and EWR template handling

Rework GroundObjectBuyMenu to support templates
This commit is contained in:
RndName
2022-01-19 13:06:19 +01:00
parent d154069877
commit 5febcdd4e4
11 changed files with 1884 additions and 591 deletions

View File

@@ -1,5 +1,6 @@
from __future__ import annotations
import copy
import itertools
import logging
from dataclasses import dataclass, field
@@ -24,6 +25,7 @@ from game.data.doctrine import (
from game.data.groundunitclass import GroundUnitClass
from game.dcs.aircrafttype import AircraftType
from game.dcs.groundunittype import GroundUnitType
from gen.templates import GroundObjectTemplates, TemplateCategory
if TYPE_CHECKING:
from game.theater.start_generator import ModSettings
@@ -72,7 +74,7 @@ class Faction:
air_defenses: List[str] = field(default_factory=list)
# Possible EWR generators for this faction.
ewrs: List[str] = field(default_factory=list)
ewrs: List[GroundUnitType] = field(default_factory=list)
# Possible Missile site generators for this faction
missiles: List[str] = field(default_factory=list)
@@ -137,12 +139,68 @@ class Faction:
#: both will use it.
unrestricted_satnav: bool = False
def has_access_to_unittype(self, unit_class: GroundUnitClass) -> bool:
# All possible templates which can be generated by the faction
templates: GroundObjectTemplates = field(default=GroundObjectTemplates())
def __getitem__(self, item: str) -> Any:
return getattr(self, item)
def has_access_to_unit_type(self, unit_type: str) -> bool:
# Supports all GroundUnit lists and AirDefenses
for unit in self.ground_units:
if unit_type == unit.dcs_id:
return True
return unit_type in self.air_defenses
def has_access_to_unit_class(self, unit_class: GroundUnitClass) -> bool:
for vehicle in itertools.chain(self.frontline_units, self.artillery_units):
if vehicle.unit_class is unit_class:
return True
return False
def load_templates(self, all_templates: GroundObjectTemplates) -> None:
# This loads all faction possible sam templates and the default ones
# For legacy reasons this allows to check for template names. This can be
# improved in the future to have more control about the possible Templates.
# For example it can be possible to define the unit_types and check if all
# requirements for the template are fulfilled.
for category, template in all_templates.templates:
if (
(
category == TemplateCategory.AirDefence
and (
# Check if faction has the template name or ALL required
# unit_types in the list air_defenses. For legacy reasons this
# allows both and also the EWR template
template.name in self.air_defenses
or all(
self.has_access_to_unit_type(required_unit)
for required_unit in template.required_units
)
or template.template_type == "EWR"
)
)
or template.name in self.navy_generators
or template.name in self.missiles
or template.name in self.coastal_defenses
or (
template.template_type
in self.building_set + ["fob", "ammo", "factory"]
)
or (template.template_type == "carrier" and self.aircraft_carrier)
or (template.template_type == "lha" and self.helicopter_carrier)
or category == TemplateCategory.Armor
):
# Make a deep copy of a template and add it to the template_list.
# This is required to have faction independent templates. Otherwise
# the reference would be the same and changes would affect all.
faction_template = copy.deepcopy(template)
# Initialize all randomizers
for group_template in faction_template.groups:
if group_template.randomizer:
group_template.randomizer.init_randomization_for_faction(self)
self.templates.add_template(category, faction_template)
@classmethod
def from_json(cls: Type[Faction], json: Dict[str, Any]) -> Faction:
faction = Faction(locales=json.get("locales"))
@@ -182,8 +240,7 @@ class Faction:
faction.logistics_units = [
GroundUnitType.named(n) for n in json.get("logistics_units", [])
]
faction.ewrs = json.get("ewrs", [])
faction.ewrs = [GroundUnitType.named(n) for n in json.get("ewrs", [])]
faction.air_defenses = json.get("air_defenses", [])
# Compatibility for older factions. All air defenses now belong to a
@@ -245,6 +302,8 @@ class Faction:
faction.unrestricted_satnav = json.get("unrestricted_satnav", False)
# Templates
faction.templates = GroundObjectTemplates()
return faction
@property
@@ -320,13 +379,13 @@ class Faction:
self.remove_vehicle("KORNET")
# high digit sams
if not mod_settings.high_digit_sams:
self.remove_air_defenses("SA10BGenerator")
self.remove_air_defenses("SA12Generator")
self.remove_air_defenses("SA20Generator")
self.remove_air_defenses("SA20BGenerator")
self.remove_air_defenses("SA23Generator")
self.remove_air_defenses("SA17Generator")
self.remove_air_defenses("KS19Generator")
self.remove_air_defenses("SA-10B/S-300PS Battery")
self.remove_air_defenses("SA-12/S-300V Battery")
self.remove_air_defenses("SA-20/S-300PMU-1 Battery")
self.remove_air_defenses("SA-20B/S-300PMU-2 Battery")
self.remove_air_defenses("SA-23/S-300VM Battery")
self.remove_air_defenses("SA-17 Grizzly Battery")
self.remove_air_defenses("KS-19 AAA Site")
return self
def remove_aircraft(self, name: str) -> None:

View File

@@ -123,7 +123,7 @@ class GroundObjectGenerator:
if not u.alive:
continue
unit_type = static_type_from_name(u.type)
unit_type = unit_type_from_name(u.type)
if not unit_type:
raise RuntimeError(
f"Unit type {u.type} is not a valid dcs unit type"

View File

@@ -158,7 +158,7 @@ class ProcurementAi:
worst_balanced: Optional[GroundUnitClass] = None
worst_fulfillment = math.inf
for unit_class in GroundUnitClass:
if not self.faction.has_access_to_unittype(unit_class):
if not self.faction.has_access_to_unit_class(unit_class):
continue
current_ratio = self.cost_ratio_of_ground_unit(cp, unit_class)

View File

@@ -1,45 +1,25 @@
from __future__ import annotations
import logging
import pickle
import random
from dataclasses import dataclass
from datetime import datetime
from typing import Any, Dict, Iterable, List, Set
from dcs.mapping import Point
from typing import List
from game import Game
from game.factions.faction import Faction
from game.scenery_group import SceneryGroup
from game.theater import PointWithHeading
from game.theater.theatergroundobject import (
AirDefenseRange,
BuildingGroundObject,
CarrierGroundObject,
EwrGroundObject,
FactoryGroundObject,
LhaGroundObject,
MissileSiteGroundObject,
SamGroundObject,
ShipGroundObject,
SceneryGroundObject,
VehicleGroupGroundObject,
CoastalSiteGroundObject,
SceneryGroundUnit,
GroundGroup,
)
from game.utils import Heading
from game.version import VERSION
from gen.coastal.coastal_group_generator import generate_coastal_group
from gen.defenses.armor_group_generator import generate_armor_group
from gen.fleet.ship_group_generator import (
generate_carrier_group,
generate_lha_group,
generate_ship_group,
)
from gen.missiles.missiles_group_generator import generate_missile_group
from gen.templates import GroundObjectTemplates, TemplateCategory, GroundObjectTemplate
from gen.naming import namegen
from gen.sam.airdefensegroupgenerator import AirDefenseRange
from gen.sam.ewr_group_generator import generate_ewr_group
from gen.sam.sam_group_generator import generate_anti_air_group
from . import (
ConflictTheater,
ControlPoint,
@@ -51,8 +31,6 @@ from ..campaignloader.campaignairwingconfig import CampaignAirWingConfig
from ..profiling import logged_duration
from ..settings import Settings
GroundObjectTemplates = Dict[str, Dict[str, Any]]
@dataclass(frozen=True)
class GeneratorSettings:
@@ -97,6 +75,9 @@ class GameGenerator:
self.settings = settings
self.generator_settings = generator_settings
with logged_duration(f"Initializing templates"):
self.load_templates()
def generate(self) -> Game:
with logged_duration("TGO population"):
# Reset name generator
@@ -142,6 +123,13 @@ class GameGenerator:
for cp in to_remove:
self.theater.controlpoints.remove(cp)
def load_templates(self) -> None:
templates = GroundObjectTemplates.from_json(
"resources/templates/templates.json"
)
self.player.load_templates(templates)
self.enemy.load_templates(templates)
class ControlPointGroundObjectGenerator:
def __init__(
@@ -170,6 +158,26 @@ class ControlPointGroundObjectGenerator:
return True
def generate_random_from_templates(
self, templates: list[GroundObjectTemplate], position: PointWithHeading
) -> None:
try:
template = random.choice(templates)
with logged_duration(
f"Ground Object generation from template {template.name}"
):
ground_object = template.generate(
namegen.random_objective_name(),
position,
self.control_point,
self.game,
)
self.control_point.connected_objectives.append(ground_object)
except NotImplementedError:
logging.error("Template Generator not implemented yet")
except IndexError:
logging.error(f"No templates to generate object")
def generate_navy(self) -> None:
skip_player_navy = self.generator_settings.no_player_navy
if self.control_point.captured and skip_player_navy:
@@ -179,21 +187,12 @@ class ControlPointGroundObjectGenerator:
if not self.control_point.captured and skip_enemy_navy:
return
for position in self.control_point.preset_locations.ships:
self.generate_ship_at(position)
def generate_ship_at(self, position: PointWithHeading) -> None:
group_id = self.game.next_group_id()
g = ShipGroundObject(
namegen.random_objective_name(), group_id, position, self.control_point
templates = list(
self.faction.templates.for_category(TemplateCategory.Naval, "ship")
)
group = generate_ship_group(self.game, g, self.faction_name)
g.groups = []
if group is not None:
g.groups.append(group)
self.control_point.connected_objectives.append(g)
for position in self.control_point.preset_locations.ships:
self.generate_random_from_templates(templates, position)
class NoOpGroundObjectGenerator(ControlPointGroundObjectGenerator):
@@ -214,16 +213,16 @@ class CarrierGroundObjectGenerator(ControlPointGroundObjectGenerator):
)
return False
# Create ground object group
group_id = self.game.next_group_id()
g = CarrierGroundObject(
namegen.random_objective_name(), group_id, self.control_point
templates = list(
self.faction.templates.for_category(TemplateCategory.Naval, "carrier")
)
self.generate_random_from_templates(
templates,
PointWithHeading.from_point(
self.control_point.position, self.control_point.heading
),
)
group = generate_carrier_group(self.faction_name, self.game, g)
g.groups = []
if group is not None:
g.groups.append(group)
self.control_point.connected_objectives.append(g)
self.control_point.name = random.choice(carrier_names)
return True
@@ -241,16 +240,16 @@ class LhaGroundObjectGenerator(ControlPointGroundObjectGenerator):
)
return False
# Create ground object group
group_id = self.game.next_group_id()
g = LhaGroundObject(
namegen.random_objective_name(), group_id, self.control_point
templates = list(
self.faction.templates.for_category(TemplateCategory.Naval, "lha")
)
self.generate_random_from_templates(
templates,
PointWithHeading.from_point(
self.control_point.position, self.control_point.heading
),
)
group = generate_lha_group(self.faction_name, self.game, g)
g.groups = []
if group is not None:
g.groups.append(group)
self.control_point.connected_objectives.append(g)
self.control_point.name = random.choice(lha_names)
return True
@@ -261,10 +260,8 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
game: Game,
generator_settings: GeneratorSettings,
control_point: ControlPoint,
templates: GroundObjectTemplates,
) -> None:
super().__init__(game, generator_settings, control_point)
self.templates = templates
def generate(self) -> bool:
if not super().generate():
@@ -291,156 +288,102 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
self.generate_coastal_sites()
def generate_armor_groups(self) -> None:
for position in self.control_point.preset_locations.armor_groups:
self.generate_armor_at(position)
def generate_armor_at(self, position: PointWithHeading) -> None:
group_id = self.game.next_group_id()
g = VehicleGroupGroundObject(
namegen.random_objective_name(),
group_id,
position,
self.control_point,
)
group = generate_armor_group(self.faction_name, self.game, g)
if group is None:
logging.error(
"Could not generate armor group for %s at %s",
g.name,
self.control_point,
)
templates = list(self.faction.templates.for_category(TemplateCategory.Armor))
if not templates:
logging.error(f"{self.faction_name} has no access to Armor templates")
return
g.groups = [group]
self.control_point.connected_objectives.append(g)
for position in self.control_point.preset_locations.armor_groups:
self.generate_random_from_templates(templates, position)
def generate_aa(self) -> None:
presets = self.control_point.preset_locations
for position in presets.long_range_sams:
self.generate_aa_at(
position,
ranges=[
{AirDefenseRange.Long},
{AirDefenseRange.Medium},
{AirDefenseRange.Short},
{AirDefenseRange.AAA},
[
AirDefenseRange.Long,
AirDefenseRange.Medium,
AirDefenseRange.Short,
AirDefenseRange.AAA,
],
)
for position in presets.medium_range_sams:
self.generate_aa_at(
position,
ranges=[
{AirDefenseRange.Medium},
{AirDefenseRange.Short},
{AirDefenseRange.AAA},
[
AirDefenseRange.Medium,
AirDefenseRange.Short,
AirDefenseRange.AAA,
],
)
for position in presets.short_range_sams:
self.generate_aa_at(
position,
ranges=[{AirDefenseRange.Short}, {AirDefenseRange.AAA}],
[AirDefenseRange.Short, AirDefenseRange.AAA],
)
for position in presets.aaa:
self.generate_aa_at(
position,
ranges=[{AirDefenseRange.AAA}],
[AirDefenseRange.AAA],
)
def generate_ewrs(self) -> None:
presets = self.control_point.preset_locations
for position in presets.ewrs:
self.generate_ewr_at(position)
templates = list(
self.faction.templates.for_category(TemplateCategory.AirDefence, "EWR")
)
if not templates:
logging.error(f"{self.faction_name} has no access to EWR templates")
return
def generate_strike_target_at(self, category: str, position: Point) -> None:
for position in self.control_point.preset_locations.ewrs:
self.generate_random_from_templates(templates, position)
obj_name = namegen.random_objective_name()
template = random.choice(list(self.templates[category].values()))
object_id = 0
group_id = self.game.next_group_id()
# TODO: Create only one TGO per objective, each with multiple units.
for unit in template:
object_id += 1
template_point = Point(unit["offset"].x, unit["offset"].y)
g = BuildingGroundObject(
obj_name,
category,
group_id,
object_id,
position + template_point,
Heading.from_degrees(unit["heading"]),
self.control_point,
unit["type"],
def generate_building_at(
self,
template_category: TemplateCategory,
building_category: str,
position: PointWithHeading,
) -> None:
templates = list(
self.faction.templates.for_category(template_category, building_category)
)
if templates:
self.generate_random_from_templates(templates, position)
else:
logging.error(
f"{self.faction_name} has no access to Building type {building_category}"
)
self.control_point.connected_objectives.append(g)
def generate_ammunition_depots(self) -> None:
for position in self.control_point.preset_locations.ammunition_depots:
self.generate_strike_target_at(category="ammo", position=position)
self.generate_building_at(TemplateCategory.Building, "ammo", position)
def generate_factories(self) -> None:
for position in self.control_point.preset_locations.factories:
self.generate_factory_at(position)
def generate_factory_at(self, point: PointWithHeading) -> None:
obj_name = namegen.random_objective_name()
group_id = self.game.next_group_id()
g = FactoryGroundObject(
obj_name,
group_id,
point,
point.heading,
self.control_point,
)
self.control_point.connected_objectives.append(g)
self.generate_building_at(TemplateCategory.Building, "factory", position)
def generate_aa_at(
self, position: Point, ranges: Iterable[Set[AirDefenseRange]]
self, position: PointWithHeading, ranges: list[AirDefenseRange]
) -> None:
group_id = self.game.next_group_id()
g = SamGroundObject(
namegen.random_objective_name(),
group_id,
position,
self.control_point,
)
groups = generate_anti_air_group(self.game, g, self.faction, ranges)
if not groups:
templates = []
for aa_range in ranges:
for template in self.faction.templates.for_category(
TemplateCategory.AirDefence, aa_range.name
):
templates.append(template)
if len(templates) > 0:
# Only take next (smaller) aa_range when no template available for the
# most requested range. Otherwise break the loop and continue
break
if templates:
self.generate_random_from_templates(templates, position)
else:
logging.error(
"Could not generate air defense group for %s at %s",
g.name,
self.control_point,
f"{self.faction_name} has no access to SAM Templates ({', '.join([range.name for range in ranges])})"
)
return
g.groups = groups
self.control_point.connected_objectives.append(g)
def generate_ewr_at(self, position: PointWithHeading) -> None:
group_id = self.game.next_group_id()
g = EwrGroundObject(
namegen.random_objective_name(),
group_id,
position,
self.control_point,
)
group = generate_ewr_group(self.game, g, self.faction)
if group is None:
logging.error(
"Could not generate ewr group for %s at %s",
g.name,
self.control_point,
)
return
g.groups = [group]
self.control_point.connected_objectives.append(g)
def generate_scenery_sites(self) -> None:
presets = self.control_point.preset_locations
@@ -448,143 +391,93 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
self.generate_tgo_for_scenery(scenery_group)
def generate_tgo_for_scenery(self, scenery: SceneryGroup) -> None:
obj_name = namegen.random_objective_name()
category = scenery.category
group_id = self.game.next_group_id()
object_id = 0
# Special Handling for scenery Objects based on trigger zones
g = BuildingGroundObject(
namegen.random_objective_name(),
scenery.category,
scenery.position,
Heading.from_degrees(0),
self.control_point,
)
ground_group = GroundGroup(
self.game.next_group_id(),
scenery.zone_def.name,
PointWithHeading.from_point(scenery.position, Heading.from_degrees(0)),
[],
g,
)
ground_group.static_group = True
g.groups.append(ground_group)
# Each nested trigger zone is a target/building/unit for an objective.
for zone in scenery.zones:
object_id += 1
local_position = zone.position
local_dcs_identifier = zone.name
g = SceneryGroundObject(
obj_name,
category,
group_id,
object_id,
local_position,
self.control_point,
local_dcs_identifier,
zone,
scenery_unit = SceneryGroundUnit(
zone.id,
zone.name,
"",
PointWithHeading.from_point(zone.position, Heading.from_degrees(0)),
g,
)
scenery_unit.zone = zone
ground_group.units.append(scenery_unit)
self.control_point.connected_objectives.append(g)
return
self.control_point.connected_objectives.append(g)
def generate_missile_sites(self) -> None:
templates = list(self.faction.templates.for_category(TemplateCategory.Missile))
if not templates:
logging.error(f"{self.faction_name} has no access to Missile templates")
return
for position in self.control_point.preset_locations.missile_sites:
self.generate_missile_site_at(position)
def generate_missile_site_at(self, position: PointWithHeading) -> None:
group_id = self.game.next_group_id()
g = MissileSiteGroundObject(
namegen.random_objective_name(), group_id, position, self.control_point
)
group = generate_missile_group(self.game, g, self.faction_name)
g.groups = []
if group is not None:
g.groups.append(group)
self.control_point.connected_objectives.append(g)
return
self.generate_random_from_templates(templates, position)
def generate_coastal_sites(self) -> None:
templates = list(self.faction.templates.for_category(TemplateCategory.Coastal))
if not templates:
logging.error(f"{self.faction_name} has no access to Coastal templates")
return
for position in self.control_point.preset_locations.coastal_defenses:
self.generate_coastal_site_at(position)
def generate_coastal_site_at(self, position: PointWithHeading) -> None:
group_id = self.game.next_group_id()
g = CoastalSiteGroundObject(
namegen.random_objective_name(),
group_id,
position,
self.control_point,
position.heading,
)
group = generate_coastal_group(self.game, g, self.faction_name)
g.groups = []
if group is not None:
g.groups.append(group)
self.control_point.connected_objectives.append(g)
return
self.generate_random_from_templates(templates, position)
def generate_strike_targets(self) -> None:
building_set = list(set(self.faction.building_set) - {"oil"})
if not building_set:
logging.error("Faction has no buildings defined")
logging.error(f"{self.faction_name} has no buildings defined")
return
for position in self.control_point.preset_locations.strike_locations:
category = random.choice(building_set)
self.generate_strike_target_at(category, position)
self.generate_building_at(TemplateCategory.Building, category, position)
def generate_offshore_strike_targets(self) -> None:
if "oil" not in self.faction.building_set:
logging.error("Faction does not support offshore strike targets")
logging.error(
f"{self.faction_name} does not support offshore strike targets"
)
return
for position in self.control_point.preset_locations.offshore_strike_locations:
self.generate_strike_target_at("oil", position)
self.generate_building_at(TemplateCategory.Building, "oil", position)
class FobGroundObjectGenerator(AirbaseGroundObjectGenerator):
def generate(self) -> bool:
self.generate_fob()
self.generate_armor_groups()
self.generate_factories()
self.generate_ammunition_depots()
self.generate_aa()
self.generate_ewrs()
self.generate_scenery_sites()
self.generate_strike_targets()
self.generate_offshore_strike_targets()
if self.faction.missiles:
self.generate_missile_sites()
if self.faction.coastal_defenses:
self.generate_coastal_sites()
return True
if super(FobGroundObjectGenerator, self).generate():
self.generate_fob()
return True
return False
def generate_fob(self) -> None:
category = "fob"
obj_name = self.control_point.name
template = random.choice(list(self.templates[category].values()))
point = self.control_point.position
# Pick from preset locations
object_id = 0
group_id = self.game.next_group_id()
# TODO: Create only one TGO per objective, each with multiple units.
for unit in template:
object_id += 1
template_point = Point(unit["offset"].x, unit["offset"].y)
g = BuildingGroundObject(
obj_name,
category,
group_id,
object_id,
point + template_point,
Heading.from_degrees(unit["heading"]),
self.control_point,
unit["type"],
is_fob_structure=True,
)
self.control_point.connected_objectives.append(g)
self.generate_building_at(
TemplateCategory.Building,
"fob",
PointWithHeading.from_point(
self.control_point.position, self.control_point.heading
),
)
class GroundObjectGenerator:
def __init__(self, game: Game, generator_settings: GeneratorSettings) -> None:
self.game = game
self.generator_settings = generator_settings
with open("resources/groundobject_templates.p", "rb") as f:
self.templates: GroundObjectTemplates = pickle.load(f)
def generate(self) -> None:
# Copied so we can remove items from the original list without breaking
@@ -610,10 +503,10 @@ class GroundObjectGenerator:
)
elif isinstance(control_point, Fob):
generator = FobGroundObjectGenerator(
self.game, self.generator_settings, control_point, self.templates
self.game, self.generator_settings, control_point
)
else:
generator = AirbaseGroundObjectGenerator(
self.game, self.generator_settings, control_point, self.templates
self.game, self.generator_settings, control_point
)
return generator.generate()

View File

@@ -6,21 +6,23 @@ from abc import ABC
from collections.abc import Sequence
from dataclasses import dataclass
from enum import Enum
from typing import Iterator, List, TYPE_CHECKING, Union, Optional
from typing import Iterator, List, TYPE_CHECKING, Union, Optional, Any
from dcs.vehicles import vehicle_map
from dcs.ships import ship_map
from dcs.mapping import Point
from dcs.triggers import TriggerZone
from dcs.unit import Unit
from dcs.vehicles import vehicle_map
from game.dcs.helpers import unit_type_from_name
from ..data.radar_db import LAUNCHER_TRACKER_PAIRS, TELARS, TRACK_RADARS
from ..dcs.groundunittype import GroundUnitType
from ..dcs.unittype import UnitType
from ..point_with_heading import PointWithHeading
from ..utils import Distance, Heading, meters
if TYPE_CHECKING:
from gen.templates import UnitTemplate, GroupTemplate, TemplateRandomizer
from gen.templates import UnitTemplate, GroupTemplate
from .controlpoint import ControlPoint
from ..ato.flighttype import FlightType
@@ -85,7 +87,7 @@ class GroundUnit:
position: PointWithHeading
ground_object: TheaterGroundObject
alive: bool = True
_dcs_type: Optional[GroundUnitType] = None
_unit_type: Optional[UnitType[Any]] = None
@staticmethod
def from_template(id: int, t: UnitTemplate, go: TheaterGroundObject) -> GroundUnit:
@@ -98,17 +100,24 @@ class GroundUnit:
)
@property
def unit_type(self) -> Optional[GroundUnitType]:
if not self._dcs_type:
def unit_type(self) -> Optional[UnitType[Any]]:
if not self._unit_type:
try:
if self.type in vehicle_map:
dcs_unit_type = vehicle_map[self.type]
vehicle_type = db.vehicle_type_from_name(self.type)
self._unit_type = next(GroundUnitType.for_dcs_type(vehicle_type))
elif self.type in ship_map:
ship_type = db.ship_type_from_name(self.type)
# TODO Allow handling of Ships. This requires extension of UnitType
return None
elif (static_type := db.static_type_from_name(self.type)) is not None:
# TODO Allow handling of Statics
return None
else:
return None
self._dcs_type = next(GroundUnitType.for_dcs_type(dcs_unit_type))
except StopIteration:
return None
return self._dcs_type
return self._unit_type
def kill(self) -> None:
self.alive = False