def __init__(self, owner, baseSpeed, timeToReload, damage):
        super().__init__(owner,
                         baseSpeed,
                         timeToReload,
                         damage,
                         bulletRadius=5)

        self.soundEffect = QSoundEffect(self)
        self.soundEffect.setSource(QUrl.fromLocalFile("sounds/handgun.wav"))
        self.soundEffect.setVolume(0.4)
    def __init__(self, owner, baseSpeed, timeToReload, damage, bulletsPerShot):
        super().__init__(owner,
                         baseSpeed,
                         timeToReload,
                         damage,
                         bulletRadius=3)

        self.bulletsPerShot = bulletsPerShot

        self.soundEffect = QSoundEffect(self)
        self.soundEffect.setSource(QUrl.fromLocalFile("sounds/shotgun.wav"))
        self.soundEffect.setVolume(0.1)
    def __init__(self, owner, baseSpeed, timeToReload, damage, bulletsPerShot):
        super().__init__(owner,
                         baseSpeed,
                         timeToReload,
                         damage,
                         bulletRadius=6)

        self.bulletsPerShot = bulletsPerShot
        self.explosionBullets = []

        self.soundEffect_launcher = QSoundEffect(self)
        self.soundEffect_launcher.setSource(
            QUrl.fromLocalFile("sounds/grenade_launcher.wav"))
        self.soundEffect_launcher.setVolume(0.1)

        self.soundEffect_explosion = QSoundEffect(self)
        self.soundEffect_explosion.setSource(
            QUrl.fromLocalFile("sounds/explosion.wav"))
        self.soundEffect_explosion.setVolume(1.5)
    def getSoundEffect(self, filePath):

        if filePath in self.soundEffects:
            return self.soundEffects[filePath]
        else:
            soundEffect = QSoundEffect()
            soundEffect.setSource(QUrl.fromLocalFile(filePath))
            soundEffect.setVolume(0.1)
            self.soundEffects[filePath] = soundEffect
            return soundEffect
Example #5
0
    def __init__(self,
                 id,
                 spawn_x,
                 spawn_y,
                 aov,
                 v_max,
                 maxHealth,
                 r=30,
                 alpha=0,
                 texturePath="textures/robot_base.png"):

        super().__init__()

        self.id = id
        self.pos = QVector2D(spawn_x, spawn_y)
        self.spawn = QVector2D(spawn_x, spawn_y)
        self.aov = aov
        self.r = r
        self.alpha = alpha  # unit: degrees
        self.texture = QPixmap(texturePath)

        self.a = 0  # unit: pixels/second^2
        self.a_max = A_MAX  # unit: pixels/second^2
        self.v = 0  # unit: pixels/second
        self.v_max = v_max  # unit pixels/second

        self.a_alpha = 0  # unit: degrees/second^2
        self.a_alpha_max = A_ALPHA_MAX  # unit: degrees/second^2
        self.v_alpha = 0  # unit: degrees/second
        self.v_alpha_max = V_ALPHA_MAX  # unit: degrees/second

        self.guns = []
        self.selected_gun = None
        self.currentGunIndex = 0

        self.maxHealth = maxHealth
        self.health = maxHealth
        self.healthBar = HealthBar(maxHealth)
        self.active = True
        self.protected = False
        self.timeToRespawn = 0
        self.protectionTime = 0

        self.deathSound = QSoundEffect(self)
        self.deathSound.setSource(QUrl.fromLocalFile("sounds/death.wav"))
        self.deathSound.setVolume(0.1)

        self.emptyGunSound = QSoundEffect(self)
        self.emptyGunSound.setSource(
            QUrl.fromLocalFile("sounds/empty_gun.wav"))
        self.emptyGunSound.setVolume(0.1)

        self.respawnSound = QSoundEffect(self)
        self.respawnSound.setSource(QUrl.fromLocalFile("sounds/respawn.wav"))
        self.respawnSound.setVolume(0.1)
