import pygame import sys import copy from settings import * from player_class import * from ghosts_class import * # Variable for speed, position, movement objects vec = pygame.math.Vector2 class Program: # Basic initializations def __init__(self): # Initialization and variables pygame.init() self.clock = pygame.time.Clock() self.screen = pygame.display.set_mode((SCREEN_W, SCREEN_H)) self.running = True self.state = 'start' self.background = None # Below width/height is test stuff # self.cell_width = MAZE_W//28 # self.cell_height = MAZE_H//30 self.cell_width = MAZE_W//COL self.cell_height = MAZE_H//ROW self.walls = [] self.roads = [] self.coins = [] self.ghosts = [] self.ghost_pos = [] self.high_scores = [[]] self.player_start_point = None self.hs_file = None self.load() self.player = Player(self, copy.copy(self.player_start_point)) self.make_ghosts() print("ON START pix pos: ", self.player.pix_pos, "direction: ", self.player.direction, "stored direction: ", self.player.stored_direction) # My "game loop," handle's states for game and keeps it running until termination def run(self): while self.running: if self.state == 'start': self.start_events() self.start_update() self.start_draw() if self.state == 'playing': self.playing_events() self.playing_update() self.playing_draw() if self.state == 'game over': self.game_over_events() self.game_over_update() self.game_over_draw() self.clock.tick(FPS) pygame.quit() sys.exit() # ###################################### Helper Functions ###################################### # includes: a draw_text function that more easily center's and positions text on the screen # a load function that loads in files and parse's data # a make_ghosts function that adds ghost's to a list # a draw_grid function that draws the grid for debugging and later for recoloring roads and walls # a draw_coins function that draws the coins on the map # a reset function to reset the game after game_over def draw_text(self, words, screen, pos, size, color, font_name, centered=False): font = pygame.font.SysFont(font_name, size) text = font.render(words, False, color) text_size = text.get_size() if centered: pos[0] = pos[0] - text_size[0] // 2 pos[1] = pos[1] - text_size[1] // 2 screen.blit(text, pos) def load(self): self.background = pygame.image.load('potMap4.png') self.background = pygame.transform.scale(self.background, (MAZE_W, MAZE_H)) # Open and process bin_map2 file, creates the list of walls, and coins in vectors with proper grid coords # Also puts the player and ghosts in proper position on the grid with open("bin_map2.txt", 'r') as file: for y_index, line in enumerate(file): for x_index, char in enumerate(line): if char == '1': self.walls.append(vec(x_index, y_index)) elif char == 'C': self.coins.append(vec(x_index, y_index)) self.roads.append(vec(x_index, y_index)) elif char == 'P': self.player_start_point = [x_index, y_index] self.roads.append(vec(x_index, y_index)) elif char in ["5", "4", "3", "2"]: pygame.draw.rect(self.background, BG_COL, (x_index*self.cell_width, y_index*self.cell_height, self.cell_width, self.cell_height)) self.ghost_pos.append([x_index, y_index]) elif char == 'B': pygame.draw.rect(self.background, BG_COL, (x_index*self.cell_width, y_index*self.cell_height, self.cell_width, self.cell_height)) with open("high_scores.txt", 'r+') as self.hs_file: self.high_scores = self.hs_file.read().splitlines() self.high_scores = [int(i) for i in self.high_scores] def make_ghosts(self): for g_idx, pos in enumerate(self.ghost_pos): self.ghosts.append(Ghosts(self, vec(pos), g_idx)) # outlines the grid for debug purposes, then from walls and roads list colors in both def draw_grid(self): for x in range(SCREEN_W//self.cell_width): pygame.draw.line(self.background, RED, (x*self.cell_width, 0), (x*self.cell_width, SCREEN_H)) for x in range(SCREEN_H//self.cell_height): pygame.draw.line(self.background, RED, (0, x*self.cell_height), (SCREEN_W, x*self.cell_height)) for wall in self.walls: pygame.draw.rect(self.background, GREY, (wall.x*self.cell_width, wall.y*self.cell_height, self.cell_width, self.cell_height)) for road in self.roads: pygame.draw.rect(self.background, BG_COL, (road.x*self.cell_width, road.y*self.cell_height, self.cell_width, self.cell_height)) # for coin in self.coins: # pygame.draw.rect(self.background, ORANGE, (coin.x*self.cell_width, coin.y*self.cell_height, self.cell_width, self.cell_height)) # looks through coins list, then draws coins in appropriate cells def draw_coins(self): for coin in self.coins: pygame.draw.circle(self.screen, PLAYER_TEST_COLOR, (int(coin.x*self.cell_width)+self.cell_width//2+BUFFERS//2, int(coin.y*self.cell_height)+self.cell_height//2+BUFFERS//2), 3) # resets lives, coins, current score, and redraws coins from file, then switches state back to playing def reset(self): self.player.lives = 4 # 4 because for some reason when I have 3 it only displays 2 self.coins = [] self.player.current_score = 0 with open("bin_map2.txt", 'r') as file: for yindex, line in enumerate(file): for xindex, char in enumerate(line): if char == 'C': self.coins.append(vec(xindex, yindex)) self.roads.append(vec(xindex, yindex)) self.state = "playing" # ###################################### Start Functions ####################################### # Handle's button events def start_events(self): for event in pygame.event.get(): if event.type == pygame.QUIT: self.running = False if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: self.running = False if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE: self.state = 'playing' def start_update(self): pass # Draw's Screen using helper functions def start_draw(self): self.screen.fill(BG_COL) self.draw_text("PUSH SPACE BAR TO START", self.screen, [SCREEN_W // 2, SCREEN_H // 2], START_TEXT_SIZE, ORANGE, START_FONT, True) self.draw_text("1 Player Only", self.screen, [SCREEN_W // 2, SCREEN_H // 2 + 30], START_TEXT_SIZE, BLUE, START_FONT, True) self.draw_text("USE ARROW KEYS TO MOVE PACMAN", self.screen, [SCREEN_W // 2, SCREEN_H // 2 + 70], START_TEXT_SIZE, GREY, START_FONT, True) self.draw_text("COLLECT COINS & DON'T LET GHOSTS TOUCH YOU", self.screen, [SCREEN_W // 2, SCREEN_H // 2 + 85], START_TEXT_SIZE, GREY, START_FONT, True) self.draw_text("High Scores", self.screen, [SCREEN_W // 2, 20], START_TEXT_SIZE, WHITE, START_FONT, True) for hs in range(len(self.high_scores)): if hs < 5: self.draw_text("{one}: {two}".format(one=hs+1, two=self.high_scores[hs]), self.screen, [SCREEN_W // 2, 40 + 15 * hs], START_TEXT_SIZE, WHITE, START_FONT, True) # print(hs.index(hs), hs) pygame.display.update() # ###################################### Playing Functions ####################################### # handles keyboard events during game def playing_events(self): for event in pygame.event.get(): if event.type == pygame.QUIT: self.running = False if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: self.running = False if event.type == pygame.KEYDOWN: if event.key == pygame.K_UP: self.player.move(vec(0, -1)) print("up") if event.key == pygame.K_DOWN: self.player.move(vec(0, 1)) print("down") if event.key == pygame.K_LEFT: self.player.move(vec(-1, 0)) print("left") if event.key == pygame.K_RIGHT: self.player.move(vec(1, 0)) print("right") # part of game loop, calls update functions for each entity def playing_update(self): self.player.update() for ghost in self.ghosts: ghost.update() for ghost in self.ghosts: if ghost.grid_pos == self.player.grid_pos: self.life_loss() # Draw's Screen using helper functions def playing_draw(self): self.screen.fill(BG_COL) self.screen.blit(self.background, (BUFFERS//2, BUFFERS//2)) self.draw_coins() self.draw_grid() self.draw_text('HIGH SCORE: {}'.format(self.high_scores[0]), self.screen, [SCREEN_W//6 + 15, 2], 15, WHITE, START_FONT) self.draw_text('CURRENT SCORE: {}'.format(self.player.current_score), self.screen, [SCREEN_W//2 + 40, 0], 15, WHITE, START_FONT) self.player.draw() for ghost in self.ghosts: ghost.draw() pygame.display.update() # calculates what happens when a ghost-player collision happens def life_loss(self): self.player.lives -= 1 if self.player.lives == 0: self.check_highscores() self.state = "game over" else: self.player.grid_pos = vec(self.player_start_point) # reset player to start point self.player.pix_pos = self.player.get_pix_pos() # finds player's "new" position self.player.direction = [1, 0] # makes player move right again, stopped some bugs self.player.stored_direction = None # reset's player stored direction self.player.moving = "SHOULD" # flag to stop bugs that made player refuse to move when game reset # print("pix pos: ", self.player.pix_pos, "direction: ", self.player.direction, # "stored direction: ", self.player.stored_direction) # below resets ghosts back to starting position for ghost in self.ghosts: ghost.grid_pos = vec(ghost.starting_point) ghost.pix_pos = ghost.get_pix_pos() # ###################################### Game Over Functions ####################################### # handle's keyboard events during game_over state def game_over_events(self): for event in pygame.event.get(): if event.type == pygame.QUIT: self.running = False if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE: self.reset() if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: self.running = False def game_over_update(self): pass # draw's on screen using helper functions def game_over_draw(self): self.screen.fill(BG_COL) self.draw_text("PUSH SPACE BAR TO PLAY AGAIN", self.screen, [SCREEN_W // 2, SCREEN_H // 2], START_TEXT_SIZE, BRIGHT_GREEN, START_FONT, True) self.draw_text("PUSH ESC TO QUIT", self.screen, [SCREEN_W // 2, SCREEN_H // 2 - 20], START_TEXT_SIZE, RED, START_FONT, True) self.draw_text("GAME OVER", self.screen, [SCREEN_W // 2, SCREEN_H // 2 - 55], 30, RED, START_FONT, True) self.draw_text('YOUR SCORE: {}'.format(self.player.current_score), self.screen, [SCREEN_W // 2, SCREEN_H // 2 + 20], START_TEXT_SIZE, BLUE, START_FONT, True) pygame.display.update() # compares player's current_score to high_score list, if >, adds to high_score list # for purposes I didn't feel like recording duplicate scores def check_highscores(self): for hs in range(len(self.high_scores)): print(self.player.current_score, " > ", self.high_scores[hs]) if int(self.player.current_score) > int(self.high_scores[hs]): self.high_scores.append(self.player.current_score) break self.high_scores.sort(reverse=True) # makes scores go 100->90->80 instead of 80->90->100 with open("high_scores.txt", 'r+') as self.hs_file: for hs in range(len(self.high_scores)): if hs < 5: self.hs_file.writelines('%s\n' % str(self.high_scores[hs]))