updates to strike missions; frontline operations invalid units placement fixed; minor UI updates

This commit is contained in:
Vasyl Horbachenko 2018-09-09 04:15:44 +03:00
parent e0d82da6cb
commit 4ba1dd87e8
25 changed files with 176 additions and 97 deletions

View File

@ -17,7 +17,7 @@ from game.game import Game
from theater import start_generator from theater import start_generator
from userdata import persistency, logging as logging_module from userdata import persistency, logging as logging_module
assert len(sys.argv) == 3, "__init__.py should be started with two mandatory arguments: %UserProfile% location and application version" assert len(sys.argv) >= 3, "__init__.py should be started with two mandatory arguments: %UserProfile% location and application version"
persistency.setup(sys.argv[1]) persistency.setup(sys.argv[1])
dcs.planes.FlyingType.payload_dirs = [os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources\\payloads")] dcs.planes.FlyingType.payload_dirs = [os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources\\payloads")]
@ -36,6 +36,9 @@ def is_version_compatible(save_version):
current_version = VERSION_STRING.split(".") current_version = VERSION_STRING.split(".")
save_version = save_version.split(".") save_version = save_version.split(".")
if "--ignore-save" in sys.argv:
return False
if current_version[:2] == save_version[:2]: if current_version[:2] == save_version[:2]:
return True return True

View File

@ -32,6 +32,7 @@ class BaseAttackEvent(Event):
if self.is_successfull(debriefing): if self.is_successfull(debriefing):
if self.from_cp.captured: if self.from_cp.captured:
self.to_cp.captured = True self.to_cp.captured = True
self.to_cp.ground_objects = []
self.to_cp.base.filter_units(db.UNIT_BY_COUNTRY[self.attacker_name]) self.to_cp.base.filter_units(db.UNIT_BY_COUNTRY[self.attacker_name])
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY) self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)

View File

@ -127,7 +127,10 @@ class Game:
if event_class == BaseAttackEvent and enemy_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD: if event_class == BaseAttackEvent and enemy_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD:
pass pass
else: else:
self.events.append(event_class(self.player, self.enemy, player_cp, enemy_cp, self)) if event_class == StrikeEvent and not enemy_cp.ground_objects:
pass
else:
self.events.append(event_class(self.player, self.enemy, player_cp, enemy_cp, self))
elif self._roll(enemy_probability, enemy_cp.base.strength): elif self._roll(enemy_probability, enemy_cp.base.strength):
if event_class in enemy_generated_types: if event_class in enemy_generated_types:
continue continue
@ -141,15 +144,15 @@ class Game:
if event_class == NavalInterceptEvent: if event_class == NavalInterceptEvent:
if player_cp.radials == LAND: if player_cp.radials == LAND:
continue continue
elif event_class == StrikeEvent:
if not player_cp.ground_objects:
continue
elif event_class == BaseAttackEvent: elif event_class == BaseAttackEvent:
if enemy_cap_generated: if enemy_cap_generated:
continue continue
if enemy_cp.base.total_armor == 0: if enemy_cp.base.total_armor == 0:
continue continue
enemy_cap_generated = True enemy_cap_generated = True
elif event_class == AntiAAStrikeEvent:
if player_cp.base.total_aa == 0:
continue
enemy_generated_types.append(event_class) enemy_generated_types.append(event_class)
self.events.append(event_class(self.enemy, self.player, enemy_cp, player_cp, self)) self.events.append(event_class(self.enemy, self.player, enemy_cp, player_cp, self))

View File

@ -47,7 +47,13 @@ class StrikeOperation(Operation):
def generate(self): def generate(self):
targets = [] # type: typing.List[typing.Tuple[str, Point]] targets = [] # type: typing.List[typing.Tuple[str, Point]]
category_counters = {} # type: typing.Dict[str, int] category_counters = {} # type: typing.Dict[str, int]
processed_groups = []
for object in self.to_cp.ground_objects: for object in self.to_cp.ground_objects:
if object.group_id in processed_groups:
continue
processed_groups.append(object.group_id)
category_counters[object.category] = category_counters.get(object.category, 0) + 1 category_counters[object.category] = category_counters.get(object.category, 0) + 1
markpoint_name = "{}{}".format(object.name_abbrev, category_counters[object.category]) markpoint_name = "{}{}".format(object.name_abbrev, category_counters[object.category])
targets.append((markpoint_name, object.position)) targets.append((markpoint_name, object.position))
@ -68,4 +74,6 @@ class StrikeOperation(Operation):
clients={}, clients={},
at=self.defenders_starting_position) at=self.defenders_starting_position)
self.briefinggen.title = "Strike"
self.briefinggen.description = "Destroy infrastructure assets and military supplies in the region. Each building destroyed will lower targets strength."
super(StrikeOperation, self).generate() super(StrikeOperation, self).generate()