class Shotgun(Gun):
    def __init__(self, owner, baseSpeed, timeToReload, damage, bulletsPerShot):
        super().__init__(owner,
                         baseSpeed,
                         timeToReload,
                         damage,
                         bulletRadius=3)

        self.bulletsPerShot = bulletsPerShot

        self.soundEffect = QSoundEffect(self)
        self.soundEffect.setSource(QUrl.fromLocalFile("sounds/shotgun.wav"))
        self.soundEffect.setVolume(0.1)

    def update(self, deltaTime, levelMatrix, robotsDict):
        super().update(deltaTime, levelMatrix, robotsDict)

        for bullet in self.bullets:
            bullet.update(deltaTime, levelMatrix, robotsDict)
            if bullet.isTooOld() or bullet.collidesWithWorld(levelMatrix):
                self.bullets.remove(bullet)
                del bullet
                continue

            robot = bullet.collidesWithRobots(robotsDict)
            if robot != None:
                self.hitSignal.emit(robot.id, self.damage)
                self.bullets.remove(bullet)
                del bullet

    def fire(self, direction):
        MAX_SCATTER_ANGLE = 10
        MAX_SCATTER_SPEED = 20

        if self.readyToFire():
            bulletSpeed = self.baseSpeed + max(0, self.owner.v)
            baseAngle = vectorToAngle(direction)
            for i in range(self.bulletsPerShot):
                scatteredAngle = baseAngle + random.uniform(
                    -MAX_SCATTER_ANGLE, MAX_SCATTER_ANGLE)
                scatteredDirection = angleToVector(scatteredAngle)
                scatteredSpeed = bulletSpeed + random.uniform(
                    -MAX_SCATTER_SPEED, MAX_SCATTER_SPEED)
                self.bullets.append(
                    Bullet(self.owner, self.pos, scatteredDirection,
                           scatteredSpeed, self.bulletRadius, 1))

            self.resetTimer()
            self.soundEffect.play()
class Handgun(Gun):
    def __init__(self, owner, baseSpeed, timeToReload, damage):
        super().__init__(owner,
                         baseSpeed,
                         timeToReload,
                         damage,
                         bulletRadius=5)

        self.soundEffect = QSoundEffect(self)
        self.soundEffect.setSource(QUrl.fromLocalFile("sounds/handgun.wav"))
        self.soundEffect.setVolume(0.4)

    def update(self, deltaTime, levelMatrix, robotsDict):
        super().update(deltaTime, levelMatrix, robotsDict)

        for bullet in self.bullets:
            bullet.update(deltaTime, levelMatrix, robotsDict)
            if bullet.collidesWithWorld(levelMatrix):
                self.bullets.remove(bullet)
                del bullet
                continue

            robot = bullet.collidesWithRobots(robotsDict)
            if robot != None:
                self.hitSignal.emit(robot.id, self.damage)
                self.bullets.remove(bullet)
                del bullet

    def fire(self, direction):
        if self.readyToFire():
            bulletSpeed = self.baseSpeed + max(0, self.owner.v)
            bullet = Bullet(self.owner, self.pos, direction, bulletSpeed,
                            self.bulletRadius, 10)
            self.bullets.append(bullet)
            self.resetTimer()
            self.soundEffect.play()
