dcs-retribution/game/scenery_group.py
2023-09-03 23:41:15 +02:00

133 lines
4.7 KiB
Python

from __future__ import annotations
from typing import Iterable, List, TYPE_CHECKING, Optional
from dcs.mapping import Polygon
from dcs.triggers import TriggerZone, TriggerZoneCircular, TriggerZoneQuadPoint
from game.data.groups import GroupTask
from game.theater.theatergroundobject import NAME_BY_CATEGORY
if TYPE_CHECKING:
from dcs.mapping import Point
def _point_in_zone(zone: TriggerZone, pos: Point) -> bool:
if isinstance(zone, TriggerZoneCircular):
return zone.position.distance_to_point(pos) < zone.radius
elif isinstance(zone, TriggerZoneQuadPoint):
return Polygon(pos._terrain, zone.verticies).point_in_poly(pos)
raise RuntimeError(f"Invalid trigger-zone: {zone.name}")
class SceneryGroupError(RuntimeError):
"""Error for when there are insufficient conditions to create a SceneryGroup."""
pass
class SceneryGroup:
"""Store information about a scenery objective."""
def __init__(
self, zone_def: TriggerZone, zones: Iterable[TriggerZone], category: str
) -> None:
self.zone_def = zone_def
self.zones = zones
self.position = zone_def.position
self.category = category
@staticmethod
def from_trigger_zones(trigger_zones: Iterable[TriggerZone]) -> List[SceneryGroup]:
"""Define scenery objectives based on their encompassing blue/red circle."""
zone_definitions = []
white_zones = []
scenery_groups = []
# Aggregate trigger zones into different groups based on color.
for zone in trigger_zones:
if SceneryGroup.is_blue(zone):
zone_definitions.append(zone)
if SceneryGroup.is_white(zone):
white_zones.append(zone)
# For each objective definition.
for zone_def in zone_definitions:
zone_def_name = zone_def.name
if len(zone_def.properties) == 0:
raise SceneryGroupError(
"Undefined SceneryGroup category in TriggerZone: " + zone_def_name
)
# Arbitrary campaign design requirement: First property must define the category.
zone_def_category = zone_def.properties[1].get("value").lower()
valid_white_zones = []
for zone in list(white_zones):
if _point_in_zone(zone_def, zone.position):
valid_white_zones.append(zone)
white_zones.remove(zone)
if len(valid_white_zones) > 0 and zone_def_category in NAME_BY_CATEGORY:
scenery_groups.append(
SceneryGroup(zone_def, valid_white_zones, zone_def_category)
)
elif len(valid_white_zones) == 0:
raise SceneryGroupError(
"No white triggerzones found in: " + zone_def_name
)
elif zone_def_category not in NAME_BY_CATEGORY:
raise SceneryGroupError(
"Incorrect TriggerZone category definition for: "
+ zone_def_name
+ " in campaign definition. TriggerZone category: "
+ zone_def_category
)
return scenery_groups
@staticmethod
def group_task_for_scenery_group_category(category: str) -> Optional[GroupTask]:
if category == "allycamp":
return GroupTask.ALLY_CAMP
elif category == "ammo":
return GroupTask.AMMO
elif category == "commandcenter":
return GroupTask.COMMAND_CENTER
elif category == "comms":
return GroupTask.COMMS
elif category == "derrick":
return GroupTask.DERRICK
elif category == "factory":
return GroupTask.FACTORY
elif category == "farp":
return GroupTask.FARP
elif category == "fob":
return GroupTask.FOB
elif category == "fuel":
return GroupTask.FUEL
elif category == "oil":
return GroupTask.OIL
elif category == "power":
return GroupTask.POWER
elif category == "village":
return GroupTask.VILLAGE
elif category == "ware":
return GroupTask.WARE
elif category == "ww2bunker":
return GroupTask.WW2_BUNKER
return None
@staticmethod
def is_blue(zone: TriggerZone) -> bool:
# Blue in RGB is [0 Red], [0 Green], [1 Blue]. Ignore the fourth position: Transparency.
return zone.color[1] == 0 and zone.color[2] == 0 and zone.color[3] == 1
@staticmethod
def is_white(zone: TriggerZone) -> bool:
# White in RGB is [1 Red], [1 Green], [1 Blue]. Ignore the fourth position: Transparency.
return zone.color[1] == 1 and zone.color[2] == 1 and zone.color[3] == 1