def tick(self): """ Apply one tick of damage :return: None """ # Reset actor states (to clear damage effect from previous ticks for previous_target in self.actors: self._set_actor_state(previous_target, False) if self.effectDuration == 0: self.end() if self.effectDuration > 0: # Reduce the remaining duration with one tick. self.effectDuration -= 1 # Find all targets in range self._actors = [] for tile in self.tiles: for actor in tile.actors: self.actors.append(actor) # apply damage to every target for target in self.actors: damage_amount = roll_hit_die(self.effectHitDie) message( self.source.name.capitalize() + ' hits ' + target.name + ' for ' + str(damage_amount) + ' Damage.', "GAME") target.takeDamage(damage_amount, self.source.owner) # Modify actor states self._set_actor_state(target, True)
def tryDropItem(self, item): """ Player attempts to drop an item. This function is meant to be called from the GUI. """ if isinstance(item, Equipment): if item.isEquiped: message("You can't drop an equiped item.") return self.dropItem(item)
def unEquipItem(self, item): """ basic implementation of equiping, doesn't take into account equipment slots. Should be overridden in subclass implementations. """ #can only unequip if item is equiped if item in self.equipedItems: self.equipedItems.remove(item) item.isEquiped = False message(self.name.capitalize() + ' unequips a ' + item.name + '.', "GAME")
def followPortal(self, portal): """ Send player through specified portal. """ #Game message message(portal.message, "GAME") #Move the player to the destination destinationLevel = portal.destinationPortal.level destinationTile = portal.destinationPortal.tile self.moveToLevel(destinationLevel, destinationTile) #change the current level of the game to the destinationlevel myGame = destinationLevel.owner myGame.current_level = destinationLevel
def pickUpItem(self, item): """ Make this character pick up an item. Arguments item - the item to be picked up """ # remove the item from its tile and level item.removeFromLevel() # add the item to the inventory of this character self.addItem(item) # message message(self.name.capitalize() + ' picks up a ' + item.name + '.', "GAME")
def takeHeal(self, amount, healer): """ function to heal a given amount of hitpoints arguments amount - the number of hitpoints to heal healer - the source of teh healing """ # Heal by the given amount if amount > 0: self.currentHitPoints += amount message( self.name.capitalize() + ' gains ' + str(amount) + ' hitpoints from a ' + healer.name + '.', "GAME") game_event(self.__class__.__name__, self.json)
def attack(self, target): """ Attack another Character Arguments target - the Character to be attacked """ # Check if the attack hits hitRoll = roll_hit_die("1d100") # In case of an equal accuracy and dodge rating there is a 50% chance to hit toHit = 100 - (50 + self.accuracy - target.dodge) message( self.name.capitalize() + ' attacks ' + target.name + ': ' + str(hitRoll) + ' vs ' + str(toHit), "COMBAT") if hitRoll < toHit: # Miss, no damage message( self.name.capitalize() + ' attacks ' + target.name + ' but misses!', "COMBAT") else: # Hit, there will be damage, bonusDamage depends on how strongly the hit connects bonusDamagePercent = (hitRoll - toHit) / 100.0 damagePercent = 1 + bonusDamagePercent # targets armor neutralizes part of the damage damage = int(damagePercent * self.damage) - target.armor if damage > 0: message( self.name.capitalize() + ' attacks ' + target.name + ' and hits for ' + str(damage) + ' Damage (' + str(damagePercent) + ' damage factor)', "COMBAT") target.takeDamage(damage, self) else: message( self.name.capitalize() + ' attacks ' + target.name + ' and hits but it has no effect.', "COMBAT")
def equipItem(self, item): """ basic implementation of equiping, doesn't take into account equipment slots. Should be overridden in subclass implementations. """ #can only equip if item is in inventory if item in self.inventory.items: #can only equip if not yet equiped if item not in self.equipedItems: self.equipedItems.append(item) item.isEquiped = True message( self.name.capitalize() + ' equips a ' + item.name + '.', "GAME")
def applyTo(self, target): """ Confuse effect will be applied to target monster. :target: Monster object :return: None """ if not isinstance(target, WarrensGame.Actors.Monster): raise GameError("Can not apply confuse effect to " + str(target)) confused_turns = self.effectDuration WarrensGame.AI.ConfusedMonsterAI(self, target, confused_turns) target.level.active_effects.append(self) self.actors.append(target) message( target.name + ' is confused for ' + str(confused_turns) + ' turns.', "GAME")
def levelUp(self): """ Increase level of this player """ message("You feel stronger!", "GAME") self.json["playerLevel"] += 1 self.json[ "nextLevelXp"] = GAME.XP_BASE + GAME.XP_BASE * GAME.XP_FACTOR * ( self.playerLevel * self.playerLevel - 1) self._baseAccuracy += GAME.PLAYER_LEVEL_ACCURACY self._baseDodge += GAME.PLAYER_LEVEL_DODGE self._baseDamage += GAME.PLAYER_LEVEL_DAMAGE self._baseArmor += GAME.PLAYER_LEVEL_ARMOR self._baseBody += GAME.PLAYER_LEVEL_BODY self._baseMind += GAME.PLAYER_LEVEL_MIND
def takeDamage(self, amount, attacker): """ Function to take damage from an attacker. :param amount: the incoming amount of damage :param attacker: the attacking Actor :return: None """ if self.state_alive: # apply damage if possible if amount > 0: self.currentHitPoints -= amount # check for death if self.currentHitPoints <= 0: message(self.name.capitalize() + ' is killed!', "COMBAT") self._killedBy(attacker) game_event(self.__class__.__name__, self.json)
def dropItem(self, item): """ Make this character drop an item on the current tile. Arguments item - the item to be dropped """ #unequip it if required if item in self.equipedItems: self.unEquipItem(item) #if it is in the inventory remove it if item in self.inventory.items: self.inventory.remove(item) #add it to the current tile of the character item.moveToLevel(self.level, self.tile) #message message(self.name.capitalize() + ' drops a ' + item.name + '.', "GAME")
def take_turn(self): """ Make this AI object take one turn. For a confused monster the monster will move in a random direction. """ # Try to move in a random direction message(self.character.name + ' stumbles around (confused).', "GAME") direction = self.random_direction() if direction is not None: self.character.moveAlongVector(*direction) # Switch back to regular AI if confusedTurns are over self.confusedTurns -= 1 if self.confusedTurns == 0: self.character.AI = self.originalAI self.character.state_confused = False message(self.character.name + ' is no longer confused.', "GAME")
def _killedBy(self, attacker): """ This function handles the death of this Character """ if self.state_alive: if type(attacker) is Player: # Yield experience to the player message(attacker.name + ' gains ' + str(self.xpValue) + ' XP.', "GAME") attacker.gainXp(self.xpValue) if type(attacker) is Monster: if attacker.killedByText != '': message(attacker.killedByText, "GAME") # Transform this character into a corpse and remove AI self.char = '%' self.sprite_id = SPRITES.MONSTER_RIP self.sprite_overlay_id = None self._AI = None self.name += " corpse" self.json["state_alive"] = False
def take_turn(self): """ Take one turn """ message( self.character.name + ' at ' + str(self.character.tile) + ' takes turn.', "AI") # Only take action if we are in a level if self.character.level is None: message(" Not in a level, can't take action.", "AI") return # Only take action if we find a player on our level if not self.character.level.player_present: message(" No player found on level, staying put", "AI") return for player in self.character.level.players: # Only take action if player is not dead. if not player.state_alive: message(" Player is dead, no action needed", "AI") return else: # TODO medium: read this from the config file via monsterlibrary range_of_sight = 8 range_of_attack = 2 distance = distance_between_actors(self.character, player) # Only take action if player is within range of sight if distance > range_of_sight: message(" Player is out of sight, no action needed", "AI") return # Attack if player is within range of attack elif distance < range_of_attack: message(" Attacking player", "AI") self.character.attack(player) return else: message(" Moving towards player", "AI") self.character.moveTowards(player) return