mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
intercept mission
This commit is contained in:
parent
a61f487196
commit
781ddce09d
6
.idea/dcs_pmcliberation.iml
generated
6
.idea/dcs_pmcliberation.iml
generated
@ -1,8 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.6" jdkType="Python SDK" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.6 (venv)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="TestRunnerService">
|
||||
|
||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6 (venv)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
17
__init__.py
17
__init__.py
@ -35,7 +35,22 @@ theater.senaki.base.aa = {
|
||||
AirDefence.AAA_ZU_23_on_Ural_375: 2,
|
||||
}
|
||||
|
||||
op = game.mission.CaptureOperation.playerless(m, theater.kutaisi, theater.senaki)
|
||||
"""
|
||||
op = game.mission.CaptureOperation(m, m.country("USA"), m.country("Russia"), theater.senaki, theater.batumi, {A_10C: 2}, {F_15C: 2}, {Armor.MBT_M1A2_Abrams: 4}, {Su_27: 4}, {Armor.MBT_T_55: 4}, {})
|
||||
op.generate()
|
||||
"""
|
||||
|
||||
op = game.mission.InterceptOperation(m,
|
||||
m.country("USA"),
|
||||
m.country("Russia"),
|
||||
theater.batumi,
|
||||
m.terrain.batumi(),
|
||||
escort={Su_27: 2},
|
||||
transport={An_26B: 2},
|
||||
interceptors={M_2000C: 2})
|
||||
op.generate()
|
||||
|
||||
if not os.path.exists("./build"):
|
||||
os.mkdir("./build")
|
||||
|
||||
m.save("build/output.miz")
|
||||
|
||||
55
game/event.py
Normal file
55
game/event.py
Normal file
@ -0,0 +1,55 @@
|
||||
import typing
|
||||
|
||||
import dcs
|
||||
|
||||
from theater.controlpoint import *
|
||||
from .mission import *
|
||||
|
||||
class Event:
|
||||
silent = False
|
||||
operation = None # type: Operation
|
||||
|
||||
def failure(self):
|
||||
pass
|
||||
|
||||
def success(self):
|
||||
pass
|
||||
|
||||
class InterceptEvent(Event):
|
||||
pass
|
||||
|
||||
class CaptureEvent(Event):
|
||||
silent = True
|
||||
|
||||
def __init__(self, from_cp: ControlPoint, to_cp: ControlPoint):
|
||||
pass
|
||||
|
||||
def player_defending(self, from_cp: ControlPoint, to_cp: ControlPoint, interceptors: typing.Dict[PlaneType, int]):
|
||||
assert not self.operation
|
||||
|
||||
cas = from_cp.base.scramble_cas(to_cp)
|
||||
escort = from_cp.base.scramble_sweep(to_cp)
|
||||
attackers = from_cp.base.assemble_cap(to_cp)
|
||||
|
||||
self.operation = CaptureOperation(from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
cas=cas,
|
||||
escort=escort,
|
||||
attack=attackers,
|
||||
intercept=interceptors,
|
||||
defense=to_cp.base.armor,
|
||||
aa=to_cp.base.aa)
|
||||
|
||||
def player_attacking(self, from_cp: ControlPoint, to_cp: ControlPoint, cas: typing.Dict[PlaneType, int], escort: typing.Dict[PlaneType, int], armor: typing.Dict[Armor, int]):
|
||||
assert not self.operation
|
||||
|
||||
interceptors = to_cp.base.scramble_sweep()
|
||||
|
||||
self.operation = CaptureOperation(from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
cas=cas,
|
||||
escort=escort,
|
||||
attack=armor,
|
||||
intercept=interceptors,
|
||||
defense=to_cp.base.armor,
|
||||
aa=to_cp.base.aa)
|
||||
11
game/game.py
11
game/game.py
@ -2,9 +2,20 @@ import typing
|
||||
|
||||
from theater.conflicttheater import *
|
||||
from theater.controlpoint import *
|
||||
from .event import *
|
||||
|
||||
class Game:
|
||||
events = [] # type: typing.List[Event]
|
||||
|
||||
def __init__(self, theater: ConflictTheater):
|
||||
self.theater = theater
|
||||
|
||||
def _fill_cap_events(self):
|
||||
for cp in [x for x in self.theater.controlpoints if x.captured]:
|
||||
for connected_cp in [x for x in cp.connected_points if not x.captured]:
|
||||
self.events.append(CaptureEvent(cp, connected_cp))
|
||||
|
||||
def pass_turn(self):
|
||||
self.events = [] # type: typing.List[Event]
|
||||
self._fill_cap_events()
|
||||
|
||||
|
||||
@ -23,6 +23,8 @@ class Operation:
|
||||
class CaptureOperation(Operation):
|
||||
def __init__(self,
|
||||
mission: Mission,
|
||||
attacker: Country,
|
||||
defender: Country,
|
||||
from_cp: ControlPoint,
|
||||
to_cp: ControlPoint,
|
||||
cas: typing.Dict[PlaneType, int],
|
||||
@ -31,13 +33,7 @@ class CaptureOperation(Operation):
|
||||
intercept: typing.Dict[PlaneType, int],
|
||||
defense: typing.Dict[Armor, int],
|
||||
aa: typing.Dict[AirDefence, int]):
|
||||
conflict = None
|
||||
if from_cp.captured:
|
||||
assert not to_cp.captured
|
||||
conflict = to_cp.conflict_attack(from_cp, US, THEM)
|
||||
else:
|
||||
assert not from_cp.captured
|
||||
conflict = to_cp.conflict_attack(from_cp, THEM, US)
|
||||
conflict = to_cp.conflict_attack(from_cp, attacker, defender)
|
||||
|
||||
super(CaptureOperation, self).__init__(mission, conflict)
|
||||
self.from_cp = from_cp
|
||||
@ -51,49 +47,37 @@ class CaptureOperation(Operation):
|
||||
|
||||
self.aa = aa
|
||||
|
||||
@classmethod
|
||||
def player_defending(self, from_cp: ControlPoint, to_cp: ControlPoint, interceptors: typing.Dict[PlaneType, int]):
|
||||
cas = from_cp.base.scramble_cas(to_cp)
|
||||
escort = from_cp.base.scramble_sweep(to_cp)
|
||||
attackers = from_cp.base.assemble_cap(to_cp)
|
||||
|
||||
return CaptureOperation(from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
cas=cas,
|
||||
escort=escort,
|
||||
attack=attackers,
|
||||
intercept=interceptors,
|
||||
defense=to_cp.base.armor,
|
||||
aa=to_cp.base.aa)
|
||||
|
||||
@classmethod
|
||||
def player_attacking(self, from_cp: ControlPoint, to_cp: ControlPoint, cas: typing.Dict[PlaneType, int], escort: typing.Dict[PlaneType, int], armor: typing.Dict[Armor, int]):
|
||||
interceptors = to_cp.base.scramble_sweep()
|
||||
|
||||
return CaptureOperation(from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
cas=cas,
|
||||
escort=escort,
|
||||
attack=armor,
|
||||
intercept=interceptors,
|
||||
defense=to_cp.base.armor,
|
||||
aa=to_cp.base.aa)
|
||||
|
||||
@classmethod
|
||||
def playerless(self, mission: Mission, from_cp: ControlPoint, to_cp: ControlPoint):
|
||||
return CaptureOperation(mission=mission,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
cas=from_cp.base.scramble_cas(to_cp),
|
||||
escort=from_cp.base.scramble_sweep(to_cp),
|
||||
attack=from_cp.base.assemble_cap(to_cp),
|
||||
intercept=to_cp.base.scramble_interceptors(0.5),
|
||||
defense=to_cp.base.assemble_defense(0.5),
|
||||
aa=to_cp.base.aa)
|
||||
|
||||
def generate(self):
|
||||
self.armorgen.generate(self.attack, self.defense)
|
||||
self.airgen.generate_cas(self.cas)
|
||||
self.airgen.generate_escort(self.escort)
|
||||
self.airgen.generate_interceptors(self.intercept)
|
||||
self.airgen.generate_cas_escort(self.escort)
|
||||
self.airgen.generate_defense(self.intercept)
|
||||
self.aagen.generate(self.aa)
|
||||
|
||||
class InterceptOperation(Operation):
|
||||
def __init__(self,
|
||||
mission: Mission,
|
||||
attacker: Country,
|
||||
defender: Country,
|
||||
destination: ControlPoint,
|
||||
destination_port: Airport,
|
||||
escort: typing.Dict[PlaneType, int],
|
||||
transport: typing.Dict[PlaneType, int],
|
||||
interceptors: typing.Dict[PlaneType, int]):
|
||||
conflict = Conflict.intercept_conflict(
|
||||
attacker=attacker,
|
||||
defender=defender,
|
||||
position=destination.position,
|
||||
heading=0
|
||||
)
|
||||
|
||||
super(InterceptOperation, self).__init__(mission, conflict)
|
||||
self.destination_port = destination_port
|
||||
self.escort = escort
|
||||
self.transport = transport
|
||||
self.interceptors = interceptors
|
||||
|
||||
def generate(self):
|
||||
self.airgen.generate_transport(self.transport, self.destination_port)
|
||||
self.airgen.generate_transport_escort(self.escort)
|
||||
self.airgen.generate_interception(self.interceptors)
|
||||
|
||||
108
gen/aircraft.py
108
gen/aircraft.py
@ -19,9 +19,12 @@ from dcs.task import *
|
||||
|
||||
SPREAD_DISTANCE_FACTOR = 1, 2
|
||||
ESCORT_MAX_DIST = 30000
|
||||
WORKAROUND_WAYP_DIST = 1000
|
||||
|
||||
WARM_START_ALTITUDE = 6000
|
||||
WARM_START_AIRSPEED = 300
|
||||
|
||||
INTERCEPT_ALT = 15000
|
||||
CAS_ALTITUDE = 3000
|
||||
|
||||
INTERCEPT_MAX_DISTANCE_FACTOR = 15
|
||||
@ -49,6 +52,7 @@ class AircraftConflictGenerator:
|
||||
at: Point = None,
|
||||
airport: Airport = None) -> PlaneGroup:
|
||||
starttype = airport == None and StartType.Warm or StartType.Cold
|
||||
print("generating {} ({}) at {} {}".format(unit, count, at, airport, side))
|
||||
return self.m.flight_group(
|
||||
country=side,
|
||||
name=name,
|
||||
@ -61,52 +65,112 @@ class AircraftConflictGenerator:
|
||||
start_type=starttype,
|
||||
group_size=count)
|
||||
|
||||
def _generate_escort(self, units: typing.Dict[PlaneType, int], airport: Airport, side: Country, location: Point):
|
||||
assert len(self.escort_targets) > 0
|
||||
|
||||
for type, count in units.items():
|
||||
group = self._generate_group(
|
||||
name=namegen.next_escort_group_name(),
|
||||
side=side,
|
||||
unit=type,
|
||||
count=count,
|
||||
at=location,
|
||||
airport=airport)
|
||||
|
||||
group.task = Escort.name
|
||||
group.load_task_default_loadout(dcs.task.Escort)
|
||||
|
||||
heading = group.position.heading_between_point(self.conflict.position)
|
||||
position = group.position # type: Point
|
||||
wayp = group.add_waypoint(position.point_from_heading(heading, WORKAROUND_WAYP_DIST), CAS_ALTITUDE)
|
||||
|
||||
for group in self.escort_targets:
|
||||
wayp.tasks.append(EscortTaskAction(group.id, engagement_max_dist=ESCORT_MAX_DIST))
|
||||
|
||||
def generate_cas(self, attackers: typing.Dict[PlaneType, int], airport: Airport = None):
|
||||
assert len(self.escort_targets) == 0
|
||||
|
||||
for type, count in attackers.items():
|
||||
group = self._generate_group(
|
||||
name=namegen.next_cas_group_name(),
|
||||
side=self.conflict.attackers_side,
|
||||
unit=type,
|
||||
count=count,
|
||||
at=airport == None and self._group_point(self.conflict.air_attackers_location) or None,
|
||||
at=airport is None and self._group_point(self.conflict.air_attackers_location) or None,
|
||||
airport=airport)
|
||||
self.escort_targets.append(group)
|
||||
|
||||
group.add_waypoint(self.conflict.position, CAS_ALTITUDE)
|
||||
group.task = CAS.name
|
||||
group.load_task_default_loadout(CAS)
|
||||
|
||||
def generate_escort(self, attackers: typing.Dict[PlaneType, int], airport: Airport = None):
|
||||
for type, count in attackers.items():
|
||||
group = self._generate_group(
|
||||
name=namegen.next_escort_group_name(),
|
||||
side=self.conflict.attackers_side,
|
||||
unit=type,
|
||||
count=count,
|
||||
at=airport == None and self._group_point(self.conflict.air_attackers_location) or None,
|
||||
airport=airport)
|
||||
def generate_cas_escort(self, attackers: typing.Dict[PlaneType, int], airport: Airport = None):
|
||||
self._generate_escort(
|
||||
units=attackers,
|
||||
airport=airport,
|
||||
side=self.conflict.attackers_side,
|
||||
location=airport is None and self._group_point(self.conflict.air_attackers_location) or None
|
||||
)
|
||||
|
||||
group.task = Escort.name
|
||||
group.load_task_default_loadout(dcs.task.Escort.name)
|
||||
def generate_transport_escort(self, escort: typing.Dict[PlaneType, int], airport: Airport = None):
|
||||
self._generate_escort(
|
||||
units=escort,
|
||||
airport=airport,
|
||||
side=self.conflict.defenders_side,
|
||||
location=airport is None and self._group_point(self.conflict.air_defenders_location) or None
|
||||
)
|
||||
|
||||
heading = group.position.heading_between_point(self.conflict.position)
|
||||
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):
|
||||
def generate_defense(self, defenders: typing.Dict[PlaneType, int], airport: Airport = None):
|
||||
for type, count in defenders.items():
|
||||
group = self._generate_group(
|
||||
name=namegen.next_intercept_group_name(),
|
||||
side=self.conflict.defenders_side,
|
||||
unit=type,
|
||||
count=count,
|
||||
at=airport == None and self._group_point(self.conflict.air_defenders_location) or None,
|
||||
at=airport is None and self._group_point(self.conflict.air_defenders_location) or None,
|
||||
airport=airport)
|
||||
|
||||
group.task = FighterSweep.name
|
||||
group.load_task_default_loadout(dcs.task.FighterSweep())
|
||||
group.load_task_default_loadout(FighterSweep)
|
||||
wayp = group.add_waypoint(self.conflict.position, CAS_ALTITUDE)
|
||||
wayp.tasks.append(dcs.task.EngageTargets(max_distance=self.conflict.size * INTERCEPT_MAX_DISTANCE_FACTOR))
|
||||
wayp.tasks.append(dcs.task.OrbitAction())
|
||||
|
||||
def generate_transport(self, transport: typing.Dict[PlaneType, int], destination: Airport):
|
||||
assert len(self.escort_targets) == 0
|
||||
|
||||
for type, count in transport.items():
|
||||
group = self._generate_group(
|
||||
name=namegen.next_transport_group_name(),
|
||||
side=self.conflict.defenders_side,
|
||||
unit=type,
|
||||
count=count,
|
||||
at=self._group_point(self.conflict.air_defenders_location),
|
||||
airport=None
|
||||
)
|
||||
|
||||
group.task = Transport.name
|
||||
|
||||
self.escort_targets.append(group)
|
||||
group.land_at(destination)
|
||||
|
||||
def generate_interception(self, interceptors: typing.Dict[PlaneType, int], airport: Airport = None):
|
||||
for type, count in interceptors.items():
|
||||
group = self._generate_group(
|
||||
name=namegen.next_intercept_group_name(),
|
||||
side=self.conflict.attackers_side,
|
||||
unit=type,
|
||||
count=count,
|
||||
at=airport is None and self._group_point(self.conflict.air_attackers_location) or None,
|
||||
airport=airport
|
||||
)
|
||||
|
||||
group.task = FighterSweep.name
|
||||
group.load_task_default_loadout(FighterSweep)
|
||||
|
||||
heading = group.position.heading_between_point(self.conflict.position)
|
||||
initial_wayp = group.add_waypoint(group.position.point_from_heading(heading, WORKAROUND_WAYP_DIST), INTERCEPT_ALT)
|
||||
initial_wayp.tasks.append(EngageTargets())
|
||||
|
||||
wayp = group.add_waypoint(self.conflict.position, 0)
|
||||
wayp.tasks.append(EngageTargets())
|
||||
|
||||
@ -2,6 +2,7 @@ import typing
|
||||
import pdb
|
||||
import dcs
|
||||
|
||||
from random import randint
|
||||
from dcs import Mission
|
||||
|
||||
from dcs.mission import *
|
||||
@ -17,18 +18,42 @@ def _opposite_heading(h):
|
||||
return h+180
|
||||
|
||||
GROUND_DISTANCE_FACTOR = 2
|
||||
AIR_DISTANCE_FACTOR = 5
|
||||
AIR_DISTANCE = 8000
|
||||
|
||||
INTERCEPT_ATTACKERS_HEADING = -45, 45
|
||||
INTERCEPT_DEFENDERS_HEADING = -10, 10
|
||||
INTERCEPT_ATTACKERS_DISTANCE = 60000
|
||||
INTERCEPT_DEFENDERS_DISTANCE = 30000
|
||||
|
||||
class Conflict:
|
||||
def __init__(self, attacker: Country, attack_heading: int, defender: Country, defense_heading: int, position: Point, size: int):
|
||||
self.attackers_side = attacker
|
||||
self.defenders_side = defender
|
||||
self.position = position
|
||||
self.size = size
|
||||
@classmethod
|
||||
def capture_conflict(self, attacker: Country, attack_heading: int, defender: Country, defense_heading: int, position: Point, size: int):
|
||||
instance = self()
|
||||
instance.attackers_side = attacker
|
||||
instance.defenders_side = defender
|
||||
instance.position = position
|
||||
instance.size = size
|
||||
|
||||
self.ground_attackers_location = self.position.point_from_heading(attack_heading, self.size * GROUND_DISTANCE_FACTOR)
|
||||
self.ground_defenders_location = self.position.point_from_heading(defense_heading, self.size * GROUND_DISTANCE_FACTOR)
|
||||
instance.ground_attackers_location = instance.position.point_from_heading(attack_heading, instance.size * GROUND_DISTANCE_FACTOR)
|
||||
instance.ground_defenders_location = instance.position.point_from_heading(defense_heading, instance.size * GROUND_DISTANCE_FACTOR)
|
||||
|
||||
self.air_attackers_location = self.position.point_from_heading(attack_heading, self.size * AIR_DISTANCE_FACTOR)
|
||||
self.air_defenders_location = self.position.point_from_heading(defense_heading, self.size * AIR_DISTANCE_FACTOR)
|
||||
instance.air_attackers_location = instance.position.point_from_heading(attack_heading, AIR_DISTANCE)
|
||||
instance.air_defenders_location = instance.position.point_from_heading(defense_heading, AIR_DISTANCE)
|
||||
|
||||
return instance
|
||||
|
||||
@classmethod
|
||||
def intercept_conflict(self, attacker: Country, defender: Country, position: Point, heading: int):
|
||||
from theater.conflicttheater import SIZE_REGULAR
|
||||
|
||||
instance = self()
|
||||
instance.attackers_side = attacker
|
||||
instance.defenders_side = defender
|
||||
|
||||
instance.position = position
|
||||
instance.size = SIZE_REGULAR
|
||||
|
||||
instance.air_attackers_location = instance.position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + heading, INTERCEPT_ATTACKERS_DISTANCE)
|
||||
instance.air_defenders_location = instance.position.point_from_heading(random.randint(*INTERCEPT_DEFENDERS_HEADING) + heading, INTERCEPT_DEFENDERS_DISTANCE)
|
||||
|
||||
return instance
|
||||
|
||||
@ -21,6 +21,10 @@ class NameGenerator:
|
||||
self.number += 1
|
||||
return "AA Unit {}".format(self.number)
|
||||
|
||||
def next_transport_group_name(self):
|
||||
self.number += 1
|
||||
return "Transport Unit {}".format(self.number)
|
||||
|
||||
|
||||
namegen = NameGenerator()
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import math
|
||||
from dcs.mapping import *
|
||||
from dcs.country import *
|
||||
|
||||
from gen.conflictgen import Conflict
|
||||
from gen.conflictgen import *
|
||||
|
||||
class ControlPoint:
|
||||
connected_points = [] # type: typing.List[ControlPoint]
|
||||
@ -27,12 +27,12 @@ class ControlPoint:
|
||||
def connect(self, to):
|
||||
self.connected_points.append(to)
|
||||
|
||||
def find_radial(self, heading: int):
|
||||
def find_radial(self, heading: int, ignored_radial: int = None):
|
||||
closest_radial = 0
|
||||
closest_radial_delta = 360
|
||||
for radial in self.radials:
|
||||
for radial in [x for x in self.radials if x != ignored_radial]:
|
||||
delta = math.fabs(radial - heading)
|
||||
if closest_radial_delta < delta:
|
||||
if delta < closest_radial_delta:
|
||||
closest_radial = radial
|
||||
closest_radial_delta = delta
|
||||
|
||||
@ -42,13 +42,13 @@ class ControlPoint:
|
||||
cp = from_cp # type: ControlPoint
|
||||
|
||||
attack_radial = self.find_radial(cp.position.heading_between_point(self.position))
|
||||
defense_radial = self.find_radial(self.position.heading_between_point(cp.position))
|
||||
defense_radial = self.find_radial(self.position.heading_between_point(cp.position), ignored_radial=attack_radial)
|
||||
|
||||
return Conflict(attacker=attacker,
|
||||
attack_heading=attack_radial,
|
||||
defender=defender,
|
||||
defense_heading=defense_radial,
|
||||
position=self.position,
|
||||
size=self.size)
|
||||
return Conflict.capture_conflict(attacker=attacker,
|
||||
attack_heading=attack_radial,
|
||||
defender=defender,
|
||||
defense_heading=defense_radial,
|
||||
position=self.position,
|
||||
size=self.size)
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user