From 54e24dff39c2b55959f656f0c684847b652564df Mon Sep 17 00:00:00 2001 From: RndName Date: Tue, 15 Feb 2022 17:02:52 +0100 Subject: [PATCH] Improve Layout System and adopt to review - adopted to review comments - removed the role from layouts - reworked the Groups within the layouts - added more documentation - rebased to latest changes --- doc/layouts/layouts.md | 195 +++++++----------- game/armedforces/armedforces.py | 63 +++--- game/armedforces/forcegroup.py | 157 ++++++++------ game/ato/flightwaypoint.py | 3 +- game/factions/__init__.py | 2 +- game/factions/faction.py | 8 +- game/layout/__init__.py | 6 +- game/layout/layout.py | 142 +++++++------ game/layout/layoutloader.py | 180 +++++++--------- game/layout/layoutmapping.py | 90 +++----- game/theater/start_generator.py | 5 - game/theater/theatergroundobject.py | 8 +- game/theater/theatergroup.py | 9 +- qt_ui/windows/QLiberationWindow.py | 5 +- .../groundobject/QGroundObjectBuyMenu.py | 144 ++++++------- resources/{units => }/groups/Ally-Flak.yaml | 1 - .../groups/Carrier_Strike_Group_8.yaml | 1 - .../{units => }/groups/Chinese-Navy.yaml | 1 - .../{units => }/groups/Cold-War-Flak.yaml | 1 - resources/{units => }/groups/Flak.yaml | 1 - resources/{units => }/groups/Freya.yaml | 1 - resources/{units => }/groups/HQ-7.yaml | 1 - resources/{units => }/groups/Hawk.yaml | 1 - resources/{units => }/groups/KS-19.yaml | 1 - resources/{units => }/groups/NASAMS-B.yaml | 1 - resources/{units => }/groups/NASAMS-C.yaml | 1 - resources/{units => }/groups/Patriot.yaml | 1 - resources/{units => }/groups/Rapier.yaml | 1 - resources/{units => }/groups/Roland.yaml | 1 - .../{units => }/groups/Russian-Navy.yaml | 1 - resources/{units => }/groups/SA-10.yaml | 1 - resources/{units => }/groups/SA-10B.yaml | 1 - resources/{units => }/groups/SA-11.yaml | 1 - resources/{units => }/groups/SA-12.yaml | 1 - resources/{units => }/groups/SA-17.yaml | 1 - resources/{units => }/groups/SA-2.yaml | 1 - resources/{units => }/groups/SA-20.yaml | 1 - resources/{units => }/groups/SA-20B.yaml | 1 - resources/{units => }/groups/SA-23.yaml | 1 - resources/{units => }/groups/SA-3.yaml | 1 - resources/{units => }/groups/SA-5.yaml | 1 - resources/{units => }/groups/SA-6.yaml | 1 - resources/{units => }/groups/Silkworm.yaml | 1 - resources/{units => }/groups/WW2LST.yaml | 1 - resources/layouts/anti_air/AAA_Mobile.yaml | 32 ++- resources/layouts/anti_air/AAA_Radar.yaml | 41 ++-- resources/layouts/anti_air/AAA_Site.yaml | 30 ++- .../layouts/anti_air/Cold_War_Flak_Site.yaml | 62 +++--- .../layouts/anti_air/Early-Warning_Radar.yaml | 18 +- resources/layouts/anti_air/Flak_Site.yaml | 92 ++++----- .../layouts/anti_air/Freya_EWR_Site.yaml | 92 ++++----- resources/layouts/anti_air/HQ-7_Site.yaml | 35 ++-- resources/layouts/anti_air/Hawk_Site.yaml | 59 +++--- .../layouts/anti_air/NASAMS_AIM-120B.yaml | 32 +-- .../layouts/anti_air/NASAMS_AIM-120C.yaml | 32 +-- .../layouts/anti_air/Patriot_Battery.yaml | 96 ++++----- .../layouts/anti_air/Rapier_AA_Site.yaml | 32 +-- resources/layouts/anti_air/Roland_Site.yaml | 32 +-- resources/layouts/anti_air/S-300_Site.yaml | 166 ++++++++------- .../layouts/anti_air/SA-11_Buk_Battery.yaml | 32 +-- .../anti_air/SA-17_Grizzly_Battery.yaml | 32 +-- .../layouts/anti_air/SA-2_S-75_Site.yaml | 32 +-- .../layouts/anti_air/SA-3_S-125_Site.yaml | 32 +-- .../layouts/anti_air/SA-5_S-200_Site.yaml | 42 ++-- resources/layouts/anti_air/SA-6_Kub_Site.yaml | 22 +- .../anti_air/Short_Range_Anti_Air.yaml | 26 +-- .../layouts/anti_air/WW2_Ally_Flak_Site.yaml | 72 +++---- resources/layouts/anti_air/WW2_Flak_Site.yaml | 22 +- .../anti_air/legacy_ground_templates.miz | Bin 17619 -> 17637 bytes resources/layouts/buildings/allycamp1.yaml | 154 +++++++------- resources/layouts/buildings/ammo1.yaml | 32 +-- resources/layouts/buildings/comms.yaml | 18 +- resources/layouts/buildings/derrick1.yaml | 48 ++--- resources/layouts/buildings/factory1.yaml | 34 +-- resources/layouts/buildings/farp1.yaml | 68 +++--- resources/layouts/buildings/fob1.yaml | 50 ++--- resources/layouts/buildings/fuel1.yaml | 42 ++-- resources/layouts/buildings/oil1.yaml | 22 +- resources/layouts/buildings/power1.yaml | 60 +++--- resources/layouts/buildings/village1.yaml | 64 +++--- resources/layouts/buildings/ware1.yaml | 34 +-- resources/layouts/buildings/ww2bunker1.yaml | 54 ++--- resources/layouts/buildings/ww2bunker2.yaml | 100 ++++----- resources/layouts/defenses/Silkworm.yaml | 58 +++--- resources/layouts/defenses/missile.yaml | 46 ++--- .../layouts/ground_forces/Armor_Group.yaml | 20 +- .../Armor_Group_with_Anti-Air.yaml | 38 ++-- resources/layouts/naval/Carrier_Group.yaml | 25 ++- .../layouts/naval/Carrier_Strike_Group_8.yaml | 36 ++-- resources/layouts/naval/LHA_Group.yaml | 25 ++- resources/layouts/naval/Naval-Group.yaml | 36 ++-- resources/layouts/naval/Naval-Two-Ship.yaml | 22 +- resources/layouts/naval/WW2-LST.yaml | 21 +- 93 files changed, 1606 insertions(+), 1710 deletions(-) rename resources/{units => }/groups/Ally-Flak.yaml (93%) rename resources/{units => }/groups/Carrier_Strike_Group_8.yaml (92%) rename resources/{units => }/groups/Chinese-Navy.yaml (91%) rename resources/{units => }/groups/Cold-War-Flak.yaml (88%) rename resources/{units => }/groups/Flak.yaml (95%) rename resources/{units => }/groups/Freya.yaml (94%) rename resources/{units => }/groups/HQ-7.yaml (88%) rename resources/{units => }/groups/Hawk.yaml (92%) rename resources/{units => }/groups/KS-19.yaml (88%) rename resources/{units => }/groups/NASAMS-B.yaml (91%) rename resources/{units => }/groups/NASAMS-C.yaml (91%) rename resources/{units => }/groups/Patriot.yaml (93%) rename resources/{units => }/groups/Rapier.yaml (90%) rename resources/{units => }/groups/Roland.yaml (88%) rename resources/{units => }/groups/Russian-Navy.yaml (94%) rename resources/{units => }/groups/SA-10.yaml (95%) rename resources/{units => }/groups/SA-10B.yaml (94%) rename resources/{units => }/groups/SA-11.yaml (92%) rename resources/{units => }/groups/SA-12.yaml (94%) rename resources/{units => }/groups/SA-17.yaml (92%) rename resources/{units => }/groups/SA-2.yaml (92%) rename resources/{units => }/groups/SA-20.yaml (95%) rename resources/{units => }/groups/SA-20B.yaml (94%) rename resources/{units => }/groups/SA-23.yaml (94%) rename resources/{units => }/groups/SA-3.yaml (92%) rename resources/{units => }/groups/SA-5.yaml (92%) rename resources/{units => }/groups/SA-6.yaml (90%) rename resources/{units => }/groups/Silkworm.yaml (87%) rename resources/{units => }/groups/WW2LST.yaml (88%) diff --git a/doc/layouts/layouts.md b/doc/layouts/layouts.md index 5bbb8134..46cb1404 100644 --- a/doc/layouts/layouts.md +++ b/doc/layouts/layouts.md @@ -1,157 +1,120 @@ -# ArmedForces and the Layout System +# The Layout System -Armed Forces and the Layout System is a complete rework of the generator-based logic to build theater-ground-objects (Liberation Objects which group ground units). -This will change underlying parts of the code base which will allow major improvements to the Ground Warfare in upcoming features. +The Layout System is a new way of defining how ground objects like SAM Sites or other Vehicle / Ship Groups will be generated (which type of units, how many units, alignment and orientation). It is a complete rework of the previous generator-based logic which was written in python code. The new system allows to define layouts with easy to write yaml code and the use of the DCS Mission Editor for easier placement of the units. The layout system also introduced a new logical grouping of Units and layouts for them, the Armed Forces, which will allow major improvements to the Ground Warfare in upcoming features. **Armed Forces**\ -TODO Describe the introduction of ArmedForces which are similar to the AirWing and Squadrons. -The armed forces of each coalition contain multiple ForceGroups. A ForceGroup is a logical set of units (Vehicles, Ships, Statics) and corresponding Layouts for these units. - -TODO Picture / Example to describe what it is... for example with Hawk Battery or S-300 Battery +The Armed Forces is a new system introduced with the layout system which will allow to identitfy and group possible units from the faction together with available layouts for these groups. It is comparable to the AirWing and Squadron implementation but just for Ground and Naval Forces. All possible Force Groups (grouping of 1 or more units and and the available layouts for them) will be generated during campaign initialization and will be used later by many different systems. A Force Group can also include static objects which was not possible before the introduction of the layout system. It is also possible to define presets of these Force Groups within the faction file which is handy for more complex groups like a SA-10 Battery or similar. Example: [SA-10/S-300PS](/resources/groups/SA-10.yaml) which includes all the units like SR, TR, LN and has the layout of a [S-300 Battery](/resources/layouts/anti_air/S-300_Site.yaml) **The Layout System**\ -In the previous system the generator was written in python and generated a group with a defined and static logic, written in code. -The layout sytem will now decouple the alignment / positioning from units and the definition of theire actual type (like Ural-375). -The template system allows to define the layout and set which unit types or classes (All logistic units for example) are able to fit into the template. -Ultimately this will allow to have generalized templates which can be reused by multiple types of units. Best example is the definition of a SAM layout. -Previously we had a generator for every different SAM Site, now we can just reuse the alignemnt (e.g. 6 Launchers in a circle alignment) with more generalization. +In the previous system the generator which created the ground object was written in python which made modifications and reusability very complicated. To allow easier handling of the layouts and decoupling of alignment of units and the actual unit type (for example Ural-375) the layout system was introduced. Previously we had a generator for every different SAM Site, now we can just reuse the alignemnt (e.g. 6 Launchers in a circle alignment) for multiple SAM Systems and introduce more variety. + +This new System allows Users and Designers to easily create or modify layouts as the new alginment and orientation of units is defined with the DCS Mission editor. An additional .yaml file allows the configuration of the layout with settings like allow unit types or random amounts of units. In total the new system reduces the complexity and allows to precisely align / orient units as needed and create realistic looking ground units. -This also allows Users and Designers to easily create or modify templates as the new templates are defined with the DCS Mission editor and an additional .yaml file which provides mapping information. -In total the new system reduces the complexity and allows to precisely align / orient units as needed and create realistic looking ground units. As the whole ground unit generation and handling was reworked it is now also possible to add static units to a ground object, so even Fortifcation or similar can be added to templates in the future. -## General Concept +### General Concept ![Overview](layouts.png) -TODO: Describe the general flow of the Template system -TODO: Describe the serialization (Developer Tools: Import Templates) +All possible Force Groups will be generated during campaign initialization by checking all possible units for the specific faction and all available layouts. The code will automatically match general layouts with available units. It is also possible to define preset groups within the faction files which group many units and the prefered layouts for the group. This is especially handy for unique layouts which are not defined as `global`. For example complex sam sites like the S-300 or Patriot which have very specific alignment of the different units. -TODO Lifecycle: -The template will be automatically validated on campaign generation against the player and enemy factions. -If the factions support the template (based on the unit_types and unit_classes) then it will be added to the game. -If a faction does not support a group from the template it will be removed if optional == True otherwise the complete template will be marked as unsupported and will not be used for the game. -During campaign initialization the start_generator will request unit_groups for the preset locations defined by the campaign designer. The faction will then offer possible groups and the matching template. -The Liberation Group (TheaterGroundObject) is then being generated from this UnitGroup. +Layouts will be matched to units based on the special definition given in the corresponding yaml file. For example a layout which is defined as global and allows the unit_class SHORAD will automatically be used for all available SHORAD units which are defined in the faction file. -- GroundWar (Frontline) currently does **not** use the template system -- User can buy new SAM or ArmorGroup using this template system -- Campaign Designers can also define precicsly (if needed) which template or UnitGroup should be placed at a specific location by using TriggerZones with custom properties +TODO Describe the optional flag. + +All these generated ForceGroups will be managed by the ArmedForces class of the specific coalition. This class will be used by other parts of the system like the start_generator or the BuyMenu. The ArmedForces class will then generate the TheaterGroundObject which will be used by liberation. Example for a customized Ground Object Buy Menu which makes use of Templates and UnitGroups: ![ground_object_buy_menu.png](ground_object_buy_menu.png) -### The template miz +## How to modify or add layouts -*Important*: Every unit_type has to be in a separate Group for the template to work. -The template system merges the groups back together later with the group_id property (defaults to 1 which means that all groups in the template will be merged to group 1) +A layout consists of two special files: -The function of the miz is to have the positioning and alignment of all the units within the template. Coordinates and headings will be used for the generated group. +- layout.miz which defines the actual positioning and alignment of the groups / units +- layout.yaml which defines the necessary information like amount of units, possible types or classes. -Unit Count per group has to be the amount set with the unit_count property. +To add a new template a new yaml has to be created as every yaml can only define exact one template. Best practice is to copy paste an existing template which is similar to the one to be created as starting point. The next step is to create a new .miz file and align Units and statics to the wishes. Even if existing ones can be reused, best practice is to always create a fresh one to prevent side effects. +The most important part is to use a new Group for every different Unit Type. It is not possible to mix Unit Types in one group within a template. For example it is not possible to have a logistic truck and a AAA in the same group. The miz file will only be used to get the exact position and orientation of the units, therefore it is irrelevant which type of unit will be used. The unit type will be later defined inside the yaml file. +For the next step all Group names have to be added to the yaml file. Take care to that these names match exactly! Assign the unit_types or unit_classes properties to math the needs. -During template generation the system will go through all possible units and will assign the respective unit_type to the units up to the maximum allow unit_count from the mapping. +**Important**: Whenever changes were made to layouts they have to be re-imported into Liberation. See below. + + +### The Layout miz + +The miz file is used to define the positioning and orientation of the different units for the template. The actual unit which is used is irrelevant. It is important to use a unique and meaningful name for the groups as this will be used in the yaml file as well. The information which will be extracted from the miz file are just the coordinates and heading of the units. + +*Important*: Every different unit type has to be in a separate Group for the template to work. You can not add units of different types to the same group. They can get merged back together during generation by setting the group property. In the example below both groups `AAA Site 0` and `AAA Site 1` have the group = 1 which means that they will be in the same dcs group during generation. + +TODO max amount of possible units is defined from the miz. Example if later the group should have 6 units than there have to be 6 defined in the miz. ![template_miz_example.png](layout_miz_example.png) -### The template yaml +### The Layout configuration file + +TODO Description about the layout yaml file.\ Possible Information: | Property | Type | Required | Description | Example | |---------------|-----------------------|----------|----------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------| | Name | (str) | Yes | A name to identify the template | `name: Armor Group` | -| Role | (GroupRole) | Yes | The role which the template should fit in | `role: AntiAir` or `role: GroundForce` | | Tasks | (list of GroupTask) | Yes | A list of tasks which the template can fullfill | `tasks: - AAA - SHORAD` | | Generic | (bool, default False) | No | If this is true this template will be used to create general unitGroups | | | Description | (str) | No | Short description of the template | | -| category | (str) | No | Only used for building templates to identify the type of the building | `category: ammo` | | Groups | (list of Groups) | Yes | see below for definition of a group | | -| template_file | (str) | No | the .miz file which has the template included. Only needed if the file has a different name than the yaml file | `template_file: resources/templates/anti_air/legacy_ground_templates.miz` | +| layout_file | (str) | No | the .miz file which has the groups / units of the layout included. Only needed if the file has a different name than the yaml file | `layout_file: resources/layouts/naval/legacy_naval_templates.miz` | -Groups within the template are defined as following: +TODO Group and SubGroup + +A group has 1..N sub groups. The name of the Group will be used later within the DCS group name. + +All SubGroups will be merged into one DCS Group + +Every unit type has to be defined as a sub group as following: | Property | Type | Required | Description | Example | |--------------|------------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------| | name | (str) | Yes | The group name used in the .miz. Must match exactly! | | | optional | (bool, default: False) | No | Defines wether the template can be used without this group if the faction has no access to the unit type or the user wants to disable this group | | -| group | (int, default: 1) | No | The ID of the group which the templategroup will be merged into | | | unit_count | (list of int) | No | Amount of units to be generated for this group. Can be fixed or a range where it will be picked randomly | | | unit_types | (list dcs unit types) | No | Specific unit_types for ground units. Complete list from pydcs: [Vehicles.py](https://github.com/pydcs/dcs/blob/master/dcs/vehicles.py). This list is extended by all supported mods! | | | unit_classes | (list unit classes) | No | Unit_classes of supported units. Defined in [UnitClass](/game/data/units.py) | | | statics | (list static types) | No | Specific unit_types of statics. Complete list from pydcs: [Statics.py](https://github.com/pydcs/dcs/blob/master/dcs/statics.py) | | -Complete example of a generic template for AAA Groups: +Complete example of a generic template for an Aircraft Carrier group: ``` -name: AAA Site -description: A standard AAA template +name: Carrier Group generic: true -role: AntiAir tasks: - - AAA + - AircraftCarrier groups: - - name: AAA Site 0 - group: 1 - unit_count: - - 2 - - 6 - unit_classes: - - AAA - - name: AAA Site 1 - optional: true - group: 1 - unit_count: - - 1 - - 2 - unit_classes: - - Logistics -template_file: resources/templates/anti_air/AAA.miz + - Carrier: # Group Name of the DCS Group + - name: Carrier Group 0 # Sub Group used in the layout.miz + unit_count: + - 1 + unit_classes: + - AircraftCarrier + - Escort: # Group name of the 2nd Group + - name: Carrier Group 1 + unit_count: + - 4 + unit_classes: + - Destroyer +layout_file: resources/layouts/naval/legacy_naval_templates.miz ``` -### Roles, Tasks and Classes - -TODO Describe Role, Tasking and Classes - -[GroupRole and GroupTask](/game/data/groups.py) - -[UnitClass](/game/data/units.py) - -## How to add / modify a template - -A template consists of two special files: - -- template.miz which defines the actual positioning and alignment of the groups / units -- template.yaml which defines the necessary information like amount of units, possible types or classes. - -To add a new template a new yaml has to be created as every yaml can only define exact one template. Best practice is to copy paste an existing template which is similar to the one to be created as starting point. The next step is to create a new .miz file and align Units and statics to the wishes. Even if existing ones can be reused, best practice is to always create a fresh one to prevent side effects. -The most important part is to use a new Group for every different Unit Type. It is not possible to mix Unit Types in one group within a template. For example it is not possible to have a logistic truck and a AAA in the same group. The miz file will only be used to get the exact position and orientation of the units, therefore it is irrelevant which type of unit will be used. The unit type will be later defined inside the yaml file. -For the next step all Group names have to be added to the yaml file. Take care to that these names match exactly! Assign the unit_types or unit_classes properties to math the needs. - -TODO Improve this with images and more detailed description - -**IMPORTANT**: Due to performance increase the templates get serialized to a pickle file in the save dir as `templates.p`. When templates were modified a manual re-import of all templates has to be triggered. -This can be done by either deleting this file or using the Liberation UI. There is a special option in the ToolBar under Tools: Import Templates. - - -## Import Layouts into Liberation -TODO Describe the serialization and import. - -For performance improvements all layouts are serialized to a so called pickle file. Every time changes are made to the layouts this file has to be recreated. -It will also be recreated after each Liberation update as it will check the Version Number and recreate it when changes are recognized. - -This file is stored in the save folder +### Import Layouts into Liberation +For performance improvements all layouts are serialized to a so called pickle file inside the save folder defined in the liberation preferences. Every time changes are made to the layouts this file has to be recreated. It can be recreated by either deleting the layouts.p file manually or using the special option in the Liberation Toolbar (Developer Tools -> Import Layouts). It will also be recreated after each Liberation update as it will check the Version Number and recreate it when changes are recognized. ## Migration from Generators - -- All generators removed and migrated to templates -- These templates will in the next step be generalized - The previous generators were migrated using a script which build a group using the generator. All of these groups were save into one .miz file [original_generator_layouts.miz](/resources/layouts/original_generator_layouts.miz). This miz file can be used to verify the templates and to generalize similar templates to decouple the layout from the actual units. As this is a time-consuming and sphisticated task this will be done over time. With the first step the technical requirements will be fulfilled so that the generalization can happen afterwards the technical pr gets merged. @@ -165,53 +128,39 @@ During migration all default factions were automatically updated, so they will w What was changed: - Removed the `ewrs` list. All EWRs are now defined in the list "air_defense_units". - Added the `air_defense_units` list. All units with the Role AntiAir can be defined here as [GroundUnitType](/game/dcs/groundunittype.py). All possible units are defined in [/resources/units/ground_units](/resources/units/ground_units) -- Added `preset_groups`. This list allows to define Preset Groups (described above) like SAM Systems consisting of Launcher, SR, TR and so on instead of adding them each to "air_defense_units". The presets are defined in [/resources/units/unit_groups](/resources/units/groups) +- Added `preset_groups`. This list allows to define Preset Groups (described above) like SAM Systems consisting of Launcher, SR, TR and so on instead of adding them each to "air_defense_units". The presets are defined in [/resources/groups](/resources/groups) - Migrated `air_defenses` to air_defense_units and preset_sets. - `Missiles` are migrated to GroundUnitTypes instead of Generator names (see air_defense_units for how to use) - Removed `cruisers`, `destroyers` and `naval_generators`. Migrated them to naval_units and preset_groups - added `naval_units` with the correct ship name found here [/resources/units/ships](/resources/units/ships) - `aircraft_carrier` and `helicopter_carrier` were moved to `naval_units` as well. -## Unit Groups +## Preset Groups -TODO Explain more - -- Sum up groups of different units which are used together (like a sam site). -- UnitGroup allows to define this logical group and add this to the faction file. -- UnitGroups can have preferred templates +Instead of adding the exact name of the previous generator to add complex groups like SAM sites or similar to the faction it is now possible to add preset groups to the faction file. As described earlier such a preset group (Force Group) can be defined very easy with a yaml file. This file allows to define the name, tasking, units, statics and the prefered layouts. The first task defines the primary role of the ForceGroup which gets generated from the preset. Example: ``` -name: SA-10/S-300PS # The name which will be used in the faction file -role: AntiAir # The role of the Group -tasks: - - LORAD # The task the Group can fulfill -ground_units: +name: SA-10/S-300PS # The name of the group +tasks: # Define at least 1 task + - LORAD # The task(s) the Group can fulfill +units: # Define at least 1 unit - SAM SA-10 S-300 "Grumble" Clam Shell SR - SAM SA-10 S-300 "Grumble" Big Bird SR - SAM SA-10 S-300 "Grumble" C2 - SAM SA-10 S-300 "Grumble" Flap Lid TR - SAM SA-10 S-300 "Grumble" TEL D - SAM SA-10 S-300 "Grumble" TEL C -ship_units: - - # Add some naval units here -statics: +statics: # Optional - # Add some statics here -templates: - - S-300 Site # The template names which should be used by this group +layouts: # Define at least one layout + - S-300 Site # prefered layouts for these groups ``` -A list of all available units is accessible here: [/resources/units/unit_groups](/resources/units/groups) - - -### Optional Tasks which can be done later -- [ ] Complex Presets which allow campaign designer to specify the exact forcegroup or layout which should be used. -- [ ] Generalize all layouts (Like MERAD or SHORAD templates) -- [ ] Reuse the layouts for the frontline -- [ ] Add UI Implementation to choose which templates should be used during new game wizard (like AirWing Config) -- [ ] Rework "Names_By_Category" to just use the Tasking instead of a string. -- [ ] Add remaining missing classes to the units which currently dont have a class - +Resources: +- A list of all available preset groups can be found here: [/resources/groups](/resources/groups) +- All possible tasks can be found in the [/game/data/groups.py](/game/data/groups.py) +- Units are defined with the variant name found in [/resources/units](/resources/units) diff --git a/game/armedforces/armedforces.py b/game/armedforces/armedforces.py index 0c44b428..ecc11876 100644 --- a/game/armedforces/armedforces.py +++ b/game/armedforces/armedforces.py @@ -2,21 +2,20 @@ from __future__ import annotations import random from typing import TYPE_CHECKING, Iterator, Optional -from game import db -from game.data.groups import GroupRole, GroupTask +from game.data.groups import GroupTask from game.armedforces.forcegroup import ForceGroup +from game.layout import LAYOUTS from game.profiling import logged_duration if TYPE_CHECKING: from game.factions.faction import Faction -# TODO More comments and rename class ArmedForces: """TODO Description""" # All available force groups for a specific Role - forces: dict[GroupRole, list[ForceGroup]] + forces: list[ForceGroup] def __init__(self, faction: Faction): with logged_duration(f"Loading armed forces for {faction.name}"): @@ -24,50 +23,36 @@ class ArmedForces: def add_or_update_force_group(self, new_group: ForceGroup) -> None: """TODO Description""" - if new_group.role in self.forces: - # Check if a force group with the same units exists - for force_group in self.forces[new_group.role]: - if ( - force_group.units == new_group.units - and force_group.tasks == new_group.tasks - ): - # Update existing group if units and tasks are equal - force_group.update_group(new_group) - return + # Check if a force group with the same units exists + for force_group in self.forces: + if ( + force_group.units == new_group.units + and force_group.tasks == new_group.tasks + ): + # Update existing group if units and tasks are equal + force_group.update_group(new_group) + return # Add a new force group - self.add_force_group(new_group) - - def add_force_group(self, force_group: ForceGroup) -> None: - """Adds a force group to the forces""" - if force_group.role in self.forces: - self.forces[force_group.role].append(force_group) - else: - self.forces[force_group.role] = [force_group] + self.forces.append(new_group) def _load_forces(self, faction: Faction) -> None: - """Initialize all armed_forces for the given faction""" - # This function will create a ForgeGroup for each global Layout and PresetGroup - self.forces = {} + """Initialize the ArmedForces for the given faction. + This will create a ForceGroup for each generic Layout and PresetGroup""" - preset_layouts = [ - layout - for preset_group in faction.preset_groups - for layout in preset_group.layouts - ] + # Initialize with preset_groups from the faction + self.forces = [preset_group for preset_group in faction.preset_groups] - # Generate Troops for all generic layouts and presets - for layout in db.LAYOUTS.layouts: - if ( - layout.generic or layout in preset_layouts - ) and layout.usable_by_faction(faction): + # Generate ForceGroup for all generic layouts by iterating over + # all layouts which are usable by the given faction. + for layout in LAYOUTS.layouts: + if layout.generic and layout.usable_by_faction(faction): # Creates a faction compatible GorceGroup self.add_or_update_force_group(ForceGroup.for_layout(layout, faction)) def groups_for_task(self, group_task: GroupTask) -> Iterator[ForceGroup]: - for groups in self.forces.values(): - for unit_group in groups: - if group_task in unit_group.tasks: - yield unit_group + for force_group in self.forces: + if group_task in force_group.tasks: + yield force_group def groups_for_tasks(self, tasks: list[GroupTask]) -> list[ForceGroup]: groups = [] diff --git a/game/armedforces/forcegroup.py b/game/armedforces/forcegroup.py index 16e3bf86..05b24c41 100644 --- a/game/armedforces/forcegroup.py +++ b/game/armedforces/forcegroup.py @@ -9,18 +9,20 @@ from typing import ClassVar, TYPE_CHECKING, Type, Any, Iterator, Optional import yaml from dcs import Point -from game import db -from game.data.groups import GroupRole, GroupTask +from game.data.groups import GroupTask from game.data.radar_db import UNITS_WITH_RADAR from game.dcs.groundunittype import GroundUnitType +from game.dcs.helpers import static_type_from_name from game.dcs.shipunittype import ShipUnitType from game.dcs.unittype import UnitType from game.point_with_heading import PointWithHeading -from game.layout.layout import TheaterLayout, AntiAirLayout, GroupLayout +from game.layout.layout import TgoLayout, AntiAirLayout, TgoLayoutGroup from dcs.unittype import UnitType as DcsUnitType, VehicleType, ShipType, StaticType from game.theater.theatergroup import TheaterGroup +from game.layout import LAYOUTS + if TYPE_CHECKING: from game import Game from game.factions.faction import Faction @@ -29,25 +31,40 @@ if TYPE_CHECKING: @dataclass class ForceGroup: - """A logical group of multiple units and layouts which have a specific tasking""" + """A logical group of multiple units and layouts which have a specific tasking. + + ForceGroups will be generated during game and coalition initialization based on + generic layouts and preset forcegroups. + + Every ForceGroup must have at least one unit, one task and one layout. + + A preset ForceGroup can for example be a S-300 SAM Battery which used many + different unit types which all together handle a specific tasking (AirDefense) + For this example the ForceGroup would consist of SR, TR, LN and so on next to + statics. This group also has the Tasking LORAD and can have multiple (at least one) + layouts which will be used to generate the actual DCS Group from it. + """ name: str units: list[UnitType[Any]] statics: list[Type[DcsUnitType]] - role: GroupRole tasks: list[GroupTask] = field(default_factory=list) - layouts: list[TheaterLayout] = field(default_factory=list) + layouts: list[TgoLayout] = field(default_factory=list) _by_name: ClassVar[dict[str, ForceGroup]] = {} - _by_role: ClassVar[dict[GroupRole, list[ForceGroup]]] = {} _loaded: bool = False @staticmethod - def for_layout(layout: TheaterLayout, faction: Faction) -> ForceGroup: - """TODO Documentation""" + def for_layout(layout: TgoLayout, faction: Faction) -> ForceGroup: + """Create a ForceGroup from the given TgoLayout which is usable by the faction + + This will iterate through all possible TgoLayoutGroups and check if the + unit_types are accessible by the faction. All accessible units will be added to + the force group + """ units: set[UnitType[Any]] = set() statics: set[Type[DcsUnitType]] = set() - for group in layout.groups: + for group in layout.all_groups: for unit_type in group.possible_types_for_faction(faction): if issubclass(unit_type, VehicleType): units.add(next(GroundUnitType.for_dcs_type(unit_type))) @@ -57,10 +74,9 @@ class ForceGroup: statics.add(unit_type) return ForceGroup( - f"{layout.role.value}: {', '.join([t.description for t in layout.tasks])}", + ", ".join([t.description for t in layout.tasks]), list(units), list(statics), - layout.role, layout.tasks, [layout], ) @@ -80,33 +96,38 @@ class ForceGroup: or type in self.statics ) - def dcs_unit_types_for_group(self, group: GroupLayout) -> list[Type[DcsUnitType]]: - """TODO Description""" + def dcs_unit_types_for_group( + self, group: TgoLayoutGroup + ) -> list[Type[DcsUnitType]]: + """Return all available DCS Unit Types which can be used in the given + TgoLayoutGroup""" unit_types = [t for t in group.unit_types if self.has_access_to_dcs_type(t)] alternative_types = [] for accessible_unit in self.units: if accessible_unit.unit_class in group.unit_classes: unit_types.append(accessible_unit.dcs_unit_type) - if accessible_unit.unit_class in group.alternative_classes: + if accessible_unit.unit_class in group.fallback_classes: alternative_types.append(accessible_unit.dcs_unit_type) return unit_types or alternative_types - def unit_types_for_group(self, group: GroupLayout) -> Iterator[UnitType[Any]]: + def unit_types_for_group(self, group: TgoLayoutGroup) -> Iterator[UnitType[Any]]: for dcs_type in self.dcs_unit_types_for_group(group): if issubclass(dcs_type, VehicleType): yield next(GroundUnitType.for_dcs_type(dcs_type)) elif issubclass(dcs_type, ShipType): yield next(ShipUnitType.for_dcs_type(dcs_type)) - def statics_for_group(self, group: GroupLayout) -> Iterator[Type[DcsUnitType]]: + def statics_for_group(self, group: TgoLayoutGroup) -> Iterator[Type[DcsUnitType]]: for dcs_type in self.dcs_unit_types_for_group(group): if issubclass(dcs_type, StaticType): yield dcs_type - def random_dcs_unit_type_for_group(self, group: GroupLayout) -> Type[DcsUnitType]: - """TODO Description""" + def random_dcs_unit_type_for_group( + self, group: TgoLayoutGroup + ) -> Type[DcsUnitType]: + """Return random DCS Unit Type which can be used in the given TgoLayoutGroup""" return random.choice(self.dcs_unit_types_for_group(group)) def update_group(self, new_group: ForceGroup) -> None: @@ -130,7 +151,7 @@ class ForceGroup: def create_ground_object_for_layout( self, - layout: TheaterLayout, + layout: TgoLayout, name: str, position: PointWithHeading, control_point: ControlPoint, @@ -139,25 +160,31 @@ class ForceGroup: """Create a TheaterGroundObject for the given template""" go = layout.create_ground_object(name, position, control_point) # Generate all groups using the randomization if it defined - for group in layout.groups: - # Choose a random unit_type for the group - try: - unit_type = self.random_dcs_unit_type_for_group(group) - except IndexError: - if group.optional: - # If group is optional it is ok when no unit_type is available - continue - # if non-optional this is a error - raise RuntimeError(f"No accessible unit for {self.name} - {group.name}") - self.create_theater_group_for_tgo(go, group, name, game, unit_type) + for group_name, groups in layout.groups.items(): + for group in groups: + # Choose a random unit_type for the group + try: + unit_type = self.random_dcs_unit_type_for_group(group) + except IndexError: + if group.optional: + # If group is optional it is ok when no unit_type is available + continue + # if non-optional this is a error + raise RuntimeError( + f"No accessible unit for {self.name} - {group.name}" + ) + tgo_group_name = f"{name} ({group_name})" + self.create_theater_group_for_tgo( + go, group, tgo_group_name, game, unit_type + ) return go def create_theater_group_for_tgo( self, ground_object: TheaterGroundObject, - group: GroupLayout, - name: str, + group: TgoLayoutGroup, + group_name: str, game: Game, unit_type: Type[DcsUnitType], unit_count: Optional[int] = None, @@ -165,25 +192,29 @@ class ForceGroup: """Create a TheaterGroup and add it to the given TGO""" # Random UnitCounter if not forced if unit_count is None: - unit_count = group.unit_counter - # Static and non Static groups have to be separated - group_id = group.group - 1 - if len(ground_object.groups) <= group_id: - # Requested group was not yet created - ground_group = TheaterGroup.from_template( - game.next_group_id(), group, ground_object, unit_type, unit_count - ) - # Set Group Name - ground_group.name = f"{name} {group_id}" - ground_object.groups.append(ground_group) - units = ground_group.units - else: - ground_group = ground_object.groups[group_id] - units = group.generate_units(ground_object, unit_type, unit_count) + unit_count = group.group_size + # Generate Units + units = group.generate_units(ground_object, unit_type, unit_count) + # Get or create the TheaterGroup + ground_group = ground_object.group_by_name(group_name) + if ground_group is not None: + # TheaterGroup with this name exists already. Extend it ground_group.units.extend(units) + else: + # TheaterGroup with the name was not created yet + ground_object.groups.append( + TheaterGroup.from_template( + game.next_group_id(), + group_name, + units, + ground_object, + unit_type, + unit_count, + ) + ) # Assign UniqueID, name and align relative to ground_object - for u_id, unit in enumerate(units): + for unit in units: unit.id = game.next_unit_id() unit.name = unit.unit_type.name if unit.unit_type else unit.type.name unit.position = PointWithHeading.from_point( @@ -209,42 +240,46 @@ class ForceGroup: @classmethod def _load_all(cls) -> None: - for file in Path("resources/units/groups").glob("*.yaml"): + for file in Path("resources/groups").glob("*.yaml"): if not file.is_file(): raise RuntimeError(f"{file.name} is not a valid ForceGroup") with file.open(encoding="utf-8") as data_file: data = yaml.safe_load(data_file) - group_role = GroupRole(data.get("role")) + name = data["name"] - group_tasks = [GroupTask.by_description(n) for n in data.get("tasks", [])] + group_tasks = [GroupTask.by_description(n) for n in data.get("tasks")] + if not group_tasks: + logging.error(f"ForceGroup {name} has no valid tasking") + continue - units = [UnitType.named(unit) for unit in data.get("units", [])] + units = [UnitType.named(unit) for unit in data.get("units")] + if not units: + logging.error(f"ForceGroup {name} has no valid units") + continue statics = [] for static in data.get("statics", []): - static_type = db.static_type_from_name(static) + static_type = static_type_from_name(static) if static_type is None: logging.error(f"Static {static} for {file} is not valid") else: statics.append(static_type) - layouts = [next(db.LAYOUTS.by_name(n)) for n in data.get("layouts")] + layouts = [LAYOUTS.by_name(n) for n in data.get("layouts")] + if not layouts: + logging.error(f"ForceGroup {name} has no valid layouts") + continue force_group = ForceGroup( - name=data.get("name"), + name=name, units=units, statics=statics, - role=group_role, tasks=group_tasks, layouts=layouts, ) cls._by_name[force_group.name] = force_group - if group_role in cls._by_role: - cls._by_role[group_role].append(force_group) - else: - cls._by_role[group_role] = [force_group] cls._loaded = True diff --git a/game/ato/flightwaypoint.py b/game/ato/flightwaypoint.py index 152b0510..1d9b01b6 100644 --- a/game/ato/flightwaypoint.py +++ b/game/ato/flightwaypoint.py @@ -11,6 +11,7 @@ from pydantic.dataclasses import dataclass from game.ato.flightwaypointtype import FlightWaypointType from game.theater import LatLon +from game.theater.theatergroup import TheaterUnit from game.utils import Distance, meters if TYPE_CHECKING: @@ -94,7 +95,7 @@ class FlightWaypoint(BaseFlightWaypoint): # having three names. A short and long form is enough. description: str = "" - targets: Sequence[MissionTarget | Unit] = [] + targets: Sequence[MissionTarget | TheaterUnit] = [] obj_name: str = "" pretty_name: str = "" only_for_player: bool = False diff --git a/game/factions/__init__.py b/game/factions/__init__.py index fa5dcc45..a4d7fa86 100644 --- a/game/factions/__init__.py +++ b/game/factions/__init__.py @@ -1,4 +1,4 @@ from .faction import Faction -from .faction_loader import FactionLoader +from .factionloader import FactionLoader FACTIONS = FactionLoader() diff --git a/game/factions/faction.py b/game/factions/faction.py index b4233333..3503cda2 100644 --- a/game/factions/faction.py +++ b/game/factions/faction.py @@ -150,7 +150,7 @@ class Faction: for unit in preset_group.units ), ) - return list(all_units) + return list(set(all_units)) @property def air_defenses(self) -> list[str]: @@ -158,7 +158,11 @@ class Faction: # This is used for the faction overview in NewGameWizard air_defenses = [a.name for a in self.air_defense_units] air_defenses.extend( - [pg.name for pg in self.preset_groups if pg.role == GroupRole.AIR_DEFENSE] + [ + pg.name + for pg in self.preset_groups + if any(task.role == GroupRole.AIR_DEFENSE for task in pg.tasks) + ] ) return sorted(air_defenses) diff --git a/game/layout/__init__.py b/game/layout/__init__.py index 92b3af23..3131c6b9 100644 --- a/game/layout/__init__.py +++ b/game/layout/__init__.py @@ -1,4 +1,4 @@ -from layout import TheaterLayout -from game.layout.layoutloader import LayoutLoader +from .layout import TgoLayout, TgoLayoutGroup +from .layoutloader import LayoutLoader -LAYOUTS = LayoutLoader() \ No newline at end of file +LAYOUTS = LayoutLoader() diff --git a/game/layout/layout.py b/game/layout/layout.py index f26f7b6a..206dd720 100644 --- a/game/layout/layout.py +++ b/game/layout/layout.py @@ -1,9 +1,10 @@ from __future__ import annotations +from collections import defaultdict import logging import random from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Type +from typing import TYPE_CHECKING, Iterator, Type from dcs import Point from dcs.unit import Unit @@ -50,98 +51,121 @@ class LayoutUnit: """Creates a LayoutUnit from a DCS Unit""" return LayoutUnit( unit.name, - Point(int(unit.position.x), int(unit.position.y)), + unit.position, int(unit.heading), ) @dataclass -class GroupLayout: - """The Layout of a TheaterGroup""" +class TgoLayoutGroup: + """The layout of a single type of unit within the TgoLayout + + Each DCS group that is spawned in the mission is composed of one or more + TgoLayoutGroup. Each TgoLayoutGroup will generate only a single type of unit. + + The merging of multiple TgoLayoutGroups to a single DCS group is defined in the + TgoLayout with a dict which uses the Dcs group name as key and the corresponding + TgoLayoutGroups as values. + + Each TgoLayoutGroup will be filled with a single type of unit when generated. The + types compatible with the position can either be specified precisely (with + unit_types) or generically (with unit_classes). If neither list specifies units + that can be fulfilled by the faction, fallback_classes will be used. This allows + the early-warning radar template, which prefers units that are defined as early + warning radars like the 55G6, but to fall back to any radar usable by the faction + if EWRs are not available. + + A TgoLayoutGroup may be optional. Factions or ForceGroups that are not able to + provide an actual unit for the TgoLayoutGroup will still be able to use the layout; + the optional TgoLayoutGroup will be omitted. + """ name: str - units: list[LayoutUnit] + layout_units: list[LayoutUnit] - # The group this template will be merged into - group: int = 1 - - # Define the amount of random units to be created by the randomizer. - # This can be a fixed int or a random value from a range of two ints as tuple + # Define the amount of units to be created. This can be a fixed int or a random + # choice from a range of two ints. If the list is empty it will use the whole group + # size / all available LayoutUnits unit_count: list[int] = field(default_factory=list) # defintion which unit types are supported unit_types: list[Type[DcsUnitType]] = field(default_factory=list) unit_classes: list[UnitClass] = field(default_factory=list) - alternative_classes: list[UnitClass] = field(default_factory=list) + fallback_classes: list[UnitClass] = field(default_factory=list) # Defines if this groupTemplate is required or not optional: bool = False - # if enabled the specific group will be generated during generation - # Can only be set to False if Optional = True - enabled: bool = True - - # TODO Caching for faction! def possible_types_for_faction(self, faction: Faction) -> list[Type[DcsUnitType]]: - """TODO Description""" + """Determine the possible dcs unit types for the TgoLayoutGroup and the given faction""" unit_types = [t for t in self.unit_types if faction.has_access_to_dcs_type(t)] alternative_types = [] for accessible_unit in faction.accessible_units: if accessible_unit.unit_class in self.unit_classes: unit_types.append(accessible_unit.dcs_unit_type) - if accessible_unit.unit_class in self.alternative_classes: + if accessible_unit.unit_class in self.fallback_classes: alternative_types.append(accessible_unit.dcs_unit_type) if not unit_types and not alternative_types and not self.optional: - raise LayoutException + raise LayoutException(f"{self.name} not usable by faction {faction.name}") return unit_types or alternative_types @property - def unit_counter(self) -> int: - """TODO Documentation""" - default = len(self.units) + def group_size(self) -> int: + """The amount of units to be generated. If unit_count is defined in the layout this will be randomized accordingly. Otherwise this will be the maximum size.""" if self.unit_count: if len(self.unit_count) == 1: - count = self.unit_count[0] - else: - count = random.choice(range(min(self.unit_count), max(self.unit_count))) - if count > default: - logging.error( - f"UnitCount for Group Layout {self.name} " - f"exceeds max available units for this group" - ) - return default - return count - return default + return self.unit_count[0] + return random.choice(range(min(self.unit_count), max(self.unit_count))) + return self.max_size @property def max_size(self) -> int: - return len(self.units) + return len(self.layout_units) def generate_units( self, go: TheaterGroundObject, unit_type: Type[DcsUnitType], amount: int ) -> list[TheaterUnit]: - """TODO Documentation""" + """Generate units of the given unit type and amount for the TgoLayoutGroup""" return [ - TheaterUnit.from_template(i, unit_type, self.units[i], go) + TheaterUnit.from_template(i, unit_type, self.layout_units[i], go) for i in range(amount) ] -class TheaterLayout: - """TODO Documentation""" +class TgoLayout: + """TgoLayout defines how a TheaterGroundObject will be generated from a ForceGroup. This defines the positioning, orientation, type and amount of the actual units - def __init__(self, name: str, role: GroupRole, description: str = "") -> None: + Each TgoLayout is defined in resources/layouts with a .yaml file which has all the + information about the Layout next to a .miz file which gives information about the + actual position (x, y) and orientation (heading) of the units. The layout file also + defines the structure of the DCS group (or groups) that will be spawned in the + mission. Complex groups like SAMs protected by point-defense require specific + grouping when used with plugins like Skynet. One group would define the main + battery (the search and track radars, launchers, C2 units, etc), another would + define PD units, and others could define SHORADs or resupply units. + + Each group (representing a DCS group) is further divided into TgoLayoutGroups. As + a TgoLayoutGroup only represents a single dcs unit type the logical dcs group of multiple unit types will be created with the usage of a dict which has the DCS Group name as key and a list of TgoLayoutGroups which will be merged into this single dcs group. + + As the TgoLayout will be used to create a TheaterGroundObject for a ForceGroup, + specialized classes inherit from this base class. For example there is a special + AiDefenseLayout which will be used to create the SamGroundObject from it. + """ + + def __init__(self, name: str, description: str = "") -> None: self.name = name - self.role = role self.description = description - self.tasks: list[GroupTask] = [] # The supported tasks - self.groups: list[GroupLayout] = [] + self.tasks: list[GroupTask] = [] # The supported - # If the template is generic it will be used the generate the general - # UnitGroups during faction initialization. Generic Groups allow to be mixed + # Mapping of group name and LayoutGroups for a specific TgoGroup + # A Group can have multiple TgoLayoutGroups which get merged together + self.groups: dict[str, list[TgoLayoutGroup]] = defaultdict(list) + + # A generic layout will be used to create generic ForceGroups during the + # campaign initialization. For each generic layout a new Group will be created. self.generic: bool = False def usable_by_faction(self, faction: Faction) -> bool: @@ -156,7 +180,7 @@ class TheaterLayout: try: return all( len(group.possible_types_for_faction(faction)) > 0 - for group in self.groups + for group in self.all_groups if not group.optional ) except LayoutException: @@ -168,22 +192,20 @@ class TheaterLayout: position: PointWithHeading, control_point: ControlPoint, ) -> TheaterGroundObject: - """TODO Documentation""" + """Create the TheaterGroundObject for the TgoLayout + + This function has to be implemented by the inheriting class to create + a specific TGO like SamGroundObject or BuildingGroundObject + """ raise NotImplementedError - def add_group(self, new_group: GroupLayout, index: int = 0) -> None: - """Adds a group in the correct order to the template""" - if len(self.groups) > index: - self.groups.insert(index, new_group) - else: - self.groups.append(new_group) - @property - def size(self) -> int: - return sum([len(group.units) for group in self.groups]) + def all_groups(self) -> Iterator[TgoLayoutGroup]: + for groups in self.groups.values(): + yield from groups -class AntiAirLayout(TheaterLayout): +class AntiAirLayout(TgoLayout): def create_ground_object( self, name: str, @@ -200,7 +222,7 @@ class AntiAirLayout(TheaterLayout): ) -class BuildingLayout(TheaterLayout): +class BuildingLayout(TgoLayout): def create_ground_object( self, name: str, @@ -224,7 +246,7 @@ class BuildingLayout(TheaterLayout): raise RuntimeError(f"Building Template {self.name} has no building category") -class NavalLayout(TheaterLayout): +class NavalLayout(TgoLayout): def create_ground_object( self, name: str, @@ -240,7 +262,7 @@ class NavalLayout(TheaterLayout): raise NotImplementedError -class DefensesLayout(TheaterLayout): +class DefensesLayout(TgoLayout): def create_ground_object( self, name: str, @@ -258,7 +280,7 @@ class DefensesLayout(TheaterLayout): raise NotImplementedError -class GroundForceLayout(TheaterLayout): +class GroundForceLayout(TgoLayout): def create_ground_object( self, name: str, diff --git a/game/layout/layoutloader.py b/game/layout/layoutloader.py index e727aa1f..502ebb86 100644 --- a/game/layout/layoutloader.py +++ b/game/layout/layoutloader.py @@ -1,4 +1,5 @@ from __future__ import annotations +from collections import defaultdict import itertools import logging @@ -13,10 +14,10 @@ from dcs import Point from dcs.unitgroup import StaticGroup from game import persistency -from game.data.groups import GroupRole, GroupTask +from game.data.groups import GroupRole from game.layout.layout import ( - TheaterLayout, - GroupLayout, + TgoLayout, + TgoLayoutGroup, LayoutUnit, AntiAirLayout, BuildingLayout, @@ -24,7 +25,7 @@ from game.layout.layout import ( GroundForceLayout, DefensesLayout, ) -from game.layout.layoutmapping import GroupLayoutMapping, LayoutMapping +from game.layout.layoutmapping import LayoutMapping from game.profiling import logged_duration from game.version import VERSION @@ -41,21 +42,21 @@ TEMPLATE_TYPES = { class LayoutLoader: - # list of layouts per category. e.g. AA or similar - _templates: dict[str, TheaterLayout] = {} + # Map of all available layouts indexed by name + _layouts: dict[str, TgoLayout] = {} def __init__(self) -> None: - self._templates = {} + self._layouts = {} def initialize(self) -> None: - if not self._templates: + if not self._layouts: with logged_duration("Loading layouts"): self.load_templates() @property - def layouts(self) -> Iterator[TheaterLayout]: + def layouts(self) -> Iterator[TgoLayout]: self.initialize() - yield from self._templates.values() + yield from self._layouts.values() def load_templates(self) -> None: """This will load all pre-loaded layouts from a pickle file. @@ -66,60 +67,43 @@ class LayoutLoader: # Load from pickle if existing with file.open("rb") as f: try: - version, self._templates = pickle.load(f) + version, self._layouts = pickle.load(f) # Check if the game version of the dump is identical to the current if version == VERSION: return except Exception as e: - logging.error(f"Error {e} reading layouts dump. Recreating.") + logging.exception(f"Error {e} reading layouts dump. Recreating.") # If no dump is available or game version is different create a new dump self.import_templates() def import_templates(self) -> None: """This will import all layouts from the template folder and dumps them to a pickle""" - mappings: dict[str, list[LayoutMapping]] = {} + self._layouts = {} + mappings: dict[str, list[LayoutMapping]] = defaultdict(list) with logged_duration("Parsing mapping yamls"): for file in Path(TEMPLATE_DIR).rglob("*.yaml"): if not file.is_file(): - continue + raise RuntimeError(f"{file.name} is not a file") with file.open("r", encoding="utf-8") as f: mapping_dict = yaml.safe_load(f) template_map = LayoutMapping.from_dict(mapping_dict, f.name) - - if template_map.layout_file in mappings: - mappings[template_map.layout_file].append(template_map) - else: - mappings[template_map.layout_file] = [template_map] + mappings[template_map.layout_file].append(template_map) with logged_duration(f"Parsing all layout miz multithreaded"): with ThreadPoolExecutor() as exe: - for miz, maps in mappings.items(): - exe.submit(self._load_from_miz, miz, maps) + exe.map(self._load_from_miz, mappings.keys(), mappings.values()) - logging.info(f"Imported {len(self._templates)} layouts") + logging.info(f"Imported {len(self._layouts)} layouts") self._dump_templates() def _dump_templates(self) -> None: file = Path(persistency.base_path()) / TEMPLATE_DUMP - dump = (VERSION, self._templates) + dump = (VERSION, self._layouts) with file.open("wb") as fdata: pickle.dump(dump, fdata) - @staticmethod - def mapping_for_group( - mappings: list[LayoutMapping], group_name: str - ) -> tuple[LayoutMapping, int, GroupLayoutMapping]: - for mapping in mappings: - for g_id, group_mapping in enumerate(mapping.groups): - if ( - group_mapping.name == group_name - or group_name in group_mapping.statics - ): - return mapping, g_id, group_mapping - raise KeyError - def _load_from_miz(self, miz: str, mappings: list[LayoutMapping]) -> None: template_position: dict[str, Point] = {} temp_mis = dcs.Mission() @@ -130,74 +114,68 @@ class LayoutLoader: # the .load_file() method: 0:00:00.920409 temp_mis.load_file(miz) - for country in itertools.chain( - temp_mis.coalition["red"].countries.values(), - temp_mis.coalition["blue"].countries.values(), - ): - for dcs_group in itertools.chain( - temp_mis.country(country.name).vehicle_group, - temp_mis.country(country.name).ship_group, - temp_mis.country(country.name).static_group, + for mapping in mappings: + # Find the group from the mapping in any coalition + for country in itertools.chain( + temp_mis.coalition["red"].countries.values(), + temp_mis.coalition["blue"].countries.values(), ): - try: - mapping, group_id, group_mapping = self.mapping_for_group( - mappings, dcs_group.name - ) - except KeyError: - logging.warning(f"No mapping for dcs group {dcs_group.name}") - continue + for dcs_group in itertools.chain( + temp_mis.country(country.name).vehicle_group, + temp_mis.country(country.name).ship_group, + temp_mis.country(country.name).static_group, + ): - template = self._templates.get(mapping.name, None) - if template is None: - # Create a new template - template = TEMPLATE_TYPES[mapping.role]( - mapping.name, mapping.role, mapping.description - ) - template.generic = mapping.generic - template.tasks = mapping.tasks - self._templates[template.name] = template - - for i, unit in enumerate(dcs_group.units): - group_template = None - for group in template.groups: - if group.name == group_mapping.name: - # We already have a layoutgroup for this dcs_group - group_template = group - if not group_template: - group_template = GroupLayout( - group_mapping.name, - [], - group_mapping.group, - group_mapping.unit_count, - group_mapping.unit_types, - group_mapping.unit_classes, - group_mapping.alternative_classes, + try: + group_name, group_mapping = mapping.group_for_name( + dcs_group.name ) - group_template.optional = group_mapping.optional - # Add the group at the correct position - template.add_group(group_template, group_id) - unit_template = LayoutUnit.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) + except KeyError: + continue - def by_name(self, template_name: str) -> Iterator[TheaterLayout]: - for template in self.layouts: - if template.name == template_name: - yield template + if not isinstance(dcs_group, StaticGroup) and max( + group_mapping.unit_count + ) > len(dcs_group.units): + logging.error( + f"Incorrect unit_count found in Layout {mapping.name}-{group_mapping.name}" + ) - def by_task(self, group_task: GroupTask) -> Iterator[TheaterLayout]: - for template in self.layouts: - if not group_task or group_task in template.tasks: - yield template + layout = self._layouts.get(mapping.name, None) + if layout is None: + # Create a new template + layout = TEMPLATE_TYPES[mapping.primary_role]( + mapping.name, mapping.description + ) + layout.generic = mapping.generic + layout.tasks = mapping.tasks + self._layouts[layout.name] = layout - def by_tasks(self, group_tasks: list[GroupTask]) -> Iterator[TheaterLayout]: - unique_templates = [] - for group_task in group_tasks: - for template in self.by_task(group_task): - if template not in unique_templates: - unique_templates.append(template) - yield from unique_templates + for i, unit in enumerate(dcs_group.units): + group_layout = None + for group in layout.all_groups: + if group.name == group_mapping.name: + # We already have a layoutgroup for this dcs_group + group_layout = group + if not group_layout: + group_layout = TgoLayoutGroup( + group_mapping.name, + [], + group_mapping.unit_count, + group_mapping.unit_types, + group_mapping.unit_classes, + group_mapping.fallback_classes, + ) + group_layout.optional = group_mapping.optional + # Add the group at the correct position + layout.groups[group_name].append(group_layout) + layout_unit = LayoutUnit.from_unit(unit) + if i == 0 and layout.name not in template_position: + template_position[layout.name] = unit.position + layout_unit.position = ( + layout_unit.position - template_position[layout.name] + ) + group_layout.layout_units.append(layout_unit) + + def by_name(self, name: str) -> TgoLayout: + self.initialize() + return self._layouts[name] diff --git a/game/layout/layoutmapping.py b/game/layout/layoutmapping.py index 5164e12f..b82a895f 100644 --- a/game/layout/layoutmapping.py +++ b/game/layout/layoutmapping.py @@ -1,13 +1,14 @@ from __future__ import annotations +from collections import defaultdict from dataclasses import dataclass, field from typing import Any, Type from dcs.unittype import UnitType as DcsUnitType -from game import db from game.data.groups import GroupRole, GroupTask from game.data.units import UnitClass +from game.dcs.helpers import unit_type_from_name @dataclass @@ -21,10 +22,6 @@ class GroupLayoutMapping: # All static units for the group statics: list[str] = field(default_factory=list) - # Defines to which tgo group the groupTemplate will be added - # This allows to merge groups back together. Default: Merge all to group 1 - group: int = field(default=1) - # How many units should be generated from the grouplayout. If only one value is # added this will be an exact amount. If 2 values are used it will be a random # amount between these values. @@ -36,31 +33,9 @@ class GroupLayoutMapping: # All unit classes the template supports. unit_classes: list[UnitClass] = field(default_factory=list) - # TODO Clarify if this is required. Only used for EWRs to also Use SR when no + # Fallback Classes which are used when the unit_classes and unit_types do not fit any accessible unit from the faction. Only used for EWRs to also Use SR when no # dedicated EWRs are available to the faction - alternative_classes: list[UnitClass] = field(default_factory=list) - - def to_dict(self) -> dict[str, Any]: - d = self.__dict__ - if not self.optional: - d.pop("optional") - if not self.statics: - d.pop("statics") - if not self.unit_types: - d.pop("unit_types") - if not self.unit_classes: - d.pop("unit_classes") - else: - d["unit_classes"] = [unit_class.value for unit_class in self.unit_classes] - if not self.alternative_classes: - d.pop("alternative_classes") - else: - d["alternative_classes"] = [ - unit_class.value for unit_class in self.alternative_classes - ] - if not self.unit_count: - d.pop("unit_count") - return d + fallback_classes: list[UnitClass] = field(default_factory=list) @staticmethod def from_dict(d: dict[str, Any]) -> GroupLayoutMapping: @@ -70,27 +45,25 @@ class GroupLayoutMapping: unit_types = [] if "unit_types" in d: for u in d["unit_types"]: - unit_type = db.unit_type_from_name(u) + unit_type = unit_type_from_name(u) if unit_type: unit_types.append(unit_type) - group = d["group"] if "group" in d else 1 unit_classes = ( [UnitClass(u) for u in d["unit_classes"]] if "unit_classes" in d else [] ) - alternative_classes = ( - [UnitClass(u) for u in d["alternative_classes"]] - if "alternative_classes" in d + fallback_classes = ( + [UnitClass(u) for u in d["fallback_classes"]] + if "fallback_classes" in d else [] ) return GroupLayoutMapping( d["name"], optional, statics, - group, unit_count, unit_types, unit_classes, - alternative_classes, + fallback_classes, ) @@ -105,40 +78,31 @@ class LayoutMapping: # Optional field to define if the template can be used to create generic groups generic: bool - # The role the template can be used for - role: GroupRole - # All taskings the template can be used for tasks: list[GroupTask] # All Groups the template has - groups: list[GroupLayoutMapping] + groups: dict[str, list[GroupLayoutMapping]] # Define the miz file for the template. Optional. If empty use the mapping name layout_file: str - def to_dict(self) -> dict[str, Any]: - d = { - "name": self.name, - "description": self.description, - "generic": self.generic, - "role": self.role.value, - "tasks": [task.description for task in self.tasks], - "groups": [group.to_dict() for group in self.groups], - "layout_file": self.layout_file, - } - if not self.description: - d.pop("description") - if not self.generic: - # Only save if true - d.pop("generic") - if not self.layout_file: - d.pop("layout_file") - return d + @property + def primary_role(self) -> GroupRole: + return self.tasks[0].role @staticmethod def from_dict(d: dict[str, Any], file_name: str) -> LayoutMapping: - groups = [GroupLayoutMapping.from_dict(group) for group in d["groups"]] + groups: dict[str, list[GroupLayoutMapping]] = defaultdict(list) + for group in d["groups"]: + for group_name, group_layouts in group.items(): + groups[group_name].extend( + [ + GroupLayoutMapping.from_dict(group_layout) + for group_layout in group_layouts + ] + ) + description = d["description"] if "description" in d else "" generic = d["generic"] if "generic" in d else False layout_file = ( @@ -149,8 +113,14 @@ class LayoutMapping: d["name"], description, generic, - GroupRole(d["role"]), tasks, groups, layout_file, ) + + def group_for_name(self, name: str) -> tuple[str, GroupLayoutMapping]: + for group_name, group_mappings in self.groups.items(): + for group_mapping in group_mappings: + if group_mapping.name == name or name in group_mapping.statics: + return group_name, group_mapping + raise KeyError diff --git a/game/theater/start_generator.py b/game/theater/start_generator.py index 462cebec..ec68edd6 100644 --- a/game/theater/start_generator.py +++ b/game/theater/start_generator.py @@ -153,11 +153,6 @@ class ControlPointGroundObjectGenerator: self.generate_navy() return True - def generate_random_ground_object( - self, unit_groups: list[ForceGroup], position: PointWithHeading - ) -> None: - self.generate_ground_object_from_group(random.choice(unit_groups), position) - def generate_ground_object_from_group( self, unit_group: ForceGroup, position: PointWithHeading ) -> None: diff --git a/game/theater/theatergroundobject.py b/game/theater/theatergroundobject.py index 89952a68..039ce266 100644 --- a/game/theater/theatergroundobject.py +++ b/game/theater/theatergroundobject.py @@ -3,7 +3,7 @@ from __future__ import annotations import itertools import logging from abc import ABC -from typing import Iterator, List, TYPE_CHECKING +from typing import Iterator, List, TYPE_CHECKING, Optional from dcs.unittype import VehicleType from dcs.vehicles import vehicle_map @@ -205,6 +205,12 @@ class TheaterGroundObject(MissionTarget): def purchasable(self) -> bool: raise NotImplementedError + def group_by_name(self, name: str) -> Optional[TheaterGroup]: + for group in self.groups: + if group.name == name: + return group + return None + class BuildingGroundObject(TheaterGroundObject): def __init__( diff --git a/game/theater/theatergroup.py b/game/theater/theatergroup.py index 2f66857d..0c34c74d 100644 --- a/game/theater/theatergroup.py +++ b/game/theater/theatergroup.py @@ -16,7 +16,7 @@ from game.point_with_heading import PointWithHeading from game.utils import Heading if TYPE_CHECKING: - from game.layout.layout import LayoutUnit, GroupLayout + from game.layout.layout import LayoutUnit, TgoLayoutGroup from game.theater import TheaterGroundObject @@ -141,16 +141,17 @@ class TheaterGroup: @staticmethod def from_template( id: int, - g: GroupLayout, + name: str, + units: list[TheaterUnit], go: TheaterGroundObject, unit_type: Type[DcsUnitType], unit_count: int, ) -> TheaterGroup: return TheaterGroup( id, - g.name, + name, PointWithHeading.from_point(go.position, go.heading), - g.generate_units(go, unit_type, unit_count), + units, go, ) diff --git a/qt_ui/windows/QLiberationWindow.py b/qt_ui/windows/QLiberationWindow.py index 00f9160a..e053a66f 100644 --- a/qt_ui/windows/QLiberationWindow.py +++ b/qt_ui/windows/QLiberationWindow.py @@ -18,10 +18,11 @@ from PySide2.QtWidgets import ( ) import qt_ui.uiconstants as CONST -from game import Game, VERSION, persistency, db +from game import Game, VERSION, persistency from game.debriefing import Debriefing from game.server import EventStream, GameContext from game.server.security import ApiKeyManager +from game.layout import LAYOUTS from qt_ui import liberation_install from qt_ui.dialogs import Dialog from qt_ui.models import GameModel @@ -400,7 +401,7 @@ class QLiberationWindow(QMainWindow): self.dialog.show() def import_templates(self): - db.LAYOUTS.import_templates() + LAYOUTS.import_templates() def showLogsDialog(self): self.dialog = QLogsWindow() diff --git a/qt_ui/windows/groundobject/QGroundObjectBuyMenu.py b/qt_ui/windows/groundobject/QGroundObjectBuyMenu.py index aa2ac5d7..71b3fb2f 100644 --- a/qt_ui/windows/groundobject/QGroundObjectBuyMenu.py +++ b/qt_ui/windows/groundobject/QGroundObjectBuyMenu.py @@ -1,3 +1,4 @@ +from collections import defaultdict import logging from dataclasses import dataclass, field from typing import Type @@ -14,6 +15,7 @@ from PySide2.QtWidgets import ( QSpinBox, QVBoxLayout, QCheckBox, + QWidget, ) from dcs.unittype import UnitType @@ -29,16 +31,17 @@ from game.theater.theatergroundobject import ( ) from game.theater.theatergroup import TheaterGroup from game.layout.layout import ( - TheaterLayout, - GroupLayout, + LayoutException, + TgoLayout, + TgoLayoutGroup, ) from qt_ui.uiconstants import EVENT_ICONS from qt_ui.windows.GameUpdateSignal import GameUpdateSignal @dataclass -class QGroupLayout: - layout: GroupLayout +class QTgoLayoutGroup: + layout: TgoLayoutGroup dcs_unit_type: Type[UnitType] amount: int unit_price: int @@ -50,41 +53,45 @@ class QGroupLayout: @dataclass -class QLayout: - layout: TheaterLayout +class QTgoLayout: + layout: TgoLayout force_group: ForceGroup - group_layouts: list[QGroupLayout] = field(default_factory=list) + groups: dict[str, list[QTgoLayoutGroup]] = field(default_factory=dict) @property def price(self) -> int: - return sum(group.price for group in self.group_layouts) + return sum(group.price for groups in self.groups.values() for group in groups) -class QGroundObjectGroupTemplate(QGroupBox): +class QTgoLayoutGroupRow(QWidget): group_template_changed = Signal() - def __init__( - self, group_id: int, force_group: ForceGroup, group_layout: GroupLayout - ) -> None: - super().__init__(f"{group_id + 1}: {group_layout.name}") + def __init__(self, force_group: ForceGroup, group: TgoLayoutGroup) -> None: + super().__init__() self.grid_layout = QGridLayout() self.setLayout(self.grid_layout) - + self.grid_layout.setColumnStretch(0, 100) self.amount_selector = QSpinBox() self.unit_selector = QComboBox() + self.unit_selector.setMinimumWidth(250) self.group_selector = QCheckBox() # Add all possible units with the price - for unit_type in force_group.unit_types_for_group(group_layout): + for unit_type in force_group.unit_types_for_group(group): self.unit_selector.addItem( f"{unit_type.name} [${unit_type.price}M]", userData=(unit_type.dcs_unit_type, unit_type.price), ) # Add all possible statics with price = 0 - for static_type in force_group.statics_for_group(group_layout): + for static_type in force_group.statics_for_group(group): self.unit_selector.addItem( f"{static_type} (Static)", userData=(static_type, 0) ) + + if self.unit_selector.count() == 0: + raise LayoutException("No units available for the TgoLayoutGroup") + + self.unit_selector.adjustSize() self.unit_selector.setEnabled(self.unit_selector.count() > 1) self.grid_layout.addWidget(self.unit_selector, 0, 0, alignment=Qt.AlignRight) self.grid_layout.addWidget(self.amount_selector, 0, 1, alignment=Qt.AlignRight) @@ -93,9 +100,7 @@ class QGroundObjectGroupTemplate(QGroupBox): self.unit_selector.currentIndex() ) - self.group_layout = QGroupLayout( - group_layout, unit_type, group_layout.unit_counter, price - ) + self.group_layout = QTgoLayoutGroup(group, unit_type, group.group_size, price) self.group_selector.setChecked(self.group_layout.enabled) self.group_selector.setEnabled(self.group_layout.layout.optional) @@ -127,11 +132,11 @@ class QGroundObjectTemplateLayout(QGroupBox): self, game: Game, ground_object: TheaterGroundObject, - layout: QLayout, - layout_changed_signal: Signal(QLayout), + layout: QTgoLayout, + layout_changed_signal: Signal(QTgoLayout), current_group_value: int, ): - super().__init__("Groups:") + super().__init__() # Connect to the signal to handle template updates self.game = game self.ground_object = ground_object @@ -158,21 +163,32 @@ class QGroundObjectTemplateLayout(QGroupBox): # Load Layout self.load_for_layout(self.layout_model) - def load_for_layout(self, layout: QLayout) -> None: + def load_for_layout(self, layout: QTgoLayout) -> None: self.layout_model = layout # Clean the current grid + self.layout_model.groups = defaultdict(list) for id in range(self.template_grid.count()): self.template_grid.itemAt(id).widget().deleteLater() - for g_id, layout_group in enumerate(self.layout_model.layout.groups): - group_row = QGroundObjectGroupTemplate( - g_id, self.layout_model.force_group, layout_group - ) - self.layout_model.group_layouts.append(group_row.group_layout) - group_row.group_template_changed.connect(self.group_template_changed) - self.template_grid.addWidget(group_row) - + for group_name, groups in self.layout_model.layout.groups.items(): + self.add_theater_group(group_name, self.layout_model.force_group, groups) self.group_template_changed() + def add_theater_group( + self, group_name: str, force_group: ForceGroup, groups: list[TgoLayoutGroup] + ) -> None: + group_box = QGroupBox(group_name) + vbox_layout = QVBoxLayout() + for group in groups: + try: + group_row = QTgoLayoutGroupRow(force_group, group) + except LayoutException: + continue + self.layout_model.groups[group_name].append(group_row.group_layout) + group_row.group_template_changed.connect(self.group_template_changed) + vbox_layout.addWidget(group_row) + group_box.setLayout(vbox_layout) + self.template_grid.addWidget(group_box) + def group_template_changed(self) -> None: price = self.layout_model.price self.buy_button.setText(f"Buy [${price}M][-${self.current_group_value}M]") @@ -182,54 +198,40 @@ class QGroundObjectTemplateLayout(QGroupBox): else: self.buy_button.setToolTip("Not enough money to buy this group") - def buy_group(self): - if not self.layout: - raise RuntimeError("No template selected. GroundObject can not be bought.") - + def buy_group(self) -> None: price = self.layout_model.price if price > self.game.blue.budget: - # Somethin went wrong. Buy button should be disabled! + # Something went wrong. Buy button should be disabled! logging.error("Not enough money to buy the group") return self.game.blue.budget -= price - self.current_group_value - self.ground_object.groups = self.generate_groups() + self.ground_object.groups = [] + for group_name, groups in self.layout_model.groups.items(): + for group in groups: + self.layout_model.force_group.create_theater_group_for_tgo( + self.ground_object, + group.layout, + f"{self.ground_object.name} ({group_name})", + self.game, + group.dcs_unit_type, # Forced Type + group.amount, # Forced Amount + ) # Replan redfor missions self.game.initialize_turn(for_red=True, for_blue=False) GameUpdateSignal.get_instance().updateGame(self.game) - def generate_groups(self) -> list[TheaterGroup]: - go = self.layout_model.layout.create_ground_object( - self.ground_object.name, - PointWithHeading.from_point( - self.ground_object.position, self.ground_object.heading - ), - self.ground_object.control_point, - ) - - for group in self.layout_model.group_layouts: - self.layout_model.force_group.create_theater_group_for_tgo( - go, - group.layout, - self.ground_object.name, - self.game, - group.dcs_unit_type, # Forced Type - group.amount, # Forced Amount - ) - - return go.groups - class QGroundObjectBuyMenu(QDialog): - layout_changed_signal = Signal(QLayout) + layout_changed_signal = Signal(QTgoLayout) def __init__( self, - parent, + parent: QWidget, ground_object: TheaterGroundObject, game: Game, current_group_value: int, - ): + ) -> None: super().__init__(parent) self.setMinimumWidth(350) @@ -241,7 +243,9 @@ class QGroundObjectBuyMenu(QDialog): self.setLayout(self.mainLayout) self.force_group_selector = QComboBox() + self.force_group_selector.setMinimumWidth(250) self.layout_selector = QComboBox() + self.layout_selector.setMinimumWidth(250) self.layout_selector.setEnabled(False) # Get the layouts and fill the combobox @@ -262,19 +266,19 @@ class QGroundObjectBuyMenu(QDialog): for group in game.blue.armed_forces.groups_for_tasks(tasks): self.force_group_selector.addItem(group.name, userData=group) self.force_group_selector.setEnabled(self.force_group_selector.count() > 1) - + self.force_group_selector.adjustSize() force_group = self.force_group_selector.itemData( self.force_group_selector.currentIndex() ) for layout in force_group.layouts: self.layout_selector.addItem(layout.name, userData=layout) - + self.layout_selector.adjustSize() selected_template = self.layout_selector.itemData( self.layout_selector.currentIndex() ) - self.layout_model = QLayout(selected_template, force_group) + self.theater_layout = QTgoLayout(selected_template, force_group) self.layout_selector.currentIndexChanged.connect(self.layout_changed) self.force_group_selector.currentIndexChanged.connect(self.force_group_changed) @@ -295,7 +299,7 @@ class QGroundObjectBuyMenu(QDialog): self.template_layout = QGroundObjectTemplateLayout( game, ground_object, - self.layout_model, + self.theater_layout, self.layout_changed_signal, current_group_value, ) @@ -311,19 +315,19 @@ class QGroundObjectBuyMenu(QDialog): self.layout_selector.clear() for layout in unit_group.layouts: self.layout_selector.addItem(layout.name, userData=layout) + self.layout_selector.adjustSize() # Enable if more than one template is available self.layout_selector.setEnabled(len(unit_group.layouts) > 1) # Enable Combobox Signals again self.layout_selector.blockSignals(False) self.layout_changed() - def layout_changed(self): + def layout_changed(self) -> None: self.layout() - self.layout_model.layout = self.layout_selector.itemData( + self.theater_layout.layout = self.layout_selector.itemData( self.layout_selector.currentIndex() ) - self.layout_model.force_group = self.force_group_selector.itemData( + self.theater_layout.force_group = self.force_group_selector.itemData( self.force_group_selector.currentIndex() ) - self.layout_model.group_layouts = [] - self.layout_changed_signal.emit(self.layout_model) + self.layout_changed_signal.emit(self.theater_layout) diff --git a/resources/units/groups/Ally-Flak.yaml b/resources/groups/Ally-Flak.yaml similarity index 93% rename from resources/units/groups/Ally-Flak.yaml rename to resources/groups/Ally-Flak.yaml index f5264f9a..a53857b3 100644 --- a/resources/units/groups/Ally-Flak.yaml +++ b/resources/groups/Ally-Flak.yaml @@ -1,5 +1,4 @@ name: Ally Flak -role: AntiAir tasks: - AAA units: diff --git a/resources/units/groups/Carrier_Strike_Group_8.yaml b/resources/groups/Carrier_Strike_Group_8.yaml similarity index 92% rename from resources/units/groups/Carrier_Strike_Group_8.yaml rename to resources/groups/Carrier_Strike_Group_8.yaml index 71d6a952..d91d6948 100644 --- a/resources/units/groups/Carrier_Strike_Group_8.yaml +++ b/resources/groups/Carrier_Strike_Group_8.yaml @@ -1,5 +1,4 @@ name: Carrier Strike Group 8 -role: Naval tasks: - Navy units: diff --git a/resources/units/groups/Chinese-Navy.yaml b/resources/groups/Chinese-Navy.yaml similarity index 91% rename from resources/units/groups/Chinese-Navy.yaml rename to resources/groups/Chinese-Navy.yaml index 8853e6c3..629a20b5 100644 --- a/resources/units/groups/Chinese-Navy.yaml +++ b/resources/groups/Chinese-Navy.yaml @@ -1,5 +1,4 @@ name: Chinese Navy -role: Naval tasks: - Navy units: diff --git a/resources/units/groups/Cold-War-Flak.yaml b/resources/groups/Cold-War-Flak.yaml similarity index 88% rename from resources/units/groups/Cold-War-Flak.yaml rename to resources/groups/Cold-War-Flak.yaml index 7a530fdb..5b44a101 100644 --- a/resources/units/groups/Cold-War-Flak.yaml +++ b/resources/groups/Cold-War-Flak.yaml @@ -1,5 +1,4 @@ name: Cold-War-Flak -role: AntiAir tasks: - AAA units: diff --git a/resources/units/groups/Flak.yaml b/resources/groups/Flak.yaml similarity index 95% rename from resources/units/groups/Flak.yaml rename to resources/groups/Flak.yaml index 262d6dec..5cf59d3c 100644 --- a/resources/units/groups/Flak.yaml +++ b/resources/groups/Flak.yaml @@ -1,5 +1,4 @@ name: Flak -role: AntiAir tasks: - AAA units: diff --git a/resources/units/groups/Freya.yaml b/resources/groups/Freya.yaml similarity index 94% rename from resources/units/groups/Freya.yaml rename to resources/groups/Freya.yaml index 1aeff638..d64a5cbb 100644 --- a/resources/units/groups/Freya.yaml +++ b/resources/groups/Freya.yaml @@ -1,5 +1,4 @@ name: Freya -role: AntiAir tasks: - SHORAD units: diff --git a/resources/units/groups/HQ-7.yaml b/resources/groups/HQ-7.yaml similarity index 88% rename from resources/units/groups/HQ-7.yaml rename to resources/groups/HQ-7.yaml index 158dcc91..195e566d 100644 --- a/resources/units/groups/HQ-7.yaml +++ b/resources/groups/HQ-7.yaml @@ -1,5 +1,4 @@ name: HQ-7 -role: AntiAir tasks: - SHORAD units: diff --git a/resources/units/groups/Hawk.yaml b/resources/groups/Hawk.yaml similarity index 92% rename from resources/units/groups/Hawk.yaml rename to resources/groups/Hawk.yaml index a449e5a4..50ff4954 100644 --- a/resources/units/groups/Hawk.yaml +++ b/resources/groups/Hawk.yaml @@ -1,5 +1,4 @@ name: Hawk -role: AntiAir tasks: - MERAD units: diff --git a/resources/units/groups/KS-19.yaml b/resources/groups/KS-19.yaml similarity index 88% rename from resources/units/groups/KS-19.yaml rename to resources/groups/KS-19.yaml index 90b105b9..3b5811e1 100644 --- a/resources/units/groups/KS-19.yaml +++ b/resources/groups/KS-19.yaml @@ -1,5 +1,4 @@ name: KS-19 -role: AntiAir tasks: - AAA units: diff --git a/resources/units/groups/NASAMS-B.yaml b/resources/groups/NASAMS-B.yaml similarity index 91% rename from resources/units/groups/NASAMS-B.yaml rename to resources/groups/NASAMS-B.yaml index 0b31c541..d626bdb6 100644 --- a/resources/units/groups/NASAMS-B.yaml +++ b/resources/groups/NASAMS-B.yaml @@ -1,5 +1,4 @@ name: NASAMS AIM-120B -role: AntiAir tasks: - MERAD units: diff --git a/resources/units/groups/NASAMS-C.yaml b/resources/groups/NASAMS-C.yaml similarity index 91% rename from resources/units/groups/NASAMS-C.yaml rename to resources/groups/NASAMS-C.yaml index 71487e30..4c0a900c 100644 --- a/resources/units/groups/NASAMS-C.yaml +++ b/resources/groups/NASAMS-C.yaml @@ -1,5 +1,4 @@ name: NASAMS AIM-120C -role: AntiAir tasks: - MERAD units: diff --git a/resources/units/groups/Patriot.yaml b/resources/groups/Patriot.yaml similarity index 93% rename from resources/units/groups/Patriot.yaml rename to resources/groups/Patriot.yaml index fea36eb8..8581c77b 100644 --- a/resources/units/groups/Patriot.yaml +++ b/resources/groups/Patriot.yaml @@ -1,5 +1,4 @@ name: Patriot -role: AntiAir tasks: - LORAD units: diff --git a/resources/units/groups/Rapier.yaml b/resources/groups/Rapier.yaml similarity index 90% rename from resources/units/groups/Rapier.yaml rename to resources/groups/Rapier.yaml index 146e52a1..aaa4b0fe 100644 --- a/resources/units/groups/Rapier.yaml +++ b/resources/groups/Rapier.yaml @@ -1,5 +1,4 @@ name: Rapier -role: AntiAir tasks: - SHORAD units: diff --git a/resources/units/groups/Roland.yaml b/resources/groups/Roland.yaml similarity index 88% rename from resources/units/groups/Roland.yaml rename to resources/groups/Roland.yaml index 4d74891a..ecdb6bcf 100644 --- a/resources/units/groups/Roland.yaml +++ b/resources/groups/Roland.yaml @@ -1,5 +1,4 @@ name: Roland -role: AntiAir tasks: - SHORAD units: diff --git a/resources/units/groups/Russian-Navy.yaml b/resources/groups/Russian-Navy.yaml similarity index 94% rename from resources/units/groups/Russian-Navy.yaml rename to resources/groups/Russian-Navy.yaml index aea04a06..afe7a19a 100644 --- a/resources/units/groups/Russian-Navy.yaml +++ b/resources/groups/Russian-Navy.yaml @@ -1,5 +1,4 @@ name: Russian Navy -role: Naval tasks: - Navy units: diff --git a/resources/units/groups/SA-10.yaml b/resources/groups/SA-10.yaml similarity index 95% rename from resources/units/groups/SA-10.yaml rename to resources/groups/SA-10.yaml index d7cf1b54..4619accc 100644 --- a/resources/units/groups/SA-10.yaml +++ b/resources/groups/SA-10.yaml @@ -1,5 +1,4 @@ name: SA-10/S-300PS -role: AntiAir tasks: - LORAD units: diff --git a/resources/units/groups/SA-10B.yaml b/resources/groups/SA-10B.yaml similarity index 94% rename from resources/units/groups/SA-10B.yaml rename to resources/groups/SA-10B.yaml index 3753e474..0194d155 100644 --- a/resources/units/groups/SA-10B.yaml +++ b/resources/groups/SA-10B.yaml @@ -1,5 +1,4 @@ name: SA-10B/S-300PS -role: AntiAir tasks: - LORAD units: diff --git a/resources/units/groups/SA-11.yaml b/resources/groups/SA-11.yaml similarity index 92% rename from resources/units/groups/SA-11.yaml rename to resources/groups/SA-11.yaml index 5a92e706..f57a8a1f 100644 --- a/resources/units/groups/SA-11.yaml +++ b/resources/groups/SA-11.yaml @@ -1,5 +1,4 @@ name: SA-11 -role: AntiAir tasks: - MERAD units: diff --git a/resources/units/groups/SA-12.yaml b/resources/groups/SA-12.yaml similarity index 94% rename from resources/units/groups/SA-12.yaml rename to resources/groups/SA-12.yaml index 2065b0a6..3f20b75d 100644 --- a/resources/units/groups/SA-12.yaml +++ b/resources/groups/SA-12.yaml @@ -1,5 +1,4 @@ name: SA-12/S-300V -role: AntiAir tasks: - LORAD units: diff --git a/resources/units/groups/SA-17.yaml b/resources/groups/SA-17.yaml similarity index 92% rename from resources/units/groups/SA-17.yaml rename to resources/groups/SA-17.yaml index d6194841..b8627e05 100644 --- a/resources/units/groups/SA-17.yaml +++ b/resources/groups/SA-17.yaml @@ -1,5 +1,4 @@ name: SA-17 -role: AntiAir tasks: - MERAD units: diff --git a/resources/units/groups/SA-2.yaml b/resources/groups/SA-2.yaml similarity index 92% rename from resources/units/groups/SA-2.yaml rename to resources/groups/SA-2.yaml index fdeed9aa..72124b7a 100644 --- a/resources/units/groups/SA-2.yaml +++ b/resources/groups/SA-2.yaml @@ -1,5 +1,4 @@ name: SA-2/S-75 -role: AntiAir tasks: - MERAD units: diff --git a/resources/units/groups/SA-20.yaml b/resources/groups/SA-20.yaml similarity index 95% rename from resources/units/groups/SA-20.yaml rename to resources/groups/SA-20.yaml index 8b49f398..e5d9b84c 100644 --- a/resources/units/groups/SA-20.yaml +++ b/resources/groups/SA-20.yaml @@ -1,5 +1,4 @@ name: SA-20/S-300PMU-1 -role: AntiAir tasks: - LORAD units: diff --git a/resources/units/groups/SA-20B.yaml b/resources/groups/SA-20B.yaml similarity index 94% rename from resources/units/groups/SA-20B.yaml rename to resources/groups/SA-20B.yaml index 73ab7b28..82e74289 100644 --- a/resources/units/groups/SA-20B.yaml +++ b/resources/groups/SA-20B.yaml @@ -1,5 +1,4 @@ name: SA-20B/S-300PMU-2 -role: AntiAir tasks: - LORAD units: diff --git a/resources/units/groups/SA-23.yaml b/resources/groups/SA-23.yaml similarity index 94% rename from resources/units/groups/SA-23.yaml rename to resources/groups/SA-23.yaml index e9d63f4e..e6439299 100644 --- a/resources/units/groups/SA-23.yaml +++ b/resources/groups/SA-23.yaml @@ -1,5 +1,4 @@ name: SA-23/S-300VM -role: AntiAir tasks: - LORAD units: diff --git a/resources/units/groups/SA-3.yaml b/resources/groups/SA-3.yaml similarity index 92% rename from resources/units/groups/SA-3.yaml rename to resources/groups/SA-3.yaml index 391264dd..5e5dc8bf 100644 --- a/resources/units/groups/SA-3.yaml +++ b/resources/groups/SA-3.yaml @@ -1,5 +1,4 @@ name: SA-3/S-125 -role: AntiAir tasks: - MERAD units: diff --git a/resources/units/groups/SA-5.yaml b/resources/groups/SA-5.yaml similarity index 92% rename from resources/units/groups/SA-5.yaml rename to resources/groups/SA-5.yaml index 2d36301a..c523c766 100644 --- a/resources/units/groups/SA-5.yaml +++ b/resources/groups/SA-5.yaml @@ -1,5 +1,4 @@ name: SA-5/S-200 -role: AntiAir tasks: - LORAD units: diff --git a/resources/units/groups/SA-6.yaml b/resources/groups/SA-6.yaml similarity index 90% rename from resources/units/groups/SA-6.yaml rename to resources/groups/SA-6.yaml index c597a96c..e54a7e2b 100644 --- a/resources/units/groups/SA-6.yaml +++ b/resources/groups/SA-6.yaml @@ -1,5 +1,4 @@ name: SA-6 -role: AntiAir tasks: - MERAD units: diff --git a/resources/units/groups/Silkworm.yaml b/resources/groups/Silkworm.yaml similarity index 87% rename from resources/units/groups/Silkworm.yaml rename to resources/groups/Silkworm.yaml index 426580ff..f77f2c34 100644 --- a/resources/units/groups/Silkworm.yaml +++ b/resources/groups/Silkworm.yaml @@ -1,5 +1,4 @@ name: Silkworm -role: Defenses tasks: - Coastal units: diff --git a/resources/units/groups/WW2LST.yaml b/resources/groups/WW2LST.yaml similarity index 88% rename from resources/units/groups/WW2LST.yaml rename to resources/groups/WW2LST.yaml index 73a377b6..e2e1e115 100644 --- a/resources/units/groups/WW2LST.yaml +++ b/resources/groups/WW2LST.yaml @@ -1,5 +1,4 @@ name: WW2LST -role: Naval tasks: - Navy units: diff --git a/resources/layouts/anti_air/AAA_Mobile.yaml b/resources/layouts/anti_air/AAA_Mobile.yaml index 6d5a0c7f..ac88b6e4 100644 --- a/resources/layouts/anti_air/AAA_Mobile.yaml +++ b/resources/layouts/anti_air/AAA_Mobile.yaml @@ -1,23 +1,21 @@ name: AAA Mobile description: A standard AAA template -generic: true -role: AntiAir +generic: true # This Layout will be used to generate ForceGroups tasks: - AAA groups: - - name: AAA Mobile 0 - group: 1 - unit_count: - - 2 - - 6 - unit_classes: - - AAA - - name: AAA Mobile 1 - optional: true - group: 1 - unit_count: - - 1 - - 2 - unit_classes: - - Logistics + - AAA: # Group Name + - name: AAA Mobile 0 # Sub group which will be merged into the group + unit_count: + - 2 + - 6 + unit_classes: + - AAA + - name: AAA Mobile 1 # Sub group which will be merged into the group + optional: true + unit_count: + - 1 + - 2 + unit_classes: + - Logistics layout_file: resources/layouts/anti_air/AAA.miz \ No newline at end of file diff --git a/resources/layouts/anti_air/AAA_Radar.yaml b/resources/layouts/anti_air/AAA_Radar.yaml index 05ea74b2..cbdde869 100644 --- a/resources/layouts/anti_air/AAA_Radar.yaml +++ b/resources/layouts/anti_air/AAA_Radar.yaml @@ -1,29 +1,26 @@ name: AAA Radar Site description: AAA Template with a Radar generic: false -role: AntiAir tasks: - AAA groups: - - name: AAA Radar Site 0 - group: 1 - unit_count: - - 1 - unit_classes: - - SearchRadar - - name: AAA Radar Site 1 - group: 1 - unit_count: - - 2 - - 6 - unit_classes: - - AAA - - name: AAA Radar Site 2 - optional: true - group: 1 - unit_count: - - 1 - - 2 - unit_classes: - - Logistics + - AAA: + - name: AAA Radar Site 0 + unit_count: + - 1 + unit_classes: + - SearchRadar + - name: AAA Radar Site 1 + unit_count: + - 2 + - 6 + unit_classes: + - AAA + - name: AAA Radar Site 2 + optional: true + unit_count: + - 1 + - 2 + unit_classes: + - Logistics layout_file: resources/layouts/anti_air/AAA.miz \ No newline at end of file diff --git a/resources/layouts/anti_air/AAA_Site.yaml b/resources/layouts/anti_air/AAA_Site.yaml index 5c8f8757..2504462f 100644 --- a/resources/layouts/anti_air/AAA_Site.yaml +++ b/resources/layouts/anti_air/AAA_Site.yaml @@ -1,23 +1,21 @@ name: AAA Site description: A standard AAA template generic: true -role: AntiAir tasks: - AAA groups: - - name: AAA Site 0 - group: 1 - unit_count: - - 2 - - 6 - unit_classes: - - AAA - - name: AAA Site 1 - optional: true - group: 1 - unit_count: - - 1 - - 2 - unit_classes: - - Logistics + - AAA: + - name: AAA Site 0 + unit_count: + - 2 + - 6 + unit_classes: + - AAA + - name: AAA Site 1 + optional: true + unit_count: + - 1 + - 2 + unit_classes: + - Logistics layout_file: resources/layouts/anti_air/AAA.miz \ No newline at end of file diff --git a/resources/layouts/anti_air/Cold_War_Flak_Site.yaml b/resources/layouts/anti_air/Cold_War_Flak_Site.yaml index aaceceed..c0028079 100644 --- a/resources/layouts/anti_air/Cold_War_Flak_Site.yaml +++ b/resources/layouts/anti_air/Cold_War_Flak_Site.yaml @@ -1,36 +1,36 @@ name: Cold War Flak Site -role: AntiAir tasks: - AAA groups: - - name: Cold War Flak Site Radar - optional: true # Only available to Late Cold War - unit_count: - - 1 - unit_classes: - - SearchRadar - - name: Cold War Flak Site Flak - unit_count: - - 4 - - 6 - unit_types: - - flak18 - - name: Cold War Flak Site S-60 - unit_count: - - 2 - unit_types: - - S-60_Type59_Artillery - - name: Cold War Flak Site AAA - optional: true - unit_count: - - 2 - unit_classes: - - AAA - - name: Cold War Flak Site Logistics - optional: true - unit_count: - - 1 - - 2 - unit_classes: - - Logistics + - AAA: + - name: Cold War Flak Site Radar + optional: true # Only available to Late Cold War + unit_count: + - 1 + unit_classes: + - SearchRadar + - name: Cold War Flak Site Flak + unit_count: + - 4 + - 6 + unit_types: + - flak18 + - name: Cold War Flak Site S-60 + unit_count: + - 2 + unit_types: + - S-60_Type59_Artillery + - name: Cold War Flak Site AAA + optional: true + unit_count: + - 2 + unit_classes: + - AAA + - name: Cold War Flak Site Logistics + optional: true + unit_count: + - 1 + - 2 + unit_classes: + - Logistics layout_file: resources/layouts/anti_air/flak.miz diff --git a/resources/layouts/anti_air/Early-Warning_Radar.yaml b/resources/layouts/anti_air/Early-Warning_Radar.yaml index 6e5573b0..6047a6cf 100644 --- a/resources/layouts/anti_air/Early-Warning_Radar.yaml +++ b/resources/layouts/anti_air/Early-Warning_Radar.yaml @@ -1,15 +1,15 @@ name: Early-Warning Radar generic: true -role: AntiAir tasks: - EarlyWarningRadar groups: - - name: Early-Warning Radar 0 - unit_count: - - 1 - unit_classes: - - EarlyWarningRadar - alternative_classes: - - SearchRadar - - SearchTrackRadar + - EWR: + - name: Early-Warning Radar 0 + unit_count: + - 1 + unit_classes: + - EarlyWarningRadar + alternative_classes: + - SearchRadar + - SearchTrackRadar layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/Flak_Site.yaml b/resources/layouts/anti_air/Flak_Site.yaml index df966c96..1d2d0f76 100644 --- a/resources/layouts/anti_air/Flak_Site.yaml +++ b/resources/layouts/anti_air/Flak_Site.yaml @@ -1,51 +1,51 @@ name: Flak Site -role: AntiAir tasks: - AAA groups: - - name: Flak Site 0 - unit_count: - - 4 - unit_types: - - flak38 - - flak18 - - flak36 - - flak37 - - flak41 - - flak30 - - name: Flak Site 1 - unit_count: - - 1 - unit_types: - - flak38 - - name: Flak Site 2 - unit_count: - - 1 - unit_types: - - flak36 - - name: Flak Site 3 - unit_count: - - 2 - unit_types: - - Flakscheinwerfer_37 - - name: Flak Site 4 - unit_count: - - 1 - unit_types: - - Maschinensatz_33 - - name: Flak Site 5 - unit_count: - - 1 - unit_types: - - KDO_Mod40 - - name: Flak Site 6 - unit_count: - - 1 - unit_types: - - Kubelwagen_82 - - name: Flak Site 7 - unit_count: - - 4 - unit_types: - - Blitz_36-6700A + - Flak: + - name: Flak Site 0 + unit_count: + - 4 + unit_types: + - flak38 + - flak18 + - flak36 + - flak37 + - flak41 + - flak30 + - name: Flak Site 1 + unit_count: + - 1 + unit_types: + - flak38 + - name: Flak Site 2 + unit_count: + - 1 + unit_types: + - flak36 + - name: Flak Site 3 + unit_count: + - 2 + unit_types: + - Flakscheinwerfer_37 + - name: Flak Site 4 + unit_count: + - 1 + unit_types: + - Maschinensatz_33 + - name: Flak Site 5 + unit_count: + - 1 + unit_types: + - KDO_Mod40 + - name: Flak Site 6 + unit_count: + - 1 + unit_types: + - Kubelwagen_82 + - name: Flak Site 7 + unit_count: + - 4 + unit_types: + - Blitz_36-6700A layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/Freya_EWR_Site.yaml b/resources/layouts/anti_air/Freya_EWR_Site.yaml index 54ac6a90..6d77d907 100644 --- a/resources/layouts/anti_air/Freya_EWR_Site.yaml +++ b/resources/layouts/anti_air/Freya_EWR_Site.yaml @@ -1,51 +1,51 @@ name: Freya EWR Site -role: AntiAir tasks: - SHORAD groups: - - name: Freya EWR Site 0 - unit_count: - - 1 - unit_types: - - FuMG-401 - - name: Freya EWR Site 1 - unit_count: - - 4 - unit_types: - - flak38 - - name: Freya EWR Site 2 - unit_count: - - 4 - unit_types: - - flak18 - - name: Freya EWR Site 3 - unit_count: - - 1 - unit_types: - - Kubelwagen_82 - - name: Freya EWR Site 4 - unit_count: - - 1 - unit_types: - - Sd_Kfz_7 - - name: Freya EWR Site 5 - unit_count: - - 1 - unit_types: - - Sd_Kfz_2 - - name: Freya EWR Site 6 - unit_count: - - 1 - unit_types: - - Maschinensatz_33 - - name: Freya EWR Site 7 - unit_count: - - 1 - unit_types: - - KDO_Mod40 - - name: Freya EWR Site 8 - unit_count: - - 3 - unit_types: - - soldier_mauser98 + - Freya: + - name: Freya EWR Site 0 + unit_count: + - 1 + unit_types: + - FuMG-401 + - name: Freya EWR Site 1 + unit_count: + - 4 + unit_types: + - flak38 + - name: Freya EWR Site 2 + unit_count: + - 4 + unit_types: + - flak18 + - name: Freya EWR Site 3 + unit_count: + - 1 + unit_types: + - Kubelwagen_82 + - name: Freya EWR Site 4 + unit_count: + - 1 + unit_types: + - Sd_Kfz_7 + - name: Freya EWR Site 5 + unit_count: + - 1 + unit_types: + - Sd_Kfz_2 + - name: Freya EWR Site 6 + unit_count: + - 1 + unit_types: + - Maschinensatz_33 + - name: Freya EWR Site 7 + unit_count: + - 1 + unit_types: + - KDO_Mod40 + - name: Freya EWR Site 8 + unit_count: + - 3 + unit_types: + - soldier_mauser98 layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/HQ-7_Site.yaml b/resources/layouts/anti_air/HQ-7_Site.yaml index eeafb707..1c0f7a29 100644 --- a/resources/layouts/anti_air/HQ-7_Site.yaml +++ b/resources/layouts/anti_air/HQ-7_Site.yaml @@ -1,24 +1,21 @@ name: HQ-7 Site -role: AntiAir tasks: - SHORAD groups: - - name: HQ-7 Site 0 - group: 1 - unit_count: - - 1 - unit_types: - - HQ-7_STR_SP - - name: HQ-7 Site 1 - group: 1 - unit_count: - - 2 - unit_types: - - HQ-7_LN_SP - - name: HQ-7 Site 2 - group: 2 - unit_count: - - 2 - unit_types: - - Ural-375 ZU-23 + - HQ-7: + - name: HQ-7 Site 0 + unit_count: + - 1 + unit_types: + - HQ-7_STR_SP + - name: HQ-7 Site 1 + unit_count: + - 2 + unit_types: + - HQ-7_LN_SP + - name: HQ-7 Site 2 + unit_count: + - 2 + unit_types: + - Ural-375 ZU-23 layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/Hawk_Site.yaml b/resources/layouts/anti_air/Hawk_Site.yaml index a4872fee..60c15886 100644 --- a/resources/layouts/anti_air/Hawk_Site.yaml +++ b/resources/layouts/anti_air/Hawk_Site.yaml @@ -1,36 +1,33 @@ name: Hawk Site -role: AntiAir tasks: - MERAD groups: - - name: Hawk Site 0 - group: 1 - unit_count: - - 1 - unit_types: - - Hawk sr - - name: Hawk Site 1 - group: 1 - unit_count: - - 1 - unit_types: - - Hawk pcp - - name: Hawk Site 2 - group: 1 - unit_count: - - 1 - unit_types: - - Hawk tr - - name: Hawk Site 3 - group: 1 - unit_count: - - 6 - unit_types: - - Hawk ln - - name: Hawk Site 4 - group: 2 - unit_count: - - 1 - unit_types: - - Vulcan + - Hawk: # Main Battery as one group + - name: Hawk Site 0 + unit_count: + - 1 + unit_types: + - Hawk sr + - name: Hawk Site 1 + unit_count: + - 1 + unit_types: + - Hawk pcp + - name: Hawk Site 2 + unit_count: + - 1 + unit_types: + - Hawk tr + - name: Hawk Site 3 + unit_count: + - 6 + unit_types: + - Hawk ln + - PD: # Point Defense as separate group + - name: Hawk Site 4 + optional: true + unit_count: + - 1 + unit_types: + - Vulcan layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/NASAMS_AIM-120B.yaml b/resources/layouts/anti_air/NASAMS_AIM-120B.yaml index 7325aa27..63351bff 100644 --- a/resources/layouts/anti_air/NASAMS_AIM-120B.yaml +++ b/resources/layouts/anti_air/NASAMS_AIM-120B.yaml @@ -1,21 +1,21 @@ name: NASAMS AIM-120B -role: AntiAir tasks: - MERAD groups: - - name: NASAMS AIM-120B 1 - unit_count: - - 1 - unit_types: - - NASAMS_Radar_MPQ64F1 - - name: NASAMS AIM-120B 0 - unit_count: - - 1 - unit_types: - - NASAMS_Command_Post - - name: NASAMS AIM-120B 2 - unit_count: - - 4 - unit_types: - - NASAMS_LN_B + - NASAMS: + - name: NASAMS AIM-120B 1 + unit_count: + - 1 + unit_types: + - NASAMS_Radar_MPQ64F1 + - name: NASAMS AIM-120B 0 + unit_count: + - 1 + unit_types: + - NASAMS_Command_Post + - name: NASAMS AIM-120B 2 + unit_count: + - 4 + unit_types: + - NASAMS_LN_B layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/NASAMS_AIM-120C.yaml b/resources/layouts/anti_air/NASAMS_AIM-120C.yaml index 230f4903..6403ab53 100644 --- a/resources/layouts/anti_air/NASAMS_AIM-120C.yaml +++ b/resources/layouts/anti_air/NASAMS_AIM-120C.yaml @@ -1,21 +1,21 @@ name: NASAMS AIM-120C -role: AntiAir tasks: - MERAD groups: - - name: NASAMS AIM-120C 1 - unit_count: - - 1 - unit_types: - - NASAMS_Radar_MPQ64F1 - - name: NASAMS AIM-120C 0 - unit_count: - - 1 - unit_types: - - NASAMS_Command_Post - - name: NASAMS AIM-120C 2 - unit_count: - - 4 - unit_types: - - NASAMS_LN_C + - NASAMS: + - name: NASAMS AIM-120C 1 + unit_count: + - 1 + unit_types: + - NASAMS_Radar_MPQ64F1 + - name: NASAMS AIM-120C 0 + unit_count: + - 1 + unit_types: + - NASAMS_Command_Post + - name: NASAMS AIM-120C 2 + unit_count: + - 4 + unit_types: + - NASAMS_LN_C layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/Patriot_Battery.yaml b/resources/layouts/anti_air/Patriot_Battery.yaml index c4997cb7..1137c236 100644 --- a/resources/layouts/anti_air/Patriot_Battery.yaml +++ b/resources/layouts/anti_air/Patriot_Battery.yaml @@ -1,56 +1,50 @@ name: Patriot Battery -role: AntiAir tasks: - LORAD groups: - - name: Patriot Battery 0 - group: 1 - unit_count: - - 1 - unit_types: - - Patriot str - - name: Patriot Battery 1 - group: 1 - unit_count: - - 1 - unit_types: - - Patriot AMG - - name: Patriot Battery 2 - group: 1 - unit_count: - - 1 - unit_types: - - Patriot ECS - - name: Patriot Battery 3 - group: 1 - unit_count: - - 1 - unit_types: - - Patriot cp - - name: Patriot Battery 4 - group: 1 - unit_count: - - 1 - unit_types: - - Patriot EPP - - name: Patriot Battery 5 - group: 1 - unit_count: - - 8 - unit_types: - - Patriot ln - - name: Patriot Battery 6 - optional: true - group: 2 - unit_count: - - 2 - unit_classes: - - AAA - - name: Patriot Battery 7 - optional: true - group: 2 - unit_count: - - 2 - unit_classes: - - SHORAD + - Patriot: + - name: Patriot Battery 0 + unit_count: + - 1 + unit_types: + - Patriot str + - name: Patriot Battery 1 + unit_count: + - 1 + unit_types: + - Patriot AMG + - name: Patriot Battery 2 + unit_count: + - 1 + unit_types: + - Patriot ECS + - name: Patriot Battery 3 + unit_count: + - 1 + unit_types: + - Patriot cp + - name: Patriot Battery 4 + unit_count: + - 1 + unit_types: + - Patriot EPP + - name: Patriot Battery 5 + unit_count: + - 8 + unit_types: + - Patriot ln + - AAA: + - name: Patriot Battery 6 + optional: true + unit_count: + - 2 + unit_classes: + - AAA + - PD: + - name: Patriot Battery 7 + optional: true + unit_count: + - 2 + unit_classes: + - SHORAD layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/Rapier_AA_Site.yaml b/resources/layouts/anti_air/Rapier_AA_Site.yaml index 0bb571b7..ca65c374 100644 --- a/resources/layouts/anti_air/Rapier_AA_Site.yaml +++ b/resources/layouts/anti_air/Rapier_AA_Site.yaml @@ -1,21 +1,21 @@ name: Rapier AA Site -role: AntiAir tasks: - SHORAD groups: - - name: Rapier AA Site 0 - unit_count: - - 1 - unit_types: - - rapier_fsa_blindfire_radar - - name: Rapier AA Site 1 - unit_count: - - 1 - unit_types: - - rapier_fsa_optical_tracker_unit - - name: Rapier AA Site 2 - unit_count: - - 2 - unit_types: - - rapier_fsa_launcher + - Rapier: + - name: Rapier AA Site 0 + unit_count: + - 1 + unit_types: + - rapier_fsa_blindfire_radar + - name: Rapier AA Site 1 + unit_count: + - 1 + unit_types: + - rapier_fsa_optical_tracker_unit + - name: Rapier AA Site 2 + unit_count: + - 2 + unit_types: + - rapier_fsa_launcher layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/Roland_Site.yaml b/resources/layouts/anti_air/Roland_Site.yaml index 9abfb991..feaf050f 100644 --- a/resources/layouts/anti_air/Roland_Site.yaml +++ b/resources/layouts/anti_air/Roland_Site.yaml @@ -1,21 +1,21 @@ name: Roland Site -role: AntiAir tasks: - SHORAD groups: - - name: Roland Site 0 - unit_count: - - 1 - unit_types: - - Roland Radar - - name: Roland Site 1 - unit_count: - - 2 - unit_types: - - Roland ADS - - name: Roland Site 2 - unit_count: - - 1 - unit_types: - - M 818 + - Roland: + - name: Roland Site 0 + unit_count: + - 1 + unit_types: + - Roland Radar + - name: Roland Site 1 + unit_count: + - 2 + unit_types: + - Roland ADS + - name: Roland Site 2 + unit_count: + - 1 + unit_types: + - M 818 layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/S-300_Site.yaml b/resources/layouts/anti_air/S-300_Site.yaml index 9337f244..3940d979 100644 --- a/resources/layouts/anti_air/S-300_Site.yaml +++ b/resources/layouts/anti_air/S-300_Site.yaml @@ -1,89 +1,85 @@ name: S-300 Site -role: AntiAir tasks: - LORAD groups: - - name: S-300 Site SR1 - group: 1 - unit_count: - - 1 - unit_types: - - S-300PS 40B6MD sr # SA-10 - - S-300PS SA-10B 40B6MD MAST sr # SA-10B - - S-300V 9S15 sr # SA-12 - - S-300PMU1 40B6MD sr # SA-20 + B, is the 5N66E - - S-300VM 9S15M2 sr # SA-23 - - name: S-300 Site SR2 - group: 1 - unit_count: - - 1 - unit_types: - - S-300PS 64H6E sr # SA-10 - - S-300PS 64H6E TRAILER sr # SA-10B - - S-300V 9S19 sr # SA-12 - - S-300PMU1 64N6E sr # SA-20 + B - - S-300VM 9S19M2 sr # SA-23 - - name: S-300 Site CP - group: 1 - unit_count: - - 1 - unit_types: - - S-300PS 54K6 cp # SA-10 - - S-300PS SA-10B 54K6 cp # SA-10B - - S-300V 9S457 cp # SA-12 - - S-300PMU1 54K6 cp # SA-20 - - S-300PMU2 54K6E2 cp # SA-20B - - S-300VM 9S457ME cp # SA-23 - - name: S-300 Site TR - group: 1 - unit_count: - - 1 - unit_types: - - S-300PS 40B6M tr # SA-10 - - S-300PS 30N6 TRAILER tr # SA-10B - - S-300V 9S32 tr # SA-12 - - S-300PMU1 40B6M tr # SA-20, is the 30N6E! - - S-300PMU2 92H6E tr # SA-20B - - S-300VM 9S32ME tr # SA-23 - - name: S-300 Site LN1 - group: 1 - unit_count: - - 3 - unit_types: - - S-300PS 5P85C ln # SA-10 - - S-300PS 5P85SE_mod ln # SA-10B - - S-300V 9A82 ln # SA-12 - - S-300PMU1 5P85CE ln # SA-20 - - S-300PMU2 5P85SE2 ln # SA-20B - - S-300VM 9A82ME ln # SA-23 - - name: S-300 Site LN2 - group: 1 - unit_count: - - 3 - unit_types: - - S-300PS 5P85D ln # SA-10 - - S-300PS 5P85SU_mod ln # SA-10B - - S-300V 9A83 ln # SA-12 - - S-300PMU1 5P85DE ln # SA-20 - - S-300PMU2 5P85SE2 ln # SA-20B - - S-300VM 9A83ME ln # SA-23 - - name: S-300 Site AAA - group: 2 - unit_count: - - 2 - unit_classes: - - AAA - - name: S-300 Site SHORAD1 - group: 3 - unit_count: - - 0 - - 2 - unit_classes: - - SHORAD - - name: S-300 Site SHORAD2 - group: 3 - unit_count: - - 0 - - 2 - unit_classes: - - SHORAD + - S-300: + - name: S-300 Site SR1 + unit_count: + - 1 + unit_types: + - S-300PS 40B6MD sr # SA-10 + - S-300PS SA-10B 40B6MD MAST sr # SA-10B + - S-300V 9S15 sr # SA-12 + - S-300PMU1 40B6MD sr # SA-20 + B, is the 5N66E + - S-300VM 9S15M2 sr # SA-23 + - name: S-300 Site SR2 + unit_count: + - 1 + unit_types: + - S-300PS 64H6E sr # SA-10 + - S-300PS 64H6E TRAILER sr # SA-10B + - S-300V 9S19 sr # SA-12 + - S-300PMU1 64N6E sr # SA-20 + B + - S-300VM 9S19M2 sr # SA-23 + - name: S-300 Site CP + unit_count: + - 1 + unit_types: + - S-300PS 54K6 cp # SA-10 + - S-300PS SA-10B 54K6 cp # SA-10B + - S-300V 9S457 cp # SA-12 + - S-300PMU1 54K6 cp # SA-20 + - S-300PMU2 54K6E2 cp # SA-20B + - S-300VM 9S457ME cp # SA-23 + - name: S-300 Site TR + unit_count: + - 1 + unit_types: + - S-300PS 40B6M tr # SA-10 + - S-300PS 30N6 TRAILER tr # SA-10B + - S-300V 9S32 tr # SA-12 + - S-300PMU1 40B6M tr # SA-20, is the 30N6E! + - S-300PMU2 92H6E tr # SA-20B + - S-300VM 9S32ME tr # SA-23 + - name: S-300 Site LN1 + unit_count: + - 3 + unit_types: + - S-300PS 5P85C ln # SA-10 + - S-300PS 5P85SE_mod ln # SA-10B + - S-300V 9A82 ln # SA-12 + - S-300PMU1 5P85CE ln # SA-20 + - S-300PMU2 5P85SE2 ln # SA-20B + - S-300VM 9A82ME ln # SA-23 + - name: S-300 Site LN2 + unit_count: + - 3 + unit_types: + - S-300PS 5P85D ln # SA-10 + - S-300PS 5P85SU_mod ln # SA-10B + - S-300V 9A83 ln # SA-12 + - S-300PMU1 5P85DE ln # SA-20 + - S-300PMU2 5P85SE2 ln # SA-20B + - S-300VM 9A83ME ln # SA-23 + - AAA: + - name: S-300 Site AAA + optional: true + unit_count: + - 2 + unit_classes: + - AAA + - PD: + - name: S-300 Site SHORAD1 + optional: true + unit_count: + - 0 + - 2 + unit_classes: + - SHORAD + - name: S-300 Site SHORAD2 + optional: true + unit_count: + - 0 + - 2 + unit_classes: + - SHORAD diff --git a/resources/layouts/anti_air/SA-11_Buk_Battery.yaml b/resources/layouts/anti_air/SA-11_Buk_Battery.yaml index bcda0241..bd02e2fb 100644 --- a/resources/layouts/anti_air/SA-11_Buk_Battery.yaml +++ b/resources/layouts/anti_air/SA-11_Buk_Battery.yaml @@ -1,21 +1,21 @@ name: SA-11 Buk Battery -role: AntiAir tasks: - MERAD groups: - - name: SA-11 Buk Battery 0 - unit_count: - - 1 - unit_types: - - SA-11 Buk SR 9S18M1 - - name: SA-11 Buk Battery 1 - unit_count: - - 1 - unit_types: - - SA-11 Buk CC 9S470M1 - - name: SA-11 Buk Battery 2 - unit_count: - - 4 - unit_types: - - SA-11 Buk LN 9A310M1 + - SA-11: + - name: SA-11 Buk Battery 0 + unit_count: + - 1 + unit_types: + - SA-11 Buk SR 9S18M1 + - name: SA-11 Buk Battery 1 + unit_count: + - 1 + unit_types: + - SA-11 Buk CC 9S470M1 + - name: SA-11 Buk Battery 2 + unit_count: + - 4 + unit_types: + - SA-11 Buk LN 9A310M1 layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/SA-17_Grizzly_Battery.yaml b/resources/layouts/anti_air/SA-17_Grizzly_Battery.yaml index 653f2846..a3ff78a5 100644 --- a/resources/layouts/anti_air/SA-17_Grizzly_Battery.yaml +++ b/resources/layouts/anti_air/SA-17_Grizzly_Battery.yaml @@ -1,21 +1,21 @@ name: SA-17 Grizzly Battery -role: AntiAir tasks: - MERAD groups: - - name: SA-17 Grizzly Battery 0 - unit_count: - - 1 - unit_types: - - SA-11 Buk SR 9S18M1 - - name: SA-17 Grizzly Battery 1 - unit_count: - - 1 - unit_types: - - SA-11 Buk CC 9S470M1 - - name: SA-17 Grizzly Battery 2 - unit_count: - - 3 - unit_types: - - SA-17 Buk M1-2 LN 9A310M1-2 + - SA-17: + - name: SA-17 Grizzly Battery 0 + unit_count: + - 1 + unit_types: + - SA-11 Buk SR 9S18M1 + - name: SA-17 Grizzly Battery 1 + unit_count: + - 1 + unit_types: + - SA-11 Buk CC 9S470M1 + - name: SA-17 Grizzly Battery 2 + unit_count: + - 3 + unit_types: + - SA-17 Buk M1-2 LN 9A310M1-2 layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/SA-2_S-75_Site.yaml b/resources/layouts/anti_air/SA-2_S-75_Site.yaml index d722aa1a..a34b697f 100644 --- a/resources/layouts/anti_air/SA-2_S-75_Site.yaml +++ b/resources/layouts/anti_air/SA-2_S-75_Site.yaml @@ -1,21 +1,21 @@ name: SA-2/S-75 Site -role: AntiAir tasks: - MERAD groups: - - name: SA-2/S-75 Site 0 - unit_count: - - 1 - unit_types: - - p-19 s-125 sr - - name: SA-2/S-75 Site 1 - unit_count: - - 1 - unit_types: - - SNR_75V - - name: SA-2/S-75 Site 2 - unit_count: - - 6 - unit_types: - - S_75M_Volhov + - SA-2: + - name: SA-2/S-75 Site 0 + unit_count: + - 1 + unit_types: + - p-19 s-125 sr + - name: SA-2/S-75 Site 1 + unit_count: + - 1 + unit_types: + - SNR_75V + - name: SA-2/S-75 Site 2 + unit_count: + - 6 + unit_types: + - S_75M_Volhov layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/SA-3_S-125_Site.yaml b/resources/layouts/anti_air/SA-3_S-125_Site.yaml index f4429f26..9478f04f 100644 --- a/resources/layouts/anti_air/SA-3_S-125_Site.yaml +++ b/resources/layouts/anti_air/SA-3_S-125_Site.yaml @@ -1,21 +1,21 @@ name: SA-3/S-125 Site -role: AntiAir tasks: - MERAD groups: - - name: SA-3/S-125 Site 0 - unit_count: - - 1 - unit_types: - - p-19 s-125 sr - - name: SA-3/S-125 Site 1 - unit_count: - - 1 - unit_types: - - snr s-125 tr - - name: SA-3/S-125 Site 2 - unit_count: - - 4 - unit_types: - - 5p73 s-125 ln + - SA-3: + - name: SA-3/S-125 Site 0 + unit_count: + - 1 + unit_types: + - p-19 s-125 sr + - name: SA-3/S-125 Site 1 + unit_count: + - 1 + unit_types: + - snr s-125 tr + - name: SA-3/S-125 Site 2 + unit_count: + - 4 + unit_types: + - 5p73 s-125 ln layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/SA-5_S-200_Site.yaml b/resources/layouts/anti_air/SA-5_S-200_Site.yaml index ae45cadc..3264f5bf 100644 --- a/resources/layouts/anti_air/SA-5_S-200_Site.yaml +++ b/resources/layouts/anti_air/SA-5_S-200_Site.yaml @@ -1,26 +1,26 @@ name: SA-5/S-200 Site -role: AntiAir tasks: - LORAD groups: - - name: SA-5/S-200 Site 0 - unit_count: - - 1 - unit_types: - - RLS_19J6 - - name: SA-5/S-200 Site 1 - unit_count: - - 1 - unit_types: - - RPC_5N62V - - name: SA-5/S-200 Site 2 - unit_count: - - 1 - unit_types: - - Ural-375 - - name: SA-5/S-200 Site 3 - unit_count: - - 6 - unit_types: - - S-200_Launcher + - SA-5: + - name: SA-5/S-200 Site 0 + unit_count: + - 1 + unit_types: + - RLS_19J6 + - name: SA-5/S-200 Site 1 + unit_count: + - 1 + unit_types: + - RPC_5N62V + - name: SA-5/S-200 Site 2 + unit_count: + - 1 + unit_types: + - Ural-375 + - name: SA-5/S-200 Site 3 + unit_count: + - 6 + unit_types: + - S-200_Launcher layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/SA-6_Kub_Site.yaml b/resources/layouts/anti_air/SA-6_Kub_Site.yaml index acc1d219..3407711c 100644 --- a/resources/layouts/anti_air/SA-6_Kub_Site.yaml +++ b/resources/layouts/anti_air/SA-6_Kub_Site.yaml @@ -1,16 +1,16 @@ name: SA-6 Kub Site -role: AntiAir tasks: - MERAD groups: - - name: SA-6 Kub Site 0 - unit_count: - - 1 - unit_types: - - Kub 1S91 str - - name: SA-6 Kub Site 1 - unit_count: - - 4 - unit_types: - - Kub 2P25 ln + - SA-6: + - name: SA-6 Kub Site 0 + unit_count: + - 1 + unit_types: + - Kub 1S91 str + - name: SA-6 Kub Site 1 + unit_count: + - 4 + unit_types: + - Kub 2P25 ln layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/Short_Range_Anti_Air.yaml b/resources/layouts/anti_air/Short_Range_Anti_Air.yaml index bfc81bd5..2c837522 100644 --- a/resources/layouts/anti_air/Short_Range_Anti_Air.yaml +++ b/resources/layouts/anti_air/Short_Range_Anti_Air.yaml @@ -1,19 +1,19 @@ name: Short Range Anti Air Group -role: AntiAir generic: true tasks: - SHORAD groups: - - name: SHORAD Group 0 - unit_count: - - 2 - unit_classes: - - SHORAD - - name: SHORAD Group 1 - optional: true - unit_count: - - 1 - - 2 - unit_classes: - - Logistics + - SHORAD: + - name: SHORAD Group 0 + unit_count: + - 2 + unit_classes: + - SHORAD + - name: SHORAD Group 1 + optional: true + unit_count: + - 1 + - 2 + unit_classes: + - Logistics layout_file: resources/layouts/anti_air/shorad.miz diff --git a/resources/layouts/anti_air/WW2_Ally_Flak_Site.yaml b/resources/layouts/anti_air/WW2_Ally_Flak_Site.yaml index 3d8078c3..dc0d78d1 100644 --- a/resources/layouts/anti_air/WW2_Ally_Flak_Site.yaml +++ b/resources/layouts/anti_air/WW2_Ally_Flak_Site.yaml @@ -1,41 +1,41 @@ name: WW2 Ally Flak Site -role: AntiAir tasks: - AAA groups: - - name: WW2 Ally Flak Site 0 - unit_count: - - 4 - unit_types: - - QF_37_AA - - name: WW2 Ally Flak Site 1 - unit_count: - - 8 - unit_types: - - M1_37mm - - name: WW2 Ally Flak Site 2 - unit_count: - - 8 - unit_types: - - M45_Quadmount - - name: WW2 Ally Flak Site 3 - unit_count: - - 1 - unit_types: - - Willys_MB - - name: WW2 Ally Flak Site 4 - unit_count: - - 1 - unit_types: - - M30_CC - - name: WW2 Ally Flak Site 5 - unit_count: - - 1 - unit_types: - - M4_Tractor - - name: WW2 Ally Flak Site 6 - unit_count: - - 1 - unit_types: - - Bedford_MWD + - Flak: + - name: WW2 Ally Flak Site 0 + unit_count: + - 4 + unit_types: + - QF_37_AA + - name: WW2 Ally Flak Site 1 + unit_count: + - 8 + unit_types: + - M1_37mm + - name: WW2 Ally Flak Site 2 + unit_count: + - 8 + unit_types: + - M45_Quadmount + - name: WW2 Ally Flak Site 3 + unit_count: + - 1 + unit_types: + - Willys_MB + - name: WW2 Ally Flak Site 4 + unit_count: + - 1 + unit_types: + - M30_CC + - name: WW2 Ally Flak Site 5 + unit_count: + - 1 + unit_types: + - M4_Tractor + - name: WW2 Ally Flak Site 6 + unit_count: + - 1 + unit_types: + - Bedford_MWD layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/WW2_Flak_Site.yaml b/resources/layouts/anti_air/WW2_Flak_Site.yaml index 9bebd7b7..442b0fb3 100644 --- a/resources/layouts/anti_air/WW2_Flak_Site.yaml +++ b/resources/layouts/anti_air/WW2_Flak_Site.yaml @@ -1,16 +1,16 @@ name: WW2 Flak Site -role: AntiAir tasks: - AAA groups: - - name: WW2 Flak Site 0 - unit_count: - - 6 - unit_types: - - flak18 - - name: WW2 Flak Site 1 - unit_count: - - 1 - unit_types: - - Blitz_36-6700A + - Flak: + - name: WW2 Flak Site 0 + unit_count: + - 6 + unit_types: + - flak18 + - name: WW2 Flak Site 1 + unit_count: + - 1 + unit_types: + - Blitz_36-6700A layout_file: resources/layouts/anti_air/legacy_ground_templates.miz diff --git a/resources/layouts/anti_air/legacy_ground_templates.miz b/resources/layouts/anti_air/legacy_ground_templates.miz index ee1711920651e823cce8f9b791b35440f49abd10..e4e354edde9ee61be88a6e3f1db1bdbe6a5435bd 100644 GIT binary patch delta 15983 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 delta 15873 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 diff --git a/resources/layouts/buildings/allycamp1.yaml b/resources/layouts/buildings/allycamp1.yaml index 075fc0b7..cd6d052a 100644 --- a/resources/layouts/buildings/allycamp1.yaml +++ b/resources/layouts/buildings/allycamp1.yaml @@ -1,84 +1,84 @@ name: allycamp1 generic: true -role: Building tasks: - StrikeTarget - AllyCamp groups: - - name: allycamp1 0 - statics: - - allycamp1 0-0 - - allycamp1 0-1 - - allycamp1 0-2 - - allycamp1 0-3 - unit_count: - - 4 - unit_types: - - FARP Tent - - name: allycamp1 1 - statics: - - allycamp1 1-0 - - allycamp1 1-1 - - allycamp1 1-2 - - allycamp1 1-3 - - allycamp1 1-4 - - allycamp1 1-5 - - allycamp1 1-6 - - allycamp1 1-7 - - allycamp1 1-8 - - allycamp1 1-9 - - allycamp1 1-10 - - allycamp1 1-11 - - allycamp1 1-12 - - allycamp1 1-13 - - allycamp1 1-14 - - allycamp1 1-15 - - allycamp1 1-16 - - allycamp1 1-17 - - allycamp1 1-18 - - allycamp1 1-19 - - allycamp1 1-20 - - allycamp1 1-21 - unit_count: - - 22 - unit_types: - - Haystack 4 - - name: allycamp1 2 - statics: - - allycamp1 2-0 - - allycamp1 2-1 - - allycamp1 2-2 - - allycamp1 2-3 - - allycamp1 2-4 - - allycamp1 2-5 - - allycamp1 2-6 - - allycamp1 2-7 - - allycamp1 2-8 - - allycamp1 2-9 - - allycamp1 2-10 - - allycamp1 2-11 - unit_count: - - 12 - unit_types: - - Haystack 3 - - name: allycamp1 3 - statics: - - allycamp1 3-0 - - allycamp1 3-1 - - allycamp1 3-2 - - allycamp1 3-3 - - allycamp1 3-4 - - allycamp1 3-5 - - allycamp1 3-6 - - allycamp1 3-7 - unit_count: - - 8 - unit_types: - - Concertina wire - - name: allycamp1 4 - group: 2 # Vehicle and static can not be mixed - unit_count: - - 4 - unit_types: - - house2arm + - AllyCamp: + - name: allycamp1 0 + statics: + - allycamp1 0-0 + - allycamp1 0-1 + - allycamp1 0-2 + - allycamp1 0-3 + unit_count: + - 4 + unit_types: + - FARP Tent + - name: allycamp1 1 + statics: + - allycamp1 1-0 + - allycamp1 1-1 + - allycamp1 1-2 + - allycamp1 1-3 + - allycamp1 1-4 + - allycamp1 1-5 + - allycamp1 1-6 + - allycamp1 1-7 + - allycamp1 1-8 + - allycamp1 1-9 + - allycamp1 1-10 + - allycamp1 1-11 + - allycamp1 1-12 + - allycamp1 1-13 + - allycamp1 1-14 + - allycamp1 1-15 + - allycamp1 1-16 + - allycamp1 1-17 + - allycamp1 1-18 + - allycamp1 1-19 + - allycamp1 1-20 + - allycamp1 1-21 + unit_count: + - 22 + unit_types: + - Haystack 4 + - name: allycamp1 2 + statics: + - allycamp1 2-0 + - allycamp1 2-1 + - allycamp1 2-2 + - allycamp1 2-3 + - allycamp1 2-4 + - allycamp1 2-5 + - allycamp1 2-6 + - allycamp1 2-7 + - allycamp1 2-8 + - allycamp1 2-9 + - allycamp1 2-10 + - allycamp1 2-11 + unit_count: + - 12 + unit_types: + - Haystack 3 + - name: allycamp1 3 + statics: + - allycamp1 3-0 + - allycamp1 3-1 + - allycamp1 3-2 + - allycamp1 3-3 + - allycamp1 3-4 + - allycamp1 3-5 + - allycamp1 3-6 + - allycamp1 3-7 + unit_count: + - 8 + unit_types: + - Concertina wire + - AllyCamp2: # Vehicle and static can not be mixed + - name: allycamp1 4 + unit_count: + - 4 + unit_types: + - house2arm layout_file: resources/layouts/buildings/buildings.miz diff --git a/resources/layouts/buildings/ammo1.yaml b/resources/layouts/buildings/ammo1.yaml index 7df45bd6..f588f667 100644 --- a/resources/layouts/buildings/ammo1.yaml +++ b/resources/layouts/buildings/ammo1.yaml @@ -1,22 +1,22 @@ name: ammo1 generic: true -role: Building tasks: - Ammo groups: - - name: ammo1 0 - statics: - - ammo1 0-0 - unit_count: - - 1 - unit_types: - - .Ammunition depot - - name: ammo1 1 - statics: - - ammo1 1-0 - - ammo1 1-1 - unit_count: - - 2 - unit_types: - - Hangar B + - Ammo: + - name: ammo1 0 + statics: + - ammo1 0-0 + unit_count: + - 1 + unit_types: + - .Ammunition depot + - name: ammo1 1 + statics: + - ammo1 1-0 + - ammo1 1-1 + unit_count: + - 2 + unit_types: + - Hangar B layout_file: resources/layouts/buildings/buildings.miz diff --git a/resources/layouts/buildings/comms.yaml b/resources/layouts/buildings/comms.yaml index cb31ae15..fd8fed19 100644 --- a/resources/layouts/buildings/comms.yaml +++ b/resources/layouts/buildings/comms.yaml @@ -1,16 +1,16 @@ name: comms generic: true -role: Building tasks: - StrikeTarget - Comms groups: - - name: comms1 0 - statics: - - comms1 0-0 - unit_count: - - 1 - unit_types: - - TV tower - - Comms tower M + - Comms: + - name: comms1 0 + statics: + - comms1 0-0 + unit_count: + - 1 + unit_types: + - TV tower + - Comms tower M layout_file: resources/layouts/buildings/buildings.miz diff --git a/resources/layouts/buildings/derrick1.yaml b/resources/layouts/buildings/derrick1.yaml index bbfb6afc..9b0342bb 100644 --- a/resources/layouts/buildings/derrick1.yaml +++ b/resources/layouts/buildings/derrick1.yaml @@ -1,31 +1,31 @@ name: derrick1 generic: true -role: Building tasks: - StrikeTarget - Derrick groups: - - name: derrick1 0 - statics: - - derrick1 0-0 - - derrick1 0-1 - unit_count: - - 2 - unit_types: - - Oil derrick - - name: derrick1 1 - statics: - - derrick1 1-0 - - derrick1 1-1 - unit_count: - - 2 - unit_types: - - Pump station - - name: derrick1 2 - statics: - - derrick1 2-0 - unit_count: - - 1 - unit_types: - - Subsidiary structure 2 + - Derrick: + - name: derrick1 0 + statics: + - derrick1 0-0 + - derrick1 0-1 + unit_count: + - 2 + unit_types: + - Oil derrick + - name: derrick1 1 + statics: + - derrick1 1-0 + - derrick1 1-1 + unit_count: + - 2 + unit_types: + - Pump station + - name: derrick1 2 + statics: + - derrick1 2-0 + unit_count: + - 1 + unit_types: + - Subsidiary structure 2 layout_file: resources/layouts/buildings/buildings.miz diff --git a/resources/layouts/buildings/factory1.yaml b/resources/layouts/buildings/factory1.yaml index d42acc94..e69358e6 100644 --- a/resources/layouts/buildings/factory1.yaml +++ b/resources/layouts/buildings/factory1.yaml @@ -1,23 +1,23 @@ name: factory1 generic: true -role: Building tasks: - Factory groups: - - name: factory1 0 - statics: - - factory1 0-0 - unit_count: - - 1 - unit_types: - - Tech combine - - name: factory1 1 - statics: - - factory1 1-0 - - factory1 1-1 - - factory1 1-2 - unit_count: - - 3 - unit_types: - - Tech hangar A + - Factory: + - name: factory1 0 + statics: + - factory1 0-0 + unit_count: + - 1 + unit_types: + - Tech combine + - name: factory1 1 + statics: + - factory1 1-0 + - factory1 1-1 + - factory1 1-2 + unit_count: + - 3 + unit_types: + - Tech hangar A layout_file: resources/layouts/buildings/buildings.miz diff --git a/resources/layouts/buildings/farp1.yaml b/resources/layouts/buildings/farp1.yaml index 90c0f621..60d5c016 100644 --- a/resources/layouts/buildings/farp1.yaml +++ b/resources/layouts/buildings/farp1.yaml @@ -1,41 +1,41 @@ name: farp1 generic: true -role: Building tasks: - StrikeTarget - Farp groups: - - name: farp1 0 - statics: - - farp1 0-0 - - farp1 0-1 - - farp1 0-2 - - farp1 0-3 - - farp1 0-4 - unit_count: - - 5 - unit_types: - - FARP Tent - - name: farp1 1 - statics: - - farp1 1-0 - unit_count: - - 1 - unit_types: - - FARP Ammo Dump Coating - - name: farp1 2 - statics: - - farp1 2-0 - unit_count: - - 1 - unit_types: - - FARP CP Blindage - - name: farp1 3 - statics: - - farp1 3-0 - - farp1 3-1 - unit_count: - - 2 - unit_types: - - FARP Fuel Depot + - Farp: + - name: farp1 0 + statics: + - farp1 0-0 + - farp1 0-1 + - farp1 0-2 + - farp1 0-3 + - farp1 0-4 + unit_count: + - 5 + unit_types: + - FARP Tent + - name: farp1 1 + statics: + - farp1 1-0 + unit_count: + - 1 + unit_types: + - FARP Ammo Dump Coating + - name: farp1 2 + statics: + - farp1 2-0 + unit_count: + - 1 + unit_types: + - FARP CP Blindage + - name: farp1 3 + statics: + - farp1 3-0 + - farp1 3-1 + unit_count: + - 2 + unit_types: + - FARP Fuel Depot layout_file: resources/layouts/buildings/buildings.miz diff --git a/resources/layouts/buildings/fob1.yaml b/resources/layouts/buildings/fob1.yaml index 49435608..f9479e9a 100644 --- a/resources/layouts/buildings/fob1.yaml +++ b/resources/layouts/buildings/fob1.yaml @@ -1,31 +1,31 @@ name: fob1 generic: true -role: Building tasks: - FOB groups: - - name: fob1 0 - statics: - - fob1 0-0 - unit_count: - - 1 - unit_types: - - .Command Center - - name: fob1 1 - statics: - - fob1 1-0 - - fob1 1-1 - - fob1 1-2 - unit_count: - - 3 - unit_types: - - Barracks 2 - - name: fob1 2 - statics: - - fob1 2-0 - - fob1 2-1 - unit_count: - - 2 - unit_types: - - Garage small B + - FOB: + - name: fob1 0 + statics: + - fob1 0-0 + unit_count: + - 1 + unit_types: + - .Command Center + - name: fob1 1 + statics: + - fob1 1-0 + - fob1 1-1 + - fob1 1-2 + unit_count: + - 3 + unit_types: + - Barracks 2 + - name: fob1 2 + statics: + - fob1 2-0 + - fob1 2-1 + unit_count: + - 2 + unit_types: + - Garage small B layout_file: resources/layouts/buildings/buildings.miz diff --git a/resources/layouts/buildings/fuel1.yaml b/resources/layouts/buildings/fuel1.yaml index 8d35c8a0..c7a7a656 100644 --- a/resources/layouts/buildings/fuel1.yaml +++ b/resources/layouts/buildings/fuel1.yaml @@ -1,28 +1,28 @@ name: fuel1 generic: true -role: Building tasks: - StrikeTarget - Fuel groups: - - name: fuel1 0 - statics: - - fuel1 0-0 - - fuel1 0-1 - - fuel1 0-2 - - fuel1 0-3 - - fuel1 0-4 - - fuel1 0-5 - unit_count: - - 6 - unit_types: - - Tank - - name: fuel1 1 - statics: - - fuel1 1-0 - - fuel1 1-1 - unit_count: - - 2 - unit_types: - - Tank 3 + - Fuel: + - name: fuel1 0 + statics: + - fuel1 0-0 + - fuel1 0-1 + - fuel1 0-2 + - fuel1 0-3 + - fuel1 0-4 + - fuel1 0-5 + unit_count: + - 6 + unit_types: + - Tank + - name: fuel1 1 + statics: + - fuel1 1-0 + - fuel1 1-1 + unit_count: + - 2 + unit_types: + - Tank 3 layout_file: resources/layouts/buildings/buildings.miz diff --git a/resources/layouts/buildings/oil1.yaml b/resources/layouts/buildings/oil1.yaml index a5f1c810..3f19272d 100644 --- a/resources/layouts/buildings/oil1.yaml +++ b/resources/layouts/buildings/oil1.yaml @@ -1,18 +1,18 @@ name: oil1 generic: true -role: Building tasks: - OffShoreStrikeTarget - Oil groups: - - name: oil1 0 - statics: - - oil1 0-0 - - oil1 0-1 - - oil1 0-2 - - oil1 0-3 - unit_count: - - 4 - unit_types: - - Oil platform + - Oil: + - name: oil1 0 + statics: + - oil1 0-0 + - oil1 0-1 + - oil1 0-2 + - oil1 0-3 + unit_count: + - 4 + unit_types: + - Oil platform layout_file: resources/layouts/buildings/buildings.miz diff --git a/resources/layouts/buildings/power1.yaml b/resources/layouts/buildings/power1.yaml index 81e85329..622474fb 100644 --- a/resources/layouts/buildings/power1.yaml +++ b/resources/layouts/buildings/power1.yaml @@ -1,37 +1,37 @@ name: power1 generic: true -role: Building tasks: - StrikeTarget - Power groups: - - name: power1 0 - statics: - - power1 0-0 - unit_count: - - 1 - unit_types: - - Repair workshop - - name: power1 1 - statics: - - power1 1-0 - unit_count: - - 1 - unit_types: - - Workshop A - - name: power1 2 - statics: - - power1 2-0 - - power1 2-1 - unit_count: - - 2 - unit_types: - - Garage B - - name: power1 3 - statics: - - power1 3-0 - unit_count: - - 1 - unit_types: - - Farm B + - Power: + - name: power1 0 + statics: + - power1 0-0 + unit_count: + - 1 + unit_types: + - Repair workshop + - name: power1 1 + statics: + - power1 1-0 + unit_count: + - 1 + unit_types: + - Workshop A + - name: power1 2 + statics: + - power1 2-0 + - power1 2-1 + unit_count: + - 2 + unit_types: + - Garage B + - name: power1 3 + statics: + - power1 3-0 + unit_count: + - 1 + unit_types: + - Farm B layout_file: resources/layouts/buildings/buildings.miz diff --git a/resources/layouts/buildings/village1.yaml b/resources/layouts/buildings/village1.yaml index ea178fc1..2ba7eeff 100644 --- a/resources/layouts/buildings/village1.yaml +++ b/resources/layouts/buildings/village1.yaml @@ -1,39 +1,39 @@ name: village1 generic: true -role: Building tasks: - StrikeTarget - Village groups: - - name: village1 0 - statics: - - village1 0-0 - - village1 0-1 - unit_count: - - 2 - unit_types: - - Small house 1A - - name: village1 1 - statics: - - village1 1-0 - - village1 1-1 - - village1 1-2 - - village1 1-3 - - village1 1-4 - - village1 1-5 - - village1 1-6 - - village1 1-7 - unit_count: - - 8 - unit_types: - - Small werehouse 1 - - name: village1 2 - statics: - - village1 2-0 - - village1 2-1 - - village1 2-2 - unit_count: - - 3 - unit_types: - - Small house 1B + - Village: + - name: village1 0 + statics: + - village1 0-0 + - village1 0-1 + unit_count: + - 2 + unit_types: + - Small house 1A + - name: village1 1 + statics: + - village1 1-0 + - village1 1-1 + - village1 1-2 + - village1 1-3 + - village1 1-4 + - village1 1-5 + - village1 1-6 + - village1 1-7 + unit_count: + - 8 + unit_types: + - Small werehouse 1 + - name: village1 2 + statics: + - village1 2-0 + - village1 2-1 + - village1 2-2 + unit_count: + - 3 + unit_types: + - Small house 1B layout_file: resources/layouts/buildings/buildings.miz diff --git a/resources/layouts/buildings/ware1.yaml b/resources/layouts/buildings/ware1.yaml index dc9975da..a00889ee 100644 --- a/resources/layouts/buildings/ware1.yaml +++ b/resources/layouts/buildings/ware1.yaml @@ -1,24 +1,24 @@ name: ware1 generic: true -role: Building tasks: - StrikeTarget - Ware groups: - - name: ware1 0 - statics: - - ware1 0-0 - unit_count: - - 1 - unit_types: - - Warehouse - - name: ware1 1 - statics: - - ware1 1-0 - - ware1 1-1 - - ware1 1-2 - unit_count: - - 3 - unit_types: - - Hangar A + - Ware: + - name: ware1 0 + statics: + - ware1 0-0 + unit_count: + - 1 + unit_types: + - Warehouse + - name: ware1 1 + statics: + - ware1 1-0 + - ware1 1-1 + - ware1 1-2 + unit_count: + - 3 + unit_types: + - Hangar A layout_file: resources/layouts/buildings/buildings.miz diff --git a/resources/layouts/buildings/ww2bunker1.yaml b/resources/layouts/buildings/ww2bunker1.yaml index b489067d..a36af76a 100644 --- a/resources/layouts/buildings/ww2bunker1.yaml +++ b/resources/layouts/buildings/ww2bunker1.yaml @@ -1,34 +1,34 @@ name: ww2bunker1 generic: true -role: Building tasks: - StrikeTarget - WW2Bunker groups: - - name: ww2bunker1 0 - statics: - - ww2bunker1 0-0 - - ww2bunker1 0-1 - - ww2bunker1 0-2 - - ww2bunker1 0-3 - unit_count: - - 4 - unit_types: - - Siegfried Line - - name: ww2bunker1 1 - statics: - - ww2bunker1 1-0 - - ww2bunker1 1-1 - - ww2bunker1 1-2 - - ww2bunker1 1-3 - unit_count: - - 4 - unit_types: - - Fire Control Bunker - - name: ww2bunker1 2 - group: 2 # Vehicle and static can not be mixed - unit_count: - - 4 - unit_types: - - SK_C_28_naval_gun + - Bunker: + - name: ww2bunker1 0 + statics: + - ww2bunker1 0-0 + - ww2bunker1 0-1 + - ww2bunker1 0-2 + - ww2bunker1 0-3 + unit_count: + - 4 + unit_types: + - Siegfried Line + - name: ww2bunker1 1 + statics: + - ww2bunker1 1-0 + - ww2bunker1 1-1 + - ww2bunker1 1-2 + - ww2bunker1 1-3 + unit_count: + - 4 + unit_types: + - Fire Control Bunker + - Bunker2: # Vehicle and static can not be mixed + - name: ww2bunker1 2 + unit_count: + - 4 + unit_types: + - SK_C_28_naval_gun layout_file: resources/layouts/buildings/buildings.miz diff --git a/resources/layouts/buildings/ww2bunker2.yaml b/resources/layouts/buildings/ww2bunker2.yaml index c29ad1e1..1ac71b4e 100644 --- a/resources/layouts/buildings/ww2bunker2.yaml +++ b/resources/layouts/buildings/ww2bunker2.yaml @@ -1,57 +1,57 @@ name: ww2bunker2 generic: true -role: Building tasks: - StrikeTarget - WW2Bunker groups: - - name: ww2bunker2 0 - statics: - - ww2bunker2 0-0 - unit_count: - - 1 - unit_types: - - Fire Control Bunker - - name: ww2bunker2 1 - statics: - - ww2bunker2 1-0 - - ww2bunker2 1-1 - unit_count: - - 2 - unit_types: - - Siegfried Line - - name: ww2bunker2 2 - statics: - - ww2bunker2 2-0 - - ww2bunker2 2-1 - - ww2bunker2 2-2 - - ww2bunker2 2-3 - - ww2bunker2 2-4 - - ww2bunker2 2-5 - - ww2bunker2 2-6 - - ww2bunker2 2-7 - unit_count: - - 8 - unit_types: - - Concertina wire - - name: ww2bunker2 3 - statics: - - ww2bunker2 3-0 - unit_count: - - 1 - unit_types: - - Belgian gate - - name: ww2bunker2 4 - statics: - - ww2bunker2 4-0 - - ww2bunker2 4-1 - - ww2bunker2 4-2 - - ww2bunker2 4-3 - - ww2bunker2 4-4 - - ww2bunker2 4-5 - - ww2bunker2 4-6 - unit_count: - - 7 - unit_types: - - Czech hedgehogs 1 + - Bunker: + - name: ww2bunker2 0 + statics: + - ww2bunker2 0-0 + unit_count: + - 1 + unit_types: + - Fire Control Bunker + - name: ww2bunker2 1 + statics: + - ww2bunker2 1-0 + - ww2bunker2 1-1 + unit_count: + - 2 + unit_types: + - Siegfried Line + - name: ww2bunker2 2 + statics: + - ww2bunker2 2-0 + - ww2bunker2 2-1 + - ww2bunker2 2-2 + - ww2bunker2 2-3 + - ww2bunker2 2-4 + - ww2bunker2 2-5 + - ww2bunker2 2-6 + - ww2bunker2 2-7 + unit_count: + - 8 + unit_types: + - Concertina wire + - name: ww2bunker2 3 + statics: + - ww2bunker2 3-0 + unit_count: + - 1 + unit_types: + - Belgian gate + - name: ww2bunker2 4 + statics: + - ww2bunker2 4-0 + - ww2bunker2 4-1 + - ww2bunker2 4-2 + - ww2bunker2 4-3 + - ww2bunker2 4-4 + - ww2bunker2 4-5 + - ww2bunker2 4-6 + unit_count: + - 7 + unit_types: + - Czech hedgehogs 1 layout_file: resources/layouts/buildings/buildings.miz diff --git a/resources/layouts/defenses/Silkworm.yaml b/resources/layouts/defenses/Silkworm.yaml index 3797e942..c9d9829c 100644 --- a/resources/layouts/defenses/Silkworm.yaml +++ b/resources/layouts/defenses/Silkworm.yaml @@ -1,34 +1,34 @@ name: Silkworm -role: Defenses tasks: - Coastal groups: - - name: SilkwormGenerator 0 - unit_count: - - 1 - unit_classes: - - SearchRadar - - name: SilkwormGenerator 1 - unit_count: - - 5 - unit_classes: - - Missile - - name: SilkwormGenerator 2 - optional: true - unit_count: - - 1 - unit_classes: - - Logistics - - name: SilkwormGenerator 3 - optional: true - unit_count: - - 1 - unit_classes: - - AAA - - name: SilkwormGenerator 4 - optional: true - unit_count: - - 1 - unit_classes: - - SHORAD + - Silkworm: + - name: SilkwormGenerator 0 + unit_count: + - 1 + unit_classes: + - SearchRadar + - name: SilkwormGenerator 1 + unit_count: + - 5 + unit_classes: + - Missile + - name: SilkwormGenerator 2 + optional: true + unit_count: + - 1 + unit_classes: + - Logistics + - name: SilkwormGenerator 3 + optional: true + unit_count: + - 1 + unit_classes: + - AAA + - name: SilkwormGenerator 4 + optional: true + unit_count: + - 1 + unit_classes: + - SHORAD layout_file: resources/layouts/defenses/defenses.miz \ No newline at end of file diff --git a/resources/layouts/defenses/missile.yaml b/resources/layouts/defenses/missile.yaml index c280b08e..3a75f04c 100644 --- a/resources/layouts/defenses/missile.yaml +++ b/resources/layouts/defenses/missile.yaml @@ -1,29 +1,29 @@ name: Missile -role: Defenses generic: true tasks: - Missile groups: - - name: ScudGenerator 0 - unit_count: - - 3 - unit_classes: - - Missile - - name: ScudGenerator 1 - unit_count: - - 1 - unit_classes: - - Logistics - - name: ScudGenerator 2 - optional: true - unit_count: - - 1 - unit_classes: - - AAA - - name: ScudGenerator 3 - optional: true - unit_count: - - 1 - unit_classes: - - SHORAD + - Missile: + - name: ScudGenerator 0 + unit_count: + - 3 + unit_classes: + - Missile + - name: ScudGenerator 1 + unit_count: + - 1 + unit_classes: + - Logistics + - name: ScudGenerator 2 + optional: true + unit_count: + - 1 + unit_classes: + - AAA + - name: ScudGenerator 3 + optional: true + unit_count: + - 1 + unit_classes: + - SHORAD layout_file: resources/layouts/defenses/defenses.miz diff --git a/resources/layouts/ground_forces/Armor_Group.yaml b/resources/layouts/ground_forces/Armor_Group.yaml index 6b72e2c7..acee4ca5 100644 --- a/resources/layouts/ground_forces/Armor_Group.yaml +++ b/resources/layouts/ground_forces/Armor_Group.yaml @@ -1,17 +1,17 @@ name: Armor Group -role: GroundForce generic: true tasks: - BaseDefense - FrontLine groups: - - name: Armor Group 0 - unit_count: - - 2 - - 6 - unit_classes: - - APC - - ATGM - - IFV - - Tank + - Armor Group: + - name: Armor Group 0 + unit_count: + - 2 + - 6 + unit_classes: + - APC + - ATGM + - IFV + - Tank layout_file: resources/layouts/ground_forces/ground_forces.miz diff --git a/resources/layouts/ground_forces/Armor_Group_with_Anti-Air.yaml b/resources/layouts/ground_forces/Armor_Group_with_Anti-Air.yaml index f3ed36e4..50d9be53 100644 --- a/resources/layouts/ground_forces/Armor_Group_with_Anti-Air.yaml +++ b/resources/layouts/ground_forces/Armor_Group_with_Anti-Air.yaml @@ -1,26 +1,26 @@ name: Armor Group with Anti-Air -role: GroundForce generic: true tasks: - BaseDefense - FrontLine groups: - - name: Armor Group with Anti-Air 0 - unit_count: - - 2 - - 6 - unit_classes: - - APC - - ATGM - - IFV - - Tank - - name: Armor Group with Anti-Air 1 - optional: true - unit_count: - - 1 - - 2 - unit_classes: - - AAA - - SHORAD - - Manpad + - Armor Group: + - name: Armor Group with Anti-Air 0 + unit_count: + - 2 + - 6 + unit_classes: + - APC + - ATGM + - IFV + - Tank + - name: Armor Group with Anti-Air 1 + optional: true + unit_count: + - 1 + - 2 + unit_classes: + - AAA + - SHORAD + - Manpad layout_file: resources/layouts/ground_forces/ground_forces.miz diff --git a/resources/layouts/naval/Carrier_Group.yaml b/resources/layouts/naval/Carrier_Group.yaml index 5e9eb11d..b8368694 100644 --- a/resources/layouts/naval/Carrier_Group.yaml +++ b/resources/layouts/naval/Carrier_Group.yaml @@ -1,19 +1,18 @@ name: Carrier Group -role: Naval generic: true tasks: - AircraftCarrier groups: - - name: Carrier Group 0 - group: 1 - unit_count: - - 1 - unit_classes: - - AircraftCarrier - - name: Carrier Group 1 - group: 2 - unit_count: - - 4 - unit_classes: - - Destroyer + - Carrier: + - name: Carrier Group 0 + unit_count: + - 1 + unit_classes: + - AircraftCarrier + - Escort: + - name: Carrier Group 1 + unit_count: + - 4 + unit_classes: + - Destroyer layout_file: resources/layouts/naval/legacy_naval_templates.miz diff --git a/resources/layouts/naval/Carrier_Strike_Group_8.yaml b/resources/layouts/naval/Carrier_Strike_Group_8.yaml index c5cebb1c..09f15eb3 100644 --- a/resources/layouts/naval/Carrier_Strike_Group_8.yaml +++ b/resources/layouts/naval/Carrier_Strike_Group_8.yaml @@ -1,25 +1,23 @@ name: Carrier Strike Group 8 -role: Naval generic: true tasks: - AircraftCarrier groups: - - name: Carrier Strike Group 8 0 - group: 1 - unit_count: - - 1 - unit_types: - - Stennis - - name: Carrier Strike Group 8 1 - group: 2 - unit_count: - - 5 - unit_types: - - USS_Arleigh_Burke_IIa - - name: Carrier Strike Group 8 2 - group: 2 - unit_count: - - 2 - unit_types: - - TICONDEROG + - Carrier: + - name: Carrier Strike Group 8 0 + unit_count: + - 1 + unit_types: + - Stennis + - Escort: + - name: Carrier Strike Group 8 1 + unit_count: + - 4 + unit_types: + - USS_Arleigh_Burke_IIa + - name: Carrier Strike Group 8 2 + unit_count: + - 1 + unit_types: + - TICONDEROG layout_file: resources/layouts/naval/legacy_naval_templates.miz diff --git a/resources/layouts/naval/LHA_Group.yaml b/resources/layouts/naval/LHA_Group.yaml index 69239a35..b0b2b8f4 100644 --- a/resources/layouts/naval/LHA_Group.yaml +++ b/resources/layouts/naval/LHA_Group.yaml @@ -1,19 +1,18 @@ name: LHA Group generic: true -role: Naval tasks: - HelicopterCarrier groups: - - name: LHA Group 0 - group: 1 - unit_count: - - 1 - unit_classes: - - HelicopterCarrier - - name: LHA Group 1 - group: 2 - unit_count: - - 2 - unit_classes: - - Destroyer + - LHA: + - name: LHA Group 0 + unit_count: + - 1 + unit_classes: + - HelicopterCarrier + - Escort: + - name: LHA Group 1 + unit_count: + - 2 + unit_classes: + - Destroyer layout_file: resources/layouts/naval/legacy_naval_templates.miz diff --git a/resources/layouts/naval/Naval-Group.yaml b/resources/layouts/naval/Naval-Group.yaml index bf20faa4..4d39a9ce 100644 --- a/resources/layouts/naval/Naval-Group.yaml +++ b/resources/layouts/naval/Naval-Group.yaml @@ -1,23 +1,23 @@ name: Naval Group -role: Naval tasks: - Navy groups: - - name: Naval Group 0 - unit_count: - - 2 - unit_classes: - - Frigate - - name: Naval Group 1 - unit_count: - - 2 - unit_classes: - - Destroyer - - name: Naval Group 2 - optional: true - unit_count: - - 0 - - 1 - unit_classes: - - Cruiser + - Naval Group: + - name: Naval Group 0 + unit_count: + - 2 + unit_classes: + - Frigate + - name: Naval Group 1 + unit_count: + - 2 + unit_classes: + - Destroyer + - name: Naval Group 2 + optional: true + unit_count: + - 0 + - 1 + unit_classes: + - Cruiser layout_file: resources/layouts/naval/naval.miz diff --git a/resources/layouts/naval/Naval-Two-Ship.yaml b/resources/layouts/naval/Naval-Two-Ship.yaml index 823ac864..54242c7a 100644 --- a/resources/layouts/naval/Naval-Two-Ship.yaml +++ b/resources/layouts/naval/Naval-Two-Ship.yaml @@ -1,17 +1,17 @@ name: Naval Two Ship -role: Naval generic: true tasks: - Navy groups: - - name: Naval Two Ship - unit_count: - - 2 - unit_classes: - - Frigate - - Destroyer - - Cruiser - - Boat - - Submarine - - LandingShip + - Naval Two Ship: + - name: Naval Two Ship + unit_count: + - 2 + unit_classes: + - Frigate + - Destroyer + - Cruiser + - Boat + - Submarine + - LandingShip layout_file: resources/layouts/naval/naval.miz diff --git a/resources/layouts/naval/WW2-LST.yaml b/resources/layouts/naval/WW2-LST.yaml index dd775aa9..cb6fdc6f 100644 --- a/resources/layouts/naval/WW2-LST.yaml +++ b/resources/layouts/naval/WW2-LST.yaml @@ -3,14 +3,15 @@ role: Naval tasks: - Navy groups: - - name: WW2 LST Group 0 - unit_count: - - 1 - unit_types: - - USS_Samuel_Chase - - name: WW2 LST Group 1 - unit_count: - - 3 - unit_types: - - LST_Mk2 + - LST: + - name: WW2 LST Group 0 + unit_count: + - 1 + unit_types: + - USS_Samuel_Chase + - name: WW2 LST Group 1 + unit_count: + - 3 + unit_types: + - LST_Mk2 layout_file: resources/layouts/naval/legacy_naval_templates.miz