class Gamestate(SchedulerInterface, EventInterface): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.player_starting_pos = (128, 48) self.game_flags = {} self.trophies_obtained = {} self.player_health = 3 self.fade_darkener = Darkener() self.bg = GraphicInterface() self.bg.setSprite(toStringPath(BG_GRAPHICS_FULL_PATH + ["loading.png"])) self.bg.setSpriteLayer(LOADING_LAYER) self.mappe = None self.ending_player = None self.ending_text = None self.ending_text2 = None self.ending_text3 = None self.ending_squids = [] self.player_health_sprites = [] for i in range(3): self.player_health_sprites.append(GraphicInterface()) self.player_health_sprites[-1].setSprite(toStringPath(UI_GRAPHICS_FULL_PATH + ["health.png"])) self.player_health_sprites[-1].setPos(HEALTH_X_OFFSET + HEALTH_X_STEP*i, getWindowDimensionsScaled()[1] + HEALTH_Y_OFFSET) self.player_health_sprites[-1].setSpriteFollowCamera(True) self.player_health_sprites[-1].setSpriteLayer(UI_LAYER) self.squid_trophy_sprites = [] for i in range(3): self.squid_trophy_sprites.append(GraphicInterface()) self.squid_trophy_sprites[-1].setSpriteFollowCamera(True) self.squid_trophy_sprites[-1].setPos(SQUID_X_OFFSET + SQUID_X_STEP*i, getWindowDimensionsScaled()[1] + SQUID_Y_OFFSET) self.squid_trophy_sprites[-1].setSpriteLayer(UI_LAYER) self.Schedule(self.createStartingMap, 2) self.addCustomListener(SQUID_TROPHY_OBTAINED_EVENT, self.squidTrophyObtained) self.addQueryListener(SQUID_TROPHY_OBTAINED_QUERY, self.checkTrophyObtained) self.addQueryListener(ALL_SQUID_TROPHIES_OBTAINED_QUERY, self.hasAllTrophies) self.addCustomListener(PLAYER_REDUCE_HP_EVENT, self.reduceHP) self.addCustomListener(PLAYER_MAX_HP_EVENT, self.maxHP) self.addQueryListener(CHECK_PLAYER_DEAD_QUERY, self.checkPlayerDead) self.addCustomListener(PUSH_SAVE_EVENT, self.pushSave) self.addCustomListener(FINAL_SQUID_OBTAINED_EVENT , self.finalSquidObtained) self.addCustomListener(GAME_LOST_EVENT, self.gameLost) self.loadSave() def createStartingMap(self): self.mappe = createMap(MAP_NAME, self.player_starting_pos) #just one map for now self.fade_darkener.fadeIn(60) if self.bg: self.bg.destroy() self.bg = None playMusic(toStringPath(FULL_MUSIC_PATH + ["bgmusic.ogg"])) def getMapTileAtPos(self, *args, **kwargs): if self.mappe: return self.mappe.getMapTileAtPos(*args, **kwargs) return None def squidTrophyObtained(self, trophy_index): self.trophies_obtained[trophy_index] = True self.updateTrophyGraphics() def updateTrophyGraphics(self): for trophy in self.squid_trophy_sprites: trophy.setSpriteAlpha(0.0) for trophy_index in self.trophies_obtained.keys(): index = SQUID_ENUM.get(trophy_index, 999) try: self.squid_trophy_sprites[index].setSprite(toStringPath(CHAR_GRAPHICS_FULL_PATH + [SQUID_GRAPHICS[trophy_index]])) self.squid_trophy_sprites[index].setSpriteAlpha(1.0) except (KeyError, IndexError): pass def checkTrophyObtained(self, trophy_index): return self.trophies_obtained.get(trophy_index, False) def hasAllTrophies(self): for i in {SQUID_TROPHY_BLUE_INDEX, SQUID_TROPHY_RED_INDEX, SQUID_TROPHY_GREEN_INDEX}: if not self.checkTrophyObtained(i): return False return True def maxHP(self): self.player_health = 3 self.updateHealth() def reduceHP(self): self.player_health -= 1 self.updateHealth() def updateHealth(self): for i in range(3): if i < self.player_health: self.player_health_sprites[i].setSpriteAnimation("FULL") else: self.player_health_sprites[i].setSpriteAnimation("EMPTY") def checkPlayerDead(self): return self.player_health <= 0 def setEventFlag(self, key, val): self.game_flags[key] = val def getEventFlag(self, key, default = "asdfadsgiangiang"): try: return self.game_flags[key] except KeyError: if default != "asdfadsgiangiang": return default raise def pushSave(self): savegame.setValue(SAVE_TROPHIES, copy.deepcopy(self.trophies_obtained)) savegame.setValue(SAVE_EVENT_FLAGS, copy.deepcopy(self.game_flags)) savegame.setValue(SAVE_PLAYER_POS, callQuery(PLAYER_GET_POS_QUERY)) savegame.commitSave(SAVE_GAME_NAME) def loadSave(self): try: savegame.loadSave(SAVE_GAME_NAME) except ResourceUnavailableError: pass self.game_flags = copy.deepcopy(savegame.getValue(SAVE_EVENT_FLAGS, {})) self.trophies_obtained = copy.deepcopy(savegame.getValue(SAVE_TROPHIES, {})) playerpos = savegame.getValue(SAVE_PLAYER_POS, None) if playerpos is None: playerpos = (128, 48) self.player_starting_pos = playerpos self.updateTrophyGraphics() def finalSquidObtained(self): for actor in self.mappe.getAllActors(): actor.pauseActor() self.fade_darkener.fadeOut(60) self.Schedule(self.startCredits, 60) def startCredits(self): self.removeMapData() resetLag() self.fade_darkener.fadeIn(60) playMusic(toStringPath(FULL_MUSIC_PATH + ["ending.ogg"])) self.removeEndingData() self.ending_player = GraphicInterface() self.ending_player.setSprite(toStringPath(CHAR_GRAPHICS_FULL_PATH + ["char.png"])) self.ending_player.setPos(128,128) self.ending_player.setSpriteCenter(True, True) self.ending_player.setSpriteFollowCamera(True) self.ending_player.setSpriteAnimation("WALK_DOWN") self.ending_text = createLabel("Thanks for playing!", font = "kaimono3.gfont2") self.ending_text.setPos(128, 64) self.ending_text.follow_camera = True self.ending_text.setCenter(True,True) self.ending_text2 = createLabel("You got the", font = "kaimono3.gfont2") self.ending_text2.setPos(128, 192) self.ending_text2.follow_camera = True self.ending_text2.setCenter(True,True) self.ending_text3 = createLabel("Golden Squid!", font = "kaimono3.gfont2") self.ending_text3.setPos(128, 176) self.ending_text3.follow_camera = True self.ending_text3.setCenter(True,True) xoffset = getWindowDimensionsScaled()[0]/2 - (len(SQUID_GRAPHICS)-1)*ENDING_SQUID_X_SPACING/2 for i, val in enumerate(SQUID_GRAPHICS.values()): self.ending_squids.append(GraphicInterface()) self.ending_squids[-1].setSprite(toStringPath(CHAR_GRAPHICS_FULL_PATH + [val])) self.ending_squids[-1].setSpriteCenter(True, True) self.ending_squids[-1].setSpriteFollowCamera(True) self.ending_squids[-1].setPos(xoffset + i*ENDING_SQUID_X_SPACING, 96) def removeMapData(self): if self.mappe: self.mappe.destroy() self.mappe = None if self.bg: self.bg.destroy() self.bg = None for sprite in self.player_health_sprites: sprite.destroy() self.player_health_sprites.clear() for sprite in self.squid_trophy_sprites: sprite.destroy() self.squid_trophy_sprites.clear() def removeEndingData(self): if self.ending_player: self.ending_player.destroy() self.ending_player = None if self.ending_text: self.ending_text.destroy() self.ending_text = None if self.ending_text2: self.ending_text2.destroy() self.ending_text2 = None if self.ending_text3: self.ending_text3.destroy() self.ending_text3 = None for squid in self.ending_squids: squid.destroy() self.ending_squids.clear() def gameLost(self): self.fade_darkener.fadeOut(60) self.Schedule(self.resetToSave, 60) def resetToSave(self): stopMusic() if self.mappe: self.mappe.destroy() self.loadSave() if self.bg: self.bg.destroy() self.fade_darkener.fadeIn(1) self.bg = GraphicInterface() self.bg.setSprite(toStringPath(BG_GRAPHICS_FULL_PATH + ["loading.png"])) self.bg.setSpriteLayer(LOADING_LAYER) self.bg.setSpriteFollowCamera(True) self.Schedule(self.createStartingMap, 2) def destroy(self): super().destroy() self.removeMapData() self.removeEndingData() if self.fade_darkener: self.fade_darkener.destroy() self.fade_darkener = None
class BaseEnemy(BaseActor): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.enemy = True self.enemy_index = None self.stunned = False self.stun_knockback = False self.stun_stars = None self.Schedule(self.updateOffscreen, 10, True) def getEnemyIndex(self): return self.enemy_index def startWander(self): if self.checkWanderAction(): self.setVelocity(*random.choice(WANDER_VELS)) self.Schedule(self._wanderWait, WANDER_FREQUENCY) def _wanderWait(self): if self.checkWanderAction(): self.setVelocity(0, 0) self.Schedule(self.startWander, WANDER_FREQUENCY) def checkWanderAction(self): return not self.isHurt() and not self.isStunned() def updateOffscreen(self): if self.checkSpriteOffscreen(): self.cleanupSelf() def stunEnemy(self, x, y): self.setStunned(True) self.setSpriteAnimation( ACTOR_MOVEMENT_ANIMATION_DICT[self.facing][False][0]) self.setKnockbackVel(x, y, STUN_VEL) self.Schedule(self._finishStunKnockback, STUN_KNOCKBACK_TIME) self.stun_knockback = True self.removeStunStars() self.stun_stars = GraphicInterface() self.stun_stars.setSprite( toStringPath(EFFECTS_GRAPHICS_FULL_PATH + ["stun_stars.png"])) self.stun_stars.setSpriteOffset(y=STUN_STAR_HEIGHT) self.stun_stars.setSpriteLayer(STUN_STAR_LAYER) self.stun_stars.setSpriteCenter(True, True) self._updateSpritePos() return False #for minotaur charge def _finishStunKnockback(self): self.stun_knockback = False self.setVelocity(0, 0) def isStunned(self): return self.stunned def setStunned(self, val): self.stunned = val def removeStunStars(self): if self.stun_stars: self.stun_stars.destroy() self.stun_stars = None #overwritten stuff def _updateSpritePos(self): super()._updateSpritePos() if self.stun_stars: self.stun_stars.setPos(*self.getPos()) def updateMovementAnimation(self): if not self.isStunned(): super().updateMovementAnimation() def _endHurt(self): super()._endHurt() self.cleanupSelf() def destroy(self): super().destroy() self.removeStunStars()
class BaseActor(GraphicInterface, MovementInterfaceFrames, SchedulerInterface, EventInterface): def __init__(self, mappe, *args, **kwargs): super().__init__(*args, **kwargs) self.shadow = GraphicInterface() self.shadow.setSprite( toStringPath(CHAR_GRAPHICS_FULL_PATH + ["playershadow.png"])) self.shadow.setSpriteCenter(True, True) self.shadow.setSpriteLayer(SHADOW_LAYER) self.actor_id = -1 self.paused = False self._mappe = weakRef(mappe) self.facing = DIRECTION_DOWN self.moving = False self.collision = True self.flying = False self.base_layer = ACTOR_LAYER self.z_pos = 0 self.enemy = False self.player = False self.room_index = (0, 0) self.hurt = False self.event_flag = None #used for some enemies self.updateActorLayer(0) self.setSpriteCenter(x=True) self.Schedule(self.updateAnimation, 1, True) @property def mappe(self): return unWeakRef(self._mappe) @mappe.setter def mappe(self, val): debugMessage("Cannot set map directly") def setActorId(self, actor_id): self.actor_id = actor_id def getActorId(self): return self.actor_id def setBaseLayer(self, val): self.base_layer = val def getBaseLayer(self): return self.base_layer def updateActorLayer(self, offset): self.setSpriteLayer(self.base_layer + offset) def setRoomIndex(self, val): self.room_index = val def getRoomIndex(self): return self.room_index def checkCollision(self, x=None, y=None): if x is None: x = self.getPos()[0] if y is None: y = self.getPos()[1] tile = getMapTileAtPos(x, y) return tile is None or tile.getCollision() def checkOverPit(self, x=None, y=None): if x is None: x = self.getPos()[0] if y is None: y = self.getPos()[1] tile = getMapTileAtPos(x, y) return tile is not None and tile.getPit() def updateAnimation(self): #check state self.updateMoving() self.updateMovementAnimation() def updateMovementAnimation(self): ani, flip = ACTOR_MOVEMENT_ANIMATION_DICT[self.facing][self.moving] self.setSpriteAnimation(ani, False, "DEFAULT") self.setSpriteFlip(flip) def updateMoving(self): if abs(self.velocity[1]) + abs(self.velocity[0]) != 0: self.moving = True else: self.moving = False def cleanupSelf(self): if not self.destroyed: self.mappe.removeActor(self.getActorId()) def isEnemy(self): return self.enemy def isFlying(self): return self.flying def isPlayer(self): return self.player def pauseActor(self): if not self.paused: self.pauseMovement() self.pauseSpriteAnimations() self.pauseAllScheduledListeners() self.paused = True def unpauseActor(self): if self.paused: self.unpauseMovement() self.unpauseSpriteAnimations() self.unpauseAllScheduledListeners() self.paused = False def removeShadow(self): if self.shadow: self.shadow.destroy() self.shadow = None def setHurt(self, time): self.hurt = True self.Unschedule(self._hurtFlash) self.Schedule(self._hurtFlash, 2, True) self.Unschedule(self._endHurt) self.Schedule(self._endHurt, time) def _hurtFlash(self): self.setSpriteAlpha(abs(self.getSpriteAlpha() - 1.0)) def _endHurt(self): self.Unschedule(self._hurtFlash) self.setSpriteAlpha(1.0) self.hurt = False def isHurt(self): return self.hurt def updateFacing(self): if abs(self.velocity[1]) > abs(self.velocity[0]): if self.velocity[1] > 0: self.facing = DIRECTION_UP else: self.facing = DIRECTION_DOWN elif abs(self.velocity[1]) != abs(self.velocity[0]): if self.velocity[0] > 0: self.facing = DIRECTION_RIGHT elif self.velocity[0] < 0: self.facing = DIRECTION_LEFT if self.velocity[0] > 0 and self.facing == DIRECTION_LEFT: self.facing = DIRECTION_RIGHT elif self.velocity[0] < 0 and self.facing == DIRECTION_RIGHT: self.facing = DIRECTION_LEFT elif self.velocity[1] > 0 and self.facing == DIRECTION_DOWN: self.facing = DIRECTION_UP elif self.velocity[1] < 0 and self.facing == DIRECTION_UP: self.facing = DIRECTION_DOWN def setKnockbackVel(self, x, y, vel): xoffset, yoffset = map(operator.sub, self.getPos(), (x, y)) hypot = math.sqrt(xoffset**2 + yoffset**2) if hypot == 0: #avoid divide by 0 hypot = 1 xoffset = 1 yoffset = 0 self.setVelocity(vel * xoffset / hypot, vel * yoffset / hypot) def setEventFlag(self, val): self.event_flag = val #overwritten stuff def setSprite(self, *args, **kwargs): super().setSprite(*args, **kwargs) self.setSpriteOtherOffset(ACTOR_Y_OFFSET, y=-self.getSpriteHeight() / 6) def _updateSpritePos(self): x, y = self.getPos() try: self.sprite.setPos(x, y + self.z_pos) except AttributeError: pass if self.shadow: self.shadow.setPos(x, y - 2) def runMovement(self, *args, **kwargs): super().runMovement(*args, **kwargs) self.updateFacing() self.updateAnimation() def setPos(self, x=None, y=None): if not self.collision or self.checkCollision() or ( not self.isFlying() and self.checkOverPit()): #failsafe if already in a wall super().setPos(x, y) else: oldpos = self.getPos() if x is None: x = oldpos[0] if y is None: y = oldpos[1] if not self.checkCollision(x) and ( self.isFlying() or not self.checkOverPit(x)): #extremely simple collision super().setPos(x) if not self.checkCollision(y=y) and (self.isFlying() or not self.checkOverPit(y=y)): super().setPos(y=y) def destroy(self): super().destroy() self.removeShadow()
class Player(SchedulerInterface, GraphicInterface, EventInterface): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.jumping = False self.invulnerable = False self.z_pos = 0 self.jump_speed = 0 self.jumping_y_speed = 0 self.jumping_x_speed = 0 self.dead = False self.shadow = GraphicInterface() self.shadow.setSprite(toStringPath(*PLAYER_SHADOW_PATH)) self.shadow.setSpriteCenter(True,True) self.shadow.setSpriteLayer(0.5) self.setSprite(toStringPath(*PLAYER_CHAR_PATH)) self.Schedule(self.updateMovement, 1, True) self.addCustomListener(PLAYER_JUMP_EVENT, self.playerJump) def getZPos(self): return self.z_pos def updateMovement(self): self.z_pos += self.jump_speed if self.z_pos < 0: self.jumpFinished() pos = list(self.getPos()) xspeed = 0 yspeed = 0 if not self.jumping: if not self.dead: if checkKeyHeld(MOVE_UP): yspeed = PLAYER_MOVEMENT_SPEED_Y elif checkKeyHeld(MOVE_DOWN): yspeed = -PLAYER_MOVEMENT_SPEED_Y if checkKeyHeld(MOVE_RIGHT): xspeed = PLAYER_MOVEMENT_SPEED_X elif checkKeyHeld(MOVE_LEFT): xspeed = -PLAYER_MOVEMENT_SPEED_X else: yspeed = self.jumping_y_speed xspeed = self.jumping_x_speed if not self.dead: pos[0] = max(0, min(getWindowDimensionsScaled()[0] - self.getSpriteWidth(), pos[0] + xspeed)) pos[1] = max(0, min(MAX_Y_POSITION, pos[1] + yspeed)) self.setPos(*pos) def playerJump(self): if not self.jumping and not self.invulnerable: self.jumping_y_speed = 0 self.jumping_x_speed = 0 if checkKeyHeld(MOVE_UP): self.jumping_y_speed = PLAYER_MOVEMENT_SPEED_Y elif checkKeyHeld(MOVE_DOWN): self.jumping_y_speed = -PLAYER_MOVEMENT_SPEED_Y if checkKeyHeld(MOVE_RIGHT): self.jumping_x_speed = PLAYER_MOVEMENT_SPEED_X elif checkKeyHeld(MOVE_LEFT): self.jumping_x_speed = -PLAYER_MOVEMENT_SPEED_X self.jumping = True self.setSpriteAnimation("JUMP") self.jump_speed = JUMP_STARTING_SPEED self.scheduleUnique(self.updateGravity, 1, True) def jumpFinished(self): self.z_pos = 0 self.jump_speed = 0 self.Unschedule(self.updateGravity) self.jumping = False if not self.invulnerable: self.setSpriteAnimation("RUN") def updateGravity(self): self.jump_speed -= JUMP_GRAVITY def checkInvulnerable(self): return self.invulnerable def gotHit(self): self.invulnerable = True self.setSpriteAnimation("HURT") self.Schedule(self.flashAnimation, 2, True) self.Schedule(self.finishInvulnerability, INVULNERABLE_TIME) def flashAnimation(self): self.setSpriteAlpha(abs(self.getSpriteAlpha() - 1.0)) def finishInvulnerability(self): self.Unschedule(self.flashAnimation) self.setSpriteAlpha(1.0) if not self.dead: self.setSpriteAnimation("RUN") else: self.setSpriteAnimation("DEAD") self.Schedule(self.deathFallBehind, 1, True) self.invulnerable = False def setKilled(self): self.dead = True def checkDead(self): return self.dead def deathFallBehind(self): self.setPos(self.getPos()[0] - BG_SCROLL_RATE) #overwritten stuff def _updateSpritePos(self): pos = self.getPos() try: self.sprite.setPos(pos[0], pos[1] + self.z_pos) except AttributeError: pass self.shadow.setPos(pos[0] + self.getSpriteWidth()/2, pos[1] + 1) def destroy(self): super().destroy() if self.shadow: self.shadow.destroy() self.shadow = None