diff --git a/.gitignore b/.gitignore
index 27cfdac2..fdbab2df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
*.pyc
__pycache__
-output.miz
+build/*
.DS_Store
diff --git a/.idea/dcs_pmcliberation.iml b/.idea/dcs_pmcliberation.iml
new file mode 100644
index 00000000..c8efcbb4
--- /dev/null
+++ b/.idea/dcs_pmcliberation.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 00000000..146ab09b
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 00000000..65531ca9
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 00000000..c32a91ea
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..94a25f7f
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 00000000..6e390244
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,534 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1527543981624
+
+
+ 1527543981624
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ file://$PROJECT_DIR$/game/mission.py
+ 41
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/__init__.py b/__init__.py
index f04cf3c1..97b25218 100755
--- a/__init__.py
+++ b/__init__.py
@@ -5,33 +5,37 @@ import os
import gen
import theater.caucasus
+import game.mission
+
+from dcs.planes import *
+from dcs.vehicles import *
m = dcs.Mission()
theater = theater.caucasus.CaucasusTheater()
-conflict = theater.controlpoints[0].conflict_attack(theater.controlpoints[1], m.country("USA"), m.country("Russia"))
+theater.kutaisi.base.aircraft = {
+ A_10C: 4,
+ F_15C: 4,
+}
-"""
-conflict = gen.Conflict(
- heading=100,
- attacker=m.country("USA"),
- defender=m.country("Russia"),
- point=m.terrain.krymsk().position,
- size=10000)
- """
+theater.kutaisi.base.armor = {
+ Armor.MBT_M1A2_Abrams: 4,
+}
-armor_conflict = gen.ArmorConflictGenerator(m, conflict)
-armor_conflict.generate(
- attackers={dcs.vehicles.Armor.MBT_M1A2_Abrams: 8},
- defenders={dcs.vehicles.Armor.MBT_T_80U: 10})
+theater.senaki.base.aircraft = {
+ MiG_21Bis: 8,
+}
-aircraft_conflict = gen.AircraftConflictGenerator(m, conflict)
-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})
+theater.senaki.base.armor = {
+ Armor.MBT_T_55: 6,
+}
-aa_conflict = gen.AAConflictGenerator(m, conflict)
-aa_conflict.generate({dcs.vehicles.AirDefence.AAA_ZU_23_on_Ural_375: 3})
+theater.senaki.base.aa = {
+ AirDefence.AAA_ZU_23_on_Ural_375: 2,
+}
-m.save("output.miz")
+op = game.mission.CaptureOperation.playerless(m, theater.kutaisi, theater.senaki)
+op.generate()
+
+m.save("build/output.miz")
diff --git a/game/game.py b/game/game.py
index f841abf6..dc43daed 100644
--- a/game/game.py
+++ b/game/game.py
@@ -1,11 +1,10 @@
import typing
from theater.conflicttheater import *
+from theater.controlpoint import *
class Game:
def __init__(self, theater: ConflictTheater):
self.theater = theater
- def pass_time(self, time: int):
- pass
diff --git a/game/mission.py b/game/mission.py
new file mode 100644
index 00000000..52d5fe70
--- /dev/null
+++ b/game/mission.py
@@ -0,0 +1,99 @@
+import typing
+
+from globals import *
+from dcs.mission import *
+from dcs.unitgroup import *
+from dcs.vehicles import *
+from theater.controlpoint import *
+from theater.base import *
+
+from gen.armor import *
+from gen.aircraft import *
+from gen.aaa import *
+from gen.conflictgen import *
+
+class Operation:
+ def __init__(self, mission: Mission, conflict: Conflict):
+ self.mission = mission
+ self.conflict = conflict
+ self.armorgen = ArmorConflictGenerator(self.mission, self.conflict)
+ self.airgen = AircraftConflictGenerator(self.mission, self.conflict)
+ self.aagen = AAConflictGenerator(self.mission, self.conflict)
+
+class CaptureOperation(Operation):
+ def __init__(self,
+ mission: Mission,
+ from_cp: ControlPoint,
+ to_cp: ControlPoint,
+ cas: typing.Dict[PlaneType, int],
+ escort: typing.Dict[PlaneType, int],
+ attack: typing.Dict[Armor, int],
+ 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)
+
+ super(CaptureOperation, self).__init__(mission, conflict)
+ self.from_cp = from_cp
+ self.to_cp = to_cp
+ self.cas = cas
+ self.escort = escort
+ self.intercept = intercept
+
+ self.attack = attack
+ self.defense = defense
+
+ 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.aagen.generate(self.aa)
diff --git a/game/side.py b/game/side.py
deleted file mode 100644
index a374112d..00000000
--- a/game/side.py
+++ /dev/null
@@ -1,3 +0,0 @@
-import typing
-import dcs
-
diff --git a/gen/aircraft.py b/gen/aircraft.py
index 7685daca..f5e4181e 100644
--- a/gen/aircraft.py
+++ b/gen/aircraft.py
@@ -72,7 +72,7 @@ class AircraftConflictGenerator:
airport=airport)
self.escort_targets.append(group)
- group.add_waypoint(self.conflict.point, CAS_ALTITUDE)
+ group.add_waypoint(self.conflict.position, CAS_ALTITUDE)
group.task = CAS.name
def generate_escort(self, attackers: typing.Dict[PlaneType, int], airport: Airport = None):
@@ -88,7 +88,7 @@ class AircraftConflictGenerator:
group.task = Escort.name
group.load_task_default_loadout(dcs.task.Escort.name)
- heading = group.position.heading_between_point(self.conflict.point)
+ 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)
@@ -107,6 +107,6 @@ class AircraftConflictGenerator:
group.task = FighterSweep.name
group.load_task_default_loadout(dcs.task.FighterSweep())
- wayp = group.add_waypoint(self.conflict.point, CAS_ALTITUDE)
+ 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())
diff --git a/gen/armor.py b/gen/armor.py
index 41784fd1..c6789cd6 100644
--- a/gen/armor.py
+++ b/gen/armor.py
@@ -43,7 +43,7 @@ class ArmorConflictGenerator:
position=self._group_point(at),
group_size=1,
move_formation=PointAction.OffRoad)
- wayp = group.add_waypoint(self.conflict.point)
+ wayp = group.add_waypoint(self.conflict.position)
wayp.tasks = []
def generate(self, attackers: typing.Dict[UnitType, int], defenders: typing.Dict[UnitType, int]):
diff --git a/gen/conflictgen.py b/gen/conflictgen.py
index c3dbc9b8..e9cac6f9 100644
--- a/gen/conflictgen.py
+++ b/gen/conflictgen.py
@@ -20,18 +20,15 @@ GROUND_DISTANCE_FACTOR = 2
AIR_DISTANCE_FACTOR = 5
class Conflict:
- 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):
+ 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.point = point
+ self.position = position
self.size = size
- 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.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)
- 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.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)
diff --git a/globals.py b/globals.py
index b3dc6753..540a4a5a 100644
--- a/globals.py
+++ b/globals.py
@@ -1,4 +1,5 @@
import dcs
-mission = dcs.mission.Mission()
-country = mission.country("USA")
+MISSION = dcs.mission.Mission()
+US = MISSION.country("USA")
+THEM = MISSION.country("Russia")
diff --git a/shop/db.py b/shop/db.py
new file mode 100644
index 00000000..fc2771a5
--- /dev/null
+++ b/shop/db.py
@@ -0,0 +1,44 @@
+import typing
+import dcs
+
+import globals
+
+from dcs.vehicles import *
+from dcs.unitgroup import *
+from dcs.planes import *
+from dcs.task import *
+from dcs.unittype import *
+
+PRICES = {
+ # planes
+
+ Su_25T: 10,
+ A_10A: 15,
+ A_10C: 20,
+
+ Su_27: 20,
+ Su_33: 23,
+ F_15C: 25,
+ M_2000C: 17,
+
+ MiG_15bis: 10,
+ MiG_21Bis: 13,
+
+ # armor
+
+ Armor.MBT_T_55: 18,
+ Armor.MBT_T_80U: 20,
+ Armor.MBT_T_90: 22,
+
+ Armor.MBT_M60A3_Patton: 15,
+ Armor.MBT_M1A2_Abrams: 20,
+
+ Armor.ATGM_M1134_Stryker: 12,
+ Armor.APC_BTR_80: 10,
+}
+
+UNIT_BY_TASK = {
+ FighterSweep: [Su_27, Su_33, Su_25, F_15C, MiG_15bis, MiG_21Bis, MiG_29A, ],
+ CAS: [Su_25T, A_10A, A_10C, ],
+ CAP: [Armor.MBT_T_90, Armor.MBT_T_80U, Armor.MBT_T_55, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, Armor.APC_BTR_80, ]
+}
diff --git a/theater/base.py b/theater/base.py
index a8ef67d9..cfb2cba9 100644
--- a/theater/base.py
+++ b/theater/base.py
@@ -1,11 +1,88 @@
import typing
import dcs
+import math
-from .controlpoint import *
+from shop import db
+from theater.controlpoint import ControlPoint
from dcs.planes import *
from dcs.vehicles import *
+from dcs.task import *
+
+PLANES_IN_GROUP = 2
+PLANES_IMPORTANCE_FACTOR = 2
+ARMOR_IMPORTANCE_FACTOR = 4
+
class Base:
- aircraft = [] # type: typing.Dict[PlaneType, int]
- armor = [] # type: typing.Dict[Armor, int]
+ aircraft = {} # type: typing.Dict[PlaneType, int]
+ armor = {} # type: typing.Dict[Armor, int]
+ aa = {} # type: typing.Dict[AirDefence, int]
+
+ def __init__(self):
+ pass
+
+ @property
+ def total_planes(self) -> int:
+ return sum(self.aircraft.values())
+
+ @property
+ def total_armor(self) -> int:
+ return sum(self.armor.values())
+
+ def _find_best_unit(self, dict, for_type: Task, count: int) -> typing.Dict:
+ sorted_planes = [key for key in dict.keys() if key in db.UNIT_BY_TASK[for_type]]
+ sorted_planes.sort(key=lambda x: db.PRICES[x], reverse=True)
+
+ result = {}
+ for plane in sorted_planes:
+ existing_count = dict[plane] # type: int
+ if not existing_count:
+ continue
+
+ result_unit_count = min(count, existing_count)
+ count -= result_unit_count
+ result[plane] = result.get(plane, 0) + result_unit_count
+
+ return result
+
+ def _find_best_planes(self, for_type: Task, count: int) -> typing.Dict[PlaneType, int]:
+ return self._find_best_unit(self.aircraft, for_type, count)
+
+ def _find_best_armor(self, for_type: Task, count: int) -> typing.Dict[PlaneType, int]:
+ return self._find_best_unit(self.armor, for_type, count)
+
+ def _group_sizes(self, total_planes: int) -> typing.List[int]:
+ total_scrambled = 0
+ for _ in range(math.ceil(total_planes / PLANES_IN_GROUP)):
+ total_scrambled += PLANES_IN_GROUP
+ yield total_scrambled < total_planes and PLANES_IN_GROUP or total_planes - total_scrambled
+
+ def _group_sizes_for(self, target: ControlPoint) -> typing.List[int]:
+ total_planes = target.importance * PLANES_IMPORTANCE_FACTOR
+ total_scrambled = 0
+ for _ in range(math.ceil(total_planes / PLANES_IN_GROUP)):
+ total_scrambled += PLANES_IN_GROUP
+ yield PLANES_IN_GROUP and total_scrambled < total_planes or total_planes - total_scrambled
+
+ def commit_scramble(self, scrambled_aircraft: typing.Dict[PlaneType, int]):
+ for k, c in scrambled_aircraft:
+ self.aircraft[k] -= c
+ assert self.aircraft[k] >= 0
+ if self.aircraft[k] == 0:
+ del self.aircraft[k]
+
+ def scramble_cas(self, for_target: ControlPoint) -> typing.Dict[PlaneType, int]:
+ return self._find_best_planes(CAS, int(for_target.importance * PLANES_IMPORTANCE_FACTOR))
+
+ def scramble_sweep(self, for_target: ControlPoint) -> typing.Dict[PlaneType, int]:
+ return self._find_best_planes(FighterSweep, int(for_target.importance * PLANES_IMPORTANCE_FACTOR))
+
+ def scramble_interceptors(self, factor: float) -> typing.Dict[PlaneType, int]:
+ return self._find_best_planes(FighterSweep, int(self.total_planes * factor))
+
+ def assemble_cap(self, for_target: ControlPoint) -> typing.Dict[Armor, int]:
+ return self._find_best_armor(CAP, int(for_target.importance * ARMOR_IMPORTANCE_FACTOR))
+
+ def assemble_defense(self, factor: float) -> typing.Dict[Armor, int]:
+ return self._find_best_armor(CAP, int(self.total_armor * factor))
diff --git a/theater/caucasus.py b/theater/caucasus.py
index 01b8ecfc..3810fd79 100644
--- a/theater/caucasus.py
+++ b/theater/caucasus.py
@@ -4,11 +4,20 @@ 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())
+ kutaisi = ControlPoint(caucasus.Kutaisi.position, ALL_RADIALS, SIZE_SMALL, IMPORTANCE_LOW)
+ senaki = ControlPoint(caucasus.Senaki.position, ALL_RADIALS, SIZE_REGULAR, IMPORTANCE_LOW)
+ kobuleti = ControlPoint(caucasus.Kobuleti.position, COAST_VERTICAL, SIZE_SMALL, IMPORTANCE_LOW)
+ batumi = ControlPoint(caucasus.Batumi.position, COAST_VERTICAL, SIZE_REGULAR, IMPORTANCE_MEDIUM)
+ sukhumi = ControlPoint(caucasus.Sukhumi.position, COAST_VERTICAL, SIZE_REGULAR, IMPORTANCE_MEDIUM)
+ gudauta = ControlPoint(caucasus.Gudauta.position, COAST_VERTICAL, SIZE_REGULAR, IMPORTANCE_MEDIUM)
+ sochi = ControlPoint(caucasus.Sochi.position, COAST_VERTICAL, SIZE_BIG, IMPORTANCE_HIGH)
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])
+ self.add_controlpoint(self.kutaisi, connected_to=[self.senaki])
+ self.add_controlpoint(self.senaki, connected_to=[self.kobuleti, self.sukhumi])
+ self.add_controlpoint(self.kobuleti, connected_to=[self.batumi])
+ self.add_controlpoint(self.batumi)
+
+ self.add_controlpoint(self.sukhumi, connected_to=[self.gudauta])
+ self.add_controlpoint(self.gudauta, connected_to=[self.sochi])
+ self.add_controlpoint(self.sochi)
diff --git a/theater/conflicttheater.py b/theater/conflicttheater.py
index 293b0ac7..3bc2be79 100644
--- a/theater/conflicttheater.py
+++ b/theater/conflicttheater.py
@@ -3,10 +3,24 @@ import dcs
from .controlpoint import *
-class ConflictTheater:
- controlpoints = [] # type: typing.Collection[ControlPoint]
+SIZE_SMALL = 600
+SIZE_REGULAR = 1000
+SIZE_BIG = 2000
+SIZE_LARGE = 3000
- def add_controlpoint(self, point: ControlPoint, connected_to: typing.Collection[ControlPoint]):
+IMPORTANCE_LOW = 1
+IMPORTANCE_MEDIUM = 2
+IMPORTANCE_HIGH = 3
+
+ALL_RADIALS = [0, 45, 90, 135, 180, 225, 270, 315, ]
+COAST_VERTICAL = [45, 90, 135, ]
+COAST_HORIZONTAL = [315, 0, 45, ]
+
+
+class ConflictTheater:
+ controlpoints = [] # type: typing.List[ControlPoint]
+
+ def add_controlpoint(self, point: ControlPoint, connected_to: typing.Collection[ControlPoint] = []):
for connected_point in connected_to:
point.connect(to=connected_point)
diff --git a/theater/controlpoint.py b/theater/controlpoint.py
index 11a93068..5f0f58cf 100644
--- a/theater/controlpoint.py
+++ b/theater/controlpoint.py
@@ -1,30 +1,54 @@
import typing
import dcs
+import math
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
+ connected_points = [] # type: typing.List[ControlPoint]
+ position = None # type: Point
captured = False
- base = None # type: Base
+ strength = 100
+ base: None # type: theater.base.Base
- def __init__(self, point: Point, size: int, importance: int, captured: bool, base: Base):
- self.point = point
+ def __init__(self, point: Point, radials: typing.Collection[int], size: int, importance: int):
+ import theater.base
+
+ self.position = point
self.size = size
self.importance = importance
- self.captured = captured
- self.base = base
+ self.captured = False
+ self.radials = radials
+ self.base = theater.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)
+ def find_radial(self, heading: int):
+ closest_radial = 0
+ closest_radial_delta = 360
+ for radial in self.radials:
+ delta = math.fabs(radial - heading)
+ if closest_radial_delta < delta:
+ closest_radial = radial
+ closest_radial_delta = delta
+
+ return closest_radial
+
+ def conflict_attack(self, from_cp, attacker: Country, defender: Country) -> Conflict:
+ 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))
+
+ return Conflict(attacker=attacker,
+ attack_heading=attack_radial,
+ defender=defender,
+ defense_heading=defense_radial,
+ position=self.position,
+ size=self.size)