class CreateAccountScene(Scene): header = None name_box = None create_btn = None back_btn = None # Create the various gui elements def start(self, screen): width = screen.get_width() height = screen.get_height() if self.already_loaded: self.name_box.set_text("") else: self.header = Label(pygame.rect.Rect(width / 2 - 200, 10, 400, 30), "Add new account", options={ Options.BACKGROUND: (20, 61, 89), Options.FOREGROUND: (244, 180, 26), Options.BORDER_WIDTH: 0, }) self.name_box = Textbox(pygame.rect.Rect(width/2 - 200, height/2 - 50, 400, 30), "", options={ Options.BORDER_WIDTH: 0, }) self.create_btn = Button(pygame.rect.Rect(width / 2 - 100, height / 2 + 50, 200, 30), "Create", options={ Options.BACKGROUND: (20, 61, 89), Options.FOREGROUND: (244, 180, 26), Options.HOVERED_BACKGROUND: (10, 30, 45), Options.BORDER_WIDTH: 0, }) self.back_btn = Button(pygame.rect.Rect(10, 10, 60, 40), "Back", { Options.BORDER_WIDTH: 0, Options.BACKGROUND: (20, 61, 89), Options.FOREGROUND: (244, 180, 26), Options.HOVERED_BACKGROUND: (10, 30, 45), Options.FONT: pygame.font.SysFont("Comic Sans MS", 15) }) self.already_loaded = True # Check the buttons and switch to corresponding scenes when clicked def update(self, event): for elt in (self.name_box, self.create_btn, self.back_btn): elt.update(event) self.create_btn.set_enabled(len(self.name_box.text) > 0) if self.create_btn.clicked: new_account = Account(self.name_box.text) Account.save_to_file(new_account) Scene.push_scene(1) if self.back_btn.clicked: Scene.push_scene(1) # Clear the screen and draw the gui def draw(self, screen): screen.fill((82, 173, 200)) for elt in (self.header, self.name_box, self.create_btn, self.back_btn): elt.draw(screen)
class SelectAccountScene(Scene): header = None no_account_text = None close_btn = None account_btns = [] visible_acc_btns = [] delete_btns = {} up_btn, down_btn = None, None create_btn = None btn_index = 0 first_offset = None # Create the various gui elements def start(self, screen): width = screen.get_width() height = screen.get_height() if self.already_loaded: btn_options = { Options.BORDER_WIDTH: 0, Options.BACKGROUND: (20, 61, 89), Options.FOREGROUND: (244, 180, 26), Options.HOVERED_BACKGROUND: (10, 30, 45), } del_btn_options = { Options.BORDER_WIDTH: 0, Options.BACKGROUND: (200, 20, 40), Options.FOREGROUND: (244, 180, 26), Options.HOVERED_BACKGROUND: (150, 5, 20), } original_names = [btn.text for btn in self.account_btns] account_names = Account.get_account_names() for name in original_names: if name not in account_names: btn_to_delete = self.account_btns[[ idx for idx, btn in enumerate(self.account_btns) if btn.text == name ][0]] self.account_btns.remove(btn_to_delete) self.delete_btns.pop(btn_to_delete) for name in account_names: if name not in original_names: btn = Button(pygame.rect.Rect(0, 0, 0, 0), name, btn_options) self.account_btns.append(btn) del_btn = Button(pygame.rect.Rect(0, 0, 30, 30), "X", del_btn_options) self.delete_btns[btn] = del_btn self.account_btns.sort(key=(lambda btn: btn.text)) else: self.header = Label(pygame.rect.Rect(width / 2 - 200, 10, 400, 30), "Select account to load", options={ Options.BACKGROUND: (20, 61, 89), Options.FOREGROUND: (244, 180, 26), Options.BORDER_WIDTH: 0, }) close_image = Resources.get("close") close_rect = pygame.rect.Rect(width - 75, 25, 50, 50) self.close_btn = Image( close_rect, close_image, { Options.BACKGROUND: (200, 0, 0), Options.HOVERED_BACKGROUND: (100, 0, 0) }) self.no_account_text = Label( pygame.rect.Rect(width / 2 - 350, height / 2, 550, 30), "No account created. Click the New button to make one.", options={ Options.BACKGROUND: (82, 173, 200), Options.FOREGROUND: (20, 61, 89), Options.BORDER_WIDTH: 0, }) self.first_offset = pygame.rect.Rect(width / 2 - 125, 75, 250, 30) btn_options = { Options.BORDER_WIDTH: 0, Options.BACKGROUND: (20, 61, 89), Options.FOREGROUND: (244, 180, 26), Options.HOVERED_BACKGROUND: (10, 30, 45), } del_btn_options = { Options.BORDER_WIDTH: 0, Options.BACKGROUND: (200, 20, 40), Options.FOREGROUND: (244, 180, 26), Options.HOVERED_BACKGROUND: (150, 5, 20), } account_names = Account.get_account_names() for name in account_names: btn = Button(pygame.rect.Rect(0, 0, 0, 0), name, btn_options) self.account_btns.append(btn) del_btn = Button(pygame.rect.Rect(0, 0, 30, 30), "X", del_btn_options) self.delete_btns[btn] = del_btn btn_options = { Options.BORDER_WIDTH: 0, Options.BACKGROUND: (20, 61, 89), Options.FOREGROUND: (244, 180, 26), Options.HOVERED_BACKGROUND: (10, 30, 45), Options.FONT: pygame.font.SysFont("Comic Sans MS", 15) } self.up_btn = Button( pygame.rect.Rect(width * 5 / 6, height * 2 / 6, 60, 40), "Up", btn_options) self.create_btn = Button( pygame.rect.Rect(width * 5 / 6, height * 3 / 6 - 20, 60, 40), "New", btn_options) self.down_btn = Button( pygame.rect.Rect(width * 5 / 6, height * 4 / 6 - 40, 60, 40), "Down", btn_options) self.already_loaded = True Account.current_account = None self.btn_index = 0 self.reposition_buttons() # Check the buttons and switch to corresponding scenes when clicked def update(self, event): for btn in self.visible_acc_btns: btn.update(event) if btn.clicked: Account.current_account = Account.load_from_file(btn.text) Scene.push_scene(4) self.delete_btns[btn].update(event) if self.delete_btns[btn].clicked: Account.account_to_delete = Account.load_from_file(btn.text) Scene.push_scene(3) for btn in (self.create_btn, self.up_btn, self.down_btn, self.close_btn): btn.update(event) if self.create_btn.clicked: Scene.push_scene(2) elif self.close_btn.clicked: pygame.event.post(pygame.event.Event(pygame.QUIT)) elif self.up_btn.clicked: self.btn_index -= 1 self.reposition_buttons() elif self.down_btn.clicked: self.btn_index += 1 self.reposition_buttons() def reposition_buttons(self): self.visible_acc_btns.clear() offset = 0 for btn in self.account_btns[self.btn_index:self.btn_index + 10]: btn.rect = self.first_offset.copy().move(0, offset) btn.recreate() self.visible_acc_btns.append(btn) offset += 50 self.delete_btns[btn].rect.top = btn.rect.top self.delete_btns[btn].rect.left = btn.rect.right + 10 self.delete_btns[btn].recreate() self.up_btn.set_enabled(self.btn_index > 0) self.down_btn.set_enabled(self.btn_index < len(self.account_btns) - 1) @staticmethod def near_mouse(point, max_dist): mouse_pos = pygame.mouse.get_pos() distance = ((point[0] - mouse_pos[0])**2) + ( (point[1] - mouse_pos[1])**2) return distance < (max_dist**2) # Clear the screen and draw the gui def draw(self, screen): screen.fill((82, 173, 200)) for elt in (self.header, self.close_btn): elt.draw(screen) if len(self.account_btns) == 0: self.no_account_text.draw(screen) else: for btn in self.visible_acc_btns: btn.draw(screen) if self.near_mouse(self.delete_btns[btn].rect.center, 25): self.delete_btns[btn].draw(screen) width, height = screen.get_width(), screen.get_height() scroll_rect = pygame.Rect(width * 5 / 6 + 100, height * 1 / 6, 10, height * 4 / 6) pygame.draw.rect(screen, (100, 100, 100), scroll_rect) height_diff = (height * 4 / 6) / len(self.account_btns) scroll_rect.top = height * 1 / 6 + height_diff * self.btn_index scroll_rect.height = height_diff pygame.draw.rect(screen, (50, 50, 50), scroll_rect) for btn in (self.create_btn, self.up_btn, self.down_btn): btn.draw(screen)
class StatsScene(Scene): header = None back_btn = None statistics = [] visible_stats = [] up_btn, down_btn = None, None stat_index = 0 first_offset = None all_metrics = {} @staticmethod def create_metrics(): StatsScene.all_metrics = { "Total Wins": (lambda acc: acc.stats[Account.TOTAL_WINS]), "Total Losses": (lambda acc: acc.stats[Account.TOTAL_LOSSES]), "Total Money": (lambda acc: acc.stats[Account.TOTAL_MONEY]), "Unlocked Pens": (lambda acc: acc.stats[Account.UNLOCKED_PENS]), } def start(self, screen): width = screen.get_width() height = screen.get_height() if not self.already_loaded: StatsScene.create_metrics() self.header = Label(pygame.rect.Rect(width / 2 - 200, 10, 400, 30), "Statistics", options={ Options.BACKGROUND: (20, 61, 89), Options.FOREGROUND: (244, 180, 26), Options.BORDER_WIDTH: 0, }) self.first_offset = pygame.rect.Rect(width / 2 - 125, 75, 250, 30) label_options = { Options.BACKGROUND: (82, 173, 200), Options.FOREGROUND: (20, 61, 89), Options.BORDER_WIDTH: 0 } for metric in StatsScene.all_metrics.keys(): text = f"{metric}: {StatsScene.all_metrics[metric](Account.current_account)}" stat_lbl = Label(pygame.rect.Rect(0, 0, 0, 0), text, label_options) self.statistics.append(stat_lbl) btn_options = { Options.BORDER_WIDTH: 0, Options.BACKGROUND: (20, 61, 89), Options.FOREGROUND: (244, 180, 26), Options.HOVERED_BACKGROUND: (10, 30, 45), Options.FONT: pygame.font.SysFont("Comic Sans MS", 15) } self.back_btn = Button(pygame.rect.Rect(10, 10, 60, 40), "Back", btn_options) self.up_btn = Button( pygame.rect.Rect(width * 5 / 6, height * 2 / 6, 60, 40), "Up", btn_options) self.down_btn = Button( pygame.rect.Rect(width * 5 / 6, height * 4 / 6 - 40, 60, 40), "Down", btn_options) self.already_loaded = True self.stat_index = 0 self.reposition_stats() def update(self, event): for btn in (self.back_btn, self.up_btn, self.down_btn): btn.update(event) if self.back_btn.clicked: Scene.pop_scene() if self.up_btn.clicked: self.stat_index -= 1 self.reposition_stats() elif self.down_btn.clicked: self.stat_index += 1 self.reposition_stats() def reposition_stats(self): self.visible_stats.clear() offset = 0 for stat in self.statistics[self.stat_index:self.stat_index + 10]: stat.rect = self.first_offset.copy().move(0, offset) stat.recreate() self.visible_stats.append(stat) offset += 50 self.up_btn.set_enabled(self.stat_index > 0) self.down_btn.set_enabled(self.stat_index < len(self.statistics) - 1) def draw(self, screen): screen.fill((82, 173, 200)) self.header.draw(screen) for stat in self.visible_stats: stat.draw(screen) width, height = screen.get_width(), screen.get_height() scroll_rect = pygame.rect.Rect(width * 5 / 6 + 100, height * 1 / 6, 10, height * 4 / 6) pygame.draw.rect(screen, (100, 100, 100), scroll_rect) height_diff = (height * 4 / 6) / len(self.statistics) scroll_rect.top = height * 1 / 6 + height_diff * self.stat_index scroll_rect.height = height_diff pygame.draw.rect(screen, (50, 50, 50), scroll_rect) for btn in (self.back_btn, self.up_btn, self.down_btn): btn.draw(screen)
class PenSelectScene(Scene): header = None back_btn = None select_btn = None purchase_btn = None name_text = None density_text = None restitution_text = None description_lines = [] coins_image = None coins_text = None pen_images = [] visible_pen_images = [] left_btn, right_btn = None, None center_pos = None pen_index = 0 def start(self, screen): if not self.already_loaded: PenData.load_all_pens(Resources.get("all_pens")) width = screen.get_width() height = screen.get_height() self.back_btn = Button( pygame.Rect(10, 10, 60, 40), "Back", { Options.BORDER_WIDTH: 0, Options.BACKGROUND: (20, 61, 89), Options.FOREGROUND: (244, 180, 26), Options.HOVERED_BACKGROUND: (10, 30, 45), Options.FONT: pygame.font.SysFont("Comic Sans MS", 15) }) label_options = { Options.BACKGROUND: (20, 61, 89), Options.FOREGROUND: (244, 180, 26), Options.BORDER_WIDTH: 0, } self.header = Label(pygame.Rect(width / 2 - 200, 10, 400, 30), "Select your weapon!", label_options) self.coins_text = Label( pygame.Rect(width - 110, height - 55, 100, 40), "0", label_options) label_options = { Options.BACKGROUND: (82, 173, 200), Options.FOREGROUND: (20, 61, 89), Options.BORDER_WIDTH: 0, Options.FONT: pygame.font.SysFont("Comic Sans MS", 18) } self.density_text = Label(pygame.Rect(width / 5, 110, 100, 20), "Density: ", label_options) self.restitution_text = Label(pygame.Rect(width / 5, 130, 100, 20), "Restitution: ", label_options) self.name_text = Label( pygame.Rect(width / 2 - 45, height - 125, 90, 50), "", label_options) self.description_lines = [ Label(pygame.Rect(width * 2 / 3, 100 + i * 25, 100, 20), "", label_options) for i in range(0, 3) ] self.coins_image = Image( pygame.Rect(width - 175, height - 60, 50, 50), Resources.get("coin"), {Options.BACKGROUND: (82, 173, 200)}) btn_options = { Options.BORDER_WIDTH: 0, Options.BACKGROUND: (20, 61, 89), Options.FOREGROUND: (244, 180, 26), Options.HOVERED_BACKGROUND: (10, 30, 45), Options.FONT: pygame.font.SysFont("Comic Sans MS", 25) } self.left_btn = Button(pygame.Rect(10, height / 2 - 20, 20, 30), "<", btn_options) self.right_btn = Button( pygame.Rect(width - 30, height / 2 - 20, 20, 30), ">", btn_options) self.select_btn = Button( pygame.Rect(width / 2 - 45, height - 75, 90, 50), "Select", btn_options) self.purchase_btn = Button( pygame.Rect(width / 2 - 125, height - 75, 250, 40), "", btn_options) self.center_pos = pygame.Rect(width / 2 - 50, height / 2 - 50, 100, 100) for pen in PenData.all_pens: self.pen_images.append( Image(self.center_pos, Resources.get(pen.image_file), {Options.BACKGROUND: (82, 173, 200)})) self.already_loaded = True self.reposition_images() self.update_shop_data() self.reset_coin_text() def update(self, event): for elt in (self.back_btn, self.left_btn, self.right_btn): elt.update(event) cur_pen = PenData.all_pens[self.pen_index] if cur_pen.name in Account.current_account.pens: self.select_btn.update(event) if self.select_btn.clicked: PenData.current_pen = cur_pen Scene.push_scene(6) else: self.purchase_btn.set_enabled( Account.current_account.money >= cur_pen.cost) self.purchase_btn.update(event) if self.purchase_btn.clicked: Account.current_account.purchase_pen(cur_pen) self.reset_coin_text() if self.back_btn.clicked: Scene.pop_scene() if self.left_btn.clicked or self.right_btn.clicked: if self.left_btn.clicked: self.pen_index -= 1 self.reposition_images() elif self.right_btn.clicked: self.pen_index += 1 self.reposition_images() self.update_shop_data() def reposition_images(self): self.visible_pen_images.clear() if self.pen_index > 0: img = self.pen_images[self.pen_index - 1] self.visible_pen_images.append(img) img.rect = self.center_pos.copy().move(-300, 40) self.visible_pen_images.append(self.pen_images[self.pen_index]) self.pen_images[self.pen_index].rect = self.center_pos if self.pen_index < len(self.pen_images) - 1: img = self.pen_images[self.pen_index + 1] self.visible_pen_images.append(img) img.rect = self.center_pos.copy().move(300, 40) self.left_btn.set_enabled(self.pen_index > 0) self.right_btn.set_enabled(self.pen_index < len(self.pen_images) - 1) def reset_coin_text(self): self.coins_text.text = str(Account.current_account.money) self.coins_text.recreate() def update_shop_data(self): cur_pen = PenData.all_pens[self.pen_index] self.name_text.text = cur_pen.name self.name_text.recreate() self.density_text.text = f"Density: {cur_pen.density}" self.density_text.recreate() self.restitution_text.text = f"Restitution: {cur_pen.restitution}" self.restitution_text.recreate() line_index = 0 for line in cur_pen.description: self.description_lines[line_index].text = line self.description_lines[line_index].recreate() line_index += 1 if cur_pen.name not in Account.current_account.pens: self.purchase_btn.text = f"Purchase for {cur_pen.cost}" self.purchase_btn.recreate() def draw(self, screen): screen.fill((82, 173, 200)) for elt in (self.header, self.back_btn, self.left_btn, self.right_btn, self.name_text, self.coins_image, self.coins_text, self.density_text, self.restitution_text): elt.draw(screen) for line in self.description_lines: line.draw(screen) if PenData.all_pens[ self.pen_index].name in Account.current_account.pens: self.select_btn.draw(screen) else: self.purchase_btn.draw(screen) for img in self.visible_pen_images: img.draw(screen)
class EnemySelectScene(Scene): header = None back_btn = None select_btn = None name_text = None random_diff_btn = None easy_btn = None normal_btn = None hard_btn = None difficulty = -1 random_pen_data = None pen_images = [] visible_pen_images = [] left_btn, right_btn = None, None center_pos = None pen_index = 0 def start(self, screen): if not self.already_loaded: PenData.load_all_pens() width = screen.get_width() height = screen.get_height() self.back_btn = Button( pygame.Rect(10, 10, 60, 40), "Back", { Options.BORDER_WIDTH: 0, Options.BACKGROUND: (20, 61, 89), Options.FOREGROUND: (244, 180, 26), Options.HOVERED_BACKGROUND: (10, 30, 45), Options.FONT: pygame.font.SysFont("Comic Sans MS", 15) }) self.header = Label( pygame.Rect(width / 2 - 200, 10, 400, 30), "Choose your opponent!", { Options.BACKGROUND: (20, 61, 89), Options.FOREGROUND: (244, 180, 26), Options.BORDER_WIDTH: 0, }) self.name_text = Label( pygame.Rect(width / 2 - 45, height - 125, 90, 50), "", { Options.BACKGROUND: (82, 173, 200), Options.FOREGROUND: (20, 61, 89), Options.BORDER_WIDTH: 0, Options.FONT: pygame.font.SysFont("Comic Sans MS", 18) }) btn_options = { Options.BORDER_WIDTH: 0, Options.BACKGROUND: (20, 61, 89), Options.FOREGROUND: (244, 180, 26), Options.HOVERED_BACKGROUND: (10, 30, 45), Options.FONT: pygame.font.SysFont("Comic Sans MS", 25) } self.left_btn = Button(pygame.Rect(10, height / 2 - 20, 20, 30), "<", btn_options) self.right_btn = Button( pygame.Rect(width - 30, height / 2 - 20, 20, 30), ">", btn_options) self.select_btn = Button( pygame.Rect(width / 2 - 45, height - 75, 90, 50), "Select", btn_options) btn_options[Options.TOGGLED_BACKGROUND] = (5, 20, 30) self.random_diff_btn = ToggleButton( pygame.Rect(width * 1 / 5 - 50, 100, 100, 30), "Random", btn_options) self.easy_btn = ToggleButton( pygame.Rect(width * 2 / 5 - 50, 100, 100, 30), "Easy", btn_options) self.normal_btn = ToggleButton( pygame.Rect(width * 3 / 5 - 50, 100, 100, 30), "Normal", btn_options) self.hard_btn = ToggleButton( pygame.Rect(width * 4 / 5 - 50, 100, 100, 30), "Hard", btn_options) toggle_group = [ self.random_diff_btn, self.easy_btn, self.normal_btn, self.hard_btn ] for elt in toggle_group: elt.set_group(toggle_group) self.easy_btn.toggle() self.center_pos = pygame.Rect(width / 2 - 50, height / 2 - 50, 100, 100) self.random_pen_data = PenData.dict_to_pen({ "name": "Random", "image_file": "random_pen" }) for pen in [self.random_pen_data] + PenData.all_pens: self.pen_images.append( Image(self.center_pos, Resources.get(pen.image_file), {Options.BACKGROUND: (82, 173, 200)})) self.already_loaded = True self.reposition_images() self.update_enemy_data() def update(self, event): for elt in (self.back_btn, self.left_btn, self.right_btn, self.select_btn, self.random_diff_btn, self.easy_btn, self.normal_btn, self.hard_btn): elt.update(event) if self.select_btn.clicked: PenData.current_enemy_pen = \ random.choice(PenData.all_pens) if self.pen_index == 0 else \ PenData.all_pens[self.pen_index - 1] PenData.current_enemy_difficulty = \ AIDifficulty.EASY if self.easy_btn.toggled else \ AIDifficulty.NORMAL if self.normal_btn.toggled else \ AIDifficulty.HARD if self.hard_btn.toggled else \ random.choice(list(AIDifficulty)) Scene.push_scene(7) if self.back_btn.clicked: Scene.pop_scene() if self.left_btn.clicked or self.right_btn.clicked: if self.left_btn.clicked: self.pen_index -= 1 self.reposition_images() elif self.right_btn.clicked: self.pen_index += 1 self.reposition_images() self.update_enemy_data() def reposition_images(self): self.visible_pen_images.clear() if self.pen_index > 0: img = self.pen_images[self.pen_index - 1] self.visible_pen_images.append(img) img.rect = self.center_pos.copy().move(-300, 40) self.visible_pen_images.append(self.pen_images[self.pen_index]) self.pen_images[self.pen_index].rect = self.center_pos if self.pen_index < len(self.pen_images) - 1: img = self.pen_images[self.pen_index + 1] self.visible_pen_images.append(img) img.rect = self.center_pos.copy().move(300, 40) self.left_btn.set_enabled(self.pen_index > 0) self.right_btn.set_enabled(self.pen_index < len(self.pen_images) - 1) def update_enemy_data(self): self.name_text.text = self.random_pen_data.name if self.pen_index == 0 else \ PenData.all_pens[self.pen_index - 1].name self.name_text.recreate() def draw(self, screen): screen.fill((82, 173, 200)) for elt in (self.header, self.back_btn, self.left_btn, self.right_btn, self.name_text, self.select_btn, self.easy_btn, self.normal_btn, self.hard_btn, self.random_diff_btn): elt.draw(screen) for img in self.visible_pen_images: img.draw(screen)