def take_turn(self, fov_map, game_map, entities): results = [] mob = self.owner mob.fighter.fov_range = 1 # Blind Mob AI if self.duration > 0: self.duration -= 1 else: # Blind Wore Off Restore Default mob.ai = self.previous_ai mob.fighter.fov_range = MOBS.get(mob.json_index).get('fov_range') results.append({ 'message': Message('The %s can see clearly now!' % mob.name, libtcod.red) }) return results
def use(self, item_entity, **kwargs): results = [] item_component = item_entity.item # Check if Item Has a "Use" or "Equip" Function if not item_component.use_function: equippable_component = item_entity.equippable if equippable_component: results.append({'equip': item_entity}) else: results.append({ 'message': Message('The %s cannot be used.' % item_entity.name, libtcod.yellow) }) else: # Check Item Type if Targeting Component is Required 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} # Most Important Connecting Function # Note: Pass the user and various catch-all-keyword-args item_use_results = item_component.use_function( self.owner, **kwargs) # Check if Item is Altered after Usage for item_use_result in item_use_results: # Remove Item from Inventory if item_use_result.get('consumed'): self.remove_item(item_entity) break # Keep Item in Inventory if item_use_result.get('reuseable'): # results.append({'message': Message("")}) break results.extend(item_use_results) return results
def break_container(*args, **kwargs): # print('break_container') attacker_entity = args[0] container_entity = args[1] entities = kwargs.get('entities') # print('container_entity:', container_entity.name) # print('attacker_entity:', attacker_entity.name) # print('container inventory:', container_entity.inventory.items) results = [{"change_map_object": [container_entity, 2], "spawn_particle": ["hit", container_entity.position.x, container_entity.position.y, None], 'message': Message('You break open the {}...'.format(container_entity.name.lower()), tcod.yellow)}] for item in container_entity.inventory.items: entities.append(item) container_entity.inventory.drop_all_items() return results
def kill_player(player): player.char = "%" # 37 player.color = libtcod.dark_red # return 'You died!', GameStates.PLAYER_DEAD return Message('You died!', libtcod.red), GameStates.PLAYER_DEAD
def advance_on_current_target(self, entities, game_map, fov_map, dist, radius, results, target_x, target_y): mob = self.owner # Check to Cast Spell or Perform Skill can_cast_spells = getattr(self.owner.spellcaster, 'has_spells', None) if self.current_target and can_cast_spells: if self.owner.spellcaster.has_spells: # Select Spell # Check Self HP to Heal if self.owner.fighter.hp <= self.owner.fighter.max_hp // 2 and self.owner.spellcaster.has_spell_type( "restorative"): try: spell_to_cast = choice([ spell for spell in self.owner.spellcaster.spells if spell.type & {"restorative"} and spell.is_ready ]) target = self.owner except IndexError: # Empty sequence/no spells spell_to_cast = None target = None elif self.owner.faction.check_enemy( self.current_target.faction.faction_name): try: spell_to_cast = choice([ spell for spell in self.owner.spellcaster.spells if spell.type & {"offensive", "buff", "environment"} and spell.is_ready ]) target = self.current_target except IndexError: # Empty sequence/no spells spell_to_cast = None target = None else: spell_to_cast = None target = None if spell_to_cast: results.extend( self.owner.spellcaster.cast(spell_to_cast, caster=self.owner, target=target)) return results # Check Enemy to cast Spell # if self.owner.faction.check_enemy(self.current_target.faction.faction_name) and \ # mob.position.distance_to(self.current_target.position.x, self.current_target.position.y) < 5: # # Select Self Buff or Offensive Spell # results.extend(self.owner.spellcaster.cast(cast_thorn_spike, spell, target=self.current_target)) # return results # Check if Stuck to Reset Path if self.stuck_time > self.stuck_time_max: self.path = [] self.stuck_time = 0 # Target is Within Range, Attack if self.current_target: if self.current_target.fighter.hp > 0 and \ mob.position.distance_to(self.current_target.position.x, self.current_target.position.y) <= self.owner.fighter.attack_range: # print('# Target is Within Range, Attack') attack_results = mob.fighter.attack(self.current_target) results.extend(attack_results) return results # Close distance to Target or Evade if (self.current_target and not self.path) or \ (self.last_target_position and not self.path) or \ (self.current_target and not (target_y, target_x) in self.path): self.path = mob.position.movement_function( target_x, target_y, game_map, ) # self.path = mob.position.move_astar(target_x, target_y, game_map) # Move Entity to Next Floor if Seeking if self.encounter.main_target == game_map.stairs and \ self.owner.position.x == game_map.stairs.position.x and \ self.owner.position.y == game_map.stairs.position.y: entities.remove(self.owner) self.encounter.mob_list.remove(self.owner) game_map.next_floor_entities.append(self.owner) tile_index = game_map.tileset_tiles[self.owner.position.y][ self.owner.position.x] game_map.tile_cost[self.owner.position.y][ self.owner.position.x] = game_map.tile_set.get( '%s' % tile_index).get('tile_cost') results.append({ 'message': Message('{} ascended to the next level!'.format( self.owner.name)) }) # Finally Move if len(self.path) > 0: results.extend(self.move_on_path(game_map, entities, results)) self.direction_vector = get_direction(self.owner.position.x, self.owner.position.y, target_x, target_y) return results
def activate_event(self, entities, particles): results = [] if self.event_type == 'spawn_mob': mob_indexes = self.function_kwargs.get('mobs') faction = self.function_kwargs.get('faction') ai_type = self.function_kwargs.get('ai_type') area = self.function_kwargs.get('area_of_interest') spawn_x, spawn_y = self.function_kwargs.get('position') target_entity = self.function_kwargs.get('target_entity') follow_entity = self.function_kwargs.get('follow_entity') origin_x = self.function_kwargs.get('origin_x') origin_y = self.function_kwargs.get('origin_y') e = Encounter(self.game_map, area, len(self.game_map.encounters) + 1, ai_type=ai_type) if self.game_map.player.position.distance_to(spawn_x, spawn_y) < 5: results.append({ 'message': Message( "You spot reinforcements coming from the above floor!", tcod.dark_yellow) }) else: results.append({ 'Message': Message("You hear barking and yelling in the distance...", tcod.dark_yellow) }) for mob_index in mob_indexes: x, y = self.game_map.obtain_closest_spawn_point( spawn_x, spawn_y) mob_stats = MOBS.get(mob_index) if x and y: self.game_map.tile_cost[y][x] = 99 entities.append( generate_mob(x, y, mob_stats, mob_index, e, faction, ai_type, entities, follow_entity=follow_entity, target_entity=target_entity, origin_x=origin_x, origin_y=origin_y)) else: print( 'Error! Coudn\'t find open spawn point for {} at ({}, {})' .format(mob_stats.get('name'), spawn_x, spawn_y)) # (x, y, mob_stats, mob_index, encounter_group, faction, ai, entities, dialogue_component=None): elif self.event_type == 'open_gate': # print('event_type:', self.event_type) map_objects = self.function_kwargs.get('map_objects') default_floor_tile = self.function_kwargs.get( 'default_floor_tile', "2") for map_object in map_objects: # print('removing : ', map_object) self.game_map.map_objects.remove(map_object) place_tile(self.game_map, map_object.position.x, map_object.position.y, default_floor_tile) # results.append({"change_map_object": [map_object, 2], "message": Message("The {} has opened!".format(map_object.name))}) results.append({ "message": Message("The {} has opened!".format(map_object.name)) }) return results
def create_item_entity(item_index, x=None, y=None): item_stats = ITEMS.get(item_index) if x and y: position_component = Position(x, y) else: position_component = None # Assemble an Item Entity with it's Required Components if item_stats.get('type') == 'consumable': item_component = Item( use_function=eval(item_stats.get('use_function', "nothing")), amount=item_stats.get('amount'), radius=item_stats.get('radius'), damage=item_stats.get('damage'), targeting_message=Message(item_stats.get('targeting_message')), targeting=item_stats.get('targeting'), maximum_range=item_stats.get('range'), targeting_type=item_stats.get('targeting_type'), description=item_stats.get('description')) item_entity = Entity(item_stats.get('glyph'), item_stats.get('color'), item_stats.get('name'), json_index=item_index, position=position_component, render_order=RenderOrder.ITEM, item=item_component) elif item_stats.get('type') == 'reuseable': item_component = Item(use_function=eval( item_stats.get('use_function', "nothing")), name=item_stats.get('name'), text=item_stats.get('text'), description=item_stats.get('description')) item_entity = Entity( item_stats.get('glyph'), item_stats.get('color'), item_stats.get('name'), position=position_component, json_index=item_index, render_order=RenderOrder.ITEM, item=item_component, ) elif item_stats.get('type') == 'equip': equippable_component = Equippable( item_stats.get('slot'), power_bonus=item_stats.get('attack', 0), defense_bonus=item_stats.get('defense', 0), max_hp_bonus=item_stats.get('hp', 0), description=item_stats.get('description')) item_entity = Entity(item_stats.get('glyph'), item_stats.get('color'), item_stats.get('name'), position=position_component, render_order=RenderOrder.ITEM, json_index=item_index, equippable=equippable_component) else: print( 'Item Name: %s, Item type: %s isn\'t a suitable item type! Double check ./assets/item.json' % (item_stats.get('name'), item_stats.get('type'))) raise ValueError return item_entity
def _generate_random_items(number_of_items, dungeon_level): # Create Item Entities item_entities = [] item_chances = { item: spawn_chance( [[item_stats.get('spawn_chance'), item_stats.get('item_level')]], dungeon_level) for item, item_stats in ITEMS.items() if not item_stats.get('unique', False) } # Generate Random Number of Items for i in range(number_of_items): # Randomly Select an Item to Spawn item_index = random_choice_from_dict(item_chances) item_stats = ITEMS[item_index] if item_stats.get('type') == 'consumable': item_component = Item( use_function=eval(item_stats.get('use_function', "nothing")), amount=item_stats.get('amount'), radius=item_stats.get('radius'), damage=item_stats.get('damage'), targeting_message=Message(item_stats.get('targeting_message')), targeting=item_stats.get('targeting'), maximum_range=item_stats.get('range'), targeting_type=item_stats.get('targeting_type'), description=item_stats.get('description')) item_entity = Entity(item_stats.get('glyph'), item_stats.get('color'), item_stats.get('name'), item_index, render_order=RenderOrder.ITEM, item=item_component) elif item_stats.get('type') == 'reuseable': item_component = Item(use_function=eval( item_stats.get('use_function', "nothing")), name=item_stats.get('name'), text=item_stats.get('text'), description=item_stats.get('description')) item_entity = Entity( item_stats.get('glyph'), item_stats.get('color'), item_stats.get('name'), item_index, render_order=RenderOrder.ITEM, item=item_component, ) elif item_stats.get('type') == 'equip': equippable_component = Equippable( item_stats.get('slot'), power_bonus=item_stats.get('attack', 0), defense_bonus=item_stats.get('defense', 0), max_hp_bonus=item_stats.get('hp', 0), description=item_stats.get('description')) item_entity = Entity(item_stats.get('glyph'), item_stats.get('color'), item_stats.get('name'), item_index, equippable=equippable_component) item_entities.append(item_entity) return item_entities
def cast_teleport(*args, **kwargs): caster = args[0] game_map = kwargs.get("game_map") target_x = kwargs.get("target_x") target_y = kwargs.get("target_y") reveal_all = kwargs.get("reveal_all") results = [{ 'message': Message('You focus on the crystal\'s reflection...', tcod.light_green) }] if (game_map.walkable[target_y][target_x] and game_map.tile_cost[target_y][target_x] == 1 and game_map.explored[target_y][target_x]) or reveal_all: results.append({ 'consumed': True, 'message': Message('A bright light engulfs you for a moment.', tcod.light_green) }) # 10% Chance of Teleporting to to a Random Location! teleport_roll = randint(1, 10) if teleport_roll == 1: # Attempt to Find a Random Location max_tries = 30 tries = 0 random_x, random_y = None, None while tries < max_tries: random_x = randint(1, game_map.width - 1) random_y = randint(1, game_map.height - 1) # print('\n', random_x, random_y) # print(game_map.walkable[random_y][random_x], game_map.tile_cost[random_y][random_x], game_map.tileset_tiles[random_y][random_x]) if game_map.walkable[random_y][random_x] and game_map.tile_cost[ random_y][random_x] == 1: break random_x, random_y = None, None tries += 1 # Fail safe if no proper location is found if not random_x or not random_y: results.append({ 'message': Message( 'Nothing seems to have happened? You no longer have a crystal in your hands.', tcod.light_yellow) }) else: results.append({ 'message': Message( 'You have teleported somewhere else entirely? You look around warily.', tcod.light_red) }) game_map.walkable[random_y][random_x] = True game_map.transparent[random_y][random_x] = True caster.position.x, caster.position.y = random_x, random_y else: results.append({ 'message': Message('You have teleported successfully!', tcod.light_green) }) game_map.walkable[caster.position.y][caster.position.x] = True game_map.transparent[caster.position.y][caster.position.x] = True caster.position.x, caster.position.y = target_x, target_y else: results.extend([{ 'consumed': False, 'message': Message( 'But you can\'t focus on the location. Nothing appears to have happened.', tcod.yellow) }]) return results
def cast_fireball(*args, **kwargs): caster = args[0] entities = kwargs.get('entities') fov_map = kwargs.get('fov_map') damage = kwargs.get('damage') radius = kwargs.get('radius') game_map = kwargs.get('game_map') target_x = kwargs.get('target_x') target_y = kwargs.get('target_y') results = [] # Tile not within range if not fov_map.fov[target_x][target_y]: results.append({ 'consumed': False, 'message': Message('You cannot target this area!', tcod.yellow) }) return results results.append({ 'consumed': True, 'message': Message( 'The fireball explodes, burning everything within %s tiles!' % radius, tcod.orange) }) # Damage Entities in Radius # _entities = [entity for entity in entities if entity.position] for entity in entities: if entity.position.distance(target_x, target_y) <= radius and entity.fighter: results.append({ 'message': Message( 'The %s gets burned for %s hit points.' % (entity.name, damage), tcod.orange) }) results.extend(entity.fighter.take_damage(damage, caster)) # "Destroy" Map Object print(game_map.map_objects) for entity in game_map.map_objects: if entity.position.distance( target_x, target_y ) <= radius and entity.map_object.breakable and "flammable" in entity.map_object.properties: # map_object_component = entity.map_object if entity.inventory: for item in entity.inventory.items: entities.append(item) entity.inventory.drop_all_items() results.append({"change_map_object": [entity, 2]}) # Apply Fire Particle to Each Affected Tile # lower_bound = radius // 2 # upper_bound = ceil(radius / 2) fire_particle_system = ParticleSystem() for x in range(target_x - radius, target_x + radius + 1): for y in range(target_y - radius, target_y + radius + 1): dx = target_x - x dy = target_y - y distance_squared = dx * dx + dy * dy # Check if Obstacle or Flammable Tile if game_map.tile_cost[y][ x] != 0 and distance_squared <= radius * radius: results.append( {"spawn_particle": ["fire", x, y, fire_particle_system]}) return results
def cast_lightning(*args, **kwargs): caster = args[0] entities = kwargs.get('entities') # fov_map = kwargs.get('fov_map') damage = kwargs.get('damage') game_map = kwargs.get("game_map") maximum_range = kwargs.get('maximum_range') results = [] target = None closest_distance = maximum_range + 1 # Search Closest Entity for entity in entities: if entity.fighter and entity != caster and ( entity.position.x, entity.position.y) in caster.fighter.curr_fov_map: distance = caster.position.distance_to(entity.position.x, entity.position.y) if distance < closest_distance and caster.faction.check_enemy( entity.faction.faction_name): target = entity closest_distance = distance if target: lightning_astar_path = caster.position.move_astar(target.position.x, target.position.y, game_map, diagonal_cost=1.00) results.append({ 'consumed': True, 'target': target, 'message': Message( 'A lightning bolt strikes %s with a loud thunder! %s sustains %s damage.' % (target.name, target.name, damage)) }) results.extend(target.fighter.take_damage(damage, caster)) # Create Lightning Particles Along Path to Target Entity # TODO: Check if surrounding tiles conduct electricity lightning_particle_system = ParticleSystem() for path_x, path_y in lightning_astar_path: results.append({ 'spawn_particle': ["lightning", path_y, path_x, lightning_particle_system] }) else: results.append({ 'consumed': False, 'target': None, 'message': Message('No enemy is close enough to strike.', tcod.red) }) return results
def see_through_key_hole(*args, **kwargs): door_entity = args[1] results = [{'see_through_key_hole': door_entity, 'message': Message('You look through the key hole into the next room.', tcod.yellow)}] return results
def event_gate(*args, **kwargs): return [{'message': Message("You can't spot a keyhole. There has to be another way to open the gate?", tcod.dark_yellow)}]
def already_toggle_switch(*args, **kwargs): return [{'message': Message("The lever has already been pulled.", tcod.dark_yellow)}]
def burn(*args, **kwargs): results = [] caster = args[0] stove_entity = args[1] burn_damage = 10 player = kwargs.get('player') if caster == player: identifier = "You burn yourself for {} damage.".format(burn_damage) tcod_color = tcod.flame else: identifier = "The {} burns itself for {} damage.".format(caster.name, burn_damage) tcod_color = tcod.dark_yellow fire_particle_system = ParticleSystem() results.append({"spawn_particle": ["fire", caster.position.x, caster.position.y, fire_particle_system], 'message': Message('{}'.format(identifier), tcod_color)}) results.extend(caster.fighter.take_damage(burn_damage)) return results
def empty_chest(*args, **kwargs): # print('empty_chest') results = [] results.append({'chest': False, 'message': Message('The chest is already been opened.', tcod.yellow)}) return results