########################################################################################################################
#    programmed by Zachary Hunter
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
########################################################################################################################

import pygame, random, sys, time
from pygame.locals import *

#set up some variables
WINDOWWIDTH = 1024
WINDOWHEIGHT = 600
FPS = 6000

ZOMBIESIZE =40
CRAWLERSIZE = 55
ADDNEWZOMBIERATE = 30
ADDNEWCRAWLERRATE = ADDNEWZOMBIERATE

NORMALZOMBIESPEED = 10
CRAWLERZOMBIESPEED = NORMALZOMBIESPEED / 2

PLAYERMOVERATE = 20
BULLETSPEED = 50
ADDNEWBULLETRATE = 10

highScore = 0

TEXTCOLOR = (255, 255, 255)
RED = (255, 0, 0)

def terminate():
    pygame.quit()
    sys.exit()

def waitForPlayerToPressKey():
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                terminate()
            if event.type == KEYDOWN:
                if event.key == K_ESCAPE: # pressing escape quits
                    terminate()
                if event.key == K_RETURN:
                    return
                if event.key == ord('q'):
                    return

def playerHasHitZombie(playerRect, zombies):
    for z in zombies:
        if playerRect.colliderect(z['rect']):
            return True
    return False

def bulletHasHitZombie(bullets, zombies):
    for b in bullets:
        if b['bulletRect'].colliderect(z['rect']):
            return True
    return False

def bulletHasHitCrawler(bullets, crawlers):
    for b in bullets:
        if b['bulletRect'].colliderect(c['rect']):
            return True
    return False

def drawText(text, font, surface, x, y):
    textobj = font.render(text, 1, TEXTCOLOR)
    textrect = textobj.get_rect()
    textrect.topleft = (x, y)
    surface.blit(textobj, textrect)

def showScore():
    drawText('creepers gotten past: %s' % (zombiesGottenPast), font, windowSurface, 10, 0)
    drawText('score: %s' % (score), font, windowSurface, 10, 30)

def closeHighScoreTextFile():
    #highScoreText.flush
    highScoreText.close


# set up pygame, the window, and the mouse cursor
pygame.init()
mainClock = pygame.time.Clock()
windowSurface = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT), pygame.FULLSCREEN)
pygame.display.set_caption('Creeper Defence')
pygame.mouse.set_visible(False)

# set up fonts
font = pygame.font.SysFont(None, 48)

# set up sounds
gameOverSound = pygame.mixer.Sound('gameover.mp3')
pygame.mixer.music.load('aether2.ogg')

# set up images
playerImage = pygame.image.load('player.png')
playerRect = playerImage.get_rect()

bulletImage = pygame.image.load('bullet.png')
bulletRect = bulletImage.get_rect()

zombieImage = pygame.image.load('Creeper.png')
crawlerImage = pygame.image.load('crawler.png')

backgroundImage = pygame.image.load('background.png')
rescaledBackground = pygame.transform.scale(backgroundImage, (WINDOWWIDTH, WINDOWHEIGHT))

