Esempio n. 1
0
class Zombie(object):

    # Call constructor
    def __init__(self, screenWidth, screenHeight, spawnx, spawny, vel, health,
                 image, drawBounds):
        # SET PARAMETER VARIABLES #
        self.screenWidth = screenWidth
        self.screenHeight = screenHeight
        self.x = spawnx
        self.y = spawny
        self.vel = vel
        self.health = health
        self.maxHealth = health
        self.drawBounds = drawBounds
        # SET PARAMETER VARIABLES #

        # CREATE SIZE OF THE IMAGE #
        self.scaleWidth = self.screenWidth / 1280
        self.scaleHeight = self.screenHeight / 768
        self.image = image
        if self.scaleWidth != 1 or self.scaleHeight != 1:
            self.image = pygame.transform.scale(
                self.image,
                (int(self.image.get_width() * self.scaleWidth),
                 int(self.image.get_height() *
                     self.scaleWidth)))  # yes i know that's two scalewidths
        self.image = pygame.transform.scale(
            self.image,
            (int(self.image.get_rect().width * 0.75),
             int(self.image.get_rect().height * 0.75)))  # rescale image
        # CREATE SIZE OF THE IMAGE #

        # SET BASIC VARIABLES #
        self.zombie = self.image
        self.isAlive = True
        self.dx = 0
        self.dy = 0
        self.Rect = pygame.Rect(
            (self.x, self.y),
            (self.zombie.get_width(), self.zombie.get_height()))
        self.originalRectWidth = self.Rect.width  # I keep originals because it width/height changes as the zombie rotates. Which makes the health bars look bad.
        self.originalRectHeight = self.Rect.height
        self.isFrozen = False
        self.frozenParticleTimer = None
        # SET BASIC VARIABLES #

        # ZOMBIE ATTACK SYSTEM #
        self.canAttack = True
        self.existingAttackTimer = False
        self.attackTimer = None
        # ZOMBIE ATTACK SYSTEM #

        # SET ZOMBIE-COLLISION RECTANGLES #
        self.left_Rect = pygame.Rect(
            (self.Rect.x - 1, self.Rect.y),
            (1, self.Rect.height))  # Make the rect to the left of main rect
        self.right_Rect = pygame.Rect(
            (self.Rect.x + self.Rect.width + 1, self.Rect.y),
            (1, self.Rect.height))  # Make the rect to the right of main rect
        self.top_Rect = pygame.Rect(
            (self.Rect.x, self.Rect.y - 1),
            (self.Rect.width, 1))  # Make the rect to the top of main rect
        self.bottom_Rect = pygame.Rect(
            (self.Rect.x, self.Rect.y + self.Rect.height),
            (self.Rect.width, 1))  # Make the rect to the bottom of main rect
        # SET RECTANGLES #

        # SET WALL-COLLISION RECTANGLES #
        self.colRectWidth = self.Rect.width * 0.60
        self.colRectHeight = self.Rect.height * 0.60
        self.colRectX = self.Rect.center[0] - (self.colRectWidth / 2)
        self.colRectY = self.Rect.center[1] - (self.colRectHeight / 2)
        self.col_left_Rect = pygame.Rect(
            (self.colRectX, self.colRectY),
            (1, self.colRectHeight))  # left wall ( smaller for collision )
        self.col_right_Rect = pygame.Rect(
            (self.colRectX + self.colRectWidth - 1, self.colRectY),
            (1, self.colRectHeight))  # right wall ( smaller for collision )
        self.col_top_Rect = pygame.Rect(
            (self.colRectX, self.colRectY - 1),
            (self.colRectWidth, 1))  # top wall ( smaller for collision )
        self.col_bottom_Rect = pygame.Rect(
            (self.colRectX, self.colRectY + self.colRectHeight - 1),
            (self.colRectWidth, 1))  # bottom wall ( smaller for collision )
        # SET WALL-COLLISION RECTANGLES #

        # COLLISION BOUND VARIABLES #
        self.leftWallCollided = False
        self.rightWallCollided = False
        self.topWallCollided = False
        self.bottomWallCollided = False
        # COLLISION BOUND VARIABLES #

        # HEALTH BAR #
        self.healthBarWidth = self.Rect.width
        self.healthBar = pygame.Rect((self.x, self.y - 1),
                                     (self.Rect.width, 1))
        # HEALTH BAR #

        # ON FIRE BASED DATA #
        self.onFire = False
        self.fireDamageTimer = None
        self.onFireOffTimer = None
        # ON FIRE BASED DATA #

    # Get width
    def getWidth(self):
        return self.Rect.width

    # Get height
    def getHeight(self):
        return self.Rect.height

    # Set Displacement
    def setD(self, dx, dy):
        self.dx = dx
        self.dy = dy

    # Set Displacement X
    def setDX(self, dx):
        self.dx = dx

    # Set Displacement Y
    def setDY(self, dy):
        self.dy = dy

    # Get zombie rectangle
    def getRect(self, r):
        return self.Rect

    # Get zombie collision rectangle
    def getCollisionRect(self):
        return pygame.Rect((self.colRectX, self.colRectY),
                           (self.colRectWidth, self.colRectHeight))

    # Get zombie wall rects
    def getWall(self,
                wall):  # wall --> (1 = left, 2 = right, 3 = top, 4 = bottom)
        if wall == 1:
            return self.left_Rect
        elif wall == 2:
            return self.right_Rect
        elif wall == 3:
            return self.top_Rect
        elif wall == 4:
            return self.bottom_Rect

    # Get zombie collision wall rects
    def getBlockCollisionWall(
            self, wall):  # wall --> (1 = left, 2 = right, 3 = top, 4 = bottom)
        if wall == 1:
            return self.col_left_Rect
        elif wall == 2:
            return self.col_right_Rect
        elif wall == 3:
            return self.col_right_Rect
        elif wall == 4:
            return self.col_right_Rect

    # Update wall collider rectangles:
    def updateWallColliderRectangles(self):
        self.colRectWidth = self.Rect.width * 0.60
        self.colRectHeight = self.Rect.height * 0.60
        self.colRectX = self.Rect.center[0] - (self.colRectWidth / 2)
        self.colRectY = self.Rect.center[1] - (self.colRectHeight / 2)
        self.col_left_Rect = pygame.Rect(
            (self.colRectX, self.colRectY),
            (1, self.colRectHeight))  # left wall ( smaller for collision )
        self.col_right_Rect = pygame.Rect(
            (self.colRectX + self.colRectWidth - 1, self.colRectY),
            (1, self.colRectHeight))  # right wall ( smaller for collision )
        self.col_top_Rect = pygame.Rect(
            (self.colRectX, self.colRectY - 1),
            (self.colRectWidth, 1))  # top wall ( smaller for collision )
        self.col_bottom_Rect = pygame.Rect(
            (self.colRectX, self.colRectY + self.colRectHeight - 1),
            (self.colRectWidth, 1))  # bottom wall ( smaller for collision )

    # Get if a collision is bound:
    def getCollisionBound(
            self, wall):  # wall --> (1 = left, 2 = right, 3 = top, 4 = bottom)
        if wall == 1:
            return self.leftWallCollided
        elif wall == 2:
            return self.rightWallCollided
        elif wall == 3:
            return self.topWallCollided
        elif wall == 4:
            return self.bottomWallCollided

    # Set a collision bound:
    def setCollisionBound(
        self, wall, _bool
    ):  # wall --> (1 = left, 2 = right, 3 = top, 4 = bottom) || _bool --> (True, False)
        if wall == 1:
            self.leftWallCollided = _bool
        elif wall == 2:
            self.rightWallCollided = _bool
        elif wall == 3:
            self.topWallCollided = _bool
        elif wall == 4:
            self.bottomWallCollided = _bool

    # Collision movement prevention:
    def checkWallCollision(self, playerRect):
        if self.leftWallCollided == True or self.rightWallCollided == True:
            self.setDX(0)
        if self.topWallCollided == True or self.bottomWallCollided == True:
            self.setDY(0)

    # Check double colliders ( prevent corner bugs )
    def checkDoubleWallCollision(self):
        if self.topWallCollided == True and self.leftWallCollided == True and self.bottomWallCollided == True and self.rightWallCollided == True:  # If every wall has collided
            middleVector = Vec2d(
                int(self.screenWidth / 2),
                int(self.screenHeight /
                    2))  # create a vector of the screen middle
            zombieVector = Vec2d(
                int(self.Rect.center[0]),
                int(self.Rect.center[1]))  # create a vector of the zombie
            zombieToMiddleVector = Vec2d(
                middleVector - zombieVector
            )  # create a vector of the direction between middle and zombie
            zombieToMiddleVector = zombieToMiddleVector.normalized(
            )  # normalize to the shortest length
            zombiepos = self.x, self.y  # throw zombie coordinates in a tuple so they can be adjusted via zombieToMiddleVector
            zombiepos += zombieToMiddleVector * (self.getVelocity() * 2
                                                 )  # translate the zombie
            self.x, self.y = zombiepos  # unpack the tuple back into the x,y coordinates of the zombie

    # Set an attack timer
    def setAttackTimer(self, attackDelay, elapsedSeconds):
        self.attackTimer = Timer(attackDelay, elapsedSeconds)
        self.existingAttackTimer = True

    # Get if the zombie can attack
    def getAttackStatus(self):
        return self.canAttack

    # Set if the zombie can attack
    def setAttackAbility(self, status):
        self.canAttack = status

    # Get Zombie X
    def getX(self):
        return self.x

    # Get Zombie Y
    def getY(self):
        return self.y

    # Get Zombie Displacement X
    def getDX(self):
        return self.dx

    # Get Zombie Displacement Y
    def getDY(self):
        return self.dy

    # Get Zombie Displacement
    def getD(self):
        return (self.dx, self.dy)

    # Get Zombie Velocity
    def getVelocity(self):
        return self.vel

    # Get Zombie Rect
    def getRect(self):
        return self.Rect

    # Get Zombie life status
    def getLifeStatus(self):
        return self.isAlive

    # Set Zombie life status
    def setLifeStatus(self, status):
        self.isAlive = status

    # Translate Zombie Location
    def translate(self, dx, dy):
        self.x += dx
        self.y += dy

    # Translate zombie x coordinate
    def translateX(self, dx):
        self.x += dx

    # Translate zombie y coordinate
    def translateY(self, dy):
        self.y += dy

    # Rotation Common Function:
    def rotateToAngle(
        self, x1, y1, x2, y2
    ):  # (x,y)1 is the coordinates to rotate too | (x,y)2 is the point your rotating too
        return 360 - math.atan2(y2 - y1, x2 - x1) * 180 / math.pi

    # Change Health Function:
    def changeHealth(self, health):
        self.health += health

    # Get zombie health
    def getHealth(self):
        return self.health

    # Set if the zombie is frozen or not
    def setFrozen(self, isFrozen, elapsedSeconds):
        self.isFrozen = isFrozen
        if self.isFrozen == True:  # if the program is setting the zombie frozen (True)
            self.frozenParticleTimer = Timer(2, elapsedSeconds)

    # Get whether the zombie is frozen or not
    def getFrozen(self):
        return self.isFrozen

    # Set the zombie on fire
    def setOnFire(self, game_Clock):
        if self.onFire != True:  # if the zombie isn't already on fire
            self.onFire = True
            self.fireDamageTimer = Timer(1, game_Clock.getElapsedSeconds())
            self.onFireOffTimer = Timer(4, game_Clock.getElapsedSeconds())

    # Rotate Zombie
    def rotate(self, angle):
        self.zombie = pygame.transform.rotate(
            self.image, angle)  # Create a Transformation of the original image
        self.Rect = self.zombie.get_rect(
            center=(self.x, self.y))  # Recenter to the position
        """ RESET RECTANGLES """
        self.left_Rect = pygame.Rect(
            (self.Rect.x - 1, self.Rect.y),
            (1, self.Rect.height))  # Make the rect to the left of main rect
        self.right_Rect = pygame.Rect(
            (self.Rect.x + self.Rect.width + 1, self.Rect.y),
            (1, self.Rect.height))  # Make the rect to the right of main rect
        self.top_Rect = pygame.Rect(
            (self.Rect.x, self.Rect.y - 1),
            (self.Rect.width, 1))  # Make the rect to the top of main rect
        self.bottom_Rect = pygame.Rect(
            (self.Rect.x, self.Rect.y + self.Rect.height),
            (self.Rect.width, 1))  # Make the rect to the bottom of main rect
        """ RESET RECTANGLES """

    # Path zombie to player
    def pathFind(self, playerRect):
        self.setD(0, 0)  # Start Displacement at 0
        if self.Rect.colliderect(
                playerRect
        ) != True:  # See if the zombie is not overlapping the player
            if self.x > playerRect.center[
                    0] + 2:  # see if zombie is to the right of player, add 2 to end stutter area
                self.setDX(-self.getVelocity())
            if self.x < playerRect.center[
                    0] - 2:  # see if zombie is to the left of player, subtract 2 to end stutter area
                self.setDX(self.getVelocity())
            if self.y > playerRect.center[
                    1] + 2:  # see if player is on top of zombie, add 2 to end stutter area
                self.setDY(-self.getVelocity())
            if self.y < playerRect.center[
                    1] - 2:  # see if player is under zombie , subtract 2 to end stutter area
                self.setDY(self.getVelocity())

    # Update neccessary zombie logic
    def update(self, playerRect, game_Clock, zombieParticleManager):
        """ FIND PATH TO PLAYER: """
        self.pathFind(playerRect)
        """ ROTATE ZOMBIE TO WHERE IT'S MOVING OR TO THE PLAYER IF THEY'RE OVERLAPPING """
        rotationAngle = 0  # Default it at 0
        if self.Rect.colliderect(
                playerRect):  # check if your overlapping the player
            rotationAngle = self.rotateToAngle(
                self.getX(), self.getY(), playerRect.center[0],
                playerRect.center[1])  # rotate to player
        else:
            rotationAngle = self.rotateToAngle(
                self.getX(), self.getY(),
                self.getX() + self.dx,
                self.getY() + self.dy)  # rotate to travel direction
        self.rotate(rotationAngle)
        """ ROTATE ZOMBIE TO WHERE IT'S MOVING OR TO THE PLAYER IF THEY'RE OVERLAPPING """
        """ CHECK IF THE ZOMBIE IS ON FIRE AND FROZEN, ACT ACCORDINGLY """
        if self.getFrozen(
        ) == True and self.onFire == True:  # if the zombie is frozen and on fire
            self.setFrozen(
                False, game_Clock.getElapsedSeconds())  # unfreeze the zombie
            self.onFire = False  # make the zombie no longer on fire
        """ CHECK IF THE ZOMBIE IS ON FIRE AND FROZEN, ACT ACCORDINGLY """
        """ IF THE ZOMBIE IS ON FIRE, UPDATE ACCORDINGLY """
        if self.onFire == True:  # if the zombie is on fire
            if self.onFireOffTimer.getAlert(
            ) == True:  # if the zombie is finished being on fire
                self.onFire = False  # Take the zombie off being on fire
            else:  # if the zombie is still on fire
                self.onFireOffTimer.update(game_Clock.getElapsedSeconds(
                ))  # update the timer to take the zombie off fire
                if self.fireDamageTimer.getAlert(
                ) == True:  # if a seconds elapsed and it's time to take damage
                    self.changeHealth(-1)  # lose 1 hp
                    self.fireDamageTimer = Timer(
                        1, game_Clock.getElapsedSeconds())
                    zombieParticleManager.addParticles(
                        self.getRect().center[0],
                        self.getRect().center[1], (226, 88, 34),
                        self.getWidth() / 2,
                        self.getHeight() / 2)  # spawn fire particles
                else:  # if it hasn't
                    self.fireDamageTimer.update(game_Clock.getElapsedSeconds(
                    ))  # update the take damage timer
        """ IF THE ZOMIBE IS ON FIRE, UPDATE ACCORDINGLY """
        """ CHECK IF THE ZOMBIE IS EVEN ALIVE """
        if self.health <= 0:
            self.setLifeStatus(False)
        """ CHECK IF THE ZOMBIE IS EVEN ALIVE """
        """ CHECK IF THE ZOMBIE CAN ATTACK """
        if self.existingAttackTimer == True:
            if self.attackTimer.getAlert() == True:
                self.existingAttackTimer = False
                self.attackTimer = None
                self.setAttackAbility(True)
            else:
                self.attackTimer.update(game_Clock.getElapsedSeconds())
                self.setAttackAbility(False)
        else:
            self.setAttackAbility(True)
        """ CHECK IF THE ZOMBIE CAN ATTACK """
        """ UPDATE HEALTH BAR """
        missingHp = float(self.getHealth() / self.maxHealth)
        self.healthBarWidth = float(self.originalRectWidth * missingHp)
        healthBarX = self.getX() - (self.originalRectWidth / 2)
        healthBarY = self.getY() - (self.originalRectHeight / 2)
        self.healthBar = pygame.Rect((int(healthBarX), int(healthBarY)),
                                     (int(self.healthBarWidth), 2))
        """ UPDATE HEALTH BAR """

        self.updateWallColliderRectangles(
        )  # Update collision rectangles for adjusted zombie

    # Update post collision logic
    def finalUpdate(self, playerRect, game_Clock):
        if self.getFrozen() == True:  # if the zombie is frozen
            if self.frozenParticleTimer != None:  # if a frozen particle timer exists
                if self.frozenParticleTimer.getAlert(
                ) == True:  # if the frozen particle timer is finished
                    self.frozenParticleTimer = Timer(
                        2, game_Clock.getElapsedSeconds())
                    zombieParticleManager.addParticles(
                        self.getRect().center[0],
                        self.getRect().center[1], (0, 191, 255),
                        self.getWidth() / 2,
                        self.getHeight() / 2)  # spawn ice particles
            else:
                self.frozenParticleTimer = Timer(
                    2, game_Clock.getElapsedSeconds())  # set a timer
        else:  # if not
            self.translate(self.dx, self.dy)  # Translate zombie
        """ ROTATE ZOMBIE TO WHERE IT'S MOVING OR TO THE PLAYER IF THEY'RE OVERLAPPING """
        rotationAngle = 0  # Default it at 0
        if self.Rect.colliderect(
                playerRect):  # check if your overlapping the player
            rotationAngle = self.rotateToAngle(
                self.getX(), self.getY(), playerRect.center[0],
                playerRect.center[1])  # rotate to player
        else:
            rotationAngle = self.rotateToAngle(
                self.getX(), self.getY(),
                self.getX() + self.dx,
                self.getY() + self.dy)  # rotate to travel direction
        self.rotate(rotationAngle)
        """ ROTATE ZOMBIE TO WHERE IT'S MOVING OR TO THE PLAYER IF THEY'RE OVERLAPPING """

    # Draw the zombie
    def draw(self, canvas):
        canvas.blit(self.zombie, self.getRect())
        if self.maxHealth > 1:  # draw the health bar if it actually has health
            pygame.draw.rect(canvas, (255, 0, 0), self.healthBar,
                             0)  # draw the health bar
        if self.drawBounds == True:
            boundColor = (255, 50, 50)
            pygame.draw.rect(canvas, boundColor, self.col_left_Rect, 1)
            pygame.draw.rect(canvas, boundColor, self.col_right_Rect, 1)
            pygame.draw.rect(canvas, boundColor, self.col_top_Rect, 1)
            pygame.draw.rect(canvas, boundColor, self.col_bottom_Rect, 1)
