000Nobody / Pygame-Platformer-Shooter

First major project on Python
MIT License
77 stars 19 forks source link

New game button not clickable; cursor not in sync with mouse #2

Open JXTK opened 3 years ago

JXTK commented 3 years ago
# clicking F5 to run
import pygame, math, os, random, sys, time

from operator import sub
from pygame.locals import *

pygame.mixer.pre_init(44100, -16, 2, 2048)
pygame.init()
clock = pygame.time.Clock()

pygame.display.set_caption('The War Against COVID-19')

info_object = pygame.display.Info()
WINDOW_SIZE = (info_object.current_w, info_object.current_h)

screen = pygame.display.set_mode(WINDOW_SIZE, FULLSCREEN)

display = pygame.Surface((1920, 1080))

pygame.mouse.set_visible(True)

# Create variables
FPS = 60
last_time = time.time()
save_number = 0
main_menu = True
load_game_menu = False
game_running = False
escape_menu = False
win_screen = False
fade_out = False
fade_in = False
fade_alpha = 0
scroll = [0,0]
gravity_strength = 1.8
bullets = []
enemy_bullets = []
tile_rects = []
particles = []
enemies = []

# Load Images
cursor = pygame.transform.scale(pygame.image.load('data/images/cursor.png'), (32, 32)).convert()
cursor.set_colorkey((255, 255, 255))

instruction_img = pygame.image.load('data/images/instructions.png').convert_alpha()
title_img = pygame.image.load('data/images/title_image.png').convert_alpha()
health_bar_img = pygame.image.load('data/images/health_bar.png').convert_alpha()
overlay_img = pygame.transform.scale(pygame.image.load('data/images/black_overlay.png').convert(), WINDOW_SIZE)
overlay_img.set_alpha(150)

grass = pygame.image.load('data/images/tiles/grass.png').convert()
dirt = pygame.image.load('data/images/tiles/dirt.png').convert()
middle_platform = pygame.image.load('data/images/tiles/platform_middle.png').convert_alpha()
left_edge_platform = pygame.image.load('data/images/tiles/platform_edge.png').convert_alpha()
right_edge_platform = pygame.transform.flip(pygame.image.load('data/images/tiles/platform_edge.png').convert_alpha(), True, False)
left_transition_dirt = pygame.image.load('data/images/tiles/transition_dirt.png').convert()
right_transition_dirt = pygame.transform.flip(pygame.image.load('data/images/tiles/transition_dirt.png').convert(), True, False)
left_transition_grass = pygame.image.load('data/images/tiles/transition_grass.png').convert()
right_transition_grass = pygame.transform.flip(pygame.image.load('data/images/tiles/transition_grass.png').convert(), True, False)
left_edge_dirt = pygame.image.load('data/images/tiles/edge_dirt.png').convert_alpha()
right_edge_dirt = pygame.transform.flip(pygame.image.load('data/images/tiles/edge_dirt.png').convert_alpha(), True, False)
left_edge_grass = pygame.image.load('data/images/tiles/edge_grass.png').convert_alpha()
right_edge_grass = pygame.transform.flip(pygame.image.load('data/images/tiles/edge_grass.png').convert_alpha(), True, False)
left_side_grass_transition = pygame.image.load('data/images/tiles/side_grass_transition.png').convert_alpha()
right_side_grass_transition = pygame.transform.flip(pygame.image.load('data/images/tiles/side_grass_transition.png').convert_alpha(), True, False)
left_bottom_corner_dirt = pygame.image.load('data/images/tiles/bottom_corner_dirt.png').convert_alpha()
right_bottom_corner_dirt = pygame.transform.flip(pygame.image.load('data/images/tiles/bottom_corner_dirt.png').convert_alpha(), True, False)
bottom_dirt = pygame.image.load('data/images/tiles/bottom_dirt.png').convert_alpha()

gun_img = pygame.image.load('data/images/gun.png').convert_alpha()

projectile_img = pygame.image.load('data/images/projectile.png').convert()
projectile_img.set_colorkey((0, 0, 0))

enemy_projectile_img = pygame.image.load('data/images/enemy_projectile.png').convert()
enemy_projectile_img.set_colorkey((0, 0, 0))

def load_animations(actions, folder_name): #(['Running', 'Idle'], 'player_images')
    animation_database = {}
    for action in actions:
        image_path = 'data/' + folder_name + '/' + action
        animation_database.update({action:[]})
        for image in os.listdir(image_path):
            image_id = pygame.image.load(image_path + '/' + image).convert_alpha()
            animation_database[action].append(pygame.transform.scale(image_id, (200, 200)))
    return animation_database

player_animations = load_animations(['Running', 'Idle', 'Walking'], 'player_images')
enemy_animations = load_animations(['Idle', 'Walking'], 'enemy_images')

# Load sounds
death_sound = pygame.mixer.Sound('data/sounds/death.wav')
jump_sound = pygame.mixer.Sound('data/sounds/jump.wav')
shoot_sound = pygame.mixer.Sound('data/sounds/shoot.wav')
explosion_sound = pygame.mixer.Sound('data/sounds/explosion.wav')
enemy_hit_sound = pygame.mixer.Sound('data/sounds/enemy_hit.wav')
enemy_death_sound = pygame.mixer.Sound('data/sounds/enemy_death.wav')
player_hit_sound = pygame.mixer.Sound('data/sounds/player_hit.wav')
select_sound = pygame.mixer.Sound('data/sounds/select.wav')
jump_sound.set_volume(0.8)
shoot_sound.set_volume(0.5)
explosion_sound.set_volume(0.7)
enemy_hit_sound.set_volume(0.7)
select_sound.set_volume(0.6)