class GrenadeLauncher(Gun):
    def __init__(self, owner, baseSpeed, timeToReload, damage, bulletsPerShot):
        super().__init__(owner,
                         baseSpeed,
                         timeToReload,
                         damage,
                         bulletRadius=6)

        self.bulletsPerShot = bulletsPerShot
        self.explosionBullets = []

        self.soundEffect_launcher = QSoundEffect(self)
        self.soundEffect_launcher.setSource(
            QUrl.fromLocalFile("sounds/grenade_launcher.wav"))
        self.soundEffect_launcher.setVolume(0.1)

        self.soundEffect_explosion = QSoundEffect(self)
        self.soundEffect_explosion.setSource(
            QUrl.fromLocalFile("sounds/explosion.wav"))
        self.soundEffect_explosion.setVolume(1.5)

    def update(self, deltaTime, levelMatrix, robotsDict):
        super().update(deltaTime, levelMatrix, robotsDict)

        for bullet in self.bullets:
            bullet.update(deltaTime, levelMatrix, robotsDict)
            if bullet.isTooOld() or bullet.collidesWithWorld(levelMatrix):
                self.explode()
                self.bullets.remove(bullet)
                del bullet
                continue

            robot = bullet.collidesWithRobots(robotsDict)
            if robot != None:
                self.explode()
                self.hitSignal.emit(robot.id, self.damage)
                self.bullets.remove(bullet)
                del bullet

        for bullet in self.explosionBullets:
            bullet.update(deltaTime, levelMatrix, robotsDict)
            if bullet.isTooOld() or bullet.collidesWithWorld(levelMatrix):
                self.explosionBullets.remove(bullet)
                del bullet
                continue

            robot = bullet.collidesWithRobots(robotsDict)
            if robot != None and robot.id != self.owner.id:
                self.hitSignal.emit(robot.id, self.damage)
                self.explosionBullets.remove(bullet)
                del bullet
                continue

    def draw(self, qp):
        super().draw(qp)

        for bullet in self.explosionBullets:
            bullet.draw(qp)

    def fire(self, direction):
        if self.readyToFire():
            bulletSpeed = self.baseSpeed + max(0, self.owner.v)
            bullet = Bullet(self.owner, self.pos, direction, bulletSpeed,
                            self.bulletRadius, 1)
            self.bullets.append(bullet)

            self.resetTimer()
            self.soundEffect_launcher.play()

    def explode(self):
        MAX_SCATTER_ANGLE = 360
        MAX_SCATTER_SPEED = 20
        BULLET_SPEED = 300
        BULLET_MAX_AGE = 0.5

        for i in range(self.bulletsPerShot):
            scatteredAngle = random.uniform(-MAX_SCATTER_ANGLE,
                                            MAX_SCATTER_ANGLE)
            scatteredDirection = angleToVector(scatteredAngle)
            scatteredSpeed = BULLET_SPEED + random.uniform(
                -MAX_SCATTER_SPEED, MAX_SCATTER_SPEED)
            newBullet = Bullet(self.owner, self.bullets[0].pos,
                               scatteredDirection, scatteredSpeed,
                               self.bulletRadius - 3, BULLET_MAX_AGE)
            self.explosionBullets.append(
                Bullet(self.owner, self.bullets[0].pos, scatteredDirection,
                       scatteredSpeed, self.bulletRadius - 3, 0.5))

        self.soundEffect_explosion.play()