Esempio n. 2
0
class Level(object):

    # Call constructor:
    def __init__(self, screenWidth, screenHeight, cryptnum, accuracyLevel,
                 magAmmo, fps, tileWidth, tileHeight, blockImage, spawnerImage,
                 backgroundImage, arrowImage, playerSpawnX, playerSpawnY,
                 zombieImage, drawBounds, maxZombies, zombieHealth,
                 zombieVelMin, zombieVelMax, zombieSpawnTimeMin,
                 zombieSpawnTimeMax):
        """
        Constructor Arguments:
        screen width
        screen height
        cryptnum
        accuracyLevel
        magAmmo
        fps
        tile width
        tile height
        blockImage
        spawnerImage
        backgroundImage
        arrowImage
        playerSpawnX
        playerSpawnY
        zombieImage
        drawbounds | developer tool
        maxZombies
        zombieHealth
        zombieMinVel
        zombieMaxVel
        zombieSpawnTimeMin
        zombieSpawnTimeMax
        """

        # Basic Level Data:
        self.levelLoaded = False
        self.screenWidth = screenWidth
        self.screenHeight = screenHeight
        self.cryptnum = cryptnum
        self.fps = fps
        self.tileWidth = tileWidth
        self.tileHeight = tileHeight
        self.isMousePressed = False
        self.mousex = self.mousey = 0

        # Load Images:
        self.blockImage = blockImage
        self.spawnerImage = spawnerImage
        self.backgroundImage = backgroundImage
        self.block = pygame.transform.scale(
            self.blockImage,
            (self.tileWidth, self.tileHeight))  # transform to fit tile
        self.zblock = pygame.transform.scale(
            self.spawnerImage,
            (self.tileWidth, self.tileHeight))  # transform to fit tile
        self.background = pygame.transform.scale(
            self.backgroundImage,
            (self.screenWidth, self.screenHeight))  # transform to fit tile
        self.zombieImage = zombieImage
        self.tileZombie = pygame.transform.scale(
            self.zombieImage,
            (int(self.tileWidth * 0.60), int(self.tileHeight * 0.60)))
        self.distractionOrb = pygame.image.load(
            "Data/images/distractionorb.png")
        self.distractionOrb = pygame.transform.scale(
            self.distractionOrb,
            (self.tileWidth, self.tileHeight))  # scale to fit a tile
        self.grenadeImage = pygame.image.load("Data/images/grenade.png")
        self.bladeImage = pygame.image.load("Data/images/blade.png")
        # Set the player spawn x,y:
        self.playerSpawnX = playerSpawnX
        self.playerSpawnY = playerSpawnY

        # Choose randomized Level
        self.levelDesigns = LevelDesigns()
        self.levelDesigns.generateLevels()
        self.level = random.randint(1, self.levelDesigns.getLevelAmount())

        # Get game-progression Data
        self.maxZombies = maxZombies
        self.zombieHealth = zombieHealth
        self.zombieVelMin = zombieVelMin
        self.zombieVelMax = zombieVelMax
        self.zombieSpawnTimeMin = zombieSpawnTimeMin
        self.zombieSpawnTimeMax = zombieSpawnTimeMax
        self.zombieAttackDelay = 2
        self.accuracyLevel = accuracyLevel
        self.magammo = magAmmo
        self.ammoSize = 20

        # Level Data:
        self.levelBlit = False
        self.Rects = []
        self.zombieSpawners = []
        self.rotatingZombies = []
        self.isLevelFinished = False  # level finished variable
        self.roomEntrance = ""  #to  keep track of where the room was entered
        self.roomEntranceSet = False  # keep track of the entrance was properly recorded
        self.notifyFont = pygame.font.SysFont("Verdana", 36)
        self.bulletType = "normal"
        self.addGrenade = False
        self.addBlade = False
        self.addIceTile = False
        self.addDistractionOrb = False

        # Distraction Orb
        self.distractionOrbActive = False
        self.distractionOrbTimer = None
        self.distractionOrbRect = pygame.Rect(
            (0, 0), (self.tileWidth, self.tileHeight))  # coordinate rectangle

        # Initalize Level Based Objects:
        self.stringManager = StringManager()
        self.bulletManager = BulletManager(self.screenWidth, self.screenHeight,
                                           self.notifyFont, self.ammoSize,
                                           self.magammo, 2)
        self.zombieManager = ZombieManager(self.screenWidth, self.screenHeight,
                                           self.zombieAttackDelay, zombieImage,
                                           drawBounds)
        self.zombieParticleManager = ZombieParticleManager()
        self.bloodExplosionManager = BloodExplosionManager(
            self.screenWidth, self.screenHeight)
        self.fireTileManager = FireTileManager(self.screenWidth,
                                               self.screenHeight,
                                               self.tileWidth, self.tileHeight)
        self.iceTileManager = IceTileManager(self.screenWidth,
                                             self.screenHeight, self.tileWidth,
                                             self.tileHeight)
        self.grenadeManager = GrenadeManager(self.tileWidth, self.tileHeight,
                                             self.grenadeImage)
        self.bladeManager = BladeManager(self.screenWidth, self.screenHeight,
                                         self.tileWidth, self.tileHeight,
                                         self.bladeImage)
        self.gameClock = Clock()

        # End-level arrow data:
        self.arrowImage = arrowImage
        self.arrows = []
        self.arrows.append(
            Arrow(self.arrowImage,
                  (self.screenWidth / 2) - (self.tileWidth / 2),
                  (self.tileHeight * 2) / 2, 90, 1,
                  20))  # create the top arrow
        self.arrows.append(
            Arrow(self.arrowImage,
                  (self.screenWidth / 2) - (self.tileWidth / 2),
                  self.screenHeight - ((self.tileHeight * 2) / 2), -90, 1,
                  20))  # create the bottom arrow
        self.arrows.append(
            Arrow(self.arrowImage, (self.tileWidth * 3) / 2,
                  self.screenHeight / 2, 180, 2, 20))  # create the left arrow
        self.arrows.append(
            Arrow(self.arrowImage,
                  self.screenWidth - ((self.tileWidth * 3) / 2),
                  self.screenHeight / 2, 0, 2, 20))  # create the right arrow

    # Get level progression function (whether it's finished or not):
    def getLevelProgression(self):
        return self.isLevelFinished

    # Set the player spawn based off of level:
    def spawnPlayer(self, player):
        player.setX(self.playerSpawnX)
        player.setY(self.playerSpawnY)

    # Get tile size
    def getTileSize(self):
        return (self.tileWidth, self.tileHeight)

    # Load the level data to create blocks and spawners:
    def loadLevel(self):
        if self.level == 1:  # Check if level chosen was level 1
            levelData = self.levelDesigns.getLevel1(
            )  # Get the level Data of level 1
        elif self.level == 2:  # Check if the level chosen was level 2
            levelData = self.levelDesigns.getLevel2(
            )  # Get the level Data of level 2
        elif self.level == 3:  # Check if the level chosen was level 3
            levelData = self.levelDesigns.getLevel3(
            )  # Get the level Data of level 3
        elif self.level == 4:  # Check if level chosen was level 4
            levelData = self.levelDesigns.getLevel4(
            )  # Get the level Data of level 4
        elif self.level == 5:  # Check if level chosen was level 5
            levelData = self.levelDesigns.getLevel5(
            )  # Get the level Data of level 5
        elif self.level == 6:  # Check if level chosen was level 6
            levelData = self.levelDesigns.getLevel6(
            )  # Get the level Data of level 6
        elif self.level == 7:  # Check if level chosen was level 7
            levelData = self.levelDesigns.getLevel7(
            )  # Get the level Data of level 7
        elif self.level == 8:  # Check if level chosen was level 8
            levelData = self.levelDesigns.getLevel8(
            )  # Get the level Data of level 8
        elif self.level == 9:  # Check if level chosen was level 9
            levelData = self.levelDesigns.getLevel9(
            )  # Get the level Data of level 9
        elif self.level == 10:  # Check if level chosen was level 10
            levelData = self.levelDesigns.getLevel10(
            )  # Get the level Data of level 10
        x = 0  # Start X at 0
        y = 0  # Start Y at 0
        for row in levelData:  # run a for loop for all the rows in level data
            for col in row:  # run a for loop for all the colums in the row
                if col == "1":  # check if the column section is labeled 1
                    self.Rects.append(
                        pygame.Rect((x, y),
                                    (self.tileWidth,
                                     self.tileHeight)))  # append a rectangle
                if col == "2":  # check if the column section is labeled 2
                    self.Rects.append(
                        pygame.Rect((x, y),
                                    (self.tileWidth,
                                     self.tileHeight)))  # append a rectangle
                if col == "3":  # check if the column section is labeled 3
                    self.zombieSpawners.append(
                        ZombieSpawner(
                            x, y, self.zombieManager,
                            int(self.maxZombies /
                                self.levelDesigns.getSpawnerAmount(
                                    self.level)), self.zombieSpawnTimeMin,
                            self.zombieSpawnTimeMax, self.zombieVelMin,
                            self.zombieVelMax, self.zombieHealth)
                    )  # Initalize a zombie spawner with given data
                    self.rotatingZombies.append(0)
                x += self.tileWidth  # adjust x
            y += self.tileHeight  # adjust y
            x = 0  # reset row
        self.levelData = levelData
        self.levelLoaded = True

    # Record where the room was entered for post-battle room transitioning
    def recordRoomEntrance(self, playerRect):
        if self.playerSpawnX == self.screenWidth - self.tileWidth:  # if the player spawn x was the east entrance
            self.roomEntrance = "east"
        elif self.playerSpawnX == self.tileWidth:  # if the player spawn x was the west entrance
            self.roomEntrance = "west"
        elif self.playerSpawnY >= self.screenHeight - self.tileHeight:  # if the player spawn y was the south entrance
            self.roomEntrance = "south"
        elif self.playerSpawnY == 0:  # if the player spawn y was the north entrance
            self.roomEntrance = "north"

    # Pass pygame events:
    def passEvent(self, event, soundManager):
        if event.type == pygame.MOUSEBUTTONDOWN:
            self.isMousePressed = True
        if event.type == pygame.MOUSEBUTTONUP:
            self.isMousePressed = False
        if event.type == pygame.MOUSEMOTION:
            self.mousex, self.mousey = event.pos
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_r:
                self.bulletManager._reload(self.getGameClock(),
                                           self.stringManager, soundManager)

    # Use an item by calling it's ID
    def useItemById(self, item):
        if item == "distractionorb":
            self.addDistractionOrb = True
        elif item == "grenade":
            self.addGrenade = True
        elif item == "blade":
            self.addBlade = True
        elif item == "icetile":
            self.addIceTile = True

    # Update the current bullet type set in main
    def updateCurrentBulletType(self, bulletType):
        self.bulletType = bulletType

    # Check if a point collides with any of the blocks
    def checkPath(self, x, y):
        r = pygame.Rect((x, y), (1, 1))
        for block in range(len(self.Rects)):
            if r.colliderect(self.Rects[block]):
                return False
        return True

    # Get game clock
    def getGameClock(self):
        return self.gameClock

    # Get total ammunition from bullet amanger
    def getAmmunition(self):
        return (self.bulletManager.getAmmo(),
                self.bulletManager.getMagazines())

    # Set the accuracy level
    def setAccuracyLevel(self, accuracyLevel):
        self.accuracyLevel = accuracyLevel

    # Get the accuracy level
    def getAccuracyLevel(self):
        return self.accuracyLevel

    # Get the tile where the distraction orb will be based off of the mouse location
    def distractionOrb_setLocation(self, mousex, mousey):
        for i in range(
                int(self.screenHeight / self.tileHeight
                    )):  # run a for loop for the amount of tiles on the y axis
            for z in range(
                    int(self.screenWidth / self.tileWidth)
            ):  # run a for loop for the amount of tiles on the x axis
                tileRect = pygame.Rect(
                    (z * self.tileWidth, i * self.tileHeight),
                    (self.tileWidth, self.tileHeight
                     ))  # generate the tile as a pygame rectangle
                if tileRect.collidepoint(
                        mousex, mousey
                ) == True:  # check if the mouse lands on the tile
                    self.distractionOrbRect = tileRect  # place tile

    # Set distraction orb active
    def distractionOrb_setActive(self, activity):
        self.distractionOrbActive = activity
        self.distractionOrbTimer = Timer(
            10,
            self.getGameClock().getElapsedSeconds())

    # Add a magazine to the bullet manager
    def addMagazine(self, magazines):
        self.bulletManager.addMagazines(magazines)

    # Check if a point doesn't land on a level block
    def checkPointBlockCollision(self, x, y):
        for block in range(len(self.Rects)):  # run a for loop for all blocks
            if self.Rects[block].collidepoint(
                    x, y) == True:  # if the point lands on the block
                return True  # say that the point lands on one of the blocks
        for spawner in range(len(self.zombieSpawners)
                             ):  # run a for loop for all zombie spawners
            spawnerRect = pygame.Rect(
                (self.zombieSpawners[spawner].getX(),
                 self.zombieSpawners[spawner].getY()),
                (self.tileWidth,
                 self.tileHeight))  # generate a rect of spawner
            if spawnerRect.collidepoint(
                    x, y) == True:  # if the point lands on the block
                return True  # say that the point lands on one of the spawners
        return False  # return False if it hasn't returned true yet, because it means it hasn't landed on any blocks

    # Update level data & logic:
    def update(self, player, soundManager):
        if self.levelLoaded == False:  # Check if the level was loaded
            self.loadLevel()
            newLevelString = "Crypt: %d" % self.cryptnum
            textInstance = self.notifyFont.render(
                newLevelString, True, (255, 0, 0))  # for size reference
            self.stringManager.addText(
                newLevelString, (0, 255, 0), self.notifyFont,
                ((self.screenWidth / 2) - (textInstance.get_width() / 2),
                 self.screenHeight - (self.screenHeight * 0.75)), 4, -3,
                self.getGameClock())  # add the text for a new crypt
        if self.roomEntranceSet == False:  # if the entrance hasn't been recorded
            self.recordRoomEntrance(player.getRect())  # set the room entrance
            self.roomEntranceSet = True
        self.gameClock.update(self.fps)

        self.zombieManager.update(
            player, self.gameClock, self.zombieParticleManager,
            self.distractionOrbActive,
            self.distractionOrbRect)  # Update zombie manager
        """ CHECK IF THE LEVEL IS FINISHED """
        if self.zombieManager.getZombiesKilled(
        ) >= self.maxZombies + 4:  # check if the amount of zombies killed = the amount of every existing zombies
            self.isLevelFinished = True
        """ CHECK IF THE LEVEL IS FINISHED """
        """ UPDATE DISTRACTION ORB """
        if self.addDistractionOrb == True:  # if the player pressed Q
            self.addDistractionOrb = False  # switch/off to kill spam
            self.distractionOrb_setLocation(self.mousex,
                                            self.mousey)  # place location
            self.distractionOrb_setActive(
                True)  # set the distraction orb in the level active

        if self.distractionOrbActive == True:  # if there currently is a distraction orb
            if self.distractionOrbTimer.getAlert(
            ) == True:  # if the distraction orb is finished and must despawn
                self.distractionOrbActive = False  # turn off the distraction orb
            else:  # if not
                self.distractionOrbTimer.update(self.getGameClock(
                ).getElapsedSeconds())  # update the distraction orb timer
        """ UPDATE DISTRACTION ORB """
        """ UPDATE GRENADES """
        if self.addGrenade == True:
            self.addGrenade = False
            self.grenadeManager.addGrenade(player.getRect().center[0],
                                           player.getRect().center[1],
                                           self.mousex,
                                           self.mousey)  # spawn a grenade
        self.grenadeManager.update(self.Rects, self.zombieManager.getZombies(),
                                   player)
        """ UPDATE GRENADES """
        """ UPDATE BLADES """
        if self.addBlade == True:  # if the player pressed C
            self.addBlade = False  # turn off to kill spam
            self.bladeManager.addBlade(self.mousex, self.mousey,
                                       self.getGameClock())  # add blade
        """ UPDATE BLADES """
        """ ADD ICE TILE """
        if self.addIceTile == True:  # if player pressed v
            self.addIceTile = False  # turn off to kill spam
            self.iceTileManager.addTile(self.mousex, self.mousey,
                                        self.getGameClock())  # add a tile
        """ ADD ICE TILE """
        """ SHOOT A BULLET """
        if self.isMousePressed == True and player.getRunningStatus(
        ) == False and player.getRect().collidepoint(
                self.mousex, self.mousey
        ) == False and self.bulletManager.isReloading(
        ) == False:  # Check if player clicked and if the player isn't running and check if the mouse isn't overlapping the player and they aren't reloading
            soundManager.playSound("bang")  # play the bullet shoot sound
            self.isMousePressed = False  # Set to false to prevent bullet Spam
            self.bulletManager.addBullet(player.getX(), player.getY(),
                                         self.mousex, self.mousey,
                                         self.getAccuracyLevel(),
                                         self.bulletType)  # create a bullet
        """ CHECK PLAYER COLLISION WITH WALLS """
        for i in range(1, 5):  # run a for loop for all player walls
            player.setCollisionBound(
                i, False)  # default all wall collisions to be false
        for block in range(len(
                self.Rects)):  # run a for loop for all existing blocks
            for wall in range(1, 5):  # run a for loop for all walls
                if player.getWall(wall).colliderect(
                        self.Rects[block]
                ):  # Check if the current wall collided with the current block
                    player.setCollisionBound(wall, True)
        """ CHECK PLAYER COLLISION WITH WALLS """
        """ CHECK IF THE ZOMBIES COLLIDED WITH THE FIRE & ICE TILES """
        self.zombieManager.checkFireTileCollision(
            self.fireTileManager,
            self.getGameClock())  # update to set zombies on fire
        self.zombieManager.checkIceTileCollision(
            self.iceTileManager,
            self.getGameClock())  # update to set zombies frozen
        """ CHECK IF THE ZOMBIES COLLIDED WITH THE FIRE & ICE TILES """
        """ CHECK BULLET COLLISION WITH WALLS """
        if self.bulletManager.getBulletAmount() > 0:  # check if bullets exist
            bulletX = self.bulletManager.getBulletLocationX(
            )  # import bullet x array
            bulletY = self.bulletManager.getBulletLocationY(
            )  # import bullet y array
            for bullet in range(self.bulletManager.getBulletAmount()
                                ):  # run a for loop for all bullets
                if self.bulletManager.getBulletActivity(
                        bullet
                ) == True:  # check if the bullet is active before any relevant code happens
                    bx = bulletX[bullet] - int(
                        self.bulletManager.getBulletRadius(bullet) /
                        2)  # Set the bullet X as if it was a rectangle
                    by = bulletY[bullet] - int(
                        self.bulletManager.getBulletRadius(bullet) /
                        2)  # Set the bullet Y as if it was a rectangle
                    bl = self.bulletManager.getBulletRadius(
                        bullet
                    ) * 2  # Set the Width & Height (Length) of the bullet as if it was a rectangle
                    bulletRect = pygame.Rect(
                        (bx, by),
                        (bl, bl))  # Create the Rectangle with the created Data
                    for block in range(len(
                            self.Rects)):  # run a for loop for all blocks
                        if bulletRect.colliderect(self.Rects[block]) == True:
                            if self.bulletManager.getBulletType(
                                    bullet) != "bouncy":
                                self.bulletManager.setBulletActivity(
                                    bullet, False)
                            else:  # if the bullet type is bouncy
                                if self.bulletManager.getBouncyBulletBounces(
                                        bullet
                                ) >= 3:  # if it's bounced 3 times
                                    self.bulletManager.setBulletActivity(
                                        bullet,
                                        False)  # set the bullet inactive
                                else:
                                    _block = self.Rects[block]
                                    blockTop = pygame.Rect(
                                        (_block.x - 1, _block.y - 1),
                                        (_block.width + 1, 3))
                                    blockBottom = pygame.Rect(
                                        (_block.x,
                                         _block.y + _block.height - 1),
                                        (_block.width + 1, 2))
                                    blockLeft = pygame.Rect(
                                        (_block.x - 1, _block.y - 1),
                                        (2, _block.height + 1))
                                    blockRight = pygame.Rect(
                                        (_block.x + _block.width - 1,
                                         _block.y - 1), (3, _block.height + 1))
                                    if blockTop.colliderect(
                                            bulletRect
                                    ) or blockBottom.colliderect(bulletRect):
                                        self.bulletManager.flipVectorY(
                                            bullet)  # flip direction Y
                                    if blockLeft.colliderect(
                                            bulletRect
                                    ) or blockRight.colliderect(bulletRect):
                                        self.bulletManager.flipVectorX(
                                            bullet)  # flip direction X
                                    self.bulletManager.lungeBullet(bullet)
                                    self.bulletManager.addBouncyBulletBounce(
                                        bullet)
        """ CHECK BULLET COLLISION WITH WALLS """
        """ IF A BLOCK IS INCOMING. GO AROUND IT PATHFINDING"""
        for zombie in range(len(self.zombieManager.getZombies())
                            ):  # run a for loop for all zombies
            if self.zombieManager.getZombies()[zombie].getLifeStatus(
            ) == True:  # check if the zombie is alive before you do anything with it
                for block in range(len(
                        self.Rects)):  # run a for loop for all blocks
                    if self.zombieManager.getZombies(
                    )[zombie].getCollisionRect().colliderect(
                            self.Rects[block]
                    ) == True:  # Check if the zombie is colliding with the block in the first place
                        differenceX = self.zombieManager.getZombies(
                        )[zombie].getCollisionRect(
                        ).center[0] - self.Rects[block].center[0]
                        differenceY = self.zombieManager.getZombies(
                        )[zombie].getCollisionRect(
                        ).center[1] - self.Rects[block].center[1]
                        if differenceX < 0:
                            differenceX *= -1
                        if differenceY < 0:
                            differenceY *= -1
                        if differenceX <= self.Rects[
                                block].width / 2 and differenceX >= 0 and differenceY <= self.Rects[
                                    block].height / 2 and differenceY >= 0:
                            # Check if the zombie's collision rectangle's midleft x  is in between the block center and the right side of the block
                            if self.zombieManager.getZombies()[zombie].getRect(
                            ).midleft[0] < self.Rects[block].center[0] + self.Rects[
                                    block].width and self.zombieManager.getZombies(
                                    )[zombie].getCollisionRect(
                                    ).midleft[0] > self.Rects[block].center[0]:
                                self.zombieManager.getZombies(
                                )[zombie].setCollisionBound(
                                    1, True
                                )  # Tell the zombie's left wall is collided
                                zombieLeftSide = self.zombieManager.getZombies(
                                )[zombie].getCollisionRect().midleft[0]
                                self.zombieManager.getZombies(
                                )[zombie].translate(
                                    self.Rects[block].midright[0] -
                                    zombieLeftSide, 0
                                )  # Translate the zombie away from the wall ( to the right)
                            # Check if the zombie's collision rectangle's midright x is in between the block center and the left side of the block
                            if self.zombieManager.getZombies()[zombie].getRect(
                            ).midright[0] > self.Rects[block].center[
                                    0] - self.Rects[
                                        block].width and self.zombieManager.getZombies(
                                        )[zombie].getCollisionRect().midright[
                                            0] < self.Rects[block].center[0]:
                                self.zombieManager.getZombies(
                                )[zombie].setCollisionBound(
                                    2, True
                                )  # Tell the zombie's right wall is collided
                                zombieRightSide = self.zombieManager.getZombies(
                                )[zombie].getCollisionRect().midright[0]
                                self.zombieManager.getZombies(
                                )[zombie].translate(
                                    self.Rects[block].midleft[0] -
                                    zombieRightSide, 0
                                )  # Translate the zombie away from the wall ( to the left )
                            # Check if the zombie's collision rectangle's midtop y is in between the block center and the bottom side of the block
                            if self.zombieManager.getZombies()[zombie].getRect(
                            ).midtop[1] < self.Rects[block].center[1] + self.Rects[
                                    block].height and self.zombieManager.getZombies(
                                    )[zombie].getCollisionRect(
                                    ).midtop[1] > self.Rects[block].center[1]:
                                self.zombieManager.getZombies(
                                )[zombie].setCollisionBound(
                                    3, True
                                )  # Tell the zombie's top wall is collided
                                zombieTopSide = self.zombieManager.getZombies(
                                )[zombie].getCollisionRect().midtop[1]
                                self.zombieManager.getZombies(
                                )[zombie].translate(
                                    0, self.Rects[block].midbottom[1] -
                                    zombieTopSide
                                )  # Translate the zombie away from the wall ( to the bottom )
                            # Check if the zombie's collision rectangle's midbottom y is in between the block center and the top side of the block
                            elif self.zombieManager.getZombies(
                            )[zombie].getRect().midbottom[1] > self.Rects[
                                    block].center[1] - self.Rects[
                                        block].height and self.zombieManager.getZombies(
                                        )[zombie].getCollisionRect().midbottom[
                                            1] < self.Rects[block].center[1]:
                                self.zombieManager.getZombies(
                                )[zombie].setCollisionBound(
                                    4, True
                                )  # Tell the zombie's bottom wall is collided
                                zombieBottomSide = self.zombieManager.getZombies(
                                )[zombie].getCollisionRect().midbottom[1]
                                self.zombieManager.getZombies(
                                )[zombie].translate(
                                    0, self.Rects[block].midtop[1] -
                                    zombieBottomSide)
        """ IF A BLOCK IS INCOMING, GO AROUND IT PATHFINDING """
        """ CHECK ZOMBIE COLLISION WITH WALLS """
        for zombie in range(len(self.zombieManager.getZombies())):
            if self.zombieManager.getZombies()[zombie].getLifeStatus() == True:
                if self.zombieManager.getZombies()[zombie].getRect(
                ).colliderect(player.getRect(
                )) != True:  # if they are not colliding with the player
                    currentZombie = self.zombieManager.getZombies()[zombie]
                    for block in range(len(self.Rects)):
                        if currentZombie.getDX(
                        ) > 0:  # if the zombie's current disp. is heading right
                            midRightRect = pygame.Rect(
                                (currentZombie.getCollisionRect().midright[0] +
                                 currentZombie.getVelocity(),
                                 currentZombie.getCollisionRect().midright[1]),
                                (1, 1))  # set a rectangle in upcoming unit
                            if midRightRect.colliderect(
                                    self.Rects[block]
                            ) == True:  # if the area they are approaching has a block
                                currentZombie.setD(
                                    0, -currentZombie.getVelocity()
                                )  # stop the zombie from going in that direction and have them head upwards
                        if currentZombie.getDX(
                        ) < 0:  # if the zombie's current disp. is heading left
                            midLeftRect = pygame.Rect(
                                (currentZombie.getCollisionRect().midleft[0] -
                                 currentZombie.getVelocity(),
                                 currentZombie.getCollisionRect().midleft[1]),
                                (1, 1))  # set a rectangle in upcoming unit
                            if midLeftRect.colliderect(
                                    self.Rects[block]
                            ) == True:  # if the area they are approaching has a block
                                currentZombie.setD(
                                    0, currentZombie.getVelocity()
                                )  # stop the zombie from going in that direction and have them head downwards
                        if currentZombie.getDY(
                        ) > 0:  # if the zombie's current disp. is heading down
                            midBottomRect = pygame.Rect(
                                (currentZombie.getCollisionRect().midbottom[0],
                                 currentZombie.getCollisionRect().midbottom[1]
                                 + currentZombie.getVelocity()),
                                (1,
                                 1))  # set a rectangle in upcoming direction
                            if midBottomRect.colliderect(
                                    self.Rects[block]
                            ) == True:  # if the area they are approaching has a block
                                currentZombie.setD(
                                    currentZombie.getVelocity(), 0
                                )  # stop the zombie from going in that direction and have them head to the right
                        if currentZombie.getDY(
                        ) < 0:  # if the zombie's current disp. is heading up
                            midTopRect = pygame.Rect(
                                (currentZombie.getCollisionRect().midtop[0],
                                 currentZombie.getCollisionRect().midtop[1] -
                                 currentZombie.getVelocity()),
                                (1,
                                 1))  # set a rectangle in upcoming direction
                            if midTopRect.colliderect(
                                    self.Rects[block]
                            ) == True:  # if the area they are approaching has a block
                                currentZombie.setD(
                                    -currentZombie.getVelocity(), 0
                                )  # stop the zombie from going in that direction and have them head to the left
                        #if currentZombie.getDX() > 0 and currentZombie.getDY() > 0: # if the zombie is going northeast direction
                        #northEastRect = pygame.Rect((currentZombie.getCollisionRect().),())
        """ CHECK ZOMBIE COLLISION WITH WALLS """
        """ MANAGE COLLISION WITH BLOOD PARTICLES """
        for particle in range(self.bloodExplosionManager.getLength()):
            if self.bloodExplosionManager.getParticleActive(particle) == True:
                if player.getRect().collidepoint(
                        self.bloodExplosionManager.getX(particle),
                        self.bloodExplosionManager.getY(particle)
                ) == True:  # if the player rect overlaps the current particle
                    self.bloodExplosionManager.setParticleActive(
                        particle, False)  # set that particle inactive
                for block in range(len(
                        self.Rects)):  # run a for loop for all blocks
                    if self.Rects[block].collidepoint(
                            self.bloodExplosionManager.getX(particle),
                            self.bloodExplosionManager.getY(particle)
                    ) == True:  # if the current block overlaps current particle
                        self.bloodExplosionManager.setParticleActive(
                            particle, False)  # set that particle inactive
        """ MANAGE COLLISION WITH BLOOD PARTICLES """
        """ CHECK IF ANY ICE TILES / FIRE TILES OVERLAP, DELETE IF SO """
        for iceTile in range(len(self.iceTileManager.getTiles())
                             ):  # run a for loop for all ice tiles
            if self.iceTileManager.getActive(
                    iceTile) == True:  # if the ice tile is active
                for fireTile in range(len(self.fireTileManager.getTiles())
                                      ):  # run a for loop for all fire tiles
                    if self.fireTileManager.getActive(
                            fireTile) == True:  # if the fire tile is active
                        if self.iceTileManager.getTiles()[iceTile].colliderect(
                                self.fireTileManager.getTiles()[fireTile]
                        ) == True:  # if the ice tile is overlapping with the fire tile
                            self.iceTileManager.setActive(
                                iceTile, False)  # set ice tile inactive
                            self.fireTileManager.setActive(
                                fireTile, False)  # set fire tile inactive
        """ CHECK IF ANY ICE TILES / FIRE TILES OVERLAP, DELETE IF SO """
        """ UPDATE ARROW TRANSLATIONS """
        if self.getLevelProgression() == True:  # if the level was completed
            for arrow in range(len(
                    self.arrows)):  # run a for loop for all arrows
                self.arrows[arrow].update(self.gameClock.getElapsedFrames()
                                          )  # update the arrows translations
        """ UPDATE ARROW TRANSLATIONS """
        """ UPDATING ROTATING ZOMBIES IN SPAWNERS """
        for zombie in range(len(self.zombieSpawners)):
            self.rotatingZombies[zombie] += 6
            if self.rotatingZombies[zombie] >= 360:
                self.rotatingZombies[zombie] = 0
        """ UPDATING ROTATING ZOMBIES IN SPAWNERS """
        """ MANAGE LEVEL RESPONSIBILITIES """
        if self.isLevelFinished == False:  # If the level isn't finished (secondary measure):
            for spawner in range(len(self.zombieSpawners)
                                 ):  # Run a for loop for all zombie spawners
                self.zombieSpawners[spawner].update(
                    self.gameClock)  # Update Zombie Spawners
        self.bulletManager.update(self.getGameClock(), self.stringManager,
                                  soundManager)  # Update bullets

        if self.distractionOrbActive == True:  # if the distraction orb is active
            self.zombieManager.finalUpdate(
                self.distractionOrbRect, self.getGameClock(
                ))  # post-collision zombie update, have it face D_ORB
        else:  # if the distraction orb is not active
            self.zombieManager.finalUpdate(
                player.getRect(),
                self.getGameClock())  # Post-collision zombie update

        self.zombieManager.checkBulletCollision(
            soundManager, self.bulletManager.getBulletLocationX(),
            self.bulletManager.getBulletLocationY(), self.bulletManager,
            player, self.bloodExplosionManager, self.fireTileManager,
            self.bulletType, self.getGameClock(
            ))  # check the bullet collision between the bullets and zombies
        self.stringManager.update(self.getGameClock())
        self.bloodExplosionManager.update()
        self.zombieParticleManager.update()
        self.fireTileManager.update(self.getGameClock())
        self.iceTileManager.update(self.getGameClock())
        self.bladeManager.update(self.zombieManager.getZombies(), player,
                                 self.getGameClock())
        """ MANAGE LEVEL RESPONSIBILITIES """

    # Check zombie to player collision
    def checkZombiePlayerCollision(self, player, soundManager):
        for zombie in range(len(self.zombieManager.getZombies())
                            ):  # run a for loop for all zombies
            if self.zombieManager.getZombies()[zombie].getLifeStatus() == True:
                if self.zombieManager.getZombies()[zombie].getAttackStatus(
                ) == True:
                    if self.zombieManager.getZombies()[zombie].getRect(
                    ).colliderect(player.getRect(
                    )):  # if the current zombie is colliding with the player
                        soundManager.playSound(
                            "attack")  # play the zombie attack sound
                        player.giveHealth(-1)  # take 1 hp away from the player
                        playerVec = Vec2d(player.getRect().center[0],
                                          player.getRect().center[1])
                        zombieVec = Vec2d(
                            self.zombieManager.getZombies()
                            [zombie].getRect().center[0],
                            self.zombieManager.getZombies()
                            [zombie].getRect().center[1])
                        playerToZombieVec = Vec2d(playerVec - zombieVec)
                        playerToZombieVec = playerToZombieVec.normalized()

                        # translate to the player ( Lunge like ) by half the width and height of it's collision rectangle
                        self.zombieManager.getZombies()[zombie].translate(
                            playerToZombieVec[0] *
                            (player.getRect().width / 2),
                            playerToZombieVec[1] *
                            (player.getRect().height / 2))
                        self.zombieManager.setZombieAttacked(
                            zombie, self.getGameClock())

                        # Draw the zombie speech string:
                        zombieQuotes = [
                            "Scratch!", "Swipe!", "Chomp!", "Slash!", "Whack!",
                            "Slam!", "Grrgah!", "Kick!", "Bite!", "Stomp!"
                        ]
                        zombieSpeech = random.randint(
                            0, 9)  # Select out of the 4 speeches
                        zombieSpeech = zombieQuotes[zombieSpeech]
                        font = pygame.font.SysFont("Trebuchet MS", 10)
                        self.stringManager.addText(
                            zombieSpeech, (255, 25, 25), font,
                            self.zombieManager.getZombies()
                            [zombie].getRect().center, 1, -1,
                            self.getGameClock())
                        font = None  # Delete from memory

    # Get the velocity of the last spawned zombie
    def getLatestZombieVelocity(self):
        return self.zombieManager.getZombies()[
            len(self.zombieManager.getZombies()) - 1].getVelocity()

    # Get the amount of zombies spawned:
    def getZombiesSpawned(self):
        zombiesSpawned = 0
        for spawner in range(len(self.zombieSpawners)):
            zombiesSpawned += self.zombieSpawners[spawner].getZombiesSpawned()
        return zombiesSpawned

    # Draw relevant level data (Zombies, bullet's, blocks, spawners)
    def draw(self, canvas, playerRect):
        canvas.blit(self.background, (0, 0))  # draw background image

        # Draw arrows:
        if self.getLevelProgression() == True:  # if level is finished
            if self.getRoomEntrance(
                    playerRect
            ) != "north":  # if the player entrance isn't north
                self.arrows[0].draw(canvas)  # draw north arrow
            if self.getRoomEntrance(
                    playerRect
            ) != "south":  # if the player entrance isn't south
                self.arrows[1].draw(canvas)  # draw south arrow
            if self.getRoomEntrance(
                    playerRect) != "west":  # if the player entrance isn't west
                self.arrows[2].draw(canvas)  # draw west arrow
            if self.getRoomEntrance(
                    playerRect) != "east":  # if the player entrance isn't east
                self.arrows[3].draw(canvas)  # draw east arrow

        for rect in range(len(self.Rects)):  # for loop to run all rects
            canvas.blit(
                self.block,
                (self.Rects[rect].x, self.Rects[rect].y))  #draw a block
        for spawner in range(len(self.zombieSpawners)):
            pygame.draw.rect(canvas, (153, 0, 0),
                             (self.zombieSpawners[spawner].getX(),
                              self.zombieSpawners[spawner].getY(),
                              self.tileWidth, self.tileHeight), 0)
            zombie = pygame.transform.rotate(self.tileZombie,
                                             self.rotatingZombies[spawner])
            zombieRect = zombie.get_rect(
                center=(self.zombieSpawners[spawner].getX() +
                        int(self.tileWidth / 2),
                        self.zombieSpawners[spawner].getY() +
                        int(self.tileHeight / 2)))
            if self.zombieSpawners[spawner].getActive() == True:
                canvas.blit(zombie, zombieRect)
            canvas.blit(self.zblock, (self.zombieSpawners[spawner].getX(),
                                      self.zombieSpawners[spawner].getY()))
        if self.distractionOrbActive == True:  # if there is currently a distraction orb placed
            canvas.blit(self.distractionOrb,
                        self.distractionOrbRect)  # draw the distraction Orb
        self.bladeManager.draw(canvas)
        self.iceTileManager.draw(canvas)  # draw ice tiles
        self.fireTileManager.draw(canvas)  # draw fire tiles
        self.bulletManager.draw(canvas, playerRect)  # draw bullets
        self.zombieManager.draw(canvas)  # draw zombies
        self.grenadeManager.draw(canvas)  # draw grenades
        self.bloodExplosionManager.draw(canvas)  # draw blood explosions
        self.zombieParticleManager.draw(
            canvas)  # draw zombie fire/frost particles
        self.stringManager.draw(canvas)  # draw texts

    # Get where the room entrance was:
    def getRoomEntrance(self, playerRect):
        if self.roomEntranceSet == False:  # if the room entrance wasn't recorded yet
            self.recordRoomEntrance(playerRect)  # record the rooms entrance
            self.roomEntranceSet = True  # say that the room entrance was recorded
        return self.roomEntrance

    # Dispose of level (Data, set everything to inactive)
    def dispose(self):
        for spawner in range(len(self.zombieSpawners)):
            self.zombieSpawners[spawner].dispose()
        self.zombieManager.dispose(
        )  # dispose of all zombies and zombieManager
        self.bulletManager.dispose(
        )  # dispose of all bullets and bulletManager
        self.stringManager.dispose()  # dispose of all the strings
        self.bloodExplosionManager.dispose()  # dispose of all blood explosions
        self.zombieParticleManager.dispose()  # dispose of all zombie particles