# show the "Start" screen
windowSurface.blit(rescaledBackground, (0, 0))
pygame.draw.line(windowSurface, RED, (WINDOWWIDTH / 2 + 20, WINDOWHEIGHT - 50), (WINDOWWIDTH / 2 + 20, 0), 1)
windowSurface.blit(playerImage, (WINDOWWIDTH / 2, WINDOWHEIGHT - 50))
drawText('Creeper Defence', font, windowSurface, (WINDOWWIDTH / 2) - 105, (WINDOWHEIGHT / 3))
drawText('Press Enter to start', font, windowSurface, (WINDOWWIDTH / 2) - 135, (WINDOWHEIGHT / 3) + 50)
pygame.display.update()
waitForPlayerToPressKey()
while True:
    # set up the start of the game

    zombies = []
    crawlers = []
    bullets = []

    zombiesGottenPast = 0
    score = 0




    playerRect.topleft = (WINDOWWIDTH / 2, WINDOWHEIGHT - 50)
    moveLeft = moveRight = False
    shoot = False

    zombieAddCounter = 0
    crawlerAddCounter = 0
    bulletAddCounter = 40
    pygame.mixer.music.play(-1, 0.0)

    while True: # the game loop runs while the game part is playing
        for event in pygame.event.get():
            if event.type == QUIT:
                terminate()

            if event.type == KEYDOWN:
                if event.key == K_LEFT or event.key == ord('a'):
                    moveRight = False
                    moveLeft = True
                if event.key == K_RIGHT or event.key == ord('d'):
                    moveLeft = False
                    moveRight = True

                if event.key == K_SPACE:
                    shoot = True


            if event.type == KEYUP:
                if event.key == K_ESCAPE:
                        terminate()

                if event.key == K_LEFT or event.key == ord('a'):
                    moveLeft = False
                if event.key == K_RIGHT or event.key == ord('d'):
                    moveRight = False

                if event.key == K_SPACE:
                    shoot = False

        # Add new zombies at the top of the screen, if needed.
        zombieAddCounter += 1
        if zombieAddCounter == ADDNEWCRAWLERRATE:
            zombieAddCounter = 0
            zombieSize = ZOMBIESIZE
            newZombie = {'rect': pygame.Rect(random.randint(0, WINDOWWIDTH-zombieSize), 0 - zombieSize, zombieSize, zombieSize),
                        'surface':pygame.transform.scale(zombieImage, (zombieSize, zombieSize)),
                        }

            zombies.append(newZombie)

        # Add new crawlers at the top of the screen, if needed.
        crawlerAddCounter += 1
        if crawlerAddCounter == ADDNEWZOMBIERATE:
            crawlerAddCounter = 0
            crawlerSize = CRAWLERSIZE
            newCrawler = {'rect': pygame.Rect(random.randint(0, WINDOWWIDTH-crawlerSize), 0 - crawlerSize, crawlerSize, crawlerSize),
                        'surface':pygame.transform.scale(crawlerImage, (crawlerSize, crawlerSize * 2)),
                        }

            crawlers.append(newCrawler)

        # add new bullet
        bulletAddCounter += 1
        if bulletAddCounter >= ADDNEWBULLETRATE and shoot == True:
            bulletAddCounter = 100000
            bulletSize = (15, 5)
            newBullet = {'bulletRect': windowSurface.blit(bulletImage, (playerRect.centerx - 2,playerRect.centery))}
            bullets.append(newBullet)

        # Move the player around.
        if moveLeft and playerRect.left > 0:
            playerRect.move_ip(-1 * PLAYERMOVERATE, 0)
        if moveRight and playerRect.right < WINDOWWIDTH:
            playerRect.move_ip(PLAYERMOVERATE, 0)

        # Move the zombies down.
        for z in zombies:
            z['rect'].move_ip(0, NORMALZOMBIESPEED)

        # Move the crawlers down.
        for c in crawlers:
            c['rect'].move_ip(0, CRAWLERZOMBIESPEED)

        # move the bullet
        for b in bullets:
            b['bulletRect'].move_ip(0, -1 * BULLETSPEED)

        # Delete zombies that have fallen past the bottom.
        for z in zombies[:]:
            if z['rect'].top > WINDOWHEIGHT:
                zombies.remove(z)
                zombiesGottenPast += 1

        # Delete crawlers that have fallen past the bottom.
        for c in crawlers[:]:
            if c['rect'].top > WINDOWHEIGHT:
                crawlers.remove(c)
                zombiesGottenPast += 1

        # check if the bullet has hit the zombie
        for z in zombies:
            if bulletHasHitZombie(bullets, zombies):
                score += 1
                zombies.remove(z)
                bullets.remove(b)
        for c in crawlers:
            if bulletHasHitCrawler(bullets, crawlers):
                score += 1
                crawlers.remove(c)
                bullets.remove(b)


        # delete bullets going past the top
        for b in bullets[:]:
            if b['bulletRect'].top < 0:
                bullets.remove(b)

        # Draw the game world on the window.
        windowSurface.blit(rescaledBackground, (0, 0))

        # Draw the player's rectangle, rails
        pygame.draw.line(windowSurface, RED, (playerRect.centerx - 1, playerRect.centery - 20), (playerRect.centerx - 1, 0), 1)
        windowSurface.blit(playerImage, playerRect)

        # Draw each baddie
        for z in zombies:
            windowSurface.blit(z['surface'], z['rect'])

        for c in crawlers:
            windowSurface.blit(c['surface'], c['rect'])

        # draw each bullet
        for b in bullets:
            windowSurface.blit(bulletImage, b['bulletRect'])

        # Draw the score and how many zombies got past
        showScore()

        # update the display
        pygame.display.update()

        # Check if any of the zombies has hit the player.
        if playerHasHitZombie(playerRect, zombies):
            break
        if playerHasHitZombie(playerRect, crawlers):
           break

        # check if score is over 5 which means game over
        if zombiesGottenPast >= 5:
            break

        mainClock.tick(FPS)

    # Stop the game and show the "Game Over" screen.
    pygame.mixer.music.stop()
    gameOverSound.play()
    time.sleep(0.1)
    if zombiesGottenPast >= 5:
        windowSurface.blit(rescaledBackground, (0, 0))
        pygame.draw.line(windowSurface, RED, (WINDOWWIDTH / 2 + 20, WINDOWHEIGHT - 50), (WINDOWWIDTH / 2 + 20, 0), 1)
        windowSurface.blit(playerImage, (WINDOWWIDTH / 2, WINDOWHEIGHT - 50))
        showScore()
        drawText('GAME OVER', font, windowSurface, (WINDOWWIDTH / 2) - 85, (WINDOWHEIGHT / 3))
        drawText('you let to many zombies get past', font, windowSurface, (WINDOWWIDTH / 2)- 115, (WINDOWHEIGHT / 3) + 100)
        drawText('Press enter to play again or escape to exit', font, windowSurface, (WINDOWWIDTH / 3) - 215, (WINDOWHEIGHT / 3) + 150)
        pygame.display.update()
        waitForPlayerToPressKey()
    if playerHasHitZombie(playerRect, zombies):
        windowSurface.blit(rescaledBackground, (0, 0))
        pygame.draw.line(windowSurface, RED, (WINDOWWIDTH / 2 + 20, WINDOWHEIGHT - 50), (WINDOWWIDTH / 2 + 20, 0), 1)
        windowSurface.blit(playerImage, (WINDOWWIDTH / 2, WINDOWHEIGHT - 50))
        showScore()
        drawText('GAME OVER!', font, windowSurface, (WINDOWWIDTH / 2) - 85, (WINDOWHEIGHT / 3))
        drawText('a zombie has hit you!', font, windowSurface, (WINDOWWIDTH / 2.1) - 115, (WINDOWHEIGHT / 3) +100)
        drawText('Press enter to play again or escape to exit.', font, windowSurface, (WINDOWWIDTH / 2.5) - 215, (WINDOWHEIGHT / 3) + 150)
        pygame.display.update()
        waitForPlayerToPressKey()
    gameOverSound.stop()
