def _high_score(self): high_score_file = HighScoreManager() file_name = high_score_file._filename high_score_file.load(file_name) score_root = Tk() score_root.title("High Score Board") high_score_board = Text(score_root, height=25, width=25) high_score_board.pack() high_score = high_score_file.get_entries()
def _handle_game_over(self, won=False): """Handles game over Parameter: won (bool): If True, signals the game was won (otherwise lost) """ self._won = won self.stop() self._button_pause.config(state=DISABLED) if self._won == True: message_dialog = "Congratulation! You won!" else: message_dialog = "Oops, you lost, try again" high_score_file = HighScoreManager() file_name = high_score_file._filename high_score_file.load(file_name) # high_root = Tk() # if(high_score_file.does_score_qualify(self._score)==True): msg.showinfo("Tower", message_dialog)
class TowerGameApp(Stepper): """Top-level GUI application for a simple tower defence game""" # All private attributes for ease of reading _current_tower = None _paused = False _won = None _level = None _wave = None _score = None _coins = None _lives = None _master = None _game = None _view = None # Task 3.3 _selected_tower = None def __init__(self, master: tk.Tk, delay: int = 20): """Construct a tower defence game in a root window Parameters: master (tk.Tk): Window to place the game into """ self._master = master super().__init__(master, delay=delay) master.title('Towers') self._game = game = TowerGame() self.setup_menu() # create a game view and draw grid borders self._view = view = GameView(master, size=game.grid.cells, cell_size=game.grid.cell_size, bg='antique white') view.pack(side=tk.LEFT, expand=True) self._right_frame = tk.Frame(master, bg=BACKGROUND_COLOUR) self._right_frame.pack(side=tk.LEFT, fill=tk.Y) # Task 2.4 (High Scores): load files into a high score manager instance self._high_score_manager = HighScoreManager() self._high_score_manager.load(self._high_score_manager._filename) # Task 1.3 (Status Bar): instantiate status bar self._status = status = StatusBar(self._right_frame) status.pack(side=tk.TOP, fill=tk.X, anchor=tk.N, expand=False) # Task 2.3 (Shop): instantiation towers = [SimpleTower, MissileTower, EnergyTower, SlowTower] shop = tk.Frame(self._right_frame) shop.pack(side=tk.TOP, anchor=tk.N, fill=tk.X) # Create views for each tower & store to update if availability changes self._tower_views = [] for tower_class in towers: tower = tower_class(self._game.grid.cell_size // 2) v = ShopTowerView(shop, tower, bg=BACKGROUND_COLOUR, highlight="#4b3b4a", click_command=lambda class_=tower_class: self. select_tower(class_)) v.pack(fill=tk.X) self._tower_views.append( (tower, v) ) # Can use to check if tower is affordable when refreshing view # Task 1.5 (Play Controls): instantiate widgets here self._control = control = tk.Frame(self._right_frame) self._next_wave = tk.Button(control, text="Next Wave", command=self.next_wave, font=("TkDefaultFont", 12, "normal")) self._next_wave.pack(side=tk.LEFT) self._play = tk.Button(control, text="Play", command=self._toggle_paused, font=("TkDefaultFont", 12, "normal")) self._play.pack(side=tk.LEFT) self._control.pack(side=tk.BOTTOM, anchor=tk.S) # Task 3.3 (Upgrade Tower): GUI self._upgrade_button = tk.Button(self._right_frame, text="Upgrade selected tower", state="disabled", command=self.upgrade_selected_tower, font=("TkDefaultFont", 12, "normal")) self._upgrade_button.pack(side=tk.BOTTOM, anchor=tk.S) self._selected_tower_level_up_cost = tk.Label( self._right_frame, text="Cost to upgrade: N/A", bg=BACKGROUND_COLOUR, fg="white", font=("TkDefaultFont", 12, "normal")) self._selected_tower_level_up_cost.pack(side=tk.BOTTOM, anchor=tk.S) self._selected_tower_level = tk.Label( self._right_frame, text="Selected tower level: None", bg=BACKGROUND_COLOUR, fg="white", font=("TkDefaultFont", 12, "normal")) self._selected_tower_level.pack(side=tk.BOTTOM, anchor=tk.S) # bind game events game.on("enemy_death", self._handle_death) game.on("enemy_escape", self._handle_escape) game.on("cleared", self._handle_wave_clear) # Task 1.2 (Tower Placement): bind mouse events to canvas here self._view.bind("<Motion>", self._move) self._view.bind("<Button-1>", self._left_click) self._view.bind("<Leave>", self._mouse_leave) # Task 2.1: bind mouse event self._view.bind("<Button-2>", self._sell_tower) # Level self._level = MyLevel() self.select_tower(SimpleTower) view.draw_borders(game.grid.get_border_coordinates()) # Get ready for the game self._setup_game() self._check_availability() def setup_menu(self): """Sets up the application menu""" # Task 1.4: construct file menu here self._menubar = tk.Menu(self._master) self._master.config(menu=self._menubar) self._filemenu = tk.Menu(self._menubar) self._menubar.add_cascade(label="File", menu=self._filemenu) self._filemenu.add_command(label="New Game", command=self._new_game) self._filemenu.add_command(label="High Scores", command=self._show_high_scores) self._filemenu.add_command(label="Exit", command=self._exit) def _toggle_paused(self, paused=None): """Toggles or sets the paused state Parameters: paused (bool): Toggles/pauses/unpauses if None/True/False, respectively """ if paused is None: paused = not self._paused # Task 1.5 (Play Controls): Reconfigure the pause button here if paused: self.pause() self._play.configure(text="Play") else: self.start() self._play.configure(text="Pause") self._paused = paused def _setup_game(self): """Sets up the game""" self._wave = 0 self._score = 0 self._coins = 5000 self._lives = 20 self._won = False # Task 1.3 (Status Bar): Update status here self.refresh_status() # Task 1.5 (Play Controls): Re-enable the play controls here (if they were ever disabled) self._next_wave.configure(state="normal") self._play.configure(state="normal") self._game.reset() # Auto-start the first wave self.next_wave() self._toggle_paused(paused=True) def _new_game(self): """Menu handler - restarts the game""" self._setup_game() self.refresh_view() def _exit(self): """Menu handler - exits the application""" confirm = messagebox.askyesno( "", "Are you sure you want to exit the game?") if confirm == True: self._master.destroy() def refresh_view(self): """Refreshes the game view""" if self._step_number % 2 == 0: self._view.draw_enemies(self._game.enemies) self._view.draw_towers(self._game.towers) self._view.draw_obstacles(self._game.obstacles) self._check_availability() def _step(self): """ Perform a step every interval Triggers a game step and updates the view Returns: (bool) True if the game is still running """ self._game.step() self.refresh_view() return not self._won # Task 1.2 (Tower Placement): Complete event handlers here (including docstrings!) def _move(self, event): """ Handles the mouse moving over the game view canvas Parameter: event (tk.Event): Tkinter mouse event """ if self._current_tower.get_value() > self._coins: return # move the shadow tower to mouse position position = event.x, event.y self._current_tower.position = position legal, grid_path = self._game.attempt_placement(position) # find the best path and covert positions to pixel positions path = [ self._game.grid.cell_to_pixel_centre(position) for position in grid_path.get_shortest() ] # draw the path self._view.draw_path(path) # check if current position is a legal position to place a tower at legal = True cell_position = self._game.grid.pixel_to_cell(position) if not self._game.grid.is_cell_valid(cell_position): legal = False if cell_position in self._game.towers: legal = False try: self._game.generate_path(cell_position) except KeyError: legal = False # draw the shadow tower or cross if legal == False self._view.draw_preview(self._current_tower, legal) def _mouse_leave(self, event): """ Handles the mouse leaving the game view canvas Parameter: event (tk.Event): Tkinter mouse event """ # Task 1.2 (Tower placement) self._view.delete('shadow', 'range', 'path') def _left_click(self, event): """ Handles the mouse left clicking on the game view canvas Parameter: event (tk.Event): Tkinter mouse event """ self.reset_upgrade_gui() if self._current_tower is None: return position = event.x, event.y cell_position = self._game.grid.pixel_to_cell(position) try: # Task 3.3 (Upgrade Tower): update GUI when left clicked on a tower self._selected_tower = self._game.towers[cell_position] selected_tower_level = self._selected_tower.level selected_tower_upgrade_cost = self._selected_tower.level_cost self._selected_tower_level.configure( text="Selected tower level: " + str(selected_tower_level)) self._selected_tower_level_up_cost.configure( text="Cost to upgrade: " + str(selected_tower_upgrade_cost)) if selected_tower_upgrade_cost > self._coins: self._upgrade_button.configure(text="Not enough coins!") else: self._upgrade_button.configure(state="normal") except KeyError: if self._current_tower.get_value() > self._coins: return elif self._game.place(cell_position, tower_type=self._current_tower.__class__): self._coins -= self._current_tower.get_value() self.refresh_status() self.refresh_view() def _sell_tower(self, event): """ Handles the mouse right clicking on the game view canvas Parameter: event (tk.Event): Tkinter mouse event """ self.reset_upgrade_gui() position = event.x, event.y cell_position = self._game.grid.pixel_to_cell(position) try: # refund 80% of the tower's cost to player (including upgrade costs) # math.floor is used to keep self._coins an integer tower_at_position = self._game.towers[cell_position] self._coins += math.floor(0.8 * tower_at_position.get_value()) self.refresh_status() self._game.remove(cell_position) self.refresh_view() except KeyError: # do nothing if a tower does not exist at the point of right click return def next_wave(self): """Sends the next wave of enemies against the player""" if self._wave == self._level.get_max_wave(): return self._wave += 1 # Task 1.3 (Status Bar): Update the current wave display here self.refresh_status() # Task 1.5 (Play Controls): Disable the add wave button here (if this is the last wave) if self._wave == self._level.waves: self._next_wave.configure(state="disabled") # Generate wave and enqueue wave = self._level.get_wave(self._wave) for step, enemy in wave: enemy.set_cell_size(self._game.grid.cell_size) self._game.queue_wave(wave) def select_tower(self, tower): """ Set 'tower' as the current tower Parameters: tower (AbstractTower): The new tower type """ self._current_tower = tower(self._game.grid.cell_size) def _handle_death(self, enemies): """ Handles enemies dying Parameters: enemies (list<AbstractEnemy>): The enemies which died in a step """ bonus = len(enemies)**.5 for enemy in enemies: self._coins += enemy.points self._score += int(enemy.points * bonus) # Task 1.3 (Status Bar): Update coins & score displays here self.refresh_status() def _handle_escape(self, enemies): """ Handles enemies escaping (not being killed before moving through the grid Parameters: enemies (list<AbstractEnemy>): The enemies which escaped in a step """ self._lives -= len(enemies) if self._lives < 0: self._lives = 0 # Task 1.3 (Status Bar): Update lives display here self.refresh_status() # Handle game over if self._lives == 0: self._handle_game_over(won=False) def _handle_wave_clear(self): """Handles an entire wave being cleared (all enemies killed)""" if self._wave == self._level.get_max_wave(): self._handle_game_over(won=True) def _handle_game_over(self, won=False): """Handles game over Parameter: won (bool): If True, signals the game was won (otherwise lost) """ self._won = won self.stop() # Task 1.4 (Dialogs): show game over dialog here result_message = "" if won: result_message = "You won the game." else: result_message = "You lost the game." messagebox.showinfo("Game over!", result_message) self._next_wave.configure(state="disabled") self._play.configure(state="disabled") # Task 2.4 (High Scores): asks user for name if score is high enough if self._high_score_manager.does_score_qualify(self._score): username = tk.simpledialog.askstring( "", "You are in the top 10! Enter your name below to go on the leaderboard!", parent=self._master) if username is not None: self._high_score_manager.add_entry(username, self._score) self._high_score_manager.save() def refresh_status(self): """Update the status bar (and others related)""" self._status.set_wave(self._wave, self._level.waves) self._status.set_score(self._score) self._status.set_coin(self._coins) self._status.set_lives(self._lives) def _check_availability(self): """Checks and sets availability of towers in the shop""" for towers in self._tower_views: tower_value = towers[0].get_value() if self._coins < tower_value: towers[1].set_available(False) else: towers[1].set_available(True) def _show_high_scores(self): """Handles displaying the highest scores and players.""" high_score_root = tk.Tk() high_score_root.title("High Scores") high_score_root.resizable(False, False) high_score_data = self._high_score_manager.get_entries() # list of names and scores names_scores = [] for element in high_score_data: name_score = element['name'], element['score'] names_scores.append(name_score) title = tk.Label(high_score_root, text="Top 10 Players", font=("TkDefaultFont", 15, "bold")) title.pack(anchor=tk.N) # create GUI entries for each name/score for e in names_scores: name_score_frame = tk.Frame(high_score_root, width=250, height=26) name_score_frame.pack() name_score_frame.pack_propagate(False) name = tk.Label(name_score_frame, text=e[0]) name.pack(side=tk.LEFT, padx=10, pady=5) score = tk.Label(name_score_frame, text=e[1]) score.pack(side=tk.RIGHT, padx=10, pady=5) high_score_root.mainloop() def reset_upgrade_gui(self): """Resets the upgrade tower GUI in task 3.3 to initial state""" self._selected_tower_level_up_cost.configure( text="Cost to upgrade: N/A") self._selected_tower_level.configure(text="Selected tower level: None") self._selected_tower = None self._upgrade_button.configure(state="disabled", text="Upgrade selected tower") def upgrade_selected_tower(self): """Upgrade the currently selected tower""" if self._selected_tower is None: return else: self._coins -= self._selected_tower.level_cost self._selected_tower.level += 1 self.reset_upgrade_gui() self.refresh_status()