View File

@ -100,8 +100,10 @@ class ArmorConflictGenerator:
attacker_groups = list(db.unitdict_split(attackers, single_fight_attackers_count)) attacker_groups = list(db.unitdict_split(attackers, single_fight_attackers_count))
for attacker_group_dict, target_group_dict in zip_longest(attacker_groups, defender_groups): for attacker_group_dict, target_group_dict in zip_longest(attacker_groups, defender_groups):
padding = FRONTLINE_CAS_PADDING if FRONTLINE_CAS_PADDING < self.conflict.distance else 0
position = self.conflict.position.point_from_heading(self.conflict.heading, position = self.conflict.position.point_from_heading(self.conflict.heading,
random.randint(FRONTLINE_CAS_PADDING, int(self.conflict.distance - FRONTLINE_CAS_PADDING))) random.randint(padding, int(self.conflict.distance - padding)))
self._generate_fight_at(attacker_group_dict, target_group_dict, position) self._generate_fight_at(attacker_group_dict, target_group_dict, position)
def generate_passengers(self, count: int): def generate_passengers(self, count: int):

View File

@ -152,36 +152,48 @@ class Conflict:
@classmethod @classmethod
def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> typing.Tuple[Point, int, int]: def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> typing.Tuple[Point, int, int]:
center_position, heading = cls.frontline_position(from_cp, to_cp) center_position, heading = cls.frontline_position(from_cp, to_cp)
left_position, right_position = None, None
left_position = center_position if not theater.is_on_land(center_position):
pos = cls._find_ground_position(center_position, FRONTLINE_LENGTH, _heading_sum(heading, -90), theater)
for offset in range(0, int(FRONTLINE_LENGTH / 2), 1000): if pos:
pos = center_position.point_from_heading(_heading_sum(heading, -90), offset)
if not theater.is_on_land(pos):
break
else:
left_position = pos
right_position = center_position
for offset in range(0, int(FRONTLINE_LENGTH / 2), 1000):
pos = center_position.point_from_heading(_heading_sum(heading, 90), offset)
if not theater.is_on_land(pos):
break
else:
right_position = pos right_position = pos
center_position = pos
else:
pos = cls._find_ground_position(center_position, FRONTLINE_LENGTH, _heading_sum(heading, +90), theater)
if pos:
left_position = pos
center_position = pos
print("{} - {} {}".format(from_cp, to_cp, center_position))
return left_position, _heading_sum(heading, 90), right_position.distance_to_point(left_position) if left_position is None:
left_position = cls._extend_ground_position(center_position, int(FRONTLINE_LENGTH/2), _heading_sum(heading, -90), theater)
if right_position is None:
right_position = cls._extend_ground_position(center_position, int(FRONTLINE_LENGTH/2), _heading_sum(heading, 90), theater)
return left_position, _heading_sum(heading, 90), int(right_position.distance_to_point(left_position))
@classmethod @classmethod
def _find_ground_location(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point: def _extend_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point:
for _ in range(0, int(max_distance), 800): pos = initial
for _ in range(3): for offset in range(0, int(max_distance), 500):
if theater.is_on_land(initial): new_pos = initial.point_from_heading(heading, offset)
return initial if theater.is_on_land(new_pos):
pos = new_pos
else:
return pos
initial = initial.random_point_within(1000, 1000) return pos
initial = initial.point_from_heading(heading, 800) @classmethod
def _find_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point:
pos = initial
for _ in range(0, int(max_distance), 500):
if theater.is_on_land(pos):
return pos
pos = pos.point_from_heading(heading, 500)
logging.info("Didn't find ground position!") logging.info("Didn't find ground position!")
return None return None
@ -195,10 +207,10 @@ class Conflict:
distance = to_cp.size * GROUND_DISTANCE_FACTOR distance = to_cp.size * GROUND_DISTANCE_FACTOR
attackers_location = position.point_from_heading(attack_heading, distance) attackers_location = position.point_from_heading(attack_heading, distance)
attackers_location = Conflict._find_ground_location(attackers_location, distance * 2, _heading_sum(attack_heading, 180), theater) attackers_location = Conflict._find_ground_position(attackers_location, distance * 2, _heading_sum(attack_heading, 180), theater)
defenders_location = position.point_from_heading(defense_heading, distance) defenders_location = position.point_from_heading(defense_heading, distance)
defenders_location = Conflict._find_ground_location(defenders_location, distance * 2, _heading_sum(defense_heading, 180), theater) defenders_location = Conflict._find_ground_position(defenders_location, distance * 2, _heading_sum(defense_heading, 180), theater)
return cls( return cls(
position=position, position=position,
@ -238,7 +250,7 @@ class Conflict:
def ground_attack_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater): def ground_attack_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
heading = random.choice(to_cp.radials) heading = random.choice(to_cp.radials)
initial_location = to_cp.position.random_point_within(*GROUND_ATTACK_DISTANCE) initial_location = to_cp.position.random_point_within(*GROUND_ATTACK_DISTANCE)
position = Conflict._find_ground_location(initial_location, GROUND_INTERCEPT_SPREAD, _heading_sum(heading, 180), theater) position = Conflict._find_ground_position(initial_location, GROUND_INTERCEPT_SPREAD, _heading_sum(heading, 180), theater)
if not position: if not position:
heading = to_cp.find_radial(to_cp.position.heading_between_point(from_cp.position)) heading = to_cp.find_radial(to_cp.position.heading_between_point(from_cp.position))
position = to_cp.position.point_from_heading(heading, to_cp.size * GROUND_DISTANCE_FACTOR) position = to_cp.position.point_from_heading(heading, to_cp.size * GROUND_DISTANCE_FACTOR)
@ -306,7 +318,7 @@ class Conflict:
distance = to_cp.size * GROUND_DISTANCE_FACTOR distance = to_cp.size * GROUND_DISTANCE_FACTOR
defenders_location = position.point_from_heading(defense_heading, distance) defenders_location = position.point_from_heading(defense_heading, distance)
defenders_location = Conflict._find_ground_location(defenders_location, distance * 2, _heading_sum(defense_heading, 180), theater) defenders_location = Conflict._find_ground_position(defenders_location, distance * 2, _heading_sum(defense_heading, 180), theater)
return cls( return cls(
position=position, position=position,
@ -351,7 +363,7 @@ class Conflict:
def transport_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater): def transport_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
frontline_position, heading = cls.frontline_position(from_cp, to_cp) frontline_position, heading = cls.frontline_position(from_cp, to_cp)
initial_dest = frontline_position.point_from_heading(heading, TRANSPORT_FRONTLINE_DIST) initial_dest = frontline_position.point_from_heading(heading, TRANSPORT_FRONTLINE_DIST)
dest = cls._find_ground_location(initial_dest, from_cp.position.distance_to_point(to_cp.position) / 3, heading, theater) dest = cls._find_ground_position(initial_dest, from_cp.position.distance_to_point(to_cp.position) / 3, heading, theater)
if not dest: if not dest:
radial = to_cp.find_radial(to_cp.position.heading_between_point(from_cp.position)) radial = to_cp.find_radial(to_cp.position.heading_between_point(from_cp.position))
dest = to_cp.position.point_from_heading(radial, to_cp.size * GROUND_DISTANCE_FACTOR) dest = to_cp.position.point_from_heading(radial, to_cp.size * GROUND_DISTANCE_FACTOR)

View File

@ -13,6 +13,7 @@ CATEGORY_MAPPING = {
"warehouse": [Warehouse.Warehouse], "warehouse": [Warehouse.Warehouse],
"fuel": [Warehouse.Tank], "fuel": [Warehouse.Tank],
"ammo": [Warehouse.Ammunition_depot], "ammo": [Warehouse.Ammunition_depot],
"farp": [Fortification.FARP_Tent],
} }
@ -40,7 +41,7 @@ class GroundObjectsGenerator:
country=side, country=side,
name=ground_object.string_identifier, name=ground_object.string_identifier,
_type=unit_type, _type=unit_type,
position=Point(*ground_object.location), position=ground_object.position,
heading=ground_object.heading heading=ground_object.heading
) )
@ -50,7 +51,7 @@ class GroundObjectsGenerator:
country=side, country=side,
name=ground_object.string_identifier, name=ground_object.string_identifier,
_type=random.choice(CATEGORY_MAPPING[ground_object.category]), _type=random.choice(CATEGORY_MAPPING[ground_object.category]),
position=Point(*ground_object.location), position=ground_object.position,
heading=ground_object.heading heading=ground_object.heading
) )

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -4,41 +4,66 @@ import typing
from game import db from game import db
from gen.groundobjectsgen import TheaterGroundObject from gen.groundobjectsgen import TheaterGroundObject
from dcs.mission import Mission from dcs.mission import Mission
from dcs.terrain import PersianGulf from dcs.mapping import Point
m = Mission() m = Mission()
m.load_file("./cau_groundobjects.miz") m.load_file("./cau_groundobjects.miz")
result = {} result = {}
result_by_groups = {} # type: typing.Dict[int, TheaterGroundObject]
ids_counters = {}
def append_group(cp_id, category, group_id, object_id, position, heading): def append_group(cp_id, category, group_id, object_id, position, heading):
global result global result
global result_by_groups
ground_object = TheaterGroundObject(category, cp_id, group_id, object_id, position, heading)
if cp_id not in result: if cp_id not in result:
result[cp_id] = [] result[cp_id] = []
result[cp_id].append(ground_object)
result[cp_id].append(TheaterGroundObject(category, cp_id, group_id, object_id, position, heading)) result_by_groups_key = "{}_{}_{}".format(cp_id, category, group_id)
if result_by_groups_key not in result_by_groups:
result_by_groups[result_by_groups_key] = []
result_by_groups[result_by_groups_key].append(ground_object)
def parse_name(name: str) -> typing.Tuple: def parse_name(name: str) -> typing.Tuple:
args = str(name).split("|") args = str(name.split()[0]).split("|")
if len(args) == 3:
args.append("1")
return args[0], int(args[1]), int(args[2]), int(args[3]) return args[0], int(args[1]), int(args[2])
for group in m.country("Russia").static_group + m.country("Russia").vehicle_group: for group in m.country("Russia").static_group + m.country("Russia").vehicle_group:
try: try:
category, cp_id, group_id, object_id = parse_name(str(group.name)) category, cp_id, group_id = parse_name(str(group.name))
except: except:
print("Failed to parse {}".format(group.name)) print("Failed to parse {}".format(group.name))
continue continue
append_group(cp_id, category, group_id, object_id, [group.position.x, group.position.y], group.units[0].heading) ids_counters_key = "{}_{}".format(cp_id, group_id)
ids_counters[ids_counters_key] = ids_counters.get(ids_counters_key, 0) + 1
object_id = ids_counters[ids_counters_key]
append_group(cp_id, category, group_id, object_id, group.position, group.units[0].heading)
GROUP_TRESHOLD = 300
did_check_pairs = []
for group_id, objects_in_group in result_by_groups.items():
for a in objects_in_group:
for b in objects_in_group:
if (a, b) in did_check_pairs:
continue
did_check_pairs.append((a, b))
distance = a.position.distance_to_point(b.position)
if distance > GROUP_TRESHOLD:
print("Objects {} and {} in group {} are too far apart ({})!".format(a.string_identifier, b.string_identifier, group_id, distance))
print("Total {} objects".format(sum([len(x) for x in result.values()]))) print("Total {} objects".format(sum([len(x) for x in result.values()])))
with open("../cau_groundobjects.p", "wb") as f: with open("../cau_groundobjects.p", "wb") as f:
pickle.dump(result, f) pickle.dump(result, f)

