From f4a3aef2d5956466c18eec5e6f12c0e07d9d4d0e Mon Sep 17 00:00:00 2001 From: Vasiliy Horbachenko Date: Thu, 24 May 2018 11:53:40 +0300 Subject: [PATCH] base mission generation framework --- .gitignore | 2 ++ __init__.py | 20 ++++++++++++++---- game/game.py | 11 ++++++++++ game/side.py | 3 +++ gen/aaa.py | 40 ++++++++++++++++++++++++++++++++++++ gen/aircraft.py | 26 +++++++++++++++-------- gen/armor.py | 6 +++--- gen/conflictgen.py | 20 +++++++++++------- gen/naming.py | 4 ++++ map/controlpoint.py | 5 ----- {map => theater}/__init__.py | 0 theater/base.py | 11 ++++++++++ theater/caucasus.py | 14 +++++++++++++ theater/conflicttheater.py | 16 +++++++++++++++ theater/controlpoint.py | 30 +++++++++++++++++++++++++++ 15 files changed, 180 insertions(+), 28 deletions(-) create mode 100644 game/game.py create mode 100644 game/side.py delete mode 100644 map/controlpoint.py rename {map => theater}/__init__.py (100%) create mode 100644 theater/base.py create mode 100644 theater/caucasus.py create mode 100644 theater/conflicttheater.py create mode 100644 theater/controlpoint.py diff --git a/.gitignore b/.gitignore index a295864e..27cfdac2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *.pyc __pycache__ +output.miz +.DS_Store diff --git a/__init__.py b/__init__.py index 2f3da4e8..f04cf3c1 100755 --- a/__init__.py +++ b/__init__.py @@ -4,22 +4,34 @@ import dcs import os import gen +import theater.caucasus m = dcs.Mission() +theater = theater.caucasus.CaucasusTheater() +conflict = theater.controlpoints[0].conflict_attack(theater.controlpoints[1], m.country("USA"), m.country("Russia")) + + +""" conflict = gen.Conflict( heading=100, attacker=m.country("USA"), defender=m.country("Russia"), point=m.terrain.krymsk().position, size=10000) + """ armor_conflict = gen.ArmorConflictGenerator(m, conflict) armor_conflict.generate( attackers={dcs.vehicles.Armor.MBT_M1A2_Abrams: 8}, - defenders={dcs.vehicles.Armor.MBT_T_80U: 6}) + defenders={dcs.vehicles.Armor.MBT_T_80U: 10}) aircraft_conflict = gen.AircraftConflictGenerator(m, conflict) -aircraft_conflict.generate_cas({dcs.planes.A_10C: 2}) -aircraft_conflict.generate_escort({dcs.planes.F_15C: 2}) -aircraft_conflict.generate_interceptors({dcs.planes.Su_27: 2}) +aircraft_conflict.generate_cas({dcs.planes.A_10C: 4}) +aircraft_conflict.generate_escort({dcs.planes.F_15C: 4}) +aircraft_conflict.generate_interceptors({dcs.planes.Su_27: 6}) + +aa_conflict = gen.AAConflictGenerator(m, conflict) +aa_conflict.generate({dcs.vehicles.AirDefence.AAA_ZU_23_on_Ural_375: 3}) + +m.save("output.miz") diff --git a/game/game.py b/game/game.py new file mode 100644 index 00000000..f841abf6 --- /dev/null +++ b/game/game.py @@ -0,0 +1,11 @@ +import typing + +from theater.conflicttheater import * + +class Game: + def __init__(self, theater: ConflictTheater): + self.theater = theater + + def pass_time(self, time: int): + pass + diff --git a/game/side.py b/game/side.py new file mode 100644 index 00000000..a374112d --- /dev/null +++ b/game/side.py @@ -0,0 +1,3 @@ +import typing +import dcs + diff --git a/gen/aaa.py b/gen/aaa.py index e69de29b..c35c27d5 100644 --- a/gen/aaa.py +++ b/gen/aaa.py @@ -0,0 +1,40 @@ +import typing +import pdb +import dcs + +from random import randint + +import globals + +from .conflictgen import * +from .naming import * + +from dcs.mission import * +from dcs.vehicles import * +from dcs.unitgroup import * +from dcs.unittype import * +from dcs.mapping import * +from dcs.point import * +from dcs.task import * + +DISTANCE_FACTOR = 4, 5 + +class AAConflictGenerator: + def __init__(self, mission: Mission, conflict: Conflict): + self.m = mission + self.conflict = conflict + + def generate(self, units: typing.Dict[UnitType, int]): + for type, count in units.items(): + for _ in range(count): + p = self.conflict.ground_defenders_location.random_point_within( + self.conflict.size * DISTANCE_FACTOR[1], + self.conflict.size * DISTANCE_FACTOR[0]) + + self.m.vehicle_group( + country=self.conflict.defenders_side, + name=namegen.next_ground_group_name(), + _type=type, + position=p, + group_size=1) + diff --git a/gen/aircraft.py b/gen/aircraft.py index bc670d4c..7685daca 100644 --- a/gen/aircraft.py +++ b/gen/aircraft.py @@ -17,14 +17,15 @@ from dcs.mapping import * from dcs.point import * from dcs.task import * -SPREAD_DISTANCE_FACTOR = 0.1, 0.25 -SPREAD_DISTANCE_SIZE_FACTOR = 0.5 +SPREAD_DISTANCE_FACTOR = 1, 2 ESCORT_MAX_DIST = 30000 WARM_START_ALTITUDE = 6000 WARM_START_AIRSPEED = 300 CAS_ALTITUDE = 3000 +INTERCEPT_MAX_DISTANCE_FACTOR = 15 + class AircraftConflictGenerator: escort_targets = [] # type: typing.List[PlaneGroup] @@ -37,8 +38,7 @@ class AircraftConflictGenerator: int(self.conflict.size * SPREAD_DISTANCE_FACTOR[0]), int(self.conflict.size * SPREAD_DISTANCE_FACTOR[1]), ) - - return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_SIZE_FACTOR) + return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_FACTOR[0]) def _generate_group( self, @@ -48,7 +48,7 @@ class AircraftConflictGenerator: count: int, at: Point = None, airport: Airport = None) -> PlaneGroup: - starttype = airport == None and StartType.Warm or StartType.Runway + starttype = airport == None and StartType.Warm or StartType.Cold return self.m.flight_group( country=side, name=name, @@ -86,9 +86,14 @@ class AircraftConflictGenerator: airport=airport) group.task = Escort.name - for group in self.escort_targets: - group.tasks.append(EscortTaskAction(group.id, engagement_max_dist=ESCORT_MAX_DIST)) + group.load_task_default_loadout(dcs.task.Escort.name) + heading = group.position.heading_between_point(self.conflict.point) + position = group.position # type: Point + wayp = group.add_waypoint(position.point_from_heading(heading, 3000), CAS_ALTITUDE) + + for group in self.escort_targets: + wayp.tasks.append(EscortTaskAction(group.id, engagement_max_dist=ESCORT_MAX_DIST)) def generate_interceptors(self, defenders: typing.Dict[PlaneType, int], airport: Airport = None): for type, count in defenders.items(): @@ -100,5 +105,8 @@ class AircraftConflictGenerator: at=airport == None and self._group_point(self.conflict.air_defenders_location) or None, airport=airport) - group.add_waypoint(self.conflict.point, CAS_ALTITUDE) - group.task = FighterSweep() + group.task = FighterSweep.name + group.load_task_default_loadout(dcs.task.FighterSweep()) + wayp = group.add_waypoint(self.conflict.point, CAS_ALTITUDE) + wayp.tasks.append(dcs.task.EngageTargets(max_distance=self.conflict.size * INTERCEPT_MAX_DISTANCE_FACTOR)) + wayp.tasks.append(dcs.task.OrbitAction()) diff --git a/gen/armor.py b/gen/armor.py index dc5f8140..41784fd1 100644 --- a/gen/armor.py +++ b/gen/armor.py @@ -18,8 +18,8 @@ from dcs.point import * from dcs.task import * from dcs.country import * -SPREAD_DISTANCE_FACTOR = 0.01, 0.1 -SPREAD_DISTANCE_SIZE_FACTOR = 0.5 +SPREAD_DISTANCE_FACTOR = 0.1, 0.3 +SPREAD_DISTANCE_SIZE_FACTOR = 0.1 class ArmorConflictGenerator: def __init__(self, mission: Mission, conflict: Conflict): @@ -42,7 +42,7 @@ class ArmorConflictGenerator: unit, position=self._group_point(at), group_size=1, - move_formation=PointAction.OnRoad) + move_formation=PointAction.OffRoad) wayp = group.add_waypoint(self.conflict.point) wayp.tasks = [] diff --git a/gen/conflictgen.py b/gen/conflictgen.py index 035f8c16..c3dbc9b8 100644 --- a/gen/conflictgen.py +++ b/gen/conflictgen.py @@ -2,6 +2,8 @@ import typing import pdb import dcs +from dcs import Mission + from dcs.mission import * from dcs.vehicles import * from dcs.unitgroup import * @@ -14,18 +16,22 @@ from dcs.country import * def _opposite_heading(h): return h+180 -GROUND_DISTANCE_FACTOR = 1 -AIR_DISTANCE_FACTOR = 4 +GROUND_DISTANCE_FACTOR = 2 +AIR_DISTANCE_FACTOR = 5 class Conflict: - def __init__(self, heading: int, attacker: Country, defender: Country, point: Point, size: int): + trigger_zone = None # type: TriggerZone + activation_trigger = None # type: Trigger + + def __init__(self, attacker: Country, attack_heading: int, defender: Country, defense_heading: int, point: Point, size: int): self.attackers_side = attacker self.defenders_side = defender self.point = point self.size = size - self.ground_attackers_location = self.point.point_from_heading(heading, self.size * GROUND_DISTANCE_FACTOR) - self.ground_defenders_location = self.point.point_from_heading(_opposite_heading(heading), self.size * GROUND_DISTANCE_FACTOR) + self.ground_attackers_location = self.point.point_from_heading(attack_heading, self.size * GROUND_DISTANCE_FACTOR) + self.ground_defenders_location = self.point.point_from_heading(defense_heading, self.size * GROUND_DISTANCE_FACTOR) + + self.air_attackers_location = self.point.point_from_heading(attack_heading, self.size * AIR_DISTANCE_FACTOR) + self.air_defenders_location = self.point.point_from_heading(defense_heading, self.size * AIR_DISTANCE_FACTOR) - self.air_attackers_location = self.point.point_from_heading(heading, self.size * AIR_DISTANCE_FACTOR) - self.air_defenders_location = self.point.point_from_heading(_opposite_heading(heading), self.size * AIR_DISTANCE_FACTOR) diff --git a/gen/naming.py b/gen/naming.py index e8675c13..f80d363d 100644 --- a/gen/naming.py +++ b/gen/naming.py @@ -16,6 +16,10 @@ class NameGenerator: def next_intercept_group_name(self): self.number += 1 return "Intercept Unit {}".format(self.number) + + def next_ground_group_name(self): + self.number += 1 + return "AA Unit {}".format(self.number) namegen = NameGenerator() diff --git a/map/controlpoint.py b/map/controlpoint.py deleted file mode 100644 index 54c42958..00000000 --- a/map/controlpoint.py +++ /dev/null @@ -1,5 +0,0 @@ -import dcs - -class ControlPoint: - def __init__(self): - pass diff --git a/map/__init__.py b/theater/__init__.py similarity index 100% rename from map/__init__.py rename to theater/__init__.py diff --git a/theater/base.py b/theater/base.py new file mode 100644 index 00000000..a8ef67d9 --- /dev/null +++ b/theater/base.py @@ -0,0 +1,11 @@ +import typing +import dcs + +from .controlpoint import * + +from dcs.planes import * +from dcs.vehicles import * + +class Base: + aircraft = [] # type: typing.Dict[PlaneType, int] + armor = [] # type: typing.Dict[Armor, int] diff --git a/theater/caucasus.py b/theater/caucasus.py new file mode 100644 index 00000000..01b8ecfc --- /dev/null +++ b/theater/caucasus.py @@ -0,0 +1,14 @@ +from dcs.terrain import caucasus + +from .conflicttheater import * +from .base import * + +class CaucasusTheater(ConflictTheater): + sukhumi = ControlPoint(caucasus.Sukhumi().position, 1000, 5, True, Base()) + krymsk = ControlPoint(caucasus.Krymsk().position, 1500, 10, False, None) + kransnodar = ControlPoint(caucasus.KrasnodarCenter().position, 3000, 30, False, Base()) + + def __init__(self): + self.add_controlpoint(self.sukhumi, [self.krymsk]) + self.add_controlpoint(self.krymsk, [self.sukhumi, self.kransnodar]) + self.add_controlpoint(self.kransnodar, [self.krymsk]) diff --git a/theater/conflicttheater.py b/theater/conflicttheater.py new file mode 100644 index 00000000..293b0ac7 --- /dev/null +++ b/theater/conflicttheater.py @@ -0,0 +1,16 @@ +import typing +import dcs + +from .controlpoint import * + +class ConflictTheater: + controlpoints = [] # type: typing.Collection[ControlPoint] + + def add_controlpoint(self, point: ControlPoint, connected_to: typing.Collection[ControlPoint]): + for connected_point in connected_to: + point.connect(to=connected_point) + + self.controlpoints.append(point) + + def player_bases(self) -> typing.Collection[ControlPoint]: + return [point for point in self.controlpoints if point.captured and point.base] diff --git a/theater/controlpoint.py b/theater/controlpoint.py new file mode 100644 index 00000000..11a93068 --- /dev/null +++ b/theater/controlpoint.py @@ -0,0 +1,30 @@ +import typing +import dcs + +from dcs.mapping import * +from dcs.country import * + +from gen.conflictgen import Conflict +from .base import * + +class ControlPoint: + connected_points = [] # type: typing.Collection[ControlPoint] + point = None # type: Point + captured = False + base = None # type: Base + + def __init__(self, point: Point, size: int, importance: int, captured: bool, base: Base): + self.point = point + self.size = size + self.importance = importance + self.captured = captured + self.base = base + + def connect(self, to): + self.connected_points.append(to) + + def conflict_attack(self, x, attacker: Country, defender: Country) -> Conflict: + #heading = heading_between_points(self.point.x, self.point.y, x.point.x, x.point.y) + return Conflict(attacker, 0, defender, 90, self.point, self.size) + +