def activate(self, action): """ gets the target location, and ensures that it is within the line of sight. It then checks for entities within the radius, damaging any that are close enough to hit (take note, there’s no exception for the player, so you can get blasted by your own fireball!). If no enemies were hit at all, the Impossible exception is raised, and the scroll isn’t consumed, as it would probably be frustrating to waste a scroll on something like a misclick. Assuming at least one entity was damaged, the scroll is consumed. """ target_xy = action.target_xy consumer = action.entity if not self.engine.game_map.visible[target_xy]: raise exceptions.Impossible( "You cannot target an area that you cannot see.") targets_hit = False results = [] actors = self.engine.game_map.has_comp("fighter") for actor in actors: if src.utils.distance(actor.x, actor.y, *target_xy) <= self.radius: action.msg += f"The {actor.name} is engulfed in a fiery explosion! " actor.fighter.hp -= self.damage if actor.fighter.is_dead(): results.append(DieAction(entity=actor, cause=consumer)) targets_hit = True if not targets_hit: raise exceptions.Impossible("There are no targets in the radius.") self.consume() return results
def equip_to_slot(self, slot, item): """ Attempts to equip an item to a slot on the actor. If successful, returns a string describing what happened. If not, returns an empty string. """ # Check if the item is equippable if not getattr(item, "equippable", None): raise exceptions.Impossible("f{item.name} is not an Equippable!") # Check if item's slot is valid # Convert the enum to a str so we can compare... item_slot = item.equippable.equipment_type.name if item_slot not in self.slots: raise exceptions.Impossible( f"The item's slot ({slot}) is not valid!") # Item slot must also match the provided slot. if item_slot != slot: raise exceptions.Impossible( f"The item's slot ({slot}) does not match the provided slot {item_slot}!" ) msg = '' # current_item = getattr(self, slot) current_item = self.slots[slot] if current_item is not None: msg += self.unequip_from_slot(slot) # Equip the new item self.slots[item_slot] = item msg += self.equip_message(item.name) return msg
def perform(self): """ Take the stairs, if any exist at the entity's location. """ player_location = (self.entity.x, self.entity.y) stair_location = self.dungeon.current_map.upstairs_location if player_location == stair_location: # Do we have a level above us? if self.dungeon.dlevel == DUNGEON_TOP_LEVEL: raise exceptions.Impossible("Cannot go upstairs at top of dungeon!") self.dungeon.move_upstairs(self.entity) self.msg = "You ascend the stairs." else: raise exceptions.Impossible("There are no stairs here.")
def perform(self): """ Removes an item from an entity's inventory and places it on the current game map, at the entity's coordinates. """ # If the item is an equipped item, first unequip it. if self.entity.equipment.is_equipped(self.item): self.entity.equipment.toggle_equip(self.item) # If stackable, drop all of them if "stackable" in self.item: amount = self.item.stackable.size else: amount = 1 result = self.entity.inventory.rm_inv_item(self.item, amount) result.x = self.entity.x result.y = self.entity.y if result: # Put it on the map self.entity.gamemap.add_item(result) if amount == 1: self.msg = f"You dropped a {result.name}." else: self.msg = f"You dropped {amount} {result.name}s." else: raise exceptions.Impossible( "You cannot drop an item you do not have!")
def perform(self): """Picks up an item from the entities' current location and adds it to their inventory.""" inventory = self.entity.inventory # items_on_location = self.entity.gamemap.get_items_at(self.entity.x, self.entity.y) items_on_location = self.entity.gamemap.filter("item", x=self.entity.x, y=self.entity.y) for item in items_on_location: # If stackable, pickup all of them if "stackable" in item: amount = item.stackable.size else: amount = 1 result = self.entity.gamemap.rm_item(item) if result: result.x = -1 result.y = -1 letter = inventory.add_inv_item(result, amount) else: raise Exception('No result from rm_item!') if result.name == "money": self.msg = f"({letter}) - ${amount}" elif amount > 1: self.msg = f"({letter}) - {result}s" else: self.msg = f"({letter}) - {result}" return raise exceptions.Impossible("There is nothing here to pick up.")
def activate(self, action): """Finds the closest actor to the consumer and deals lightning damage to that actor. Then consumes this consumable.""" consumer = action.entity target = None closest_distance = self.maximum_range + 1.0 for actor in self.engine.game_map.actors: if actor is not consumer and self.parent.gamemap.visible[actor.x, actor.y]: distance = src.utils.distance(consumer.x, consumer.y, actor.x, actor.y) if distance < closest_distance: target = actor closest_distance = distance if target: action.msg = f"A lighting bolt zaps the {target.name} with a roaring crack!! " target.fighter.hp -= self.damage self.consume() else: raise exceptions.Impossible("No enemy is close enough to strike.") if target.fighter.is_dead(): return DieAction(entity=target, cause=consumer)
def perform(self): """Performs the attack - first we validate if the attack is valid, then we start running through all the attacks that compose the full attack. For each attack we: * roll a hit die * compare it to the 'target number' * If the roll is less than the target, that attack will hit. * We then calculate the damage and generate an appropriate message. * For each additional hit, there is a -1 tohit penalty. """ target = self.target_actor if not target: raise exceptions.Impossible("Nothing to attack!") elif target == self.entity: if self.entity.is_player(): raise exceptions.Impossible("You cannot attack yourself!") raise exceptions.Impossible(f"The {target} wobbles in place.") results = [] # Iterate through all the attacks for atk in self.offense.attacks: if self.roll_hit_die() < self.calc_target_number(target): dmg = self.execute_damage(target, atk) # Generate appropriate message if dmg > 0: self.hit_msg(target, atk, dmg) else: # Blocking message self.blocked_msg(target) # A hit was executed - check for any passive reactions if target.has_comp("passive"): # Hopefully we can just reverse the dx and dy to target the attacker. results.append( PassiveAttack(entity=target, dx=self.dx * -1, dy=self.dy * -1)) else: self.miss(target) # Check if the target is dead... if target.fighter.is_dead(): results.append(DieAction(entity=target, cause=self.entity)) return results
def activate(self, action): """If the selected actor is valid, confuses that actor and consumes this consumable.""" consumer = action.entity # Get the actor at the location target = action.target_actor if not self.engine.game_map.visible[action.target_xy]: raise exceptions.Impossible( "You cannot target an area that you cannot see.") if not target: raise exceptions.Impossible("You must select an enemy to target.") if target is consumer: raise exceptions.Impossible("You cannot confuse yourself!") action.msg = f"The eyes of the {target.name} look vacant, as it starts to stumble around!" target.states.add_state("confused", self.number_of_turns) self.consume()
def activate(self, action): """Heals the consumer and consumes the entity holding this consumable.""" consumer = action.entity healing_amount = self.amount + random.randint(1, self.amount) amount_recovered = consumer.fighter.heal(healing_amount) if amount_recovered > 0: action.msg = f"You consume the {self.parent.name}, and recover {amount_recovered} HP!" self.consume() else: raise exceptions.Impossible(f"Your health is already full.")
def perform(self): """Attempts to moves the entity a destination coordinate. We conduct a thorough set of checks to make sure the move is valid, and then move the entity. If the entity walks into a trap, we return a TrapAction for that trap. """ if "trapped" in self.entity.states.states: # return WriggleAction(self.entity, self.dx, self.dy) raise exceptions.Impossible("Actor is trapped and cannot move!") dest_x, dest_y = self.dest_xy if not self.entity.gamemap.in_bounds(dest_x, dest_y): # Destination is out of bounds. raise exceptions.Impossible("That way is out of bounds!") if not self.entity.gamemap.walkable(dest_x, dest_y): # Destination is blocked by a tile. if self.entity.is_player(): raise exceptions.Impossible("That way is not walkable!") # No msg for other monsters return # Theoretically, this won’t ever trigger, it's a safeguard. if self.blocking_entity: # Destination is blocked by an entity. if self.entity.is_player: raise exceptions.Impossible("That way is blocked.") # No msg for other monsters return self.entity.move(self.dx, self.dy) # Did we trigger a trap? trap = self.entity.gamemap.get_trap_at(dest_x, dest_y) if trap: # Trigger it return TrapAction(self.entity, trap)
def toggle_equip(self, item): """ Attempts to equip an unequipped item, or unequip an equipped item. Returns a string describing what happened. """ # Can we equip it? if not getattr(item, "equippable", None): raise exceptions.Impossible("f{item.name} is not an Equippable!") # Does the item slot match what is available? slot = item.equippable.equipment_type.name if self.slots[slot] == item: return self.unequip_from_slot(slot) else: return self.equip_to_slot(slot, item)
def perform(self): """ Take the stairs, if any exist at the entity's location. """ player_location = (self.entity.x, self.entity.y) stair_location = self.dungeon.current_map.downstairs_location if player_location == stair_location: new_level_generated = False # Do we have a level below us yet? if self.dungeon.dlevel == len(self.dungeon.map_list): # Generate a new level and add it to the map_list self.dungeon.generate_floor() new_level_generated = True self.dungeon.move_downstairs(self.entity) if new_level_generated: # This solves the annoying problem of the player appearing on top of other monsters. # We have to populate after placing the player. self.dungeon.populate_map(self.dungeon.dlevel) self.msg = "You descend the stairs." else: raise exceptions.Impossible("There are no stairs here.")
def test_Impossible__is_Exception(): i = exceptions.Impossible() assert isinstance(i, Exception)