def _getTorpedoToFire(self, enemy: Enemy, enterprise: Enterprise) -> BaseEnemyTorpedo: """ Args: enemy: The Klingon, Commander, or Super Commander that is firing enterprise: Where Captain Kirk is waiting Returns: A torpedo of the correct kind """ # # Use the enterprise arcade position rather than compute the sector center; That way we # can use Arcade collision detection # klingonPoint: ArcadePoint = ArcadePoint(x=enemy.center_x, y=enemy.center_y) enterprisePoint: ArcadePoint = ArcadePoint(x=enterprise.center_x, y=enterprise.center_y) speeds: TorpedoSpeeds = self._intelligence.getTorpedoSpeeds(playerType=self._gameState.playerType) klingonTorpedo: KlingonTorpedo = KlingonTorpedo(speed=speeds.klingon) klingonTorpedo.center_x = klingonPoint.x klingonTorpedo.center_y = klingonPoint.y klingonTorpedo.inMotion = True klingonTorpedo.destinationPoint = enterprisePoint klingonTorpedo.firedFromPosition = enemy.gameCoordinates klingonTorpedo.firedBy = enemy.id klingonTorpedo.followers = self.torpedoFollowers return klingonTorpedo
def _getTorpedoToFire(self, enemy: Enemy, enterprise: Enterprise) -> BaseEnemyTorpedo: """ Must be implemented by subclass to create correct type of torpedo Args: enemy: The Klingon, Commander, or Super Commander that is firing enterprise: Where Captain Kirk is waiting Returns: A torpedo of the correct kind """ klingonPoint: ArcadePoint = ArcadePoint(x=enemy.center_x, y=enemy.center_y) enterprisePoint: ArcadePoint = ArcadePoint(x=enterprise.center_x, y=enterprise.center_y) speeds: TorpedoSpeeds = self._intelligence.getTorpedoSpeeds( playerType=self._gameState.playerType) commanderTorpedo: CommanderTorpedo = CommanderTorpedo( speed=speeds.commander) commanderTorpedo.center_x = klingonPoint.x commanderTorpedo.center_y = klingonPoint.y commanderTorpedo.inMotion = True commanderTorpedo.destinationPoint = enterprisePoint commanderTorpedo.firedFromPosition = enemy.gameCoordinates commanderTorpedo.firedBy = enemy.id commanderTorpedo.followers = self.torpedoFollowers return commanderTorpedo
def fireEnterpriseTorpedoesAtKlingons(self, quadrant: Quadrant): if self._gameState.torpedoCount <= 0: self._soundMachine.playSound(SoundType.UnableToComply) self._messageConsole.displayMessage( "You are out of photon torpedoes!!") return # Take a shortcut out of here self._messageConsole.displayMessage("Firing Torpedoes!!") enterprise: Enterprise = quadrant.enterprise enemies: Enemies = Enemies([]) enemies.extend(quadrant.klingons) enemies.extend(quadrant.commanders) enemies.extend(quadrant.superCommanders) numberOfEnemies: int = len(enemies) if numberOfEnemies == 0: self._messageConsole.displayMessage( "Don't waste torpedoes. Nothing to fire at") self._soundMachine.playSound(SoundType.Inaccurate) else: startingPoint: ArcadePoint = ArcadePoint(x=enterprise.center_x, y=enterprise.center_y) # # Only fire as many torpedoes as we have available # TODO: Fire only 'N' torpedoes at randomly selected enemies # for enemy in enemies: endPoint: ArcadePoint = ArcadePoint(x=enemy.center_x, y=enemy.center_y) clearLineOfSight: LineOfSightResponse = self._doWeHaveLineOfSight( quadrant, startingPoint, endPoint) if clearLineOfSight.answer is True: self._pointAtEnemy(enterprise=enterprise, enemy=enemy) if self._intelligence.rand( ) <= self._gameSettings.photonTorpedoMisfireRate: self._messageConsole.displayMessage( f'Torpedo pointed at {enemy} misfired') self._soundMachine.playSound( SoundType.PhotonTorpedoMisfire) else: self._fireTorpedo(enterprise=enterprise, enemy=enemy) self._soundMachine.playSound( SoundType.PhotonTorpedoFired) self._gameState.torpedoCount -= 1 else: msg: str = ( f'Cannot fire at {enemy.id} ' f'because a {clearLineOfSight.obstacle.id} is in the way' ) self._messageConsole.displayMessage(message=msg) self.logger.info(msg) enterprise.angle = 0
def testComputeCenterPointMid(self): startPoint: ArcadePoint = ArcadePoint(x=0, y=0) endPoint: ArcadePoint = ArcadePoint(x=400, y=400) midPoint: ArcadePoint = Computer.computeCenterPoint(start=startPoint, end=endPoint) self.logger.debug(f'{startPoint=} {endPoint=} {midPoint=}') expectedPoint: ArcadePoint = ArcadePoint(x=200, y=200) self.assertEqual(expectedPoint, midPoint, 'Mid Center point does not match')
def testComputeCenterPointLong(self): startPoint: ArcadePoint = ArcadePoint(x=10, y=10) endPoint: ArcadePoint = ArcadePoint(x=SCREEN_HEIGHT-10, y=QUADRANT_GRID_WIDTH-10) midPoint: ArcadePoint = Computer.computeCenterPoint(start=startPoint, end=endPoint) self.logger.debug(f'{startPoint=} {endPoint=} {midPoint=}') expectedPoint: ArcadePoint = ArcadePoint(x=415, y=320) self.assertEqual(expectedPoint, midPoint, 'Long Center point does not match')
def testComputeAngleToTargetDirectWest(self): """ East is right """ shooter: ArcadePoint = ArcadePoint(x=500.0, y=100.0) deadMeat: ArcadePoint = ArcadePoint(x=100.0, y=100.0) actualAngle: float = self.smarty.computeAngleToTarget(shooter=shooter, deadMeat=deadMeat) expectedAngle: float = 180.0 self.assertEqual(expectedAngle, actualAngle, 'Bad computation')
def testComputeAngleToTargetDirectNorth(self): """ North is up """ shooter: ArcadePoint = ArcadePoint(x=100, y=100) deadMeat: ArcadePoint = ArcadePoint(x=100, y=500) actualAngle = self.smarty.computeAngleToTarget(shooter=shooter, deadMeat=deadMeat) expectedAngle = 90 self.assertEqual(expectedAngle, actualAngle, 'Bad computation')
def testComputeAngleToTargetDirectSouth(self): """ South is down """ shooter: ArcadePoint = ArcadePoint(x=100.0, y=500.0) deadMeat: ArcadePoint = ArcadePoint(x=100.0, y=100.0) actualAngle: float = self.smarty.computeAngleToTarget(shooter=shooter, deadMeat=deadMeat) expectedAngle: float = -90.0 self.assertEqual(expectedAngle, actualAngle, 'Bad computation')
def testDoMotion(self): currentPoint: ArcadePoint = ArcadePoint(x=0, y=0) destinationPoint: ArcadePoint = ArcadePoint(x=0, y=500) self.smoothMotion.computeArcadeMotion( currentPoint=currentPoint, destinationPoint=destinationPoint, spriteRotationAngle=0, rotationalSpeed=TestSmoothMotion.TEST_ROTATIONAL_SPEED) mockSprite: MagicMock(spec=GamePiece)
def _pointAtTarget(self, shooter: Sprite, target: GamePiece, rotationAngle: int = 125): currentPoint: ArcadePoint = ArcadePoint(x=shooter.center_x, y=shooter.center_y) destinationPoint: ArcadePoint = ArcadePoint(x=target.center_x, y=target.center_y) normalAngle: float = self._computer.computeAngleToTarget( shooter=currentPoint, deadMeat=destinationPoint) shooter.angle = normalAngle + rotationAngle self._baseMediatorLogger.info(f'{normalAngle=} - {shooter.angle=}')
def klingonTorpedoExplosionPoint(self) -> ArcadePoint: if self._ktxPoint is None: self._ktxPoint = ArcadePoint( x=HALF_SCREEN_WIDTH + EXPLOSION_X_OFFSET, y=HALF_SCREEN_HEIGHT + EXPLOSION_Y_OFFSET) return self._ktxPoint
def superCommanderTorpedoExplosionPoint(self) -> ArcadePoint: if self._stxPoint is None: self._stxPoint = ArcadePoint( x=HALF_SCREEN_WIDTH + EXPLOSION_X_OFFSET, y=HALF_SCREEN_HEIGHT - EXPLOSION_Y_OFFSET) return self._stxPoint
def gamePositionToScreenPoint(cls, gameCoordinates: Coordinates) -> ArcadePoint: """ This is strictly for the GameView; GamePieces need to use GamePiece.gamePositionToScreenPosition Computes x,y arcade position within the galaxy Args: gameCoordinates: The game coordinates Returns: Arcade x,y Position """ sectorX: int = gameCoordinates.x sectorY: int = gameCoordinates.y # # because game coordinates are top L - R and down # and game coordinates are 0-based adjustSectorX: int = sectorX adjustSectorY: int = (QUADRANT_ROWS - sectorY) - 1 xMargins = -12 # Font Fudge Factor yMargins = -12 # Font Fudge Factor x = (adjustSectorX * QUADRANT_PIXEL_WIDTH) + HALF_QUADRANT_PIXEL_WIDTH + xMargins y = (adjustSectorY * QUADRANT_PIXEL_HEIGHT ) + HALF_QUADRANT_PIXEL_HEIGHT + yMargins + CONSOLE_HEIGHT return ArcadePoint(x=x, y=y)
def phaserFirePoint(self) -> ArcadePoint: if self._phaserFirePoint is None: x: int = HALF_SCREEN_WIDTH + (HALF_SCREEN_WIDTH // 2) self._phaserFirePoint = ArcadePoint(x=x, y=HALF_SCREEN_HEIGHT) return self._phaserFirePoint
def enterpriseTorpedoExplosionPoint(self) -> ArcadePoint: if self._etxPoint is None: self._etxPoint = ArcadePoint(x=HALF_SCREEN_WIDTH // 2, y=HALF_SCREEN_HEIGHT // 2) return self._etxPoint
def on_mouse_press(self, x: float, y: float, button: int, key_modifiers: int): """ Called when the user presses a mouse button. """ if button == MOUSE_BUTTON_LEFT: arcadePoint: ArcadePoint = ArcadePoint(x=x, y=y) if x < QUADRANT_GRID_WIDTH and y >= CONSOLE_HEIGHT: self._enterpriseMediator.impulse(quadrant=self._quadrant, arcadePoint=arcadePoint)
def _placePhaserBolt(self, enterprise: Enterprise, enemy: Enemy): start: ArcadePoint = ArcadePoint(x=enterprise.center_x, y=enterprise.center_y) end: ArcadePoint = ArcadePoint(x=enemy.center_x, y=enemy.center_y) centerPoint: ArcadePoint = self._computer.computeCenterPoint( start=start, end=end) phaserBolt: PhaserBolt = PhaserBolt( textureList=self._phaserFireTextures) phaserBolt.center_x = centerPoint.x phaserBolt.center_y = centerPoint.y self._pointAtTarget(shooter=phaserBolt, target=enterprise, rotationAngle=180) self._phaserBolts.append(phaserBolt)
def testGamePositionToScreenPositionZeroNine(self): sectorCoordinates: Coordinates = Coordinates(x=0, y=9) arcadePoint: ArcadePoint = GamePiece.gamePositionToScreenPosition( gameCoordinates=sectorCoordinates) self.logger.info(f'{arcadePoint=}') expectedPoint: ArcadePoint = ArcadePoint(x=32.0, y=222.0) self.assertEqual(expectedPoint, arcadePoint, 'Calculation must have changed')
def computeCenterPoint(cls, start: ArcadePoint, end: ArcadePoint) -> ArcadePoint: x1: float = start.x x2: float = end.x y1: float = start.y y2: float = end.y midX: float = (x1 + x2) // 2 midY: float = (y1 + y2) // 2 return ArcadePoint(x=midX, y=midY)
def _fireTorpedo(self, enterprise: Enterprise, enemy: Enemy): enterprisePoint: ArcadePoint = ArcadePoint(x=enterprise.center_x, y=enterprise.center_y) klingonPoint: ArcadePoint = ArcadePoint(x=enemy.center_x, y=enemy.center_y) speeds: TorpedoSpeeds = self._intelligence.getTorpedoSpeeds( playerType=self._gameState.playerType) torpedo: PhotonTorpedo = PhotonTorpedo(speed=speeds.enterprise) torpedo.center_x = enterprisePoint.x torpedo.center_y = enterprisePoint.y torpedo.inMotion = True torpedo.firedAt = enemy.id torpedo.destinationPoint = klingonPoint self._torpedoes.append(torpedo) self._messageConsole.displayMessage( f'Enterprise fire from: {enterprise.gameCoordinates} at Klingon {enemy.id}' )
def update(self): if self.inMotion is True: radianInfo: RadianInfo = self.computeArcadeMotion( currentPoint=ArcadePoint(x=self.center_x, y=self.center_y), destinationPoint=self.destinationPoint, spriteRotationAngle=self.angle, rotationalSpeed=self.rotationSpeed) self.doMotion(gamePiece=self, destinationPoint=self.destinationPoint, angleDiffRadians=radianInfo.angleDiffRadians, actualAngleRadians=radianInfo.actualAngleRadians)
def gamePositionToScreenPosition(cls, gameCoordinates: Coordinates) -> ArcadePoint: sectorX: int = gameCoordinates.x sectorY: int = gameCoordinates.y # # because game coordinates are top L - R and down # and game coordinates are 0-based adjustSectorX: int = sectorX adjustSectorY: int = (QUADRANT_ROWS - sectorY) - 1 x = (adjustSectorX * QUADRANT_PIXEL_WIDTH) + HALF_QUADRANT_PIXEL_WIDTH y = (adjustSectorY * QUADRANT_PIXEL_HEIGHT) + HALF_QUADRANT_PIXEL_HEIGHT + CONSOLE_HEIGHT arcadePoint: ArcadePoint = ArcadePoint(x=x, y=y) return arcadePoint
def update(self, quadrant: Quadrant): enterprise: Enterprise = quadrant.enterprise arcadePoint: ArcadePoint = GamePiece.gamePositionToScreenPosition(quadrant.enterpriseCoordinates) arcadeX: float = arcadePoint.x arcadeY: float = arcadePoint.y if enterprise.inMotion is True: self.logger.debug(f'Enterprise arcade position: ({arcadeX},{arcadeY})') enterprise.destinationPoint = ArcadePoint(x=arcadeX, y=arcadeY) enterprise.update() else: enterprise.center_x = arcadeX enterprise.center_y = arcadeY
def on_mouse_release(self, x: float, y: float, button: int, keyModifiers: int): """ Called when the user presses a mouse button. """ self.logger.info(f'{button=} {keyModifiers=}') if button == MOUSE_BUTTON_LEFT and keyModifiers == arcadeKey.MOD_CTRL: # Try klingons first clickedEnemies: List[Sprite] = get_sprites_at_point( point=(x, y), sprite_list=self._quadrantMediator.klingonList) # The Commanders if len(clickedEnemies) == 0: clickedEnemies = get_sprites_at_point( point=(x, y), sprite_list=self._quadrantMediator.commanderList) # Must be Super Commanders if len(clickedEnemies) == 0: clickedEnemies = get_sprites_at_point( point=(x, y), sprite_list=self._quadrantMediator.superCommanderList) for enemy in clickedEnemies: print(f'Delete {enemy}') enemy.remove_from_sprite_lists() elif button == MOUSE_BUTTON_LEFT: if self._selectedGamePiece is None: clickedPaletteSprites: List[Sprite] = get_sprites_at_point( point=(x, y), sprite_list=self._staticSprites) for paletteSprite in clickedPaletteSprites: paletteSprite.color = color.BLACK self._selectedGamePiece = paletteSprite else: # A palette sprite is selected self._placeSpriteOnBoard(x=x, y=y) self.logger.info(f'Clear selected Sprite') self._selectedGamePiece.color = color.WHITE self._selectedGamePiece = cast(GamePiece, None) arcadePoint: ArcadePoint = ArcadePoint(x=x, y=y) self._quadrantMediator.handleMousePress(quadrant=self._quadrant, arcadePoint=arcadePoint, button=button, keyModifiers=keyModifiers)
def update(self): if self.inMotion is True: radianInfo: RadianInfo = self.computeArcadeMotion( currentPoint=ArcadePoint(x=self.center_x, y=self.center_y), destinationPoint=self.destinationPoint, spriteRotationAngle=self.angle, rotationalSpeed=self.rotationSpeed) # self._baseEnemyTorpedoLogger.debug(f'{radianInfo=} {self.destinationPoint=}') self.doMotion(gamePiece=self, destinationPoint=self.destinationPoint, angleDiffRadians=radianInfo.angleDiffRadians, actualAngleRadians=radianInfo.actualAngleRadians) # self.doMotion2(gamePiece=self, destinationPoint=self.destinationPoint) self._potentiallyCreateAFollower()
def _fireTorpedoesAtEnterpriseIfNecessary(self, quadrant: Quadrant, enemies: Enemies, rotationAngle: int = 125): """ Args: quadrant: Quadrant we are in enemies: The enemies to fire from rotationAngle: The offset to add to the image when pointing at the enterprise """ currentTime: float = self._gameEngine.gameClock for enemy in enemies: deltaClockTime: float = currentTime - enemy.lastTimeCheck if deltaClockTime > enemy.firingInterval: self._baseTorpedoMediatorLogger.debug( f'Time for {enemy} to fire torpedoes') endPoint: ArcadePoint = ArcadePoint( x=quadrant.enterprise.center_x, y=quadrant.enterprise.center_y) lineOfSightResponse: LineOfSightResponse = self._doWeHaveLineOfSight( quadrant, shooter=enemy, endPoint=endPoint) if lineOfSightResponse.answer is True: self._pointAtEnterprise(enemy=enemy, enterprise=quadrant.enterprise, rotationAngle=rotationAngle) self._fireTorpedo(enemy=enemy, enterprise=quadrant.enterprise) else: self._playCannotFireSound() self._messageConsole.displayMessage( f'{enemy.id} cannot shoot, blocked by {lineOfSightResponse.obstacle.id}' ) enemy.lastTimeCheck = round(currentTime)
def _doWeHaveLineOfSight(self, quadrant: Quadrant, shooter: Enemy, endPoint: ArcadePoint) -> LineOfSightResponse: startingPoint: ArcadePoint = ArcadePoint(x=shooter.center_x, y=shooter.center_y) obstacles: SpriteList = SpriteList() if quadrant.hasPlanet is True: obstacles.append(quadrant.planet) if quadrant.hasStarBase is True: obstacles.append(quadrant.starBase) otherEnemies: Enemies = self.__buildEligibleEnemyObstacles( shooter=shooter, enemies=quadrant.klingons) obstacles.extend(otherEnemies) results: LineOfSightResponse = self._hasLineOfSight( startingPoint=startingPoint, endPoint=endPoint, obstacles=obstacles) self._baseTorpedoMediatorLogger.debug(f'{results=}') return results
def testGalaxyArcadePosition_276_530(self): expectedArcade: ArcadePoint = ArcadePoint(x=276, y=530) actualArcade: ArcadePoint = Computer.gamePositionToScreenPoint(gameCoordinates=Coordinates(x=4, y=4)) self.assertEqual(expectedArcade, actualArcade, 'Computer is broken')
def testGalaxyArcadePosition_84_786(self): expectedArcade: ArcadePoint = ArcadePoint(x=84, y=786) actualArcade: ArcadePoint = Computer.gamePositionToScreenPoint(gameCoordinates=Coordinates(x=1, y=0)) self.assertEqual(expectedArcade, actualArcade, 'Computer is broken')
def testGalaxyArcadePosition_596_201(self): expectedArcade: ArcadePoint = ArcadePoint(x=596, y=210) actualArcade: ArcadePoint = Computer.gamePositionToScreenPoint(gameCoordinates=Coordinates(x=9, y=9)) self.assertEqual(expectedArcade, actualArcade, 'Computer is broken')