Esempio n. 3
0
class ZombieSpawner(object):

    # Call Constructor
    def __init__(self, x, y, zombieManager, maxZombies, spawnTimeMin,
                 spawnTimeMax, minVel, maxVel, hp):
        self.x = x
        self.y = y
        self.zombieManager = zombieManager

        self.maxZombies = maxZombies
        self.zombiesSpawned = 0

        self.spawnTimeMin = spawnTimeMin
        self.spawnTimeMax = spawnTimeMax

        self.minVel = minVel
        self.maxVel = maxVel
        self.hp = hp

        self.spawnZombie = False
        self.zombieSpawned = True

        self.firstZombieSpawned = False

        self.spawnerActive = True

    # Get spawner location x
    def getX(self):
        return self.x

    # Get spawner location y
    def getY(self):
        return self.y

    # Get activity
    def getActive(self):
        return self.spawnerActive

    # Get amount of zombies spawner has spawned:
    def getZombiesSpawned(self):
        return self.zombiesSpawned

    # Spawn Zombie
    def spawn(self):
        self.zombieSpawned = False  # say the zombie hasnt spawned yet
        vel = random.randint(self.minVel, self.maxVel)
        self.zombieManager.addZombie(self.x, self.y, vel, self.hp)
        self.zombiesSpawned += 1
        self.zombieSpawned = True

    # Begin Timer
    def begin(self, game_clock):
        self.timeTillNextZombie = random.randint(
            self.spawnTimeMin,
            self.spawnTimeMax)  # get the time till get a next zombie
        self.timer = Timer(
            self.timeTillNextZombie, game_clock.getElapsedSeconds()
        )  # begin the timer, at the length of the var above and give the currents seconds elapsed

    # Manage Zombies
    def update(self, game_clock):
        if self.spawnerActive == True:
            if self.zombiesSpawned <= self.maxZombies:
                if self.firstZombieSpawned == False:  # Check if the first zombie has spawned
                    self.firstZombieSpawned = True  # Say that the first zombie has spawned
                    self.spawn()  # spawn the first zombie
                if self.zombieSpawned == True:  # check if a zombie has finished spawning
                    self.zombieSpawned = False  # turn it off to reduce spam
                    self.begin(game_clock)  # begin a timer for the next zombie
                self.timer.update(
                    game_clock.getElapsedSeconds()
                )  # Update the timer, give the elapsed seconds in the _internal.gameclock
                if self.timer.getAlert(
                ) == True:  # check if the timer finished
                    self.spawn()  # spawn a new zombie
            else:
                self.spawnerActive = False

    # Dispose of spawner
    def dispose(self):
        self.spawnerActive = False
