def recv(sock): encoded = sock.recv(RECV_SIZE) message = Message(encoded=encoded) if message.valid: if message.state is not None: message.state = ServerState(message.state) return (message.identifier, int(message.number), message.data, message.state) return None, None, None, None
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(f'The {item_entity.name} can not be used.', tcod.yellow) }) else: if item_component.targeting and not (kwargs.get('target_x') and 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 tcod.map_is_in_fov(fov_map, target_x, target_y): results.append({ 'consumed': False, 'message': Message('You can not target a tile outside of your field of view.', tcod.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( f'The eyes of the {entity.name} look vacant, as they start to stumble around.', tcod.light_green) }) break else: results.append({ 'consumed': False, 'message': Message('There is no targetable enemy at the location.', tcod.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 tcod.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( f'A lightning bolt strikes the {target.name} with a loud thunder! The damage is {damage}.', tcod.orange) }) results.extend(target.fighter.take_damage(damage)) else: results.append({ 'consumed': False, 'target': None, 'message': Message('No enemy close enough to strike', tcod.red) }) return results
def add_message(self, message: Message) -> None: # split the message if necessary new_msg_lines = wrap(message.text, self.width) for line in new_msg_lines: # if message buffer is full, remove first # line to make room for new ones. if len(self.messages) == self.height: del self.messages[0] # Now add new message self.messages.append(Message(line, message.color))
def attack(self, target): results = [] damage = self.power - target.fighter.defense if damage > 0: results.append({ 'message': Message( f'{self.owner.name.capitalize()} attacks {target.name} for {damage} hit points!', tcod.white) }) results.extend(target.fighter.take_damage(damage)) else: results.append({ 'message': Message( f'{self.owner.name.capitalize()} attacks {target.name} but does no damage.', tcod.white) }) 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 tcod.map_is_in_fov(fov_map, target_x, target_y): results.append({ 'consumed': False, 'message': Message('You can not target a tile outside of your field of view', tcod.yellow) }) return results results.append({ 'consumed': True, 'message': Message( f'The fireball explodes, burning everything in a {radius} tile radius.', tcod.orange) }) for entity in entities: if entity.distance(target_x, target_y) <= radius and entity.fighter: results.append({ 'message': Message( f'The {entity.name} gets burned for {damage} hit points!', tcod.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 can not carry anymore; inventory is full.', tcod.yellow) }) else: self.items.append(item) results.append({ 'item_added': item, 'message': Message(f'You pick up the {item.name}.', tcod.blue) }) return results
def kill_monster(monster: Entity) -> Message: death_message = Message(f'{monster.name.capitalize()} is dead!', tcod.orange) monster.char = '%' monster.color = tcod.dark_red monster.blocks = False monster.fighter = None monster.ai = None monster.name = f'remains of {monster.name.capitalize()}' 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', tcod.yellow) }) else: entity.fighter.heal(amount) results.append({ 'consumed': True, 'message': Message('Your wounds start to feel better!', tcod.green) }) 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, constants['colors']) player.fighter.heal(player.fighter.max_hp // 2) message_log.add_message( Message('You take a moment to rest, and recover your strength.', tcod.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( f'The {self.owner.name} is no longer confused.', tcod.red)}) return results
def drop_item(self, item): results = [] if self.owner.equipment.main_hand == item or self.owner.equipment.off_hand == item: self.owser.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(f'You dropped the {item.name}', tcod.yellow) }) return results
def kill_player(player: Entity) -> Tuple[Message, GameStates]: player.char = '%' player.color = tcod.dark_red player.fighter.hp = 0 return Message('You died!', tcod.red), GameStates.PLAYER_DEAD
def play_game(player, entities, game_map, message_log, game_state, con, panel, constants): fov_recompute = True fov_map = initialize_fov(game_map) input_handler = InputHandler() previous_game_state = game_state targeting_item = None mouse = tcod.Mouse() while True: 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 tcod.console_flush() clear_all(con, entities) for event in tcod.event.get(): input_handler.dispatch(event) # print(f'Current game state: {game_state}') input_handler.set_game_state(game_state) user_input = input_handler.get_user_input() move = user_input.get('move') wait = user_input.get('wait') pickup = user_input.get('pickup') show_inventory = user_input.get('show_inventory') drop_inventory = user_input.get('drop_inventory') inventory_index = user_input.get('inventory_index') take_stairs = user_input.get('take_stairs') level_up = user_input.get('level_up') show_character_screen = user_input.get('show_character_screen') exit = user_input.get("exit") fullscreen = user_input.get("fullscreen") left_click = user_input.get('left_click') right_click = user_input.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('Nothing to pick up.', tcod.yellow)) if show_inventory: previous_game_state = game_state game_state = GameStates.SHOW_INVENTORY 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)) else: player_turn_results.extend(player.inventory.drop(item)) 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 con.clear() break else: message_log.add_message( Message('There are no stairs here.', tcod.yellow)) 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 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 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 right_click: player_turn_results.append({'targeting_cancelled': True}) if exit: if game_state in (GameStates.CHARACTER_SCREEN, GameStates.DROP_INVENTORY, GameStates.SHOW_INVENTORY): game_state = previous_game_state elif game_state == GameStates.TARGETING: player_turn_results.append({'targeting_cancelled': True}) else: if game_state != GameStates.PLAYER_DEAD: save_game(player, entities, game_map, message_log, game_state) raise SystemExit() if fullscreen: tcod.console_set_fullscreen(not tcod.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('item_consumed') item_dropped = player_turn_result.get('item_dropped') equip = player_turn_result.get('equip') targeting = player_turn_result.get('targeting') targeting_cancelled = player_turn_result.get('targeting_cancelled') xp = player_turn_result.get('xp') 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 equip: equip_results = GameStates.ENEMY_TURN for equip_result in equip_results: equipped = equip_result.get('equipped') dequipped = equip_result.get('dequipped') if equipped: message_log.add_message( Message(f'You equipped the {equipped.name}')) if dequipped: message_log.add_message( Message(f'You dequipped the {dequipped.name}')) game_state = GameStates.ENEMY_TURN if targeting: previous_game_state = game_state game_state = GameStates.TARGETING targeting_item = targeting message_log.add_message(targeting_item.item.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(f'You gain {xp} experience points.')) if leveled_up: message_log.add_message( Message( f'Your battle skills grow stronger. You have reached level {player.level.current_level}.', tcod.yellow)) 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 else: game_state = GameStates.PLAYERS_TURN
def send(sock, identifier, number, data=None, state=None): sock.sendall(Message(identifier, number, data, state).encode())
def place_entities(self, room, entities, colors): # get a random number of monsters, and items 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) num_monsters = randint(0, max_monsters_per_room) num_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(num_monsters): # choose a random location in the room 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 ]): monster_chance = random_choice_from_dict(monster_chances) if monster_chance == 'orc': fighter_component = Fighter(hp=20, defense=0, power=4, xp=35) ai_component = BasicMonster() monster = Entity(x, y, 'o', colors.get('orc'), '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', colors.get('troll'), 'Troll', blocks=True, render_order=RenderOrder.ACTOR, fighter=fighter_component, ai=ai_component) entities.append(monster) for i in range(num_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, '!', colors.get('magic_item'), '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, '/', tcod.sky, 'Sword', equippable=equippable_component) elif item_choice == 'shield': equippable_component = Equippable(EquipmentSlots.MAIN_HAND, defense_bonus=1) item = Entity(x, y, '[', tcod.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 fot the fireball, or right-click to cancel', tcod.light_cyan), damage=25, radius=3) item = Entity(x, y, '*', colors.get('magic_item'), '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', tcod.light_cyan), damage=12, radius=3) item = Entity(x, y, '?', colors.get('magic_item'), '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, '&', colors.get('magic_item'), 'Lighning Scroll', render_order=RenderOrder.ITEM, item=item_component) entities.append(item)