View File

@ -1,14 +1,14 @@
import pickle import pickle
from dcs.mission import Mission from dcs.mission import Mission
from dcs.terrain import PersianGulf
m = Mission() for terrain in ["cau", "gulf"]:
m.load_file("./gulf_terrain.miz") m = Mission()
m.load_file("./{}_terrain.miz".format(terrain))
landmap = [] landmap = []
for plane_group in m.country("USA").plane_group: for plane_group in m.country("USA").plane_group:
landmap.append([(x.position.x, x.position.y) for x in plane_group.points]) landmap.append([(x.position.x, x.position.y) for x in plane_group.points])
with open("../gulflandmap.p", "wb") as f: with open("../{}landmap.p".format(terrain), "wb") as f:
pickle.dump(landmap, f) pickle.dump(landmap, f)

View File

@ -13,7 +13,7 @@ class CaucasusTheater(ConflictTheater):
overview_image = "caumap.gif" overview_image = "caumap.gif"
reference_points = {(-317948.32727306, 635639.37385346): (282.5, 319), reference_points = {(-317948.32727306, 635639.37385346): (282.5, 319),
(-355692.3067714, 617269.96285781): (269, 352), } (-355692.3067714, 617269.96285781): (269, 352), }
landmap_poly = load_poly("resources\\caulandmap.p") landmap = load_landmap("resources\\caulandmap.p")
daytime_map = { daytime_map = {
"dawn": (6, 9), "dawn": (6, 9),
"day": (9, 18), "day": (9, 18),

View File

@ -4,7 +4,7 @@ import itertools
import dcs import dcs
from dcs.mapping import Point from dcs.mapping import Point
from .landmap import ray_tracing from .landmap import Landmap, poly_contains
from .controlpoint import ControlPoint from .controlpoint import ControlPoint
from .theatergroundobject import TheaterGroundObject from .theatergroundobject import TheaterGroundObject
@ -52,12 +52,11 @@ class ConflictTheater:
reference_points = None # type: typing.Dict reference_points = None # type: typing.Dict
overview_image = None # type: str overview_image = None # type: str
landmap_poly = None landmap = None # type: landmap.Landmap
daytime_map = None # type: typing.Dict[str, typing.Tuple[int, int]] daytime_map = None # type: typing.Dict[str, typing.Tuple[int, int]]
def __init__(self): def __init__(self):
self.controlpoints = [] self.controlpoints = []
self.groundobjects = []
def set_groundobject(self, dictionary: typing.Dict[int, typing.Collection[TheaterGroundObject]]): def set_groundobject(self, dictionary: typing.Dict[int, typing.Collection[TheaterGroundObject]]):
for id, value in dictionary.items(): for id, value in dictionary.items():
@ -73,14 +72,20 @@ class ConflictTheater:
self.controlpoints.append(point) self.controlpoints.append(point)
def is_on_land(self, point: Point) -> bool: def is_on_land(self, point: Point) -> bool:
if not self.landmap_poly: if not self.landmap:
return True return True
for poly in self.landmap_poly: # check first poly (main land poly)
if ray_tracing(point.x, point.y, poly): if not poly_contains(point.x, point.y, self.landmap[0]):
return True return False
return False # check others polys (exclusion zones from main)
for poly in self.landmap[1:]:
if poly_contains(point.x, point.y, poly):
# point is in one of the exclusion zones, meaning that it's in the lake or something
return False
return True
def player_points(self) -> typing.Collection[ControlPoint]: def player_points(self) -> typing.Collection[ControlPoint]:
return [point for point in self.controlpoints if point.captured] return [point for point in self.controlpoints if point.captured]

View File

@ -9,7 +9,7 @@ from .theatergroundobject import TheaterGroundObject
class ControlPoint: class ControlPoint:
connected_points = [] # type: typing.List[ControlPoint] connected_points = None # type: typing.List[ControlPoint]
ground_objects = None # type: typing.Collection[TheaterGroundObject] ground_objects = None # type: typing.Collection[TheaterGroundObject]
position = None # type: Point position = None # type: Point
captured = False captured = False
@ -26,6 +26,7 @@ class ControlPoint:
self.full_name = name self.full_name = name
self.position = position self.position = position
self.at = at self.at = at
self.ground_objects = []
self.size = size self.size = size
self.importance = importance self.importance = importance

View File

@ -1,7 +1,10 @@
import pickle import pickle
import typing
Landmap = typing.Collection[typing.Collection[typing.Tuple[float, float]]]
def load_poly(filename: str): def load_landmap(filename: str) -> Landmap:
try: try:
with open(filename, "rb") as f: with open(filename, "rb") as f:
return pickle.load(f) return pickle.load(f)
@ -9,7 +12,7 @@ def load_poly(filename: str):
return None return None
def ray_tracing(x, y, poly): def poly_contains(x, y, poly):
n = len(poly) n = len(poly)
inside = False inside = False
xints = 0.0 xints = 0.0
@ -25,3 +28,11 @@ def ray_tracing(x, y, poly):
inside = not inside inside = not inside
p1x, p1y = p2x, p2y p1x, p1y = p2x, p2y
return inside return inside
def poly_centroid(poly) -> typing.Tuple[float, float]:
x_list = [vertex[0] for vertex in poly]
y_list = [vertex[1] for vertex in poly]
x = sum(x_list) / len(poly)
y = sum(y_list) / len(poly)
return (x, y)

View File

@ -3,7 +3,7 @@ from dcs import mapping
from .conflicttheater import * from .conflicttheater import *
from .base import * from .base import *
from .landmap import load_poly from .landmap import load_landmap
class PersianGulfTheater(ConflictTheater): class PersianGulfTheater(ConflictTheater):
@ -11,7 +11,7 @@ class PersianGulfTheater(ConflictTheater):
overview_image = "persiangulf.gif" overview_image = "persiangulf.gif"
reference_points = {(persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (321, 145), reference_points = {(persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (321, 145),
(persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (347, 82), } (persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (347, 82), }
landmap_poly = load_poly("resources\\gulflandmap.p") landmap = load_landmap("resources\\gulflandmap.p")
daytime_map = { daytime_map = {
"dawn": (6, 8), "dawn": (6, 8),
"day": (8, 16), "day": (8, 16),

View File

@ -8,6 +8,7 @@ NAME_BY_CATEGORY = {
"fuel": "Fuel depot", "fuel": "Fuel depot",
"defense": "AA Defense Site", "defense": "AA Defense Site",
"warehouse": "Warehouse", "warehouse": "Warehouse",
"farp": "FARP",
} }
ABBREV_NAME = { ABBREV_NAME = {
@ -16,6 +17,7 @@ ABBREV_NAME = {
"fuel": "FUEL", "fuel": "FUEL",
"defense": "AA", "defense": "AA",
"warehouse": "WARE", "warehouse": "WARE",
"farp": "FARP",
} }
@ -24,25 +26,21 @@ class TheaterGroundObject:
cp_id = 0 cp_id = 0
group_id = 0 group_id = 0
heading = 0 heading = 0
location = None # type: typing.Collection[int] position = None # type: Point
category = None # type: str category = None # type: str
def __init__(self, category, cp_id, group_id, object_id, location, heading): def __init__(self, category, cp_id, group_id, object_id, position, heading):
self.category = category self.category = category
self.cp_id = cp_id self.cp_id = cp_id
self.group_id = group_id self.group_id = group_id
self.object_id = object_id self.object_id = object_id
self.location = location self.position = position
self.heading = heading self.heading = heading
@property @property
def string_identifier(self): def string_identifier(self):
return "{}|{}|{}|{}".format(self.category, self.cp_id, self.group_id, self.object_id) return "{}|{}|{}|{}".format(self.category, self.cp_id, self.group_id, self.object_id)
@property
def position(self) -> Point:
return Point(*self.location)
@property @property
def name_abbrev(self) -> str: def name_abbrev(self) -> str:
return ABBREV_NAME[self.category] return ABBREV_NAME[self.category]

View File

@ -1,3 +1,5 @@
import webbrowser
from tkinter import * from tkinter import *
from tkinter.ttk import * from tkinter.ttk import *
from .styles import STYLES from .styles import STYLES
@ -64,7 +66,16 @@ class ConfigurationMenu(Menu):
Checkbutton(body, variable=self.night_var, **STYLES["radiobutton"]).grid(row=4, column=1, sticky=E) Checkbutton(body, variable=self.night_var, **STYLES["radiobutton"]).grid(row=4, column=1, sticky=E)
Button(body, text="Back", command=self.dismiss, **STYLES["btn-primary"]).grid(row=5, column=1, sticky=E, pady=30) Button(body, text="Back", command=self.dismiss, **STYLES["btn-primary"]).grid(row=5, column=1, sticky=E, pady=30)
Button(body, text="Cheat +200m", command=self.cheat_money, **STYLES["btn-danger"]).grid(row=6, column=1)
Label(body, text="Contributors: ", **STYLES["widget"]).grid(row=6, column=0, sticky=W)
Label(body, text="shdwp - author, maintainer", **STYLES["widget"]).grid(row=7, column=0, sticky=W)
Button(body, text="[github]", command=lambda: webbrowser.open_new_tab("http://github.com/shdwp"), **STYLES["widget"]).grid(row=7, column=1, sticky=E)
Label(body, text="Khopa - contributions", **STYLES["widget"]).grid(row=8, column=0, sticky=W)
Button(body, text="[github]", command=lambda: webbrowser.open_new_tab("http://github.com/Khopa"), **STYLES["widget"]).grid(row=8, column=1, sticky=E)
Button(body, text="Cheat +200m", command=self.cheat_money, **STYLES["btn-danger"]).grid(row=10, column=1, pady=30)
def cheat_money(self): def cheat_money(self):
self.game.budget += 200 self.game.budget += 200

View File

@ -13,6 +13,6 @@ class CorruptedSaveMenu(Menu):
def display(self): def display(self):
self.window.clear_right_pane() self.window.clear_right_pane()
Label(text="Your save game was corrupted!", **STYLES["widget"]).grid(row=0, column=0) Label(text="Your save game is either incompatible or was corrupted!", **STYLES["widget"]).grid(row=0, column=0)
Label(text="Please restore it by replacing \"liberation_save\" file with \"liberation_save_tmp\" to restore last saved copy.", **STYLES["widget"]).grid(row=1, column=0) Label(text="Please restore it by replacing \"liberation_save\" file with \"liberation_save_tmp\" to restore last saved copy.", **STYLES["widget"]).grid(row=1, column=0)
Label(text="You can find those files under user DCS directory.", **STYLES["widget"]).grid(row=2, column=0) Label(text="You can find those files under user Saved Games\\DCS directory.", **STYLES["widget"]).grid(row=2, column=0)

View File

@ -8,14 +8,14 @@ from .styles import STYLES, RED
UNITTYPES_FOR_EVENTS = { UNITTYPES_FOR_EVENTS = {
FrontlineAttackEvent: [CAS, PinpointStrike], FrontlineAttackEvent: [[CAS, PinpointStrike], [CAP]],
FrontlinePatrolEvent: [CAP, PinpointStrike], FrontlinePatrolEvent: [[CAP, PinpointStrike], [CAP]],
BaseAttackEvent: [CAP, CAS, PinpointStrike], BaseAttackEvent: [[CAP, CAS, PinpointStrike], [CAP, CAS, PinpointStrike]],
StrikeEvent: [CAP, CAS], StrikeEvent: [[CAP, CAS], [CAP]],
InterceptEvent: [CAP], InterceptEvent: [[CAP], [CAP]],
InsurgentAttackEvent: [CAS], InsurgentAttackEvent: [[CAS], [CAP]],
NavalInterceptEvent: [CAS], NavalInterceptEvent: [[CAS], [CAP]],
InfantryTransportEvent: [Embarking], InfantryTransportEvent: [[Embarking], [CAP]],
} }
AI_BAN_FOR_EVENTS = { AI_BAN_FOR_EVENTS = {
@ -115,7 +115,8 @@ class EventMenu(Menu):
Label(self.frame, text="Client slots", **STYLES["widget"]).grid(row=row, column=3, columnspan=2) Label(self.frame, text="Client slots", **STYLES["widget"]).grid(row=row, column=3, columnspan=2)
row += 1 row += 1
filter_to = UNITTYPES_FOR_EVENTS[self.event.__class__] filter_attackers_index = 0 if self.game.is_player_attack(self.event) else 1
filter_to = UNITTYPES_FOR_EVENTS[self.event.__class__][filter_attackers_index]
for unit_type, count in self.base.aircraft.items(): for unit_type, count in self.base.aircraft.items():
if filter_to and db.unit_task(unit_type) not in filter_to: if filter_to and db.unit_task(unit_type) not in filter_to:
continue continue
@ -262,12 +263,6 @@ class EventMenu(Menu):
elif type(self.event) is NavalInterceptEvent: elif type(self.event) is NavalInterceptEvent:
e = self.event # type: NavalInterceptEvent e = self.event # type: NavalInterceptEvent
if self.game.is_player_attack(self.event):
e.player_attacking(strikegroup=scrambled_aircraft, clients=scrambled_clients)
else:
e.player_defending(interceptors=scrambled_aircraft, clients=scrambled_clients)
elif type(self.event) is AntiAAStrikeEvent:
e = self.event # type: AntiAAStrikeEvent
if self.game.is_player_attack(self.event): if self.game.is_player_attack(self.event):
e.player_attacking(strikegroup=scrambled_aircraft, clients=scrambled_clients) e.player_attacking(strikegroup=scrambled_aircraft, clients=scrambled_clients)
else: else:

View File

@ -86,6 +86,10 @@ class EventResultsMenu(Menu):
header("Enemy losses") header("Enemy losses")
if self.debriefing.destroyed_objects:
Label(self.frame, text="Ground assets", **STYLES["widget"]).grid(row=row)
Label(self.frame, text="{}".format(len(self.debriefing.destroyed_objects)), **STYLES["widget"]).grid(column=1, row=row)
for unit_type, count in self.enemy_losses.items(): for unit_type, count in self.enemy_losses.items():
if count == 0: if count == 0:
continue continue

View File

@ -51,10 +51,9 @@ class MainMenu(Menu):
nonlocal row, body nonlocal row, body
frame = LabelFrame(body, **STYLES["label-frame"]) frame = LabelFrame(body, **STYLES["label-frame"])
frame.grid(row=row, sticky=NSEW) frame.grid(row=row, sticky=NSEW)
Message(frame, text="{}{} at {}".format( Message(frame, text="{}{}".format(
event.defender_name == self.game.player and "Enemy attacking: " or "", event.defender_name == self.game.player and "Enemy attacking: " or "",
event, event
event.to_cp,
), aspect=1600, **STYLES["widget"]).grid(column=0, row=0, sticky=NSEW) ), aspect=1600, **STYLES["widget"]).grid(column=0, row=0, sticky=NSEW)
Button(body, text=">", command=self.start_event(event), **STYLES["btn-primary"]).grid(column=1, row=row, sticky=E) Button(body, text=">", command=self.start_event(event), **STYLES["btn-primary"]).grid(column=1, row=row, sticky=E)
row += 1 row += 1
@ -63,9 +62,8 @@ class MainMenu(Menu):
nonlocal row, body nonlocal row, body
Label(body, text=text, **STYLES["strong"]).grid(column=0, columnspan=2, row=row, sticky=N+EW, pady=(pady,0)); row += 1 Label(body, text=text, **STYLES["strong"]).grid(column=0, columnspan=2, row=row, sticky=N+EW, pady=(pady,0)); row += 1
#Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1
events = self.game.events events = self.game.events
events.sort(key=lambda x: x.to_cp.name)
events.sort(key=lambda x: x.from_cp.name) events.sort(key=lambda x: x.from_cp.name)
events.sort(key=lambda x: x.informational and 2 or (self.game.is_player_attack(x) and 1 or 0)) events.sort(key=lambda x: x.informational and 2 or (self.game.is_player_attack(x) and 1 or 0))
@ -74,7 +72,7 @@ class MainMenu(Menu):
for event in events: for event in events:
if not event.informational: if not event.informational:
if self.game.is_player_attack(event): if self.game.is_player_attack(event):
new_destination = event.from_cp.name new_destination = "From {} to {}".format(event.from_cp.name, event.to_cp.name)
else: else:
new_destination = "Enemy attack" new_destination = "Enemy attack"
if destination != new_destination: if destination != new_destination:

View File

@ -81,6 +81,7 @@ class OverviewCanvas:
if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp): if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp):
frontline_pos, heading, distance = Conflict.frontline_vector(cp, connected_cp, self.game.theater) frontline_pos, heading, distance = Conflict.frontline_vector(cp, connected_cp, self.game.theater)
distance = max(distance, 1000)
start_coords = self.transform_point(frontline_pos, treshold=10) start_coords = self.transform_point(frontline_pos, treshold=10)
end_coords = self.transform_point(frontline_pos.point_from_heading(heading, distance), treshold=60) end_coords = self.transform_point(frontline_pos.point_from_heading(heading, distance), treshold=60)