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 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 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 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 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 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 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 Particle(object): def __init__( self, viewport =None, x :int =0, y :int =0, life :int =0, angle :float =0, speed :int =1, fadeout :bool =True, charType :int =0, byStep :bool =False, active :bool =False, damage :int =0, damageEveryStep :bool =False, byPlayer :bool =True, ): self.viewport = viewport self.movementTimer = Timer() self.init( x=x, y=y, life=life, angle=angle, speed=speed, fadeout=fadeout, byStep=byStep, charType=charType, active=active, damage=damage, damageEveryStep=damageEveryStep, byPlayer=byPlayer) def init( self, x :int =0, y :int =0, life :int =0, angle :float =0, speed :int =1, fadeout :bool =True, byStep :bool =False, charType :int =0, active :bool =False, damage :int =0, damageEveryStep :bool =False, byPlayer :bool =True, color =None, ): self.x = x self.y = y self.life = life self.originalLife = life self.angle = angle self.speed = speed self.fadeout = fadeout self.byStep = byStep self.charType = charType self.damage = damage self.damageEveryStep = damageEveryStep self.byPlayer = byPlayer self.angleInRadians = angle * math.pi / 180 self.velocity = { 'x': speed * math.cos(self.angleInRadians), 'y': -speed * math.sin(self.angleInRadians) } self.color, self.attr = ColorPalette.getColorByColor(Color.brightmagenta) if color is not None: self.color = color[0] self.attr = color[1] self.setChar() self.rx = 0.0 self.ry = 0.0 self.movementTimer.setTimer(self.speed) self.active = active def __repr__(self): return "Particle {}/{}".format( self.x, self.y ) def advance(self, dt): if self.active is False: return if self.byStep: return self.movementTimer.advance(dt) self.makeStep(dt) def fadeoutSetColor(self): if self.life > (self.originalLife / 2): self.attr = Screen.A_BOLD else: self.attr = Screen.A_NORMAL def setChar(self): if self.charType == 0: pass elif self.charType == 1: if self.life > ((self.originalLife / 3) * 2): self.char = 'O' elif self.life < ((self.originalLife / 3) * 1): self.char = '.' else: self.char = 'o' elif self.charType == 2: if self.life > ((self.originalLife / 3) * 2): self.char = '#' elif self.life < ((self.originalLife / 3) * 1): self.char = ':' else: self.char = '|' elif self.charType == 3: if self.life > ((self.originalLife / 3) * 2): self.char = 'O' elif self.life < ((self.originalLife / 3) * 1): self.char = '.' else: self.char = 'o' elif self.charType == 4: if self.life > ((self.originalLife / 3) * 2): self.char = '¦' elif self.life < ((self.originalLife / 3) * 1): self.char = '.' else: self.char = ':' elif self.charType == 5: self.char = '-' else: raise Exception("Invalid charType: {}".format(self.charType)) def makeStep(self, dt, adjustLife=True): if self.life <= 0: self.active = False return if self.fadeout: self.fadeoutSetColor() self.setChar() if self.speed > 0: xFloat = self.velocity['x'] * (dt * 100) + self.rx yFloat = self.velocity['y'] * (dt * 100) + self.ry xChange = int(round(xFloat)) yChange = int(round(yFloat)) # accumulate pos we could not handle yet changeRestX = xFloat - xChange - self.rx changeRestY = yFloat - yChange - self.ry self.rx += changeRestX self.ry += changeRestY # change pos self.x += xChange self.y += yChange if (self.damageEveryStep and self.damage > 0 and (xChange != 0 or yChange != 0)): # hitLocations = [Coordinates(self.x, self.y)] hitLocations = [] hitLocations.append(self) messaging.add( type=MessageType.AttackAt, data= { 'hitLocations': hitLocations, 'damage': self.damage, 'byPlayer': self.byPlayer, 'direction': Direction.none, 'knockback': False, 'stun': False, 'sourceRenderable': None, } ) if False: logging.info("Real change: X: {} Y: {}".format(xFloat, yFloat)) logging.info("Round change: X: {} Y: {}".format(xChange, yChange)) logging.info("Change Rest: X: {} Y: {}".format(changeRestX, changeRestY)) logging.info("New Rest: X: {} Y: {}".format(self.rx, self.ry)) logging.info("New Pos: X: {} Y: {}".format(self.x, self.y)) if adjustLife: self.life -= 1 def draw(self): self.viewport.addstr( self.y, self.x, self.char, self.color, self.attr) def isActive(self): return self.active def setActive(self, active): self.active = active