diff --git a/__init__.py b/__init__.py index cd71579c..927f9d98 100755 --- a/__init__.py +++ b/__init__.py @@ -65,6 +65,7 @@ try: else: proceed_to_main_menu(game) except Exception as e: + print(e) ui.corruptedsavemenu.CorruptedSaveMenu(w).display() w.run() diff --git a/icon.ico b/icon.ico new file mode 100644 index 00000000..733e3be9 Binary files /dev/null and b/icon.ico differ diff --git a/resources/ui/terrain_caucasus.png b/resources/ui/terrain_caucasus.png new file mode 100644 index 00000000..d27ee1ce Binary files /dev/null and b/resources/ui/terrain_caucasus.png differ diff --git a/resources/ui/terrain_nevada.png b/resources/ui/terrain_nevada.png new file mode 100644 index 00000000..682b5644 Binary files /dev/null and b/resources/ui/terrain_nevada.png differ diff --git a/resources/ui/terrain_pg.png b/resources/ui/terrain_pg.png new file mode 100644 index 00000000..d8ecc453 Binary files /dev/null and b/resources/ui/terrain_pg.png differ diff --git a/ui/basemenu.py b/ui/basemenu.py index 8cc15640..1e2e86f2 100644 --- a/ui/basemenu.py +++ b/ui/basemenu.py @@ -1,6 +1,7 @@ from ui.eventmenu import * from game.game import * +from .styles import STYLES class BaseMenu(Menu): @@ -9,7 +10,6 @@ class BaseMenu(Menu): 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 @@ -26,15 +26,15 @@ class BaseMenu(Menu): 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))).grid(row=row, sticky=W) + Label(self.frame, text="{}".format(db.unit_type_name(unit_type)), **STYLES["widget"]).grid(row=row, sticky=W) - label = Label(self.frame, text="({})".format(existing_units)) + label = Label(self.frame, text="({})".format(existing_units), **STYLES["widget"]) label.grid(column=1, row=row) self.bought_amount_labels[unit_type] = label - Label(self.frame, text="{}m".format(unit_price)).grid(column=2, row=row) - Button(self.frame, text="+", command=self.buy(unit_type)).grid(column=3, row=row) - Button(self.frame, text="-", command=self.sell(unit_type)).grid(column=4, row=row) + Label(self.frame, text="{}m".format(unit_price), **STYLES["widget"]).grid(column=2, row=row, sticky=E) + Button(self.frame, text="+", command=self.buy(unit_type), **STYLES["btn-primary"]).grid(column=3, row=row, padx=(10,0)) + Button(self.frame, text="-", command=self.sell(unit_type), **STYLES["btn-warning"]).grid(column=4, row=row, padx=(10,5)) row += 1 units = { @@ -45,13 +45,21 @@ class BaseMenu(Menu): AirDefence: db.find_unittype(AirDefence, self.game.player), } - self.budget_label = Label(self.frame, text="Budget: {}m".format(self.game.budget)) + # Header + head = Frame(self.frame, **STYLES["header"]) + head.grid(row=row, column=0, columnspan=5, 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) + row += 1 + + self.budget_label = Label(self.frame, text="Budget: {}m".format(self.game.budget), **STYLES["widget"]) self.budget_label.grid(row=row, sticky=W) - Button(self.frame, text="Back", command=self.dismiss).grid(column=4, row=row) + Button(self.frame, text="Back", command=self.dismiss, **STYLES["btn-primary"]).grid(column=4, row=row, padx=(0,15), pady=(0,5)) row += 1 for task_type, units in units.items(): - Label(self.frame, text="{}".format(db.task_name(task_type))).grid(row=row, columnspan=5); row += 1 + Label(self.frame, text="{}".format(db.task_name(task_type)), **STYLES["strong"]).grid(row=row, columnspan=5, sticky=NSEW); row += 1 units = list(set(units)) units.sort(key=lambda x: db.PRICES[x]) diff --git a/ui/configurationmenu.py b/ui/configurationmenu.py index 7add0536..a44a5b05 100644 --- a/ui/configurationmenu.py +++ b/ui/configurationmenu.py @@ -1,5 +1,6 @@ from tkinter import * from tkinter.ttk import * +from .styles import STYLES from ui.window import * @@ -34,22 +35,36 @@ class ConfigurationMenu(Menu): def display(self): self.window.clear_right_pane() - Label(self.frame, text="Player coalition skill").grid(row=0, column=0) - Label(self.frame, text="Enemy coalition skill").grid(row=1, column=0) + # Header + head = Frame(self.frame, **STYLES["header"]) + head.grid(row=0, column=0, columnspan=2, sticky=NSEW) + Label(head, text="Configuration", **STYLES["title"]).grid() - OptionMenu(self.frame, self.player_skill_var, "Average", "Good", "High", "Excellent").grid(row=0, column=1) - OptionMenu(self.frame, self.enemy_skill_var, "Average", "Good", "High", "Excellent").grid(row=1, column=1) + # Body + body = Frame(self.frame, **STYLES["body"]) + body.grid(row=1, column=0, sticky=NSEW) - Label(self.frame, text="Aircraft cold start").grid(row=2, column=0) - Label(self.frame, text="Takeoff only for player group").grid(row=3, column=0) - Label(self.frame, text="Disable night missions").grid(row=4, column=0) + Label(body, text="Player coalition skill", **STYLES["widget"]).grid(row=0, column=0, sticky=W) + Label(body, text="Enemy coalition skill", **STYLES["widget"]).grid(row=1, column=0, sticky=W) - Checkbutton(self.frame, variable=self.cold_start_var).grid(row=2, column=1) - Checkbutton(self.frame, variable=self.takeoff_var).grid(row=3, column=1) - Checkbutton(self.frame, variable=self.night_var).grid(row=4, column=1) + p_skill = OptionMenu(body, self.player_skill_var, "Average", "Good", "High", "Excellent") + p_skill.grid(row=0, column=1, sticky=E, pady=5) + p_skill.configure(**STYLES["btn-primary"]) - Button(self.frame, text="Back", command=self.dismiss).grid(row=5, column=0, columnspan=1) - Button(self.frame, text="Cheat +200m", command=self.cheat_money).grid(row=6, column=1) + e_skill = OptionMenu(body, self.enemy_skill_var, "Average", "Good", "High", "Excellent") + e_skill.grid(row=1, column=1, sticky=E) + e_skill.configure(**STYLES["btn-primary"]) + + Label(body, text="Aircraft cold start", **STYLES["widget"]).grid(row=2, column=0, sticky=W) + Label(body, text="Takeoff only for player group", **STYLES["widget"]).grid(row=3, column=0, sticky=W) + Label(body, text="Disable night missions", **STYLES["widget"]).grid(row=4, column=0, sticky=W) + + Checkbutton(body, variable=self.cold_start_var, **STYLES["radiobutton"]).grid(row=2, column=1, sticky=E) + Checkbutton(body, variable=self.takeoff_var, **STYLES["radiobutton"]).grid(row=3, column=1, sticky=E) + Checkbutton(body, variable=self.night_var, **STYLES["radiobutton"]).grid(row=4, column=1, sticky=E) + + Button(body, text="Back", command=self.dismiss, **STYLES["btn-primary"]).grid(row=5, column=1, sticky=E, pady=30) + Button(body, text="Cheat +200m", command=self.cheat_money, **STYLES["btn-danger"]).grid(row=6, column=1) def cheat_money(self): self.game.budget += 200 diff --git a/ui/corruptedsavemenu.py b/ui/corruptedsavemenu.py index 04a85fa0..85264011 100644 --- a/ui/corruptedsavemenu.py +++ b/ui/corruptedsavemenu.py @@ -1,5 +1,6 @@ from tkinter import * from tkinter.ttk import * +from .styles import STYLES from ui.window import * @@ -12,6 +13,6 @@ class CorruptedSaveMenu(Menu): def display(self): self.window.clear_right_pane() - Label(text="Your save game was corrupted!").grid(row=0, column=0) - Label(text="Please restore it by replacing \"liberation_save\" file with \"liberation_save_tmp\" to restore last saved copy.").grid(row=1, column=0) - Label(text="You can find those files under user DCS directory.").grid(row=2, column=0) + Label(text="Your save game 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) diff --git a/ui/eventmenu.py b/ui/eventmenu.py index 9d352d57..26ae3f3d 100644 --- a/ui/eventmenu.py +++ b/ui/eventmenu.py @@ -4,6 +4,7 @@ from ui.eventresultsmenu import * from game import * from game.event import * +from .styles import STYLES UNITTYPES_FOR_EVENTS = { @@ -44,39 +45,46 @@ class EventMenu(Menu): 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, sticky=None): nonlocal row - Label(self.frame, text=text).grid(row=_row and _row or row, column=_column and _column or 0, sticky=sticky) + Label(self.frame, text=text, **STYLES["widget"]).grid(row=_row and _row or row, column=_column and _column or 0, sticky=sticky) if _row is None: row += 1 def scrable_row(unit_type, unit_count): nonlocal row - Label(self.frame, text="{} ({})".format(db.unit_type_name(unit_type), unit_count)).grid(row=row, sticky=W) + 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=W) + scramble_entry.grid(column=1, row=row, sticky=E, padx=5) scramble_entry.insert(0, "0") self.aircraft_scramble_entries[unit_type] = scramble_entry - Button(self.frame, text="+", command=self.scramble_half(True, unit_type)).grid(column=2, row=row) + Button(self.frame, text="+", command=self.scramble_half(True, unit_type), **STYLES["btn-primary"]).grid(column=2, row=row) client_entry = Entry(self.frame, width=2) - client_entry.grid(column=3, row=row, sticky=E) + client_entry.grid(column=3, row=row, sticky=E, padx=5) client_entry.insert(0, "0") self.aircraft_client_entries[unit_type] = client_entry - Button(self.frame, text="+", command=self.client_one(unit_type)).grid(column=4, row=row) + Button(self.frame, text="+", command=self.client_one(unit_type), **STYLES["btn-primary"]).grid(column=4, row=row) row += 1 def scramble_armor_row(unit_type, unit_count): nonlocal row - Label(self.frame, text="{} ({})".format(db.unit_type_name(unit_type), unit_count)).grid(row=row, sticky=W) + 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.insert(0, "0") - scramble_entry.grid(column=1, row=row) + scramble_entry.grid(column=1, row=row, sticky=E, padx=5) self.armor_scramble_entries[unit_type] = scramble_entry - Button(self.frame, text="+", command=self.scramble_half(False, unit_type)).grid(column=2, row=row) + Button(self.frame, text="+", command=self.scramble_half(False, unit_type),**STYLES["btn-primary"]).grid(column=2, row=row) row += 1 @@ -84,21 +92,18 @@ class EventMenu(Menu): if threat_descr: threat_descr = "Approx. {}".format(threat_descr) - Label(self.frame, text="{}. {}".format(self.event, threat_descr)).grid(row=row, column=0, columnspan=5) + # Header + header("Mission Menu", "title") + + # Mission Description + Label(self.frame, text="{}. {}".format(self.event, threat_descr), **STYLES["mission-preview"]).grid(row=row, column=0, columnspan=5, sticky=S+EW, padx=5, pady=5) row += 1 - Button(self.frame, text="Commit", command=self.start).grid(column=3, row=row, sticky=E) - Button(self.frame, text="Back", command=self.dismiss).grid(column=4, row=row, sticky=E) - - awacs_enabled = self.game.budget >= AWACS_BUDGET_COST and NORMAL or DISABLED - Checkbutton(self.frame, text="AWACS ({}m)".format(AWACS_BUDGET_COST), var=self.awacs, state=awacs_enabled).grid(row=row, column=0, sticky=W) - row += 1 - - label("Aircraft") + header("Aircraft :") if self.base.aircraft: - Label(self.frame, text="Amount").grid(row=row, column=1, columnspan=2) - Label(self.frame, text="Client slots").grid(row=row, column=3, columnspan=2) + 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 filter_to = UNITTYPES_FOR_EVENTS[self.event.__class__] @@ -114,16 +119,29 @@ class EventMenu(Menu): if not self.base.total_planes: label("None", sticky=W) - label("Armor") + header("Armor :") + armor_counter = 0 for unit_type, count in self.base.armor.items(): if filter_to and db.unit_task(unit_type) not in filter_to: continue - scramble_armor_row(unit_type, count) + armor_counter += 1 - if not self.base.total_armor: + if not self.base.total_armor or armor_counter == 0: label("None", sticky=W) + header("Support :") + # Options + awacs_enabled = self.game.budget >= AWACS_BUDGET_COST and NORMAL or DISABLED + Checkbutton(self.frame, var=self.awacs, state=awacs_enabled, **STYLES["radiobutton"]).grid(row=row, column=0, sticky=E) + Label(self.frame, text="AWACS ({}m)".format(AWACS_BUDGET_COST), **STYLES["widget"]).grid(row=row, column=3, sticky=W, padx=5, pady=5) + row += 1 + + header("Ready ?") + 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 _scrambled_aircraft_count(self, unit_type: UnitType) -> int: value = self.aircraft_scramble_entries[unit_type].get() if value and int(value) > 0: diff --git a/ui/eventresultsmenu.py b/ui/eventresultsmenu.py index 7bfe1662..16db01d2 100644 --- a/ui/eventresultsmenu.py +++ b/ui/eventresultsmenu.py @@ -3,6 +3,7 @@ from ui.window import * from game.game import * from userdata.debriefing import * +from .styles import STYLES class EventResultsMenu(Menu): @@ -13,6 +14,7 @@ class EventResultsMenu(Menu): 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 @@ -21,46 +23,82 @@ class EventResultsMenu(Menu): 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: - Label(self.frame, text="Play the mission and save debriefing to").grid(row=0, column=0) - Label(self.frame, text=debriefing_directory_location()).grid(row=1, column=0) - """ - For debugging purposes - """ + 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 + + Separator(self.frame, orient=HORIZONTAL).grid(row=row, sticky=EW); + 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 - row = 3 - Separator(self.frame, orient=HORIZONTAL).grid(row=row, sticky=EW); row += 1 - Label(self.frame, text="Cheat operation results: ").grid(row=row); row += 1 - Button(self.frame, text="full enemy losses", command=self.simulate_result(0, 1)).grid(row=row); row += 1 - Button(self.frame, text="full player losses", command=self.simulate_result(1, 0)).grid(row=row); row += 1 - Button(self.frame, text="some enemy losses", command=self.simulate_result(0, 0.8)).grid(row=row); row += 1 - Button(self.frame, text="some player losses", command=self.simulate_result(0.8, 0)).grid(row=row); row += 1 else: row = 0 if self.event.is_successfull(self.debriefing): - Label(self.frame, text="Operation success").grid(row=row, columnspan=1); row += 1 + header("Operation success", "title-green") else: - Label(self.frame, text="Operation failed").grid(row=row, columnspan=1); row += 1 + header("Operation failed", "title-red") + + header("Player losses") - Separator(self.frame, orient='horizontal').grid(row=row, columnspan=1, sticky=NE); row += 1 - Label(self.frame, text="Player losses").grid(row=row, columnspan=1); row += 1 for unit_type, count in self.player_losses.items(): - Label(self.frame, text=db.unit_type_name(unit_type)).grid(row=row) - Label(self.frame, text="{}".format(count)).grid(column=1, row=row) + 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 - Separator(self.frame, orient='horizontal').grid(row=row, columnspan=1, sticky=NE); row += 1 - Label(self.frame, text="Enemy losses").grid(columnspan=1, row=row); row += 1 + header("Enemy losses") + for unit_type, count in self.enemy_losses.items(): if count == 0: continue - Label(self.frame, text=db.unit_type_name(unit_type)).grid(row=row) - Label(self.frame, text="{}".format(count)).grid(column=1, row=row) + 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).grid(columnspan=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 @@ -106,8 +144,10 @@ class EventResultsMenu(Menu): 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)) + 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()} diff --git a/ui/mainmenu.py b/ui/mainmenu.py index b7aeb2be..63106d20 100644 --- a/ui/mainmenu.py +++ b/ui/mainmenu.py @@ -1,11 +1,13 @@ -import pickle - -from ui.basemenu import * -from ui.overviewcanvas import * -from ui.configurationmenu import * - 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): @@ -18,45 +20,57 @@ class MainMenu(Menu): self.upd.update() self.frame = self.window.right_pane - self.frame.grid_columnconfigure(0, weight=1) + self.frame.columnconfigure(0, weight=1) + self.frame.rowconfigure(0, weight=1) def display(self): persistency.save_game(self.game) self.window.clear_right_pane() self.upd.update() - row = 1 + row = 0 + + # Header : + header = Frame(self.frame, **STYLES["header"]) + Button(header, text="Configuration", command=self.configuration_menu, **STYLES["btn-primary"]).grid(column=0, row=0, sticky=NE) + Label(header, text="Budget: {}m (+{}m)".format(self.game.budget, self.game.budget_reward_amount), **STYLES["strong"]).grid(column=1, row=0, sticky=NSEW, padx=50) + Button(header, text="Pass turn", command=self.pass_turn, **STYLES["btn-primary"]).grid(column=2, row=0, sticky=NW) + header.grid(column=0, row=0, sticky=N+EW) + + body = LabelFrame(self.frame, **STYLES["body"]) + body.grid(column=0, row=1, sticky=NSEW) def label(text): - nonlocal row - Label(self.frame, text=text).grid(row=row, sticky=NW) + nonlocal row, body + frame = LabelFrame(body, **STYLES["label-frame"]) + frame.grid(row=row, sticky=NSEW, columnspan=2) + Label(frame, text=text, **STYLES["widget"]).grid(row=row, sticky=NS) row += 1 def event_button(event): - nonlocal row - Message(self.frame, text="{}{} at {}".format( + nonlocal row, body + frame = LabelFrame(body, **STYLES["label-frame"]) + frame.grid(row=row, sticky=NSEW) + Message(frame, text="{}{} at {}".format( event.defender_name == self.game.player and "Enemy attacking: " or "", event, event.to_cp, - ), aspect=1600).grid(column=0, row=row, sticky=NW) - Button(self.frame, text=">", command=self.start_event(event)).grid(column=0, row=row, sticky=NE+S); row += 1 + ), 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 - def destination_header(text, separator=True): - nonlocal row - if separator: - Separator(self.frame, orient=HORIZONTAL).grid(row=row, sticky=EW); row += 1 - Label(self.frame, text=text).grid(column=0, row=row, sticky=N); row += 1 + def destination_header(text, pady=0): + nonlocal row, body + Label(body, text=text, **STYLES["strong"]).grid(column=0, columnspan=2, row=row, sticky=N+EW, pady=(pady,0)); row += 1 - Button(self.frame, text="Configuration", command=self.configuration_menu).grid(column=0, row=0, sticky=NE) - Button(self.frame, text="Pass turn", command=self.pass_turn).grid(column=0, row=0, sticky=NW) - Label(self.frame, text="Budget: {}m (+{}m)".format(self.game.budget, self.game.budget_reward_amount)).grid(column=0, row=0, sticky=N) - Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1 + #Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1 events = self.game.events 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)) destination = None + deliveries = False for event in events: if not event.informational: if self.game.is_player_attack(event): @@ -64,10 +78,13 @@ class MainMenu(Menu): else: new_destination = "Enemy attack" if destination != new_destination: - destination_header(new_destination, destination is not None) + destination_header(new_destination) destination = new_destination if event.informational: + if not deliveries: + deliveries = True + destination_header("Deliveries", 15) label(str(event)) else: event_button(event) @@ -92,3 +109,7 @@ class MainMenu(Menu): self.basemenu = BaseMenu(self.window, self, self.game, cp) self.basemenu.display() + + + + diff --git a/ui/newgamemenu.py b/ui/newgamemenu.py index 23bcfec6..8f34db6d 100644 --- a/ui/newgamemenu.py +++ b/ui/newgamemenu.py @@ -1,7 +1,9 @@ +import os from tkinter import * from tkinter.ttk import * from ui.window import * +from .styles import STYLES class NewGameMenu(Menu): @@ -14,6 +16,7 @@ class NewGameMenu(Menu): 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() @@ -57,23 +60,69 @@ class NewGameMenu(Menu): def display(self): self.window.clear_right_pane() - Label(self.frame, text="Player country").grid(row=0, column=0) - Radiobutton(self.frame, text="USA", variable=self.selected_country, value=0).grid(row=1, column=0) - Radiobutton(self.frame, text="Russia", variable=self.selected_country, value=1).grid(row=2, column=0) + # 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() - Label(self.frame, text="Terrain").grid(row=0, column=1) - Radiobutton(self.frame, text="Caucasus", variable=self.selected_terrain, value=0).grid(row=1, column=1) - Radiobutton(self.frame, text="Nevada", variable=self.selected_terrain, value=1).grid(row=2, column=1) - Radiobutton(self.frame, text="Persian Gulf", variable=self.selected_terrain, value=2).grid(row=3, column=1) + # Body + body = Frame(self.frame, **STYLES["body"]) + body.grid(row=1, column=0, sticky=NSEW) - Label(self.frame, text="Options").grid(row=1, column=2) - Checkbutton(self.frame, text="SAMs", variable=self.sams).grid(row=1, column=2) - Checkbutton(self.frame, text="Mid game", variable=self.midgame).grid(row=2, column=2) + # Country Selection + country = LabelFrame(body, text="Player Side", **STYLES["label-frame"]) + country.grid(row=0, column=0, sticky=NW, padx=5) + Radiobutton(country, variable=self.selected_country, value=0, **STYLES["radiobutton"]).grid(row=0, column=0, + sticky=W) + Label(country, text="USA", **STYLES["widget"]).grid(row=0, column=1, sticky=W) + Radiobutton(country, variable=self.selected_country, value=1, **STYLES["radiobutton"]).grid(row=1, column=0, + sticky=W) + Label(country, text="Russia", **STYLES["widget"]).grid(row=1, column=1, sticky=W) - Label(self.frame, text="Multiplier").grid(row=0, column=3) - Entry(self.frame, textvariable=self.multiplier).grid(row=1, column=3) + # Terrain Selection + terrain = LabelFrame(body, text="Terrain", **STYLES["label-frame"]) + terrain.grid(row=0, column=1, sticky=N, padx=5) - Button(self.frame, text="Proceed", command=self.proceed).grid(row=5, column=0, columnspan=4) + 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.png").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.png").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.png").grid(row=2, column=2, padx=5) + + # Misc Options + options = LabelFrame(body, text="Misc Options", **STYLES["label-frame"]) + options.grid(row=0, column=2, 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, diff --git a/ui/overviewcanvas.py b/ui/overviewcanvas.py index d3ca3ac7..e3522e35 100644 --- a/ui/overviewcanvas.py +++ b/ui/overviewcanvas.py @@ -50,11 +50,12 @@ class OverviewCanvas: title = cp.name font = ("Helvetica", 10) - id = self.canvas.create_text(coords[0]+1, coords[1]+1, text=title, fill='white', font=font) - self.canvas.tag_bind(id, "", self.display(cp)) id = self.canvas.create_text(coords[0], coords[1], text=title, font=font) self.canvas.tag_bind(id, "", self.display(cp)) + id = self.canvas.create_text(coords[0]+1, coords[1]+1, text=title, fill='white', font=font) + self.canvas.tag_bind(id, "", self.display(cp)) + def _player_color(self): return self.game.player == "USA" and "blue" or "red" @@ -116,7 +117,8 @@ class OverviewCanvas: self.create_cp_title((coords[0] + arc_size/4, coords[1] + arc_size/4), cp) units_title = "{}/{}/{}".format(cp.base.total_planes, cp.base.total_armor, cp.base.total_aa) - self.canvas.create_text(coords[0], coords[1] - arc_size / 1.5, text=units_title, font=("Helvetica", 10)) + self.canvas.create_text(coords[0]+1, coords[1] - arc_size / 1.5 +1, text=units_title, font=("Helvetica", 10), fill=color) + self.canvas.create_text(coords[0], coords[1] - arc_size / 1.5, text=units_title, font=("Helvetica", 10), fill="white") def display(self, cp: ControlPoint): def action(_): diff --git a/ui/styles.py b/ui/styles.py new file mode 100644 index 00000000..10a2d48d --- /dev/null +++ b/ui/styles.py @@ -0,0 +1,45 @@ +# 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" + +# Fonts +FONT_FAMILY = "Trebuchet MS" +DEFAULT_FONT = (FONT_FAMILY, 8) +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": 25, "pady": 35} +STYLES["strong"] = {"font": BOLD_FONT, "bg": BG_TITLE_COLOR, "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["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["btn-primary"] = {"bg": GREEN, "fg": FG_COLOR, "padx": PADDING_X, "pady": 2, "font": DEFAULT_FONT} +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 index cb9a17e0..a71c83cb 100644 --- a/ui/window.py +++ b/ui/window.py @@ -1,6 +1,6 @@ from tkinter import * from game.game import * - +from .styles import BG_COLOR,BG_TITLE_COLOR class Window: image = None @@ -9,21 +9,24 @@ class Window: def __init__(self): self.tk = Tk() + self.tk.title("DCS Liberation") + self.tk.iconbitmap("icon.ico") + self.tk.resizable(True, True) self.tk.grid_columnconfigure(0, weight=1) self.tk.grid_rowconfigure(0, weight=1) - self.frame = Frame(self.tk) + self.frame = Frame(self.tk, bg=BG_COLOR) self.frame.grid(column=0, row=0, sticky=NSEW) - self.frame.grid_columnconfigure(0, minsize=300) - self.frame.grid_columnconfigure(1, minsize=400) + 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) + 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) + self.right_pane = Frame(self.frame, bg=BG_COLOR) self.right_pane.grid(row=0, column=1, sticky=NSEW) self.tk.focus()