class Ghost: def __init__(self, sprites, start_tile, exit_spawn_path, square_size, map, player_rect, move_time, move_speed, choose_target_tile, start_at_power_points): self.sprites = sprites self.start_tile = start_tile self.exit_spawn_path = exit_spawn_path self.square_size = square_size self.map = map self.move_time = move_time self.reset_move_speed = self.move_speed = move_speed self.state = "normal" # the other state is "chased" self.chassed_sprite_kind = "chased" self.move_anim = Anim(self.sprites["right"], speed=0.12) self.start_at_power_points = start_at_power_points # will start if less than X power points are eaten self.move_timer = 0 self.chased_change_color_timer = 0 self.pos = [ self.start_tile[0] * self.square_size, self.start_tile[1] * self.square_size ] self.rect = pygame.Rect(self.pos[0], self.pos[1], self.square_size, self.square_size) level_nb = 1 self.timer = Timer(level_nb) # "Scatter" # Scatter -> fixed tile to reach (outside screen) | Chase -> attack pac man | Frightened -> random self.mode = self.timer.phases[self.timer.current_phase_index][0] self.move_direction = "left" self.moving_to = ( 8, 7 ) # the first move after the ghost left the spawn (going to the left tile) self.choose_target_tile = choose_target_tile self.direction_conversion = { "right": (1, 0), "left": (-1, 0), "down": (0, 1), "up": (0, -1) } # def shortest_path_to(self, target_tile): # return target_tile def move(self, player_current_tile, surface, power_points_infos): if self.start_at_power_points <= power_points_infos["total"] - len( power_points_infos["current"]) and time.time( ) > self.move_timer: self.player_current_tile = player_current_tile self.move_timer = time.time() + self.move_time if len(self.exit_spawn_path) != 0: move_direction = self.exit_spawn_path[0][0] moving_to = self.exit_spawn_path[0][1] else: # if the ghost is outside the spawn cage move_direction = self.move_direction moving_to = self.moving_to if move_direction == "left": self.pos[0] -= self.move_speed self.rect.x = self.pos[0] if self.pos[0] / self.square_size < moving_to[ 0]: # if exceed the target tile self.pos[0] = moving_to[0] * self.square_size self.rect.x = self.pos[0] if self.rect.right <= 0: # if outside the screen self.rect.x = surface.get_width() self.pos[0] = self.rect.x self.moving_to = ( surface.get_width() // self.square_size - 1, moving_to[1]) else: if len(self.exit_spawn_path) != 0: self.exit_spawn_path.pop(0) else: self.moving_to = self.choose_next_tile() elif move_direction == "right": self.pos[0] += self.move_speed self.rect.x = self.pos[0] if self.pos[0] / self.square_size > moving_to[ 0]: # if exceed the target tile self.pos[0] = moving_to[0] * self.square_size self.rect.x = self.pos[0] if self.rect.left >= surface.get_width(): self.rect.right = 0 self.pos[0] = self.rect.x self.moving_to = (0, moving_to[1]) else: if len(self.exit_spawn_path) != 0: self.exit_spawn_path.pop(0) else: self.moving_to = self.choose_next_tile() elif move_direction == "up": self.pos[1] -= self.move_speed self.rect.y = self.pos[1] if self.pos[1] / self.square_size < moving_to[ 1]: # if exceed the target tile self.pos[1] = moving_to[1] * self.square_size self.rect.y = self.pos[1] if len(self.exit_spawn_path) != 0: self.exit_spawn_path.pop(0) else: self.moving_to = self.choose_next_tile() elif move_direction == "down": self.pos[1] += self.move_speed self.rect.y = self.pos[1] if self.pos[1] / self.square_size > moving_to[ 1]: # if exceed the target tile self.pos[1] = moving_to[1] * self.square_size self.rect.y = self.pos[1] if len(self.exit_spawn_path) != 0: self.exit_spawn_path.pop(0) else: self.moving_to = self.choose_next_tile() def possible_next_tile_algo(self): adjacency_tiles = {} if self.move_direction != "up": adjacency_tiles["down"] = self.map[self.moving_to[1] + 1][self.moving_to[0]] if self.move_direction != "down": adjacency_tiles["up"] = self.map[self.moving_to[1] - 1][self.moving_to[0]] if self.move_direction != "left": adjacency_tiles["right"] = self.map[self.moving_to[1]][ self.moving_to[0] + 1] if self.move_direction != "right": adjacency_tiles["left"] = self.map[self.moving_to[1]][ self.moving_to[0] - 1] while True: for direction, tile in adjacency_tiles.items(): if tile == "#" or tile == "b": del adjacency_tiles[direction] break else: break return list(adjacency_tiles.keys()) def choose_next_tile(self): # update the timer self.timer.update() if self.timer.timer > self.timer.phases[ self.timer.current_phase_index][1]: self.timer.start_time = time.time() self.timer.current_phase_index += 1 self.mode = self.timer.phases[self.timer.current_phase_index][0] target_tile, possibles_next_direction = self.choose_target_tile() if len(possibles_next_direction) == 1: next_direction = possibles_next_direction[0] # shortest path (in a straight line) to the target tile else: shortest_line = float("+inf") current_chosen_direction = None for direction in possibles_next_direction: conv = self.direction_conversion[direction] tile = (self.moving_to[0] + conv[0], self.moving_to[1] + conv[1]) # lenght from this tile to the target tile line_lenght = math.sqrt( abs(tile[0] - target_tile[0])**2 + abs(tile[1] - target_tile[1])**2) if line_lenght < shortest_line: shortest_line = line_lenght current_chosen_direction = direction next_direction = current_chosen_direction if next_direction == "right": next_tile = [self.moving_to[0] + 1, self.moving_to[1]] elif next_direction == "left": next_tile = [self.moving_to[0] - 1, self.moving_to[1]] elif next_direction == "down": next_tile = [self.moving_to[0], self.moving_to[1] + 1] elif next_direction == "up": next_tile = [self.moving_to[0], self.moving_to[1] - 1] self.move_direction = next_direction return next_tile def draw(self, surface): # if self.state == "chased": self.move_anim.do() if self.state == "chased": if self.chased_start_time + 10 > time.time(): if self.chased_start_time + 7 < time.time( ) and self.chased_change_color_timer < time.time(): self.chased_change_color_timer = time.time() + 0.3 if self.chassed_sprite_kind == "chased": self.chassed_sprite_kind = "chased_white" else: self.chassed_sprite_kind = "chased" else: self.chassed_sprite_kind = "chased" self.state = "normal" sprite = self.sprites[self.chassed_sprite_kind][ self.move_anim.s_index] else: # draw the normal sprite self.move_speed = self.reset_move_speed sprite = self.sprites[self.move_direction][self.move_anim.s_index] # pygame.draw.rect(surface, sprite, self.rect) surface.blit( sprite, (self.rect.x + (self.square_size - sprite.get_width()) / 2, self.rect.y + (self.square_size - sprite.get_height()) / 2 + 1.5)) def collide(self, surface, player): if self.rect.colliderect(player.rect): if self.state == "chased": # self.pos = [self.start_tile[0] * self.square_size, self.start_tile[1] * self.square_size] self.pos[0] = 9 * self.square_size self.pos[1] = 9 * self.square_size self.rect = pygame.Rect(self.pos[0], self.pos[1], self.square_size, self.square_size) self.exit_spawn_path = [("up", (9, 8)), ("up", (9, 7))] player.score += 200 self.moving_to = (8, 7) self.move_direction = "left" self.state = "normal" print("pac man eat ghost") else: player.killed = True print("ghost eat pac man")
class Player: def __init__(self, square_size, obstacles, sprites): self.square_size = square_size self.obstacles = obstacles self.sprites = sprites self.move_anim = Anim(sprites["moving"]["right"], speed=0.12) self.death_anim = Anim(sprites["death"], speed=0.1) self.font = pygame.font.SysFont("coopbl", 55) self.timer = {"move": 0} self.move_time = 0.02 self.portals = {1: None, 2: None, "space_pressed": False} # initial amount of lives self.lives = 3 # the player will move 2 pixel every move_time sec self.reset_move_speed = self.move_speed = 3 self.color = (211, 144, 11) self.direction = {"current": "left", "next": None, "previous": None, "right": (1, 0), "left": (-1, 0), "down": (0, 1), "up": (0, -1)} self.killed = False self.make_death_anim = False self.make_ghost_respawn = False self.start_pos = [self.square_size * 9, self.square_size * 11] self.rect = pygame.Rect(self.start_pos[0], self.start_pos[1], self.square_size, self.square_size) self.define_current_tile() self.score = 0 def define_current_tile(self): current_tile_x = int(self.rect.centerx / self.square_size) current_tile_y = int(self.rect.centery / self.square_size) self.current_tile = [current_tile_x, current_tile_y] def user_input(self): keys_pressed = pygame.key.get_pressed() if ((keys_pressed[pygame.K_LEFT] or keys_pressed[pygame.K_a]) and not ( keys_pressed[pygame.K_RIGHT] or keys_pressed[pygame.K_d])): self.direction["next"] = "left" if ((keys_pressed[pygame.K_RIGHT] or keys_pressed[pygame.K_d]) and not ( keys_pressed[pygame.K_LEFT] or keys_pressed[pygame.K_a])): self.direction["next"] = "right" if ((keys_pressed[pygame.K_DOWN] or keys_pressed[pygame.K_s]) and not ( keys_pressed[pygame.K_UP] or keys_pressed[pygame.K_w])): self.direction["next"] = "down" if ((keys_pressed[pygame.K_UP] or keys_pressed[pygame.K_w]) and not ( keys_pressed[pygame.K_DOWN] or keys_pressed[pygame.K_s])): self.direction["next"] = "up" def change_direction(self, surface): if self.direction["next"] is not None: next_rect = self.rect.copy() next_rect.x += self.move_speed * self.direction[self.direction["next"]][0] next_rect.y += self.move_speed * self.direction[self.direction["next"]][1] if next_rect.right > 0 and next_rect.left < surface.get_width(): if next_rect.collidelist(self.obstacles) == -1: self.direction["current"] = self.direction["next"] self.direction["previous"] = self.direction["current"] self.direction["next"] = None def move(self, surface): self.user_input() if time.time() > self.timer["move"]: self.timer["move"] = time.time() + self.move_time self.define_current_tile() self.change_direction(surface) if self.direction["current"] is not None: self.rect.x += self.move_speed * self.direction[self.direction["current"]][0] self.rect.y += self.move_speed * self.direction[self.direction["current"]][1] # if the player is outside the screen at the right if self.rect.x > surface.get_width(): self.rect.x = 0 - self.square_size # if the player is outside the screen at the left if self.rect.x < 0 - self.square_size: self.rect.x = surface.get_width() if self.current_tile == self.portals[2]: self.current_tile = self.portals[1] self.rect.x = self.portals[1][0] * self.square_size self.rect.y = self.portals[1][1] * self.square_size # if the player HAS collide with an obstacle collide_index = self.rect.collidelist(self.obstacles) if collide_index != -1: if self.direction["current"] == "up": self.rect.y = self.obstacles[collide_index].bottom elif self.direction["current"] == "down": self.rect.y = self.obstacles[collide_index].top - self.square_size elif self.direction["current"] == "right": self.rect.x = self.obstacles[collide_index].left - self.square_size elif self.direction["current"] == "left": self.rect.x = self.obstacles[collide_index].right self.direction["previous"] = self.direction["current"] self.direction["current"] = None def draw_score(self, surface): label = self.font.render(f"Score : {self.score}", 1, (222, 222, 222)) x_pos = surface.get_width() - label.get_width() - self.square_size y_pos = self.square_size // 2 - label.get_height() // 2 surface.blit(label, (x_pos, y_pos)) def draw_lives(self, surface): label = self.font.render(f"Lives : {self.lives}", 1, (222, 222, 222)) x_pos = surface.get_width() // 2 - label.get_width() // 2 y_pos = surface.get_height() - label.get_height() - 4 surface.blit(label, (x_pos, y_pos)) def draw(self, surface): self.draw_score(surface) self.draw_lives(surface) self.draw_portals(surface) if not self.make_death_anim: self.move_anim.do() if self.direction["current"] is None: self.move_anim.s_index = 0 direction = "right" else: direction = self.direction["current"] sprite = self.sprites["moving"][direction][self.move_anim.s_index] pos_x = self.rect.x + (self.square_size - sprite.get_width()) / 2 pos_y = (self.rect.y + (self.square_size - sprite.get_height()) / 2 + 1) surface.blit(sprite, (pos_x, pos_y)) # death anim if self.make_death_anim: self.move_speed = 0 sprite = self.death_anim.current_sprite pos_x = self.rect.x + (self.square_size - sprite.get_width()) / 2 pos_y = (self.rect.y + (self.square_size - sprite.get_height()) / 2 + 1) surface.blit(sprite, (pos_x, pos_y)) if self.death_anim.do() == "end": # reset self.make_death_anim = False self.make_ghost_respawn = True self.reset_pos() def reset_pos(self): self.rect.x = self.start_pos[0] self.rect.y = self.start_pos[1] self.move_speed = self.reset_move_speed self.direction["current"] = "left" self.portals = {1: None, 2: None, "space_pressed": False} # Tests if the player is killed or not def test_kill(self): if self.killed is True: self.killed = False self.lives -= 1 self.death_anim.s_index = 0 self.make_death_anim = True self.portals = {1: None, 2: None, "space_pressed": False} return True def create_portals(self): keys_pressed = pygame.key.get_pressed() if keys_pressed[pygame.K_SPACE]: if not self.portals["space_pressed"]: self.portals["space_pressed"] = True tile = self.current_tile.copy() if self.portals[1] == None: self.portals[1] = tile elif self.portals[2] == None: self.portals[2] = tile else: self.portals["space_pressed"] = False def draw_portals(self, surface): if self.portals[1] != None: surface.blit(self.sprites["portals"]["blue"], (self.portals[1][0] * self.square_size, self.portals[1][1] * self.square_size )) if self.portals[2] != None: surface.blit(self.sprites["portals"]["orange"], (self.portals[2][0] * self.square_size, self.portals[2][1] * self.square_size ))