mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Improve Layout loading and ForceGroup generation
- fix layout not preserving the correct group index - fix ForceGroup generation merging preset_groups with generics
This commit is contained in:
parent
72682e4db3
commit
50b82f6383
@ -35,12 +35,6 @@ class ArmedForces:
|
|||||||
"""Initialize the ArmedForces for the given faction.
|
"""Initialize the ArmedForces for the given faction.
|
||||||
This will create a ForceGroup for each generic Layout and PresetGroup"""
|
This will create a ForceGroup for each generic Layout and PresetGroup"""
|
||||||
|
|
||||||
# Initialize with preset_groups from the faction
|
|
||||||
self.forces = [
|
|
||||||
preset_group.initialize_for_faction(faction)
|
|
||||||
for preset_group in faction.preset_groups
|
|
||||||
]
|
|
||||||
|
|
||||||
# Generate ForceGroup for all generic layouts by iterating over
|
# Generate ForceGroup for all generic layouts by iterating over
|
||||||
# all layouts which are usable by the given faction.
|
# all layouts which are usable by the given faction.
|
||||||
for layout in LAYOUTS.layouts:
|
for layout in LAYOUTS.layouts:
|
||||||
@ -48,6 +42,10 @@ class ArmedForces:
|
|||||||
# Creates a faction compatible GorceGroup
|
# Creates a faction compatible GorceGroup
|
||||||
self.add_or_update_force_group(ForceGroup.for_layout(layout, faction))
|
self.add_or_update_force_group(ForceGroup.for_layout(layout, faction))
|
||||||
|
|
||||||
|
# Add all preset groups afterwards to prevent them being merged with generics
|
||||||
|
for preset_group in faction.preset_groups:
|
||||||
|
self.forces.append(preset_group.initialize_for_faction(faction))
|
||||||
|
|
||||||
def groups_for_task(self, group_task: GroupTask) -> Iterator[ForceGroup]:
|
def groups_for_task(self, group_task: GroupTask) -> Iterator[ForceGroup]:
|
||||||
for force_group in self.forces:
|
for force_group in self.forces:
|
||||||
if group_task in force_group.tasks:
|
if group_task in force_group.tasks:
|
||||||
@ -59,7 +57,7 @@ class ArmedForces:
|
|||||||
for group in self.groups_for_task(task):
|
for group in self.groups_for_task(task):
|
||||||
if group not in groups:
|
if group not in groups:
|
||||||
groups.append(group)
|
groups.append(group)
|
||||||
return groups
|
return sorted(groups, key=lambda g: g.name)
|
||||||
|
|
||||||
def random_group_for_task(self, group_task: GroupTask) -> Optional[ForceGroup]:
|
def random_group_for_task(self, group_task: GroupTask) -> Optional[ForceGroup]:
|
||||||
unit_groups = list(self.groups_for_task(group_task))
|
unit_groups = list(self.groups_for_task(group_task))
|
||||||
|
|||||||
@ -21,7 +21,7 @@ from game.theater.theatergroundobject import (
|
|||||||
NavalGroundObject,
|
NavalGroundObject,
|
||||||
)
|
)
|
||||||
from game.layout import LAYOUTS
|
from game.layout import LAYOUTS
|
||||||
from game.layout.layout import TgoLayout, TgoLayoutGroup
|
from game.layout.layout import TgoLayout, TgoLayoutUnitGroup
|
||||||
from game.point_with_heading import PointWithHeading
|
from game.point_with_heading import PointWithHeading
|
||||||
from game.theater.theatergroup import IadsGroundGroup, IadsRole, TheaterGroup
|
from game.theater.theatergroup import IadsGroundGroup, IadsRole, TheaterGroup
|
||||||
from game.utils import escape_string_for_lua
|
from game.utils import escape_string_for_lua
|
||||||
@ -67,10 +67,10 @@ class ForceGroup:
|
|||||||
"""
|
"""
|
||||||
units: set[UnitType[Any]] = set()
|
units: set[UnitType[Any]] = set()
|
||||||
statics: set[Type[DcsUnitType]] = set()
|
statics: set[Type[DcsUnitType]] = set()
|
||||||
for group in layout.all_groups:
|
for unit_group in layout.all_unit_groups:
|
||||||
if group.optional and not group.fill:
|
if unit_group.optional and not unit_group.fill:
|
||||||
continue
|
continue
|
||||||
for unit_type in group.possible_types_for_faction(faction):
|
for unit_type in unit_group.possible_types_for_faction(faction):
|
||||||
if issubclass(unit_type, VehicleType):
|
if issubclass(unit_type, VehicleType):
|
||||||
units.add(next(GroundUnitType.for_dcs_type(unit_type)))
|
units.add(next(GroundUnitType.for_dcs_type(unit_type)))
|
||||||
elif issubclass(unit_type, ShipType):
|
elif issubclass(unit_type, ShipType):
|
||||||
@ -89,11 +89,11 @@ class ForceGroup:
|
|||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def has_unit_for_layout_group(self, group: TgoLayoutGroup) -> bool:
|
def has_unit_for_layout_group(self, unit_group: TgoLayoutUnitGroup) -> bool:
|
||||||
for unit in self.units:
|
for unit in self.units:
|
||||||
if (
|
if (
|
||||||
unit.dcs_unit_type in group.unit_types
|
unit.dcs_unit_type in unit_group.unit_types
|
||||||
or unit.unit_class in group.unit_classes
|
or unit.unit_class in unit_group.unit_classes
|
||||||
):
|
):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
@ -102,9 +102,9 @@ class ForceGroup:
|
|||||||
"""Initialize a ForceGroup for the given Faction.
|
"""Initialize a ForceGroup for the given Faction.
|
||||||
This adds accessible units to LayoutGroups with the fill property"""
|
This adds accessible units to LayoutGroups with the fill property"""
|
||||||
for layout in self.layouts:
|
for layout in self.layouts:
|
||||||
for group in layout.all_groups:
|
for unit_group in layout.all_unit_groups:
|
||||||
if group.fill and not self.has_unit_for_layout_group(group):
|
if unit_group.fill and not self.has_unit_for_layout_group(unit_group):
|
||||||
for unit_type in group.possible_types_for_faction(faction):
|
for unit_type in unit_group.possible_types_for_faction(faction):
|
||||||
if issubclass(unit_type, VehicleType):
|
if issubclass(unit_type, VehicleType):
|
||||||
self.units.append(
|
self.units.append(
|
||||||
next(GroundUnitType.for_dcs_type(unit_type))
|
next(GroundUnitType.for_dcs_type(unit_type))
|
||||||
@ -138,38 +138,44 @@ class ForceGroup:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def dcs_unit_types_for_group(
|
def dcs_unit_types_for_group(
|
||||||
self, group: TgoLayoutGroup
|
self, unit_group: TgoLayoutUnitGroup
|
||||||
) -> list[Type[DcsUnitType]]:
|
) -> list[Type[DcsUnitType]]:
|
||||||
"""Return all available DCS Unit Types which can be used in the given
|
"""Return all available DCS Unit Types which can be used in the given
|
||||||
TgoLayoutGroup"""
|
TgoLayoutGroup"""
|
||||||
unit_types = [t for t in group.unit_types if self.has_access_to_dcs_type(t)]
|
unit_types = [
|
||||||
|
t for t in unit_group.unit_types if self.has_access_to_dcs_type(t)
|
||||||
|
]
|
||||||
|
|
||||||
alternative_types = []
|
alternative_types = []
|
||||||
for accessible_unit in self.units:
|
for accessible_unit in self.units:
|
||||||
if accessible_unit.unit_class in group.unit_classes:
|
if accessible_unit.unit_class in unit_group.unit_classes:
|
||||||
unit_types.append(accessible_unit.dcs_unit_type)
|
unit_types.append(accessible_unit.dcs_unit_type)
|
||||||
if accessible_unit.unit_class in group.fallback_classes:
|
if accessible_unit.unit_class in unit_group.fallback_classes:
|
||||||
alternative_types.append(accessible_unit.dcs_unit_type)
|
alternative_types.append(accessible_unit.dcs_unit_type)
|
||||||
|
|
||||||
return unit_types or alternative_types
|
return unit_types or alternative_types
|
||||||
|
|
||||||
def unit_types_for_group(self, group: TgoLayoutGroup) -> Iterator[UnitType[Any]]:
|
def unit_types_for_group(
|
||||||
for dcs_type in self.dcs_unit_types_for_group(group):
|
self, unit_group: TgoLayoutUnitGroup
|
||||||
|
) -> Iterator[UnitType[Any]]:
|
||||||
|
for dcs_type in self.dcs_unit_types_for_group(unit_group):
|
||||||
if issubclass(dcs_type, VehicleType):
|
if issubclass(dcs_type, VehicleType):
|
||||||
yield next(GroundUnitType.for_dcs_type(dcs_type))
|
yield next(GroundUnitType.for_dcs_type(dcs_type))
|
||||||
elif issubclass(dcs_type, ShipType):
|
elif issubclass(dcs_type, ShipType):
|
||||||
yield next(ShipUnitType.for_dcs_type(dcs_type))
|
yield next(ShipUnitType.for_dcs_type(dcs_type))
|
||||||
|
|
||||||
def statics_for_group(self, group: TgoLayoutGroup) -> Iterator[Type[DcsUnitType]]:
|
def statics_for_group(
|
||||||
for dcs_type in self.dcs_unit_types_for_group(group):
|
self, unit_group: TgoLayoutUnitGroup
|
||||||
|
) -> Iterator[Type[DcsUnitType]]:
|
||||||
|
for dcs_type in self.dcs_unit_types_for_group(unit_group):
|
||||||
if issubclass(dcs_type, StaticType):
|
if issubclass(dcs_type, StaticType):
|
||||||
yield dcs_type
|
yield dcs_type
|
||||||
|
|
||||||
def random_dcs_unit_type_for_group(
|
def random_dcs_unit_type_for_group(
|
||||||
self, group: TgoLayoutGroup
|
self, unit_group: TgoLayoutUnitGroup
|
||||||
) -> Type[DcsUnitType]:
|
) -> Type[DcsUnitType]:
|
||||||
"""Return random DCS Unit Type which can be used in the given TgoLayoutGroup"""
|
"""Return random DCS Unit Type which can be used in the given TgoLayoutGroup"""
|
||||||
return random.choice(self.dcs_unit_types_for_group(group))
|
return random.choice(self.dcs_unit_types_for_group(unit_group))
|
||||||
|
|
||||||
def merge_group(self, new_group: ForceGroup) -> None:
|
def merge_group(self, new_group: ForceGroup) -> None:
|
||||||
"""Merge the group with another similar group."""
|
"""Merge the group with another similar group."""
|
||||||
@ -204,22 +210,22 @@ class ForceGroup:
|
|||||||
"""Create a TheaterGroundObject for the given template"""
|
"""Create a TheaterGroundObject for the given template"""
|
||||||
go = layout.create_ground_object(name, location, control_point)
|
go = layout.create_ground_object(name, location, control_point)
|
||||||
# Generate all groups using the randomization if it defined
|
# Generate all groups using the randomization if it defined
|
||||||
for group_name, groups in layout.groups.items():
|
for tgo_group in layout.groups:
|
||||||
for group in groups:
|
for unit_group in tgo_group.unit_groups:
|
||||||
# Choose a random unit_type for the group
|
# Choose a random unit_type for the group
|
||||||
try:
|
try:
|
||||||
unit_type = self.random_dcs_unit_type_for_group(group)
|
unit_type = self.random_dcs_unit_type_for_group(unit_group)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
if group.optional:
|
if unit_group.optional:
|
||||||
# If group is optional it is ok when no unit_type is available
|
# If group is optional it is ok when no unit_type is available
|
||||||
continue
|
continue
|
||||||
# if non-optional this is a error
|
# if non-optional this is a error
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
f"No accessible unit for {self.name} - {group.name}"
|
f"No accessible unit for {self.name} - {unit_group.name}"
|
||||||
)
|
)
|
||||||
tgo_group_name = f"{name} ({group_name})"
|
tgo_group_name = f"{name} ({tgo_group.group_name})"
|
||||||
self.create_theater_group_for_tgo(
|
self.create_theater_group_for_tgo(
|
||||||
go, group, tgo_group_name, game, unit_type
|
go, unit_group, tgo_group_name, game, unit_type
|
||||||
)
|
)
|
||||||
|
|
||||||
return go
|
return go
|
||||||
@ -227,7 +233,7 @@ class ForceGroup:
|
|||||||
def create_theater_group_for_tgo(
|
def create_theater_group_for_tgo(
|
||||||
self,
|
self,
|
||||||
ground_object: TheaterGroundObject,
|
ground_object: TheaterGroundObject,
|
||||||
group: TgoLayoutGroup,
|
unit_group: TgoLayoutUnitGroup,
|
||||||
group_name: str,
|
group_name: str,
|
||||||
game: Game,
|
game: Game,
|
||||||
unit_type: Type[DcsUnitType],
|
unit_type: Type[DcsUnitType],
|
||||||
@ -237,12 +243,12 @@ class ForceGroup:
|
|||||||
# Random UnitCounter if not forced
|
# Random UnitCounter if not forced
|
||||||
if unit_count is None:
|
if unit_count is None:
|
||||||
# Choose a random group_size based on the layouts unit_count
|
# Choose a random group_size based on the layouts unit_count
|
||||||
unit_count = group.group_size
|
unit_count = unit_group.group_size
|
||||||
if unit_count == 0:
|
if unit_count == 0:
|
||||||
# No units to be created so dont create a theater group for them
|
# No units to be created so dont create a theater group for them
|
||||||
return
|
return
|
||||||
# Generate Units
|
# Generate Units
|
||||||
units = group.generate_units(ground_object, unit_type, unit_count)
|
units = unit_group.generate_units(ground_object, unit_type, unit_count)
|
||||||
# Get or create the TheaterGroup
|
# Get or create the TheaterGroup
|
||||||
ground_group = ground_object.group_by_name(group_name)
|
ground_group = ground_object.group_by_name(group_name)
|
||||||
if ground_group is not None:
|
if ground_group is not None:
|
||||||
@ -261,9 +267,9 @@ class ForceGroup:
|
|||||||
):
|
):
|
||||||
# Recreate the TheaterGroup as IadsGroundGroup
|
# Recreate the TheaterGroup as IadsGroundGroup
|
||||||
ground_group = IadsGroundGroup.from_group(ground_group)
|
ground_group = IadsGroundGroup.from_group(ground_group)
|
||||||
if group.sub_task is not None:
|
if unit_group.sub_task is not None:
|
||||||
# Use the special sub_task of the TheaterGroup
|
# Use the special sub_task of the TheaterGroup
|
||||||
iads_task = group.sub_task
|
iads_task = unit_group.sub_task
|
||||||
else:
|
else:
|
||||||
# Use the primary task of the ForceGroup
|
# Use the primary task of the ForceGroup
|
||||||
iads_task = self.tasks[0]
|
iads_task = self.tasks[0]
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
from .layout import TgoLayout, TgoLayoutGroup
|
from .layout import TgoLayout, TgoLayoutGroup, TgoLayoutUnitGroup
|
||||||
from .layoutloader import LayoutLoader
|
from .layoutloader import LayoutLoader
|
||||||
|
|
||||||
LAYOUTS = LayoutLoader()
|
LAYOUTS = LayoutLoader()
|
||||||
|
|||||||
@ -60,6 +60,21 @@ class LayoutUnit:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TgoLayoutGroup:
|
class TgoLayoutGroup:
|
||||||
|
"""The layout of a group which will generate a DCS group later. The TgoLayoutGroup has one or many TgoLayoutUnitGroup which represents a set of unit of the same type. Therefore the TgoLayoutGroup is a logical grouping of different unit_types. A TgoLayout can have one or many TgoLayoutGroup"""
|
||||||
|
|
||||||
|
# The group name which will be used later as the DCS group name
|
||||||
|
group_name: str
|
||||||
|
|
||||||
|
# The index of the group within the TgoLayout. Used to preserve that the order of
|
||||||
|
# the groups generated match the order defined in the layout yaml.
|
||||||
|
group_index: int
|
||||||
|
|
||||||
|
# List of all connected TgoLayoutUnitGroup
|
||||||
|
unit_groups: list[TgoLayoutUnitGroup] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TgoLayoutUnitGroup:
|
||||||
"""The layout of a single type of unit within the TgoLayout
|
"""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
|
Each DCS group that is spawned in the mission is composed of one or more
|
||||||
@ -95,6 +110,9 @@ class TgoLayoutGroup:
|
|||||||
unit_classes: list[UnitClass] = field(default_factory=list)
|
unit_classes: list[UnitClass] = field(default_factory=list)
|
||||||
fallback_classes: list[UnitClass] = field(default_factory=list)
|
fallback_classes: list[UnitClass] = field(default_factory=list)
|
||||||
|
|
||||||
|
# The index of the TgoLayoutGroup within the Layout
|
||||||
|
unit_index: int = field(default_factory=int)
|
||||||
|
|
||||||
# Allows a group to have a special SubTask (PointDefence for example)
|
# Allows a group to have a special SubTask (PointDefence for example)
|
||||||
sub_task: Optional[GroupTask] = None
|
sub_task: Optional[GroupTask] = None
|
||||||
|
|
||||||
@ -172,21 +190,13 @@ class TgoLayout:
|
|||||||
self.description = description
|
self.description = description
|
||||||
self.tasks: list[GroupTask] = [] # The supported
|
self.tasks: list[GroupTask] = [] # The supported
|
||||||
|
|
||||||
# Mapping of group name and LayoutGroups for a specific TgoGroup
|
# All TgoGroups this layout has.
|
||||||
# A Group can have multiple TgoLayoutGroups which get merged together
|
self.groups: list[TgoLayoutGroup] = []
|
||||||
self.groups: dict[str, list[TgoLayoutGroup]] = defaultdict(list)
|
|
||||||
|
|
||||||
# A generic layout will be used to create generic ForceGroups during the
|
# A generic layout will be used to create generic ForceGroups during the
|
||||||
# campaign initialization. For each generic layout a new Group will be created.
|
# campaign initialization. For each generic layout a new Group will be created.
|
||||||
self.generic: bool = False
|
self.generic: bool = False
|
||||||
|
|
||||||
def add_layout_group(
|
|
||||||
self, name: str, group: TgoLayoutGroup, index: int = 0
|
|
||||||
) -> None:
|
|
||||||
"""Adds the layout group to the group dict at the given index"""
|
|
||||||
# If the index is greater than the actual len it will add it after the last item
|
|
||||||
self.groups[name].insert(min(len(self.groups[name]), index), group)
|
|
||||||
|
|
||||||
def usable_by_faction(self, faction: Faction) -> bool:
|
def usable_by_faction(self, faction: Faction) -> bool:
|
||||||
# Special handling for Buildings
|
# Special handling for Buildings
|
||||||
if (
|
if (
|
||||||
@ -199,7 +209,7 @@ class TgoLayout:
|
|||||||
try:
|
try:
|
||||||
return all(
|
return all(
|
||||||
len(group.possible_types_for_faction(faction)) > 0
|
len(group.possible_types_for_faction(faction)) > 0
|
||||||
for group in self.all_groups
|
for group in self.all_unit_groups
|
||||||
if not group.optional
|
if not group.optional
|
||||||
)
|
)
|
||||||
except LayoutException:
|
except LayoutException:
|
||||||
@ -219,9 +229,9 @@ class TgoLayout:
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def all_groups(self) -> Iterator[TgoLayoutGroup]:
|
def all_unit_groups(self) -> Iterator[TgoLayoutUnitGroup]:
|
||||||
for groups in self.groups.values():
|
for group in self.groups:
|
||||||
yield from groups
|
yield from group.unit_groups
|
||||||
|
|
||||||
|
|
||||||
class AntiAirLayout(TgoLayout):
|
class AntiAirLayout(TgoLayout):
|
||||||
|
|||||||
@ -18,6 +18,7 @@ from game.data.groups import GroupRole
|
|||||||
from game.layout.layout import (
|
from game.layout.layout import (
|
||||||
TgoLayout,
|
TgoLayout,
|
||||||
TgoLayoutGroup,
|
TgoLayoutGroup,
|
||||||
|
TgoLayoutUnitGroup,
|
||||||
LayoutUnit,
|
LayoutUnit,
|
||||||
AntiAirLayout,
|
AntiAirLayout,
|
||||||
BuildingLayout,
|
BuildingLayout,
|
||||||
@ -29,10 +30,10 @@ from game.layout.layoutmapping import LayoutMapping
|
|||||||
from game.profiling import logged_duration
|
from game.profiling import logged_duration
|
||||||
from game.version import VERSION
|
from game.version import VERSION
|
||||||
|
|
||||||
TEMPLATE_DIR = "resources/layouts/"
|
LAYOUT_DIR = "resources/layouts/"
|
||||||
TEMPLATE_DUMP = "Liberation/layouts.p"
|
LAYOUT_DUMP = "Liberation/layouts.p"
|
||||||
|
|
||||||
TEMPLATE_TYPES = {
|
LAYOUT_TYPES = {
|
||||||
GroupRole.AIR_DEFENSE: AntiAirLayout,
|
GroupRole.AIR_DEFENSE: AntiAirLayout,
|
||||||
GroupRole.BUILDING: BuildingLayout,
|
GroupRole.BUILDING: BuildingLayout,
|
||||||
GroupRole.NAVAL: NavalLayout,
|
GroupRole.NAVAL: NavalLayout,
|
||||||
@ -62,7 +63,7 @@ class LayoutLoader:
|
|||||||
"""This will load all pre-loaded layouts from a pickle file.
|
"""This will load all pre-loaded layouts from a pickle file.
|
||||||
If pickle can not be loaded it will import and dump the layouts"""
|
If pickle can not be loaded it will import and dump the layouts"""
|
||||||
# We use a pickle for performance reasons. Importing takes many seconds
|
# We use a pickle for performance reasons. Importing takes many seconds
|
||||||
file = Path(persistency.base_path()) / TEMPLATE_DUMP
|
file = Path(persistency.base_path()) / LAYOUT_DUMP
|
||||||
if file.is_file():
|
if file.is_file():
|
||||||
# Load from pickle if existing
|
# Load from pickle if existing
|
||||||
with file.open("rb") as f:
|
with file.open("rb") as f:
|
||||||
@ -82,7 +83,7 @@ class LayoutLoader:
|
|||||||
self._layouts = {}
|
self._layouts = {}
|
||||||
mappings: dict[str, list[LayoutMapping]] = defaultdict(list)
|
mappings: dict[str, list[LayoutMapping]] = defaultdict(list)
|
||||||
with logged_duration("Parsing mapping yamls"):
|
with logged_duration("Parsing mapping yamls"):
|
||||||
for file in Path(TEMPLATE_DIR).rglob("*.yaml"):
|
for file in Path(LAYOUT_DIR).rglob("*.yaml"):
|
||||||
if not file.is_file():
|
if not file.is_file():
|
||||||
raise RuntimeError(f"{file.name} is not a file")
|
raise RuntimeError(f"{file.name} is not a file")
|
||||||
with file.open("r", encoding="utf-8") as f:
|
with file.open("r", encoding="utf-8") as f:
|
||||||
@ -95,11 +96,17 @@ class LayoutLoader:
|
|||||||
with ThreadPoolExecutor() as exe:
|
with ThreadPoolExecutor() as exe:
|
||||||
exe.map(self._load_from_miz, mappings.keys(), mappings.values())
|
exe.map(self._load_from_miz, mappings.keys(), mappings.values())
|
||||||
|
|
||||||
|
# Sort al the LayoutGroups with the correct index
|
||||||
|
for layout in self._layouts.values():
|
||||||
|
layout.groups.sort(key=lambda g: g.group_index)
|
||||||
|
for group in layout.groups:
|
||||||
|
group.unit_groups.sort(key=lambda ug: ug.unit_index)
|
||||||
|
|
||||||
logging.info(f"Imported {len(self._layouts)} layouts")
|
logging.info(f"Imported {len(self._layouts)} layouts")
|
||||||
self._dump_templates()
|
self._dump_templates()
|
||||||
|
|
||||||
def _dump_templates(self) -> None:
|
def _dump_templates(self) -> None:
|
||||||
file = Path(persistency.base_path()) / TEMPLATE_DUMP
|
file = Path(persistency.base_path()) / LAYOUT_DUMP
|
||||||
dump = (VERSION, self._layouts)
|
dump = (VERSION, self._layouts)
|
||||||
with file.open("wb") as fdata:
|
with file.open("wb") as fdata:
|
||||||
pickle.dump(dump, fdata)
|
pickle.dump(dump, fdata)
|
||||||
@ -127,7 +134,7 @@ class LayoutLoader:
|
|||||||
):
|
):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
g_id, group_name, group_mapping = mapping.group_for_name(
|
g_id, u_id, group_name, group_mapping = mapping.group_for_name(
|
||||||
dcs_group.name
|
dcs_group.name
|
||||||
)
|
)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -143,40 +150,46 @@ class LayoutLoader:
|
|||||||
layout = self._layouts.get(mapping.name, None)
|
layout = self._layouts.get(mapping.name, None)
|
||||||
if layout is None:
|
if layout is None:
|
||||||
# Create a new template
|
# Create a new template
|
||||||
layout = TEMPLATE_TYPES[mapping.primary_role](
|
layout = LAYOUT_TYPES[mapping.primary_role](
|
||||||
mapping.name, mapping.description
|
mapping.name, mapping.description
|
||||||
)
|
)
|
||||||
layout.generic = mapping.generic
|
layout.generic = mapping.generic
|
||||||
layout.tasks = mapping.tasks
|
layout.tasks = mapping.tasks
|
||||||
self._layouts[layout.name] = layout
|
self._layouts[layout.name] = layout
|
||||||
|
|
||||||
for i, unit in enumerate(dcs_group.units):
|
for i, unit in enumerate(dcs_group.units):
|
||||||
group_layout = None
|
unit_group = None
|
||||||
for group in layout.all_groups:
|
for _unit_group in layout.all_unit_groups:
|
||||||
if group.name == group_mapping.name:
|
if _unit_group.name == group_mapping.name:
|
||||||
# We already have a layoutgroup for this dcs_group
|
# We already have a layoutgroup for this dcs_group
|
||||||
group_layout = group
|
unit_group = _unit_group
|
||||||
if not group_layout:
|
if not unit_group:
|
||||||
group_layout = TgoLayoutGroup(
|
unit_group = TgoLayoutUnitGroup(
|
||||||
group_mapping.name,
|
group_mapping.name,
|
||||||
[],
|
[],
|
||||||
group_mapping.unit_count,
|
group_mapping.unit_count,
|
||||||
group_mapping.unit_types,
|
group_mapping.unit_types,
|
||||||
group_mapping.unit_classes,
|
group_mapping.unit_classes,
|
||||||
group_mapping.fallback_classes,
|
group_mapping.fallback_classes,
|
||||||
|
u_id,
|
||||||
)
|
)
|
||||||
group_layout.optional = group_mapping.optional
|
unit_group.optional = group_mapping.optional
|
||||||
group_layout.fill = group_mapping.fill
|
unit_group.fill = group_mapping.fill
|
||||||
group_layout.sub_task = group_mapping.sub_task
|
unit_group.sub_task = group_mapping.sub_task
|
||||||
# Add the group at the correct index
|
tgo_group = None
|
||||||
layout.add_layout_group(group_name, group_layout, g_id)
|
for _tgo_group in layout.groups:
|
||||||
|
if _tgo_group.group_name == group_name:
|
||||||
|
tgo_group = _tgo_group
|
||||||
|
if tgo_group is None:
|
||||||
|
tgo_group = TgoLayoutGroup(group_name, g_id)
|
||||||
|
layout.groups.append(tgo_group)
|
||||||
|
tgo_group.unit_groups.append(unit_group)
|
||||||
layout_unit = LayoutUnit.from_unit(unit)
|
layout_unit = LayoutUnit.from_unit(unit)
|
||||||
if i == 0 and layout.name not in template_position:
|
if i == 0 and layout.name not in template_position:
|
||||||
template_position[layout.name] = unit.position
|
template_position[layout.name] = unit.position
|
||||||
layout_unit.position = (
|
layout_unit.position = (
|
||||||
layout_unit.position - template_position[layout.name]
|
layout_unit.position - template_position[layout.name]
|
||||||
)
|
)
|
||||||
group_layout.layout_units.append(layout_unit)
|
unit_group.layout_units.append(layout_unit)
|
||||||
|
|
||||||
def by_name(self, name: str) -> TgoLayout:
|
def by_name(self, name: str) -> TgoLayout:
|
||||||
self.initialize()
|
self.initialize()
|
||||||
|
|||||||
@ -128,9 +128,11 @@ class LayoutMapping:
|
|||||||
layout_file,
|
layout_file,
|
||||||
)
|
)
|
||||||
|
|
||||||
def group_for_name(self, name: str) -> tuple[int, str, GroupLayoutMapping]:
|
def group_for_name(self, name: str) -> tuple[int, int, str, GroupLayoutMapping]:
|
||||||
|
g_id = 0
|
||||||
for group_name, group_mappings in self.groups.items():
|
for group_name, group_mappings in self.groups.items():
|
||||||
for g_id, group_mapping in enumerate(group_mappings):
|
for u_id, group_mapping in enumerate(group_mappings):
|
||||||
if group_mapping.name == name or name in group_mapping.statics:
|
if group_mapping.name == name or name in group_mapping.statics:
|
||||||
return g_id, group_name, group_mapping
|
return g_id, u_id, group_name, group_mapping
|
||||||
|
g_id += 1
|
||||||
raise KeyError
|
raise KeyError
|
||||||
|
|||||||
@ -25,7 +25,7 @@ from game.data.groups import GroupRole, GroupTask
|
|||||||
from game.layout.layout import (
|
from game.layout.layout import (
|
||||||
LayoutException,
|
LayoutException,
|
||||||
TgoLayout,
|
TgoLayout,
|
||||||
TgoLayoutGroup,
|
TgoLayoutUnitGroup,
|
||||||
)
|
)
|
||||||
from game.theater import TheaterGroundObject
|
from game.theater import TheaterGroundObject
|
||||||
from game.theater.theatergroundobject import (
|
from game.theater.theatergroundobject import (
|
||||||
@ -38,7 +38,7 @@ from qt_ui.uiconstants import EVENT_ICONS
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class QTgoLayoutGroup:
|
class QTgoLayoutGroup:
|
||||||
layout: TgoLayoutGroup
|
layout: TgoLayoutUnitGroup
|
||||||
dcs_unit_type: Type[UnitType]
|
dcs_unit_type: Type[UnitType]
|
||||||
amount: int
|
amount: int
|
||||||
unit_price: int
|
unit_price: int
|
||||||
@ -63,7 +63,7 @@ class QTgoLayout:
|
|||||||
class QTgoLayoutGroupRow(QWidget):
|
class QTgoLayoutGroupRow(QWidget):
|
||||||
group_template_changed = Signal()
|
group_template_changed = Signal()
|
||||||
|
|
||||||
def __init__(self, force_group: ForceGroup, group: TgoLayoutGroup) -> None:
|
def __init__(self, force_group: ForceGroup, group: TgoLayoutUnitGroup) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.grid_layout = QGridLayout()
|
self.grid_layout = QGridLayout()
|
||||||
self.setLayout(self.grid_layout)
|
self.setLayout(self.grid_layout)
|
||||||
@ -170,8 +170,10 @@ class QGroundObjectTemplateLayout(QGroupBox):
|
|||||||
self.layout_model.groups = defaultdict(list)
|
self.layout_model.groups = defaultdict(list)
|
||||||
for id in range(self.template_grid.count()):
|
for id in range(self.template_grid.count()):
|
||||||
self.template_grid.itemAt(id).widget().deleteLater()
|
self.template_grid.itemAt(id).widget().deleteLater()
|
||||||
for group_name, groups in self.layout_model.layout.groups.items():
|
for group in self.layout_model.layout.groups:
|
||||||
self.add_theater_group(group_name, self.layout_model.force_group, groups)
|
self.add_theater_group(
|
||||||
|
group.group_name, self.layout_model.force_group, group.unit_groups
|
||||||
|
)
|
||||||
self.group_template_changed()
|
self.group_template_changed()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -183,7 +185,7 @@ class QGroundObjectTemplateLayout(QGroupBox):
|
|||||||
return self.cost <= self.game.blue.budget
|
return self.cost <= self.game.blue.budget
|
||||||
|
|
||||||
def add_theater_group(
|
def add_theater_group(
|
||||||
self, group_name: str, force_group: ForceGroup, groups: list[TgoLayoutGroup]
|
self, group_name: str, force_group: ForceGroup, groups: list[TgoLayoutUnitGroup]
|
||||||
) -> None:
|
) -> None:
|
||||||
group_box = QGroupBox(group_name)
|
group_box = QGroupBox(group_name)
|
||||||
vbox_layout = QVBoxLayout()
|
vbox_layout = QVBoxLayout()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user