def get_wall_binary(self, node: PVector) -> str: """ Get surrounding tiles and check if they are walls. :param node: :return: """ surrounding = '' # Left if self.is_wall(node + PVector(-1, 0)): surrounding += '1' else: surrounding += '0' # Right if self.is_wall(node + PVector(1, 0)): surrounding += '1' else: surrounding += '0' # Up if self.is_wall(node + PVector(0, -1)): surrounding += '1' else: surrounding += '0' # Down if self.is_wall(node + PVector(0, 1)): surrounding += '1' else: surrounding += '0' return surrounding
def vert_offset_middle(self, surf: pygame.Surface, offset: PVector): """ Get Top Left but with vertical offset option :param surf: :param offset: :return: """ return PVector.to_tuple( PVector.from_tuple(self.get_top_left(surf)) + offset)
def write_attr(self, information) -> int: """ Writes an attribute of the map to the level. :param information: An array of strings, taken from the file. :return: None """ attr = information[1] if attr == 'lvlwidth': # Set the level width self.level_width = int(information[2]) elif attr == 'lvlheight': # Set the level height self.level_height = int(information[2]) elif 'colour' in attr: # Set one of the colour attributes red = int(information[2]) green = int(information[3]) blue = int(information[4]) colour_tup = (red, green, blue, 255) if attr == 'edgecolour': self.edge_light_colour = colour_tup self.edge_shadow_colour = colour_tup elif attr == 'edgelightcolour': self.edge_light_colour = colour_tup elif attr == 'edgeshadowcolour': self.edge_shadow_colour = colour_tup elif attr == 'fillcolour': self.fill_colour = colour_tup elif attr == 'pelletcolour': self.pellet_colour = colour_tup elif attr == 'bgcolour': self.bg_colour = colour_tup else: return -1 # An error has occurred elif 'ghost' in attr: # Add ghost corners for scatter mode corner_1 = PVector(int(information[2]), int(information[3])) corner_2 = PVector(int(information[4]), int(information[5])) if 'blinky' in attr: self.blinky_start.add_corners(corner_1, corner_2) if 'pinky' in attr: self.pinky_start.add_corners(corner_1, corner_2) if 'inky' in attr: self.inky_start.add_corners(corner_1, corner_2) if 'clyde' in attr: self.clyde_start.add_corners(corner_1, corner_2) elif attr == 'fruittype': # Add fruit type for point values self.fruit_id = int(information[2]) elif attr == 'startleveldata': logging.debug('Now reading level data.') return 1 # Toggles is_reading_level_data and sets row_num to 0. elif attr == 'endleveldata': logging.debug('Level data has been read.') return 2 else: # An error has occurred return -1 return 0 # No problems
def get_top_left(self, surf: pygame.Surface): """ Utility :param surf: :return: """ surf_half_size = PVector.from_tuple(surf.get_size()) / 2 # Center screen_center = PVector.from_tuple( self.screen.get_size()) / 2 # Center return PVector.to_tuple(screen_center - surf_half_size) # Offset
def adjust_speed(self): """ Try to speed up if we are on the correct increment to ensure that we land on the node perfectly. :return: """ if self.pos % 4 == PVector(0, 0): # Quadruple Speed self.speed = 4 elif self.pos % 2 == PVector(0, 0): # Double Speed self.speed = 2 elif self.pos % 1 == PVector(0, 0): # Normal Speed self.speed = 1 else: # Half speed, continue pass
def draw_pacman_logo(self): """ Utility :return: """ logo = self.level.get_logo() self.screen.blit(logo, self.vert_offset_middle(logo, PVector(0, -100)))
def draw_query(self): """ Draw query for text box entry :return: """ text_surf = self.render_text("PLEASE ENTER YOUR NAME:") self.screen.blit(text_surf, self.vert_offset_middle(text_surf, PVector(0, -26)))
def draw_begin_prompt(self): """ Draw Prompt to begin :return: """ font = self.create_font(16) text = self.render_text('Press any key to begin', font) self.screen.blit(text, self.vert_offset_middle(text, PVector(0, 100)))
def draw_quit_hint(self): """ Draw quit hint :return: """ text_surf = self.render_text("Press q to quit, r to try again", self.create_font(16)) self.screen.blit(text_surf, self.vert_offset_middle(text_surf, PVector(0, 150)))
def draw_level(self): """ Draws each tile in the level :return: """ for row in range(self.level.height()): for col in range(self.level.width()): surf = self.level.get_tile_surf(PVector(col, row)) self.screen.blit(surf, (col * 16, row * 16))
def update_surf(self): """ Update surfs based on Pacman's direction :return: """ self.increment_frame_num() if self.direc == PVector(self.speed, 0): self.surf = self.right_surf[self.anim_num] return if self.direc == PVector(-self.speed, 0): self.surf = self.left_surf[self.anim_num] return if self.direc == PVector(0, self.speed): self.surf = self.down_surf[self.anim_num] return if self.direc == PVector(0, -self.speed): self.surf = self.up_surf[self.anim_num] return
def check_node(self): """ Makes sure that we don't crash into a wall. :return: """ if self.is_on_node(): self.check_teleport() if not self.level.is_safe(self.nearest_node + self.direc): # If it isn't safe anymore self.direc = PVector(0, 0)
def calc_teleport(self, node: PVector) -> PVector: """ Calculate the destination teleport :param node: PVector :return: Destination """ assert node.x == 0 or node.x == self.level_width - 1 or node.y == 0 or node.y == self.level_height - 1 # Make sure it is on the edge of the map # Horizontal teleport if node.x == 0: return PVector(self.level_width - 1, node.y) if node.x == self.level_width - 1: return PVector(0, node.y) # Vertical Teleport if node.y == 0: return PVector(node.x, self.level_height - 1) if node.y == self.level_height - 1: return PVector(node.x, 0)
def get_key_strokes(self) -> None: """ Get pressed keys and try to turn in each of the directions. Only one key is processed at one time to prevent phasing through walls. :return: """ keys_pressed = pygame.key.get_pressed() if any(keys_pressed[key] for key in [pygame.K_d, pygame.K_RIGHT]): # Right self.try_to_turn(PVector(DEFAULT_SPEED, 0)) return if any(keys_pressed[key] for key in [pygame.K_a, pygame.K_LEFT]): # Left self.try_to_turn(PVector(-DEFAULT_SPEED, 0)) return if any(keys_pressed[key] for key in [pygame.K_w, pygame.K_UP]): # Up self.try_to_turn(PVector(0, -DEFAULT_SPEED)) return if any(keys_pressed[key] for key in [pygame.K_s, pygame.K_DOWN]): # Down self.try_to_turn(PVector(0, DEFAULT_SPEED)) return
def update_direc(self) -> None: """ Chases pacman if far away but runs away to corner if too close. :return: """ if abs(self.nearest_node - self.pacman.nearest_node) < PVector( 8, 8): # If Clyde is too close, he runs away self.scatter() else: self.direc = self.path_to( self.pacman.nearest_node) # Otherwise he behaves like blinky
def try_to_turn(self, direc: PVector) -> None: # BUG TESTED """ Turns orthogonally if we're directly on the node Tries to cut the corner if possible :param direc: :return: """ if self.level.is_safe(self.nearest_node + direc) and self.is_on_grid_line(): if self.is_on_node(): self.direc = direc # Right angle turn elif self.is_turning(direc): if abs(self.pos % 16) < PVector(MAX_CUT, MAX_CUT): # If we can cut self.diagonal_move = self.direc + direc # Move in both directions at once self.cut_corner = True self.direc = direc
def get_surrounding_accessibles(self, node: PVector) -> set: """ Gets surrounding nodes that are safe :param node: :return: set of safe nodes to be added to edges dictionary """ safe_nodes = set() # Left if self.is_safe(node + PVector(1, 0)): safe_nodes.add(node + PVector(1, 0)) # Right if self.is_safe(node + PVector(-1, 0)): safe_nodes.add(node + PVector(-1, 0)) # Down if self.is_safe(node + PVector(0, 1)): safe_nodes.add(node + PVector(0, 1)) # Up if self.is_safe(node + PVector(0, -1)): safe_nodes.add(node + PVector(0, -1)) return safe_nodes
def enter_box(self): """ Just like exit_box, but is the opposite. :return: """ if self.nearest_node == self.start: # We can now switch states. self.direc = PVector(0, 0) self.increment_cycle() elif self.nearest_node == self.level.ghost_door: # We're on the door, now to get in exit_node, = tuple(self.level.edges[self.level.ghost_door]) opposite_direc = self.direc_to( exit_node) * -1 # Move in the opposite direction as the exit self.direc = opposite_direc elif self.in_ghost_box(): # Move to start location self.direc = self.direc_to(self.start) elif self.nearest_node in self.level.edges[ self.level.ghost_door]: # We're right outside the ghost door self.direc = self.direc_to(self.level.ghost_door) else: # Make our way to the ghost door self.direc = self.closest_direction(self.level.ghost_door)
def load_map(self, level_num): """ Resets the level class and loads a new map :param level_num: The level number """ self.reset() f = open( os.path.join(LEVEL_FOLDER, r'level_' + str(level_num) + '.txt'), 'r') logging.info('Level {} successfully opened'.format(level_num)) # log line_num = 0 row_num = 0 use_line = False is_reading_level_data = False for line in f: line_num += 1 str_split_by_space = line.strip().split( ' ') # Read line and get identifier identifier = str_split_by_space[0] if not identifier or identifier == "'": logging.debug('Skipping empty line') use_line = False elif identifier == '#': logging.debug('This is a divider or attribute line.') use_line = False status = self.write_attr(str_split_by_space) # Add attribute. if status == -1: # Check for errors logging.warning( 'Received unknown data at line {}: {}'.format( line_num, ' '.join(str_split_by_space))) elif status > 0: # Check for level reading. Toggle and start reading if it tells object to. is_reading_level_data = not is_reading_level_data if status == 1: row_num = 0 elif not is_reading_level_data: logging.warning('Received unknown data at line {}: {}'.format( line_num, ' '.join(str_split_by_space))) else: # This means that we are reading the level data. use_line = True if use_line: logging.debug('{} tiles in row {}'.format( len(str_split_by_space), row_num)) assert len(str_split_by_space) == self.level_width, \ 'width of row {} is different from width described in file, {}'.format( len(str_split_by_space), self.level_width) # Make sure that the number of tiles in row is correct, to avoid empty spaces. for col in range(self.level_width): self.init_tile(PVector(col, row_num), str_split_by_space[col]) # Create the tile vals. Tile objects will be added later row_num += 1 self.cross_ref.load_cross_refs( self.edge_light_colour, self.fill_colour, self.edge_shadow_colour, self.pellet_colour ) # Now that we have the colours, we can create the tiles. self.attach_tiles() # Add tile objects self.build_ghost_box() # Create ghost box for ghosts to check if they should exit the box, # since ghost door is technically a wall. f.close() # Close file self.assert_start_positions( ) # Make sure that all required positions are there.