Skip to main content
added 62 characters in body
Source Link
deleted 21 characters in body
Source Link
mdfst13
  • 22.4k
  • 6
  • 34
  • 70

thisThis is my first big project that I started after completing the Pygame Shmup guide put together by KidsCanCode. I'm starting to feel like it's very close to being finished so wanted to post my code here to find out what could be improved or what isn't up to standard. Brownie points for whoever can figure out why the alien animation bugs after reinforcements are generated!

Thanks for looking!

this is my first big project that I started after completing the Pygame Shmup guide put together by KidsCanCode. I'm starting to feel like it's very close to being finished so wanted to post my code here to find out what could be improved or what isn't up to standard. Brownie points for whoever can figure out why the alien animation bugs after reinforcements are generated!

Thanks for looking!

This is my first big project that I started after completing the Pygame Shmup guide put together by KidsCanCode. I'm starting to feel like it's very close to being finished so wanted to post my code here to find out what could be improved or what isn't up to standard. Brownie points for whoever can figure out why the alien animation bugs after reinforcements are generated!

Source Link

Invaders clone made with Pygame

this is my first big project that I started after completing the Pygame Shmup guide put together by KidsCanCode. I'm starting to feel like it's very close to being finished so wanted to post my code here to find out what could be improved or what isn't up to standard. Brownie points for whoever can figure out why the alien animation bugs after reinforcements are generated!

Any comments and/or feedback would be greatly appreciated :)

Thanks for looking!

# Space WIP
# By Elias

# things to implement
    # boss introduction cinematic
    # announ. boss sound
    # victory sound

# have aliens jump down vertically rather than diagonally

# known issues:
    # aliens speed up when reinforcements are being dropped
    # boss and bossvessel rect can be coliided with in level 1 despite them not being generated yet
        # temporary fix introduced in the check collisions function not checking collision until level > 9

import math
import pygame
import random
import sys
from os import path

pygame.init()
pygame.mixer.init()

# asset folders
FONT_DIR = path.join(path.dirname(__file__), "fonts")
IMAGE_DIR = path.join(path.dirname(__file__), "images")
SOUND_DIR = path.join(path.dirname(__file__), "sounds")

# screen
WIDTH = 800
HEIGHT = 600
SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption('Space WIP')
pygame.display.set_icon(pygame.image.load(path.join(IMAGE_DIR, "logo.png")).convert_alpha())
FPS = 60

# colours
BLACK = (0, 0, 0)
BLUE = (0, 255, 240)
GREEN = (0, 255, 0)
ORANGE = (255, 173, 0)
PURPLE = (255, 0, 255)
RED = (255, 0, 0)
WHITE = (255, 255, 255)

# fonts
FONT = path.join(FONT_DIR, '8-BitMadness.ttf')
LOGOFONT = path.join(FONT_DIR, "edunline.ttf")
STARTFONT = path.join(FONT_DIR, "upheavtt.ttf")

# images
alien2_1 = pygame.image.load(path.join(IMAGE_DIR, "enemy2_1.png")).convert_alpha()
alien2_2 = pygame.image.load(path.join(IMAGE_DIR, "enemy2_2.png")).convert_alpha()
alien_images = [pygame.transform.scale(alien2_1, (40, 35)), pygame.transform.scale(alien2_2, (40, 35))]

alien3_1 = pygame.image.load(path.join(IMAGE_DIR, "enemy3_1.png")).convert_alpha()
alien3_2 = pygame.image.load(path.join(IMAGE_DIR, "enemy3_2.png")).convert_alpha()
alien_backup_images = [pygame.transform.scale(alien3_1, (40, 35)), pygame.transform.scale(alien3_2, (40, 35))]

alien1_1 = pygame.image.load(path.join(IMAGE_DIR, "enemy1_1.png")).convert_alpha()
alien1_2 = pygame.image.load(path.join(IMAGE_DIR, "enemy1_2.png")).convert_alpha()
alien_elite_images = [pygame.transform.scale(alien1_1, (40, 35)), pygame.transform.scale(alien1_2, (40, 35))]

# music
MUSICVOLUME = 0.5

BACKGROUND1 = path.join(SOUND_DIR, 'Background1.ogg')
BACKGROUND2 = path.join(SOUND_DIR, 'Background2.ogg')

# sounds
SOUNDVOLUME = 0.2

ALIENAPPEAR = pygame.mixer.Sound(path.join(SOUND_DIR, 'AlienAppear.wav'))
ALIENAPPEAR.set_volume(SOUNDVOLUME)

ALIENEXPLOSIONSOUND = pygame.mixer.Sound(path.join(SOUND_DIR, 'AlienExplosion.wav'))
ALIENEXPLOSIONSOUND.set_volume(SOUNDVOLUME)

ALIENMOVESOUND = pygame.mixer.Sound(path.join(SOUND_DIR, 'AlienMove.wav'))
ALIENMOVESOUND.set_volume(SOUNDVOLUME)

GAMEOVER = pygame.mixer.Sound(path.join(SOUND_DIR, 'GameOver.wav'))
GAMEOVER.set_volume(MUSICVOLUME)  # not an error

