mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
refactor to enum typing and many other fixes fix tests attempt to fix some typescript more typescript fixes more typescript test fixes revert all API changes update to pydcs mypy fixes Use properties to check if player is blue/red/neutral update requirements.txt black -_- bump pydcs and fix mypy add opponent property bump pydcs
312 lines
13 KiB
Python
312 lines
13 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import TYPE_CHECKING, List
|
|
|
|
from dcs import Point
|
|
from dcs.action import (
|
|
ClearFlag,
|
|
DoScript,
|
|
MarkToAll,
|
|
SetFlag,
|
|
RemoveSceneObjects,
|
|
RemoveSceneObjectsMask,
|
|
SceneryDestructionZone,
|
|
)
|
|
from dcs.condition import (
|
|
AllOfCoalitionOutsideZone,
|
|
FlagIsFalse,
|
|
FlagIsTrue,
|
|
PartOfCoalitionInZone,
|
|
TimeAfter,
|
|
TimeSinceFlag,
|
|
)
|
|
from dcs.mission import Mission
|
|
from dcs.task import Option
|
|
from dcs.translation import String
|
|
from dcs.triggers import Event, TriggerCondition, TriggerOnce
|
|
from dcs.unit import Skill
|
|
|
|
from game.theater import Airfield
|
|
from game.theater.controlpoint import Fob, TRIGGER_RADIUS_CAPTURE
|
|
|
|
if TYPE_CHECKING:
|
|
from game.game import Game
|
|
|
|
PUSH_TRIGGER_SIZE = 3000
|
|
PUSH_TRIGGER_ACTIVATION_AGL = 25
|
|
|
|
REGROUP_ZONE_DISTANCE = 12000
|
|
REGROUP_ALT = 5000
|
|
|
|
TRIGGER_WAYPOINT_OFFSET = 2
|
|
TRIGGER_MIN_DISTANCE_FROM_START = 10000
|
|
# modified since we now have advanced SAM units
|
|
TRIGGER_RADIUS_MINIMUM = 3000000
|
|
|
|
TRIGGER_RADIUS_SMALL = 50000
|
|
TRIGGER_RADIUS_MEDIUM = 100000
|
|
TRIGGER_RADIUS_LARGE = 150000
|
|
TRIGGER_RADIUS_ALL_MAP = 3000000
|
|
TRIGGER_RADIUS_CLEAR_SCENERY = 1000
|
|
|
|
|
|
class Silence(Option):
|
|
Key = 7
|
|
|
|
|
|
class TriggerGenerator:
|
|
capture_zone_types = (Fob, Airfield)
|
|
capture_zone_flag = 600
|
|
|
|
def __init__(self, mission: Mission, game: Game) -> None:
|
|
self.mission = mission
|
|
self.game = game
|
|
|
|
def _set_allegiances(self, player_coalition: str, enemy_coalition: str) -> None:
|
|
"""
|
|
Set airbase initial coalition
|
|
"""
|
|
|
|
# Empty neutrals airports
|
|
airfields = [
|
|
cp for cp in self.game.theater.controlpoints if isinstance(cp, Airfield)
|
|
]
|
|
airport_ids = {cp.airport.id for cp in airfields}
|
|
for airport in self.mission.terrain.airport_list():
|
|
if airport.id not in airport_ids:
|
|
airport.unlimited_fuel = False
|
|
airport.unlimited_munitions = False
|
|
airport.unlimited_aircrafts = False
|
|
airport.gasoline_init = 0
|
|
airport.methanol_mixture_init = 0
|
|
airport.diesel_init = 0
|
|
airport.jet_init = 0
|
|
airport.operating_level_air = 0
|
|
airport.operating_level_equipment = 0
|
|
airport.operating_level_fuel = 0
|
|
|
|
for airport in self.mission.terrain.airport_list():
|
|
if airport.id not in airport_ids:
|
|
airport.unlimited_fuel = True
|
|
airport.unlimited_munitions = True
|
|
airport.unlimited_aircrafts = True
|
|
|
|
for airfield in airfields:
|
|
cp_airport = self.mission.terrain.airport_by_id(airfield.airport.id)
|
|
if cp_airport is None:
|
|
raise RuntimeError(
|
|
f"Could not find {airfield.airport.name} in the mission"
|
|
)
|
|
if airfield.captured.is_neutral:
|
|
cp_airport.set_coalition("neutral")
|
|
elif airfield.captured.is_blue:
|
|
cp_airport.set_coalition(player_coalition)
|
|
elif airfield.captured.is_red:
|
|
cp_airport.set_coalition(enemy_coalition)
|
|
|
|
def _set_skill(self, player_coalition: str, enemy_coalition: str) -> None:
|
|
"""
|
|
Set skill level for all aircraft in the mission
|
|
"""
|
|
for coalition_name, coalition in self.mission.coalition.items():
|
|
if coalition_name == player_coalition:
|
|
skill_level = Skill(self.game.settings.player_skill)
|
|
elif coalition_name == enemy_coalition:
|
|
skill_level = Skill(self.game.settings.enemy_vehicle_skill)
|
|
else:
|
|
continue
|
|
|
|
for country in coalition.countries.values():
|
|
for vehicle_group in country.vehicle_group:
|
|
vehicle_group.set_skill(skill_level)
|
|
|
|
def _gen_markers(self) -> None:
|
|
"""
|
|
Generate markers on F10 map for each existing objective
|
|
"""
|
|
if self.game.settings.generate_marks:
|
|
mark_trigger = TriggerOnce(Event.NoEvent, "Marks generator")
|
|
mark_trigger.add_condition(TimeAfter(1))
|
|
v = 10
|
|
for cp in self.game.theater.controlpoints:
|
|
seen = set()
|
|
for ground_object in cp.ground_objects:
|
|
if ground_object.obj_name in seen:
|
|
continue
|
|
|
|
seen.add(ground_object.obj_name)
|
|
for location in ground_object.mark_locations:
|
|
zone = self.mission.triggers.add_triggerzone(
|
|
location, radius=10, hidden=True, name="MARK"
|
|
)
|
|
if cp.captured.is_blue:
|
|
name = ground_object.obj_name + " [ALLY]"
|
|
else:
|
|
name = ground_object.obj_name + " [ENEMY]"
|
|
mark_trigger.add_action(MarkToAll(v, zone.id, String(name)))
|
|
v += 1
|
|
self.mission.triggerrules.triggers.append(mark_trigger)
|
|
|
|
def _generate_clear_statics_trigger(self, scenery_clear_zones: List[Point]) -> None:
|
|
for zone_center in scenery_clear_zones:
|
|
trigger_zone = self.mission.triggers.add_triggerzone(
|
|
zone_center,
|
|
radius=TRIGGER_RADIUS_CLEAR_SCENERY,
|
|
hidden=False,
|
|
name="CLEAR",
|
|
)
|
|
clear_trigger = TriggerCondition(Event.NoEvent, "Clear Trigger")
|
|
clear_flag = self.get_capture_zone_flag()
|
|
clear_trigger.add_condition(TimeSinceFlag(clear_flag, 30))
|
|
clear_trigger.add_action(ClearFlag(clear_flag))
|
|
clear_trigger.add_action(SetFlag(clear_flag))
|
|
clear_trigger.add_action(
|
|
RemoveSceneObjects(
|
|
objects_mask=RemoveSceneObjectsMask.OBJECTS_ONLY,
|
|
zone=trigger_zone.id,
|
|
)
|
|
)
|
|
clear_trigger.add_action(
|
|
SceneryDestructionZone(destruction_level=100, zone=trigger_zone.id)
|
|
)
|
|
self.mission.triggerrules.triggers.append(clear_trigger)
|
|
|
|
enable_clear_trigger = TriggerOnce(Event.NoEvent, "Enable Clear Trigger")
|
|
enable_clear_trigger.add_condition(TimeAfter(30))
|
|
enable_clear_trigger.add_action(ClearFlag(clear_flag))
|
|
enable_clear_trigger.add_action(SetFlag(clear_flag))
|
|
# clear_trigger.add_action(MessageToAll(text=String("Enable clear trigger"),))
|
|
self.mission.triggerrules.triggers.append(enable_clear_trigger)
|
|
|
|
def _generate_capture_triggers(
|
|
self, player_coalition: str, enemy_coalition: str
|
|
) -> None:
|
|
"""Creates a pair of triggers for each control point of `cls.capture_zone_types`.
|
|
One for the initial capture of a control point, and one if it is recaptured.
|
|
Directly appends to the global `base_capture_events` var declared by `dcs_libaration.lua`
|
|
"""
|
|
for cp in self.game.theater.controlpoints:
|
|
if isinstance(cp, self.capture_zone_types) and not cp.is_carrier:
|
|
if cp.captured.is_blue:
|
|
attacking_coalition = enemy_coalition
|
|
attack_coalition_int = 1 # 1 is the Event int for Red
|
|
defending_coalition = player_coalition
|
|
defend_coalition_int = 2 # 2 is the Event int for Blue
|
|
else:
|
|
attacking_coalition = player_coalition
|
|
attack_coalition_int = 2
|
|
defending_coalition = enemy_coalition
|
|
defend_coalition_int = 1
|
|
|
|
trigger_zone = self.mission.triggers.add_triggerzone(
|
|
cp.position,
|
|
radius=TRIGGER_RADIUS_CAPTURE,
|
|
hidden=False,
|
|
name="CAPTURE",
|
|
)
|
|
flag = self.get_capture_zone_flag()
|
|
capture_trigger = TriggerCondition(Event.NoEvent, "Capture Trigger")
|
|
capture_trigger.add_condition(
|
|
AllOfCoalitionOutsideZone(
|
|
defending_coalition, trigger_zone.id, unit_type="GROUND"
|
|
)
|
|
)
|
|
capture_trigger.add_condition(
|
|
PartOfCoalitionInZone(
|
|
attacking_coalition, trigger_zone.id, unit_type="GROUND"
|
|
)
|
|
)
|
|
capture_trigger.add_condition(FlagIsFalse(flag=flag))
|
|
script_string = String(
|
|
f'base_capture_events[#base_capture_events + 1] = "{cp.id}||{attack_coalition_int}||{cp.full_name}"'
|
|
)
|
|
capture_trigger.add_action(DoScript(script_string))
|
|
capture_trigger.add_action(SetFlag(flag=flag))
|
|
self.mission.triggerrules.triggers.append(capture_trigger)
|
|
|
|
recapture_trigger = TriggerCondition(Event.NoEvent, "Capture Trigger")
|
|
recapture_trigger.add_condition(
|
|
AllOfCoalitionOutsideZone(
|
|
attacking_coalition, trigger_zone.id, unit_type="GROUND"
|
|
)
|
|
)
|
|
recapture_trigger.add_condition(
|
|
PartOfCoalitionInZone(
|
|
defending_coalition, trigger_zone.id, unit_type="GROUND"
|
|
)
|
|
)
|
|
recapture_trigger.add_condition(FlagIsTrue(flag=flag))
|
|
script_string = String(
|
|
f'base_capture_events[#base_capture_events + 1] = "{cp.id}||{defend_coalition_int}||{cp.full_name}"'
|
|
)
|
|
recapture_trigger.add_action(DoScript(script_string))
|
|
recapture_trigger.add_action(ClearFlag(flag=flag))
|
|
self.mission.triggerrules.triggers.append(recapture_trigger)
|
|
|
|
if cp.captured.is_neutral:
|
|
red_capture_trigger = TriggerCondition(
|
|
Event.NoEvent, "Capture Trigger"
|
|
)
|
|
red_capture_trigger.add_condition(
|
|
AllOfCoalitionOutsideZone(
|
|
attacking_coalition, trigger_zone.id, unit_type="GROUND"
|
|
)
|
|
)
|
|
red_capture_trigger.add_condition(
|
|
PartOfCoalitionInZone(
|
|
defending_coalition, trigger_zone.id, unit_type="GROUND"
|
|
)
|
|
)
|
|
red_capture_trigger.add_condition(FlagIsFalse(flag=flag))
|
|
script_string = String(
|
|
f'base_capture_events[#base_capture_events + 1] = "{cp.id}||{defend_coalition_int}||{cp.full_name}"'
|
|
)
|
|
red_capture_trigger.add_action(DoScript(script_string))
|
|
red_capture_trigger.add_action(SetFlag(flag=flag))
|
|
self.mission.triggerrules.triggers.append(red_capture_trigger)
|
|
|
|
inverted_recapture_trigger = TriggerCondition(
|
|
Event.NoEvent, "Capture Trigger"
|
|
)
|
|
inverted_recapture_trigger.add_condition(
|
|
AllOfCoalitionOutsideZone(
|
|
defending_coalition, trigger_zone.id, unit_type="GROUND"
|
|
)
|
|
)
|
|
inverted_recapture_trigger.add_condition(
|
|
PartOfCoalitionInZone(
|
|
attacking_coalition, trigger_zone.id, unit_type="GROUND"
|
|
)
|
|
)
|
|
inverted_recapture_trigger.add_condition(FlagIsTrue(flag=flag))
|
|
script_string = String(
|
|
f'base_capture_events[#base_capture_events + 1] = "{cp.id}||{attack_coalition_int}||{cp.full_name}"'
|
|
)
|
|
inverted_recapture_trigger.add_action(DoScript(script_string))
|
|
inverted_recapture_trigger.add_action(ClearFlag(flag=flag))
|
|
self.mission.triggerrules.triggers.append(
|
|
inverted_recapture_trigger
|
|
)
|
|
|
|
def generate(self) -> None:
|
|
player_coalition = "blue"
|
|
enemy_coalition = "red"
|
|
|
|
self._set_skill(player_coalition, enemy_coalition)
|
|
self._set_allegiances(player_coalition, enemy_coalition)
|
|
self._gen_markers()
|
|
self._generate_capture_triggers(player_coalition, enemy_coalition)
|
|
if self.game.settings.ground_start_scenery_remove_triggers:
|
|
try:
|
|
self._generate_clear_statics_trigger(self.game.scenery_clear_zones)
|
|
self.game.scenery_clear_zones.clear()
|
|
except AttributeError:
|
|
logging.info(f"Unable to create Clear Statics triggers")
|
|
|
|
@classmethod
|
|
def get_capture_zone_flag(cls) -> int:
|
|
flag = cls.capture_zone_flag
|
|
cls.capture_zone_flag += 1
|
|
return flag
|