from __future__ import annotations import argparse import json import math from dataclasses import dataclass from datetime import datetime from pathlib import Path from typing import Optional, Any, Iterator from tabulate import tabulate import dcs from dcs import Point from game import Game from game.campaignloader import CampaignAirWingConfig from game.db import FACTIONS from game.missiongenerator.tgogenerator import ( GroundObjectGenerator, ) from game.point_with_heading import PointWithHeading from game.settings import Settings from game.theater import CaucasusTheater, OffMapSpawn from game.unitmap import UnitMap from game.utils import Heading from gen.to_remove.armored_group_generator import ( FixedSizeArmorGroupGenerator, FixedSizeArmorGroupGeneratorWithAA, ) from gen.to_remove.carrier_group import ( CarrierGroupGenerator, CarrierStrikeGroup8Generator, ) from gen.to_remove.lha_group import LHAGroupGenerator from gen.to_remove.ship_group_generator import SHIP_MAP from gen.to_remove.coastal_group_generator import COASTAL_MAP from gen.templates import ( GroundObjectTemplates, TemplateCategory, GroupTemplate, UnitTemplate, TemplateEncoder, MissileTemplate, BuildingTemplate, CoastalTemplate, NavalTemplate, TemplateRandomizer, ArmorTemplate, TEMPLATE_TYPES, AirDefenceTemplate, ) from gen.to_remove.missiles_group_generator import MISSILES_MAP from gen.to_remove.airdefensegroupgenerator import AirDefenseGroupGenerator from gen.to_remove.ewr_group_generator import EWR_MAP from gen.to_remove.ewrs import EwrGenerator from gen.to_remove.sam_group_generator import SAM_MAP from gen.naming import namegen from qt_ui import liberation_install TEMPLATES_MIZ = "resources/templates/templates.miz" MIGRATE_MIZ = "resources/tools/groundobject_templates.miz" TEMPLATES_BLOB = "resources/templates/templates.json" TEMPLATES_MAP = "resources/templates/template_map.json" TABLE_FILE = "doc/templates/template_list.md" @dataclass class GroupTemplateMapping: name: str optional: bool unit_types: list[str] unit_classes: list[UnitClass] def to_json(self) -> dict[str, Any]: d = self.__dict__ if not self.optional: d.pop("optional") if not self.unit_types: d.pop("unit_types") if not self.unit_classes: d.pop("unit_classes") return d @staticmethod def from_dict(d: dict[str, Any]) -> GroupTemplateMapping: optional = d["optional"] if "optional" in d else False return GroupTemplateMapping( d["name"], optional, d["unit_types"], d["unit_classes"] ) @dataclass class TemplateMapping: name: str template_type: str description: str groups: list[GroupTemplateMapping] statics: list[str] static_mapping: Optional[GroupTemplateMapping] = None def to_json(self) -> dict[str, Any]: d = self.__dict__ if not self.static_mapping: d.pop("static_mapping") return d @staticmethod def from_dict(d: dict[str, Any]) -> TemplateMapping: groups = [GroupTemplateMapping.from_dict(group) for group in d["groups"]] static_mapping = ( GroupTemplateMapping.from_dict(d["static_mapping"]) if "static_mapping" in d else None ) return TemplateMapping( d["name"], d["template_type"], d["description"], groups, d["statics"], static_mapping, ) class TemplateMap: _map: dict[str, list[TemplateMapping]] = {} def __init__(self): self._map = {} def __getitem__(self, item: str) -> list[TemplateMapping]: return self._map[item] def __setitem__(self, key, value) -> None: self._map[key] = value def to_json(self) -> dict[str, Any]: return self._map def mapping_for_template(self, template_name: str) -> tuple[str, TemplateMapping]: for category, mappings in self._map.items(): for mapping in mappings: if mapping.name == template_name: return category, mapping raise RuntimeError(f"No mapping for template {template_name}") def mapping_for_group( self, group_name: str ) -> tuple[str, TemplateMapping, Optional[GroupTemplateMapping]]: for category, mappings in self._map.items(): for mapping in mappings: for group_mapping in mapping.groups: if group_mapping.name == group_name: return category, mapping, group_mapping if group_name in mapping.statics: return category, mapping, mapping.static_mapping raise RuntimeError(f"No mapping for group {group_name}") @property def mappings(self) -> Iterator[TemplateMapping]: for mapping in self._map.values(): yield mapping def import_templates( miz_file: str, template_map_file: str, target_file: str, table_file: str ) -> None: """Imports the template miz and the template_map as json into liberation""" temp_mis = dcs.Mission() temp_mis.load_file(miz_file) templates = GroundObjectTemplates() with open(template_map_file, "r") as f: template_map_json = json.load(f) template_map = TemplateMap() for name, mappings in template_map_json.items(): template_map[TemplateCategory(name)] = [] for mapping in mappings: template_map[TemplateCategory(name)].append( TemplateMapping.from_dict(mapping) ) template_position: dict[str, Point] = {} for static_group in temp_mis.country("USA").static_group: category, mapping, group_mapping = template_map.mapping_for_group( static_group.name ) template = templates.by_category_and_name( TemplateCategory(category), mapping.name ) static_template = None if not template: template = BuildingTemplate( mapping.name, mapping.template_type, mapping.description ) static_template = GroupTemplate(mapping.name, [], True) if group_mapping: static_template.randomizer = group_mapping.randomizer static_template.optional = group_mapping.optional template.groups.append(static_template) templates.add_template(TemplateCategory(category), template) else: for group in template.groups: if mapping.name in group.name: static_template = group if not static_template: raise RuntimeError(f"No mapping for group {static_group.name}") for i, unit in enumerate(static_group.units): unit_template = UnitTemplate.from_unit(unit) if i == 0 and template.name not in template_position: template_position[template.name] = unit.position unit_template.position = ( unit_template.position - template_position[template.name] ) static_template.units.append(unit_template) pass for vehicle_group in ( temp_mis.country("USA").vehicle_group + temp_mis.country("USA").ship_group ): category, mapping, group_mapping = template_map.mapping_for_group( vehicle_group.name ) template = templates.by_category_and_name( TemplateCategory(category), mapping.name ) if not template: template = TEMPLATE_TYPES[TemplateCategory(category)]( mapping.name, mapping.template_type, mapping.description ) templates.add_template(TemplateCategory(category), template) for i, unit in enumerate(vehicle_group.units): group_template = None for group in template.groups: if group.name == vehicle_group.name: group_template = group if not group_template: group_template = GroupTemplate( vehicle_group.name, [], False, group_mapping.randomizer, group_mapping.optional, ) template.groups.append(group_template) unit_template = UnitTemplate.from_unit(unit) if i == 0 and template.name not in template_position: template_position[template.name] = unit.position unit_template.position = ( unit_template.position - template_position[template.name] ) group_template.units.append(unit_template) pass with open(target_file, "w") as f: json.dump(templates.to_json(), f, cls=TemplateEncoder, indent="\t") # print Table print_table(target_file, table_file) def export_templates( templates_file: str, miz_file: str, template_map_file: str ) -> None: """Exports the templates.json to the templates.miz and the mapping""" # This function will only be used on mass jobs or specific fine tuning templates = GroundObjectTemplates.from_json(templates_file) # Prepare game theater = CaucasusTheater() initial_ground_position = Point(-500000, 250000) initial_water_position = Point(-350000, 250000) control_point_ground = OffMapSpawn(1, "Spawn Ground", initial_ground_position, True) control_point_water = OffMapSpawn(2, "Spawn Water", initial_water_position, True) theater.add_controlpoint(control_point_ground) theater.add_controlpoint(control_point_water) game = Game( FACTIONS["Bluefor Modern"], FACTIONS["Russia 2010"], theater, CampaignAirWingConfig({control_point_ground: [], control_point_water: []}), datetime.today(), Settings(), 10000, 10000, ) m = dcs.Mission(game.theater.terrain) country = m.country("USA") unit_map = UnitMap() template_map = TemplateMap() offset_x = 0 offset_y = 0 for count, template_category in enumerate(TemplateCategory): template_map[template_category.value] = [] temmplates_for_category = list(templates.for_category(template_category)) # Define the offset / separation category_separation = 10000 group_separation = 5000 if template_category in [TemplateCategory.Naval]: initial_position = initial_water_position control_point = control_point_water else: initial_position = initial_ground_position control_point = control_point_ground current_separation = offset_y + category_separation offset_x = 0 offset_y = current_separation max_cols = int(math.sqrt(len(temmplates_for_category))) for template in temmplates_for_category: mapping = TemplateMapping( template.name, template.template_type, template.description, [], [], ) position = Point( initial_position.x + offset_x, initial_position.y + offset_y ) # Initialize the template for group_template in template.groups: if group_template.randomizer: group_template.randomizer.init_randomization_for_faction( game.blue.faction ) ground_object = template.generate( template.name, PointWithHeading.from_point(position, Heading.from_degrees(0)), control_point, game, False, # Do not use randomization during export! ) for g_id, group in enumerate(ground_object.groups): group.name = f"{template.name} {g_id}" for u_id, unit in enumerate(group.units): unit.name = f"{template.name} {g_id}-{u_id}" group_template = template.groups[g_id] group_mapping = GroupTemplateMapping( group.name, group_template.optional, group_template.randomizer ) if group.static_group: for unit in group.units: mapping.statics.append(unit.name) # No mapping as we have no randomization yet else: mapping.groups.append(group_mapping) generator = GroundObjectGenerator(ground_object, country, game, m, unit_map) generator.generate(unique_name=False) # Prevent the ID prefix if ((offset_y - current_separation) / group_separation) < max_cols: offset_y += group_separation else: offset_y = current_separation offset_x += group_separation template_map[template_category.name].append(mapping) # Dump the template_map as json with open(template_map_file, "w") as f: json.dump(template_map, f, cls=TemplateEncoder, sort_keys=True, indent="\t") m.save(miz_file) def print_table(templates_file: str, table_file: str) -> None: # Category # Sub Category # Template Name # Unit_types templates = GroundObjectTemplates.from_json(templates_file) table = [] for template_category in TemplateCategory: category_templates = list(templates.for_category(template_category)) for template in sorted( category_templates, key=lambda x: (x.template_type, x.name) ): groups = [] options = [] for group in template.groups: units = [] group_options = [] for unit in group.units: units.append(unit.type) groups.append("