def use(self, item_entity, **kwargs): results = [] item_component = item_entity.item if item_component.use_function is None: equippable_component = item_entity.equippable if equippable_component: results.append({'equip': item_entity}) else: results.append({ 'message': Message('The {0} cannot be used'.format(item_entity.name), libtcod.yellow) }) else: if item_component.targeting and not (kwargs.get('target_x') or kwargs.get('target_y')): results.append({'targeting': item_entity}) else: kwargs = {**item_component.function_kwargs, **kwargs} item_use_results = item_component.use_function( self.owner, **kwargs) for item_use_result in item_use_results: if item_use_result.get('consumed'): self.remove_item(item_entity) results.extend(item_use_results) return results
def cast_confuse(*args, **kwargs): entities = kwargs.get('entities') fov_map = kwargs.get('fov_map') target_x = kwargs.get('target_x') target_y = kwargs.get('target_y') results = [] if not libtcod.map_is_in_fov(fov_map, target_x, target_y): results.append({ 'consumed': False, 'message': Message('You cannot target a tile outside your field of view.', libtcod.yellow) }) return results for entity in entities: if entity.x == target_x and entity.y == target_y and entity.ai: confused_ai = ConfusedMonster(entity.ai, 10) confused_ai.owner = entity entity.ai = confused_ai results.append({ 'consumed': True, 'message': Message( 'The eyes of the {0} look vacant, as he starts to stumble around!' .format(entity.name), libtcod.light_green) }) break else: results.append({ 'consumed': False, 'message': Message('There is no targetable enemy at that location.', libtcod.yellow) }) return results
def cast_lightning(*args, **kwargs): caster = args[0] entities = kwargs.get('entities') fov_map = kwargs.get('fov_map') damage = kwargs.get('damage') maximum_range = kwargs.get('maximum_range') results = [] target = None closest_distance = maximum_range + 1 for entity in entities: if entity.fighter and entity != caster and libtcod.map_is_in_fov( fov_map, entity.x, entity.y): distance = caster.distance_to(entity) if distance < closest_distance: target = entity closest_distance = distance if target: results.append({ 'consumed': True, 'target': target, 'message': Message( 'A lighting bolt strikes the {0} with a loud thunder! The damage is {1}' .format(target.name, damage)) }) results.extend(target.fighter.take_damage(damage)) else: results.append({ 'consumed': False, 'target': None, 'message': Message('No enemy is close enough to strike.', libtcod.red) }) return results
def cast_fireball(*args, **kwargs): entities = kwargs.get('entities') fov_map = kwargs.get('fov_map') damage = kwargs.get('damage') radius = kwargs.get('radius') target_x = kwargs.get('target_x') target_y = kwargs.get('target_y') results = [] if not libtcod.map_is_in_fov(fov_map, target_x, target_y): results.append({ 'consumed': False, 'message': Message('You cannot target a tile outside your field of view.', libtcod.yellow) }) return results results.append({ 'consumed': True, 'message': Message( 'The fireball explodes, burning everything within {0} tiles!'. format(radius), libtcod.orange) }) for entity in entities: if entity.distance(target_x, target_y) <= radius and entity.fighter: results.append({ 'message': Message( 'The {0} gets burned for {1} hit points.'.format( entity.name, damage), libtcod.orange) }) results.extend(entity.fighter.take_damage(damage)) return results
def add_item(self, item): results = [] if len(self.items) >= self.capacity: results.append({ 'item_added': None, 'message': Message('You cannot carry any more, your inventory is full', libtcod.yellow) }) else: results.append({ 'item_added': item, 'message': Message('You pick up the {0}!'.format(item.name), libtcod.blue) }) self.items.append(item) return results
def kill_monster(monster): death_message = Message('{0} is dead!'.format(monster.name.capitalize()), libtcod.orange) monster.char = '%' monster.color = libtcod.dark_red monster.blocks = False monster.fighter = None monster.ai = None monster.name = 'remains of ' + monster.name monster.render_order = RenderOrder.CORPSE return death_message
def heal(*args, **kwargs): entity = args[0] amount = kwargs.get('amount') results = [] if entity.fighter.hp == entity.fighter.max_hp: results.append({ 'consumed': False, 'message': Message('You are already at full health', libtcod.yellow) }) else: entity.fighter.heal(amount) results.append({ 'consumed': True, 'message': Message('Your wounds start to feel better!', libtcod.green) }) return results
def attack(self, target): results = [] damage = self.power - target.fighter.defense if damage > 0: results.append({ 'message': Message( '{0} attacks {1} for {2} hit points.'.format( self.owner.name.capitalize(), target.name, str(damage)), libtcod.white) }) results.extend(target.fighter.take_damage(damage)) else: results.append({ 'message': Message( '{0} attacks {1} but does no damage.'.format( self.owner.name.capitalize(), target.name), libtcod.white) }) return results
def next_floor(self, player, message_log, constants): self.dungeon_level += 1 entities = [player] self.tiles = self.initialize_tiles() self.make_map(constants['max_rooms'], constants['room_min_size'], constants['room_max_size'], constants['map_width'], constants['map_height'], player, entities) player.fighter.heal(player.fighter.max_hp // 2) message_log.add_message( Message('You take a moment to rest, and recover your strength.', libtcod.light_violet)) return entities
def drop_item(self, item): results = [] if self.owner.equipment.main_hand == item or self.owner.equipment.off_hand == item: self.owner.equipment.toggle_equip(item) item.x = self.owner.x item.y = self.owner.y self.remove_item(item) results.append({ 'item_dropped': item, 'message': Message('You dropped the {0}'.format(item.name), libtcod.yellow) }) return results
def from_json(json_data): use_function_name = json_data.get('use_function') targeting = json_data.get('targeting') targeting_message_json = json_data.get('targeting_message') function_kwargs = json_data.get('function_kwargs', {}) if use_function_name: use_function = getattr(item_functions, use_function_name) else: use_function = None if targeting_message_json: targeting_message = Message.from_json(targeting_message_json) else: targeting_message = None item = Item(use_function, targeting, targeting_message, **function_kwargs) return item
def take_turn(self, target, fov_map, game_map, entities): results = [] if self.number_of_turns > 0: random_x = self.owner.x + randint(0, 2) - 1 random_y = self.owner.y + randint(0, 2) - 1 if random_x != self.owner.x and random_y != self.owner.y: self.owner.move_towards(random_x, random_y, game_map, entities) self.number_of_turns -= 1 else: self.owner.ai = self.previous_ai results.append({ 'message': Message( 'The {0} is no longer confused!'.format(self.owner.name), libtcod.red) }) return results
def place_entities(self, room, entities): max_monsters_per_room = from_dungeon_level([[2, 1], [3, 4], [5, 6]], self.dungeon_level) max_items_per_room = from_dungeon_level([[1, 1], [2, 4]], self.dungeon_level) # Get a random number of monsters number_of_monsters = randint(0, max_monsters_per_room) # Get a random number of items number_of_items = randint(0, max_items_per_room) monster_chances = { 'orc': 80, 'troll': from_dungeon_level([[15, 3], [30, 5], [60, 7]], self.dungeon_level) } item_chances = { 'healing_potion': 35, 'sword': from_dungeon_level([[5, 4]], self.dungeon_level), 'shield': from_dungeon_level([[15, 8]], self.dungeon_level), 'lightning_scroll': from_dungeon_level([[25, 4]], self.dungeon_level), 'fireball_scroll': from_dungeon_level([[25, 6]], self.dungeon_level), 'confusion_scroll': from_dungeon_level([[10, 2]], self.dungeon_level) } for i in range(number_of_monsters): # Choose a random location in the room x = randint(room.x1 + 1, room.x2 - 1) y = randint(room.y1 + 1, room.y2 - 1) # Check if an entity is already in that location if not any([ entity for entity in entities if entity.x == x and entity.y == y ]): monster_choice = random_choice_from_dict(monster_chances) if monster_choice == 'orc': fighter_component = Fighter(hp=20, defense=0, power=4, xp=35) ai_component = BasicMonster() monster = Entity(x, y, 'o', libtcod.desaturated_green, 'Orc', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, ai=ai_component) else: fighter_component = Fighter(hp=30, defense=2, power=8, xp=100) ai_component = BasicMonster() monster = Entity(x, y, 'T', libtcod.darker_green, 'Troll', blocks=True, fighter=fighter_component, render_order=RenderOrder.ACTOR, ai=ai_component) entities.append(monster) for i in range(number_of_items): x = randint(room.x1 + 1, room.x2 - 1) y = randint(room.y1 + 1, room.y2 - 1) if not any([ entity for entity in entities if entity.x == x and entity.y == y ]): item_choice = random_choice_from_dict(item_chances) if item_choice == 'healing_potion': item_component = Item(use_function=heal, amount=40) item = Entity(x, y, '!', libtcod.violet, 'Healing Potion', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'sword': equippable_component = Equippable(EquipmentSlots.MAIN_HAND, power_bonus=3) item = Entity(x, y, '/', libtcod.sky, 'Sword', equippable=equippable_component) elif item_choice == 'shield': equippable_component = Equippable(EquipmentSlots.OFF_HAND, defense_bonus=1) item = Entity(x, y, '[', libtcod.darker_orange, 'Shield', equippable=equippable_component) elif item_choice == 'fireball_scroll': item_component = Item( use_function=cast_fireball, targeting=True, targeting_message=Message( 'Left-click a target tile for the fireball, or right-click to cancel.', libtcod.light_cyan), damage=25, radius=3) item = Entity(x, y, '#', libtcod.red, 'Fireball Scroll', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'confusion_scroll': item_component = Item( use_function=cast_confuse, targeting=True, targeting_message=Message( 'Left-click an enemy to confuse it, or right-click to cancel.', libtcod.light_cyan)) item = Entity(x, y, '#', libtcod.light_pink, 'Confusion Scroll', render_order=RenderOrder.ITEM, item=item_component) else: item_component = Item(use_function=cast_lightning, damage=40, maximum_range=5) item = Entity(x, y, '#', libtcod.yellow, 'Lightning Scroll', render_order=RenderOrder.ITEM, item=item_component) entities.append(item)
def kill_player(player): player.char = '%' player.color = libtcod.dark_red return Message('You died!', libtcod.red), GameStates.PLAYER_DEAD