class SceneLogo(SceneBase): def __init__(self, viewport, world): super().__init__(world=world, viewport=viewport) # teh logo coordinates = Coordinates(2, 5) textureLogo = PhenomenaTexture(phenomenaType=PhenomenaType.intro) self.renderableLogo = Renderable( texture=textureLogo, viewport=self.viewport, coordinates=coordinates, active=True, ) self.anyKeyFinishesScene = True self.timer = Timer(3) self.sceneFinished = False self.name = "Scene0 - Logo" def enter(self): self.addRenderable(self.renderableLogo) def sceneIsFinished(self): if self.timer.timeIsUp(): return True else: return False def advance(self, dt): self.timer.advance(dt)
class DragonStateChase(StateChase): def __init__(self, brain): super().__init__(brain) self.canSkillTimer = Timer() def on_enter(self): super().on_enter() self.canSkillTimer.setTimer(3.0) def process(self, dt): super().process(dt) self.canSkillTimer.advance(dt) def trySkill(self): if self.canSkillTimer.timeIsUp(): meRenderable = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.graphics.renderable.Renderable) locCenter = meRenderable.getLocationCenter() messaging.add(type=MessageType.EmitParticleEffect, data={ 'location': locCenter, 'effectType': ParticleEffectType.dragonExplosion, 'damage': 50, 'byPlayer': False, 'direction': Direction.none, }) self.canSkillTimer.reset()
class RamboStateStanding(State): name = "standing" def __init__(self, brain): super().__init__(brain) self.canAttackTimer = Timer() def on_enter(self): self.canAttackTimer.setTimer(2.0) meGroupId = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.groupid.GroupId) messaging.add( type=MessageType.EntityStanding, groupId=meGroupId.getId(), data=None ) def process(self, dt): self.canAttackTimer.advance(dt) self.tryAttack() def tryAttack(self): if self.canAttackTimer.timeIsUp(): self.brain.pop() self.brain.push("attacking")
class Player(): def __init__(self): self.name = 'Player' self.points = 0 self.isPlayer = True self.isAttacking = False self.isAlive = True self.attackTimer = Timer(0.0) def advance(self, deltaTime: float): self.attackTimer.advance(deltaTime) if self.attackTimer.timeIsUp(): self.isAttacking = False self.attackTimer.setActive(False) def setAttacking(self, attackTime: float): self.isAttacking = True self.attackTimer.setTimer(attackTime) self.attackTimer.start() def setAlive(self, alive): self.isAlive = alive def __repr__(self): return "Player"
class Defense(): def __init__(self): self.coordinates = None self.timer = Timer(1.0) self.isActive = False def advance(self, dt): self.timer.advance(dt) if self.timer.timeIsUp(): self.isActive = False
class Texture(object): def __init__(self, type, width=0, height=0, name=''): self.type = type self.width: int = width self.height: int = height self.active: bool = True self.name = name # color related self.overwriteColorTimer = Timer(0.25, active=False) self.overwriteColor = None def init(self): pass def draw(self, viewport): pass def advance(self, deltaTime: float): # reset overwrite color if self.overwriteColorTimer.timeIsUp(): self.overwriteColor = None self.overwriteColorTimer.stop() self.overwriteColorTimer.advance(deltaTime) def setOverwriteColorFor(self, time: float, color: Color): if self.overwriteColorTimer.isActive(): logger.debug("{} Color already active on new set color".format( self.name)) self.overwriteColor = color self.overwriteColorTimer.setTimer(time) self.overwriteColorTimer.reset() def advanceStep(self): pass def setActive(self, active: bool): self.active = active def isActive(self) -> bool: return self.active def setName(self, name: str): self.name = name def __repr__(self): return self.name
class DamageStat(object): def __init__(self): self.damage = 100 self.dmgTimer = Timer(1.0) def addDamage(self, damage): self.damage += damage def process(self, dt): self.dmgTimer.advance(dt) if self.dmgTimer.timeIsUp(): self.dmg -= 10 self.dmgTimer.reset() def getDamageStat(self): return self.damage
class StateAttack(State): name = "attack" def __init__(self, brain): State.__init__(self, brain) self.attackTimer = Timer(instant=True) def on_enter(self): meEnemy = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.gamelogic.enemy.Enemy) meGroupId = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.groupid.GroupId) self.attackTimer.setTimer(meEnemy.enemyInfo.attackTime) self.setTimer(meEnemy.enemyInfo.attackStateTime) messaging.add( type=MessageType.EntityAttack, groupId=meGroupId.getId(), data=None ) def process(self, dt): self.attackTimer.advance(dt) if self.attackTimer.timeIsUp(): logger.info(self.name + " I'm attacking, via offensiveattack!") self.attackTimer.reset() offensiveAttack = self.brain.owner.world.component_for_entity( self.brain.owner.entity, OffensiveAttack) offensiveAttack.attack() if self.timeIsUp(): # too long attacking. lets switch to chasing logger.info("{}: Too long attacking, switch to chasing".format(self.owner)) self.brain.pop() self.brain.push("chase")
class StateChase(State): name = "chase" def __init__(self, brain): State.__init__(self, brain) meEnemy = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.gamelogic.enemy.Enemy) # basically move speed self.lastInputTimer = Timer(meEnemy.enemyInfo.chaseStepDelay, instant=True) # try attacking when timer is finished self.canAttackTimer = Timer() # we need to know player location, or we could just handle on every new # PlayerLocation message self.lastKnownPlayerPosition = None def on_enter(self): meEnemy = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.gamelogic.enemy.Enemy) stateTimeRnd = random.randrange(-100 * meEnemy.enemyInfo.chaseTimeRnd, 100 * meEnemy.enemyInfo.chaseTimeRnd) self.setTimer(meEnemy.enemyInfo.chaseTime + (stateTimeRnd / 100)) self.canAttackTimer.setTimer(meEnemy.enemyInfo.enemyCanAttackPeriod) self.canAttackTimer.reset() if not Config.allowEnemyAttacking: self.canAttackTimer.setActive(False) def tryAttacking(self): if self.canAttackTimer.timeIsUp(): logger.debug("{}: Check if i can attack player".format(self.name)) if self.canAttackPlayer(): if (EntityFinder.numEnemiesInState(self.brain.owner.world, 'attack') < Config.maxEnemiesInStateAttacking): self.brain.pop() self.brain.push("attackwindup") self.canAttackTimer.reset() def tryMoving(self): # only move if we can not hit him (even on cooldown) # this is quiet... taxing. and not really necessary? # if not self.canAttackPlayer(): if True: # movement speed, and direction if self.lastInputTimer.timeIsUp(): self.getInputChase() self.lastInputTimer.reset() def trySkill(self): # stickfigure has no skills pass def process(self, dt): meAttackable = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.gamelogic.attackable.Attackable) if meAttackable.isStunned: return self.lastInputTimer.advance(dt) self.canAttackTimer.advance(dt) # update player position if new location self.checkForNewPlayerPosition() self.tryAttacking() # note that if we want to attack, as identified a few lines above, # we will be in state attackWindup, and not reach here self.trySkill() self.tryMoving() # switch to wander if exhausted if self.timeIsUp(): logger.debug("{}: Too long chasing, switching to wander".format( self.owner)) self.brain.pop() self.brain.push("wander") def checkForNewPlayerPosition(self): # check if there are any new player position messages for message in messaging.getByType(MessageType.PlayerLocation): self.lastKnownPlayerPosition = message.data def canAttackPlayer(self): if self.lastKnownPlayerPosition is None: # we may not yet have received a location. # find it directly via player entity # this is every time we go into chase state playerEntity = EntityFinder.findPlayer(self.brain.owner.world) # player not spawned if playerEntity is not None: playerRenderable = self.brain.owner.world.component_for_entity( playerEntity, system.graphics.renderable.Renderable) self.lastKnownPlayerPosition = playerRenderable.getLocationAndSize( ) canAttack = AiHelper.canAttackPlayer(self.brain.owner, self.lastKnownPlayerPosition) return canAttack def getInputChase(self): meGroupId = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.groupid.GroupId) meRenderable = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.graphics.renderable.Renderable) if not Config.allowEnemyMovement: return moveX, moveY, dontChangeDirection = AiHelper.getAttackVectorToPlayer( self.owner, meRenderable) # only move if we really move a character if moveX != 0 or moveY != 0: directMessaging.add( groupId=meGroupId.getId(), type=DirectMessageType.moveEnemy, data={ 'x': moveX, 'y': moveY, 'dontChangeDirection': dontChangeDirection, 'updateTexture': True, 'force': False, }, )
class StateAttack(State): name = "attack" def __init__(self, brain): State.__init__(self, brain) self.attackMoveTimer = Timer() # Timer(0.5, instant=False) # windup and cooldown def on_enter(self): meEnemy = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.gamelogic.enemy.Enemy) meGroupId = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.groupid.GroupId) messaging.add( type=MessageType.EntityAttack, groupId=meGroupId.getId(), data=None ) # self.attackTimer.setTimer(meEnemy.enemyInfo.attackTime) # self.setTimer(meEnemy.enemyInfo.attackTime) self.stepsTodo = 30 self.attackMoveTimer.init() self.attackMoveTimer.setTimer(0.1) self.setTimer(3.0) # even though we attack multiple times (each step) # only send EntityAttack once (change animation) messaging.add( type=MessageType.EntityAttack, groupId=meGroupId.getId(), data={} ) def process(self, dt): self.attackMoveTimer.advance(dt) # check if we got stunned meAttackable = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.gamelogic.attackable.Attackable) if meAttackable.isStunned: self.brain.pop() self.brain.push("chase") # check if we should do one attack step if self.attackMoveTimer.timeIsUp(): logger.info("{}: I'm attacking, step {}".format(self.owner, self.stepsTodo)) if self.stepsTodo > 0: self.attackMoveTimer.reset() self.stepsTodo -= 1 meRenderable = self.brain.owner.world.component_for_entity( self.brain.owner.entity, Renderable) meGroupId = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.groupid.GroupId) if self.stepsTodo % 2 == 0: offensiveAttack = self.brain.owner.world.component_for_entity( self.brain.owner.entity, OffensiveAttack) offensiveAttack.attack() if meRenderable.direction is Direction.left: x = -1 else: x = 1 directMessaging.add( groupId = meGroupId.getId(), type = DirectMessageType.moveEnemy, data = { 'x': x, 'y': 0, 'dontChangeDirection': False, 'updateTexture': False, 'force': True }, ) else: self.attackMoveTimer.stop() if self.timeIsUp(): # too long attacking. lets switch to chasing logger.info("{}: Too long attacking, switch to chasing".format( self.owner)) self.brain.pop() self.brain.push("chase")
class SceneProcessor(esper.Processor): def __init__(self, viewport, sceneManager): super().__init__() self.viewport = viewport self.sceneManager = sceneManager self.xCenter = Config.columns / 2 - 5 self.state = State.start self.screenMoveTimer = Timer(0.1) self.lastKnownPlayerPos = None self.gameoverTimer = Timer(3.0, active=False) # show gameover for this long def setState(self, state): logging.debug("Set to state: {}".format(state)) self.state = state def process(self, dt): self.screenMoveTimer.advance(dt) self.gameoverTimer.advance(dt) for message in messaging.getByType(MessageType.Gameover): self.gameoverTimer.reset() self.gameoverTimer.start() self.setState(State.gameover) messaging.add(type=MessageType.EmitPhenomenaTexture, data={ 'phenomenaTextureType': PhenomenaType.gameover, 'location': Coordinates(10, 10), 'staticLocation': True, 'direction': Direction.right, 'physics': False, }) if self.state is State.gameover: for message in messaging.getByType(MessageType.PlayerKeypress): if self.gameoverTimer.timeIsUp(): messaging.add(type=MessageType.ClearRenderables, data={}) self.setState(State.start) elif self.state is State.start: self.sceneManager.restartScene() self.setState(State.brawl) for message in messaging.getByType(MessageType.EntityDying): # if no enemies are alive, we want to go to the next akt if self.numEnemiesAlive() == 0: self.screenMoveTimer.start() if self.state is not State.gameover: self.setState(State.pushToNextScene) break for message in messaging.getByType(MessageType.PlayerLocation): self.lastKnownPlayerPos = message.data if self.state is State.pushToNextScene: # if suddenly enemies appear, let the player free if self.numEnemiesVisible() > 0: self.setState(State.brawl) if self.state is State.brawl: if (self.numEnemiesVisible() == 0 and not self.enemiesLeftOfChar(self.lastKnownPlayerPos.x)): self.screenMoveTimer.start() self.setState(State.pushToEnemies) if self.state is State.pushToEnemies: if self.numEnemiesVisible() > 0: self.setState(State.brawl) playerScreenCoords = self.viewport.getScreenCoords(message.data) # adjust viewport on move if self.state is State.pushToNextScene: # screen follows player # player is left side of screen (screen pushes player right) if playerScreenCoords.x != 10: distance = int(playerScreenCoords.x - 10) if distance < 0: self.adjustViewport(-1) self.screenMoveTimer.reset() elif distance > 0: self.adjustViewport(1) self.screenMoveTimer.reset() elif self.state is State.pushToEnemies: # screen follows player # player is middle of the screen if playerScreenCoords.x != self.xCenter: distance = int(playerScreenCoords.x - self.xCenter) if distance < 0: self.adjustViewport(-1) self.screenMoveTimer.reset() elif distance > 0: self.adjustViewport(1) self.screenMoveTimer.reset() elif self.state is State.brawl: if not self.enemiesLeftOfChar(self.lastKnownPlayerPos.x): # player can move freely # coming close to left/right of the screen will move it if playerScreenCoords.x >= Config.moveBorderRight: distance = playerScreenCoords.x - Config.moveBorderRight self.adjustViewport(distance) if playerScreenCoords.x <= Config.moveBorderLeft: distance = playerScreenCoords.x - Config.moveBorderLeft self.adjustViewport(distance) # /adjust viewport on move # let the scene decide if we need more enemies self.sceneManager.currentScene.handlePosition( message.data, self.viewport.getx(), self.numEnemiesAlive()) for message in messaging.getByType(MessageType.PlayerKeypress): key = message.data['key'] self.sceneManager.handlePlayerKeyPress(key) if key == ord('k'): logger.info("Scene: Kill All Enemies") self.killAllEnemies() if key == ord('n'): logger.info("Scene: Go to next part") self.killAllEnemies() enemyCell = self.sceneManager.currentScene.getNextEnemy() playerEntity = EntityFinder.findPlayer(self.world) meGroupId = self.world.component_for_entity( playerEntity, system.groupid.GroupId) renderable = self.world.component_for_entity( playerEntity, system.graphics.renderable.Renderable) distX = enemyCell.spawnX - renderable.coordinates.x directMessaging.add( groupId=meGroupId.getId(), type=DirectMessageType.movePlayer, data={ 'x': distX, 'y': 0, 'dontChangeDirection': False, 'whenMoved': None, }, ) # move screen animation if self.screenMoveTimer.timeIsUp( ) and self.lastKnownPlayerPos is not None: playerScreenCoords = self.viewport.getScreenCoords( self.lastKnownPlayerPos) if self.state is State.pushToNextScene: # screen follows player # player is left side of screen (screen pushes player right) if playerScreenCoords.x != 10: distance = int(playerScreenCoords.x - 10) if distance < 0: self.adjustViewport(-1) self.screenMoveTimer.reset() elif distance > 0: self.adjustViewport(1) self.screenMoveTimer.reset() else: self.screenMoveTimer.stop() elif self.state is State.pushToEnemies: # screen follows player # player is middle of the screen if playerScreenCoords.x != self.xCenter: distance = int(playerScreenCoords.x - self.xCenter) if distance < 0: self.adjustViewport(-1) self.screenMoveTimer.reset() elif distance > 0: self.adjustViewport(1) self.screenMoveTimer.reset() else: self.screenMoveTimer.stop() self.sceneManager.advance(dt) def adjustViewport(self, xoff): viewportChanged = self.viewport.adjustViewport(xoff) if viewportChanged: messaging.add( type=MessageType.ScreenMove, data={ 'x': xoff, }, ) def killAllEnemies(self): for _, ai in self.world.get_component(system.gamelogic.ai.Ai): ai.brain.pop() ai.brain.push('dead') def numEnemiesAlive(self) -> int: count = 0 for _, ai in self.world.get_component(system.gamelogic.ai.Ai): if ai.brain.state.name != 'dead' and ai.brain.state.name != 'dying': count += 1 return count def numEnemiesVisible(self) -> int: count = 0 for _, (ai, renderable) in self.world.get_components( system.gamelogic.ai.Ai, system.graphics.renderable.Renderable): if (ai.brain.state.name != 'dead' and ai.brain.state.name != 'dying' and renderable.coordinates.x > self.viewport.getx() and renderable.coordinates.x < self.viewport.getRightX()): count += 1 return count def enemiesLeftOfChar(self, playerX): for _, (ai, renderable) in self.world.get_components( system.gamelogic.ai.Ai, system.graphics.renderable.Renderable): if (ai.brain.state.name != 'dead' and ai.brain.state.name != 'dying' and renderable.coordinates.x < playerX): return True return False
class SceneIntro(SceneBase): def __init__(self, viewport, world): super().__init__(world=world, viewport=viewport) self.textureEmiter = TextureEmiter(viewport=viewport, world=world) renderableCache.init(viewport=viewport) textureCopter = PhenomenaTexture( phenomenaType=PhenomenaType.roflcopter, name="Scene1 chopper") self.copterSpawn = Coordinates(13, -1 * textureCopter.height) renderableCopter = Renderable( texture=textureCopter, viewport=self.viewport, coordinates=self.copterSpawn, active=True, ) texturePlayer = CharacterTexture( characterAnimationType=CharacterAnimationType.standing, characterTextureType=CharacterTextureType.player, name='Scene1 Player') coordinates = Coordinates(24, 13) renderablePlayer = Renderable( texture=texturePlayer, viewport=self.viewport, coordinates=coordinates, active=True, ) textureEnemy = CharacterTexture( characterAnimationType=CharacterAnimationType.standing, characterTextureType=CharacterTextureType.player, name='Scene1 Enemy') coordinates = Coordinates(Config.columns, 13) renderableEnemy = Renderable( texture=textureEnemy, viewport=self.viewport, coordinates=coordinates, active=True, ) self.renderableCopter = renderableCopter self.renderablePlayer = renderablePlayer self.renderableEnemy = renderableEnemy self.myTimer = Timer(0.5) self.state = IntroSceneState.wait1 self.name = "Scene1 - Intro Animation" self.anyKeyFinishesScene = True self.init() def advance(self, dt): self.myTimer.advance(dt) self.handleState() def enter(self): self.addRenderable(self.renderableCopter) def sceneIsFinished(self) -> bool: if self.state is IntroSceneState.done: return True else: return False def handleState(self): # interactive, aka a hack, but it works if (self.state is IntroSceneState.wait1 or self.state is IntroSceneState.flydown or self.state is IntroSceneState.drop or self.state is IntroSceneState.flyup): c1, a1 = ColorPalette.getColorByColor(Color.blue) c2, a2 = ColorPalette.getColorByColor(Color.brightblue) self.viewport.addstr(5, 40, "N Key Rollover", c1, a1) self.viewport.addstr(6, 40, "Escape from Hong Kong", c2, a2) c3, a3 = ColorPalette.getColorByColor(Color.cyan) self.viewport.addstr(8, 40, "Moving: arrow keys, shift to strafe", c3, a3) self.viewport.addstr(9, 40, "Select attack: 1 2 3 4", c3, a3) self.viewport.addstr(10, 40, "Attack : space", c3, a3) self.viewport.addstr(11, 40, "Skills : q w e r", c3, a3) self.viewport.addstr(12, 40, "Heal, Port : f g", c3, a3) # state if self.state is IntroSceneState.wait1: # for next scene: Flydown if self.myTimer.timeIsUp(): self.state = IntroSceneState.flydown self.myTimer.setTimer(0.1) self.renderableCopter.setActive(True) logger.debug("Scene: Go to State: Flydown") elif self.state is IntroSceneState.flydown: if self.myTimer.timeIsUp(): self.myTimer.reset() self.renderableCopter.coordinates.y += 1 # for next scene: Drop if self.renderableCopter.coordinates.y == 8: self.myTimer.setTimer(0.1) logger.debug("Scene: Go to State: Drop") self.addRenderable(self.renderablePlayer) self.state = IntroSceneState.drop elif self.state is IntroSceneState.drop: # for next scene: Flyup if self.myTimer.timeIsUp(): self.myTimer.reset() self.renderableCopter.coordinates.y -= 1 if self.renderableCopter.coordinates.y == self.copterSpawn.y: self.myTimer.setTimer(0.1) self.addRenderable(self.renderableEnemy) self.renderableEnemy.texture.changeAnimation( CharacterAnimationType.walking, direction=Direction.left) self.state = IntroSceneState.spawnenemy self.isShowMap = True logger.info("Scene: Go to State: SpawnEnemy") elif self.state is IntroSceneState.spawnenemy: if self.myTimer.timeIsUp(): self.myTimer.reset() self.renderableEnemy.coordinates.x -= 1 self.renderableEnemy.advanceStep() if (self.renderableEnemy.coordinates.x == self.renderablePlayer.coordinates.x + 15): self.renderableEnemy.texture.changeAnimation( CharacterAnimationType.standing, direction=Direction.none) self.myTimer.setTimer(2.0) self.state = IntroSceneState.speakenemy self.textureEmiter.showSpeechBubble( 'The princess is in another castle...', time=3.0, parentRenderable=self.renderableEnemy ) elif self.state is IntroSceneState.speakenemy: if self.myTimer.timeIsUp(): self.state = IntroSceneState.leaveenemy self.renderableEnemy.texture.changeAnimation( CharacterAnimationType.walking, direction=Direction.right) self.myTimer.setTimer(0.1) elif self.state is IntroSceneState.leaveenemy: if self.myTimer.timeIsUp(): self.myTimer.reset() self.renderableEnemy.advanceStep() self.renderableEnemy.coordinates.x += 1 if (self.renderableEnemy.coordinates.x == Config.columns): self.state = IntroSceneState.done elif self.state is IntroSceneState.done: pass
class StateWander(State): name = "wander" def __init__(self, brain): State.__init__(self, brain) meEnemy = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.gamelogic.enemy.Enemy) self.lastInputTimer = Timer(meEnemy.enemyInfo.wanderStepDelay, instant=True) self.destCoord = Coordinates() self.destIsPoint = False def on_enter(self): meEnemy = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.gamelogic.enemy.Enemy) stateTimeRnd = random.randrange( -100 * meEnemy.enemyInfo.wanderTimeRnd, 100 * meEnemy.enemyInfo.wanderTimeRnd) self.setTimer(meEnemy.enemyInfo.wanderTime + (stateTimeRnd / 100)) self.chooseDestination() def process(self, dt): meRenderable = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.graphics.renderable.Renderable) meAttackable = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.gamelogic.attackable.Attackable) meEnemy = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.gamelogic.enemy.Enemy) if meAttackable.isStunned: return self.lastInputTimer.advance(dt) if self.lastInputTimer.timeIsUp(): self.getInputWander() self.lastInputTimer.reset() if self.timeIsUp(): if (EntityFinder.numEnemiesInState(self.brain.owner.world, 'chase') < Config.maxEnemiesInStateChase): logger.info("{}: Too long wandering, chase again a bit".format( self.owner)) self.brain.pop() self.brain.push("chase") elif Utility.isIdentical(meRenderable.getLocation(), self.destCoord): # No reset of wander state atm, just a new location self.chooseDestination() else: # check if player is close for message in messaging.getByType(MessageType.PlayerLocation): distance = Utility.distance( message.data, meRenderable.getLocation()) if distance['sum'] < meEnemy.enemyInfo.wanderAttackDistance: logger.info("{}: Player is close, chasing".format(self.owner)) self.brain.pop() self.brain.push("chase") def getInputWander(self): meRenderable = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.graphics.renderable.Renderable) meGroupId = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.groupid.GroupId) if not Config.allowEnemyMovement: return x, y = AiHelper.getVectorToPlayer( source=meRenderable.coordinates, dest=self.destCoord) directMessaging.add( groupId = meGroupId.getId(), type = DirectMessageType.moveEnemy, data = { 'x': x, 'y': y, 'dontChangeDirection': False, 'updateTexture': True, 'force': False, }, ) def chooseDestination(self): meRenderable = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.graphics.renderable.Renderable) # if true: go to a static point close to the current enemy position # if false: go to a point relative to the enemy # self.destIsPoint = random.choice([True, False]) # note that getLocation() will return a reference. we need to copy it here. playerEntity = EntityFinder.findPlayer(self.brain.owner.world) if not playerEntity: return playerRenderable = self.brain.owner.world.component_for_entity( playerEntity, system.graphics.renderable.Renderable) self.destCoord = AiHelper.pickDestAroundPlayer( playerRenderable, distanceX=meRenderable.texture.width, distanceY=meRenderable.texture.height) if Config.showEnemyWanderDest: messaging.add( type=MessageType.EmitTextureMinimal, data={ 'char': '.', 'timeout': self.timer, 'coordinate': self.destCoord, 'color': Color.grey } )
class RamboStateAttacking(State): name = "attacking" def __init__(self, brain): super().__init__(brain) self.cooldownTimer = Timer() self.attackingTimer = Timer() def on_enter(self): self.cooldownTimer.setTimer(0.5) self.attackingTimer.setTimer(4.0) playerEntity = EntityFinder.findPlayer(self.brain.owner.world) if playerEntity is None: return meRenderable = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.graphics.renderable.Renderable) meGroupId = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.groupid.GroupId) self.turnIfNecessary(playerEntity, meRenderable, meGroupId) # attack messaging.add(type=MessageType.EntityAttack, groupId=meGroupId.getId(), data=None) def turnIfNecessary(self, playerEntity, meRenderable, meGroupId): playerRenderable = self.brain.owner.world.component_for_entity( playerEntity, system.graphics.renderable.Renderable) x, y = AiHelper.getVectorToPlayer(source=meRenderable.coordinates, dest=playerRenderable.getLocation()) if x > 0: playerDir = Direction.right cx = 1 else: playerDir = Direction.left cx = -1 if playerDir is not meRenderable.getDirection(): directMessaging.add( groupId=meGroupId.getId(), type=DirectMessageType.moveEnemy, data={ 'x': cx, 'y': 0, 'dontChangeDirection': False, 'updateTexture': False, 'force': False, }, ) def process(self, dt): self.cooldownTimer.advance(dt) self.attackingTimer.advance(dt) self.tryShooting() def tryShooting(self): if self.attackingTimer.timeIsUp(): self.brain.pop() self.brain.push('standing') if self.cooldownTimer.timeIsUp(): meRenderable = self.brain.owner.world.component_for_entity( self.brain.owner.entity, system.graphics.renderable.Renderable) locCenter = meRenderable.getAttackBaseLocation() messaging.add(type=MessageType.EmitParticleEffect, data={ 'location': locCenter, 'effectType': ParticleEffectType.bullet, 'damage': 50, 'byPlayer': False, 'direction': meRenderable.getDirection(), }) self.cooldownTimer.reset()
class InputProcessor(esper.Processor): def __init__(self): super().__init__() self.movementTimer = Timer(1.0 / Config.movementKeysPerSec, instant=True) self.keyCache = [] def process(self, deltaTime): self.handleKeyboardInput() self.advance(deltaTime) def advance(self, deltaTime): self.movementTimer.advance(deltaTime) def handleKeyboardInput(self): playerEntity = EntityFinder.findPlayer(self.world) if playerEntity is None: return player = self.world.component_for_entity( playerEntity, system.gamelogic.player.Player) renderable = self.world.component_for_entity( playerEntity, system.graphics.renderable.Renderable) attackable = self.world.component_for_entity( playerEntity, system.gamelogic.attackable.Attackable) # return if we cannot handle key, but cache it first if attackable.isStunned or player.isAttacking: for message in messaging.getByType(MessageType.PlayerKeypress): # store a few movement commands if len(self.keyCache) >= 1: del (self.keyCache[0]) self.keyCache.append(message) return if not player.isAlive: return didMove = False for message in self.keyCache: # identical to bottom loop atm apm.tick(message.data['time']) didMoveTmp = self.handleKeyPress(message.data['key'], player, renderable, playerEntity) if didMoveTmp: didMove = True self.keyCache.clear() for message in messaging.getByType(MessageType.PlayerKeypress): apm.tick(message.data['time']) didMoveTmp = self.handleKeyPress(message.data['key'], player, renderable, playerEntity) if didMoveTmp: didMove = True # to allow diagonal movement, we allow multiple movement keys per input # cycle, without resetting the timer. if didMove: self.movementTimer.reset() def handleKeyPress(self, key, player, playerRenderable, playerEntity): didMove = False x = 0 y = 0 if self.movementTimer.timeIsUp(): dontChangeDirection = False if key == Screen.KEY_LEFT: if Config.xDoubleStep: x = -2 else: x = -1 didMove = True elif key == Screen.KEY_RIGHT: if Config.xDoubleStep: x = 2 else: x = 1 didMove = True if key == 393: # shift left dontChangeDirection = True if Config.xDoubleStep: x = -2 else: x = -1 didMove = True elif key == Screen.KEY_RIGHT: if Config.xDoubleStep: x = 2 else: x = 1 didMove = True if key == 402: # shift right dontChangeDirection = True if Config.xDoubleStep: x = 2 else: x = 1 didMove = True elif key == Screen.KEY_UP: y = -1 didMove = True elif key == Screen.KEY_DOWN: y = 1 didMove = True meGroupId = self.world.component_for_entity(playerEntity, system.groupid.GroupId) if didMove: directMessaging.add( groupId=meGroupId.getId(), type=DirectMessageType.movePlayer, data={ 'x': x, 'y': y, 'dontChangeDirection': dontChangeDirection, 'whenMoved': None, }, ) return didMove
class DamageProcessor(esper.Processor): def __init__(self): super().__init__() self.dmgTimer = Timer(1.0) def process(self, dt): self.dmgTimer.advance(dt) if self.dmgTimer.timeIsUp(): damageStat.addDamage(-10) self.dmgTimer.reset() damageSumPlayer = 0 for msg in messaging.getByType(MessageType.AttackAt): for entity, (meAtk, groupId, renderable) in self.world.get_components( Attackable, GroupId, Renderable ): hitLocations = msg.data['hitLocations'] damage = msg.data['damage'] byPlayer = msg.data['byPlayer'] direction = msg.data['direction'] knockback = msg.data['knockback'] stun = msg.data['stun'] if 'sourceRenderable' in msg.data: sourceRenderable = msg.data['sourceRenderable'] else: sourceRenderable = None if renderable.isHitBy(hitLocations): directMessaging.add( groupId=groupId.id, type=DirectMessageType.receiveDamage, data={ 'damage': damage, 'byPlayer': byPlayer, 'direction': direction, 'knockback': knockback, 'stun': stun, 'sourceRenderable': sourceRenderable, 'destinationEntity': entity, 'hitLocations': hitLocations, } ) if byPlayer: damageSumPlayer += damage # check if we should announce our awesomeness if damageSumPlayer > Config.announceDamage: # find player for ent, (groupId, player) in self.world.get_components( GroupId, Player ): directMessaging.add( groupId = groupId.getId(), type = DirectMessageType.activateSpeechBubble, data = { 'text': 'Cowabunga!', 'time': 1.0, } ) damageStat.addDamage(damageSumPlayer)