Inspiration

I wrote a few ideas based on Halloween down and decided this one was possible to complete in the time provided while having some complexity.

What it does

The program will give the players rounds of approximately 30 seconds to click on as many minsters on the screen as they can to earn points to move onto the next round with the aim of achieving a high score. Monsters have different movement styles and have slight variation between one another whether that be starting position or number of clicks it takes to defeat. After a round is finished, a user will then be asked if they wish to continue if they meet a certain number of points and if so they play another round with a higher score multiplier allowing them to get higher scores. If they wish to stop or don't meet the required number of points then their score is compared to see if it is a high score and if so they provide their username and their score is saved to the high scores files. If not then they just get an output of the top 10 scores and the usernames of those who got them.

How I built it

I started off just practicing with a single image moving it around on the background and seeing if I could get some interaction out of it by clicking on the image. After that, I made the image part of an object which would move and be destroyed if clicked on providing the user points and created the other monster classed with their unique movement styles and special features. I then worked on the loops which would spawn new monsters and move them around the screen and how to handle clicking now that there were multiple monsters on the screen rather than just the one. Once all of this was done, I added in the time and score to the GUI and what would happen when the time runs out. My final task was then reading the high scores from a file and inserting the new score if necessary and then outputting the high scores at the end of the game.

Challenges I ran into

I attempted to install a mySQL database onto my computer to allow myself to store scores in a large database but I was having issues with the install so I had to change to a file based-system instead. I don't believe that all of pygame was installed properly so the command meant to close the window without issue isn't present so my game doesn't end but instead it crashes once finished with.

Accomplishments that I'm proud of

I managed to learn pygame and produce a functioning program in the span of time provided.

What I learned

Basics in using pygame including moving images around a screen, and how to work with a user input by handling an event based system. This will help me in the future with producing other GUIs not related to games since I now have an idea on how event-based systems work.

What's next for Haunted Halloween Blast

Move from storing high scores on a file to a database such as SQL. Tidying up the images so that they have transparent backgrounds. Providing users with a menu. Having new levels rather than just the same one with a higher score required to progress each time. Move all user input into the GUI rather than having some command line input and output when it comes to high scores. Music. Tidying up the code into more than just a main() method and a method for high scores. Providing more information, such as how many hits a Frankenstein has left or what the points threshold for the next level is. More variety in monsters and patterns.

from pygame import *
import random
font.init()

class monster():
    def __init__(self):
        self.points
        self.monsterImage
        self.position

    def getPoints(self):
        return self.points

    def getPosition(self):
        return self.position

    def getImage(self):
        return self.monsterImage

class frankenstein(monster):
    def __init__(self):
        self.monsterImage = image.load("frankenstein.png").convert()
        self.position = self.monsterImage.get_rect()
        self.position[0] = 1120
        self.position[1] = 400
        self.health = random.randint(20, 30)
        self.points = 50 * self.health

    def move(self):
        self.position = self.position.move(-15,0)
        if self.position[0] < 0:
            return False
        else:
            return True

    def damaged(self):
        self.health -= 1
        if self.health == 0:
            return self.points
        else:
            return 0



class bat(monster):
    def __init__(self):
        self.points = 25
        self.monsterImage = image.load("bat.png").convert()
        self.position = self.monsterImage.get_rect()
        self.variation = random.randint(0, 60)
        self.position[1] += self.variation

    def move(self):
        self.position = self.position.move(20,0)
        if self.position[0] > 1123:
            return False
        else:
            return True

    def damaged(self):
        return self.points

class reverseBat(monster):
    def __init__(self):
        self.points = 25
        self.monsterImage = image.load("bat.png").convert()
        self.position = self.monsterImage.get_rect()
        self.position[0] = 1123
        self.variation = random.randint(0, 60)
        self.position[1] += self.variation

    def move(self):
        self.position = self.position.move(-20,0)
        if self.position[0] < 0:
            return False
        else:
            return True

    def damaged(self):
        return self.points

class spider(monster):
    def __init__(self):
        self.points = 50
        self.monsterImage = image.load("spider.jpg").convert()
        self.position = self.monsterImage.get_rect()
        self.verticalMove = 10
        self.variation = random.randint(0, 1000)
        self.position[0] += self.variation

    def move(self):
        self.position = self.position.move(0, self.verticalMove)
        if self.position[1] >= 250:
            self.verticalMove *= -1
            return True
        elif self.position[1] <= 0:
            return False
        else:
            return True

    def damaged(self):
        return self.points

class pumpkin(monster):
    def __init__(self):
        self.points = 250
        self.monsterImage = image.load("pumpkin.png").convert()
        self.position = self.monsterImage.get_rect()
        self.position[0] = random.randint(0, 800)
        self.position[1] = random.randint(0, 500)
        self.existFrames = 10

    def move(self):
        self.existFrames -= 1
        if self.existFrames == 0:
            return False
        else:
            return True

    def damaged(self):
        return self.points

