diff --git a/__init__.py b/__init__.py index 0579cdea..0b199404 100755 --- a/__init__.py +++ b/__init__.py @@ -17,7 +17,7 @@ from game.game import Game from theater import start_generator 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]) 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(".") save_version = save_version.split(".") + if "--ignore-save" in sys.argv: + return False + if current_version[:2] == save_version[:2]: return True diff --git a/game/event/baseattack.py b/game/event/baseattack.py index 0650ec0c..a85b4640 100644 --- a/game/event/baseattack.py +++ b/game/event/baseattack.py @@ -32,6 +32,7 @@ class BaseAttackEvent(Event): if self.is_successfull(debriefing): if self.from_cp.captured: 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.affect_strength(+self.STRENGTH_RECOVERY) diff --git a/game/game.py b/game/game.py index 7ee777b5..3046b644 100644 --- a/game/game.py +++ b/game/game.py @@ -127,7 +127,10 @@ class Game: if event_class == BaseAttackEvent and enemy_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD: pass 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): if event_class in enemy_generated_types: continue @@ -141,15 +144,15 @@ class Game: if event_class == NavalInterceptEvent: if player_cp.radials == LAND: continue + elif event_class == StrikeEvent: + if not player_cp.ground_objects: + continue elif event_class == BaseAttackEvent: if enemy_cap_generated: continue if enemy_cp.base.total_armor == 0: continue enemy_cap_generated = True - elif event_class == AntiAAStrikeEvent: - if player_cp.base.total_aa == 0: - continue enemy_generated_types.append(event_class) self.events.append(event_class(self.enemy, self.player, enemy_cp, player_cp, self)) diff --git a/game/operation/strike.py b/game/operation/strike.py index 85bac18a..430235e0 100644 --- a/game/operation/strike.py +++ b/game/operation/strike.py @@ -47,7 +47,13 @@ class StrikeOperation(Operation): def generate(self): targets = [] # type: typing.List[typing.Tuple[str, Point]] category_counters = {} # type: typing.Dict[str, int] + processed_groups = [] 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 markpoint_name = "{}{}".format(object.name_abbrev, category_counters[object.category]) targets.append((markpoint_name, object.position)) @@ -68,4 +74,6 @@ class StrikeOperation(Operation): clients={}, 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() diff --git a/gen/armor.py b/gen/armor.py index 7ba55402..876fa13c 100644 --- a/gen/armor.py +++ b/gen/armor.py @@ -100,8 +100,10 @@ class ArmorConflictGenerator: 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): + padding = FRONTLINE_CAS_PADDING if FRONTLINE_CAS_PADDING < self.conflict.distance else 0 + 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) def generate_passengers(self, count: int): diff --git a/gen/conflictgen.py b/gen/conflictgen.py index b7dc71fe..30e4a53a 100644 --- a/gen/conflictgen.py +++ b/gen/conflictgen.py @@ -152,36 +152,48 @@ class Conflict: @classmethod 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) + left_position, right_position = None, None - left_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: - 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: + if not theater.is_on_land(center_position): + pos = cls._find_ground_position(center_position, FRONTLINE_LENGTH, _heading_sum(heading, -90), theater) + if 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 - def _find_ground_location(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point: - for _ in range(0, int(max_distance), 800): - for _ in range(3): - if theater.is_on_land(initial): - return initial + def _extend_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point: + pos = initial + for offset in range(0, int(max_distance), 500): + new_pos = initial.point_from_heading(heading, offset) + 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!") return None @@ -195,10 +207,10 @@ class Conflict: distance = to_cp.size * GROUND_DISTANCE_FACTOR 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 = 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( 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): heading = random.choice(to_cp.radials) 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: 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) @@ -306,7 +318,7 @@ class Conflict: distance = to_cp.size * GROUND_DISTANCE_FACTOR 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( position=position, @@ -351,7 +363,7 @@ class Conflict: 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) 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: 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) diff --git a/gen/groundobjectsgen.py b/gen/groundobjectsgen.py index b4d9f16b..ccbd4220 100644 --- a/gen/groundobjectsgen.py +++ b/gen/groundobjectsgen.py @@ -13,6 +13,7 @@ CATEGORY_MAPPING = { "warehouse": [Warehouse.Warehouse], "fuel": [Warehouse.Tank], "ammo": [Warehouse.Ammunition_depot], + "farp": [Fortification.FARP_Tent], } @@ -40,7 +41,7 @@ class GroundObjectsGenerator: country=side, name=ground_object.string_identifier, _type=unit_type, - position=Point(*ground_object.location), + position=ground_object.position, heading=ground_object.heading ) @@ -50,7 +51,7 @@ class GroundObjectsGenerator: country=side, name=ground_object.string_identifier, _type=random.choice(CATEGORY_MAPPING[ground_object.category]), - position=Point(*ground_object.location), + position=ground_object.position, heading=ground_object.heading ) diff --git a/resources/cau_groundobjects.p b/resources/cau_groundobjects.p index 4a47c810..1cf0d8a6 100644 Binary files a/resources/cau_groundobjects.p and b/resources/cau_groundobjects.p differ diff --git a/resources/caulandmap.p b/resources/caulandmap.p index 16ce2213..14b19f18 100644 Binary files a/resources/caulandmap.p and b/resources/caulandmap.p differ diff --git a/resources/tools/cau_groundobjects.miz b/resources/tools/cau_groundobjects.miz index aefc0e55..6f1976f4 100644 Binary files a/resources/tools/cau_groundobjects.miz and b/resources/tools/cau_groundobjects.miz differ diff --git a/resources/tools/cau_terrain.miz b/resources/tools/cau_terrain.miz index 6655e5ae..20d0c5db 100644 Binary files a/resources/tools/cau_terrain.miz and b/resources/tools/cau_terrain.miz differ diff --git a/resources/tools/generate_groundobjectsmap.py b/resources/tools/generate_groundobjectsmap.py index 8557fb23..256b6f1f 100644 --- a/resources/tools/generate_groundobjectsmap.py +++ b/resources/tools/generate_groundobjectsmap.py @@ -4,41 +4,66 @@ import typing from game import db from gen.groundobjectsgen import TheaterGroundObject from dcs.mission import Mission -from dcs.terrain import PersianGulf +from dcs.mapping import Point m = Mission() m.load_file("./cau_groundobjects.miz") result = {} +result_by_groups = {} # type: typing.Dict[int, TheaterGroundObject] +ids_counters = {} def append_group(cp_id, category, group_id, object_id, position, heading): global result + global result_by_groups + + ground_object = TheaterGroundObject(category, cp_id, group_id, object_id, position, heading) if cp_id not in result: 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: - args = str(name).split("|") - if len(args) == 3: - args.append("1") + args = str(name.split()[0]).split("|") - 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: try: - category, cp_id, group_id, object_id = parse_name(str(group.name)) + category, cp_id, group_id = parse_name(str(group.name)) except: print("Failed to parse {}".format(group.name)) 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()]))) + + with open("../cau_groundobjects.p", "wb") as f: pickle.dump(result, f) diff --git a/resources/tools/generate_landmap.py b/resources/tools/generate_landmap.py index a758e0ee..5e472aaa 100644 --- a/resources/tools/generate_landmap.py +++ b/resources/tools/generate_landmap.py @@ -1,14 +1,14 @@ import pickle from dcs.mission import Mission -from dcs.terrain import PersianGulf -m = Mission() -m.load_file("./gulf_terrain.miz") +for terrain in ["cau", "gulf"]: + m = Mission() + m.load_file("./{}_terrain.miz".format(terrain)) -landmap = [] -for plane_group in m.country("USA").plane_group: - landmap.append([(x.position.x, x.position.y) for x in plane_group.points]) + landmap = [] + for plane_group in m.country("USA").plane_group: + landmap.append([(x.position.x, x.position.y) for x in plane_group.points]) -with open("../gulflandmap.p", "wb") as f: - pickle.dump(landmap, f) + with open("../{}landmap.p".format(terrain), "wb") as f: + pickle.dump(landmap, f) diff --git a/theater/caucasus.py b/theater/caucasus.py index 82e1c4b6..fd824c4a 100644 --- a/theater/caucasus.py +++ b/theater/caucasus.py @@ -13,7 +13,7 @@ class CaucasusTheater(ConflictTheater): overview_image = "caumap.gif" reference_points = {(-317948.32727306, 635639.37385346): (282.5, 319), (-355692.3067714, 617269.96285781): (269, 352), } - landmap_poly = load_poly("resources\\caulandmap.p") + landmap = load_landmap("resources\\caulandmap.p") daytime_map = { "dawn": (6, 9), "day": (9, 18), diff --git a/theater/conflicttheater.py b/theater/conflicttheater.py index baa3f445..5f78c198 100644 --- a/theater/conflicttheater.py +++ b/theater/conflicttheater.py @@ -4,7 +4,7 @@ import itertools import dcs from dcs.mapping import Point -from .landmap import ray_tracing +from .landmap import Landmap, poly_contains from .controlpoint import ControlPoint from .theatergroundobject import TheaterGroundObject @@ -52,12 +52,11 @@ class ConflictTheater: reference_points = None # type: typing.Dict overview_image = None # type: str - landmap_poly = None + landmap = None # type: landmap.Landmap daytime_map = None # type: typing.Dict[str, typing.Tuple[int, int]] def __init__(self): self.controlpoints = [] - self.groundobjects = [] def set_groundobject(self, dictionary: typing.Dict[int, typing.Collection[TheaterGroundObject]]): for id, value in dictionary.items(): @@ -73,14 +72,20 @@ class ConflictTheater: self.controlpoints.append(point) def is_on_land(self, point: Point) -> bool: - if not self.landmap_poly: + if not self.landmap: return True - for poly in self.landmap_poly: - if ray_tracing(point.x, point.y, poly): - return True + # check first poly (main land poly) + if not poly_contains(point.x, point.y, self.landmap[0]): + 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]: return [point for point in self.controlpoints if point.captured] diff --git a/theater/controlpoint.py b/theater/controlpoint.py index f8985244..72e44017 100644 --- a/theater/controlpoint.py +++ b/theater/controlpoint.py @@ -9,7 +9,7 @@ from .theatergroundobject import TheaterGroundObject class ControlPoint: - connected_points = [] # type: typing.List[ControlPoint] + connected_points = None # type: typing.List[ControlPoint] ground_objects = None # type: typing.Collection[TheaterGroundObject] position = None # type: Point captured = False @@ -26,6 +26,7 @@ class ControlPoint: self.full_name = name self.position = position self.at = at + self.ground_objects = [] self.size = size self.importance = importance diff --git a/theater/landmap.py b/theater/landmap.py index ce99252e..a52a00eb 100644 --- a/theater/landmap.py +++ b/theater/landmap.py @@ -1,7 +1,10 @@ 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: with open(filename, "rb") as f: return pickle.load(f) @@ -9,7 +12,7 @@ def load_poly(filename: str): return None -def ray_tracing(x, y, poly): +def poly_contains(x, y, poly): n = len(poly) inside = False xints = 0.0 @@ -25,3 +28,11 @@ def ray_tracing(x, y, poly): inside = not inside p1x, p1y = p2x, p2y 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) + diff --git a/theater/persiangulf.py b/theater/persiangulf.py index 0cc3e06a..0c06b2f7 100644 --- a/theater/persiangulf.py +++ b/theater/persiangulf.py @@ -3,7 +3,7 @@ from dcs import mapping from .conflicttheater import * from .base import * -from .landmap import load_poly +from .landmap import load_landmap class PersianGulfTheater(ConflictTheater): @@ -11,7 +11,7 @@ class PersianGulfTheater(ConflictTheater): overview_image = "persiangulf.gif" 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), } - landmap_poly = load_poly("resources\\gulflandmap.p") + landmap = load_landmap("resources\\gulflandmap.p") daytime_map = { "dawn": (6, 8), "day": (8, 16), diff --git a/theater/theatergroundobject.py b/theater/theatergroundobject.py index b0e3a3fa..83a11fc4 100644 --- a/theater/theatergroundobject.py +++ b/theater/theatergroundobject.py @@ -8,6 +8,7 @@ NAME_BY_CATEGORY = { "fuel": "Fuel depot", "defense": "AA Defense Site", "warehouse": "Warehouse", + "farp": "FARP", } ABBREV_NAME = { @@ -16,6 +17,7 @@ ABBREV_NAME = { "fuel": "FUEL", "defense": "AA", "warehouse": "WARE", + "farp": "FARP", } @@ -24,25 +26,21 @@ class TheaterGroundObject: cp_id = 0 group_id = 0 heading = 0 - location = None # type: typing.Collection[int] + position = None # type: Point 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.cp_id = cp_id self.group_id = group_id self.object_id = object_id - self.location = location + self.position = position self.heading = heading @property def string_identifier(self): return "{}|{}|{}|{}".format(self.category, self.cp_id, self.group_id, self.object_id) - @property - def position(self) -> Point: - return Point(*self.location) - @property def name_abbrev(self) -> str: return ABBREV_NAME[self.category] diff --git a/ui/configurationmenu.py b/ui/configurationmenu.py index a44a5b05..2a82ba1b 100644 --- a/ui/configurationmenu.py +++ b/ui/configurationmenu.py @@ -1,3 +1,5 @@ +import webbrowser + from tkinter import * from tkinter.ttk import * 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) 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): self.game.budget += 200 diff --git a/ui/corruptedsavemenu.py b/ui/corruptedsavemenu.py index 85264011..40f05e69 100644 --- a/ui/corruptedsavemenu.py +++ b/ui/corruptedsavemenu.py @@ -13,6 +13,6 @@ class CorruptedSaveMenu(Menu): def display(self): 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="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) diff --git a/ui/eventmenu.py b/ui/eventmenu.py index a7673038..55076591 100644 --- a/ui/eventmenu.py +++ b/ui/eventmenu.py @@ -8,14 +8,14 @@ from .styles import STYLES, RED UNITTYPES_FOR_EVENTS = { - FrontlineAttackEvent: [CAS, PinpointStrike], - FrontlinePatrolEvent: [CAP, PinpointStrike], - BaseAttackEvent: [CAP, CAS, PinpointStrike], - StrikeEvent: [CAP, CAS], - InterceptEvent: [CAP], - InsurgentAttackEvent: [CAS], - NavalInterceptEvent: [CAS], - InfantryTransportEvent: [Embarking], + FrontlineAttackEvent: [[CAS, PinpointStrike], [CAP]], + FrontlinePatrolEvent: [[CAP, PinpointStrike], [CAP]], + BaseAttackEvent: [[CAP, CAS, PinpointStrike], [CAP, CAS, PinpointStrike]], + StrikeEvent: [[CAP, CAS], [CAP]], + InterceptEvent: [[CAP], [CAP]], + InsurgentAttackEvent: [[CAS], [CAP]], + NavalInterceptEvent: [[CAS], [CAP]], + InfantryTransportEvent: [[Embarking], [CAP]], } 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) 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(): if filter_to and db.unit_task(unit_type) not in filter_to: continue @@ -262,12 +263,6 @@ class EventMenu(Menu): elif type(self.event) is 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): e.player_attacking(strikegroup=scrambled_aircraft, clients=scrambled_clients) else: diff --git a/ui/eventresultsmenu.py b/ui/eventresultsmenu.py index dc149bed..f3341f0e 100644 --- a/ui/eventresultsmenu.py +++ b/ui/eventresultsmenu.py @@ -86,6 +86,10 @@ class EventResultsMenu(Menu): 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(): if count == 0: continue diff --git a/ui/mainmenu.py b/ui/mainmenu.py index 63106d20..07dac796 100644 --- a/ui/mainmenu.py +++ b/ui/mainmenu.py @@ -51,10 +51,9 @@ class MainMenu(Menu): nonlocal row, body frame = LabelFrame(body, **STYLES["label-frame"]) 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, - event.to_cp, + event ), 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) row += 1 @@ -63,9 +62,8 @@ class MainMenu(Menu): nonlocal row, body 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.sort(key=lambda x: x.to_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)) @@ -74,7 +72,7 @@ class MainMenu(Menu): for event in events: if not event.informational: 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: new_destination = "Enemy attack" if destination != new_destination: diff --git a/ui/overviewcanvas.py b/ui/overviewcanvas.py index e3522e35..5a56f101 100644 --- a/ui/overviewcanvas.py +++ b/ui/overviewcanvas.py @@ -81,6 +81,7 @@ class OverviewCanvas: 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) + distance = max(distance, 1000) start_coords = self.transform_point(frontline_pos, treshold=10) end_coords = self.transform_point(frontline_pos.point_from_heading(heading, distance), treshold=60)