diff --git a/game.py b/game.py new file mode 100644 index 0000000..1000a20 --- /dev/null +++ b/game.py @@ -0,0 +1,296 @@ +from random import randint + +import project_constants as const + +from display_assets import blit_graphics +from searching_algorithms import a_star + +from minefield import Minefield + +from mine_models.time_mine import TimeMine + +from ui.ui_components_manager import UiComponentsManager +from ui.text_box import TextBox +from ui.button import Button +from ui.input_box import InputBox + + +class Game: + def __init__(self, turn=0, minefield=None, window=None): + # loading game settings + self.window = window if window is not None else const.SCREEN + + # setting in game data + self.turn = turn + self.turn_on_which_last_action_was_taken = turn + self.goal = (0, 0) + + # assigning minefield and agent to variables + self.minefield = minefield if minefield is not None else Minefield(const.MAP_RANDOM_10x10) + self.initial_minefield = self.minefield.__copy__() + self.agent = self.minefield.agent + self.agent_action = None + self.millisecond_timer = 0 + self.delta_time = 0 + self.action_timer = 0 + self.action_delta_time = 0 + + # declaring and initializing gui components + # ui_component managers + self.in_game_gui_components_manager = UiComponentsManager() + self.game_over_gui_components_manager = UiComponentsManager() + + # in game gui + self.input_box_row = InputBox((0, 0), (0, 0)) + self.input_box_column = InputBox((0, 0), (0, 0)) + self.button_auto = Button((0, 0), (0, 0)) + self.button_random = Button((0, 0), (0, 0)) + self.button_ok = Button((0, 0), (0, 0)) + + # game over screen + self.text_box_game_over = TextBox((0, 0), (0, 0)) + self.button_try_again = Button((0, 0), (0, 0)) + self.button_close = Button((0, 0), (0, 0)) + + # draws minefield and agent instances + def draw_minefield(self): + blit_graphics(self.minefield) + self.agent.update_and_draw(self.window, self.action_delta_time, self.minefield) + + # draws menu + def run_in_game_menu_overlay(self, mouse_position, events): + # drawing and updating all gui components + self.in_game_gui_components_manager.run_all(self.window, mouse_position, events) + self.in_game_gui_components_manager.switch_selected_objects_with_arrow_keys(mouse_position, events) + + # setting random goal if random button is clicked + if self.button_random.is_clicked(mouse_position, events): + self.goal = (randint(0, 9), randint(0, 9)) + self.input_box_row.set_texts(user_input=str(self.goal[0])) + self.input_box_column.set_texts(user_input=str(self.goal[1])) + + # if any input box is empty don't allow player to run game + self.button_ok.set_flags(is_active=not (self.input_box_row.empty() or self.input_box_column.empty())) + + # updating goal if input boxes are filled and highlighting chosen tile destination + if not (self.input_box_row.empty() or self.input_box_column.empty()): + # updating goal + self.goal = (min(9, int(self.input_box_row.get_input())), min(9, int(self.input_box_column.get_input()))) + + # highlighting chosen tile destination + self.window.blit(const.HIGHLIGHT, const.get_tile_coordinates(self.goal)) + + # makes agent take next action + def agent_take_next_action(self, action_sequence): + # give agent an action if there is one + self.agent.take_action(self.agent_action) + + # check what the next action is going to be + self.agent_action = action_sequence.pop(0) + + # start animating agents next action + self.agent.animate(self.agent_action) + + # updates in game time and timers + def update_time(self, number_of_seconds_since_last_tick): + self.millisecond_timer += number_of_seconds_since_last_tick / 1000 + self.action_timer = self.millisecond_timer / const.TURN_INTERVAL + self.delta_time = number_of_seconds_since_last_tick / 1000 + self.action_delta_time = self.delta_time / const.TURN_INTERVAL + + # updates number of turns in game + def update_turns(self): + # if one turns time passed: updating number of turns + if self.millisecond_timer >= const.TURN_INTERVAL: + self.turn += 1 + self.minefield.next_turn() + + # resetting timer + self.millisecond_timer %= const.TURN_INTERVAL + + # returns turn number + def get_turn_number(self): + return self.turn + + # gets action sequence for agent + def get_action_sequence(self): + return a_star.graphsearch( + initial_state=a_star.State( + row=self.agent.row, + column=self.agent.column, + direction=self.agent.direction + ), + minefield=self.minefield, + tox=self.goal[0], + toy=self.goal[1] + ) + + # initializes attributes before game loop begins + def initialize_before_game_loop(self): + self.agent_action = None + self.millisecond_timer = 0 + self.action_timer = 0 + self.delta_time = 0 + self.action_delta_time = 0 + + for component in self.in_game_gui_components_manager.selectable_ui_components: + component.set_flags(is_active=False) + + # gives agent last action + def agent_take_last_action(self): + self.agent.take_action(self.agent_action) + + # cleans up after game loop ends + def cleanup_after_game_loop(self): + self.agent.update_and_draw(self.window, self.action_delta_time, self.minefield) + self.agent.reset_actions() + + for comp in self.in_game_gui_components_manager.selectable_ui_components: + comp.set_flags(is_active=True) + + if not any([comp.is_selected for comp in self.in_game_gui_components_manager.selectable_ui_components]): + self.input_box_row.set_is_selected(True) + + # returns True if agents should take an action in this turn, or False otherwise + def agent_should_take_next_action(self, action_sequence): + number_of_turns_since_last_action = self.turn - self.turn_on_which_last_action_was_taken + agent_action_cost = 1 if self.agent_action != const.Action.GO else self._get_next_tiles_entering_cost() + + if number_of_turns_since_last_action >= agent_action_cost and any(action_sequence): + self.turn_on_which_last_action_was_taken = self.turn + + return True + + return False + + # returns True if agent made all actions he was instructed to do, or False otherwise + def agent_made_all_actions(self, action_sequence): + number_of_turns_since_last_action = self.turn - self.turn_on_which_last_action_was_taken + agent_action_cost = 1 if self.agent_action != const.Action.GO else self._get_next_tiles_entering_cost() + + if number_of_turns_since_last_action >= agent_action_cost and not any(action_sequence): + self.turn_on_which_last_action_was_taken = self.turn + + return True + + return False + + # runs game over screen + def run_game_over_screen(self, mouse_position, events): + self.window.fill((255, 255, 255)) + self.game_over_gui_components_manager.run_all(self.window, mouse_position, events) + + # resetting minefield to the original state + if self.button_try_again.is_clicked(mouse_position, events): + del self.minefield + del self.agent + self.minefield = self.initial_minefield.__copy__() + self.agent = self.initial_minefield.agent + + # initializes ui components and assigns them to attributes + def initialize_gui_components(self): + # calculating in game gui coordinates + gui_width = const.V_SIDE_MENU_WIDTH + ib_height, bt_height = const.V_INPUT_BOX_HEIGHT, const.V_BUTTON_HEIGHT + gui_x = const.V_TILE_AREA_WIDTH + 2 * const.V_SCREEN_PADDING + const.V_NUMBER_PADDING + gui_y = const.SCREEN.get_height() / 2 - (2 * ib_height + 2 * bt_height + 30) / 2 + + # creating in game gui components + self.input_box_row = InputBox( + position=(gui_x, gui_y), + dimensions=(gui_width, ib_height), + text="row", + box_color=(100, 200, 100), + bottom_strip_color=(120, 220, 120), + inner_box_color=(120, 220, 120), + outline_color=(80, 180, 80), + outline_additional_pixel=True, + valid_input_characters="1234567890", + input_centered=True, + clear_input_on_click=True + ) + + self.input_box_column = InputBox( + position=(gui_x, gui_y + ib_height + 10), + dimensions=(gui_width, ib_height), + text="column", + box_color=(100, 200, 100), + bottom_strip_color=(120, 220, 120), + inner_box_color=(120, 220, 120), + outline_color=(80, 180, 80), + outline_additional_pixel=True, + valid_input_characters="1234567890", + input_centered=True, + clear_input_on_click=True + ) + + self.button_auto = Button( + position=(gui_x, gui_y + 2 * ib_height + 20), + dimensions=(gui_width, bt_height), + text="auto", + box_color=(100, 200, 100), + outline_color=(80, 180, 80), + outline_additional_pixel=True + ) + + self.button_random = Button( + position=(gui_x, gui_y + 2 * ib_height + bt_height + 30), + dimensions=(gui_width, bt_height), + text="random", + box_color=(100, 200, 100), + outline_color=(80, 180, 80), + outline_additional_pixel=True + ) + + self.button_ok = Button( + position=(gui_x, gui_y + 2 * ib_height + 2 * bt_height + 40), + dimensions=(gui_width, bt_height), + text="ok", + box_color=(100, 200, 100), + outline_color=(80, 180, 80), + outline_additional_pixel=True + ) + + gui_list = [self.input_box_row, self.input_box_column, self.button_auto, self.button_random, self.button_ok] + self.in_game_gui_components_manager = UiComponentsManager(gui_list) + + # creating game over gui components + self.text_box_game_over = TextBox(position=(150, 200), dimensions=(500, 100), text="Game Over") + self.button_try_again = Button(position=(350, 350), dimensions=(100, 50), text="Try again") + self.button_close = Button(position=(350, 450), dimensions=(100, 50), text="Close") + + gui_list = [self.text_box_game_over, self.button_try_again, self.button_close] + self.game_over_gui_components_manager = UiComponentsManager(gui_list, select_first_item=False) + + # returns true if there is a time mine that went out of time + def time_mine_exploded(self): + # auxiliary function that checks if a tile contains a time mine which timer went to 0 + for i in range(const.V_GRID_HOR_TILES): + for j in range(const.V_GRID_VER_TILES): + mine = self.minefield.matrix[i][j].mine + if mine is not None and isinstance(mine, TimeMine) and mine.timer <= 0: + + return True + + return False + + # sets all in game components is_active flags to a given value + def set_is_active_flag_for_all_in_game_gui_components(self, is_active): + for component in self.in_game_gui_components_manager.selectable_ui_components: + component.set_flags(is_active=is_active) + + # returns cost of entering tile in front of the agent + def _get_next_tiles_entering_cost(self): + row, column = self._get_next_agent_position() + + return self.minefield.matrix[row][column].cost.value + + # returns position in front of agent + def _get_next_agent_position(self): + # heading either up or down + if self.agent_action == const.Action.GO and self.agent.direction.value % 2 == 0: + return max(0, min(9, self.agent.row + self.agent.direction.value - 1)), self.agent.column + + # heading either left or right + else: + return self.agent.row, max(0, min(9, self.agent.column - self.agent.direction.value + 2)) diff --git a/main.py b/main.py index 6cf9933..5ef7617 100644 --- a/main.py +++ b/main.py @@ -1,38 +1,10 @@ # libraries import pygame from pyglet.gl import * # for blocky textures -import random + # other files of this project +from game import Game import project_constants as const -import minefield as mf -from mine_models.time_mine import TimeMine - - - -import searching_algorithms.a_star as a_star - -from display_assets import blit_graphics -from project_constants import HIGHLIGHT, INPUT_ROW, INPUT_COLUMN, RANDOM_BUTTON, OK_BUTTON -from ui.ui_components_list import UiComponentsList - - -def _get_next_agent_position(action, agent): - # counting next agents terrain tile - # heading either up or down - if const.Direction(agent.direction).value % 2 == 0 and action == const.Action.GO: - next_row = min(9, agent.row + const.Direction(agent.direction).value - 1) - next_column = agent.column - - # heading either left or right - else: - next_row = agent.row - next_column = min(9, agent.column - const.Direction(agent.direction).value + 2) - - return next_row, next_column -from project_constants import HIGHLIGHT, INPUT_ROW, INPUT_COLUMN, RANDOM_BUTTON, OK_BUTTON -from ui.ui_components_list import UiComponentsList -from ui.text_box import TextBox -from ui.button import Button def main(): @@ -46,30 +18,18 @@ def main(): # FPS clock clock = pygame.time.Clock() - # in-game turn number - turn = 0 - last_turn = 0 - - # create an instance of Minefield, pass necessary data - minefield = mf.Minefield(const.MAP_RANDOM_10x10) - - # getting agent's instance - agent = minefield.agent + # creating some auxiliary functions + def is_quit_button_pressed(_events): + return any([_event.type == pygame.QUIT for _event in _events]) # setting flags for program running = True in_menu = True is_game_over = False - # creating list storing all ui components - ui_components_list = UiComponentsList([INPUT_ROW, INPUT_COLUMN, RANDOM_BUTTON, OK_BUTTON]) - - # initializing goal position - row, column = 0, 0 - - # drawing map so black screen doesn't appear - blit_graphics(minefield) - + # create and initialize_gui_components game instance + game = Game() + game.initialize_gui_components() while running: @@ -80,167 +40,105 @@ def main(): while running and in_menu: events = pygame.event.get() - for event in events: - if event.type == pygame.QUIT: - running = False + # checking if game should stop running + running = not is_quit_button_pressed(events) - # graphics (from display_assets) - blit_graphics(minefield) + # drawing minefield and agent instances + game.draw_minefield() - agent.update_and_draw(const.SCREEN, 0, minefield) + # drawing gui overlay + game.run_in_game_menu_overlay(pygame.mouse.get_pos(), events) - # drawing gui - ui_components_list.run_all(const.SCREEN, pygame.mouse.get_pos(), events) + # ticking to maintain good fps number + clock.tick(const.V_FPS) - # highlighting chosen tile destination (if exists) - if not(INPUT_ROW.empty() or INPUT_COLUMN.empty()): - row = min(9, int(INPUT_ROW.get_input())) - column = min(9, int(INPUT_COLUMN.get_input())) - const.SCREEN.blit(HIGHLIGHT, const.get_tile_coordinates((row, column))) - - # updating graphics + # updating window graphics pygame.display.flip() - ui_components_list.switch_selected_objects_with_arrow_keys(events, pygame.mouse.get_pos()) - - OK_BUTTON.set_flags(is_active=(not(INPUT_ROW.empty() or INPUT_COLUMN.empty()))) - - if OK_BUTTON.is_clicked(pygame.mouse.get_pos(), events) or OK_BUTTON.enter_pressed(events): - in_menu = False - - if RANDOM_BUTTON.is_clicked(pygame.mouse.get_pos(), events) or RANDOM_BUTTON.enter_pressed(events): - INPUT_ROW.set_texts(user_input=str(random.randint(0, 9))) - INPUT_COLUMN.set_texts(user_input=str(random.randint(0, 9))) - - clock.tick(const.V_FPS) + # if ok button is clicked then leave menu section + in_menu = not game.button_ok.is_clicked(pygame.mouse.get_pos(), events) # ========================== # # ==== BEFORE GAME LOOP ==== # # ========================== # - if running: - for component in ui_components_list.selectable_ui_components: - component.set_flags(is_active=False) + # getting action sequence for agent + action_sequence = game.get_action_sequence() - action_sequence = a_star.graphsearch( - initial_state=a_star.State( - row=minefield.agent.position[0], - column=minefield.agent.position[1], - direction=minefield.agent.direction), - minefield=minefield, tox=row, toy=column) - - # initializing variables - in_game_timer = 0 - action = None - action_cost = 1 - next_row, next_column = agent.row, agent.column - - # =================== # - # ==== GAME LOOP ==== # - # =================== # + # initializing game attributes before the game loop + game.initialize_before_game_loop() # =================== # # ==== GAME LOOP ==== # # =================== # while running and not in_menu and not is_game_over: + events = pygame.event.get() - for event in pygame.event.get(): - if event.type == pygame.QUIT: - running = False - - for event in pygame.event.get(): - if event.type == pygame.QUIT: - running = False + # checking if game should stop running + running = not is_quit_button_pressed(events) # FPS control clock.tick(const.V_FPS) - delta_time = clock.get_time() / 1000 - action_delta_time = delta_time / const.ACTION_INTERVAL - # graphics (from display_assets) - blit_graphics(minefield) - const.SCREEN.blit(HIGHLIGHT, const.get_tile_coordinates((row, column))) + # getting time since last tick + time = clock.get_time() - agent.update_and_draw(const.SCREEN, action_delta_time, minefield) + # drawing minefield and agent instances + game.draw_minefield() - # drawing ui components so they don't "disappear" - ui_components_list.draw_all(const.SCREEN, pygame.mouse.get_pos()) + # drawing inactive gui components so they don't "disappear" + game.run_in_game_menu_overlay(pygame.mouse.get_pos(), events) + game.set_is_active_flag_for_all_in_game_gui_components(False) # updating graphics pygame.display.flip() # update turn - if in_game_timer >= const.ACTION_INTERVAL: - in_game_timer %= const.ACTION_INTERVAL - turn += 1 - minefield.next_turn() - action_cost = 1 if action != const.Action.GO else minefield.matrix[next_row][next_column].cost.value + game.update_time(time) + game.update_turns() # make the next move from sequence of actions - if turn - last_turn >= action_cost and any(action_sequence): - last_turn = turn + if game.agent_should_take_next_action(action_sequence): + # give agent next action + game.agent_take_next_action(action_sequence) - agent.take_action(action) - - action = action_sequence.pop(0) - - agent.animate(action) - - next_row, next_column = _get_next_agent_position(action, agent) - - elif turn - last_turn >= action_cost: - agent.take_action(action) - - agent.update_and_draw(const.SCREEN, delta_time, minefield) - agent.reset_actions() + # reset values after the game loop + if game.agent_made_all_actions(action_sequence): + # clean up after game loop + game.agent_take_last_action() + game.cleanup_after_game_loop() + # reset in_menu flag in_menu = True - if not any([x.is_selected for x in ui_components_list.ui_components]): - INPUT_ROW.set_is_selected(True) + is_game_over = game.time_mine_exploded() - for component in ui_components_list.selectable_ui_components: - component.set_flags(is_active=True) - - in_game_timer += delta_time - for x in minefield.matrix: - for y in x: - if y.mine is not None: - if isinstance(y.mine, TimeMine): - if y.mine.timer == 0: - is_game_over = True - - # temporarily disabling game over screen + # TODO temporarily disabling game over screen + # if you want to enable game over comment line below is_game_over = False while running and is_game_over: + events = pygame.event.get() + clock.tick(const.V_FPS) - const.SCREEN.fill((255, 255, 255)) - tb = TextBox(position=(150, 200), dimensions=(500, 100), text="Game Over") - cont = Button(position=(350, 350), dimensions=(100, 50), text="Try again") - close = Button(position=(350, 450), dimensions=(100, 50), text="Close") - ev = pygame.event.get() - if close.is_clicked(pygame.mouse.get_pos(), ev): - running = False - if cont.is_clicked(pygame.mouse.get_pos(), ev): - minefield = mf.Minefield(const.MAP_RANDOM_10x10) - agent = minefield.agent - in_menu = True + + # runs game over screen + game.run_game_over_screen(pygame.mouse.get_pos(), events) + + # checking if game should stop running + running = not is_quit_button_pressed(events) \ + and not game.button_close.is_clicked(pygame.mouse.get_pos(), events) + + # resetting is_game_over variable if player tries again + if game.button_try_again.is_clicked(pygame.mouse.get_pos(), events): + game.cleanup_after_game_loop() is_game_over = False - for component in ui_components_list.selectable_ui_components: - component.set_flags(is_active=True) - cont.draw(const.SCREEN, pygame.mouse.get_pos()) - close.draw(const.SCREEN, pygame.mouse.get_pos()) - tb.draw(const.SCREEN) + in_menu = True + + # updating graphics pygame.display.flip() - # is_game_over = False - - minefield.disarm_mine(1,1) - - - if __name__ == "__main__": main() diff --git a/minefield.py b/minefield.py index 0074e0c..58ffa38 100644 --- a/minefield.py +++ b/minefield.py @@ -4,21 +4,13 @@ import tile as tl from mine_models.time_mine import TimeMine import json_generator as jg -import pygame - -import time - -from display_assets import blit_graphics -from project_constants import HIGHLIGHT, INPUT_ROW, INPUT_COLUMN, RANDOM_BUTTON, OK_BUTTON -from ui.ui_components_list import UiComponentsList - - class Minefield: def __init__(self, json_path): self.turn = 0 self.agent = ag.Agent(const.MAP_RANDOM_10x10) + self.json_path = json_path # open JSON with minefield info json_gen = jg.JsonGenerator() @@ -54,6 +46,11 @@ class Minefield: if mine is not None and isinstance(mine, TimeMine): mine.timer = max(0, mine.starting_time - int(self.turn / 4)) + def disarm_mine(self, x, y): + tile = self.matrix[x][y] + mine = tile.mine + mine.disarm() + # ================ # # === MOVEMENT === # # ================ # @@ -68,29 +65,11 @@ class Minefield: return False - def disarm_mine(self, x, y): - tile = self.matrix[x][y] - mine = tile.mine - mine.disarm() + # ============= # + # === OTHER === # + # ============= # - # This for loop is temporary solution for graphics to refresh. It works weird but it will be changed eventually. - for i in range(3): - time.sleep(1) - self.next_turn() - # FPS control - pygame.time.Clock().tick(const.V_FPS) - delta_time = pygame.time.Clock().get_time() / 1000 - action_delta_time = delta_time / const.ACTION_INTERVAL - - # graphics (from display_assets) - blit_graphics(self) - const.SCREEN.blit(HIGHLIGHT, const.get_tile_coordinates((x, y))) - - self.agent.update_and_draw(const.SCREEN, 0, self) - - # drawing ui components so they don't "disappear" - UiComponentsList([INPUT_ROW, INPUT_COLUMN, RANDOM_BUTTON, OK_BUTTON]).draw_all(const.SCREEN, - pygame.mouse.get_pos()) - - # updating graphics - pygame.display.flip() \ No newline at end of file + # method that allows copying classes instance + def __copy__(self): + copy = Minefield(self.json_path) + return copy diff --git a/project_constants.py b/project_constants.py index 2efef74..8c72078 100644 --- a/project_constants.py +++ b/project_constants.py @@ -22,7 +22,7 @@ V_NAME_OF_WINDOW = "MineFusion TM" DIR_ASSETS = os.path.join("resources", "assets") V_FPS = 60 -ACTION_INTERVAL = 0.6 # interval between two actions in seconds +TURN_INTERVAL = 0.3 # interval between two turns in seconds V_TILE_SIZE = 60 V_GRID_VER_TILES = 10 # vertical (number of rows) @@ -165,6 +165,11 @@ INPUT_ROW = InputBox( position=(_gui_x, _gui_y), dimensions=(_gui_width, _ib_height), text="row", + box_color=(100, 200, 100), + bottom_strip_color=(120, 220, 120), + inner_box_color=(120, 220, 120), + outline_color=(80, 180, 80), + outline_additional_pixel=True, valid_input_characters="1234567890", input_centered=True, clear_input_on_click=True @@ -173,6 +178,11 @@ INPUT_COLUMN = InputBox( position=(_gui_x, _gui_y + _ib_height + 10), dimensions=(_gui_width, _ib_height), text="column", + box_color=(100, 200, 100), + bottom_strip_color=(120, 220, 120), + inner_box_color=(120, 220, 120), + outline_color=(80, 180, 80), + outline_additional_pixel=True, valid_input_characters="1234567890", input_centered=True, clear_input_on_click=True @@ -180,12 +190,18 @@ INPUT_COLUMN = InputBox( RANDOM_BUTTON = Button( position=(_gui_x, _gui_y + 2 * _ib_height + 20), dimensions=(_gui_width, _bt_height), - text="random" + text="random", + box_color=(100, 200, 100), + outline_color=(80, 180, 80), + outline_additional_pixel=True ) OK_BUTTON = Button( position=(_gui_x, _gui_y + 2 * _ib_height + _bt_height + 30), dimensions=(_gui_width, _bt_height), - text="ok" + text="ok", + box_color=(100, 200, 100), + outline_color=(80, 180, 80), + outline_additional_pixel=True ) diff --git a/ui/input_box.py b/ui/input_box.py index 8eb2dc7..eaf5801 100644 --- a/ui/input_box.py +++ b/ui/input_box.py @@ -25,10 +25,10 @@ class InputBox(Button): input_box_position=None, input_box_dimensions=None, user_input="", - valid_input_characters="abcdefghijklmnoprstuwxyz1234567890", + valid_input_characters="abcdefghijklmnoprstuwxyz1234567890 ", box_color=(195, 195, 195), input_box_color=(225, 245, 245), - writing_highlight_color=(150, 255, 255), + writing_highlight_color=(200, 255, 255), inner_box_color=(205, 205, 205), bottom_strip_color=(180, 180, 180), font=pygame.font.get_default_font(), @@ -179,6 +179,7 @@ class InputBox(Button): # is not active else: input_box_color = self.ib_inactive_color + text_background_color = self.ib_inactive_color # drawing inner (upper) box pygame.draw.rect(window, diff --git a/ui/ui_components_list.py b/ui/ui_components_manager.py similarity index 88% rename from ui/ui_components_list.py rename to ui/ui_components_manager.py index 2a07b51..c6eb2a4 100644 --- a/ui/ui_components_list.py +++ b/ui/ui_components_manager.py @@ -3,13 +3,15 @@ from ui.button import Button from ui.input_box import InputBox -class UiComponentsList: +class UiComponentsManager: selected = 0 def __init__(self, ui_components=None, select_first_item=True): + self.ui_components = list() + self.selectable_ui_components = list() + if ui_components is not None: self.ui_components = ui_components - self.selectable_ui_components = list() for component in ui_components: self.selectable_ui_components.append(component) if issubclass(component.__class__, Button) else 0 @@ -17,7 +19,8 @@ class UiComponentsList: else: self.ui_components = list() - self.selectable_ui_components[0].set_is_selected(select_first_item) + if any(self.selectable_ui_components): + self.selectable_ui_components[0].set_is_selected(select_first_item) def run_all(self, window, mouse_position, events): for component in filter(lambda x: x not in self.selectable_ui_components, self.ui_components): @@ -36,7 +39,7 @@ class UiComponentsList: for component in self.selectable_ui_components: component.draw(window, mouse_position) - def switch_selected_objects_with_arrow_keys(self, events, mouse_position): + def switch_selected_objects_with_arrow_keys(self, mouse_position, events): for event in events: if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: