import pygame from pygame.locals import * import pickle from os import path import time # Add this import pygame.init() clock = pygame.time.Clock() fps = 60 screen_width = 1000 screen_height = 1000 screen = pygame.display.set_mode((screen_width, screen_height)) #define game variables TILE_SIZE = 50 game_over = 0 main_menu = True level = 1 max_levels = 3 forks_collected = 0 # Stopwatch variables start_time = None elapsed_time = 0 #load images bg_img = pygame.image.load('img/BACKDROP.png') restart_btn = pygame.image.load('img/restart_btn.png') start_img = pygame.image.load('img/start_btn.png') exit_img = pygame.image.load('img/exit_btn.png') #function to print the you win message upon ending the game def draw_text(text, font, color, x, y): img = font.render(text, True, color) screen.blit(img, (x, y)) #function to reset the level def reset_level(level): global start_time player.reset(100, screen_height - 130) goomba_group.empty() spike_group.empty() exit_group.empty() #load in level and create world again if path.exists(f'level{level}_data'): pickle_in = open(f'level{level}_data', 'rb') world_data = pickle.load(pickle_in) world = World(world_data) # Reset the stopwatch start_time = time.time() return world class Button(): def __init__(self, x, y, image): self.image = image self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y self.clicked = False def draw(self): action = False #get mouse position pos = pygame.mouse.get_pos() #check mouseover and clicked conditions if self.rect.collidepoint(pos): if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False: action = True self.clicked = True if pygame.mouse.get_pressed()[0] == 0: self.clicked = False #draw button screen.blit(self.image, self.rect) return action class Key(pygame.sprite.Sprite): def __init__(self, x, y): pygame.sprite.Sprite.__init__(self) img = pygame.image.load('img/9.png') self.image = pygame.transform.scale(img, (TILE_SIZE, TILE_SIZE)) self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y class Player(): def __init__(self, x, y): self.reset(x, y) def update(self, game_over): global start_time, elapsed_time dx = 0 dy = 0 walk_cooldown = 5 if game_over == 0: #get keypresses key = pygame.key.get_pressed() if key[pygame.K_SPACE] and self.jumped == False and self.in_air == False: self.vel_y = -15 self.jumped = True if key[pygame.K_SPACE] == False: self.jumped = False if key[pygame.K_LEFT]: dx -= 5 self.counter += 1 self.direction = -1 if key[pygame.K_RIGHT]: dx += 5 self.counter += 1 self.direction = 1 if key[pygame.K_LEFT] == False and key[pygame.K_RIGHT] == False: self.counter = 0 self.index = 0 if self.direction == 1: self.image = self.images_right[self.index] if self.direction == -1: self.image = self.images_left[self.index] #handle animation if self.counter > walk_cooldown: self.counter = 0 self.index += 1 if self.index >= len(self.images_right): self.index = 0 if self.direction == 1: self.image = self.images_right[self.index] if self.direction == -1: self.image = self.images_left[self.index] #add gravity self.vel_y += 1 if self.vel_y > 10: self.vel_y = 10 dy += self.vel_y #check for collision self.in_air = True for tile in world.tile_list: #check for collision in x directions if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height): dx = 0 #check for collision in y directions if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height): #check if bellow ground if self.vel_y < 0: dy = tile[1].bottom - self.rect.top self.vel_y = 0 #check if above ground elif self.vel_y >= 0: dy = tile[1].top - self.rect.bottom self.vel_y = 0 self.in_air = False #check for collision with enemies if pygame.sprite.spritecollide(self, spike_group, False): game_over = -1 #check for collision with spikes if pygame.sprite.spritecollide(self, goomba_group, False): game_over = -1 #check for collision with exit if pygame.sprite.spritecollide(self, exit_group, False) and forks_collected == 1: game_over = 1 if level == max_levels: font = pygame.font.SysFont('Bauhaus 93', 70) draw_text('Congratulations! You win!', font, (255, 255, 255), screen_width // 2 - 200, screen_height // 2) pygame.display.update() pygame.time.wait(3000) # Display for 3 seconds #update player coordinates self.rect.x += dx self.rect.y += dy elif game_over == -1: self.image = self.dead_image if self.rect.y > 200: self.rect.y -= 5 # Stop the stopwatch start_time = None #draw player onto screen screen.blit(self.image, self.rect) return game_over def reset(self, x, y): self.images_right = [] self.images_left = [] self.index = 0 self.counter = 0 for num in range(1, 5): img_right = pygame.image.load(f'img/garfield_{num}.png') img_right = pygame.transform.scale(img_right, (40, 80)) img_left = pygame.transform.flip(img_right, True, False) self.images_right.append(img_right) self.images_left.append(img_left) self.dead_image = pygame.image.load("img/GRAVE.png") self.image = self.images_right[self.index] self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y self.width = self.image.get_width() self.height = self.image.get_height() self.vel_y = 0 self.jumped = False self.direction = 0 self.in_air = True class World(): def __init__(self, data): self.tile_list = [] #load images basic_block_img = pygame.image.load('img/0.png') top_left_corner_img = pygame.image.load('img/1.png') basic_block_img = pygame.image.load('img/2.png') top_flat_img = pygame.image.load('img/3.png') right_side_img = pygame.image.load('img/4.png') left_side_img = pygame.image.load('img/5.png') middle_block_img = pygame.image.load('img/7.png') spike = pygame.image.load('img/8.png') key = pygame.image.load('img/9.png') goomba = pygame.image.load('img/10.png') save_img = pygame.image.load('img/save_btn.png') load_img = pygame.image.load('img/load_btn.png') row_count = 0 for row in data: col_count = 0 for tile in row: if tile == 1: fork = Fork(col_count * TILE_SIZE, row_count * TILE_SIZE) fork_group.add(fork) if tile == 2: img = pygame.transform.scale(top_left_corner_img, (TILE_SIZE, TILE_SIZE)) img_rect = img.get_rect() img_rect.x = col_count * TILE_SIZE img_rect.y = row_count * TILE_SIZE tile = (img, img_rect) self.tile_list.append(tile) if tile == 3: img = pygame.transform.scale(basic_block_img, (TILE_SIZE, TILE_SIZE)) img_rect = img.get_rect() img_rect.x = col_count * TILE_SIZE img_rect.y = row_count * TILE_SIZE tile = (img, img_rect) self.tile_list.append(tile) if tile == 4: img = pygame.transform.scale(top_flat_img, (TILE_SIZE, TILE_SIZE)) img_rect = img.get_rect() img_rect.x = col_count * TILE_SIZE img_rect.y = row_count * TILE_SIZE tile = (img, img_rect) self.tile_list.append(tile) if tile == 5: img = pygame.transform.scale(right_side_img, (TILE_SIZE, TILE_SIZE)) img_rect = img.get_rect() img_rect.x = col_count * TILE_SIZE img_rect.y = row_count * TILE_SIZE tile = (img, img_rect) self.tile_list.append(tile) if tile == 6: img = pygame.transform.scale(left_side_img, (TILE_SIZE, TILE_SIZE)) img_rect = img.get_rect() img_rect.x = col_count * TILE_SIZE img_rect.y = row_count * TILE_SIZE tile = (img, img_rect) self.tile_list.append(tile) if tile == 7: img = pygame.transform.scale(middle_block_img, (TILE_SIZE, TILE_SIZE)) img_rect = img.get_rect() img_rect.x = col_count * TILE_SIZE img_rect.y = row_count * TILE_SIZE tile = (img, img_rect) self.tile_list.append(tile) if tile == 8: exit = Exit(col_count * TILE_SIZE, row_count * TILE_SIZE) exit_group.add(exit) if tile == 9: spike = Spike(col_count * TILE_SIZE, row_count * TILE_SIZE) spike_group.add(spike) if tile == 10: goomba = Enemy(col_count * TILE_SIZE, row_count * TILE_SIZE + 4) goomba_group.add(goomba) col_count += 1 row_count += 1 def draw(self): for tile in self.tile_list: screen.blit(tile[0], tile[1]) class Enemy(pygame.sprite.Sprite): def __init__(self, x, y): pygame.sprite.Sprite.__init__(self) self.image = pygame.image.load('img/10.png') self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y self.move_direction = 1 self.move_counter = 0 def update(self): self.rect.x += self.move_direction self.move_counter += 1 if abs(self.move_counter) > 50: self.move_direction *= -1 self.move_counter *= -1 class Spike(pygame.sprite.Sprite): def __init__(self, x, y): pygame.sprite.Sprite.__init__(self) img = pygame.image.load('img/8.png') self.image = pygame.transform.scale(img, (TILE_SIZE, TILE_SIZE)) self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y class Fork(pygame.sprite.Sprite): def __init__(self, x, y): pygame.sprite.Sprite.__init__(self) self.image = pygame.image.load('img/9.png') self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y class Exit(pygame.sprite.Sprite): def __init__(self, x, y): pygame.sprite.Sprite.__init__(self) img = pygame.image.load('img/6.png') self.image = pygame.transform.scale(img, (TILE_SIZE, TILE_SIZE)) self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y def update(self, player): if pygame.sprite.collide_rect(self, player) and player.has_key: return True return False player = Player(100, screen_height - 130) goomba_group = pygame.sprite.Group() spike_group = pygame.sprite.Group() fork_group = pygame.sprite.Group() exit_group = pygame.sprite.Group() #load in world data a create world if path.exists(f'level{level}_data'): pickle_in = open(f'level{level}_data', 'rb') world_data = pickle.load(pickle_in) world = World(world_data) #create buttons restart_button = Button(screen_width // 2 - 50, screen_height // 2 + 100, restart_btn) start_button = Button(screen_width // 2 - 350, screen_height // 2 + 100, start_img) exit_button = Button(screen_width // 2 + 150, screen_height // 2 + 100, exit_img) run = True while run: clock.tick(fps) screen.blit(bg_img, (0, 0)) if main_menu: if exit_button.draw(): run = False if start_button.draw(): main_menu = False start_time = time.time() # Start the stopwatch else: world.draw() if game_over == 0: goomba_group.update() #update score #check if coin has been collectied if pygame.sprite.spritecollide(player, fork_group, True): forks_collected += 1 goomba_group.draw(screen) spike_group.draw(screen) fork_group.draw(screen) exit_group.draw(screen) game_over = player.update(game_over) # Update the stopwatch if start_time is not None: elapsed_time = time.time() - start_time # Display the stopwatch font = pygame.font.SysFont('Bauhaus 93', 30) draw_text(f'Time: {int(elapsed_time)}s', font, (255, 255, 255), 10, 10) # If player has died if game_over == -1: if restart_button.draw(): world_data = [] world = reset_level(level) game_over = 0 forks_collected = 0 # Reset the stopwatch start_time = time.time() # If player has completed level if game_over == 1: # Reset and next level level += 1 if level <= max_levels: # Reset level world_data = [] world = reset_level(level) game_over = 0 else: # Restart game if restart_button.draw(): level = 1 # Reset level world_data = [] world = reset_level(level) game_over = 0 forks_collected = 0 # Reset the stopwatch start_time = time.time() # Display the stopwatch on game over screen if game_over == -1 or game_over == 1: draw_text(f'Total Time/ High Score: {int(elapsed_time)}s', font, (255, 255, 255), screen_width // 2 - 100, screen_height // 2) for event in pygame.event.get(): if event.type == pygame.QUIT: run = False pygame.display.update() pygame.quit()