def scoreWriting(score):
    scoreFile = open('highscores.txt', 'r')
    scores = []
    intScores = []
    for line in scoreFile:
        scores.append(line.rstrip())
        count = 0
        for data in line.split(","):
            if count == 1:
                intScores.append(int(data[1:]))
            count += 1
    scoreFile.close()
    if len(scores) < 10:
        userName = input("You got a high score! Enter your name.")
        if len(scores) == 0:
            scores.append(userName + ", " + str(score))
        else:
            posFind = False
            i = 0
            while posFind == False and i < len(intScores):
                if score > intScores[i]:
                    scores.insert(i, userName + ", " + str(score))
                    posFind = True
                else:
                    i += 1
            if posFind == False:
                scores.append(userName + ", " + str(score))
        scoreFile = open('highscores.txt', 'w')
        for i in range(0, len(scores)):
            scoreFile.write(scores[i] + "\n")
        scoreFile.close()
    else:
        if score > intScores[9]:
            userName = input("You got a high score! Enter your name.")
            posFind = False
            i = 0
            while posFind == False and i < len(intScores):
                if score > intScores[i]:
                    scores.insert(i, userName + ", " + str(score))
                    posFind = True
                    scores.pop(10)
                else:
                    i += 1
            scoreFile = open('highscores.txt', 'w')
            for i in range(0, len(scores)):
                scoreFile.write(scores[i] + "\n")
            scoreFile.close()
        else:
            print("Sorry but you didn't get a high score. Thanks for Playing.")
    for i in range(0, len(scores)):
        print(str(i + 1) + ": " + scores[i])

def main():

    screen = display.set_mode((1123, 750))
    resetSquare = image.load("resetSquare.jpg").convert()
    background = image.load("HauntedHouse.jpg").convert()
    screen.blit(background, (0,0))
    event.set_blocked(None)
    event.set_allowed([MOUSEBUTTONUP, MOUSEBUTTONDOWN, QUIT, KEYDOWN])

    spawnDelay = 0
    monsters = []
    score = 0
    roundTime = 30
    timeGap = 10
    multiplier = 1
    levelLimit = 3000
    nextLevelAddition = 2000

    while 1:
        if roundTime > 0:
            for currentEvent in event.get():
                if currentEvent.type in (QUIT, QUIT):
                    sys.exit()
                elif currentEvent.type in (MOUSEBUTTONDOWN, MOUSEBUTTONDOWN):
                    mousePos = mouse.get_pos()
                    mouseX = mousePos[0]
                    mouseY = mousePos[1]
                    clickHits = 0
                    clickScore = 0
                    for i in range(0, len(monsters)):
                        monsterMiss = False
                        while i < len(monsters) and monsterMiss == False:
                            currentMonster = monsters[i]
                            currentMonsterxLimit = currentMonster.getPosition()[0] + currentMonster.getPosition()[2]
                            currentMonsteryLimit = currentMonster.getPosition()[1] + currentMonster.getPosition()[3]
                            if mouseX >= currentMonster.getPosition()[0] and mouseX <= currentMonsterxLimit and mouseY >= currentMonster.getPosition()[1] and mouseY <= currentMonsteryLimit:
                                monsterPoints = currentMonster.damaged()
                                clickHits += 1
                                if monsterPoints > 0:
                                    clickScore += monsterPoints
                                    screen.blit(background, currentMonster.getPosition(), currentMonster.getPosition())
                                    monsters.pop(i)
                                else:
                                    monsterMiss = True
                            else:
                                monsterMiss = True
                    score += clickHits * clickScore * multiplier
            timeGap -= 1
            if timeGap == 0:
                roundTime -= 1
                timeGap = 10

            currentFont = font.SysFont(None, 25)
            scoreText =  currentFont.render("Score: " + str(score), True, (255, 255, 255))
            screen.blit(resetSquare, (10, 730))
            screen.blit(scoreText, (10, 730))
            timeText =  currentFont.render("Time: " + str(roundTime), True, (255, 255, 255))
            screen.blit(resetSquare, (200, 730))
            screen.blit(timeText, (200, 730))

            if spawnDelay == 0 and len(monsters) < 20:
                spawnDelay = random.randint(5, 10)
                monsterSpawn = random.randint(0, 4)
                if monsterSpawn == 0:
                    monsters.append(bat())
                elif monsterSpawn == 1:
                    monsters.append(reverseBat())
                elif monsterSpawn == 2:
                    monsters.append(frankenstein())
                elif monsterSpawn == 3:
                    monsters.append(pumpkin())
                else:
                    monsters.append(spider())
            for i in range(0, len(monsters)):
                screen.blit(background, monsters[i].getPosition(), monsters[i].getPosition())
            for i in range(0, len(monsters)):
                currentMove = False
                while currentMove == False and i < len(monsters):
                    if monsters[i].move() == True:
                        screen.blit(monsters[i].getImage(), monsters[i].getPosition())
                        currentMove = True
                    else:
                        monsters.pop(i)

            time.delay(100)
            spawnDelay -= 1
            display.update()
        else:
            if score >= levelLimit:
                multiplier += 1
                valid = False
                while valid == False:
                    continueChoice = input("Would you like to continue to the next level? (Y/N)").upper()
                    if continueChoice == "Y" or continueChoice == "N":
                        valid = True
                    else:
                        print("Invalid input, please try again.")
                if continueChoice == "Y":
                    roundTime = 30
                    timeGap = 10
                    monsters = []
                    screen.blit(background, (0, 0))
                    levelLimit += nextLevelAddition
                    nextLevelAddition *= 2
                else:
                    scoreWriting(score)
                    sys.quit()
            else:
                print("Your score isn't high enough to progress to the next level.")
                scoreWriting(score)
                sys.quit()

main()

Built With

Share this project:

Updates