Example #9
0
class Game(QObject):
    #Modes
    DEAL_ONE = 0
    DEAL_FOUR = 1

    logSignal = pyqtSignal(str)
    sweepSignal = pyqtSignal()

    def __init__(self, pointlimit, deal_mode=0):
        super(Game, self).__init__()
        self.pointlimit = pointlimit
        self.deal_mode = deal_mode
        self.players = []
        self.deck = Deck()
        self.table = []
        self.round = 0
        self.current_player = None
        self.last_pick = None
        self.cardDraw = 1  #multiplier to account for draw with cards
        self.spadeDraw = 1  #multiplier to account for draw with spades
        self.one_human = False  #True if only one human player in game
        self.setup_sound()

    def setup_sound(self):
        self.deal_sound = QSoundEffect()
        self.deal_sound.setSource(QUrl.fromLocalFile('sound/playcard.wav'))

        if self.deal_mode == Game.DEAL_FOUR:
            self.deal_sound.setLoopCount(4)

    def add_player(self, name, difficulty):
        if difficulty == 5:
            self.players.append(Player(name))
        else:
            self.players.append(AIPlayer(name, difficulty))

        self.one_human = self.is_only_one_human()

    def new_round(self):
        self.deck = Deck()
        self.deck.populate()
        self.deck.shuffle()
        self.table = []
        for player in self.players:
            player.deck = []
            player.sweeps = 0
        self.round += 1
        self.current_player = self.players[0]

    def next_player(self):
        self.current_player = self.players[
            (self.players.index(self.current_player) + 1) % len(self.players)]

    def do_action(self):
        player = self.current_player
        hand_card = None
        for card in player.hand:
            if card.selected:
                hand_card = card

        if hand_card == None:
            return False

        table_cards = [card for card in self.table if card.selected]

        if len(table_cards) == 0:
            player.hand.remove(hand_card)
            self.table.append(hand_card)
            self.logSignal.emit('{} trailed with {}\n'.format(
                player, hand_card))

        else:
            if type(player) is Player:
                if not is_valid_combo(hand_card.get_hand_value(),
                                      [c.value for c in table_cards]):
                    self.logSignal.emit('Invalid move\n')
                    return False

            for c in table_cards:
                player.deck.append(c)
                self.table.remove(c)
            player.hand.remove(hand_card)
            player.deck.append(hand_card)
            self.last_pick = player

            self.logSignal.emit('{} used {} to capture:\n{}\n'.format(
                player, hand_card, '\n'.join([str(c) for c in table_cards])))

            if len(self.table) == 0:
                player.sweeps += 1
                self.logSignal.emit('Sweep!\n')
                self.sweepSignal.emit()

        return True

    def select_move_for_ai(self):
        player = self.current_player
        hand_c, table_c = get_move(player.hand, self.table, player.difficulty)

        hand_c.select()

        if table_c != None:
            for c in table_c:
                c.select()

    def initial_deal(self):
        #Deals four cards to each player and the table, two cards at a time
        self.logSignal.emit('\nDealing new round....\n\n')
        for i in range(2):
            for player in self.players:
                player.hand.append(self.deck.draw_top_card())
                player.hand.append(self.deck.draw_top_card())
            self.table.append(self.deck.draw_top_card())
            self.table.append(self.deck.draw_top_card())

    def deal(self):
        if self.deal_mode == Game.DEAL_ONE:
            self.deal_one()

        if self.deal_mode == Game.DEAL_FOUR:
            self.deal_four()

    def deal_one(self):
        if self.deck.get_size() > 0:
            self.current_player.hand.append(self.deck.draw_top_card())
            self.deal_sound.play()
            if self.deck.get_size() == 0:
                self.logSignal.emit('\n\nLast deal!\n')

    def deal_four(self):  #Deal four cards to each player, two cards at a time
        if sum([len(p.hand)
                for p in self.players]) == 0 and self.deck.get_size() > 0:
            self.logSignal.emit('\n\nDealing new cards...\n')
            self.deal_sound.play()

            for i in range(2):
                for player in self.players:
                    player.hand.append(self.deck.draw_top_card())
                    player.hand.append(self.deck.draw_top_card())

            if self.deck.get_size() == 0:
                self.logSignal.emit('Last deal!\n')

    def end_round(self):
        player = self.last_pick
        table_cards = [card for card in self.table]

        self.logSignal.emit(
            '\n\n{} was the last to capture and received:\n{}\n'.format(
                player, '\n'.join([str(c) for c in self.table])))
        self.logSignal.emit('\nEnd of round\n')

        for c in table_cards:
            player.deck.append(c)
            self.table.remove(c)

        self.count_points()

        if max([p.score for p in self.players]) >= self.pointlimit:
            self.logSignal.emit('\n\nGame over, score limit reached\n')
            self.logSignal.emit('Final scores:\n--------------\n')
            for p in reversed(sorted(self.players, key=lambda p: p.score)):
                self.logSignal.emit('{}: {}\n'.format(p.name, p.score))
            return True  #Return True if game ended

        self.players.append(self.players.pop(0))  #rotate players
        return False

    def count_points(self):
        points = []

        for i in range(len(self.players)):
            p = 0
            player = self.players[i]
            for card in player.deck:
                if card.value == 10 and card.suit == 'Diamonds':
                    p += 2
                if card.value == 2 and card.suit == 'Spades':
                    p += 1
                if card.value == 1:
                    p += 1

            points.append(p + player.sweeps)

        cards = [len(p.deck) for p in self.players]
        spades = [
            len([c for c in p.deck if c.suit == 'Spades'])
            for p in self.players
        ]

        #Count the winner for most cards and spades
        #If there is a draw, the corresponding multiplier is incremented and points will be carried to next round

        if cards.count(max(cards)) == 1:
            points[cards.index(max(cards))] += 1 * self.cardDraw
            self.cardDraw = 1
        else:
            self.cardDraw += 1

        if spades.count(max(spades)) == 1:
            points[spades.index(max(spades))] += 2 * self.spadeDraw
            self.spadeDraw = 1
        else:
            self.spadeDraw += 1

        for i in range(len(self.players)):
            self.players[i].score += points[i]
            self.logSignal.emit('{} received {} points.\n'.format(
                self.players[i].name, points[i]))

    def is_only_one_human(self):  #Determine if only one human player in game
        count = 0
        for p in self.players:
            if type(p) is Player:
                count += 1

        if count == 1:
            return True

        return False