Esempio n. 4
0
class Blade(object):

    # Call constructor
    def __init__(self, rect, image, timeLength, secondsElapsed):
        self.Rect = rect
        
        self.image = image
        self.texture = self.image #self.texture is the transform of self.image
        
        self.rotation = 0
        self.rotationVel = 12

        self.active = True
        self.existenceTimer = Timer(timeLength, secondsElapsed)
        self.zombiesDamaged = []
        self.zombiesDamagedTimer = []

    # Get if the blade is active
    def getActive(self):
        return self.active

    # Set if the blade is active
    def setActive(self, activity):
        self.active = activity

    # Get the rectangle
    def getRect(self):
        return self.Rect

    # Set the rectangle
    def setRect(self, rect):
        self.Rect = rect

    # Check if you can attack a zombie
    def canAttack(self, zombie, secondsElapsed):
        for attackedZombie in range(len(self.zombiesDamaged)): # run a for loop for all attacked zombies
            if self.zombiesDamaged[attackedZombie] == zombie: # if the current attacked zombie is the current zombie
                for currentTimer in range(len(self.zombiesDamagedTimer)): # run a for loop for all zombie attack timers
                    timer = self.zombiesDamagedTimer[currentTimer] # unpack tuple
                    if timer[0] == zombie: # if the timer is assigned for this current zombie
                        break # end the timer for loop
                    timer = None # if it's reached this point than the zombie has no timer
                if timer != None: # if a timer was found
                    if timer[1].getAlert() == True: # if it's attack timer has finished
                        return True # say it can attack
                    else:
                        timer[1].update(secondsElapsed) # update the timer
                        return False # say it can't attack
        return True # if it's gotten to this point, it has no timer therefore can attack

    # Update the blade
    def update(self, zombies, player, game_Clock):
        if self.existenceTimer.getAlert() == True: # if the blade is finished
            self.setActive(False) # Set the blade inactive
        else: # UPDATE BLADE:
            self.existenceTimer.update(game_Clock.getElapsedSeconds()) # update existence timer
            # Rotate blade:
            self.rotation += self.rotationVel
            self.texture = pygame.transform.rotate(self.image, self.rotation)
            self.setRect(self.texture.get_rect(center=(self.getRect().center))) # Center blade
            
            # Check collision with zombies:
            for zombie in range(len(zombies)): # run a for loop for all zombies
                if zombies[zombie].getLifeStatus() == True: # check if the zombie is active
                    if self.Rect.colliderect(zombies[zombie].getRect()) == True and self.canAttack(zombie, game_Clock.getElapsedSeconds()) == True: # if the blade collides with the zombie and can attack it
                        zombies[zombie].changeHealth(-2)
                        player.addScore(2) # give the player 2 score
                        self.zombiesDamaged.append(zombie)
                        self.zombiesDamagedTimer.append((zombie, Timer(1, game_Clock.getElapsedSeconds()))) # set a timer for when the blade can attack the zombie again

    # Draw the blade
    def draw(self, canvas):
        canvas.blit(self.texture, self.getRect()) # draw blade with rotation
