def cast_shoulder_charge(*args, **kwargs): # TODO Need to impiment shoulder charge. Not totally sure even on design '''Design idea- Cardinal Directions. Move 3(?) spaces in that direction. Bonus attack if you hit someone. Use case: Rapidly approach dangerous enemies. Also run away from other enemies. Is this WAAAAY too powerful with running away from enemies? Maybe disable stair dancing entirely. Step 1: Choose direction. This involvs direction. Do I need a diff targeting function? Step 2: Move one position. Check to see if anything is there. Just use code from movement. Repeat 3x Step 3: If combat need special code for damage (extra). OR IS ABILITY POWERFUL ENOUGH WITHOUT BONUS?''' 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 bolt of fire strikes the {0} with a boom! 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_throw_rock(*args, **kwargs): entities = kwargs.get('entities') fov_map = kwargs.get('fov_map') damage = kwargs.get('damage') target_x = kwargs.get('target_x') target_y = kwargs.get('target_y') maximum_range = kwargs.get('maximum_range') results = [] if not libtcod.map_is_in_fov(fov_map, target_x, target_y): results.append({'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.fighter: if entity.distance(target_x, target_y) <= maximum_range: results.append({'skill_used': True, 'message': Message('The rock hits the {0} and damages it for {1} hit points.'.format(entity.name, damage), libtcod.orange)}) results.extend(entity.fighter.take_damage(damage)) break #TODO test the skill_used part else: results.append({'message': Message('You cannot hit anything that far away!', libtcod.orange)}) break elif entity.x == target_x and entity.y == target_y: results.append({'message': Message('Invalid target.', libtcod.orange)}) break return results
def use(self, skill_entity, **kwargs): results = [] skill_component = skill_entity.skill if skill_component.use_function is None: results.append({ 'message': Message(' {0} cannot be activated'.format(skill_entity.name), libtcod.yellow) }) else: if skill_component.targeting and not (kwargs.get('target_x') or kwargs.get('target_y')): results.append({'targeting_s': skill_entity}) else: kwargs = {**skill_component.function_kwargs, **kwargs} skill_use_results = skill_component.use_function( self.owner, **kwargs) results.extend(skill_use_results) # TODO fix this. Why is it 'player_turn_results' in engine but just results here?) #results.append({'skill_used'}) return results
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_i': 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_quills(*args, **kwargs): # TODO this skill has been coded (not tested at all). It is never being called at moment. Delete old way if this works. results = [] if kwargs.get('attacker') is not None and kwargs.get('damage') is not None: attacker = kwargs.get('attacker') damage = kwargs.get('damage') quill_damage = damage//10 results.extend(attacker.fighter.take_damage(quill_damage)) else: results.append({'message': Message('This ability is used automatically when you are hit')}) ''' OLD WAY--> if GameStates = SHOW_SKILL: results.append({'message': Message('This abitilty is used automatically when you are hit')}) else: #caster = args[0] attacker = kwargs.get('attacker') damage = kwargs.get('damage') quill_damage = damage//10 results.extend(attacker.fighter.take_damage(quill_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 create_skill(self, character, skill_name, **kwargs): results = [] if skill_name == 'Cloak of Quills': skill_component = Skills(use_function=cast_quills, damage=40, maximum_range=5) Quills = Entity(0, 0, '*', libtcod.sky, 'Cloak of Quills', skill=skill_component) character.skill_list.add_skill(results, Quills) character.learnable_skills.remove('Cloak of Quills') character.learnable_skills.append('Cloak of Quills 2') elif skill_name == 'Shoulder Charge': skill_component = Skills(use_function=cast_shoulder_charge, damage=40, maximum_range=5) Shoulder_Charge = Entity(0, 0, '*', libtcod.sky, 'Shoulder Charge', skill=skill_component) character.skill_list.add_skill(results, Shoulder_Charge) character.learnable_skills.remove('Shoulder Charge') character.learnable_skills.append('Shoulder Charge 2') elif skill_name == 'Throw Rock': skill_component = Skills( use_function=cast_throw_rock, damage=40, maximum_range=3, targeting=True, targeting_message=Message( 'Left-click on a enemy to throw rock, or right-click to cancel.' )) Throw_Rock = Entity(0, 0, '*', libtcod.sky, 'Throw Rock', skill=skill_component) character.skill_list.add_skill(results, Throw_Rock) character.learnable_skills.remove('Throw Rock') character.learnable_skills.append('Throw Rock 2') return results
def add_skill(self, results, skill): results.append({ 'skill_added': skill, 'message': Message('You learned the ability {0}!'.format(skill), libtcod.blue) }) self.skills.append(skill) return results
def kill_monster(monster): death_message = Message('{0} is dead!'.format(monster.name.capitalize()), libtcod.orange) monster.char = CustomTile.SKULL monster.color = libtcod.white monster.blocks = False monster.fighter = None monster.ai = None monster.name = 'remains of ' + monster.name monster.render_order = RenderOrder.CORPSE return death_message
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 attack(self, target): results = [] damage = self.power - target.fighter.defense if damage > 0: if 'Quills' in target.skill_list.skills: results.append({ 'message': Message( '{0} attacks {1} for {2} hit points AND THORNS.'. 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} 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 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 kill_player(player): player.char = '%' player.color = libtcod.dark_red return Message('You died!', libtcod.red), GameStates.PLAYER_DEAD
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 = { 'wolf': 80, 'bear': from_dungeon_level([[15, 3], [30, 5], [60, 7]], self.dungeon_level) } item_chances = { 'healing_herbs': 35, 'sharp_stick': 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), 'fire_oil': 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 == 'wolf': fighter_component = Fighter(hp=20, defense=0, power=4, xp=35) ai_component = BasicMonster() skill_list_component = SkillList() monster = Entity(x, y, CustomTile.WOLF0, libtcod.white, 'Wolf', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, ai=ai_component, skill_list=skill_list_component) else: fighter_component = Fighter(hp=30, defense=2, power=8, xp=100) ai_component = BasicMonster() skill_list_component = SkillList() monster = Entity(x, y, CustomTile.BEAR0, libtcod.white, 'Bear', blocks=True, fighter=fighter_component, render_order=RenderOrder.ACTOR, ai=ai_component, skill_list=skill_list_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_herbs': item_component = Item(use_function=heal, amount=40) item = Entity(x, y, CustomTile.HEALING_HERBS, libtcod.white, 'Healing Herbs', render_order=RenderOrder.ITEM, item=item_component) elif item_choice == 'sharp_stick': equippable_component = Equippable(EquipmentSlots.MAIN_HAND, power_bonus=3) item = Entity(x, y, CustomTile.SHARP_STICK, libtcod.white, 'Sharp Stick', 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 == 'fire_oil': 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, CustomTile.FIRE_OIL, libtcod.white, 'Fire Oil', 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 play_game(player: object, entities: object, game_map: object, message_log: object, game_state: object, con: object, panel: object, constants: object) -> object: fov_recompute = True fov_map = initialize_fov(game_map) key = libtcod.Key() mouse = libtcod.Mouse() game_state = GameStates.PLAYERS_TURN previous_game_state = game_state targeting_item = None targeting_skill = None while not libtcod.console_is_window_closed(): libtcod.sys_check_for_event( libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, key, mouse) if fov_recompute: recompute_fov(fov_map, player.x, player.y, constants['fov_radius'], constants['fov_light_walls'], constants['fov_algorithm']) render_all(con, panel, entities, player, game_map, fov_map, fov_recompute, message_log, constants['screen_width'], constants['screen_height'], constants['bar_width'], constants['panel_height'], constants['panel_y'], mouse, constants['colors'], game_state) fov_recompute = False libtcod.console_flush() clear_all(con, entities) action = handle_keys(key, game_state, player) mouse_action = handle_mouse(mouse) move = action.get('move') wait = action.get('wait') pickup = action.get('pickup') show_inventory = action.get('show_inventory') drop_inventory = action.get('drop_inventory') inventory_index = action.get('inventory_index') skill_index = action.get('skill_index') take_stairs = action.get('take_stairs') level_up = action.get('level_up') gain_skill = action.get('gain_skill') show_character_screen = action.get('show_character_screen') exit = action.get('exit') fullscreen = action.get('fullscreen') show_skill = action.get('show_skill') left_click = mouse_action.get('left_click') right_click = mouse_action.get('right_click') player_turn_results = [] if move and game_state == GameStates.PLAYERS_TURN: dx, dy = move destination_x = player.x + dx destination_y = player.y + dy if not game_map.is_blocked(destination_x, destination_y): target = get_blocking_entities_at_location( entities, destination_x, destination_y) if target: attack_results = player.fighter.attack(target) player_turn_results.extend(attack_results) else: player.move(dx, dy) fov_recompute = True game_state = GameStates.ENEMY_TURN elif wait: game_state = GameStates.ENEMY_TURN elif pickup and game_state == GameStates.PLAYERS_TURN: for entity in entities: if entity.item and entity.x == player.x and entity.y == player.y: pickup_results = player.inventory.add_item(entity) player_turn_results.extend(pickup_results) break else: message_log.add_message( Message('There is nothing here to pick up.', libtcod.yellow)) if show_inventory: previous_game_state = game_state game_state = GameStates.SHOW_INVENTORY if show_skill: previous_game_state = game_state game_state = GameStates.SHOW_SKILL if drop_inventory: previous_game_state = game_state game_state = GameStates.DROP_INVENTORY if inventory_index is not None and previous_game_state != GameStates.PLAYER_DEAD and inventory_index < len( player.inventory.items): item = player.inventory.items[inventory_index] if game_state == GameStates.SHOW_INVENTORY: player_turn_results.extend( player.inventory.use(item, entities=entities, fov_map=fov_map)) elif game_state == GameStates.DROP_INVENTORY: player_turn_results.extend(player.inventory.drop_item(item)) if skill_index is not None and previous_game_state != GameStates.PLAYER_DEAD and skill_index < len( player.skill_list.skills): skill = player.skill_list.skills[skill_index] if game_state == GameStates.SHOW_SKILL: player_turn_results.extend( player.skill_list.use(skill, entities=entities, fov_map=fov_map)) if take_stairs and game_state == GameStates.PLAYERS_TURN: for entity in entities: if entity.stairs and entity.x == player.x and entity.y == player.y: entities = game_map.next_floor(player, message_log, constants) fov_map = initialize_fov(game_map) fov_recompute = True libtcod.console_clear(con) break else: message_log.add_message( Message('There are no stairs here.', libtcod.yellow)) if gain_skill: player.skill_list.create_skill(player, gain_skill) game_state = previous_game_state if level_up: if level_up == 'hp': player.fighter.base_max_hp += 20 player.fighter.hp += 20 elif level_up == 'str': player.fighter.base_power += 1 elif level_up == 'def': player.fighter.base_defense += 1 if len(player.learnable_skills) > 0: game_state = GameStates.GAIN_SKILL else: game_state = previous_game_state if show_character_screen: previous_game_state = game_state game_state = GameStates.CHARACTER_SCREEN if game_state == GameStates.TARGETING: if left_click: target_x, target_y = left_click if targeting_for_item: item_use_results = player.inventory.use(targeting_item, entities=entities, fov_map=fov_map, target_x=target_x, target_y=target_y) player_turn_results.extend(item_use_results) elif targeting_for_skill: skill_use_results = player.skill_list.use( targeting_skill, entities=entities, fov_map=fov_map, target_x=target_x, target_y=target_y) player_turn_results.extend(skill_use_results) elif right_click: player_turn_results.append({'targeting_cancelled': True}) if exit: if game_state in (GameStates.SHOW_INVENTORY, GameStates.DROP_INVENTORY, GameStates.CHARACTER_SCREEN, GameStates.SHOW_SKILL): game_state = previous_game_state elif game_state == GameStates.TARGETING: player_turn_results.append({'targeting_cancelled': True}) else: save_game(player, entities, game_map, message_log, game_state) return True if fullscreen: libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen()) for player_turn_result in player_turn_results: message = player_turn_result.get('message') dead_entity = player_turn_result.get('dead') item_added = player_turn_result.get('item_added') item_consumed = player_turn_result.get('consumed') item_dropped = player_turn_result.get('item_dropped') equip = player_turn_result.get('equip') targeting_for_item = player_turn_result.get('targeting_for_item') targeting_for_skill = player_turn_result.get('targeting_for_skill') targeting_cancelled = player_turn_result.get('targeting_cancelled') xp = player_turn_result.get('xp') skill_used = player_turn_result.get('skill_used') # TODO skill_used not used properly! if message: message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(dead_entity) else: message = kill_monster(dead_entity) message_log.add_message(message) if item_added: entities.remove(item_added) game_state = GameStates.ENEMY_TURN if item_consumed: game_state = GameStates.ENEMY_TURN if item_dropped: entities.append(item_dropped) game_state = GameStates.ENEMY_TURN if skill_used: game_state = GameStates.ENEMY_TURN if equip: equip_results = player.equipment.toggle_equip(equip) for equip_result in equip_results: equipped = equip_result.get('equipped') dequipped = equip_result.get('dequipped') if equipped: message_log.add_message( Message('You equipped the {0}'.format( equipped.name))) if dequipped: message_log.add_message( Message('You dequipped the {0}'.format( dequipped.name))) game_state = GameStates.ENEMY_TURN if targeting_for_item: previous_game_state = GameStates.PLAYERS_TURN game_state = GameStates.TARGETING targeting_item = targeting_for_item message_log.add_message(targeting_item.item.targeting_message) if targeting_for_skill: previous_game_state = GameStates.PLAYERS_TURN game_state = GameStates.TARGETING targeting_skill = targeting_for_skill # TODO fix messaging. Do skills have targeting messages??? # message_log.add_message(targeting_skill.skill.targeting_message) if targeting_cancelled: game_state = previous_game_state message_log.add_message(Message('Targeting cancelled')) if xp: leveled_up = player.level.add_xp(xp) message_log.add_message( Message('You gain {0} experience points.'.format(xp))) if leveled_up: previous_game_state = game_state game_state = GameStates.LEVEL_UP if game_state == GameStates.ENEMY_TURN: for entity in entities: if entity.ai: enemy_turn_results = entity.ai.take_turn( player, fov_map, game_map, entities) for enemy_turn_result in enemy_turn_results: message = enemy_turn_result.get('message') dead_entity = enemy_turn_result.get('dead') if message: message_log.add_message(message) if dead_entity: if dead_entity == player: message, game_state = kill_player(dead_entity) else: message = kill_monster(dead_entity) message_log.add_message(message) if game_state == GameStates.PLAYER_DEAD: break if game_state == GameStates.PLAYER_DEAD: break else: game_state = GameStates.PLAYERS_TURN