class MainMenu(object): """The Main menu object class which contains all children. Initialized with the screen resolution as an (x, y) tuple """ def __init__(self, resolution, *args, **kwargs): self.resolution = resolution self.default_name = "Player 1" self.running = True self.updating_name = False self.help = False self.buffer = 5 # px self.font = pygame.font.SysFont("arial", 20) self.magic_sequence = [273, 273, 274, 274, 276, 275, 276, 275, 98, 97] self.magic_available = ["width", "height", "nexts", "blocksize", "fallrate", "bonus_block_rate", "show_shape_spawn_rate"] self.magic_enabled = [] self.magic = [] self.magical = False super(MainMenu, self).__init__(*args, **kwargs) def load_player_data(self, player_name=None): """Loads the player's data, or default_name's.""" self.player_name = player_name or self.default_name self.data = UserData(self.player_name) def run_forever(self): """Maybe not quite forever, but until the user quits.""" # start the screen object screen = pygame.display.set_mode(self.resolution) clock = pygame.time.Clock() # background = load_image("background.png") users = get_users() if len(users) == 1: self.load_player_data(os.path.splitext(users[0])[0]) else: self.load_player_data() # TODO: player listing self.menu = KezMenu( ["Arcade Mode", lambda: GameBoard().main(screen, self)], [self.player_name, self.update_player_name], ["Controls", lambda : setattr(self, "help", not self.help)], ["Reset Scores", self.reset_scores], ["Quit", lambda: setattr(self, 'running', False)], ) # TODO: change this. should make a player screen where they can see # what accounts have been made locally, what their stats are, # what their upper limits are, rename them, cheat, etc... PLAYER_INDEX = 1 # index of the player's name self.menu.x = self.resolution[0] // 3 self.menu.y = self.resolution[1] // 2 # self.menu.enableEffect('raise-col-padding-on-focus', enlarge_time=0.1) render = lambda x : self.font.render(x, True, (222, 222, 222)) while self.running: events = pygame.event.get() if self.updating_name is True: events = self.handle_name_change(events) elif self.magic_enabled: for setting in self.magic_enabled: events = self._handle_magic_change(events, setting) if self.player_name == self.default_name: menu_name = "{} -- Click to change".format(self.default_name) else: menu_name = self.player_name self.menu.options[PLAYER_INDEX]["label"] = menu_name self.handle_magic(events) self.menu.update(events or [], clock.tick(30) / 1000.0) screen.fill((99, 99, 99)) # TODO: make better # screen.blit(background, (0, 0)) if self.help: # add controls/help to bottom controls = [ # "-- Controls --", "left: ← or A", "right: → or D", "down: ↓ or S", "rotate clockwise: ↑, W, or E", "rotate counter-clockwise: Q", "hold/swap: H", "slam down: space bar", "pause: P", ] # get the largest possible control label size control_size = self.font.size(max(controls, key=len)) # px buffer for i, control in enumerate(controls): screen.blit(render(control), ( self.resolution[0] - (control_size[0] + self.buffer), self.resolution[1] - ( (control_size[1] * len(controls)) - (i * control_size[1]) + self.buffer ) )) if self.data["wins"] or self.data["losses"]: label = "wins: {wins} losses: {losses}".format(**self.data.data) win_loss = render(label) win_loss_size = self.font.size(label) screen.blit(win_loss, ( (self.resolution[0] / 2) - (win_loss_size[0] / 2), self.buffer, )) if self.magical: self._magic_method() self.menu.draw(screen) pygame.display.flip() def _handle_keypress(self, events, keylist, cancel_list=None): """Handles keypresses into a list. Args:: events: list from pygame.event.get() keylist: list to append/delete from on user input cancel_list: list to return to should the user cancel, or None Returns: Events not consumed, an empty list, or None on cancel """ if cancel_list is None: cancel_list = keylist scrubbed_events = [] for event in events: if event.type == pygame.KEYDOWN: if event.key == 27: # esc/cancel keylist = cancel_list return None elif event.key in (92, 8): # backspace(s) keylist = keylist[:-1] elif event.key not in (13,) and len(keylist) <= 12: # 13 == enter and 12 is some limit to name length try: key = chr(event.key) except ValueError: if event.key not in range(273, 277): scrubbed_events.append(event) else: if pygame.key.get_mods() in (1, 8192): # checks for shift or caps lock, and both key = key.capitalize() keylist += key else: scrubbed_events.append(event) else: scrubbed_events.append(event) return scrubbed_events, keylist def handle_name_change(self, events): """Proccess events during a name change.""" events, player_name = self._handle_keypress( events, self.player_name, self._old_player_name, ) self.player_name = player_name if events is None: self.update_player_name() return [] else: return events def _handle_magic_change(self, events, keylist): if not events: return events, updated = self._handle_keypress( events, keylist=self.data.data[keylist], cancel_list=self.data.data["old_{}".format(keylist)], ) self.data[keylist] = updated return events or [] def _magic_width(self): self._magic_int_change("width") def _magic_height(self): self._magic_int_change("height") def _magic_nexts(self): self._magic_int_change("nexts") def _magic_blocksize(self): self._magic_int_change("blocksize") def _magic_fallrate(self): self._magic_int_change("fallrate") def _magic_bonus_block_rate(self): self._magic_int_change("bonus_block_rate") def _magic_int_change(self, key): if not "old_{}".format(key) in self.data: self.data["old_{}".format(key)] = self.data[key] self.magic_enabled.append(key) self.data[key] = "" else: old_setting = self.data.pop("old_{}".format(key)) try: self.data[key] = int(self.data[key]) except: self.data[key] = old_setting self.data.save() self.magic_enabled = [] def _magic_show_shape_spawn_rate(self): self.data["show_shape_spawn_rate"] = not self.data["show_shape_spawn_rate"] self.data.save() def _magic_method(self): """Just checks the time.""" if not hasattr(self, "_magic_init"): self._magic_init = True for key in sorted(self.magic_available, key=len): self.menu.options.append({ "label": "{}: {}".format(key, self.data[key]), "callable": getattr(self, "_magic_{}".format(key)), }) else: for key, val in self.data.items(): for option in self.menu.options: if option["label"].startswith(key): option["label"] = "{}: {}".format(key, val) break def handle_magic(self, events): """Doesn't do anything.""" for e in events or []: if e.type == 2: self.magic.append(e.key) for i, q in zip(self.magic, self.magic_sequence): if i != q: self.magic = []; break else: if len(self.magic) == len(self.magic_sequence): self.magical = True def update_player_name(self): """Called when someone clicks on the player name.""" if self.updating_name: # the 'or' here is to catch hitting enter twice with no new input self.load_player_data(self.player_name or self._old_player_name) self.menu.mouse_enabled = True self._old_player_name = None self.updating_name = False else: self.updating_name = True self._old_player_name = self.player_name # save this for cancel self.player_name = "" self.menu.mouse_enabled = False def reset_scores(self): """Called to reset all user data and reload this player's data.""" reset_users() self.load_player_data(self.player_name)
def run_forever(self): """Maybe not quite forever, but until the user quits.""" # start the screen object screen = pygame.display.set_mode(self.resolution) clock = pygame.time.Clock() # background = load_image("background.png") users = get_users() if len(users) == 1: self.load_player_data(os.path.splitext(users[0])[0]) else: self.load_player_data() # TODO: player listing self.menu = KezMenu( ["Arcade Mode", lambda: GameBoard().main(screen, self)], [self.player_name, self.update_player_name], ["Controls", lambda : setattr(self, "help", not self.help)], ["Reset Scores", self.reset_scores], ["Quit", lambda: setattr(self, 'running', False)], ) # TODO: change this. should make a player screen where they can see # what accounts have been made locally, what their stats are, # what their upper limits are, rename them, cheat, etc... PLAYER_INDEX = 1 # index of the player's name self.menu.x = self.resolution[0] // 3 self.menu.y = self.resolution[1] // 2 # self.menu.enableEffect('raise-col-padding-on-focus', enlarge_time=0.1) render = lambda x : self.font.render(x, True, (222, 222, 222)) while self.running: events = pygame.event.get() if self.updating_name is True: events = self.handle_name_change(events) elif self.magic_enabled: for setting in self.magic_enabled: events = self._handle_magic_change(events, setting) if self.player_name == self.default_name: menu_name = "{} -- Click to change".format(self.default_name) else: menu_name = self.player_name self.menu.options[PLAYER_INDEX]["label"] = menu_name self.handle_magic(events) self.menu.update(events or [], clock.tick(30) / 1000.0) screen.fill((99, 99, 99)) # TODO: make better # screen.blit(background, (0, 0)) if self.help: # add controls/help to bottom controls = [ # "-- Controls --", "left: ← or A", "right: → or D", "down: ↓ or S", "rotate clockwise: ↑, W, or E", "rotate counter-clockwise: Q", "hold/swap: H", "slam down: space bar", "pause: P", ] # get the largest possible control label size control_size = self.font.size(max(controls, key=len)) # px buffer for i, control in enumerate(controls): screen.blit(render(control), ( self.resolution[0] - (control_size[0] + self.buffer), self.resolution[1] - ( (control_size[1] * len(controls)) - (i * control_size[1]) + self.buffer ) )) if self.data["wins"] or self.data["losses"]: label = "wins: {wins} losses: {losses}".format(**self.data.data) win_loss = render(label) win_loss_size = self.font.size(label) screen.blit(win_loss, ( (self.resolution[0] / 2) - (win_loss_size[0] / 2), self.buffer, )) if self.magical: self._magic_method() self.menu.draw(screen) pygame.display.flip()
def run_forever(self): """Maybe not quite forever, but until the user quits.""" # start the screen object screen = pygame.display.set_mode(self.resolution) pygame.display.set_caption("The Tragedy of the Falling Sky v{}".format( __version__ )) clock = pygame.time.Clock() users = get_users() if len(users) == 1: self.load_player_data(os.path.splitext(users[0])[0]) else: self.load_player_data() # TODO: player listing self.menu = KezMenu( ["Arcade Mode", lambda: GameBoard().main(screen, self)], [self.player_name, self.update_player_name], ["Controls", lambda: setattr(self, "help", not self.help)], ["Reset Scores", self.reset_scores], ["Quit", lambda: setattr(self, 'running', False)], ) # TODO: change this. should make a player screen where they can see # what accounts have been made locally, what their stats are, # what their upper limits are, rename them, cheat, etc... PLAYER_INDEX = 1 # index of the player's name self.menu.x = self.resolution[0] // 2.5 self.menu.y = self.resolution[1] // 2 self.menu.color = Blocks.rgba_codes["light_blue"] self.menu.focus_color = Blocks.rgba_codes["dark_pink"] background = load_image("background.png") background = pygame.transform.scale(background, self.resolution) render = lambda x: self.font.render(x, True, self.menu.color) render_red = lambda x: self.font.render(x, True, self.menu.focus_color) while self.running: events = pygame.event.get() if self.updating_name is True: events = self.handle_name_change(events) elif self.magic_enabled: for setting in self.magic_enabled: events = self._handle_magic_change(events, setting) if self.player_name == self.default_name: menu_name = "{} -- Click to change".format(self.default_name) else: menu_name = self.player_name self.menu.options[PLAYER_INDEX]["label"] = menu_name self.handle_magic(events) self.menu.update(events or [], clock.tick(30) / 1000.0) screen.blit(background, (0, 0)) # drop shadow behind options list max_width = 0 height = 0 for option in self.menu.options: font = option.get("font", self.menu._font) size = font.size(option["label"]) max_width = max(max_width, size[0]) height += size[1] options_bg = pygame.Surface( (max_width + 10, height + 10), flags=pygame.SRCALPHA, ) options_bg.fill((0, 0, 0, 150)) screen.blit(options_bg, ( self.menu.x - 5, self.menu.y - 5, )) # title text small = self.small_title.render("the tragedy of the", True, Blocks.rgba_codes["dark_pink"]) small_size = self.small_title.size("the tragedy of the") big = self.big_title.render("FALLING SKY", True, Blocks.rgba_codes["teal"]) big_size = self.big_title.size("FALLING SKY") title_bg = pygame.Surface( (big_size[0] + 10, big_size[1] + small_size[1] + 10), flags=pygame.SRCALPHA, ) title_bg.fill((0, 0, 0, 150)) screen.blit(title_bg, ( (self.resolution[0] // 2) - (big_size[0] // 2) - 5, 115, )) screen.blit(small, (self.resolution[0] // 2 - (small_size[0] // 2), 120)) screen.blit(big, (self.resolution[0] // 2 - (big_size[0] // 2), 150)) if self.help: # add controls/help to bottom controls = [ # "-- Controls --", "left: ← or A", "right: → or D", "down: ↓ or S", "rotate clockwise: ↑, W, or E", "rotate counter-clockwise: Q", "hold/swap: H", "slam down: space bar", "pause: P", ] # get the largest possible control label size control_size = (0, 0) for control in controls: size = self.font.size(control) if size[0] > control_size[0]: control_size = size help_bg = pygame.Surface( ( control_size[0] + 10, ((control_size[1] + self.buffer) * (len(controls)) ) + 10, ), flags=pygame.SRCALPHA, ) help_bg.fill((0, 0, 0, 150)) screen.blit(help_bg, ( 25, self.resolution[1] - ( (control_size[1] + self.buffer) * (len(controls)) ) + 5 )) for i, control in enumerate(controls): screen.blit(render_red(control), ( 30, self.resolution[1] - ( (control_size[1] * (len(controls) + 1)) - (i * control_size[1]) + self.buffer ) )) if self.data["wins"] or self.data["losses"]: label = "{user_id} wins: {wins} losses: {losses}".format( **self.data ) win_loss = render(label) win_loss_size = self.font.size(label) win_loss_bg = pygame.Surface( (win_loss_size[0] + 10, win_loss_size[1] + 10), flags=pygame.SRCALPHA, ) win_loss_bg.fill((0, 0, 0, 150)) screen.blit(win_loss_bg, ( (self.resolution[0] / 2) - (win_loss_size[0] / 2) - 5, 0, )) screen.blit(win_loss, ( (self.resolution[0] / 2) - (win_loss_size[0] / 2), self.buffer, )) if self.magical: self._magic_method() self.menu.draw(screen) pygame.display.flip()