# Load Fonts
pixel_font = pygame.font.Font('data/fonts/pixel_font.ttf', 30)
pixel_font_large = pygame.font.Font('data/fonts/pixel_font.ttf', 300)
title_font = pygame.font.Font('data/fonts/title_font.ttf', 75)
title_font_large = pygame.font.Font('data/fonts/title_font.ttf', 100)

# Create save file
if not os.path.isfile('saves.txt'):
    save_file = open('saves.txt', 'w+')

#Classes
class Level():
    def __init__(self, map_name, player_pos, enemy_pos, die_height):
        self.player_pos = player_pos
        self.enemy_pos = enemy_pos
        self.tile_size = (64, 64)
        self.map_name = map_name
        self.die_height = die_height
        self.path = 'data/maps/{}.txt'.format(self.map_name)
        self.timer = 0

    def load_map(self):
        self.map = []
        with open(self.path, 'r') as f:
            data = f.read()
            data = data.split('\n')
            for row in data:
                self.map.append(list(row))

    def create_map_hitbox(self):
        global tile_rects
        tile_rects = []
        y = 0
        for layer in self.map:
            x = 0
            for tile in layer:
                if tile != '0':
                    tile_rects.append(pygame.Rect(int(x), int(y), self.tile_size[0], self.tile_size[1]))
                x += self.tile_size[0]
            y += self.tile_size[1]

    def draw(self):
        y = 0
        for layer in self.map:
            x = 0
            for tile in layer:
                if tile == '1':
                    display.blit(pygame.transform.scale(grass, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))
                elif tile == '2':
                    display.blit(pygame.transform.scale(dirt, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))
                elif tile == '3':
                    display.blit(pygame.transform.scale(left_edge_dirt, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))
                elif tile == '4':
                    display.blit(pygame.transform.scale(right_edge_dirt, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))
                elif tile == '5':
                    display.blit(pygame.transform.scale(left_edge_grass, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))
                elif tile == '6':
                    display.blit(pygame.transform.scale(right_edge_grass, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))
                elif tile == '7':
                    display.blit(pygame.transform.scale(left_edge_platform, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))
                elif tile == '8':
                    display.blit(pygame.transform.scale(middle_platform, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))
                elif tile == '9':
                    display.blit(pygame.transform.scale(right_edge_platform, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))
                elif tile == 'a':
                    display.blit(pygame.transform.scale(left_transition_dirt, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))
                elif tile == 'b':
                    display.blit(pygame.transform.scale(right_transition_dirt, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))    
                elif tile == 'c':
                    display.blit(pygame.transform.scale(left_transition_grass, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))            
                elif tile == 'd':
                    display.blit(pygame.transform.scale(right_transition_grass, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))
                elif tile == 'e':
                    display.blit(pygame.transform.scale(left_side_grass_transition, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))
                elif tile == 'f':
                    display.blit(pygame.transform.scale(right_side_grass_transition, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))
                elif tile == 'g':
                    display.blit(pygame.transform.scale(left_bottom_corner_dirt, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))
                elif tile == 'h':
                    display.blit(pygame.transform.scale(right_bottom_corner_dirt, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))
                elif tile == 'i':
                    display.blit(pygame.transform.scale(bottom_dirt, (self.tile_size[0], self.tile_size[1])), (x - scroll[0], y - scroll[1]))
                x += self.tile_size[0]
            y += self.tile_size[1]

