""" ---------------------------------------- Project: Platformer Shooter = Standard: 91883 (AS1.7) v.1 School: = Tauranga Boys' College = Author: Sam Coley = Date: June 2024 = Python: 3.12 = ---------------------------------------- """ import pygame # Import the pygame library from pygame import mixer # Import the mixer module from pygame import os # Import the os module to interact with the operating system import random # Import the random module to generate random numbers import csv # Import the csv module to handle CSV files import button # Import a custom button module mixer.init() # Initialize the mixer module to handle audio pygame.init() # Initialize the pygame module to handle graphics and events # Set the screen dimensions SCREEN_WIDTH = 800 # Width of the game window SCREEN_HEIGHT = int(SCREEN_WIDTH * 0.8) # Height of the game window, maintaining a 16:10 aspect ratio # Create the display window screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) # Create the game window pygame.display.set_caption('Shooter') # Set the title of the window clock = pygame.time.Clock() # Create a clock object to control the frame rate FPS = 60 # Set the frames per second # Game variables GRAVITY = 0.75 # Gravity constant to simulate gravity SCROLL_THRESHOLD = 200 # Threshold for screen scrolling ROWS = 16 # Number of rows in the game level COLUMNS = 150 # Number of columns in the game level TILE_SIZE = SCREEN_HEIGHT // ROWS # Size of each tile, determined by the screen height divided by the number of rows TILE_TYPES = 21 # Number of different tile types MAX_LEVELS = 7 # Maximum number of levels in the game screen_scroll = 0 # Variable to keep track of screen scrolling background_scroll = 0 # Variable to keep track of background scrolling level = 1 # Current level number start_game = False # Flag to check if the game has started start_intro = False # Flag to check if the intro animation has started # Player action variables moving_left = False # Flag to check if the player is moving left moving_right = False # Flag to check if the player is moving right shoot = False # Flag to check if the player is shooting grenade = False # Flag to check if the player is throwing a grenade grenade_thrown = False # Flag to check if the grenade has been thrown # Load sounds jump_fx = pygame.mixer.Sound('Audio/jump.mp3') # Load the jump sound effect jump_fx.set_volume(0.05) # Set the volume of the jump sound effect gunshot_fx = pygame.mixer.Sound('Audio/Gunshot.mp3') # Load the gunshot sound effect gunshot_fx.set_volume(0.05) # Set the volume of the gunshot sound effect grenade_fx = pygame.mixer.Sound('Audio/Explosion.mp3') # Load the explosion sound effect grenade_fx.set_volume(0.05) # Set the volume of the explosion sound effect # Load images start_image = pygame.image.load('Images/Background/Start.png').convert_alpha() # Load the start button image exit_image = pygame.image.load('Images/Background/Exit.png').convert_alpha() # Load the exit button image restart_image = pygame.image.load('Images/Background/Restart.png').convert_alpha() # Load the restart button image restart_image = pygame.transform.scale(restart_image, (140, 60)) # Scale the restart button image # Load background image darksky_image = pygame.image.load('Images/Background/dark_sky.jpg').convert_alpha() # Load the sky image # Load tile images into a list image_list = [] for x in range(TILE_TYPES): # Loop through each tile type image = pygame.image.load(f'Images/Tile Blocks/{x}.png') # Load the tile image image = pygame.transform.scale(image, (TILE_SIZE, TILE_SIZE)) # Scale the tile image to the correct size image_list.append(image) # Add the tile image to the list # Load icons bullet_image = pygame.image.load('Images/Icons/Bullet.png').convert_alpha() # Load the bullet icon bullet_image = pygame.transform.scale(bullet_image, (15, 15)) # Scale the bullet icon grenade_image = pygame.image.load('Images/Icons/Boggie Bomb.png').convert_alpha() # Load the grenade icon grenade_image = pygame.transform.scale(grenade_image, (20, 20)) # Scale the grenade icon # Load item box images health_box_image = pygame.image.load('Images/Icons/health box.png').convert_alpha() # Load the health box icon health_box_image = pygame.transform.scale(health_box_image, (30, 30)) # Scale the health box icon ammo_box_image = pygame.image.load('Images/Icons/ammo box.png').convert_alpha() # Load the ammo box icon ammo_box_image = pygame.transform.scale(ammo_box_image, (35, 35)) # Scale the ammo box icon grenade_box_image = pygame.image.load('Images/Icons/grenade_box.png').convert_alpha() # Load the grenade box icon grenade_box_image = pygame.transform.scale(grenade_box_image, (35, 35)) # Scale the ammo box icon # Dictionary to store item box images item_boxes = { 'Health': health_box_image, # Health box image 'Ammo': ammo_box_image, # Ammo box image 'Grenade': grenade_box_image # Grenade box image } # Define colors background = (0, 0, 128) # Background color RED = (255, 0, 0) # Red color WHITE = (255, 255, 255) # White color GREEN = (0, 255, 0) # Green color BLACK = (0, 0, 0) # Black color PINK = (235, 65, 54) # Pink color BLUE = (0, 0, 128) # Blue color # Define font font = pygame.font.SysFont('Pixel Operator 8', 20) # Set the font and size # Function to draw text on the screen def draw_text(text, font, text_col, x, y): image = font.render(text, True, text_col) # Render the text screen.blit(image, (x, y)) # Draw the text on the screen # Function to draw the background def draw_background(): screen.fill(background) # Fill the background with a solid color width = darksky_image.get_width() # Get the width of the sky image for x in range(5): # Draw multiple layers of background images screen.blit(darksky_image, ((x * width) - background_scroll * 0.5, 0)) # Draw the sky image # Function to reset the level def reset_level(): enemy_group.empty() # Empty the enemy group bullet_group.empty() # Empty the bullet group grenade_group.empty() # Empty the grenade group explosion_group.empty() # Empty the explosion group item_box_group.empty() # Empty the item box group decoration_group.empty() # Empty the decoration group lava_group.empty() # Empty the lava group exit_group.empty() # Empty the exit group data = [] # Initialize an empty list for the level data for row in range(ROWS): # Loop through each row r = [-1] * COLUMNS # Create a row with default tile values data.append(r) # Add the row to the data list return data # Return the level data # Soldier class class Soldier(pygame.sprite.Sprite): def __init__(self, char_type, x, y, scale, speed, ammo, grenades): pygame.sprite.Sprite.__init__(self) # Initialize the sprite self.alive = True # Set the soldier to be alive self.char_type = char_type # Character type (player or enemy) self.speed = speed # Movement speed self.ammo = ammo # Ammo count self.start_ammo = ammo # Initial ammo count self.shoot_cooldown = 0 # Cooldown time for shooting self.grenades = grenades # Grenade count self.health = 100 # Health self.max_health = self.health # Maximum health self.direction = 1 # Direction of movement (1: right, -1: left) self.vel_y = 0 # Vertical velocity self.jump = False # Jump flag self.in_air = True # In-air flag self.flip = False # Flip image flag self.animation_list = [] # List to store animations self.frame_index = 0 # Current animation frame index self.action = 0 # Current action (0: idle, 1: run, 2: jump, 3: death) self.update_time = pygame.time.get_ticks() # Time of last animation update self.move_counter = 0 # Counter to keep track of movement self.vision = pygame.Rect(0, 0, 150, 20) # Vision rectangle for AI self.idling = False # Idling flag for AI self.idling_counter = 0 # Counter to keep track of idling # Load all images for the animations animation_types = ['Idle', 'Run', 'Jump', 'Death'] # Different animation types for animation in animation_types: temporary_list = [] # Temporary list to store frames for each animation num_of_frames = len(os.listdir(f'Images/{self.char_type}/{animation}')) # Number of frames in the animation for i in range(num_of_frames): image = pygame.image.load(f'Images/{self.char_type}/{animation}/{i}.png').convert_alpha() # Load the image image = pygame.transform.scale(image, (int(image.get_width() * scale), int(image.get_height() * scale))) # Scale the image temporary_list.append(image) # Add the image to the temporary list self.animation_list.append(temporary_list) # Add the temporary list to the animation list self.image = self.animation_list[self.action][self.frame_index] # Set the initial image self.rect = self.image.get_rect() # Get the rect of the image self.rect.center = (x, y) # Set the initial position self.width = self.image.get_width() # Get the width of the image self.height = self.image.get_height() # Get the height of the image def update(self): self.update_animation() # Update the animation self.check_alive() # Check if the soldier is alive # Update cooldown if self.shoot_cooldown > 0: self.shoot_cooldown -= 1 # Decrease the shoot cooldown def move(self, moving_left, moving_right): screen_scroll = 0 # Variable to track screen scrolling dx = 0 # Change in x-direction dy = 0 # Change in y-direction # Moving left and right if moving_left: dx = -self.speed # Move left by decreasing x self.flip = True # Flip the image self.direction = -1 # Set the direction to left if moving_right: dx = self.speed # Move right by increasing x self.flip = False # Do not flip the image self.direction = 1 # Set the direction to right # Jumping if self.jump == True and self.in_air == False: self.vel_y = -13 # Set the vertical velocity for jumping self.jump = False # Reset the jump flag self.in_air = True # Set the in-air flag # Apply gravity self.vel_y += GRAVITY # Increase the vertical velocity by gravity if self.vel_y > 10: self.vel_y = 10 # Limit the vertical velocity dy += self.vel_y # Apply the vertical velocity to the change in y # Check for collision with tiles for tile in world.obstacle_list: if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height): dx = 0 # Stop horizontal movement if colliding with a tile if self.char_type == 'enemy': self.direction *= -1 # Change direction for AI self.move_counter = 0 # Reset move counter if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height): if self.vel_y < 0: self.vel_y = 0 # Stop upward movement dy = tile[1].bottom - self.rect.top # Adjust position elif self.vel_y >= 0: self.vel_y = 0 # Stop downward movement self.in_air = False # Set the in-air flag dy = tile[1].top - self.rect.bottom # Adjust position # Check for collision with lava if pygame.sprite.spritecollide(self, lava_group, False): self.health = 0 # Reduce health to zero if colliding with lava # Check for collision with exit level_complete = False if pygame.sprite.spritecollide(self, exit_group, False): level_complete = True # Set level complete flag if colliding with exit # Check if fallen off the map if self.rect.bottom > SCREEN_HEIGHT: self.health = 0 # Reduce health to zero if fallen off the map # Prevent player from going off-screen if self.char_type == 'player': if self.rect.left + dx < 0 or self.rect.right + dx > SCREEN_WIDTH: dx = 0 # Stop horizontal movement if hitting screen edges self.rect.x += dx # Apply horizontal movement self.rect.y += dy # Apply vertical movement # Update screen scroll for player if self.char_type == 'player': if (self.rect.right > SCREEN_WIDTH - SCROLL_THRESHOLD and background_scroll < (world.level_length * TILE_SIZE) - SCREEN_WIDTH)\ or (self.rect.left < SCROLL_THRESHOLD and background_scroll > abs(dx)): self.rect.x -= dx # Prevent player from moving out of bounds screen_scroll = -dx # Scroll the screen return screen_scroll, level_complete # Return the screen scroll and level complete flag def shoot(self): if self.shoot_cooldown == 0 and self.ammo > 0: self.shoot_cooldown = 20 # Set the shoot cooldown bullet = Bullet(self.rect.centerx + (0.75 * self.rect.size[0] * self.direction), self.rect.centery, self.direction) bullet_group.add(bullet) # Add the bullet to the bullet group self.ammo -= 1 # Decrease the ammo count gunshot_fx.play() # Play the gunshot sound effect def ai(self): if self.alive and player.alive: if self.idling == False and random.randint(1, 200) == 1: self.update_action(0) # Set action to idle self.idling = True # Set the idling flag self.idling_counter = 50 # Set the idling counter if self.vision.colliderect(player.rect): self.update_action(0) # Set action to idle self.shoot() # Shoot at the player else: if self.idling == False: if self.direction == 1: ai_moving_right = True # Move right else: ai_moving_right = False # Do not move right ai_moving_left = not ai_moving_right # Move left if not moving right self.move(ai_moving_left, ai_moving_right) # Move the AI self.update_action(1) # Set action to run self.move_counter += 1 # Increment the move counter self.vision.center = (self.rect.centerx + 75 * self.direction, self.rect.centery) # Update vision rectangle if self.move_counter > TILE_SIZE: self.direction *= -1 # Change direction if moved a full tile self.move_counter *= -1 # Reset move counter else: self.idling_counter -= 1 # Decrease idling counter if self.idling_counter <= 0: self.idling = False # Reset idling flag self.rect.x += screen_scroll # Scroll the screen with the AI def update_animation(self): ANIMATION_COOLDOWN = 100 # Time between animation frames self.image = self.animation_list[self.action][self.frame_index] # Set the current image if pygame.time.get_ticks() - self.update_time > ANIMATION_COOLDOWN: self.update_time = pygame.time.get_ticks() # Update the time self.frame_index += 1 # Move to the next frame if self.frame_index >= len(self.animation_list[self.action]): if self.action == 3: self.frame_index = len(self.animation_list[self.action]) - 1 # Keep the last frame for death else: self.frame_index = 0 # Reset to the first frame def update_action(self, new_action): if new_action != self.action: self.action = new_action # Set the new action self.frame_index = 0 # Reset the frame index self.update_time = pygame.time.get_ticks() # Update the time def check_alive(self): if self.health <= 0: self.health = 0 # Set health to zero self.speed = 0 # Stop movement self.alive = False # Set alive flag to false self.update_action(3) # Set action to death def draw(self): screen.blit(pygame.transform.flip(self.image, self.flip, False), self.rect) # Draw the soldier on the screen # World class class World(): def __init__(self): self.obstacle_list = [] # List to store obstacles def process_data(self, data): self.level_length = len(data[0]) # Length of the level for y, row in enumerate(data): # Loop through each row for x, tile in enumerate(row): # Loop through each tile in the row if tile >= 0: # If tile is not empty image = image_list[tile] # Get the image for the tile image_rect = image.get_rect() # Get the rect for the image image_rect.x = x * TILE_SIZE # Set the x position of the rect image_rect.y = y * TILE_SIZE # Set the y position of the rect tile_data = (image, image_rect) # Create a tuple with the image and rect if tile >= 0 and tile <= 8: self.obstacle_list.append(tile_data) # Add to obstacle list elif tile >= 9 and tile <= 10: lava = Lava(image, x * TILE_SIZE, y * TILE_SIZE) # Create a lava object lava_group.add(lava) # Add to lava group elif tile >= 11 and tile <= 14: decoration = Decoration(image, x * TILE_SIZE, y * TILE_SIZE) # Create a decoration object decoration_group.add(decoration) # Add to decoration group elif tile == 15: player = Soldier('player', x * TILE_SIZE, y * TILE_SIZE, 1.65, 5, 20, 5) # Create the player health_bar = HealthBar(10, 10, player.health, player.health) # Create the health bar elif tile == 16: enemy = Soldier('enemy', x * TILE_SIZE, y * TILE_SIZE, 1.65, 2, 20, 0) # Create an enemy enemy_group.add(enemy) # Add to enemy group elif tile == 17: item_box = ItemBox('Ammo', x * TILE_SIZE, y * TILE_SIZE) # Create an ammo box item_box_group.add(item_box) # Add to item box group elif tile == 18: item_box = ItemBox('Grenade', x * TILE_SIZE, y * TILE_SIZE) # Create a grenade box item_box_group.add(item_box) # Add to item box group elif tile == 19: item_box = ItemBox('Health', x * TILE_SIZE, y * TILE_SIZE) # Create a health box item_box_group.add(item_box) # Add to item box group elif tile == 20: exit = Exit(image, x * TILE_SIZE, y * TILE_SIZE) # Create an exit exit_group.add(exit) # Add to exit group return player, health_bar # Return the player and health bar def draw(self): for tile in self.obstacle_list: # Loop through each obstacle tile[1][0] += screen_scroll # Scroll the screen screen.blit(tile[0], tile[1]) # Draw the tile on the screen # Decoration class class Decoration(pygame.sprite.Sprite): def __init__(self, image, x, y): pygame.sprite.Sprite.__init__(self) # Initialize the sprite self.image = image # Set the image self.rect = self.image.get_rect() # Get the rect of the image self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height())) # Set the position def update(self): self.rect.x += screen_scroll # Scroll the screen # Lava class class Lava(pygame.sprite.Sprite): def __init__(self, image, x, y): pygame.sprite.Sprite.__init__(self) # Initialize the sprite self.image = image # Set the image self.rect = self.image.get_rect() # Get the rect of the image self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height())) # Set the position def update(self): self.rect.x += screen_scroll # Scroll the screen # Exit class class Exit(pygame.sprite.Sprite): def __init__(self, image, x, y): pygame.sprite.Sprite.__init__(self) # Initialize the sprite self.image = image # Set the image self.rect = self.image.get_rect() # Get the rect of the image self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height())) # Set the position def update(self): self.rect.x += screen_scroll # Scroll the screen # ItemBox class class ItemBox(pygame.sprite.Sprite): def __init__(self, item_type, x, y): pygame.sprite.Sprite.__init__(self) # Initialize the sprite self.item_type = item_type # Set the item type self.image = item_boxes[self.item_type] # Set the image based on item type self.rect = self.image.get_rect() # Get the rect of the image self.rect.midtop = (x + TILE_SIZE // 2, y + (TILE_SIZE - self.image.get_height())) # Set the position def update(self): self.rect.x += screen_scroll # Scroll the screen if pygame.sprite.collide_rect(self, player): # Check for collision with the player # Apply the item effect based on item type if self.item_type == 'Health': player.health += 25 # Increase health if player.health > player.max_health: player.health = player.max_health # Cap health to max health elif self.item_type == 'Ammo': player.ammo += 15 # Increase ammo elif self.item_type == 'Grenade': player.grenades += 3 # Increase grenades self.kill() # Remove the item box # HealthBar class class HealthBar(): def __init__(self, x, y, health, max_health): self.x = x # Set the x position self.y = y # Set the y position self.health = health # Set the health self.max_health = max_health # Set the maximum health def draw(self, health): self.health = health # Update the health ratio = self.health / self.max_health # Calculate the health ratio pygame.draw.rect(screen, BLACK, (self.x - 2, self.y - 2, 154, 24)) # Draw the background rectangle pygame.draw.rect(screen, RED, (self.x, self.y, 150, 20)) # Draw the red health bar pygame.draw.rect(screen, GREEN, (self.x, self.y, 150 * ratio, 20)) # Draw the green health bar # Bullet class class Bullet(pygame.sprite.Sprite): def __init__(self, x, y, direction): pygame.sprite.Sprite.__init__(self) # Initialize the sprite self.speed = 10 # Set the bullet speed self.image = bullet_image # Set the bullet image self.rect = self.image.get_rect() # Get the rect of the image self.rect.center = (x, y) # Set the initial position self.direction = direction # Set the direction def update(self): self.rect.x += (self.direction * self.speed) + screen_scroll # Move the bullet and scroll the screen if self.rect.right < 0 or self.rect.left > SCREEN_WIDTH: self.kill() # Remove the bullet if it goes off-screen for tile in world.obstacle_list: if tile[1].colliderect(self.rect): self.kill() # Remove the bullet if it collides with a tile if pygame.sprite.spritecollide(player, bullet_group, False): if player.alive: player.health -= 5 # Decrease player's health self.kill() # Remove the bullet for enemy in enemy_group: if pygame.sprite.spritecollide(enemy, bullet_group, False): if enemy.alive: enemy.health -= 25 # Decrease enemy's health self.kill() # Remove the bullet # Grenade class class Grenade(pygame.sprite.Sprite): def __init__(self, x, y, direction): pygame.sprite.Sprite.__init__(self) # Initialize the sprite self.timer = 100 # Set the timer for the grenade self.vel_y = -11 # Set the initial vertical velocity self.speed = 7 # Set the horizontal speed self.image = grenade_image # Set the grenade image self.rect = self.image.get_rect() # Get the rect of the image self.rect.center = (x, y) # Set the initial position self.width = self.image.get_width() # Get the width of the image self.height = self.image.get_height() # Get the height of the image self.direction = direction # Set the direction def update(self): self.vel_y += GRAVITY # Apply gravity dx = self.direction * self.speed # Calculate horizontal movement dy = self.vel_y # Calculate vertical movement # Check for collision with tiles for tile in world.obstacle_list: if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height): self.direction *= -1 # Change direction if colliding horizontally dx = self.direction * self.speed # Recalculate horizontal movement if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height): self.speed = 0 # Stop horizontal movement if colliding vertically if self.vel_y < 0: self.vel_y = 0 # Stop upward movement dy = tile[1].bottom - self.rect.top # Adjust position elif self.vel_y >= 0: self.vel_y = 0 # Stop downward movement dy = tile[1].top - self.rect.bottom # Adjust position self.rect.x += dx + screen_scroll # Apply horizontal movement and scroll the screen self.rect.y += dy # Apply vertical movement self.timer -= 1 # Decrease the timer if self.timer <= 0: self.kill() # Remove the grenade grenade_fx.play() # Play the explosion sound effect explosion = Explosion(self.rect.x, self.rect.y, 0.5) # Create an explosion explosion_group.add(explosion) # Add the explosion to the explosion group if abs(self.rect.centerx - player.rect.centerx) < TILE_SIZE * 2 and \ abs(self.rect.centery - player.rect.centery) < TILE_SIZE * 2: player.health -= 100 # Decrease player's health for enemy in enemy_group: if abs(self.rect.centerx - enemy.rect.centerx) < TILE_SIZE * 2 and \ abs(self.rect.centery - enemy.rect.centery) < TILE_SIZE * 2: enemy.health -= 100 # Decrease enemy's health # Explosion class class Explosion(pygame.sprite.Sprite): def __init__(self, x, y, scale): pygame.sprite.Sprite.__init__(self) # Initialize the sprite self.images = [] # List to store explosion frames for num in range(1, 6): image = pygame.image.load(f'Images/explosion/exp{num}.png').convert_alpha() # Load the explosion image image = pygame.transform.scale(image, (int(image.get_width() * scale * 3.5), int(image.get_height() * scale * 3.5))) # Scale the image self.images.append(image) # Add the image to the list self.frame_index = 0 # Current frame index self.image = self.images[self.frame_index] # Set the initial image self.rect = self.image.get_rect() # Get the rect of the image self.rect.center = (x, y) # Set the initial position self.counter = 0 # Counter to control animation speed def update(self): self.rect.x += screen_scroll # Scroll the screen EXPLOSION_SPEED = 4 # Speed of the explosion animation self.counter += 1 # Increase the counter if self.counter >= EXPLOSION_SPEED: self.counter = 0 # Reset the counter self.frame_index += 1 # Move to the next frame if self.frame_index >= len(self.images): self.kill() # Remove the explosion if all frames have been displayed else: self.image = self.images[self.frame_index] # Set the next frame # ScreenFade class class ScreenFade(): def __init__(self, direction, colour, speed): self.direction = direction # Direction of the fade self.colour = colour # Colour of the fade self.speed = speed # Speed of the fade self.fade_counter = 0 # Counter to control the fade def fade(self): fade_complete = False # Flag to check if the fade is complete self.fade_counter += self.speed # Increase the fade counter if self.direction == 1: # Vertical fade pygame.draw.rect(screen, self.colour, (0 - self.fade_counter, 0, SCREEN_WIDTH // 2, SCREEN_HEIGHT)) # Draw the top fade rectangle pygame.draw.rect(screen, self.colour, (SCREEN_WIDTH // 2 + self.fade_counter, 0, SCREEN_WIDTH, SCREEN_HEIGHT)) # Draw the bottom fade rectangle pygame.draw.rect(screen, self.colour, (0, 0 - self.fade_counter, SCREEN_WIDTH, SCREEN_HEIGHT // 2)) # Draw the left fade rectangle pygame.draw.rect(screen, self.colour, (0, SCREEN_HEIGHT // 2 + self.fade_counter, SCREEN_WIDTH, SCREEN_HEIGHT)) # Draw the right fade rectangle if self.direction == 2: # Horizontal fade pygame.draw.rect(screen, self.colour, (0, 0, SCREEN_WIDTH, 0 + self.fade_counter)) # Draw the fade rectangle if self.fade_counter >= SCREEN_WIDTH: fade_complete = True # Set the fade complete flag return fade_complete # Return the fade complete flag # Create screen fade objects intro_fade = ScreenFade(1, BLACK, 4) # Intro fade object death_fade = ScreenFade(2, PINK, 4) # Death fade object # Create button objects start_button = button.Button(SCREEN_WIDTH // 2 - 130, SCREEN_HEIGHT // 2 - 150, start_image, 1) # Start button exit_button = button.Button(SCREEN_WIDTH // 2 - 110, SCREEN_HEIGHT // 2 + 50, exit_image, 1) # Exit button restart_button = button.Button(SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 - 50, restart_image, 2) # Restart button # Create sprite groups enemy_group = pygame.sprite.Group() # Group for enemies bullet_group = pygame.sprite.Group() # Group for bullets grenade_group = pygame.sprite.Group() # Group for grenades explosion_group = pygame.sprite.Group() # Group for explosions item_box_group = pygame.sprite.Group() # Group for item boxes decoration_group = pygame.sprite.Group() # Group for decorations lava_group = pygame.sprite.Group() # Group for lava exit_group = pygame.sprite.Group() # Group for exits # Create an empty world data list world_data = [] for row in range(ROWS): r = [-1] * COLUMNS # Create a row with default tile values world_data.append(r) # Add the row to the world data list # Load level data from CSV file with open(f'level{level}_data.csv', newline='') as csvfile: reader = csv.reader(csvfile, delimiter=',') for x, row in enumerate(reader): for y, tile in enumerate(row): world_data[x][y] = int(tile) # Add the tile to the world data list world = World() # Create a world object player, health_bar = world.process_data(world_data) # Process the world data and create the player and health bar run = True # Flag to check if the game is running while run: clock.tick(FPS) # Control the frame rate if start_game == False: screen.fill(background) # Fill the screen with the background color if start_button.draw(screen): start_game = True # Start the game start_intro = True # Start the intro animation if exit_button.draw(screen): run = False # Exit the game else: draw_background() # Draw the background world.draw() # Draw the world health_bar.draw(player.health) # Draw the health bar draw_text('AMMO: ', font, WHITE, 10, 35) # Draw the ammo text for x in range(player.ammo): screen.blit(bullet_image, (90 + (x * 10), 40)) # Draw the ammo icons draw_text('GRENADES: ', font, WHITE, 10, 60) # Draw the grenades text for x in range(player.grenades): screen.blit(grenade_image, (135 + (x * 15), 60)) # Draw the grenade icons player.update() # Update the player player.draw() # Draw the player for enemy in enemy_group: enemy.ai() # Update the enemy AI enemy.update() # Update the enemy enemy.draw() # Draw the enemy bullet_group.update() # Update the bullets grenade_group.update() # Update the grenades explosion_group.update() # Update the explosions item_box_group.update() # Update the item boxes decoration_group.update() # Update the decorations lava_group.update() # Update the lava exit_group.update() # Update the exits bullet_group.draw(screen) # Draw the bullets grenade_group.draw(screen) # Draw the grenades explosion_group.draw(screen) # Draw the explosions item_box_group.draw(screen) # Draw the item boxes decoration_group.draw(screen) # Draw the decorations lava_group.draw(screen) # Draw the lava exit_group.draw(screen) # Draw the exits if start_intro == True: if intro_fade.fade(): start_intro = False # End the intro animation intro_fade.fade_counter = 0 # Reset the fade counter if player.alive: if shoot: player.shoot() # Player shoots elif grenade and grenade_thrown == False and player.grenades > 0: grenade = Grenade(player.rect.centerx + (0.5 * player.rect.size[0] * player.direction), player.rect.top, player.direction) # Create a grenade grenade_group.add(grenade) # Add the grenade to the grenade group player.grenades -= 1 # Decrease the grenade count grenade_thrown = True # Set the grenade thrown flag if player.in_air: player.update_action(2) # Set action to jump elif moving_left or moving_right: player.update_action(1) # Set action to run else: player.update_action(0) # Set action to idle screen_scroll, level_complete = player.move(moving_left, moving_right) # Move the player and get the screen scroll and level complete flag background_scroll -= screen_scroll # Scroll the background if level_complete: start_intro = True # Start the intro animation level += 1 # Move to the next level background_scroll = 0 # Reset the background scroll world_data = reset_level() # Reset the level data if level <= MAX_LEVELS: with open(f'level{level}_data.csv', newline='') as csvfile: reader = csv.reader(csvfile, delimiter=',') for x, row in enumerate(reader): for y, tile in enumerate(row): world_data[x][y] = int(tile) # Add the tile to the world data list world = World() # Create a new world object player, health_bar = world.process_data(world_data) # Process the world data and create the player and health bar else: screen_scroll = 0 # Stop screen scrolling if death_fade.fade(): if restart_button.draw(screen): death_fade.fade_counter = 0 # Reset the fade counter start_intro = True # Start the intro animation background_scroll = 0 # Reset the background scroll world_data = reset_level() # Reset the level data with open(f'level{level}_data.csv', newline='') as csvfile: reader = csv.reader(csvfile, delimiter=',') for x, row in enumerate(reader): for y, tile in enumerate(row): world_data[x][y] = int(tile) # Add the tile to the world data list world = World() # Create a new world object player, health_bar = world.process_data(world_data) # Process the world data and create the player and health bar for event in pygame.event.get(): if event.type == pygame.QUIT: run = False # Exit the game if event.type == pygame.KEYDOWN: if event.key == pygame.K_a: moving_left = True # Move left if event.key == pygame.K_d: moving_right = True # Move right if event.key == pygame.K_SPACE: shoot = True # Shoot if event.key == pygame.K_q: grenade = True # Throw grenade if event.key == pygame.K_w and player.alive: player.jump = True # Jump jump_fx.play() # Play jump sound if event.key == pygame.K_ESCAPE: run = False # Exit the game if event.type == pygame.KEYUP: if event.key == pygame.K_a: moving_left = False # Stop moving left if event.key == pygame.K_d: moving_right = False # Stop moving right if event.key == pygame.K_SPACE: shoot = False # Stop shooting if event.key == pygame.K_q: grenade = False # Stop throwing grenade grenade_thrown = False # Reset grenade thrown flag pygame.display.update() # Update the display pygame.quit() # Quit pygame