LASERSOUND = pygame.mixer.Sound(path.join(SOUND_DIR, 'Laser.wav'))
LASERSOUND.set_volume(SOUNDVOLUME)

MYSTERYEXPLOSION = pygame.mixer.Sound(path.join(SOUND_DIR, 'MysteryExplosion.wav'))
MYSTERYEXPLOSION.set_volume(SOUNDVOLUME)

PLAYERHIT = pygame.mixer.Sound(path.join(SOUND_DIR, 'PlayerHit.wav'))
PLAYERHIT.set_volume(SOUNDVOLUME)

POWERUPGENERATED = pygame.mixer.Sound(path.join(SOUND_DIR, 'PowerupGenerated.wav'))
POWERUPGENERATED.set_volume(SOUNDVOLUME)

POWERUPPICKEDUP = pygame.mixer.Sound(path.join(SOUND_DIR, 'PowerupPickedUp.wav'))
POWERUPPICKEDUP.set_volume(SOUNDVOLUME)

SATELLITEANNOUNCE = pygame.mixer.Sound(path.join(SOUND_DIR, 'Satelliteannounce.wav'))
SATELLITEANNOUNCE.set_volume(SOUNDVOLUME)

SATELLITEEXPLOSION = pygame.mixer.Sound(path.join(SOUND_DIR, 'SatelliteExplosion.wav'))
SATELLITEEXPLOSION.set_volume(SOUNDVOLUME)

# sprite groups
alien_group = pygame.sprite.Group()
alien_backup_group = pygame.sprite.Group()
alien_elite_group = pygame.sprite.Group()
alien_master_group = pygame.sprite.Group()
alien_reinforcements_group = pygame.sprite.Group()
alien_laser_group = pygame.sprite.Group()

barrier_master_group = pygame.sprite.Group()
boss_group = pygame.sprite.Group()
bossvessel_group = pygame.sprite.Group()
bossvessel_laser_group = pygame.sprite.Group()

explosion_group = pygame.sprite.Group()

laser_group = pygame.sprite.Group()

mystery_group = pygame.sprite.Group()

player_group = pygame.sprite.Group()

powerup_group = pygame.sprite.Group()

satellite_group = pygame.sprite.Group()
star_group = pygame.sprite.Group()

# miscellaneous

should_aliens_drop = False
should_aliens_move = False

ALIENBACKUPSCORE = 20
ALIENDROP = 20
ALIENELITESCORE = 30
ALIENSCORE = 10
ALIENSPEED = 10
ALIENSTARTYPOS = 65

BOSSVESSELDISTANCE = 90
BOSSVESSELMAXHEALTH = 100

LASERSPEED = 5

MYSTERYSCORE = 100
MYSTERYSPEED = 5
MYSTERYTIME = 10
MYSTERYXPOS = -75

PLAYERSPEED = 7

POWERUPSPEED = 2

SATELLITESPEED = -5
SATELLITETIME = 3
SATELLITEXPOS = WIDTH + 31

STARSPEED = 1

time_last_hit = 0


class Player(pygame.sprite.Sprite):
    # the text in between brackets above ensure my class Player inherits from
    # the pygame Sprite class
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(path.join(IMAGE_DIR, "ship.png")).convert()
        self.image = pygame.transform.scale(self.image, (45, 45))
        self.rect = self.image.get_rect(topleft=(300, 540))
        self.speed = PLAYERSPEED
        self.lives = 5
        self.score = 0
        self.last_shot = pygame.time.get_ticks() / 1000
        self.cool_down = 0.5
        self.radius = 21
        self.double_shot = False

    def update(self, keys):
        if keys[pygame.K_LEFT] and self.rect.left >= 0:
            self.rect.x -= self.speed
        if keys[pygame.K_RIGHT] and self.rect.right <= WIDTH:
            self.rect.x += self.speed