Example #10
0
    def setup_sound(self):
        self.deal_sound = QSoundEffect()
        self.deal_sound.setSource(QUrl.fromLocalFile('sound/playcard.wav'))

        if self.deal_mode == Game.DEAL_FOUR:
            self.deal_sound.setLoopCount(4)
Example #11
0
class BaseRobot(QObject):

    # This will be emittet once at the beginning of the game to tell the controller the values a_max and a_alpha_max
    robotSpecsSignal = pyqtSignal(float, float, float, float)

    # This will be emittet every tick to tell the controller current values of x, y, alpha, v, v_alpha, readyToShoot
    robotInfoSignal = pyqtSignal(float, float, float, float, float, bool)

    # This will be emitted every 10th tick
    robotsInViewSignal = pyqtSignal(dict)
    wallsInViewSignal = pyqtSignal(list)

    def __init__(self,
                 id,
                 spawn_x,
                 spawn_y,
                 aov,
                 v_max,
                 maxHealth,
                 r=30,
                 alpha=0,
                 texturePath="textures/robot_base.png"):

        super().__init__()

        self.id = id
        self.pos = QVector2D(spawn_x, spawn_y)
        self.spawn = QVector2D(spawn_x, spawn_y)
        self.aov = aov
        self.r = r
        self.alpha = alpha  # unit: degrees
        self.texture = QPixmap(texturePath)

        self.a = 0  # unit: pixels/second^2
        self.a_max = A_MAX  # unit: pixels/second^2
        self.v = 0  # unit: pixels/second
        self.v_max = v_max  # unit pixels/second

        self.a_alpha = 0  # unit: degrees/second^2
        self.a_alpha_max = A_ALPHA_MAX  # unit: degrees/second^2
        self.v_alpha = 0  # unit: degrees/second
        self.v_alpha_max = V_ALPHA_MAX  # unit: degrees/second

        self.guns = []
        self.selected_gun = None
        self.currentGunIndex = 0

        self.maxHealth = maxHealth
        self.health = maxHealth
        self.healthBar = HealthBar(maxHealth)
        self.active = True
        self.protected = False
        self.timeToRespawn = 0
        self.protectionTime = 0

        self.deathSound = QSoundEffect(self)
        self.deathSound.setSource(QUrl.fromLocalFile("sounds/death.wav"))
        self.deathSound.setVolume(0.1)

        self.emptyGunSound = QSoundEffect(self)
        self.emptyGunSound.setSource(
            QUrl.fromLocalFile("sounds/empty_gun.wav"))
        self.emptyGunSound.setVolume(0.1)

        self.respawnSound = QSoundEffect(self)
        self.respawnSound.setSource(QUrl.fromLocalFile("sounds/respawn.wav"))
        self.respawnSound.setVolume(0.1)

    def equipWithGuns(self, *guns):
        self.guns = guns
        self.selected_gun = guns[0]

    def connectSignals(self):
        self.robotSpecsSignal.connect(self.controller.receiveRobotSpecs)
        self.robotInfoSignal.connect(self.controller.receiveRobotInfo)
        self.robotsInViewSignal.connect(self.controller.receiveRobotsInView)
        self.wallsInViewSignal.connect(self.controller.receiveWallsInView)

        self.controller.fullStopSignal.connect(self.fullStop)
        self.controller.fullStopRotationSignal.connect(self.fullStopRotation)
        self.controller.shootSignal.connect(self.shoot)
        self.controller.switchToGunSignal.connect(self.swithToGun)
        self.controller.nextGunSignal.connect(self.nextGun)

    def draw(self, qp):

        qp.save()
        qp.translate(self.x, self.y)
        qp.rotate(self.alpha)
        source = QRectF(0, 0, 64, 64)
        target = QRectF(-self.r, -self.r, 2 * self.r, 2 * self.r)
        qp.drawPixmap(target, self.texture, source)
        qp.restore()

        for gun in self.guns:
            gun.draw(qp)

        qp.setPen(QPen(Qt.black))
        qp.setFont(ID_FONT)
        qp.drawText(self.boundingRect(), Qt.AlignCenter,
                    ' ')  # This shold not exist

        if self.active:
            self.healthBar.draw(qp)
        else:
            qp.drawText(self.boundingRect(), Qt.AlignCenter,
                        str(int(self.timeToRespawn) + 1))

    def drawDebugLines(self, qp):
        qp.setBrush(QBrush(Qt.NoBrush))
        pen = QPen(Qt.red)
        pen.setWidthF(1.5)
        qp.setPen(pen)
        qp.drawPath(self.view_cone())

    def update(self, deltaTime, levelMatrix, robotsDict):

        # Fetch acceleration values from your thread
        self.a, self.a_alpha = self.controller.fetchValues()
        # But not too much
        self.a = minmax(self.a, -self.a_max, self.a_max)
        self.a_alpha = minmax(self.a_alpha, -self.a_alpha_max,
                              self.a_alpha_max)

        # Apply acceleration
        self.v += self.a * deltaTime
        self.v_alpha += self.a_alpha * deltaTime
        # But not too much
        self.v = minmax(self.v, -self.v_max, self.v_max)
        self.v_alpha = minmax(self.v_alpha, -self.v_alpha_max,
                              self.v_alpha_max)

        obstacles = self.collisionRadar(levelMatrix)
        self.collideWithWalls(obstacles)

        # Apply velocity
        if self.protected:
            self.protectionTime -= deltaTime
            if self.protectionTime <= 0:
                self.protected = False

        if self.active:
            self.pos += self.v * deltaTime * self.direction()
            self.alpha += self.v_alpha * deltaTime
            self.alpha %= 360
        else:
            self.timeToRespawn -= deltaTime
            if self.timeToRespawn <= 0:
                self.respawn()

        self.collideWithRobots(robotsDict, obstacles)

        # send current information to the controller
        self.robotInfoSignal.emit(self.x, self.y, self.alpha, self.v,
                                  self.v_alpha, self.readyToFire())

        for gun in self.guns:
            gun.update(deltaTime, levelMatrix, robotsDict)

        # Health bar stuff
        healthBarPosition = self.pos - QVector2D(0, self.r + 10)
        self.healthBar.update(self.health, healthBarPosition)
        color_g = int(255 * self.health / self.maxHealth)
        color_r = 255 - color_g
        self.healthBar.setColor(QColor(color_r, color_g, 0))

    def collideWithWalls(self, obstacles):

        for rect in obstacles:

            overlap = circleRectCollision(self.pos, self.r, rect)
            if overlap:

                if abs(overlap.x()) > abs(overlap.y()):
                    self.translate(0, overlap.y())
                else:
                    self.translate(overlap.x(), 0)

                # Set speed to zero (almost)
                self.v = EPSILON_V

    def isColliding(self, obstacles):

        for rect in obstacles:
            if circleRectCollision(self.pos, self.r, rect):
                return True

        return False

    def collideWithRobots(self, robotsDict, obstacles):

        for id in robotsDict:
            if id != self.id:
                robot = robotsDict[id]
                overlap_vec = circleCircleCollision(self.pos, self.r,
                                                    robot.pos, robot.r)

                if overlap_vec:
                    if self.isColliding(obstacles):
                        robot.pos -= overlap_vec

                    else:
                        self.pos += overlap_vec / 2
                        robot.pos -= overlap_vec / 2

                    self.hook_collidedWith(robot)

    def hook_collidedWith(self, robot):
        pass

    def collisionRadar(self, levelMatrix):
        #Calculate Limits

        x_min = minmax(int((self.x - self.r - COLL_BUFFER) // 10), 0,
                       len(levelMatrix))
        x_max = minmax(int((self.x + self.r + COLL_BUFFER) // 10 + 1), 0,
                       len(levelMatrix))
        y_min = minmax(int((self.y - self.r - COLL_BUFFER) // 10), 0,
                       len(levelMatrix))
        y_max = minmax(int((self.y + self.r + COLL_BUFFER) // 10 + 1), 0,
                       len(levelMatrix))

        #Fill obstacle list
        obstacles = []
        for y in range(y_min, y_max):
            for x in range(x_min, x_max):
                if not levelMatrix[y][x].walkable():
                    obstacles.append(QRectF(x * 10, y * 10, 10, 10))

        return obstacles

    ### Slots

    def fullStop(self):

        if abs(self.v) < EPSILON_V:
            self.v = 0
            return True
        else:
            return False

    def fullStopRotation(self):

        if abs(self.v_alpha) < EPSILON_V_ALPHA:
            self.v_alpha = 0
            return True
        else:
            return False

    def shoot(self):
        if self.selected_gun != None and self.active:
            if self.selected_gun.readyToFire():
                self.selected_gun.fire(self.direction())

            else:
                self.emptyGunSound.play()

    def swithToGun(self, index):
        if index < len(self.guns):
            self.currentGunIndex = index
            self.selected_gun = self.guns[index]

    def nextGun(self, i):
        print('hello')
        self.currentGunIndex = (self.currentGunIndex + i) % len(self.guns)
        self.selected_gun = self.guns[self.currentGunIndex]

    def dealDamage(self, damage):
        if self.active and not self.protected:
            self.health = max(0, self.health - damage)
            if self.health == 0:
                self.active = False
                self.timeToRespawn = 3  # 3 seconds until respawn
                self.deathSound.play()

    def respawn(self):
        self.pos.setX(self.spawn.x())
        self.pos.setY(self.spawn.y())
        self.health = self.maxHealth
        self.active = True
        self.protected = True
        self.protectionTime = 3
        self.respawnSound.play()

    ### properties and helperfunction

    def direction(self):
        return QVector2D(math.cos(self.alpha_radians),
                         math.sin(self.alpha_radians))

    def boundingRect(self):
        return QRectF(self.x - self.r, self.y - self.r, 2 * self.r, 2 * self.r)

    def readyToFire(self):
        if self.selected_gun == None:
            return False
        else:
            return self.selected_gun.readyToFire()

    def shape(self):
        shape = QPainterPath()
        shape.addEllipse(self.pos.toPointF(), self.r, self.r)
        return shape

    def view_cone(self):
        path = QPainterPath()
        a = self.pos.toPointF()
        b = a + QPointF(5000 * math.cos(math.radians(self.alpha + self.aov)),
                        5000 * math.sin(math.radians(self.alpha + self.aov)))
        c = a + QPointF(5000 * math.cos(math.radians(self.alpha - self.aov)),
                        5000 * math.sin(math.radians(self.alpha - self.aov)))
        path.addPolygon(QPolygonF([a, b, c]))
        path.closeSubpath()
        return path

    def translate(self, x, y):
        self.pos += QVector2D(x, y)

    ### properties

    def get_alpha_radians(self):
        return math.radians(self.alpha)

    def set_alpha_radians(self, new_alpha):
        self.alpha = math.degrees(new_alpha)

    alpha_radians = property(get_alpha_radians, set_alpha_radians)

    def setController(self, controller):
        self._controller = controller

    def getController(self):
        return self._controller

    controller = property(getController, setController)

    def get_x(self):
        return self.pos.x()

    def set_x(self, new_x):
        self.pos.setX(new_x)

    x = property(get_x, set_x)

    def get_y(self):
        return self.pos.y()

    def set_y(self, new_y):
        self.pos.setY(new_y)

    y = property(get_y, set_y)