Esempio n. 5
0
class BulletManager(object):

    # Call constructor
    def __init__(self, screenWidth, screenHeight, notifyFont, ammo, magAmmo,
                 reloadTime):
        self.screenWidth = screenWidth
        self.screenHeight = screenHeight
        self.bullets = 0
        self.bulletx = []
        self.bullety = []
        self.bulletv = []
        self.bullet_isActive = []
        self.bulletType = []
        self.bulletSpeed = 8
        self.bulletStartingRadius = 5
        self.bulletRadius = []
        self.bouncyBulletBounces = []

        self.reloading = False

        self.magSize = ammo  # set the max ammo in a mag
        self.ammo = ammo  # ammo per mag
        self.magAmmo = magAmmo  # ammo in magazines
        self.magAmmoDeducted = 0  # ammo taken from magazines when reloading
        self.reloadTime = reloadTime

        self.reloadTimer = None
        self.outOfAmmo = False

        self.notifyFont = notifyFont

    # Create a bullet
    def createBullet(self, spawnx, spawny, mousex, mousey, accuracyLevel,
                     bulletType):
        self.bulletType.append(bulletType)
        self.bullet_isActive.append(True)
        displacement = 0
        if bulletType == "spray":
            displacement = random.randint(0, 16)
        self.bulletx.append(spawnx - displacement)  # Add a bullet x
        self.bullety.append(spawny - displacement)  # Add a bullet y
        self.bulletRadius.append(
            self.bulletStartingRadius)  # Add a bullet radius
        self.bouncyBulletBounces.append(0)

        if bulletType == "spray":
            _accuracyLevel = accuracyLevel + random.randint(20, 40)
        else:
            _accuracyLevel = accuracyLevel
        accuracy = random.randint(0, _accuracyLevel)
        playerVector = Vec2d(spawnx, spawny)
        directionX = random.randint(mousex - accuracy, mousex + accuracy)
        directionY = random.randint(mousey - accuracy, mousey + accuracy)
        directionVector = Vec2d(directionX, directionY)
        self.bulletv.append(Vec2d(directionVector -
                                  playerVector))  # Add a bullet vector
        self.bulletv[self.bullets] = self.bulletv[self.bullets].normalized(
        )  # normalize the vector just made to minimal length
        self.bullets += 1  # Increase bullets by 1 (for offset usage)
        self.ammo -= 1  # take away 1 ammo

    # Create a new bullet method
    def addBullet(self, spawnx, spawny, mousex, mousey, accuracyLevel,
                  bulletType):
        if self.ammo > 0:
            if bulletType == "spray":  # if the bullet type is spray
                for i in range(3):  # create 3 bullets
                    self.createBullet(spawnx, spawny, mousex, mousey,
                                      accuracyLevel, bulletType)
            else:
                self.createBullet(spawnx, spawny, mousex, mousey,
                                  accuracyLevel, bulletType)

    # Return bullet array location (x)
    def getBulletLocationX(self):
        return self.bulletx  # Bullet x array

    # Return bullet array location (y)
    def getBulletLocationY(self):
        return self.bullety  # Bullet y array

    # Get specific location of bullet (x, y)
    def getSpecificBulletLocation(self, b):
        return (self.bulletx[b], self.bullety[b])

    # Get specific vector of bullet (v)
    def getSpecificBulletVector(self, b):
        return self.bulletv[b]

    # Set specific vector bullet (v)
    def setSpecificBulletVector(self, b, v):
        self.bulletv[b] = v

    # Flip the Y direction of a bullet
    def flipVectorY(self, b):
        vectorX = self.getSpecificBulletVector(b).x
        vectorY = self.getSpecificBulletVector(b).y
        vectorY *= -1
        vector = (vectorX, vectorY)
        self.setSpecificBulletVector(b, Vec2d(vector))

    # Flip the X direction of a bullet
    def flipVectorX(self, b):
        vectorX = self.getSpecificBulletVector(b).x
        vectorY = self.getSpecificBulletVector(b).y
        vectorX *= -1
        vector = (vectorX, vectorY)
        self.setSpecificBulletVector(b, Vec2d(vector))

    # Flip the vector
    def flipVector(self, b):
        vector = self.getSpecificBulletVector(b)
        vector *= -1
        self.setSpecificBulletVector(b, Vec2d(vector))

    # Set specific location of bullet (x, y)
    def setSpecificBulletLocation(self, b, x, y):
        self.bulletx[b] = x
        self.bullety[b] = y

    # Return bullet array radius (r)
    def getBulletRadius(self, bullet):
        return self.bulletRadius[bullet]

    # Set the radius of the bullet
    def setBulletRadius(self, bullet, radius):
        self.bulletRadius[bullet] = radius

    # Set the activity of the bullet
    def setBulletActivity(self, bullet, activity):
        self.bullet_isActive[bullet] = activity

    # Get the activity of the bullet
    def getBulletActivity(self, bullet):
        return self.bullet_isActive[bullet]

    # Get the number of bullets existing
    def getBulletAmount(self):
        return self.bullets

    # Get bullet activity
    def getBulletActivity(self, b):
        return self.bullet_isActive[b]

    # Get bullet type
    def getBulletType(self, b):
        return self.bulletType[b]

    # Get the amount of times a bouncy bullet has bounced
    def getBouncyBulletBounces(self, b):
        return self.bouncyBulletBounces[b]

    # Add a bouncy bullet bounce
    def addBouncyBulletBounce(self, b):
        self.bouncyBulletBounces[b] += 1

    # Get ammunition
    def getAmmo(self):
        return self.ammo

    # Get magazines
    def getMagazines(self):
        return self.magAmmo

    # Add magazines
    def addMagazines(self, magazines):
        self.magAmmo += self.magSize * magazines

    # Get reloading status
    def isReloading(self):
        return self.reloading

    # Lunge a bouncy bullet away from the block
    def lungeBullet(self, b):
        bulletpos = self.bulletx[b], self.bullety[b]  # throw loc in tuple
        bulletpos += self.bulletv[b] * (self.bulletRadius[b] * 6
                                        )  # add to the tuple via vector tuple
        self.bulletx[b], self.bullety[
            b] = bulletpos  # unpack tuple back to original variables

    # Reload ammo
    def _reload(self, game_Clock, stringManager, soundManager):
        soundManager.playSound("reload")  # play the reload sound
        self.reloadTimer = Timer(
            self.reloadTime,
            game_Clock.getElapsedSeconds())  # start the reloading timer
        self.reloading = True  # notify that they are currently reloading
        if self.magAmmo >= self.magSize:  # if they have enough ammo to deduct a full magazine
            self.magAmmo -= self.magSize - self.ammo  # deduct 1 magazine
            self.magAmmoDeducted = self.magSize - self.ammo
        else:  # if not
            self.magAmmoDeducted = self.magAmmo
            self.magAmmo = 0
        self.ammo = 0  # take away ammo just incase the player reloading with excess
        self.outOfAmmo = False  # set that the player still has ammo

        # notify player through string manager that player is reloading #
        textInstance = self.notifyFont.render(
            "Reloading!...", True, (255, 0, 0))  # for size reference
        stringManager.addText("Reloading!...", (0, 255, 0), self.notifyFont,
                              ((self.screenWidth / 2) -
                               (textInstance.get_width() / 2),
                               self.screenHeight - (self.screenHeight * 0.75)),
                              4, -3, game_Clock)
        # notify player through string manager that player is reloading #

    # Update method:
    def update(self, game_Clock, stringManager, soundManager):
        if self.bullets > 0:  # check if bullets exist
            # UPDATE BULLETS:
            for i in range(self.bullets):  # for loop of all bullets
                if self.bullety[i] < -10 or self.bullety[
                        i] > self.screenHeight + 10:  #Only translate if it's within bounds
                    self.bullet_isActive[i] = False
                if self.bullet_isActive[i] == True:
                    """ Upcoming Tuple transfer. This may seem inefficient but it's like getters and setters, technically superior."""
                    bulletpos = self.bulletx[i], self.bullety[
                        i]  # throw loc in tuple
                    bulletpos += self.bulletv[
                        i] * self.bulletSpeed  # add to the tuple via vector tuple
                    self.bulletx[i], self.bullety[
                        i] = bulletpos  # unpack tuple back to original variables
                    """ This process is called a tuple transfer. Since it's safer then referencing the vector tuple by it's index"""
        # UPDATE RELOAD SYSTEM:
        if self.ammo <= 0:  # if the player finished their magazine
            if self.magAmmo > 0 and self.reloading == False:  # check if they have magazines left and the reload start wasn't already called
                self._reload(game_Clock, stringManager, soundManager)
            else:
                self.outOfAmmo = True  # set that the player is out of ammo
        if self.reloading == True:
            if self.reloadTimer.getAlert(
            ) == True:  # check if the timer finished
                self.ammo = self.magAmmoDeducted  # give player ammo
                self.reloading = False  # notify manager that it's finished reloading
                self.reloadTimer = None  # delete timer
            else:  # if the timer hasn't finished
                self.reloadTimer.update(
                    game_Clock.getElapsedSeconds())  # update timer

    # Draw method:
    def draw(self, canvas, playerRect):
        if self.bullets > 0:  # see if bullets exist
            for i in range(self.bullets):  # Loop for all bullets
                if self.bullet_isActive[
                        i] == True:  # Check if the bullet is active
                    b_rect = pygame.Rect(
                        (self.bulletx[i], self.bullety[i]),
                        (self.bulletRadius[i], self.bulletRadius[i]
                         ))  # Create temporary rectangle for current bullet
                    if b_rect.colliderect(
                            playerRect
                    ) != True:  # Condition: If the bullet rect isn't overlapping player rect
                        if self.getBulletType(i) == "normal":
                            color = (0, 0, 0)  # black
                        elif self.getBulletType(i) == "bouncy":
                            color = (255, 105, 180)  # hot pink
                        elif self.getBulletType(i) == "explosive":
                            color = (160, 82, 45)  # sienna brown
                        elif self.getBulletType(i) == "spray":
                            color = (255, 255, 0)  # yellow
                        elif self.getBulletType(i) == "flame":
                            color = (255, 69, 0)  # orangish red
                        pygame.draw.circle(
                            canvas, color,
                            (int(self.bulletx[i]), int(self.bullety[i])),
                            self.getBulletRadius(i), 0)  # Draw bullet

    # Dispose of all bullets:
    def dispose(self):
        for bullet in range(
                self.getBulletAmount()):  # run a for loop for all bullets
            self.setBulletActivity(bullet, False)  # kill all bullets
        # Kill all arrays
        self.bulletRadius = []
        self.bulletx = []
        self.bullety = []
        self.bulletv = []
        self.bullet_isActive = []