class Alien(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.index = 0
        self.image = alien_images[self.index]
        self.rect = self.image.get_rect()
        self.alien_last_moved = 0
        self.speed = ALIENSPEED

    def update(self, now):

        global should_aliens_drop
        global should_aliens_move

        if now - self.alien_last_moved >= 0.5:
            self.alien_last_moved = now

            if self.rect.right + self.speed >= (WIDTH - 10) or self.rect.left + self.speed <= 10:
                should_aliens_drop = True
            else:
                should_aliens_move = True


class AlienBackup(Alien):
    def __init__(self):
        super().__init__()
        self.image = alien_backup_images[Alien().index]


class AlienElite(Alien):
    def __init__(self):
        super().__init__()
        self.image = alien_elite_images[Alien().index]


class Barrier(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((5, 5))
        self.image.fill(GREEN)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y

    def update(self, keys, *args):
        pass


class Boss(Alien):
    def __init__(self):
        super().__init__()
        self.index = 0
        self.image = alien_images[self.index]
        self.rect = self.image.get_rect()
        self.alien_last_moved = 0
        self.speed = ALIENSPEED

    def update(self, now, *args):

        global should_aliens_drop
        global should_aliens_move

        if now - self.alien_last_moved >= 0.5:
            self.alien_last_moved = now

            if self.rect.right + (bossvessel.width / 2) + self.speed >= (WIDTH - 10) \
                    or self.rect.left - (bossvessel.width / 2) + self.speed <= 10:
                should_aliens_drop = True
            else:
                should_aliens_move = True


class Bossvessel(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.width = 220
        self.height = 200
        self.image = pygame.image.load(path.join(IMAGE_DIR, "boss.png")).convert_alpha()
        self.image = pygame.transform.scale(self.image, (self.width, self.height))  # 11, 10
        self.rect = self.image.get_rect()
        self.health = BOSSVESSELMAXHEALTH

    def update(self):
        self.rect.centerx = boss.rect.centerx
        self.rect.centery = boss.rect.centery + BOSSVESSELDISTANCE


class ExplosionBlue(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(path.join(IMAGE_DIR, "explosionblue.png")).convert_alpha()
        self.image = pygame.transform.scale(self.image, (45, 45))
        self.rect = self.image.get_rect()
        self.rect.x = x - 5
        self.rect.y = y - 7

        self.timer = pygame.time.get_ticks()

    def update(self, current_time):
        passed = (current_time * 1000) - self.timer
        if passed >= 200:
            self.kill()


class ExplosionGreen(ExplosionBlue):
    def __init__(self, x, y):
        super().__init__(x, y)
        self.image = pygame.image.load(path.join(IMAGE_DIR, "explosiongreen.png")).convert_alpha()
        self.image = pygame.transform.scale(self.image, (45, 45))


class ExplosionPurple(ExplosionBlue):
    def __init__(self, x, y):
        super().__init__(x, y)
        self.image = pygame.image.load(path.join(IMAGE_DIR, "explosionpurple.png")).convert_alpha()
        self.image = pygame.transform.scale(self.image, (45, 45))


class ExplosionRed(ExplosionBlue):
    def __init__(self, x, y):
        super().__init__(x, y)
        self.image = pygame.image.load(path.join(IMAGE_DIR, "explosionred.png")).convert_alpha()
        self.image = pygame.transform.scale(self.image, (45, 45))


class Laser(pygame.sprite.Sprite):
    def __init__(self, x, y, speed, colour):
        pygame.sprite.Sprite.__init__(self)
        self.width = 2
        self.height = 4
        self.image = pygame.Surface((self.width, self.height))
        self.colour = colour
        self.image.fill(self.colour)
        self.rect = self.image.get_rect()
        self.speed = speed
        self.rect.centerx = x
        self.rect.centery = y

    def update(self):
        self.rect.y -= self.speed
        if self.rect.y + (self.height / 2) <= 0 or self.rect.y - (self.height / 2) >= HEIGHT:
            self.kill()


class Mystery(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.width = 75
        self.height = 35
        self.image = pygame.image.load(path.join(IMAGE_DIR, "mystery.png")).convert_alpha()
        self.image = pygame.transform.scale(self.image, (self.width, self.height))
        self.rect = self.image.get_rect()
        self.speed = MYSTERYSPEED
        self.last_appeared = 0
        self.last_stopped = 0
        self.rect.x = MYSTERYXPOS
        self.rect.y = 25

    def update(self, level, levelstarttime, now):
        if len(mystery_group) == 1:
            self.rect.x += self.speed
            # Past lvl 3 when hit after it never returns due to speed never being reset to 5
            if not ((WIDTH / 2) - 2 <= self.rect.centerx <= (WIDTH / 2) + 2):
                self.speed = MYSTERYSPEED

        if self.rect.x >= WIDTH:
            self.last_stopped = 0
            self.kill()

        if 11 > level >= 6 and 60 >= (now - levelstarttime) >= 30:
            if self.speed == 0 and (now - self.last_stopped) >= 3 and len(mystery_group) == 1 \
                    and (WIDTH / 2) - 2 <= self.rect.centerx <= (WIDTH / 2) + 2:
                generate_alien_reinforcements()
                ALIENAPPEAR.play()
                self.rect.centerx = (WIDTH / 2) + 3
                self.speed = MYSTERYSPEED
                self.last_appeared = now
                self.rect.x = (WIDTH / 2) + 6
            if self.speed != 0 and (WIDTH / 2) - 2 <= self.rect.centerx <= (WIDTH / 2) + 2:
                self.speed = 0
                self.last_stopped = now


class Powerup(pygame.sprite.Sprite):
    def __init__(self, x, y, colour):
        pygame.sprite.Sprite.__init__(self)
        self.width = 4
        self.height = 20
        self.image = pygame.Surface((self.width, self.height))
        self.colour = colour
        self.image.fill(self.colour)
        self.rect = self.image.get_rect()
        self.speed = POWERUPSPEED
        self.generated_this_level = False
        self.rect.centerx = x
        self.rect.y = y

    def update(self):
        self.rect.y += self.speed

        if self.rect.top >= HEIGHT:
            self.kill()


class Satellite(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(path.join(IMAGE_DIR, "satellite.png")).convert_alpha()
        self.image = pygame.transform.scale(self.image, (106, 40))
        self.rect = self.image.get_rect()
        self.speed = SATELLITESPEED
        self.rect.x = SATELLITEXPOS
        self.rect.y = 25
        self.powerup_generated = False
        self.last_stopped = 0
        self.stopped_this_level = False

    def update(self, now):
        self.rect.x += self.speed

        if self.rect.right <= 0:
            self.kill()

        if len(satellite_group) > 0 and self.rect.right <= WIDTH and self.rect.left >= 0 \
                and not self.stopped_this_level:
            if random.randint(1, 200) == 1:
                self.last_stopped = pygame.time.get_ticks() / 1000
                self.speed = 0
                self.stopped_this_level = True

        if 2 > now - self.last_stopped > 1 and len(powerup_group) == 0:
            generate_powerup()
            POWERUPGENERATED.play()
            self.powerup_generated = True

        if now - self.last_stopped >= 3:
            self.speed = SATELLITESPEED


class Star(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([1, 1])
        self.image.fill(WHITE)
        self.rect = self.image.get_rect()
        self.speed = STARSPEED

    def update(self):
        self.rect.y += self.speed
        if self.rect.y >= HEIGHT:
            self.rect.y = 0


# functions
def alien_drop():
    for alien in alien_master_group:
        alien.rect.y += ALIENDROP
        alien.speed *= -1


def alien_move():
    if len(boss_group) == 0:
        for alien in alien_master_group:
            alien.rect.x += alien.speed
        for alien in alien_group:
            alien.index += 1
            if alien.index >= 2:
                alien.index = 0
            alien.image = alien_images[alien.index]
        for alien in alien_backup_group:
            alien.index += 1
            if alien.index >= 2:
                alien.index = 0
            alien.image = alien_backup_images[alien.index]
        for alien in alien_elite_group:
            alien.index += 1
            if alien.index >= 2:
                alien.index = 0
            alien.image = alien_elite_images[alien.index]
    else:
        for alien in alien_master_group:
            alien.rect.x += alien.speed
        for alien in alien_group:
            alien.index += 1
            if alien.index >= 2:
                alien.index = 0
            alien.image = alien_images[alien.index]


def alien_shoot():
    if len(alien_laser_group) <= math.trunc(len(alien_group) / 10) and len(alien_group) > 0:
        alien = random.choice(list(alien_group))
        laser = Laser(alien.rect.centerx, alien.rect.centery, -LASERSPEED, BLUE)
        alien_laser_group.add(laser)

    if len(alien_laser_group) <= math.trunc(len(alien_backup_group) / 5) and len(alien_backup_group) > 0:
        alien = random.choice(list(alien_backup_group))
        laser = Laser(alien.rect.centerx, alien.rect.centery, -LASERSPEED, GREEN)
        alien_laser_group.add(laser)

    if len(alien_laser_group) <= math.trunc(len(alien_elite_group) / 2) and len(alien_elite_group) > 0:
        alien = random.choice(list(alien_elite_group))
        laser = Laser(alien.rect.centerx, alien.rect.centery, -LASERSPEED, PURPLE)
        alien_laser_group.add(laser)

    if len(alien_laser_group) <= math.trunc(len(alien_reinforcements_group) / 2) and len(
            alien_reinforcements_group) > 0:
        alien = random.choice(list(alien_reinforcements_group))
        laser = Laser(alien.rect.centerx, alien.rect.centery, -LASERSPEED, PURPLE)
        alien_laser_group.add(laser)


def bossvessel_shoot():
    if len(bossvessel_group) > 0 and bossvessel.health > 0 and len(bossvessel_laser_group) < random.randint(0, 9):
        laser = Laser(bossvessel.rect.left + (random.randint(0, 6) * 35), bossvessel.rect.y + bossvessel.height,
                      -LASERSPEED, RED)
        bossvessel_laser_group.add(laser)


def check_collisions(now, bossgrouplen):
    global time_last_hit

    for alien in alien_master_group:
        for barrier in barrier_master_group:
            if alien.rect.colliderect(barrier.rect):
                barrier.kill()

        if alien.rect.colliderect(player.rect):
            explosion_group.add(ExplosionGreen(alien.rect.x, alien.rect.y))
            explosion_group.add(ExplosionGreen(player.rect.x, player.rect.y))
            player.lives = 0
            alien.kill()
            ALIENEXPLOSIONSOUND.play()
            player.kill()

    pygame.sprite.groupcollide(alien_laser_group, barrier_master_group, True, True)
    pygame.sprite.groupcollide(bossvessel_laser_group, barrier_master_group, True, True)
    pygame.sprite.groupcollide(laser_group, barrier_master_group, True, True)

    for laser in laser_group:
        for alien in alien_group:
            if laser.rect.colliderect(alien.rect):
                explosion_group.add(ExplosionBlue(alien.rect.x, alien.rect.y))
                player.score += ALIENSCORE
                laser.kill()
                alien.kill()
                ALIENEXPLOSIONSOUND.play()
                time_last_hit = now

        for alien in alien_backup_group:
            if laser.rect.colliderect(alien.rect):
                explosion_group.add(ExplosionGreen(alien.rect.x, alien.rect.y))
                player.score += ALIENBACKUPSCORE
                laser.kill()
                alien.kill()
                ALIENEXPLOSIONSOUND.play()
                time_last_hit = now

        for alien in alien_elite_group:
            if laser.rect.colliderect(alien.rect):
                explosion_group.add(ExplosionPurple(alien.rect.x, alien.rect.y))
                player.score += ALIENELITESCORE
                laser.kill()
                alien.kill()
                ALIENEXPLOSIONSOUND.play()
                time_last_hit = now

        for alien in alien_reinforcements_group:
            if laser.rect.colliderect(alien.rect):
                explosion_group.add(ExplosionPurple(alien.rect.x, alien.rect.y))
                player.score += ALIENELITESCORE
                laser.kill()
                alien.kill()
                ALIENEXPLOSIONSOUND.play()
                time_last_hit = now

        if bossgrouplen > 0:
            if laser.rect.colliderect(boss.rect):
                laser.kill()
                time_last_hit = now

            if laser.rect.colliderect(bossvessel.rect):
                bossvessel.health -= 1
                if bossvessel.health <= 0:
                    explosion_group.add(ExplosionGreen(boss.rect.centerx, boss.rect.y))
                    boss.kill()
                    explosion_group.add(ExplosionRed(bossvessel.rect.centerx, bossvessel.rect.y))
                    bossvessel.kill()
                laser.kill()
                time_last_hit = now

        if laser.rect.colliderect(mystery.rect):
            explosion_group.add(ExplosionRed(mystery.rect.centerx, mystery.rect.y))
            player.score += MYSTERYSCORE
            mystery.rect.x = MYSTERYXPOS
            laser.kill()
            mystery.kill()
            MYSTERYEXPLOSION.play()
            time_last_hit = now

        if laser.rect.colliderect(satellite.rect):
            explosion_group.add(ExplosionBlue(satellite.rect.centerx, satellite.rect.y))
            satellite.rect.x = SATELLITEXPOS
            laser.kill()
            satellite.kill()
            SATELLITEEXPLOSION.play()
            time_last_hit = now

    for laser in alien_laser_group:
        hits = pygame.sprite.spritecollide(laser, player_group, False, pygame.sprite.collide_circle)
        for hit in hits:
            player_hit()
            laser.kill()
            time_last_hit = now
            if player.lives == 0:
                explosion_group.add(ExplosionGreen(player.rect.x, player.rect.y))
                player.kill()

    for laser in bossvessel_laser_group:
        hits = pygame.sprite.spritecollide(laser, player_group, False, pygame.sprite.collide_circle)
        for hit in hits:
            player_hit()
            laser.kill()
            time_last_hit = now
            if player.lives == 0:
                explosion_group.add(ExplosionGreen(player.rect.x, player.rect.y))
                player.kill()

    for powerup in powerup_group:
        if powerup.rect.colliderect(player.rect):
            powerup.kill()
            POWERUPPICKEDUP.play()
            if player.lives < 5:
                randnumber = random.randint(1, 3)
                if randnumber == 1:
                    player.double_shot = True
                elif randnumber == 2:
                    num = random.randint(0, 3)
                    generate_barrier(num, num + 1)
                else:
                    player.lives += 1
            elif player.lives == 5:
                randnumber = random.randint(1, 2)
                if randnumber == 1:
                    player.double_shot = True
                else:
                    num = random.randint(0, 3)
                    generate_barrier(num, num + 1)


def draw_boss_health_bar(screen):
    pygame.draw.rect(screen, RED, pygame.Rect(5, HEIGHT - 10, WIDTH - 10, 10), 2)

    pygame.draw.rect(screen, RED,
                     pygame.Rect(5, HEIGHT - 10, (WIDTH - 10) * (bossvessel.health / BOSSVESSELMAXHEALTH), 10))


def draw_lives(surf, x, y, lives, img):
    for i in range(lives):
        img_rect = img.get_rect()
        img_rect.x = x + 30 * i
        img_rect.y = y
        surf.blit(img, img_rect)


def draw_text(surf, font, size, text, colour, x, y, location):
    font = pygame.font.Font(font, size)
    text_surface = font.render(text, True, colour)
    text_rect = text_surface.get_rect()
    if location == 'center':
        text_rect.center = (x, y)
    elif location == 'left':
        text_rect.midleft = (x, y)
    surf.blit(text_surface, text_rect)


def generate_barrier(num1, num2):
    for i in range(num1, num2):
        for x in range(20):
            for y in range(10):
                barrier = Barrier(50 + (x * 5) + (200 * i), 525 - (y * 5))
                barrier_master_group.add(barrier)


def generate_aliens_lvl1():
    for x in range(10):
        for y in range(2):
            alien = Alien()
            alien.rect.x = 50 + (x * 50)
            alien.rect.y = ALIENSTARTYPOS + (y * 45)
            alien_group.add(alien)
            alien_master_group.add(alien)


def generate_aliens_lvl2():
    for x in range(10):
        for y in range(4):
            if y in (0, 1):
                alien = AlienBackup()
                alien.rect.x = 50 + (x * 50)
                alien.rect.y = ALIENSTARTYPOS + (y * 45)
                alien_backup_group.add(alien)
                alien_master_group.add(alien)
            elif y in (2, 3):
                alien = Alien()
                alien.rect.x = 50 + (x * 50)
                alien.rect.y = ALIENSTARTYPOS + (y * 45)
                alien_group.add(alien)
                alien_master_group.add(alien)


def generate_aliens_lvl3():
    for x in range(10):
        for y in range(5):
            if y == 0:
                alien = AlienElite()
                alien.rect.x = 50 + (x * 50)
                alien.rect.y = ALIENSTARTYPOS + (y * 45)
                alien_elite_group.add(alien)
                alien_master_group.add(alien)
            elif y in (1, 2):
                alien = AlienBackup()
                alien.rect.x = 50 + (x * 50)
                alien.rect.y = ALIENSTARTYPOS + (y * 45)
                alien_backup_group.add(alien)
                alien_master_group.add(alien)
            elif y in (3, 4):
                alien = Alien()
                alien.rect.x = 50 + (x * 50)
                alien.rect.y = ALIENSTARTYPOS + (y * 45)
                alien_group.add(alien)
                alien_master_group.add(alien)


def generate_alien_reinforcements():
    for x in range(5):
        alien = AlienElite()
        alien.rect.x = ((WIDTH / 2) - 120) + (x * 50)
        alien.rect.y = ALIENSTARTYPOS
        alien_reinforcements_group.add(alien)
        alien_master_group.add(alien)


def generate_boss():
    boss.rect.centerx = WIDTH / 2
    boss.rect.y = ALIENSTARTYPOS
    alien_group.add(boss)
    alien_master_group.add(boss)
    boss_group.add(boss)


def generate_boss_vessel():
    bossvessel.rect.centerx = boss.rect.centerx
    bossvessel.rect.centery = boss.rect.centery + BOSSVESSELDISTANCE
    bossvessel_group.add(bossvessel)


def generate_mystery():
    mystery.rect.x = MYSTERYXPOS
    mystery_group.add(mystery)
    mystery.last_appeared = pygame.time.get_ticks() / 1000


def generate_powerup():
    powerup = Powerup(satellite.rect.centerx, satellite.rect.centery, ORANGE)
    if powerup.generated_this_level is False:
        powerup.generated_this_level = True
        powerup_group.add(powerup)


def generate_satellite():
    satellite.rect.x = SATELLITEXPOS
    satellite_group.add(satellite)


def generate_stars():
    for i in range(random.randint(80, 120)):
        x = random.randint(1, WIDTH - 1)
        y = random.randint(1, HEIGHT - 1)
        star = Star()
        star.rect.x = x
        star.rect.y = y

        star_group.add(star)


def intermission_screen(screen, image_name, image_scale_x, image_scale_y, screen_pos_x, screen_pos_y,
                        text, text_pos_x, text_pos_y, text_loc):
    load_screen_image = pygame.image.load(path.join(IMAGE_DIR, image_name)).convert_alpha()
    image = pygame.transform.scale(load_screen_image, (image_scale_x, image_scale_y))
    screen.blit(image, (screen_pos_x, screen_pos_y))
    draw_text(screen, FONT, 52, text, WHITE, text_pos_x, text_pos_y, text_loc)


def pauze_background_music():
    pygame.mixer.music.pause()


def play_background_music():
    if random.randint(1, 2) == 1:
        pygame.mixer.music.load(BACKGROUND1)
    else:
        pygame.mixer.music.load(BACKGROUND2)
    pygame.mixer.music.set_volume(MUSICVOLUME)
    pygame.mixer.music.play(-1)


def player_hit():
    player.lives -= 1
    PLAYERHIT.play()


def unpauze_background_music():
    pygame.mixer.music.unpause()


# miscellaneous
player = Player()
player_group.add(player)

boss = Boss()
bossvessel = Bossvessel()
mystery = Mystery()
satellite = Satellite()


class SpaceInvaders(object):
    def __init__(self):
        pygame.init()
        self.clock = pygame.time.Clock()
        self.fps = self.clock.get_fps()
        self.screen = SCREEN
        self.now = pygame.time.get_ticks() / 1000
        self.levelstarttime = 0
        self.level = 1

        self.menu()

    def game_over(self, endgame):
        pygame.mixer.music.stop()
        if endgame == 0:
            endgametext = "GAME OVER"
            endgamecolour = RED
        else:
            endgametext = "VICTORY"
            endgamecolour = GREEN

        pygame.time.wait(2000)
        draw_text(self.screen, FONT, 64, endgametext, endgamecolour, WIDTH / 2, HEIGHT / 2, 'center')
        GAMEOVER.play()
        pygame.display.update()

        pygame.time.wait(2000)
        draw_text(self.screen, FONT, 32, "Press space bar to restart", WHITE, WIDTH / 2, (HEIGHT / 2) + 50, 'center')
        draw_text(self.screen, FONT, 32, "or press escape to quit", WHITE, WIDTH / 2, (HEIGHT / 2) + 100, 'center')
        pygame.display.update()

        pygame.time.wait(2000)
        SCREEN.fill(BLACK)
        draw_text(self.screen, FONT, 64, endgametext, endgamecolour, WIDTH / 2, HEIGHT / 2, 'center')
        draw_text(self.screen, FONT, 32, "Press space bar to restart", WHITE, WIDTH / 2, (HEIGHT / 2) + 50, 'center')
        draw_text(self.screen, FONT, 32, "or press escape to quit", WHITE, WIDTH / 2, (HEIGHT / 2) + 100, 'center')
        pygame.display.update()

        while True:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_SPACE:
                        self.level = 1
                        self.new_game()
                    if event.key == pygame.K_ESCAPE:
                        pygame.quit()
                        sys.exit()

    def intermission(self):
        pauze_background_music()
        pygame.time.wait(2000)
        self.screen.fill(BLACK)

        if 11 > self.level >= 1:
            intermission_screen(self.screen, "ship.png", 45, 45, 50, (HEIGHT * 0.25) + 50,
                                '= Player', 100, (HEIGHT * 0.25) + 75, 'left')

            intermission_screen(self.screen, "enemy2_1.png", 45, 45, (WIDTH / 2) + 50, (HEIGHT * 0.25) + 50,
                                '= ' + str(ALIENSCORE) + ' points', (WIDTH / 2) + 100, (HEIGHT * 0.25) + 75, 'left')

        if 11 > self.level >= 2:
            intermission_screen(self.screen, "enemy3_2.png", 45, 45, 50, (HEIGHT * 0.25) + 150,
                                '= ' + str(ALIENBACKUPSCORE) + ' points', 100, (HEIGHT * 0.25) + 175, 'left')

        if 11 > self.level >= 3:
            intermission_screen(self.screen, "enemy1_1.png", 45, 45, (WIDTH / 2) + 50, (HEIGHT * 0.25) + 150
                                , '= ' + str(ALIENELITESCORE) + ' points', (WIDTH / 2) + 100, (HEIGHT * 0.25) + 175, 'left')

        if 11 > self.level >= 4:
            intermission_screen(self.screen, "mystery.png", 75, 35, 35, (HEIGHT * 0.25) + 250,
                                '= ' + str(MYSTERYSCORE) + ' points', 110, (HEIGHT * 0.25) + 275, 'left')

        if 11 > self.level >= 5:
            intermission_screen(self.screen, "satellite.png", 106, 40, (WIDTH / 2) + 20, (HEIGHT * 0.25) + 250
                                , "= don't shoot", (WIDTH / 2) + 125, (HEIGHT * 0.25) + 275, 'left')

            intermission_screen(self.screen, "powerup.png", 8, 40, (WIDTH / 2) + 75, (HEIGHT * 0.25) + 350
                                , "= powerup", (WIDTH / 2) + 100, (HEIGHT * 0.25) + 375, 'left')

        if self.level == 6:
            draw_text(self.screen, FONT, 52, 'Watch out for reinforcements', RED, WIDTH / 2, 50, 'center')

        if self.level <= 10:
            draw_text(self.screen, FONT, 52, 'level ' + str(int(self.level)), GREEN, WIDTH / 2, (HEIGHT * 0.25), 'center')
        elif self.level == 11:
            draw_text(self.screen, FONT, 52, 'FINAL BOSS', RED, WIDTH / 2, (HEIGHT / 2), 'center')

        pygame.display.update()
        pygame.time.wait(3000)
        unpauze_background_music()

    def menu(self):

        generate_stars()

        while True:

            button = pygame.Rect((WIDTH / 2) - 100, (HEIGHT / 2), 200, 100)

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                if event.type == pygame.MOUSEBUTTONDOWN:
                    if event.button == 1:
                        if button.collidepoint(event.pos):
                            pygame.time.wait(2000)
                            self.intermission()
                            self.new_game()
                if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
                    self.intermission()
                    self.new_game()

            keys = pygame.key.get_pressed()

            self.screen.fill(BLACK)

            pygame.draw.rect(self.screen, BLACK, button)

            star_group.update()
            star_group.draw(self.screen)

            draw_text(self.screen, STARTFONT, 64, 'START', WHITE, button.centerx, button.centery, 'center')

            draw_text(self.screen, LOGOFONT, 80, 'SPACE INVADERS', GREEN, WIDTH / 2, (HEIGHT / 2) - 100, 'center')

            draw_text(self.screen, FONT, 25, 'Or press SPACE to start the game', WHITE, WIDTH / 2,
                      (HEIGHT - 100), 'center')

            draw_text(self.screen, FONT, 25, 'Use arrow keys to move and space bar to shoot', WHITE, WIDTH / 2,
                      (HEIGHT - 50), 'center')

            pygame.display.flip()
            self.clock.tick(FPS)

    def new_game(self):
        global alien_group
        alien_group = pygame.sprite.Group()
        global alien_backup_group
        alien_backup_group = pygame.sprite.Group()
        global alien_elite_group
        alien_elite_group = pygame.sprite.Group()
        global alien_master_group
        alien_master_group = pygame.sprite.Group()
        global alien_reinforcements_group
        alien_reinforcements_group = pygame.sprite.Group()
        global alien_laser_group
        alien_laser_group = pygame.sprite.Group()
        global boss_group
        boss_group = pygame.sprite.Group()
        global bossvessel_group
        bossvessel_group = pygame.sprite.Group()
        global explosion_group
        explosion_group = pygame.sprite.Group()
        global laser_group
        laser_group = pygame.sprite.Group()
        global mystery_group
        mystery_group = pygame.sprite.Group()
        global powerup_group
        powerup_group = pygame.sprite.Group()
        global satellite_group
        satellite_group = pygame.sprite.Group()

        if self.level == 1:
            global barrier_master_group
            barrier_master_group = pygame.sprite.Group()
            global player_group
            player_group = pygame.sprite.Group()
            global star_group
            star_group = pygame.sprite.Group()
            global player
            player = Player()
            player_group.add(player)
            player.lives = 5
            player.score = 0

            generate_aliens_lvl1()

        elif self.level == 2:
            generate_aliens_lvl2()
        elif 11 > self.level >= 3:
            generate_aliens_lvl3()

        if self.level <= 3:
            generate_barrier(0, 4)

        if self.level == 11:
            generate_boss()
            generate_boss_vessel()

        generate_stars()

        play_background_music()

        mystery.last_appeared = pygame.time.get_ticks() / 1000
        satellite.appeared_this_level = False

        self.game_loop()

    def game_start(self):
        self.menu()

    def game_loop(self):
        while True:

            self.now = (pygame.time.get_ticks() / 1000)

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                elif event.type == pygame.K_SPACE and player.lives == 0:
                    SpaceInvaders()

            global should_aliens_drop
            should_aliens_drop = False

            global should_aliens_move
            should_aliens_move = False

            keys = pygame.key.get_pressed()

            if keys[pygame.K_SPACE]:
                if player.lives > 0:
                    if self.now - player.last_shot >= player.cool_down and not player.double_shot:
                        player.last_shot = self.now
                        laser = Laser(player.rect.centerx, player.rect.y, LASERSPEED, GREEN)
                        laser_group.add(laser)
                        LASERSOUND.play()
                    if self.now - player.last_shot >= player.cool_down and player.double_shot:
                        player.last_shot = self.now
                        laser = Laser(player.rect.centerx - 15, player.rect.y + 20, LASERSPEED, GREEN)
                        laser_group.add(laser)
                        laser = Laser(player.rect.centerx + 15, player.rect.y + 20, LASERSPEED, GREEN)
                        laser_group.add(laser)
                        LASERSOUND.play(1)

            self.screen.fill(BLACK)

            star_group.update()
            star_group.draw(self.screen)

            alien_master_group.update(self.now)
            alien_master_group.draw(self.screen)

            if should_aliens_drop:
                alien_drop()
                should_aliens_drop = False

            if should_aliens_move:
                alien_move()
                should_aliens_move = False

            alien_laser_group.update()
            alien_laser_group.draw(self.screen)

            barrier_master_group.update(keys)
            barrier_master_group.draw(self.screen)

            bossvessel_group.update(keys)
            bossvessel_group.draw(self.screen)

            bossvessel_laser_group.update(keys)
            bossvessel_laser_group.draw(self.screen)

            if self.level >= 10 and len(boss_group) == 1:
                draw_boss_health_bar(self.screen)

            explosion_group.update(self.now)
            explosion_group.draw(self.screen)

            laser_group.update()
            laser_group.draw(self.screen)

            mystery_group.update(self.level, self.levelstarttime, self.now)
            mystery_group.draw(self.screen)

            if self.level >= 4 and len(mystery_group) == 0 and (self.now - mystery.last_appeared) >= MYSTERYTIME:
                generate_mystery()

            player_group.update(keys)
            player_group.draw(self.screen)

            powerup_group.update(keys)
            powerup_group.draw(self.screen)

            satellite_group.update(self.now)
            satellite_group.draw(self.screen)

            if self.level >= 5 and len(satellite_group) == 0 and random.randint(1, 750) == 1 \
                    and not satellite.powerup_generated:
                generate_satellite()
                SATELLITEANNOUNCE.play()

            check_collisions(self.now, len(boss_group))

            # print(len(bossvessel_group))

            draw_text(self.screen, FONT, 32, 'FPS ' + str(int(self.clock.get_fps())), WHITE, WIDTH - 60, 14, 'center')

            draw_text(self.screen, FONT, 32, 'SCORE ' + str(int(player.score)), WHITE, WIDTH / 2, 14, 'center')

            draw_lives(self.screen, 10, 5, player.lives, pygame.transform.scale(player.image, (18, 18)))

            if player.lives == 0 and self.now - time_last_hit >= 2:
                self.game_over(len(player_group))
            if len(alien_master_group) == 0 and self.now - time_last_hit >= 2:
                satellite.stopped_this_level = False
                self.level += 1
                self.levelstarttime = self.now
                player.double_shot = False
                self.intermission()
                self.new_game()

            if len(boss_group) == 0:
                alien_shoot()
            else:
                bossvessel_shoot()

            pygame.display.flip()
            self.clock.tick(FPS)


if __name__ == '__main__':
    # Make a game instance, and run the game.
    game = SpaceInvaders()
    # game.game_loop()
    game.menu()