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
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()
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
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)
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)