diff --git a/game/db.py b/game/db.py index 656550c9..c50f286e 100644 --- a/game/db.py +++ b/game/db.py @@ -45,7 +45,10 @@ PRICES = { Su_33: 22, MiG_29A: 18, MiG_29S: 20, + MiG_29G: 18, MiG_31: 30, + J_11A: 26, + Su_30: 24, F_5E_3: 8, MiG_15bis: 4, @@ -62,6 +65,8 @@ PRICES = { F_15C: 26, F_16C_bl_52d: 20, F_14B: 22, + Tornado_IDS: 24, + # Tornado_GR4: 24, # bomber Su_17M4: 10, @@ -200,7 +205,10 @@ UNIT_BY_TASK = { M_2000C, Mirage_2000_5, P_51D_30_NA, - P_51D + P_51D, + MiG_29G, + Su_30, + J_11A ], CAS: [ F_86F_Sabre, @@ -224,6 +232,8 @@ UNIT_BY_TASK = { OH_58D, B_52H, B_1B, + Tornado_IDS, + # Tornado_GR4, ], Transport: [ IL_76MD, @@ -379,8 +389,12 @@ CARRIER_TAKEOFF_BAN = [ AirDefense units that will be spawned at control points not related to the current operation """ EXTRA_AA = { - "Russia": AirDefence.SAM_SA_8_Osa_9A33, + "Russia": AirDefence.SAM_SA_9_Strela_1_9P31, "USA": AirDefence.SAM_Linebacker_M6, + "France": AirDefence.SPAAA_Gepard, + "Germany": AirDefence.SPAAA_Gepard, + "China": AirDefence.SPAAA_ZSU_23_4_Shilka, + "UK": AirDefence.AAA_Vulcan_M163, "Russia 1955": AirDefence.AAA_ZU_23_Closed, "USA 1955": AirDefence.AAA_Vulcan_M163, "Russia 1965": AirDefence.AAA_ZU_23_Closed, @@ -389,7 +403,9 @@ EXTRA_AA = { "Russia 1988": AirDefence.AAA_ZU_23_Closed, "USA 1990": AirDefence.AAA_Vulcan_M163, "France 1990": AirDefence.AAA_Vulcan_M163, - "Germany 1990": AirDefence.AAA_Vulcan_M163 + "Germany 1990": AirDefence.AAA_Vulcan_M163, + "Iran 2015": AirDefence.SPAAA_ZSU_23_4_Shilka, + "China 2015": AirDefence.SPAAA_ZSU_23_4_Shilka } """ @@ -596,13 +612,9 @@ FACTIONS = { Mi_8MT, AirDefence.SPAAA_ZSU_23_4_Shilka, - AirDefence.SAM_SA_9_Strela_1_9P31, - AirDefence.SAM_SA_8_Osa_9A33, - AirDefence.AAA_ZU_23_Closed, AirDefence.SAM_SA_19_Tunguska_2S6, - AirDefence.SAM_SA_6_Kub_LN_2P25, - AirDefence.SAM_SA_3_S_125_LN_5P73, AirDefence.SAM_SA_11_Buk_LN_9A310M1, + AirDefence.SAM_SA_10_S_300PS_LN_5P85C, Armor.APC_BTR_80, Armor.MBT_T_90, @@ -620,7 +632,7 @@ FACTIONS = { }, "Iran 2015": { - "country": "Russia", + "country": "Iran", "side": "red", "units": [ @@ -667,6 +679,46 @@ FACTIONS = { ] }, + "China 2000": { + "country": "China", + "side": "red", + "units": [ + + MiG_21Bis, # Standing as J-7 + Su_30, + J_11A, + + IL_76MD, + IL_78M, + An_26B, + An_30M, + Yak_40, + + A_50, + + Mi_8MT, + + AirDefence.AAA_ZU_23_Closed, + AirDefence.Rapier_FSA_Launcher, # Standing as PL-9C Shorad + AirDefence.SAM_SA_10_S_300PS_LN_5P85C, # Standing as HQ-9+ + AirDefence.SAM_SA_6_Kub_LN_2P25, + # TODO : ADD HQ-7 (need pydcs support) + + Armor.MBT_T_55, + Armor.ZBD_04A, + Armor.IFV_BMP_1, + + Unarmed.Transport_Ural_375, + Unarmed.Transport_UAZ_469, + Infantry.Soldier_AK, + + CV_1143_5_Admiral_Kuznetsov, + Bulk_cargo_ship_Yakushev, + Dry_cargo_ship_Ivanov, + Tanker_Elnya_160 + ] + }, + "USA 1955": { "country": "USA", "side": "blue", @@ -803,7 +855,7 @@ FACTIONS = { }, "France 1990": { - "country": "USA", + "country": "France", "side": "blue", "units":[ M_2000C, @@ -820,8 +872,7 @@ FACTIONS = { Unarmed.Transport_M818, Infantry.Infantry_M4, - AirDefence.AAA_Vulcan_M163, - AirDefence.SAM_Linebacker_M6, + AirDefence.SAM_Roland_ADS, CVN_74_John_C__Stennis, LHA_1_Tarawa, @@ -830,10 +881,12 @@ FACTIONS = { }, "Germany 1990": { - "country": "USA", + "country": "Germany", "side": "blue", "units":[ MiG_29G, + Tornado_IDS, + F_4E, KC_135, S_3B_Tanker, @@ -850,7 +903,7 @@ FACTIONS = { Infantry.Infantry_M4, AirDefence.SPAAA_Gepard, - AirDefence.SAM_Linebacker_M6, + AirDefence.SAM_Roland_ADS, CVN_74_John_C__Stennis, LHA_1_Tarawa, @@ -1153,7 +1206,7 @@ def _validate_db(): for country_units_list in FACTIONS.values(): if unit_type in country_units_list["units"]: did_find = True - assert did_find, "{} not in country list".format(unit_type) + print("WARN : {} not in country list".format(unit_type)) # check prices for unit_type in total_set: diff --git a/game/operation/operation.py b/game/operation/operation.py index 290b62bd..2e46711c 100644 --- a/game/operation/operation.py +++ b/game/operation/operation.py @@ -125,6 +125,13 @@ class Operation: # air support self.airsupportgen.generate(self.is_awacs_enabled) + + for cp in self.game.theater.controlpoints: + if not cp.captured: + self.airgen.generate_patrol_group(cp, self.current_mission.country(self.game.enemy_country)) + else: + self.airgen.generate_patrol_group(cp, self.current_mission.country(self.game.player_country)) + for i, tanker_type in enumerate(self.airsupportgen.generated_tankers): self.briefinggen.append_frequency("Tanker {} ({})".format(TANKER_CALLSIGNS[i], tanker_type), "{}X/{} MHz AM".format(97+i, 130+i)) diff --git a/gen/aircraft.py b/gen/aircraft.py index 48177504..eed3eea8 100644 --- a/gen/aircraft.py +++ b/gen/aircraft.py @@ -10,7 +10,7 @@ from dcs.mission import * from dcs.unitgroup import * from dcs.unittype import * from dcs.task import * -from dcs.terrain.terrain import NoParkingSlotError +from dcs.terrain.terrain import NoParkingSlotError, RunwayOccupiedError SPREAD_DISTANCE_FACTOR = 1, 2 ESCORT_ENGAGEMENT_MAX_DIST = 100000 @@ -125,10 +125,13 @@ class AircraftConflictGenerator: else: group.set_frequency(251.0) - def _generate_at_airport(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, airport: Airport = None) -> FlyingGroup: + def _generate_at_airport(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, airport: Airport = None, start_type = None) -> FlyingGroup: assert count > 0 assert unit is not None + if start_type is None: + start_type = self._start_type() + logging.info("airgen: {} for {} at {}".format(unit_type, side.id, airport)) return self.m.flight_group_from_airport( country=side, @@ -136,7 +139,7 @@ class AircraftConflictGenerator: aircraft_type=unit_type, airport=self.m.terrain.airport_by_id(airport.id), maintask=None, - start_type=self._start_type(), + start_type=start_type, group_size=count, parking_slots=None) @@ -293,6 +296,51 @@ class AircraftConflictGenerator: self.escort_targets.append((group, group.points.index(waypoint))) self._rtb_for(group, self.conflict.from_cp, at) + def generate_patrol_group(self, cp: ControlPoint, country): + + aircraft = dict({k:v for k,v in cp.base.aircraft.items() if k in [u for u in db.UNIT_BY_TASK[CAP]]}) + delta = random.randint(10, 20) + + for i in range(12): + if(len(aircraft.keys())) > 0: + print(aircraft.keys()) + type = random.choice(list(aircraft.keys())) + number = random.choice([2, 4]) + if(number > aircraft[type]): + del aircraft[type] + else: + aircraft[type] = aircraft[type] - number + + try: + group = self._generate_at_airport( + name=namegen.next_unit_name(country, type), + side=country, + unit_type=type, + count=number, + client_count=0, + airport=self.m.terrain.airport_by_id(cp.at.id), + start_type=StartType.Runway) + except RunwayOccupiedError: + group = self._generate_group( + name=namegen.next_unit_name(country, type), + side=country, + unit_type=type, + count=number, + client_count=0, + at=cp.position) + + patrol_alt = random.randint(3600, 7000) + + group.points[0].alt = patrol_alt + group.points[0].ETA = delta*60 + i*10*60 + + patrolled = [] + for ground_object in cp.ground_objects: + if not ground_object.group_id in patrolled: + group.add_waypoint(ground_object.position, patrol_alt) + patrolled.append(ground_object.group_id) + + def generate_ground_attack_strikegroup(self, strikegroup: db.PlaneDict, clients: db.PlaneDict, targets: typing.List[typing.Tuple[str, Point]], at: db.StartingPosition = None, escort=True): assert not escort or len(self.escort_targets) == 0 diff --git a/gen/triggergen.py b/gen/triggergen.py index 942a1157..753770ea 100644 --- a/gen/triggergen.py +++ b/gen/triggergen.py @@ -91,7 +91,7 @@ class TriggersGenerator: push_by_trigger.append(group) - if not group.units[0].is_human(): + """if not group.units[0].is_human(): regroup_heading = self.conflict.to_cp.position.heading_between_point(player_cp.position) pos1 = group.position.point_from_heading(regroup_heading, REGROUP_ZONE_DISTANCE) @@ -112,7 +112,7 @@ class TriggersGenerator: w2.tasks.append(switch_waypoint_task) group.points[3].tasks.append(Silence(False)) - group.add_trigger_action(SwitchWaypoint(to_waypoint=4)) + group.add_trigger_action(SwitchWaypoint(to_waypoint=4))""" push_trigger = TriggerOnce(Event.NoEvent, "Push trigger") @@ -187,7 +187,7 @@ class TriggersGenerator: self.mission.triggerrules.triggers.append(trigger_two) def generate(self, player_cp: ControlPoint, is_quick: bool, activation_trigger_radius: int, awacs_enabled: bool): - player_coalition = self.game.player_country == "USA" and "blue" or "red" + player_coalition = self.game.player_country in ["USA", "France", "Germany", "Uk"] and "blue" or "red" enemy_coalition = player_coalition == "blue" and "red" or "blue" self.mission.coalition[player_coalition].bullseye = {"x": self.conflict.position.x, diff --git a/qt_ui/widgets/map/QMapControlPoint.py b/qt_ui/widgets/map/QMapControlPoint.py index 4e987e25..6a6a1ca3 100644 --- a/qt_ui/widgets/map/QMapControlPoint.py +++ b/qt_ui/widgets/map/QMapControlPoint.py @@ -81,14 +81,14 @@ class QMapControlPoint(QGraphicsRectItem): @property def brush_color(self)->QColor: - if self.parent.game.player_country == "USA": + if self.parent.game.player_country in ["USA", "France", "Germany", "UK"]: return self.model.captured and CONST.COLORS["blue"] or CONST.COLORS["red"] else: return self.model.captured and CONST.COLORS["red"] or CONST.COLORS["blue"] @property def pen_color(self) -> QColor: - if self.parent.game.player_country == "USA": + if self.parent.game.player_country in ["USA", "France", "Germany", "UK"]: return self.model.captured and CONST.COLORS["dark_blue"] or CONST.COLORS["bright_red"] else: return self.model.captured and CONST.COLORS["bright_red"] or CONST.COLORS["dark_blue"] diff --git a/qt_ui/widgets/map/QMapGroundObject.py b/qt_ui/widgets/map/QMapGroundObject.py index 92c15c0e..a6b5fd1f 100644 --- a/qt_ui/widgets/map/QMapGroundObject.py +++ b/qt_ui/widgets/map/QMapGroundObject.py @@ -25,7 +25,7 @@ class QMapGroundObject(QGraphicsRectItem): tooltip = "" for unit in units.keys(): tooltip = tooltip + str(unit) + "x" + str(units[unit]) + "\n" - self.setToolTip(tooltip + str(model.groups[0].id) + str(model.groups[0].name)) + self.setToolTip(tooltip[:-1]) else: self.setToolTip(cp.name + "'s " + self.model.category) diff --git a/ui/__init__.py b/ui/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ui/basemenu.py b/ui/basemenu.py deleted file mode 100644 index b82aaa67..00000000 --- a/ui/basemenu.py +++ /dev/null @@ -1,116 +0,0 @@ -from ui.eventmenu import * - -from game.game import * -from .styles import STYLES - - -class BaseMenu(Menu): - bought_amount_labels = None # type: typing.Collection[Label] - budget_label = None # type: Label - - def __init__(self, window: Window, parent, game: Game, cp: ControlPoint): - super(BaseMenu, self).__init__(window, parent, game) - self.cp = cp - self.base = cp.base - self.frame = window.right_pane - self.event = self.game.units_delivery_event(cp) - self.bought_amount_labels = {} - - def display(self): - self.window.clear_right_pane() - units = { - CAP: db.find_unittype(CAP, self.game.player_name), - Embarking: db.find_unittype(Embarking, self.game.player_name), - AirDefence: db.find_unittype(AirDefence, self.game.player_name), - CAS: db.find_unittype(CAS, self.game.player_name), - PinpointStrike: db.find_unittype(PinpointStrike, self.game.player_name), - } - - # Header - head = Frame(self.frame, **STYLES["header"]) - head.grid(row=0, column=0, columnspan=99, sticky=NSEW, pady=5) - Label(head, text=self.cp.name, **STYLES["title"]).grid(row=0, column=0, sticky=NW+S) - units_title = "{}/{}/{}".format(self.cp.base.total_planes, self.cp.base.total_armor, self.cp.base.total_aa) - Label(head, text=units_title, **STYLES["strong-grey"]).grid(row=0, column=1, sticky=NE+S) - - self.budget_label = Label(self.frame, text="Budget: {}m".format(self.game.budget), **STYLES["widget"]) - self.budget_label.grid(row=1, sticky=W) - Button(self.frame, text="Back", command=self.dismiss, **STYLES["btn-primary"]).grid(column=9, row=1, padx=(0,15), pady=(0,5)) - - tasks = list(units.keys()) - tasks_per_column = 3 - - column = 0 - for i, tasks_column in [(i, tasks[idx:idx+tasks_per_column]) for i, idx in enumerate(range(0, len(tasks), tasks_per_column))]: - row = 2 - - def purchase_row(unit_type, unit_price): - nonlocal row - nonlocal column - - existing_units = self.base.total_units_of_type(unit_type) - scheduled_units = self.event.units.get(unit_type, 0) - - Label(self.frame, text="{}".format(db.unit_type_name(unit_type)), **STYLES["widget"]).grid(row=row, column=column, sticky=W) - - label = Label(self.frame, text="({}) ".format(existing_units), **STYLES["widget"]) - label.grid(column=column + 1, row=row) - self.bought_amount_labels[unit_type] = label - - Label(self.frame, text="{}m".format(unit_price), **STYLES["widget"]).grid(column=column + 2, row=row, sticky=E) - Button(self.frame, text="+", command=self.buy(unit_type), **STYLES["btn-primary"]).grid(column=column + 3, row=row, padx=(10,0)) - Button(self.frame, text="-", command=self.sell(unit_type), **STYLES["btn-warning"]).grid(column=column + 4, row=row, padx=(10,5)) - row += 1 - - for task_type in tasks_column: - Label(self.frame, text="{}".format(db.task_name(task_type)), **STYLES["strong"]).grid(row=row, column=column, columnspan=5, sticky=NSEW) - row += 1 - - units_column = list(set(units[task_type])) - units_column.sort(key=lambda x: db.PRICES[x]) - for unit_type in units_column: - purchase_row(unit_type, db.PRICES[unit_type]) - - column += 5 - - def dismiss(self): - if sum([x for x in self.event.units.values()]) == 0: - self.game.units_delivery_remove(self.event) - - super(BaseMenu, self).dismiss() - - def _update_count_label(self, unit_type: UnitType): - self.bought_amount_labels[unit_type]["text"] = "({}{})".format( - self.cp.base.total_units_of_type(unit_type), - unit_type in self.event.units and ", bought {}".format(self.event.units[unit_type]) or "" - ) - - self.budget_label["text"] = "Budget: {}m".format(self.game.budget) - - def buy(self, unit_type): - def action(): - price = db.PRICES[unit_type] - if self.game.budget >= price: - self.event.deliver({unit_type: 1}) - self.game.budget -= price - - self._update_count_label(unit_type) - - return action - - def sell(self, unit_type): - def action(): - if self.event.units.get(unit_type, 0) > 0: - price = db.PRICES[unit_type] - self.game.budget += price - self.event.units[unit_type] = self.event.units[unit_type] - 1 - if self.event.units[unit_type] == 0: - del self.event.units[unit_type] - elif self.base.total_units_of_type(unit_type) > 0: - price = db.PRICES[unit_type] - self.game.budget += price - self.base.commit_losses({unit_type: 1}) - - self._update_count_label(unit_type) - - return action \ No newline at end of file diff --git a/ui/configurationmenu.py b/ui/configurationmenu.py deleted file mode 100644 index 84b552d3..00000000 --- a/ui/configurationmenu.py +++ /dev/null @@ -1,125 +0,0 @@ -import webbrowser - -from tkinter import * -from tkinter.ttk import * -from .styles import STYLES - -from userdata.logging import ShowLogsException -from ui.window import * - - -class ConfigurationMenu(Menu): - def __init__(self, window: Window, parent, game: Game): - super(ConfigurationMenu, self).__init__(window, parent, game) - self.frame = window.right_pane - self.player_skill_var = StringVar() - self.player_skill_var.set(self.game.settings.player_skill) - - self.enemy_skill_var = StringVar() - self.enemy_skill_var.set(self.game.settings.enemy_skill) - - self.enemy_vehicle_var = StringVar() - self.enemy_vehicle_var.set(self.game.settings.enemy_vehicle_skill) - - self.map_coalition_visibility_var = StringVar() - self.map_coalition_visibility_var.set(self.game.settings.map_coalition_visibility) - - self.labels_var = StringVar() - self.labels_var.set(self.game.settings.labels) - - self.takeoff_var = BooleanVar() - self.takeoff_var.set(self.game.settings.only_player_takeoff) - - self.night_var = BooleanVar() - self.night_var.set(self.game.settings.night_disabled) - - self.cold_start_var = BooleanVar() - self.cold_start_var.set(self.game.settings.cold_start) - - def dismiss(self): - self.game.settings.player_skill = self.player_skill_var.get() - self.game.settings.enemy_skill = self.enemy_skill_var.get() - self.game.settings.enemy_vehicle_skill = self.enemy_vehicle_var.get() - self.game.settings.map_coalition_visibility = self.map_coalition_visibility_var.get() - self.game.settings.labels = self.labels_var.get() - self.game.settings.only_player_takeoff = self.takeoff_var.get() - self.game.settings.night_disabled = self.night_var.get() - self.game.settings.cold_start = self.cold_start_var.get() - super(ConfigurationMenu, self).dismiss() - - def display(self): - self.window.clear_right_pane() - - # Header - head = Frame(self.frame, **STYLES["header"]) - head.grid(row=0, column=0, sticky=NSEW) - head.grid_columnconfigure(0, weight=100) - Label(head, text="Configuration", **STYLES["title"]).grid(row=0, sticky=W) - Button(head, text="Back", command=self.dismiss, **STYLES["btn-primary"]).grid(row=0, column=1, sticky=E) - - # Body - body = Frame(self.frame, **STYLES["body"]) - body.grid(row=1, column=0, sticky=NSEW) - row = 0 - - Label(body, text="Player coalition skill", **STYLES["widget"]).grid(row=row, column=0, sticky=W) - p_skill = OptionMenu(body, self.player_skill_var, "Average", "Good", "High", "Excellent") - p_skill.grid(row=row, column=1, sticky=E, pady=5) - p_skill.configure(**STYLES["btn-primary"]) - row += 1 - - Label(body, text="Enemy coalition skill", **STYLES["widget"]).grid(row=row, column=0, sticky=W) - e_skill = OptionMenu(body, self.enemy_skill_var, "Average", "Good", "High", "Excellent") - e_skill.grid(row=row, column=1, sticky=E) - e_skill.configure(**STYLES["btn-primary"]) - row += 1 - - Label(body, text="Enemy AA and vehicle skill", **STYLES["widget"]).grid(row=row, column=0, sticky=W) - e_skill = OptionMenu(body, self.enemy_vehicle_var, "Average", "Good", "High", "Excellent") - e_skill.grid(row=row, column=1, sticky=E) - e_skill.configure(**STYLES["btn-primary"]) - row += 1 - - Label(body, text="F10 Map Coalition Visibility", **STYLES["widget"]).grid(row=row, column=0, sticky=W) - map_vis = OptionMenu(body, self.map_coalition_visibility_var, "All Units", "Allied Units", "Own Aircraft", "None") - map_vis.grid(row=row, column=1, sticky=E) - map_vis.configure(**STYLES["btn-primary"]) - row += 1 - - Label(body, text="In Game Labels", **STYLES["widget"]).grid(row=row, column=0, sticky=W) - g_labels = OptionMenu(body, self.labels_var, "Full", "Abbreviated", "Dot Only", "Off") - g_labels.grid(row=row, column=1, sticky=E) - g_labels.configure(**STYLES["btn-primary"]) - row += 1 - - Label(body, text="Aircraft cold start", **STYLES["widget"]).grid(row=row, column=0, sticky=W) - Checkbutton(body, variable=self.cold_start_var, **STYLES["radiobutton"]).grid(row=row, column=1, sticky=E) - row += 1 - - Label(body, text="Takeoff only for player group", **STYLES["widget"]).grid(row=row, column=0, sticky=W) - Checkbutton(body, variable=self.takeoff_var, **STYLES["radiobutton"]).grid(row=row, column=1, sticky=E) - row += 1 - - Label(body, text="Disable night missions", **STYLES["widget"]).grid(row=row, column=0, sticky=W) - Checkbutton(body, variable=self.night_var, **STYLES["radiobutton"]).grid(row=row, column=1, sticky=E) - row += 1 - - Label(body, text="Contributors: ", **STYLES["strong"]).grid(row=row, column=0, columnspan=2, sticky=EW) - row += 1 - - Label(body, text="shdwp - author, maintainer", **STYLES["widget"]).grid(row=row, column=0, sticky=W) - Button(body, text="[github]", command=lambda: webbrowser.open_new_tab("http://github.com/shdwp"), **STYLES["widget"]).grid(row=row, column=1, sticky=E) - row += 1 - - Label(body, text="Khopa - contributions", **STYLES["widget"]).grid(row=row, column=0, sticky=W) - Button(body, text="[github]", command=lambda: webbrowser.open_new_tab("http://github.com/Khopa"), **STYLES["widget"]).grid(row=row, column=1, sticky=E) - row += 1 - - Button(body, text="Display logs", command=self.display_logs, **STYLES["btn-primary"]).grid(row=row, column=0, pady=5) - Button(body, text="Cheat +200m", command=self.cheat_money, **STYLES["btn-danger"]).grid(row=row, column=1) - - def display_logs(self): - raise ShowLogsException() - - def cheat_money(self): - self.game.budget += 200 diff --git a/ui/corruptedsavemenu.py b/ui/corruptedsavemenu.py deleted file mode 100644 index 40f05e69..00000000 --- a/ui/corruptedsavemenu.py +++ /dev/null @@ -1,18 +0,0 @@ -from tkinter import * -from tkinter.ttk import * -from .styles import STYLES - -from ui.window import * - - -class CorruptedSaveMenu(Menu): - def __init__(self, window: Window): - super(CorruptedSaveMenu, self).__init__(window, None, None) - self.frame = window.right_pane - - def display(self): - self.window.clear_right_pane() - - 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 Saved Games\\DCS directory.", **STYLES["widget"]).grid(row=2, column=0) diff --git a/ui/eventmenu.py b/ui/eventmenu.py deleted file mode 100644 index d7a55189..00000000 --- a/ui/eventmenu.py +++ /dev/null @@ -1,219 +0,0 @@ -from dcs.helicopters import helicopter_map - -from ui.eventresultsmenu import * - -from game import * -from game.event import * -from .styles import STYLES, RED - - -class EventMenu(Menu): - scramble_entries = None # type: typing.Dict[typing.Type[Task], typing.Dict[typing.Type[UnitType], typing.Tuple[Entry, Entry]]] - ca_slot_entry = None # type: Entry - error_label = None # type: Label - awacs = None # type: IntVar - - def __init__(self, window: Window, parent, game: Game, event: event.Event): - super(EventMenu, self).__init__(window, parent, game) - - self.event = event - self.scramble_entries = {k: {} for k in self.event.tasks} - - if self.event.attacker_name == self.game.player_name: - self.base = self.event.departure_cp.base - else: - self.base = self.event.to_cp.base - - self.frame = self.window.right_pane - self.awacs = IntVar() - - def display(self): - self.window.clear_right_pane() - row = 0 - - def header(text, style="strong"): - nonlocal row - head = Frame(self.frame, **STYLES["header"]) - head.grid(row=row, column=0, sticky=N+EW, columnspan=5) - Label(head, text=text, **STYLES[style]).grid() - row += 1 - - def label(text, _row=None, _column=None, columnspan=None, sticky=None): - nonlocal row - new_label = Label(self.frame, text=text, **STYLES["widget"]) - new_label.grid(row=_row and _row or row, column=_column and _column or 0, columnspan=columnspan, sticky=sticky) - - if _row is None: - row += 1 - - return new_label - - def scrable_row(task_type, unit_type, unit_count, client_slots: bool): - nonlocal row - Label(self.frame, text="{} ({})".format(db.unit_type_name(unit_type), unit_count), **STYLES["widget"]).grid(row=row, sticky=W) - - scramble_entry = Entry(self.frame, width=2) - scramble_entry.grid(column=1, row=row, sticky=E, padx=5) - scramble_entry.insert(0, "0") - Button(self.frame, text="+", command=self.scramble_half(task_type, unit_type), **STYLES["btn-primary"]).grid(column=2, row=row) - - if client_slots: - client_entry = Entry(self.frame, width=2) - client_entry.grid(column=3, row=row, sticky=E, padx=5) - client_entry.insert(0, "0") - Button(self.frame, text="+", command=self.client_one(task_type, unit_type), **STYLES["btn-primary"]).grid(column=4, row=row) - else: - client_entry = None - - self.scramble_entries[task_type][unit_type] = scramble_entry, client_entry - - row += 1 - - # Header - header("Mission Menu", "title") - - # Mission Description - Label(self.frame, text="{}".format(self.event), **STYLES["mission-preview"]).grid(row=row, column=0, columnspan=5, sticky=S+EW, padx=5, pady=5) - row += 1 - - Label(self.frame, text="Amount", **STYLES["widget"]).grid(row=row, column=1, columnspan=2) - Label(self.frame, text="Client slots", **STYLES["widget"]).grid(row=row, column=3, columnspan=2) - row += 1 - - for flight_task in self.event.tasks: - header("{}:".format(self.event.flight_name(flight_task))) - if flight_task == PinpointStrike: - if not self.base.armor: - label("No units") - for t, c in self.base.armor.items(): - scrable_row(flight_task, t, c, client_slots=False) - else: - if not self.base.aircraft: - label("No units") - for t, c in self.base.aircraft.items(): - scrable_row(flight_task, t, c, client_slots=True) - - header("Support:") - # Options - awacs_enabled = self.game.budget >= AWACS_BUDGET_COST and NORMAL or DISABLED - Label(self.frame, text="AWACS ({}m)".format(AWACS_BUDGET_COST), **STYLES["widget"]).grid(row=row, column=0, sticky=W, pady=5) - Checkbutton(self.frame, var=self.awacs, state=awacs_enabled, **STYLES["radiobutton"]).grid(row=row, column=4, sticky=E) - row += 1 - - Label(self.frame, text="Combined Arms Slots", **STYLES["widget"]).grid(row=row, sticky=W) - self.ca_slot_entry = Entry(self.frame, width=2) - self.ca_slot_entry.insert(0, "0") - self.ca_slot_entry.grid(column=3, row=row, sticky=E, padx=5) - Button(self.frame, text="+", command=self.add_ca_slot, **STYLES["btn-primary"]).grid(column=4, row=row, padx=5, sticky=W) - row += 1 - - header("Ready?") - self.error_label = label("", columnspan=4) - self.error_label["fg"] = RED - Button(self.frame, text="Commit", command=self.start, **STYLES["btn-primary"]).grid(column=0, row=row, sticky=E, padx=5, pady=(10,10)) - Button(self.frame, text="Back", command=self.dismiss, **STYLES["btn-warning"]).grid(column=3, row=row, sticky=E, padx=5, pady=(10,10)) - row += 1 - - def scramble_half(self, task: typing.Type[UnitType], unit_type: UnitType) -> typing.Callable: - def action(): - entry = self.scramble_entries[task][unit_type][0] # type: Entry - value = entry.get() - - total_units = self.base.total_units_of_type(unit_type) - - amount = int(value and value or "0") - entry.delete(0, END) - entry.insert(0, str(amount + int(math.ceil(total_units/2)))) - - return action - - def add_ca_slot(self): - value = self.ca_slot_entry.get() - amount = int(value and value or "0") - self.ca_slot_entry.delete(0, END) - self.ca_slot_entry.insert(0, str(amount+1)) - - def client_one(self, task: typing.Type[Task], unit_type: UnitType) -> typing.Callable: - def action(): - entry = self.scramble_entries[task][unit_type][1] # type: Entry - value = entry.get() - amount = int(value and value or "0") - entry.delete(0, END) - entry.insert(0, str(amount+1)) - return action - - def start(self): - if self.awacs.get() == 1: - self.event.is_awacs_enabled = True - self.game.awacs_expense_commit() - else: - self.event.is_awacs_enabled = False - - ca_slot_entry_value = self.ca_slot_entry.get() - try: - ca_slots = int(ca_slot_entry_value and ca_slot_entry_value or "0") - except: - ca_slots = 0 - self.event.ca_slots = ca_slots - - flights = {k: {} for k in self.event.tasks} # type: db.TaskForceDict - units_scramble_counts = {} # type: typing.Dict[typing.Type[UnitType], int] - tasks_scramble_counts = {} # type: typing.Dict[typing.Type[Task], int] - tasks_clients_counts = {} # type: typing.Dict[typing.Type[Task], int] - - def dampen_count(for_task: typing.Type[Task], unit_type: typing.Type[UnitType], count: int) -> int: - nonlocal units_scramble_counts - total_count = self.base.total_units_of_type(unit_type) - - total_scrambled = units_scramble_counts.get(unit_type, 0) - dampened_value = count if count + total_scrambled < total_count else total_count - total_scrambled - units_scramble_counts[unit_type] = units_scramble_counts.get(unit_type, 0) + dampened_value - - return dampened_value - - for task_type, dict in self.scramble_entries.items(): - for unit_type, (count_entry, clients_entry) in dict.items(): - try: - count = int(count_entry.get()) - except: - count = 0 - - try: - clients_count = int(clients_entry and clients_entry.get() or 0) - except: - clients_count = 0 - - dampened_count = dampen_count(task_type, unit_type, count) - tasks_clients_counts[task_type] = tasks_clients_counts.get(task_type, 0) + clients_count - tasks_scramble_counts[task_type] = tasks_scramble_counts.get(task_type, 0) + dampened_count - - flights[task_type][unit_type] = dampened_count, clients_count - - for task in self.event.ai_banned_tasks: - if tasks_clients_counts.get(task, 0) == 0 and tasks_scramble_counts.get(task, 0) > 0: - self.error_label["text"] = "Need at least one player in flight {}".format(self.event.flight_name(task)) - return - - for task in self.event.player_banned_tasks: - if tasks_clients_counts.get(task, 0) != 0: - self.error_label["text"] = "Players are not allowed on flight {}".format(self.event.flight_name(task)) - return - - if self.game.is_player_attack(self.event): - if isinstance(self.event, FrontlineAttackEvent) or isinstance(self.event, FrontlinePatrolEvent): - if self.event.from_cp.base.total_armor == 0: - self.error_label["text"] = "No ground vehicles available to attack!" - return - - self.event.player_attacking(flights) - else: - if isinstance(self.event, FrontlineAttackEvent) or isinstance(self.event, FrontlinePatrolEvent): - if self.event.to_cp.base.total_armor == 0: - self.error_label["text"] = "No ground vehicles available to defend!" - return - - self.event.player_defending(flights) - - self.game.initiate_event(self.event) - EventResultsMenu(self.window, self.parent, self.game, self.event).display() - diff --git a/ui/eventresultsmenu.py b/ui/eventresultsmenu.py deleted file mode 100644 index 38e2c160..00000000 --- a/ui/eventresultsmenu.py +++ /dev/null @@ -1,180 +0,0 @@ -from tkinter.ttk import * -from ui.window import * - -from game.game import * -from userdata.debriefing import * -from .styles import STYLES - - -class EventResultsMenu(Menu): - debriefing = None # type: Debriefing - player_losses = {} # type: typing.Dict[UnitType, int] - enemy_losses = {} # type: typing.Dict[UnitType, int] - - def __init__(self, window: Window, parent, game: Game, event: Event): - super(EventResultsMenu, self).__init__(window, parent, game) - self.frame = window.right_pane - self.frame.grid_rowconfigure(0, weight=0) - self.event = event - self.finished = False - - wait_for_debriefing(callback=self.process_debriefing) - - def display(self): - self.window.clear_right_pane() - - row = 0 - - def header(text, style="strong"): - nonlocal row - head = Frame(self.frame, **STYLES["header"]) - head.grid(row=row, column=0, sticky=N + EW, columnspan=2, pady=(0, 10)) - Label(head, text=text, **STYLES[style]).grid() - row += 1 - - def label(text, style="widget"): - nonlocal row - Label(self.frame, text=text, **STYLES[style]).grid(row=row, column=0, sticky=NW, columnspan=2) - row += 1 - - if not self.finished: - - header("You are clear for takeoff!") - - label("In DCS, open and play the mission:") - label("liberation_nextturn", "italic") - label("or") - label("liberation_nextturn_quick", "italic") - header("Then save the debriefing to the folder:") - label(debriefing_directory_location(), "italic") - header("Waiting for results...") - - pg = Progressbar(self.frame, orient="horizontal", length=200, mode="determinate") - pg.grid(row=row, column=0, columnspan=2, sticky=EW, pady=5, padx=5) - pg.start(10) - row += 1 - - """ - Label(self.frame, text="Cheat operation results: ", **STYLES["strong"]).grid(column=0, row=row, - columnspan=2, sticky=NSEW, - pady=5) - - row += 1 - Button(self.frame, text="full enemy losses", command=self.simulate_result(0, 1), - **STYLES["btn-warning"]).grid(column=0, row=row, padx=5, pady=5) - Button(self.frame, text="full player losses", command=self.simulate_result(1, 0), - **STYLES["btn-warning"]).grid(column=1, row=row, padx=5, pady=5) - row += 1 - Button(self.frame, text="some enemy losses", command=self.simulate_result(0, 0.8), - **STYLES["btn-warning"]).grid(column=0, row=row, padx=5, pady=5) - Button(self.frame, text="some player losses", command=self.simulate_result(0.8, 0), - **STYLES["btn-warning"]).grid(column=1, row=row, padx=5, pady=5) - row += 1 - """ - - else: - row = 0 - if self.event.is_successfull(self.debriefing): - header("Operation success", "title-green") - else: - header("Operation failed", "title-red") - - header("Player losses") - - for unit_type, count in self.player_losses.items(): - Label(self.frame, text=db.unit_type_name(unit_type), **STYLES["widget"]).grid(row=row) - Label(self.frame, text="{}".format(count), **STYLES["widget"]).grid(column=1, row=row) - row += 1 - - 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) - row += 1 - - for unit_type, count in self.enemy_losses.items(): - if count == 0: - continue - - Label(self.frame, text=db.unit_type_name(unit_type), **STYLES["widget"]).grid(row=row) - Label(self.frame, text="{}".format(count), **STYLES["widget"]).grid(column=1, row=row) - row += 1 - - Button(self.frame, text="Okay", command=self.dismiss, **STYLES["btn-primary"]).grid(columnspan=1, row=row) - row += 1 - - def process_debriefing(self, debriefing: Debriefing): - self.debriefing = debriefing - - debriefing.calculate_units(regular_mission=self.event.operation.regular_mission, - quick_mission=self.event.operation.quick_mission, - player_country=self.game.player_country, - enemy_country=self.game.enemy_country) - - self.game.finish_event(event=self.event, debriefing=debriefing) - self.game.pass_turn(ignored_cps=[self.event.to_cp, ]) - - self.finished = True - self.player_losses = debriefing.destroyed_units.get(self.game.player_country, {}) - self.enemy_losses = debriefing.destroyed_units.get(self.game.enemy_country, {}) - self.display() - - def simulate_result(self, player_factor: float, enemy_factor: float): - def action(): - debriefing = Debriefing({}, []) - - def count(country: Country) -> typing.Dict[UnitType, int]: - result = {} - for g in country.plane_group + country.vehicle_group + country.helicopter_group + country.ship_group: - group = g # type: Group - for unit in group.units: - unit_type = None - if isinstance(unit, Vehicle): - unit_type = vehicle_map[unit.type] - elif isinstance(unit, Ship): - unit_type = ship_map[unit.type] - else: - unit_type = unit.unit_type - - if unit_type in db.EXTRA_AA.values(): - continue - - result[unit_type] = result.get(unit_type, 0) + 1 - - return result - - player = self.event.operation.mission.country(self.game.player_country) - enemy = self.event.operation.mission.country(self.game.enemy_country) - - alive_player_units = count(player) - alive_enemy_units = count(enemy) - - destroyed_player_units = db.unitdict_restrict_count(alive_player_units, math.ceil( - sum(alive_player_units.values()) * player_factor)) - destroyed_enemy_units = db.unitdict_restrict_count(alive_enemy_units, math.ceil( - sum(alive_enemy_units.values()) * enemy_factor)) - - alive_player_units = {k: v - destroyed_player_units.get(k, 0) for k, v in alive_player_units.items()} - alive_enemy_units = {k: v - destroyed_enemy_units.get(k, 0) for k, v in alive_enemy_units.items()} - - debriefing.alive_units = { - enemy.name: alive_enemy_units, - player.name: alive_player_units, - } - - debriefing.destroyed_units = { - player.name: destroyed_player_units, - enemy.name: destroyed_enemy_units, - } - - self.finished = True - self.debriefing = debriefing - self.player_losses = debriefing.destroyed_units.get(self.game.player_country, {}) - self.enemy_losses = debriefing.destroyed_units.get(self.game.enemy_country, {}) - - self.game.finish_event(self.event, debriefing) - self.display() - self.game.pass_turn() - - return action diff --git a/ui/mainmenu.py b/ui/mainmenu.py deleted file mode 100644 index 53aadbff..00000000 --- a/ui/mainmenu.py +++ /dev/null @@ -1,58 +0,0 @@ -from game.game import * -from ui.basemenu import * -from ui.configurationmenu import * -from ui.overviewcanvas import * -from userdata import persistency -from .styles import STYLES - - -import tkinter as tk -from tkinter import ttk - - -class MainMenu(Menu): - basemenu = None # type: BaseMenu - - def __init__(self, window: Window, parent, game: Game): - super(MainMenu, self).__init__(window, parent, game) - - self.upd = OverviewCanvas(self.window.left_pane, self, game) - self.upd.update() - - self.frame = self.window.right_pane - self.frame.rowconfigure(0, weight=0) - self.frame.rowconfigure(1, weight=1) - - def display(self): - persistency.save_game(self.game) - self.window.clear_right_pane() - self.upd.update() - - header = Frame(self.frame, **STYLES["header"]) - header.grid(column=0, row=0, sticky=NSEW) - - def pass_turn(self): - self.game.pass_turn(no_action=True) - self.upd.update() - self.display() - - def configuration_menu(self): - ConfigurationMenu(self.window, self, self.game).display() - - def start_event(self, event) -> typing.Callable: - EventMenu(self.window, self, self.game, event).display() - - def go_cp(self, cp: ControlPoint): - if not cp.captured: - return - - if self.basemenu: - self.basemenu.dismiss() - self.basemenu = None - - self.basemenu = BaseMenu(self.window, self, self.game, cp) - self.basemenu.display() - - - - diff --git a/ui/newgamemenu.py b/ui/newgamemenu.py deleted file mode 100644 index de746f5b..00000000 --- a/ui/newgamemenu.py +++ /dev/null @@ -1,171 +0,0 @@ -import os -from tkinter import * -from tkinter.ttk import * - -from ui.window import * -from .styles import STYLES -from game import db - -class NewGameMenu(Menu): - selected_country = None # type: IntVar - selected_terrain = None # type: IntVar - sams = None - midgame = None - multiplier = None - - def __init__(self, window: Window, callback: typing.Callable): - super(NewGameMenu, self).__init__(window, None, None) - self.frame = window.right_pane - window.left_pane.configure(background="black") - self.callback = callback - - self.selected_country = IntVar() - self.selected_country.set(0) - - self.selected_terrain = IntVar() - self.selected_terrain.set(0) - - self.selected_time_period = StringVar() - - self.selected_blue_faction = StringVar() - self.selected_red_faction = StringVar() - - self.sams = BooleanVar() - self.sams.set(1) - - self.multiplier = StringVar() - self.multiplier.set("1") - - self.midgame = BooleanVar() - self.midgame.set(0) - - @property - def player_country_name(self): - if self.selected_country.get() == 0: - return self.selected_blue_faction.get() - else: - return self.selected_red_faction.get() - - @property - def enemy_country_name(self): - if self.selected_country.get() == 1: - return self.selected_blue_faction.get() - else: - return self.selected_red_faction.get() - - @property - def terrain_name(self) -> str: - if self.selected_terrain.get() == 0: - return "caucasus" - elif self.selected_terrain.get() == 1: - return "nevada" - else: - return "persiangulf" - - def display(self): - self.window.clear_right_pane() - - # Header - head = Frame(self.frame, **STYLES["header"]) - head.grid(row=0, column=0, sticky=NSEW) - Label(head, text="Start a new game", **STYLES["title"]).grid() - - # Body - body = Frame(self.frame, **STYLES["body"]) - body.grid(row=1, column=0, sticky=NSEW) - - # Side Selection - side = LabelFrame(body, text="Player Side", **STYLES["label-frame"]) - side.grid(row=2, column=0, sticky=NW, padx=5) - Radiobutton(side, variable=self.selected_country, value=0, **STYLES["radiobutton"]).grid(row=0, column=0, - sticky=W) - Label(side, text="BLUEFOR", **STYLES["widget"]).grid(row=0, column=1, sticky=W) - Radiobutton(side, variable=self.selected_country, value=1, **STYLES["radiobutton"]).grid(row=1, column=0, - sticky=W) - Label(side, text="REDFOR", **STYLES["widget"]).grid(row=1, column=1, sticky=W) - - # Country Selection - - blues = [c for c in db.FACTIONS if db.FACTIONS[c]["side"] == "blue"] - reds = [c for c in db.FACTIONS if db.FACTIONS[c]["side"] == "red"] - - factions = LabelFrame(body, text="Factions", **STYLES["label-frame"]) - factions.grid(row=0, column=0, sticky=NW, padx=5) - - Label(factions, text="Blue Faction", **STYLES["widget"]).grid(row=1, column=0, sticky=SE) - - self.selected_blue_faction.set(blues[0]) - blue_select = OptionMenu(factions, self.selected_blue_faction, *blues) - blue_select.configure(**STYLES["btn-primary"]) - blue_select.grid(row=1, column=1, sticky=W) - - self.selected_red_faction.set(reds[1]) - Label(factions, text="Red Faction", **STYLES["widget"]).grid(row=2, column=0, sticky=W) - red_select = OptionMenu(factions, self.selected_red_faction, *reds) - red_select.configure(**STYLES["btn-primary"]) - red_select.grid(row=2, column=1, sticky=W) - - # Terrain Selection - terrain = LabelFrame(body, text="Terrain", **STYLES["label-frame"]) - terrain.grid(row=0, column=1, sticky=N, padx=5) - - Radiobutton(terrain, variable=self.selected_terrain, value=0, **STYLES["radiobutton"]) \ - .grid(row=0, column=0, sticky=W) - Label(terrain, text="Caucasus", **STYLES["widget"]).grid(row=0, column=1, sticky=W) - self.create_label_image(terrain, "terrain_caucasus.gif").grid(row=0, column=2, padx=5) - - Radiobutton(terrain, variable=self.selected_terrain, value=1, **STYLES["radiobutton"]) \ - .grid(row=1, column=0, sticky=W) - Label(terrain, text="Nevada", **STYLES["widget"]).grid(row=1, column=1, sticky=W) - self.create_label_image(terrain, "terrain_nevada.gif").grid(row=1, column=2, padx=5) - - Radiobutton(terrain, variable=self.selected_terrain, value=2, **STYLES["radiobutton"]) \ - .grid(row=2, column=0, sticky=W) - Label(terrain, text="Persian Gulf", **STYLES["widget"]).grid(row=2, column=1, sticky=W) - self.create_label_image(terrain, "terrain_pg.gif").grid(row=2, column=2, padx=5) - - # Period selection - period = LabelFrame(body, text="Time Period", **STYLES["label-frame"]) - period.grid(row=0, column=2, sticky=N, padx=5) - - vals = list(db.TIME_PERIODS) - self.selected_time_period.set(vals[21]) - period_select = OptionMenu(period, self.selected_time_period, *vals) - period_select.configure(**STYLES["btn-primary"]) - period_select.grid(row=0, column=0, sticky=W) - #Label(terrain, text="Caucasus", **STYLES["widget"]).grid(row=0, column=1, sticky=W) - - # Misc Options - options = LabelFrame(body, text="Misc Options", **STYLES["label-frame"]) - options.grid(row=0, column=3, sticky=NE, padx=5) - - Checkbutton(options, variable=self.sams, **STYLES["radiobutton"]).grid(row=0, column=0, sticky=W) - Label(options, text="SAMs", **STYLES["widget"]).grid(row=0, column=1, sticky=W) - - Checkbutton(options, variable=self.midgame, **STYLES["radiobutton"]).grid(row=1, column=0, sticky=W) - Label(options, text="Mid Game", **STYLES["widget"]).grid(row=1, column=1, sticky=W) - - Label(options, text="Multiplier", **STYLES["widget"]).grid(row=2, column=0, sticky=W) - Entry(options, textvariable=self.multiplier).grid(row=2, column=1, sticky=W) - - # Footer with Proceed Button - footer = Frame(self.frame, **STYLES["header"]) - footer.grid(row=2, sticky=N + E + W) - Button(footer, text="Proceed", command=self.proceed, **STYLES["btn-primary"]).grid(row=0, column=0, sticky=SE, - padx=5, pady=5) - - @staticmethod - def create_label_image(parent, image): - im = PhotoImage(file=os.path.join("resources", "ui", image)) - label = Label(parent, image=im) - label.image = im - return label - - def proceed(self): - self.callback(self.player_country_name, - self.enemy_country_name, - self.terrain_name, - bool(self.sams.get()), - bool(self.midgame.get()), - float(self.multiplier.get()), - db.TIME_PERIODS[self.selected_time_period.get()]) diff --git a/ui/overviewcanvas.py b/ui/overviewcanvas.py deleted file mode 100644 index 01d305d4..00000000 --- a/ui/overviewcanvas.py +++ /dev/null @@ -1,675 +0,0 @@ -import os -import platform -from threading import Thread -from tkinter.ttk import * - -import pygame - -from theater.theatergroundobject import CATEGORY_MAP -from ui.styles import STYLES -from ui.window import * - - -EVENT_COLOR_ATTACK = (100, 100, 255) -EVENT_COLOR_DEFENSE = (255, 100, 100) - -RED = (255, 125, 125) -BRIGHT_RED = (200, 64, 64) -BLUE = (164, 164, 255) -DARK_BLUE = (45, 62, 80) -WHITE = (255, 255, 255) -GREEN = (128, 186, 128) -BRIGHT_GREEN = (64, 200, 64) -BLACK = (0, 0, 0) - -BACKGROUND = pygame.Color(0, 64, 64) -ANTIALIASING = True - -WIDTH = 1000 -HEIGHT = 700 -MAP_PADDING = 100 - -class OverviewCanvas: - mainmenu = None # type: ui.mainmenu.MainMenu - budget_label = None # type: Label - - started = None - ground_assets_icons = None # type: typing.Dict[str, pygame.Surface] - event_icons = None # type: typing.Dict[typing.Type, pygame.Surface] - selected_event_info = None # type: typing.Tuple[Event, typing.Tuple[int, int]] - frontline_vector_cache = None # type: typing.Dict[str, typing.Tuple[Point, int, int]] - - DAWN_ICON = None - DAY_ICON = None - DUSK_ICON = None - NIGHT_ICON = None - - def __init__(self, frame: Frame, parent, game: Game): - - self.parent = parent - self.game = game - - self.load_icons() - # Remove any previously existing pygame instance - pygame.quit() - - # Pygame objects - self.map = None - self.map_dusk_dawn = None - self.map_night = None - self.screen = None - self.surface: pygame.Surface = None - self.thread: Thread = None - self.clock = pygame.time.Clock() - self.expanded = True - - pygame.font.init() - self.font: pygame.font.SysFont = pygame.font.SysFont("arial", 15) - self.fontsmall: pygame.font.SysFont = pygame.font.SysFont("arial", 10) - self.ground_assets_icons = {} - - # Frontline are too heavy on performance to compute in realtime, so keep them in a cache - self.frontline_vector_cache = {} - - # Map state - self.redraw_required = True - self.zoom = 1 - self.scroll = [0, 0] - self.exited = False - - # Display options - self.display_ground_targets = BooleanVar(value=True) - self.display_forces = BooleanVar(value=True) - self.display_bases = BooleanVar(value=True) - self.display_road = BooleanVar(value=True) - self.display_rules = self.compute_display_rules() - - parent.window.tk.protocol("", self.on_close) - - self.wrapper = Frame(frame, **STYLES["frame-wrapper"]) - self.wrapper.grid(column=0, row=0, sticky=NSEW) # Adds grid - self.wrapper.pack(side=LEFT) # packs window to the left - - self.embed = Frame(self.wrapper, width=WIDTH, height=HEIGHT, borderwidth=2, **STYLES["frame-wrapper"]) - self.embed.grid(column=0, row=1, sticky=NSEW) # Adds grid - - self.options = Frame(self.wrapper, borderwidth=2, **STYLES["frame-wrapper"]) - self.options.grid(column=0, row=0, sticky=NSEW) - self.options.grid_columnconfigure(1, weight=1) - self.build_map_options_panel() - - self.init_sdl_layer() - self.init_sdl_thread() - - def build_map_options_panel(self): - col = 0 - Button(self.options, text="Configuration", command=self.parent.configuration_menu, - **{**STYLES["btn-primary"],**{ "pady": 4}}).grid(column=col, row=0, sticky=NW) - col += 1 - - money_icon = Label(self.options, image=self.MONEY_ICON, **STYLES["widget-big"]) - money_icon.grid(column=col, row=0, sticky=NE) - col += 1 - - self.current_budget = StringVar() - self.budget_label = Label(self.options, textvariable=self.current_budget, **STYLES["widget-big"]) - self.budget_label.grid(column=col, row=0, sticky=NW) - col += 1 - - self.daytime_icon = Label(self.options, image=self.DAWN_ICON, **STYLES["widget-big"]) - self.daytime_icon.grid(column=col, row=0, sticky=NE) - col += 1 - - self.current_turn = StringVar() - self.turn_label = Label(self.options, textvariable=self.current_turn, **STYLES["widget-big"]) - self.turn_label.grid(column=col, row=0, sticky=NE) - col += 1 - - Button(self.options, text="Pass turn", command=self.parent.pass_turn, - **{**STYLES["btn-primary"],**{ "pady": 4}}).grid(column=col, row=0, sticky=NE) - col += 1 - - - def map_size_toggle(self): - if self.expanded: - self.embed.configure(width=0) - self.options.configure(width=0) - self.expanded = False - else: - self.embed.configure(width=WIDTH) - self.options.configure(width=WIDTH) - self.expanded = True - - def on_close(self): - self.exited = True - if self.thread is not None: - self.thread.join() - - def load_icons(self): - if self.DAWN_ICON is None : - self.DAWN_ICON = PhotoImage(file="./resources/ui/daytime/dawn.png") - self.DAY_ICON = PhotoImage(file="./resources/ui/daytime/day.png") - self.DUSK_ICON = PhotoImage(file="./resources/ui/daytime/dusk.png") - self.NIGHT_ICON = PhotoImage(file="./resources/ui/daytime/night.png") - self.MONEY_ICON = PhotoImage(file="./resources/ui/misc/money_icon.png") - self.ORDNANCE_ICON = PhotoImage(file="./resources/ui/misc/ordnance_icon.png") - - def init_sdl_layer(self): - # Setup pygame to run in tk frame - os.environ['SDL_WINDOWID'] = str(self.embed.winfo_id()) - if platform.system == "Windows": - os.environ['SDL_VIDEODRIVER'] = 'windib' - - # Create pygame 'screen' - self.screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.DOUBLEBUF | pygame.HWSURFACE) - self.screen.fill(pygame.Color(*BLACK)) - - # Load icons resources - self.ground_assets_icons = {} - self.ground_assets_icons["target"] = pygame.image.load(os.path.join("resources", "ui", "ground_assets", "target.png")) - self.ground_assets_icons["cleared"] = pygame.image.load(os.path.join("resources", "ui", "ground_assets", "cleared.png")) - for category in CATEGORY_MAP.keys(): - self.ground_assets_icons[category] = pygame.image.load(os.path.join("resources", "ui", "ground_assets", category + ".png")) - - self.event_icons = {} - for category, image in {BaseAttackEvent: "capture", - FrontlinePatrolEvent: "attack", - FrontlineAttackEvent: "attack", - InfantryTransportEvent: "infantry", - InsurgentAttackEvent: "insurgent_attack", - ConvoyStrikeEvent: "convoy", - InterceptEvent: "air_intercept", - NavalInterceptEvent: "naval_intercept", - StrikeEvent: "strike", - UnitsDeliveryEvent: "delivery"}.items(): - self.event_icons[category] = pygame.image.load(os.path.join("resources", "ui", "events", image + ".png")) - - - # Load the map image - self.map = pygame.image.load(os.path.join("resources", self.game.theater.overview_image)).convert() - pygame.draw.rect(self.map, BLACK, (0, 0, self.map.get_width(), self.map.get_height()), 10) - pygame.draw.rect(self.map, WHITE, (0, 0, self.map.get_width(), self.map.get_height()), 5) - - # Generate map for night and dusk/dawn - self.map_night = self.map.copy() - self.map_night.fill((100, 100, 110, 128), special_flags=pygame.BLEND_MULT) - - self.map_dusk_dawn = self.map.copy() - self.map_dusk_dawn.fill((220, 150, 125, 128), special_flags=pygame.BLEND_MULT) - - - # Create surfaces for drawing - self.surface = pygame.Surface((self.map.get_width() + MAP_PADDING * 2, - self.map.get_height() + MAP_PADDING * 2)) - self.surface.set_alpha(None) - self.overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA) - - # Init pygame display - pygame.display.init() - pygame.display.update() - - def init_sdl_thread(self): - if OverviewCanvas.started is not None: - OverviewCanvas.started.exited = True - self.thread = Thread(target=self.sdl_thread) - self.thread.start() - OverviewCanvas.started = self - print("Started SDL app") - - def sdl_thread(self): - self.redraw_required = True - i = 0 - while not self.exited: - self.clock.tick(30) - self.updateOptions() - self.draw() - i += 1 - if i == 600: - self.frontline_vector_cache = {} - i = 0 - print("Stopped SDL app") - - def draw(self): - try: - self.embed.winfo_ismapped() - self.embed.winfo_manager() - except: - self.exited = True - - right_down = False - left_down = False - - # Detect changes on display rules - r = self.compute_display_rules() - if r != self.display_rules: - self.display_rules = r - self.redraw_required = True - - for event in pygame.event.get(): - if event.type == pygame.MOUSEMOTION: - self.redraw_required = True - elif event.type == pygame.MOUSEBUTTONDOWN: - - """ - Due to rendering not really supporting the zoom this is currently disabled. - @TODO: improve rendering so zoom would actually make sense - - # Scroll wheel""" - if event.button == 4: - self.zoom += 0.25 - self.redraw_required = True - elif event.button == 5: - self.zoom -= 0.25 - self.redraw_required = True - """""" - - if event.button == 3: - right_down = True - pygame.mouse.get_rel() - if event.button == 1: - left_down = True - self.redraw_required = True - elif event.type == pygame.KEYDOWN: - if event.key == pygame.K_F2: - self.display_bases.set(not self.display_bases.get()) - elif event.key == pygame.K_F3: - self.display_forces.set(not self.display_forces.get()) - elif event.key == pygame.K_F4: - self.display_ground_targets.set(not self.display_ground_targets.get()) - elif event.key == pygame.K_F5: - self.display_road.set(not self.display_road.get()) - - # If Right click pressed - if pygame.mouse.get_pressed()[2] == 1 and not right_down: - scr = pygame.mouse.get_rel() - self.scroll[0] += scr[0] - self.scroll[1] += scr[1] - self.redraw_required = True - - if self.zoom <= 0.5: - self.zoom = 0.5 - elif self.zoom > 3: - self.zoom = 3 - - if self.redraw_required: - # Fill - self.screen.fill(BACKGROUND) - self.surface.fill(BACKGROUND) - self.overlay.fill(pygame.Color(0, 0, 0, 0)) - - # Surface - cursor_pos = pygame.mouse.get_pos() - cursor_pos = ( - cursor_pos[0] / self.zoom - self.scroll[0], cursor_pos[1] / self.zoom - self.scroll[1]) - self.draw_map(self.surface, self.overlay, cursor_pos, [left_down, right_down]) - - # Scaling - scaled = pygame.transform.scale(self.surface, ( - int(self.surface.get_width() * self.zoom), int(self.surface.get_height() * self.zoom))) - self.screen.blit(scaled, (self.scroll[0]*self.zoom, self.scroll[1]*self.zoom)) - self.screen.blit(self.overlay, (0, 0)) - - pygame.display.flip() - - self.redraw_required = False - - - def draw_map(self, surface: pygame.Surface, overlay: pygame.Surface, mouse_pos: (int, int), mouse_down: [bool, bool]): - - daytime = self.game.current_turn_daytime - if daytime == "day": - self.surface.blit(self.map, (MAP_PADDING, MAP_PADDING)) - elif daytime == "night": - self.surface.blit(self.map_night, (MAP_PADDING, MAP_PADDING)) - else: - self.surface.blit(self.map_dusk_dawn, (MAP_PADDING, MAP_PADDING)) - - - # Display zoom level on overlay - zoom_lvl = self.font.render(" x " + str(self.zoom) + " ", ANTIALIASING, WHITE, DARK_BLUE) - self.overlay.blit(zoom_lvl, (self.overlay.get_width()-zoom_lvl.get_width()-5, - self.overlay.get_height()-zoom_lvl.get_height()-5)) - - # Debug - # pygame.draw.rect(surface, (255, 0, 255), (mouse_pos[0], mouse_pos[1], 5, 5), 2) - - for cp in self.game.theater.controlpoints: - coords = self._transform_point(cp.position) - - if self.display_road.get(): - for connected_cp in cp.connected_points: - connected_coords = self._transform_point(connected_cp.position) - if connected_cp.captured != cp.captured: - color = self._enemy_color() - elif connected_cp.captured and cp.captured: - color = self._player_color() - else: - color = BLACK - - pygame.draw.line(surface, color, coords, connected_coords, 2) - - if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp): - frontline = self._frontline_vector(cp, connected_cp) - if not frontline: - continue - - frontline_pos, heading, distance = frontline - - if distance < 10000: - frontline_pos = frontline_pos.point_from_heading(heading + 180, 5000) - distance = 10000 - - start_coords = self._transform_point(frontline_pos, treshold=10) - end_coords = self._transform_point(frontline_pos.point_from_heading(heading, distance), - treshold=60) - - pygame.draw.line(surface, color, start_coords, end_coords, 4) - - if self.display_ground_targets.get(): - for ground_object in cp.ground_objects: - pygame.draw.line(surface, RED, self._transform_point(cp.position), self._transform_point(ground_object.position), 1) - self.draw_ground_object(ground_object, surface, cp.captured, mouse_pos) - - if self.display_bases.get(): - mouse_down = self.draw_bases(mouse_pos, mouse_down) - - mouse_down = self.draw_events(self.surface, mouse_pos, mouse_down) - - if mouse_down[0]: - self.selected_event_info = None - - def draw_bases(self, mouse_pos, mouse_down): - for cp in self.game.theater.controlpoints: - coords = self._transform_point(cp.position) - radius = 12 * math.pow(cp.importance, 1) - radius_m = max(radius * cp.base.strength - 2, 0) - - if cp.captured: - color = self._player_color() - else: - color = self._enemy_color() - - pygame.draw.circle(self.surface, BLACK, (int(coords[0]), int(coords[1])), int(radius)) - pygame.draw.circle(self.surface, color, (int(coords[0]), int(coords[1])), int(radius_m)) - - label = self.font.render(cp.name, ANTIALIASING, (225, 225, 225), BLACK) - labelHover = self.font.render(cp.name, ANTIALIASING, (255, 255, 255), (128, 186, 128)) - labelClick = self.font.render(cp.name, ANTIALIASING, (255, 255, 255), (122, 122, 255)) - - point = coords[0] - label.get_width() / 2 + 1, coords[1] + 1 - rect = pygame.Rect(*point, label.get_width(), label.get_height()) - - if rect.collidepoint(*mouse_pos): - if mouse_down[0]: - self.surface.blit(labelClick, (coords[0] - label.get_width() / 2 + 1, coords[1] + 1)) - self._selected_cp(cp) - mouse_down[0] = False - else: - self.surface.blit(labelHover, (coords[0] - label.get_width() / 2 + 1, coords[1] + 1)) - - self.draw_base_info(self.overlay, cp, (0, 0)) - if self.selected_event_info: - if self._cp_available_for_selected_event(cp): - pygame.draw.line(self.surface, WHITE, rect.center, self.selected_event_info[1]) - - else: - self.surface.blit(label, (coords[0] - label.get_width() / 2 + 1, coords[1] + 1)) - - if self.display_forces.get(): - units_title = " {} / {} / {} ".format(cp.base.total_planes, cp.base.total_armor, cp.base.total_aa) - label2 = self.fontsmall.render(units_title, ANTIALIASING, color, (30, 30, 30)) - self.surface.blit(label2, (coords[0] - label2.get_width() / 2, coords[1] + label.get_height() + 1)) - - return mouse_down - - def draw_base_info(self, surface: pygame.Surface, control_point: ControlPoint, pos): - title = self.font.render(control_point.name, ANTIALIASING, BLACK, GREEN) - hp = self.font.render("Strength : ", ANTIALIASING, (225, 225, 225), BLACK) - - armor_txt = "ARMOR > " - for key, value in control_point.base.armor.items(): - armor_txt += key.id + " x " + str(value) + " | " - armor = self.font.render(armor_txt, ANTIALIASING, (225, 225, 225), BLACK) - - aircraft_txt = "AIRCRAFT > " - for key, value in control_point.base.aircraft.items(): - aircraft_txt += key.id + " x " + str(value) + " | " - aircraft = self.font.render(aircraft_txt, ANTIALIASING, (225, 225, 225), BLACK) - - aa_txt = "AA/SAM > " - for key, value in control_point.base.aa.items(): - aa_txt += key.id + " x " + str(value) + " | " - aa = self.font.render(aa_txt, ANTIALIASING, (225, 225, 225), BLACK) - - lineheight = title.get_height() - w = max([max([a.get_width() for a in [title, armor, aircraft, aa]]), 150]) - h = 5 * lineheight + 4 * 5 - - # Draw frame - pygame.draw.rect(surface, GREEN, (pos[0], pos[1], w + 8, h + 8)) - pygame.draw.rect(surface, BLACK, (pos[0] + 2, pos[1] + 2, w + 4, h + 4)) - pygame.draw.rect(surface, GREEN, (pos[0] + 2, pos[1], w + 4, lineheight + 4)) - - # Title - surface.blit(title, (pos[0] + 4, 4 + pos[1])) - surface.blit(hp, (pos[0] + 4, 4 + pos[1] + lineheight + 5)) - - # Draw gauge - pygame.draw.rect(surface, WHITE, - (pos[0] + hp.get_width() + 3, 4 + pos[1] + lineheight + 5, 54, lineheight)) - pygame.draw.rect(surface, BRIGHT_RED, - (pos[0] + hp.get_width() + 5, 4 + pos[1] + lineheight + 5 + 2, 50, lineheight - 4)) - pygame.draw.rect(surface, BRIGHT_GREEN, ( - pos[0] + hp.get_width() + 5, 4 + pos[1] + lineheight + 5 + 2, 50 * control_point.base.strength, lineheight - 4)) - - # Text - surface.blit(armor, (pos[0] + 4, 4 + pos[1] + lineheight * 2 + 10)) - surface.blit(aircraft, (pos[0] + 4, 4 + pos[1] + lineheight * 3 + 15)) - surface.blit(aa, (pos[0] + 4, 4 + pos[1] + lineheight * 4 + 20)) - - def draw_selected_event_info(self): - event = self.selected_event_info[0] - title = self.font.render(str(event), ANTIALIASING, BLACK, GREEN) - hint = self.font.render("Select CP to depart from.", ANTIALIASING, (225, 225, 225), BLACK) - - w = hint.get_width() - h = title.get_height() + hint.get_height() + 20 - - pos = self.overlay.get_width() / 2 - w / 2, self.overlay.get_height() - h - - # Draw frame - pygame.draw.rect(self.overlay, GREEN, (pos[0], pos[1], w + 8, h + 8)) - pygame.draw.rect(self.overlay, BLACK, (pos[0] + 2, pos[1] + 2, w + 4, h + 4)) - pygame.draw.rect(self.overlay, GREEN, (pos[0] + 2, pos[1], w + 4, title.get_height() + 4)) - - # Title - self.overlay.blit(title, (pos[0] + 4, 4 + pos[1])) - self.overlay.blit(hint, (pos[0] + 4, 4 + pos[1] + title.get_height() + 5)) - - def draw_ground_object(self, ground_object: TheaterGroundObject, surface: pygame.Surface, captured: bool, mouse_pos): - if captured: - color = self._player_color() - else: - color = self._enemy_color() - - x, y = self._transform_point(ground_object.position) - rect = pygame.Rect(x, y, 16, 16) - - if ground_object.is_dead or captured: - surface.blit(self.ground_assets_icons["cleared"], (x, y)) - else: - if ground_object.category in self.ground_assets_icons.keys(): - icon = self.ground_assets_icons[ground_object.category] - else: - icon = self.ground_assets_icons["target"] - surface.blit(icon, (x, y)) - - if rect.collidepoint(*mouse_pos): - self.draw_ground_object_info(ground_object, (x, y), color, surface) - - def draw_ground_object_info(self, ground_object: TheaterGroundObject, pos, color, surface: pygame.Surface): - lb = self.font.render(str(ground_object), ANTIALIASING, color, BLACK) - surface.blit(lb, (pos[0] + 18, pos[1])) - - def draw_events(self, surface: pygame.Surface, mouse_pos, mouse_down): - occupied_rects = [] - for cp in self.game.theater.controlpoints: - point = self._transform_point(cp.position) - occupied_rects.append(pygame.Rect(point[0] - 16, point[1] - 16, 32, 48)) - - def _location_to_rect(location: Point) -> pygame.Rect: - nonlocal occupied_rects - point = self._transform_point(location) - rect = pygame.Rect(point[0] - 16, point[1] - 16, 32, 32) - - i = 0 - while True: - result = True - for occupied_rect in occupied_rects: - if rect.colliderect(occupied_rect): - i += 1 - - if i % 2: - rect.y += occupied_rect.height - else: - rect.x += occupied_rect.width - - result = False - break - if result: - break - - occupied_rects.append(rect) - return rect - - def _events_priority_key(event: Event) -> int: - priority_list = [InfantryTransportEvent, StrikeEvent, BaseAttackEvent, UnitsDeliveryEvent] - if type(event) not in priority_list: - return 0 - else: - return priority_list.index(type(event)) + 1 - - events = self.game.events - events.sort(key=_events_priority_key, reverse=True) - - label_to_draw = None - for event in self.game.events: - location = event.location - if type(event) in [FrontlineAttackEvent, FrontlinePatrolEvent, ConvoyStrikeEvent]: - location = self._frontline_center(event.from_cp, event.to_cp) - - rect = _location_to_rect(location) - pygame.draw.rect(surface, EVENT_COLOR_ATTACK if event.is_player_attacking else EVENT_COLOR_DEFENSE, rect) - self.surface.blit(self.event_icons[event.__class__], rect.topleft) - - if rect.collidepoint(*mouse_pos) or self.selected_event_info == (event, rect.center): - if not label_to_draw: - label_to_draw = self.font.render(str(event), ANTIALIASING, WHITE, BLACK), rect.center - - if rect.collidepoint(*mouse_pos): - if mouse_down[0]: - self.selected_event_info = event, rect.center - mouse_down[0] = False - - if label_to_draw: - surface.blit(*label_to_draw) - - if self.selected_event_info: - self.draw_selected_event_info() - - return mouse_down - - def _selected_cp(self, cp): - if self.selected_event_info: - if self. _cp_available_for_selected_event(cp): - event = self.selected_event_info[0] - event.departure_cp = cp - - self.selected_event_info = None - self.parent.start_event(event) - else: - return - else: - self.parent.go_cp(cp) - - def _transform_point(self, p: Point, treshold=30) -> (int, int): - point_a = list(self.game.theater.reference_points.keys())[0] - point_a_img = self.game.theater.reference_points[point_a] - - point_b = list(self.game.theater.reference_points.keys())[1] - point_b_img = self.game.theater.reference_points[point_b] - - Y_dist = point_a_img[0] - point_b_img[0] - lon_dist = point_a[1] - point_b[1] - - X_dist = point_a_img[1] - point_b_img[1] - lat_dist = point_b[0] - point_a[0] - - Y_scale = float(Y_dist) / float(lon_dist) - X_scale = float(X_dist) / float(lat_dist) - - # --- - Y_offset = p.x - point_a[0] - X_offset = p.y - point_a[1] - - X = point_b_img[1] + X_offset * X_scale - Y = point_a_img[0] - Y_offset * Y_scale - - X += MAP_PADDING - Y += MAP_PADDING - - return X > treshold and X or treshold, Y > treshold and Y or treshold - - def _frontline_vector(self, from_cp: ControlPoint, to_cp: ControlPoint): - # Cache mechanism to avoid performing frontline vector computation on every frame - key = str(from_cp.id) + "_" + str(to_cp.id) - if key in self.frontline_vector_cache: - return self.frontline_vector_cache[key] - else: - frontline = Conflict.frontline_vector(from_cp, to_cp, self.game.theater) - self.frontline_vector_cache[key] = frontline - return frontline - - def _frontline_center(self, from_cp: ControlPoint, to_cp: ControlPoint) -> typing.Optional[Point]: - frontline_vector = self._frontline_vector(from_cp, to_cp) - if frontline_vector: - return frontline_vector[0].point_from_heading(frontline_vector[1], frontline_vector[2]/2) - else: - return None - - def _cp_available_for_selected_event(self, cp: ControlPoint) -> bool: - event = self.selected_event_info[0] - return event.is_departure_available_from(cp) - - def _player_color(self): - return self.game.player_country == "USA" and BLUE or RED - - def _enemy_color(self): - return self.game.player_country == "USA" and RED or BLUE - - def update(self): - self.redraw_required = True - self.draw() - - def compute_display_rules(self): - return sum([1 if a.get() else 0 for a in [self.display_forces, self.display_road, self.display_bases, self.display_ground_targets]]) - - def display(self, cp: ControlPoint): - def action(_): - return self.parent.go_cp(cp) - - return action - - def updateOptions(self): - self.current_turn.set("Turn : {} - {}".format(self.game.turn, self.game.current_day.strftime("%d %b %Y"))) - self.current_budget.set("{}M $ (+{}M $)".format(self.game.budget, self.game.budget_reward_amount)) - - daytime = self.game.current_turn_daytime - if daytime == "dawn": - self.daytime_icon.configure(image=self.DAWN_ICON) - elif daytime == "day": - self.daytime_icon.configure(image=self.DAY_ICON) - elif daytime == "dusk": - self.daytime_icon.configure(image=self.DUSK_ICON) - elif daytime == "night": - self.daytime_icon.configure(image=self.NIGHT_ICON) \ No newline at end of file diff --git a/ui/styles.py b/ui/styles.py deleted file mode 100644 index bc4561b7..00000000 --- a/ui/styles.py +++ /dev/null @@ -1,52 +0,0 @@ -# Style for UI - -# Padding -PADDING_X = 5 -PADDING_Y = 5 - -# Colors -FG_COLOR = "white" -FG_COLOR_LIGHT = "#dddddd" -BG_COLOR = "#4E5760" -GREEN = "#699245" -YELLOW = "#BF9A46" -RED = "#D0232E" -BG_TITLE_COLOR = "#2D3E50" -BG_SUBTITLE_COLOR = "#3E4F61" - -# Fonts -FONT_FAMILY = "Trebuchet MS" -DEFAULT_FONT = (FONT_FAMILY, 8) -FONT_BIG = (FONT_FAMILY, 12) -ITALIC = (FONT_FAMILY, 8, "italic") -BOLD_FONT = (FONT_FAMILY, 10, "bold italic") -TITLE_FONT = (FONT_FAMILY, 16, "bold italic") - -# List of styles -STYLES = {} -STYLES["label-frame"] = {"font": BOLD_FONT, "bg": BG_COLOR, "fg": FG_COLOR} -STYLES["frame-wrapper"] = {"bg": BG_COLOR, "relief":"sunken"} - -STYLES["body"] = {"bg": BG_COLOR, "padx": 10, "pady": 10} -STYLES["strong"] = {"font": BOLD_FONT, "bg": BG_TITLE_COLOR, "fg": FG_COLOR} -STYLES["substrong"] = {"font": BOLD_FONT, "bg": BG_SUBTITLE_COLOR, "fg": FG_COLOR} -STYLES["supstrong"] = {"font": BOLD_FONT, "bg": RED, "fg": FG_COLOR} -STYLES["strong-grey"] = {"font": BOLD_FONT, "bg": BG_TITLE_COLOR, "fg": FG_COLOR_LIGHT} - -STYLES["mission-preview"] = {"font": BOLD_FONT, "bg": YELLOW, "fg": FG_COLOR} - -STYLES["widget"] = {"bg": BG_COLOR, "fg": FG_COLOR, "padx": PADDING_X, "pady": PADDING_Y, "font": DEFAULT_FONT} -STYLES["widget-big"] = {"bg": BG_COLOR, "fg": FG_COLOR, "padx": PADDING_X, "pady": PADDING_Y, "font": FONT_BIG, "relief": "ridge", "borderwidth": 1, "highlightcolor": "white"} -STYLES["italic"] = {"bg": BG_COLOR, "fg": FG_COLOR, "padx": PADDING_X, "pady": PADDING_Y, "font": ITALIC} -STYLES["radiobutton"] = {"bg": BG_COLOR, "fg": "black", "padx": PADDING_X, "pady": PADDING_Y, "font": DEFAULT_FONT, - "activebackground": BG_COLOR, "highlightbackground": BG_COLOR, "selectcolor": "white"} -STYLES["title"] = {"bg": BG_TITLE_COLOR, "fg": FG_COLOR, "padx": PADDING_X, "pady": PADDING_Y, "font": TITLE_FONT} -STYLES["title-green"] = {"bg": GREEN, "fg": FG_COLOR, "padx": PADDING_X, "pady": PADDING_Y, "font": TITLE_FONT} -STYLES["title-red"] = {"bg": RED, "fg": FG_COLOR, "padx": PADDING_X, "pady": PADDING_Y, "font": TITLE_FONT} -STYLES["header"] = {"bg": BG_TITLE_COLOR} -STYLES["subheader"] = {"bg": BG_SUBTITLE_COLOR} - -STYLES["btn-primary"] = {"bg": GREEN, "fg": FG_COLOR, "padx": PADDING_X, "pady": 2, "font": DEFAULT_FONT} -STYLES["btn-primary-big"] = {"bg": GREEN, "fg": FG_COLOR, "padx": PADDING_X, "pady": 2, "font": FONT_BIG} -STYLES["btn-danger"] = {"bg": RED, "fg": FG_COLOR, "padx": PADDING_X, "pady": 2, "font": DEFAULT_FONT} -STYLES["btn-warning"] = {"bg": YELLOW, "fg": FG_COLOR, "padx": PADDING_X, "pady": 2, "font": DEFAULT_FONT} diff --git a/ui/window.py b/ui/window.py deleted file mode 100644 index 3e470dfd..00000000 --- a/ui/window.py +++ /dev/null @@ -1,164 +0,0 @@ -from tkinter import * -from tkinter import Menu as TkMenu -from tkinter import messagebox - -from .styles import BG_COLOR,BG_TITLE_COLOR -from game.game import * -from theater import persiangulf, nevada, caucasus, start_generator -from userdata import logging as logging_module - -import sys -import webbrowser - - -class Window: - - image = None - left_pane = None # type: Frame - right_pane = None # type: Frame - - def __init__(self): - self.tk = Tk() - self.tk.title("DCS Liberation") - self.tk.iconbitmap("resources/icon.ico") - self.tk.resizable(False, False) - self.tk.grid_columnconfigure(0, weight=1) - self.tk.grid_rowconfigure(0, weight=1) - - self.frame = None - self.right_pane = None - self.left_pane = None - self.build() - - menubar = TkMenu(self.tk) - filemenu = TkMenu(menubar, tearoff=0) - filemenu.add_command(label="New Game", command=lambda: self.new_game_confirm()) - filemenu.add_separator() - filemenu.add_command(label="Exit", command=lambda: self.exit()) - menubar.add_cascade(label="File", menu=filemenu) - - helpmenu = TkMenu(menubar, tearoff=0) - helpmenu.add_command(label="Online Manual", command=lambda: webbrowser.open_new_tab("https://github.com/shdwp/dcs_liberation/wiki/Manual")) - helpmenu.add_command(label="Troubleshooting Guide", command=lambda: webbrowser.open_new_tab("https://github.com/shdwp/dcs_liberation/wiki/Troubleshooting")) - helpmenu.add_command(label="Modding Guide", command=lambda: webbrowser.open_new_tab("https://github.com/shdwp/dcs_liberation/wiki/Modding-tutorial")) - helpmenu.add_separator() - helpmenu.add_command(label="Contribute", command=lambda: webbrowser.open_new_tab("https://github.com/shdwp/dcs_liberation")) - helpmenu.add_command(label="Forum Thread", command=lambda: webbrowser.open_new_tab("https://forums.eagle.ru/showthread.php?t=214834")) - helpmenu.add_command(label="Report an issue", command=self.report_issue) - menubar.add_cascade(label="Help", menu=helpmenu) - - self.tk.config(menu=menubar) - self.tk.focus() - - - def build(self): - self.frame = Frame(self.tk, bg=BG_COLOR) - self.frame.grid(column=0, row=0, sticky=NSEW) - self.frame.grid_columnconfigure(0) - self.frame.grid_columnconfigure(1) - - self.frame.grid_columnconfigure(0, weight=0) - self.frame.grid_columnconfigure(1, weight=1) - self.frame.grid_rowconfigure(0, weight=1) - - self.left_pane = Frame(self.frame, bg=BG_TITLE_COLOR) - self.left_pane.grid(row=0, column=0, sticky=NSEW) - self.right_pane = Frame(self.frame, bg=BG_COLOR) - self.right_pane.grid(row=0, column=1, sticky=NSEW) - - def clear_right_pane(self): - for i in range(100): - self.right_pane.grid_columnconfigure(1, weight=0) - self.right_pane.grid_rowconfigure(1, weight=0) - - for x in self.right_pane.winfo_children(): - x.grid_remove() - - def clear(self): - def clear_recursive(x, n=50): - if n < 0: - return - for y in x.winfo_children(): - clear_recursive(y, n-1) - x.grid_forget() - - clear_recursive(self.frame, 50) - self.left_pane.grid_remove() - self.right_pane.grid_remove() - self.build() - - def start_new_game(self, player_name: str, enemy_name: str, terrain: str, sams: bool, midgame: bool, multiplier: float, period:datetime): - - player_country = db.FACTIONS[player_name]["country"] - enemy_country = db.FACTIONS[enemy_name]["country"] - - if terrain == "persiangulf": - conflicttheater = persiangulf.PersianGulfTheater() - elif terrain == "nevada": - conflicttheater = nevada.NevadaTheater() - else: - conflicttheater = caucasus.CaucasusTheater() - - if midgame: - for i in range(0, int(len(conflicttheater.controlpoints) / 2)): - conflicttheater.controlpoints[i].captured = True - - start_generator.generate_inital_units(conflicttheater, enemy_name, sams, multiplier) - game = Game(player_name=player_name, - enemy_name=enemy_name, - theater=conflicttheater, - start_date=period) - start_generator.generate_groundobjects(conflicttheater, game) - game.budget = int(game.budget * multiplier) - game.settings.multiplier = multiplier - game.settings.sams = sams - game.settings.version = logging_module.version_string() - - if midgame: - game.budget = game.budget * 4 * len(list(conflicttheater.conflicts())) - - self.proceed_to_main_menu(game) - - def proceed_to_main_menu(self, game: Game): - from ui.mainmenu import MainMenu - self.clear() - m = MainMenu(self, None, game) - m.display() - - def proceed_to_new_game_menu(self): - from ui.newgamemenu import NewGameMenu - self.clear() - new_game_menu = NewGameMenu(self, self.start_new_game) - new_game_menu.display() - - def new_game_confirm(self): - result = messagebox.askquestion("Start a new game", "Are you sure you want to start a new game ? Your current campaign will be overriden and there is no going back !", icon='warning') - if result == 'yes': - self.proceed_to_new_game_menu() - else: - pass - - def report_issue(self): - raise logging_module.ShowLogsException() - - def exit(self): - self.tk.destroy() - sys.exit(0) - - def run(self): - self.tk.mainloop() - - -class Menu: - parent = None # type: Menu - - def __init__(self, window: Window, parent, game: Game): - self.window = window - self.parent = parent - self.game = game - - def dismiss(self): - self.parent.display() - - def display(self): - pass