class Player():
    def __init__(self, width, height, vel, jump_height, health):
        self.vel = vel
        self.width = width
        self.height = height
        self.jump_height = jump_height
        self.health = health
        self.jumping = False
        self.moving_right = False
        self.moving_left = False
        self.flip = False
        self.sprinting = False
        self.vertical_momentum = 0
        self.movement = [0, 0]
        self.frame = 0
        self.action = 'Idle'
        self.animation_speed = 0
        self.level = 'Tutorial'
        self.times_jumped = 0
        self.animation_database = player_animations
        self.rect = pygame.Rect(int(levels[self.level].player_pos[0]), int(levels[self.level].player_pos[1]), self.width, self.height)

    def update(self):
        self.move()
        self.looking(pygame.mouse.get_pos())

    def move(self):
        if self.sprinting:
            self.vel = 15
        if self.moving_right:
            self.movement[0] = self.vel
        if self.moving_left:
            self.movement[0] = -self.vel
        if self.jumping:
            self.vertical_momentum = -self.jump_height
            self.times_jumped += 1
            jump_sound.play()
            for i in range(5):
                particles.append(Particle(player.rect.midbottom[0], player.rect.midbottom[1], [(150, 150, 150), (225, 225, 225), (200, 200, 200)], -40, 40, -5, 0, 4, 10, 0.8, 0.2))
            self.jumping = False

        if not self.moving_left and not self.moving_right:
            self.movement = [0, 0]
        self.movement[1] = self.vertical_momentum

        self.rect, self.collision_types, self.hit_list = move(self.rect, tile_rects, self.movement)

        self.vertical_momentum += gravity_strength * dt
        if self.vertical_momentum > 50:
             self.vertical_momentum = 50

        if self.collision_types['bottom']:
            self.vertical_momentum = 0
            self.times_jumped = 0
        if self.collision_types['top']:
            self.vertical_momentum = 0

    def die(self):
        pygame.mixer.music.fadeout(1000)
        death_sound.play()
        self.rect.topleft = levels[self.level].player_pos
        enemies.clear()
        bullets.clear()
        particles.clear()
        enemy_bullets.clear()
        enemy_id_counter = 0
        for enemy_pos in levels[self.level].enemy_pos:
            enemies.append(Enemy(enemy_id_counter, enemy_pos, 75, 125, 100, 900, 900))
            enemy_id_counter += 1
        pygame.mixer.music.play(-1)
        self.health = 100
        self.living = True

    def draw(self):
        if self.moving_right or self.moving_left:
            if self.sprinting:
                self.change_action(self.action, 'Running', self.frame)
            else:
                self.change_action(self.action, 'Walking', self.frame)
        if self.movement[0] == 0:
            self.change_action(self.action, 'Idle', self.frame)

        if self.action == 'Idle':
            self.animation_speed = 6
        if self.action == 'Running':
            self.animation_speed = 4
        if self.action == 'Walking':
            self.animation_speed = 2

        self.frame += 1
        if self.frame >= len(self.animation_database[self.action]) * self.animation_speed:
            self.frame = 0

        current_image = self.animation_database[self.action][self.frame//self.animation_speed]

        display.blit(pygame.transform.flip(current_image, self.flip, False), (int(self.rect.x - 60 - scroll[0]), int(self.rect.y - 40 - scroll[1])))
        # pygame.draw.rect(display, (0, 0, 0), self.rect, 1)

    def change_level(self, new_level):
        #update save file
        save_data = get_saves()
        save_data[save_number] = new_level
        save_data_string = ''
        for i in save_data:
            save_data_string += i + ','

        with open('saves.txt', 'w') as f:
            f.write(save_data_string)
            f.close()

        #reset everything
        enemies.clear()
        particles.clear()
        bullets.clear()
        enemy_bullets.clear()
        levels[new_level].create_map_hitbox()
        timer = 0
        self.health = 100
        self.level = new_level
        self.rect.topleft = levels[new_level].player_pos
        for enemy_pos in levels[new_level].enemy_pos:
            enemies.append(Enemy(enemy_id_counter, enemy_pos, 75, 125, 100, 900, 900))

    def looking(self, mousepos):
        if mousepos[0] <= self.rect.centerx - scroll[0]:
            self.flip = True
        else:
            self.flip = False

    def change_action(self, current_action, new_action, frame):
        if current_action != new_action:
            current_action = new_action
            frame = 0
        self.action = current_action
        self.frame = frame

class Enemy():
    def __init__(self, id, start_pos, width, height, health, pathfind_range, attack_range):
        self.id = id
        self.start_pos = start_pos
        self.width = width
        self.height = height
        self.health = health
        self.vel = 5
        self.jump_height = 20
        self.pathfind_range = pathfind_range
        self.attack_range = attack_range
        self.shoot_timer = 0
        self.movement = [0, 0]
        self.moving_right = False
        self.moving_left = False
        self.jumping = False 
        self.vertical_momentum = 0
        self.flip = False
        self.animation_speed = 0
        self.frame = 0
        self.action = 'Idle'
        self.animation_database = enemy_animations
        self.rect = pygame.Rect(int(self.start_pos[0]), int(self.start_pos[1]), self.width, self.height)

    def update(self):
        self.move()
        self.pathfind()
        self.attack()
        self.looking()

    def move(self):
        if self.moving_right:
            self.movement[0] = self.vel
        if self.moving_left:
            self.movement[0] = -self.vel
        if self.jumping:
            self.vertical_momentum = -self.jump_height
            self.jumping = False
        self.movement[1] = self.vertical_momentum

        self.rect, self.collision_types, self.hit_list = move(self.rect, tile_rects
                                                              + [enemy.rect for enemy in enemies if enemy.id != self.id], self.movement)

        self.vertical_momentum += gravity_strength * dt
        if self.vertical_momentum > 50:
             self.vertical_momentum = 50

        if self.collision_types['bottom']:
            self.vertical_momentum = 0
            self.times_jumped = 0
        if self.collision_types['top']:
            self.vertical_momentum = 0

    def draw(self):
        if self.moving_right or self.moving_left:
                self.change_action(self.action, 'Walking', self.frame)
        if self.movement[0] == 0:
            self.change_action(self.action, 'Idle', self.frame)

        if self.action == 'Idle':
            self.animation_speed = 6
        if self.action == 'Walking':
            self.animation_speed = 2

        self.frame += 1
        if self.frame >= len(self.animation_database[self.action]) * self.animation_speed:
            self.frame = 0

        current_image = self.animation_database[self.action][self.frame//self.animation_speed]

        display.blit(pygame.transform.flip(current_image, self.flip, False), (int(self.rect.x - 60 - scroll[0]), int(self.rect.y - 40 - scroll[1])))

    def change_action(self, current_action, new_action, frame):
        if current_action != new_action:
            current_action = new_action
            frame = 0
        self.action = current_action
        self.frame = frame

    def attack(self):
        self.shoot_timer += 1 * dt
        if math.sqrt(abs((self.rect.centerx - player.rect.centerx)**2 + (self.rect.centery - player.rect.centery)**2)) <= self.attack_range:
            if self.shoot_timer >= 60:
                self.slopex = (player.rect.centerx - scroll[0]) - (self.rect.centerx - scroll[0] + 5)
                self.slopey = (player.rect.centery - scroll[1]) - (self.rect.centery - scroll[1] + 35)
                enemy_bullets.append(Projectile(self.rect.centerx + 5, self.rect.centery + 35, 15, 17, 35,
                                     math.atan2(self.slopey, self.slopex), enemy_projectile_img))
                self.shoot_timer = 0
        if self.rect.colliderect(player.rect):
            player.health -= 2

    def pathfind(self):
        if math.sqrt(abs((self.rect.centerx - player.rect.centerx)**2 + (self.rect.centery - player.rect.centery)**2)) <= self.pathfind_range:
            if 10 > abs(self.rect.x - player.rect.x) > 0:
                self.vel = 0
            else:
                self.vel = 5
                if self.rect.centerx > player.rect.centerx:
                    self.moving_left = True
                    self.moving_right = False
                if self.rect.centerx < player.rect.centerx:
                    self.moving_right = True
                    self.moving_left = False

        if not [enemy for enemy in enemies if enemy.id != self.id and enemy.rect in self.hit_list]:
            if self.collision_types['left'] or self.collision_types['right']:
                self.jumping = True

    def looking(self):
        if self.moving_right:
            self.flip = False
        if self.moving_left:
            self.flip = True

class Gun():
    global x_offset, y_offset
    x_offset, y_offset = 5, 43

    def __init__(self, image):
        self.image = image
        self.x = player.rect.centerx + x_offset - scroll[0]
        self.y = player.rect.centery + y_offset - scroll[1]

    def update(self):
        self.x = player.rect.centerx + x_offset - scroll[0]
        self.y = player.rect.centery + y_offset - scroll[1]

    def get_angle(self, mousepos):
        angle = -math.degrees(math.atan2(self.y - 10 - mousepos[1], self.x - mousepos[0]))
        return angle

    def draw(self, angle):
        if player.flip:
            rotated_gun = pygame.transform.rotate(self.image, angle)
            rect = rotated_gun.get_rect()
            display.blit(pygame.transform.flip(rotated_gun, False, False), (self.x - (rect.width/2), self.y - (rect.height/2)))
        if not player.flip:
            rotated_gun = pygame.transform.rotate(self.image, -angle)
            rect = rotated_gun.get_rect()
            display.blit(pygame.transform.flip(rotated_gun, False, True), (self.x - (rect.width/2), self.y - (rect.height/2)))

        self.gun_rect = pygame.Rect(self.x - (rect.width/2), self.y - (rect.height/2), self.image.get_width(), self.image.get_height())
        # pygame.draw.rect(display, (0, 0, 0), self.gun_rect, 1)

class Projectile():
    def __init__(self, x, y, radius, vel, damage, angle, image):
        self.x = x
        self.y = y
        self.radius = radius
        self.vel = vel
        self.damage = damage
        self.angle = angle
        self.image = image
        self.rect = pygame.Rect(self.x - self.radius, self.y - self.radius, self.radius * 2, self.radius * 2)

    def update(self):
        self.trajectory()
        self.collision_check(self.rect, tile_rects)

    def trajectory(self):
        self.x += math.cos(self.angle) * self.vel * dt
        self.y += math.sin(self.angle) * self.vel * dt

    def draw(self):
        self.rect = pygame.Rect(self.x - self.radius, self.y - self.radius, self.radius * 2, self.radius * 2)
        self.image = pygame.transform.scale(self.image, (self.radius * 2, self.radius * 2))
        display.blit(self.image, (int(self.x - scroll[0] - self.radius), int(self.y - scroll[1] - self.radius)))
        # pygame.draw.rect(display, (0, 0, 0), self.rect)

    def collision_check(self, rects, tiles):
        self.collision_types = {'top':False,'bottom':False,'right':False,'left':False}

        hit_list = collision_check(rects, tiles)

        for rect in hit_list:
            y_difference = (rect.centery - self.rect.centery)
            x_difference = (rect.centerx - self.rect.centerx)
            angle = math.atan2(y_difference, x_difference)
            angle = math.degrees(angle)

            if 45 < angle < 135:
                self.collision_types['bottom'] = True
            if -135 < angle < -45:
                self.collision_types['top'] = True
            if -45 < angle < 45:
                self.collision_types['right'] = True
            if 135 < angle < 180 or -180 < angle < -135:
                self.collision_types['left'] = True

class Particle():
    def __init__(self, x, y, colors, min_xvel, max_xvel, min_yvel,
                 max_yvel, min_radius, max_radius, shrink_rate, gravity):
        self.x = x
        self.y = y
        self.color = random.choice(colors)
        self.xvel = random.randint(min_xvel, max_xvel) / 10
        self.yvel = random.randint(min_yvel, max_yvel) / 10
        self.radius = random.randint(min_radius, max_radius)
        self.shrink_rate = shrink_rate
        self.gravity = gravity

    def update(self):
        self.x += self.xvel * dt
        self.y += self.yvel * dt
        self.radius -= self.shrink_rate * dt
        self.yvel += self.gravity * dt

    def draw(self):
        pygame.draw.circle(display, self.color, (int(self.x - scroll[0]), int(self.y - scroll[1])), int(self.radius))

class Button():
    def __init__(self, x, y, width, height, color, text, text_color, font):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.color = color
        self.pressed_color = tuple(map(sub, self.color, (50, 50, 50)))
        self.text = text
        self.text_color = text_color
        self.font = font

    def update(self):
        self.rect = pygame.Rect(self.x, self.y, self.width, self.height)
        self.rendered_text = pixel_font.render(self.text, 1, self.text_color)
        self.text_rect = self.rendered_text.get_rect()
        self.text_rect.center = self.rect.center

    def is_over(self):
        mx, my = pygame.mouse.get_pos()
        if self.x < mx < self.x + self.width and self.y < my < self.y + self.height:
            return True
        else:
            return False

    def draw(self):
        mx, my = pygame.mouse.get_pos()
        if self.is_over():
            pygame.draw.rect(display, self.pressed_color, self.rect)
        else:
            pygame.draw.rect(display, self.color, self.rect)
        pygame.draw.rect(display, self.text_color, self.rect, 5)
        display.blit(self.rendered_text, (self.text_rect.x, self.text_rect.y))

# Create classes
levels = {'Tutorial':Level('map0', (600, 490), [(2940, 250)], 1400),
          'Level 1':Level('map1', (830, -100), [(140, -145), (5375, 280), (7215, 345)], 800),
          'Level 2':Level('map2', (600, 800), [(255, 445), (1695, -130), (3925, 380), (3915, 0)], 1900),
          'Level 3':Level('map3', (50, 400), [(1790, 400), (3125, 100)], 1000),
          'Level 4':Level('map4', (295, 100), [(1105, 480), (1855, 600), (3935, 675), (4385, 925), (5045, 850)], 1500),
          'Level 5':Level('map5', (165, -200), [(4865, 350), (5720, 550), (8205, 350), (10690, 550)], 1500)} 
for level in levels:
    levels[level].load_map()

player = Player(75, 125, 10, 28, 100)

enemy_id_counter = 0
for enemy_pos in levels[player.level].enemy_pos:
    enemies.append(Enemy(enemy_id_counter, enemy_pos, 75, 125, 100, 900, 900))
    enemy_id_counter += 1

gun = Gun(gun_img)

#Functions
def collision_check(rect, tiles):
    hit_list = []
    for tile in tiles:
        if tile not in hit_list:
            if rect.colliderect(tile):
                hit_list.append(tile)
    return hit_list

def move(rect, tiles, movement):
    collision_types = {'top':False,'bottom':False,'right':False,'left':False}
    rect.x += movement[0] * dt
    hit_list = collision_check(rect, tiles)
    for tile in hit_list:
        if movement[0] * dt > 0:
            rect.right = tile.left
            collision_types['right'] = True
        elif movement[0] * dt < 0:
            rect.left = tile.right
            collision_types['left'] = True
    rect.y += movement[1] * dt
    hit_list = collision_check(rect, tiles)
    for tile in hit_list:
        if movement[1] * dt > 0:
            rect.bottom = tile.top
            collision_types['bottom'] = True
        elif movement[1] * dt < 0:
            rect.top = tile.bottom
            collision_types['top'] = True

    return rect, collision_types, hit_list

def get_saves():
    with open('saves.txt', 'r') as f:
        save_data = f.read()
        f.close()
    save_data = save_data.split(',')
    save_data = save_data[:-1]
    return save_data

def update_cursor(mousepos):
    cursor_rect = cursor.get_rect()
    mx, my = mousepos
    mx *= (WINDOW_SIZE[0]/1920)
    my *= (WINDOW_SIZE[1]/1080)
    cursor_rect.center = (mx, my)
    screen.blit(cursor, cursor_rect)

def play_bgmusic():
    pygame.mixer.music.load('data/sounds/bgmusic.wav')
    pygame.mixer.music.play(-1)
    pygame.mixer.music.set_volume(0.6)

def play_menu_music():
    pygame.mixer.music.load('data/sounds/menu_music.wav')
    pygame.mixer.music.play(-1)
    pygame.mixer.music.set_volume(0.2)

def play_win_music():
    pygame.mixer.music.load('data/sounds/win_music.wav')
    pygame.mixer.music.play(-1)
    pygame.mixer.music.set_volume(0.4)

def draw_main_menu():
    display.fill((180, 235, 235))

    new_game_button.draw()
    load_game_button.draw()
    exit_button.draw()
    display.blit(title_img, (250, 0))

    screen.blit(pygame.transform.scale(display, WINDOW_SIZE), (0, 0))

    update_cursor(pygame.mouse.get_pos())

    pygame.display.update()

def draw_load_game_menu():
    display.fill((180, 235, 235))

    for button in save_buttons:
        button.draw()

    for button in delete_save_buttons:
        button.draw()

    back_button.draw()

    screen.blit(pygame.transform.scale(display, WINDOW_SIZE), (0, 0))

    update_cursor(pygame.mouse.get_pos())

    pygame.display.update()

def draw_win_screen():
    display.fill((180, 235, 235))

    congrats_text = title_font_large.render("Congratulations!", 1, (0, 0, 0))
    you_win_text = title_font.render("You have beaten the COVID Spreaders! But can you beat King Corona himself? Find out in The War Against COVID-19 - Part 2", 1, (0, 0, 0))
    display.blit(congrats_text, (190, 150))
    display.blit(you_win_text, (40, 300))

    win_main_menu_button.draw()
    win_exit_button.draw()

    screen.blit(pygame.transform.scale(display, WINDOW_SIZE), (0, 0))

    update_cursor(pygame.mouse.get_pos())

    pygame.display.update()

def draw():
    global fade_out, fade_in, fade_alpha
    display.fill((180, 235, 235))

    levels[player.level].draw()

    for enemy in enemies:
        enemy.draw()

    player.draw()

    for particle in particles:
        particle.draw()

    for bullet in enemy_bullets:
        bullet.draw()

    for bullet in bullets:
        bullet.draw()

    gun.draw(gun.get_angle(pygame.mouse.get_pos()))

    if player.level == 'Tutorial':
        display.blit(instruction_img, (380 - scroll[0], 380 - scroll[1]))

    health_bar_rect = pygame.Rect(94, 1028, player.health*2, 19)
    pygame.draw.rect(display, (255, 0, 0), health_bar_rect)
    display.blit(health_bar_img, (30, 1000))

    level_text = pixel_font.render(player.level, 1, (0, 0, 0))
    time_text = pixel_font.render(str(round(levels[player.level].timer, 2)), 1, (0, 0, 0))
    display.blit(level_text, (30, 30))
    display.blit(time_text, (30, 60))

    def draw_escape_menu():
        resume_button = Button(710, 265, 500, 150, (50, 200, 50), "Resume Game", (0, 0, 0), pixel_font_large)
        main_menu_button = Button(710, 465, 500, 150, (75, 160, 173), "Go to Main Menu", (0, 0, 0), pixel_font_large)
        exit_fullscreen_button = Button(710, 665, 500, 150, (255, 50, 50), "Exit Fullscreen", (0, 0, 0), pixel_font_large)

        resume_button.update()
        main_menu_button.update()
        exit_fullscreen_button.update()

        display.blit(overlay_img, (0, 0))

        resume_button.draw()
        main_menu_button.draw()
        exit_fullscreen_button.draw()

    if escape_menu:
        draw_escape_menu()

    if fade_out:
        fade_alpha += 7
        fade_surface = pygame.Surface(WINDOW_SIZE)
        fade_surface.fill((0, 0, 0))
        fade_surface.set_alpha(fade_alpha)
        display.blit(fade_surface, (0, 0))
        if fade_alpha >= 300:
            fade_out = False

    if fade_in:
        fade_alpha -= 7
        fade_surface = pygame.Surface(WINDOW_SIZE)
        fade_surface.fill((0, 0, 0))
        fade_surface.set_alpha(fade_alpha)
        display.blit(fade_surface, (0, 0))
        if fade_alpha <= 0:
            fade_in = False

    screen.blit(pygame.transform.scale(display, WINDOW_SIZE), (0, 0))

    update_cursor(pygame.mouse.get_pos())

    pygame.display.update()

# Main Loop
play_menu_music()
while True:
    clock.tick()
    dt = time.time() - last_time
    dt *= FPS
    last_time = time.time()

    if main_menu:
        new_game_button = Button(560, 550, 800, 200, (75, 173, 89), "New Game", (0, 0, 0), pixel_font_large)
        exit_button = Button(1500, 900, 300, 100, (255, 50, 50), "Exit to desktop", (0, 0, 0), pixel_font_large)
        load_game_button = Button(560, 800, 800, 200, (75, 160, 173), "Load Game ({}/9)".format(len(get_saves())), (0, 0, 0), pixel_font_large)

        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

            if event.type == KEYDOWN:
                if event.key == pygame.K_f:
                    screen = pygame.display.set_mode(WINDOW_SIZE, pygame.FULLSCREEN)
                if event.key == pygame.K_ESCAPE:
                    screen = pygame.display.set_mode(WINDOW_SIZE)

            if event.type == MOUSEBUTTONDOWN:
                if event.button == 1:
                    if exit_button.is_over():
                        select_sound.play()
                        sys.exit()
                        pygame.quit()
                    if new_game_button.is_over():
                        with open('saves.txt', 'r+') as f:
                            save_data = f.read()
                            save_data = save_data.split(',')
                            if len(save_data) < 9:
                                f.write('Tutorial,')
                                f.close()
                                save_number = len(save_data) - 1
                                player.change_level('Tutorial')
                                game_running = True
                                main_menu = False
                                select_sound.play()
                                play_bgmusic()
                    if load_game_button.is_over():
                        load_game_menu = True
                        main_menu = False
                        select_sound.play()

        new_game_button.update()
        load_game_button.update()
        exit_button.update()
        draw_main_menu()

    if load_game_menu:
        save_buttons = []
        delete_save_buttons = []
        game_counter = 1
        save_button_y = 50
        for save in get_saves():
            save_buttons.append(Button(612, save_button_y, 400, 90, (255, 255, 255), "Game {}: {}".format(game_counter, save), (0, 0, 0), pixel_font_large))
            delete_save_buttons.append(Button(1032, save_button_y, 275, 90, (255, 50, 50), "Delete Game {}".format(game_counter), (0, 0, 0), pixel_font_large))
            game_counter += 1
            save_button_y += 110

        back_button = Button(1500, 930, 200, 90, (255, 50, 50), "Back", (0, 0, 0), pixel_font_large)

        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

            if event.type == KEYDOWN:
                if event.key == pygame.K_f:
                    screen = pygame.display.set_mode(WINDOW_SIZE, pygame.FULLSCREEN)
                if event.key == pygame.K_ESCAPE:
                    screen = pygame.display.set_mode(WINDOW_SIZE)

            if event.type == MOUSEBUTTONDOWN:
                if event.button == 1:
                    if back_button.is_over():
                        load_game_menu = False
                        main_menu = True
                        select_sound.play()
                    for i, button in enumerate(save_buttons):
                        if button.is_over():
                            save_number = i
                            save_data = get_saves()
                            player.change_level(save_data[save_number])
                            load_game_menu = False
                            game_running = True
                            select_sound.play()
                            play_bgmusic()
                    for i, button in enumerate(delete_save_buttons):
                        if button.is_over():
                            select_sound.play()
                            save_data = get_saves()
                            save_data.pop(i)
                            save_data_string = ''
                            for save in save_data:
                                save_data_string += save + ','

                            with open('saves.txt', 'w') as f:
                                f.write(save_data_string)
                                f.close()

        for button in save_buttons:
            button.update()
        for button in delete_save_buttons:
            button.update()
        back_button.update()
        draw_load_game_menu()

    if escape_menu:
        resume_button = Button(710, 265, 500, 150, (50, 200, 50), "Resume Game", (0, 0, 0), pixel_font_large)
        main_menu_button = Button(710, 465, 500, 150, (75, 160, 173), "Go to Main Menu", (0, 0, 0), pixel_font_large)
        exit_fullscreen_button = Button(710, 665, 500, 150, (255, 50, 50), "Exit Fullscreen", (0, 0, 0), pixel_font_large)

        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

            if event.type == KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    escape_menu = False
                if event.key == pygame.K_f:
                    screen = pygame.display.set_mode(WINDOW_SIZE, FULLSCREEN)

            if event.type == MOUSEBUTTONDOWN:
                if event.button == 1:
                    if resume_button.is_over():
                        escape_menu = False
                        select_sound.play()
                    if main_menu_button.is_over():
                        escape_menu = False
                        game_running = False
                        main_menu = True
                        select_sound.play()
                        play_menu_music()
                    if exit_fullscreen_button.is_over():
                        screen = pygame.display.set_mode(WINDOW_SIZE)
                        select_sound.play()

    if win_screen:
        win_main_menu_button = Button(560, 550, 800, 200, (75, 173, 89), "Return to Main Menu", (0, 0, 0), pixel_font_large)
        win_exit_button = Button(560, 800, 800, 200, (255, 50, 50), "Exit to desktop", (0, 0, 0), pixel_font_large)

        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

            if event.type == KEYDOWN:
                if event.key == pygame.K_f:
                    screen = pygame.display.set_mode(WINDOW_SIZE, pygame.FULLSCREEN)
                if event.key == pygame.K_ESCAPE:
                    screen = pygame.display.set_mode(WINDOW_SIZE)

            if event.type == MOUSEBUTTONDOWN:
                if event.button == 1:
                    if win_main_menu_button.is_over():
                        win_screen = False
                        main_menu = True
                        select_sound.play()
                        play_menu_music()
                    if win_exit_button.is_over():
                        select_sound.play()
                        pygame.quit()
                        sys.exit()

        win_main_menu_button.update()
        win_exit_button.update()
        draw_win_screen()

    levels[player.level].create_map_hitbox()
    if game_running:
        levels[player.level].timer += round((FPS/clock.get_fps())/100, 2)

        scroll[0] += int((player.rect.x - scroll[0] - (WINDOW_SIZE[0]/2 + player.width/2))/18) * dt
        scroll[1] += int((player.rect.y - scroll[1] - (WINDOW_SIZE[1]/2 + player.height/2))/18) * dt

    # Events
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

            if event.type == MOUSEBUTTONDOWN:
                if event.button == 1 and len(bullets) <= 20:
                    mx, my = event.pos
                    slopex = mx - (player.rect.centerx - scroll[0] + 5)
                    slopey = my - (player.rect.centery - scroll[1] + 35)
                    bullets.append(Projectile(player.rect.centerx + 5, player.rect.centery + 35, 10, 14, 15, math.atan2(slopey, slopex), projectile_img))
                    shoot_sound.play()

            if event.type == KEYDOWN:
                if event.key == pygame.K_d:
                    player.moving_right = True
                if event.key == pygame.K_a:
                    player.moving_left = True
                if event.key == pygame.K_LSHIFT:
                    player.sprinting = True
                if event.key == pygame.K_SPACE or event.key == pygame.K_w:
                    if player.times_jumped < 2:
                        player.jumping = True
                if event.key == pygame.K_f:
                    screen = pygame.display.set_mode(WINDOW_SIZE, pygame.FULLSCREEN)
                if event.key == pygame.K_ESCAPE:
                    escape_menu = True

            if event.type == KEYUP:
                if event.key == pygame.K_d:
                    player.moving_right = False
                if event.key == pygame.K_a:
                    player.moving_left = False
                if event.key == pygame.K_LSHIFT:
                    player.sprinting = False
                    player.vel = 10

    # die conditions
        if player.rect.y >= levels[player.level].die_height:
            player.die()
        if player.health <= 0:
            player.die()

    # level-change conditions
        if player.level == 'Tutorial':
            if enemies == []:
                fade_out = True
                pygame.mixer.music.fadeout(1000)
                if fade_alpha >= 300:
                    fade_in = True
                    play_bgmusic()
                    player.change_level('Level 1')
        elif player.level == 'Level 1':
            if enemies == []:
                fade_out = True
                pygame.mixer.music.fadeout(1000)
                if fade_alpha >= 300:
                    fade_in = True
                    play_bgmusic()
                    player.change_level('Level 2')
        elif player.level == 'Level 2':
            if enemies == []:
                fade_out = True
                pygame.mixer.music.fadeout(1000)
                if fade_alpha >= 300:
                    fade_in = True
                    play_bgmusic()
                    player.change_level('Level 3')
        elif player.level == 'Level 3':
            if enemies == []:
                fade_out = True
                pygame.mixer.music.fadeout(1000)
                if fade_alpha >= 300:
                    fade_in = True
                    play_bgmusic()
                    player.change_level('Level 4')
        elif player.level == 'Level 4':
            if enemies == []:
                fade_out = True
                pygame.mixer.music.fadeout(1000)
                if fade_alpha >= 300:
                    fade_in = True
                    play_bgmusic()
                    player.change_level('Level 5')
        elif player.level == 'Level 5':
            if enemies == []:
                pygame.mixer.music.fadeout(1000)
                game_running = False
                win_screen = True
                play_win_music()

    # player bullets
        for bullet in bullets:
            if len(bullets) <= 20:
                bullet.update()
                for enemy in enemies:
                    if bullet.rect.colliderect(enemy.rect):
                        if bullet in bullets:
                            bullets.remove(bullet)
                        enemy.health -= bullet.damage
                        enemy_hit_sound.play()
                        for i in range(8):
                            particles.append(Particle(bullet.x, bullet.y, [(50, 50, 50), (180, 30, 30), (180, 30, 30), (150, 150, 150), (100, 0, 0)], -25, 25, -50, 0, 3, 10, 0.4, 0.2))
                if bullet.collision_types['top'] or bullet.collision_types['bottom'] or bullet.collision_types['right'] or bullet.collision_types['left']:
                    if bullet in bullets:
                        bullets.remove(bullet)
                    explosion_sound.play()
                    if bullet.collision_types['top']:
                        for i in range(20):
                            particles.append(Particle(bullet.x, bullet.y, [(140, 140, 140), (200, 200, 200), (100, 100, 100), (123, 54, 0)], -25, 25, 10, 60, 4, 15, 0.4, 0.2))
                    if bullet.collision_types['bottom']:
                        for i in range(20):
                            particles.append(Particle(bullet.x, bullet.y, [(140, 140, 140), (200, 200, 200), (100, 100, 100), (123, 54, 0)], -25, 25, -60, -10, 4, 15, 0.4, 0.2))
                    if bullet.collision_types['right']:
                        for i in range(20):
                            particles.append(Particle(bullet.x, bullet.y, [(140, 140, 140), (200, 200, 200), (100, 100, 100), (123, 54, 0)], -60, -10, -25, 25, 4, 15, 0.4, 0.2))
                    if bullet.collision_types['left']:
                        for i in range(20):
                            particles.append(Particle(bullet.x, bullet.y, [(140, 140, 140), (200, 200, 200), (100, 100, 100), (123, 54, 0)], 10, 60, -25, 25, 4, 15, 0.4, 0.2))
            else:
                bullets.remove(bullet)

    # enemy bullets
        for bullet in enemy_bullets:
            if len(enemy_bullets) <= 40:
                bullet.update()
                if bullet.rect.colliderect(player.rect):
                    if bullet in enemy_bullets:
                        enemy_bullets.remove(bullet)
                    player.health -= bullet.damage
                    player_hit_sound.play()
                    for i in range(20):
                        particles.append(Particle(bullet.x, bullet.y, [(50, 50, 50), (180, 30, 30), (150, 150, 150), (94, 49, 91)], -25, 25, -50, 0, 4, 15, 0.4, 0.2))
                if bullet.collision_types['top'] or bullet.collision_types['bottom'] or bullet.collision_types['right'] or bullet.collision_types['left'] and not bullet.rect.colliderect(player.rect):
                    if bullet in enemy_bullets:
                        enemy_bullets.remove(bullet)
                    explosion_sound.play()
                    if bullet.collision_types['top']:
                        for i in range(23):
                            particles.append(Particle(bullet.x, bullet.y, [(140, 140, 140), (100, 100, 100), (150, 54, 54)], -25, 25, 10, 60, 4, 15, 0.4, 0.2))
                    if bullet.collision_types['bottom']:
                        for i in range(23):
                            particles.append(Particle(bullet.x, bullet.y, [(140, 140, 140), (100, 100, 100), (150, 54, 54)], -25, 25, -60, -10, 4, 15, 0.4, 0.2))
                    if bullet.collision_types['right']:
                        for i in range(23):
                            particles.append(Particle(bullet.x, bullet.y, [(140, 140, 140), (100, 100, 100), (150, 54, 54)], -60, -10, -25, 25, 4, 15, 0.4, 0.2))
                    if bullet.collision_types['left']:
                        for i in range(23):
                            particles.append(Particle(bullet.x, bullet.y, [(140, 140, 140), (100, 100, 100), (150, 54, 54)], 10, 60, -25, 25, 4, 15, 0.4, 0.2))
            else:
                enemy_bullets.remove(bullet)

        if player.moving_right and player.collision_types['bottom']:
            particles.append(Particle(player.rect.midbottom[0], player.rect.midbottom[1], [(199, 222, 90), (180, 207, 42), (110, 162, 38), (144, 88, 51), (123, 71, 32), (98, 57, 27)], -50, 0, 0, 5, 2, 8, 0.4, 0.2))
        if player.moving_left and player.collision_types['bottom']:
            particles.append(Particle(player.rect.midbottom[0], player.rect.midbottom[1], [(199, 222, 90), (180, 207, 42), (110, 162, 38), (144, 88, 51), (123, 71, 32), (98, 57, 27)], 0, 50, 0, 5, 2, 8, 0.4, 0.2))

        for particle in particles:
            particle.update()
            if particle.radius <= 0:
                particles.remove(particle)

    # enemy die conditions
        for enemy in enemies:
            if enemy.health <= 0 or enemy.rect.y >= levels[player.level].die_height:
                enemies.remove(enemy)
                enemy_death_sound.play()
                for i in range(50):
                    particles.append(Particle(enemy.rect.centerx, enemy.rect.centery, [(140, 140, 140), (255, 50, 50), (255, 50, 50), (255, 50, 50), (50, 50, 50), (100, 0, 0)], -40, 40, -80, 0, 4, 15, 0.4, 0.2))
            else:
                enemy.update()

        player.update()
        gun.update()
        draw()
000Nobody commented 3 years ago

This likely has to do with the dimensions of your monitor and the aspect ratio. What are your monitors dimensions?