def assertConsolesEqual(a, b): for y in range(libtcodpy.console_get_height(a)): for x in range(libtcodpy.console_get_width(a)): assert libtcodpy.console_get_char(a, x, y) == \ libtcodpy.console_get_char(b, x, y) assert libtcodpy.console_get_char_foreground(a, x, y) == \ libtcodpy.console_get_char_foreground(b, x, y) assert libtcodpy.console_get_char_background(a, x, y) == \ libtcodpy.console_get_char_background(b, x, y)
def test_console_buffer(console): buffer = libtcodpy.ConsoleBuffer( libtcodpy.console_get_width(console), libtcodpy.console_get_height(console), ) buffer = buffer.copy() buffer.set_fore(0, 0, 0, 0, 0, '@') buffer.set_back(0, 0, 0, 0, 0) buffer.set(0, 0, 0, 0, 0, 0, 0, 0, '@') buffer.blit(console)
def clear_all(console, entities, player): screen_width = tcod.console_get_width(console) screen_height = tcod.console_get_height(console) for entity in entities: tcod.console_put_char( console, entity.x - player.x + int(screen_width / 2), entity.y - player.y + int(screen_height / 2), " ", tcod.BKGND_NONE, )
def render(self): """ Combine the map view and the menu view by blitting one onto the other. """ console = self.map_renderer.render(self.game_data.player) sub_console = self.menu_renderer.render(InGameMenuState.keys) libtcod.console_blit(sub_console, 0, 0, 0, 0, console, libtcod.console_get_width(console) - libtcod.console_get_width(sub_console), 0) return console
def render(self): """ Render the current state of the battle. Called as many times as required by the game loop. """ console = self.renderer.render(self.game_data.battle_data, self.messages, self.selecting_pokeball) if len(self.messages) == 0 and self.display_level_up is not None: sub_console = self.level_up_renderer.render(self.display_level_up[0].creature, self.display_level_up[1]) libtcod.console_blit(sub_console, 0, 0, 0, 0, console, 0, 0) # If we're in the process of catching a creature then there is an # extra step which renders the catch graphics on top of the screen. if self.catching_with_pokeball: percent_to_display = self._percent_of_catch_to_display() message = None if percent_to_display == self.percent_of_creature_caught: message = battle_calculations.get_catch_message(self.percent_of_creature_caught, self.game_data.battle_data.defending_creature()) sub_console = self.catch_graphic_renderer.render(self.catching_with_pokeball, percent_to_display, message) libtcod.console_blit(sub_console, 0, 0, 0, 0, console, libtcod.console_get_width(console) // 2 - libtcod.console_get_width(sub_console) // 2, libtcod.console_get_height(console) // 2 - libtcod.console_get_height(sub_console) // 2) # The check to see whether to end the battle is done once in the # render function so that we can guarantee that it will get called # within a 30fps time frame. if len(self.messages) == 0 and self.display_level_up is None and self.end_battle: self.game.end_wild_battle() return console
def test_console_fill(console): width = libtcodpy.console_get_width(console) height = libtcodpy.console_get_height(console) fill = [i % 256 for i in range(width * height)] libtcodpy.console_fill_background(console, fill, fill, fill) libtcodpy.console_fill_foreground(console, fill, fill, fill) libtcodpy.console_fill_char(console, fill) # verify fill bg, fg, ch = [], [], [] for y in range(height): for x in range(width): bg.append(libtcodpy.console_get_char_background(console, x, y)[0]) fg.append(libtcodpy.console_get_char_foreground(console, x, y)[0]) ch.append(libtcodpy.console_get_char(console, x, y)) assert fill == bg assert fill == fg assert fill == ch
def test_console_fill_numpy(console): width = libtcodpy.console_get_width(console) height = libtcodpy.console_get_height(console) fill = numpy.zeros((height, width), dtype=numpy.intc) for y in range(height): fill[y, :] = y % 256 libtcodpy.console_fill_background(console, fill, fill, fill) libtcodpy.console_fill_foreground(console, fill, fill, fill) libtcodpy.console_fill_char(console, fill) # verify fill bg = numpy.zeros((height, width), dtype=numpy.intc) fg = numpy.zeros((height, width), dtype=numpy.intc) ch = numpy.zeros((height, width), dtype=numpy.intc) for y in range(height): for x in range(width): bg[y, x] = libtcodpy.console_get_char_background(console, x, y)[0] fg[y, x] = libtcodpy.console_get_char_foreground(console, x, y)[0] ch[y, x] = libtcodpy.console_get_char(console, x, y) fill = fill.tolist() assert fill == bg.tolist() assert fill == fg.tolist() assert fill == ch.tolist()
def play_game( console, panel, bar_width, message_log, options, viewing_map=False ): color_scheme = get_scheme(options.get("color_scheme"), ColorSchemes) input_scheme = get_scheme(options.get("input_scheme"), InputSchemes) game_map = GameMap(1) start_tile = LEVEL_CONFIGURATIONS.get(1).get("start_tile") if viewing_map: player_tile = (int(game_map.width / 2), int(game_map.height / 2)) else: player_tile = game_map.find_open_tile(tile_type=start_tile) player_char = "@" if not viewing_map else " " player_sight = Sight() player_fighter = Fighter(hp=20, defense=1, attack=1, damage=2) player_slots = Slots() player_container = Container(20) player = Entity( *player_tile, player_char, tcod.white, "player", render_order=RenderOrder.PLAYER, components={ "sight": player_sight, "fighter": player_fighter, "slots": player_slots, "container": player_container, }, ) game_map.entities.append(player) recompute_fov = True fov_map = game_map.generate_fov_map() memory = [ [False for y in range(game_map.height)] for x in range(game_map.width) ] key = tcod.Key() mouse = tcod.Mouse() game_state = GameStates.PLAYER_TURN previous_game_state = game_state previous_max_hp = player.fighter.max_hp key_cursor = (0, 0) menu_selection = 0 inventory_options = None looking = False combining = None throwing = None exit_queued = False while not tcod.console_is_window_closed(): if recompute_fov: player.sight.get_fov(fov_map, memory) render_all( console, panel, bar_width, message_log, game_map, player, fov_map, memory, color_scheme.value, game_state, mouse, menu_selection, key_cursor if game_state is GameStates.TARGETING else None, inventory_options, viewing_map, ) tcod.console_flush() tcod.sys_wait_for_event( tcod.EVENT_KEY_PRESS | tcod.EVENT_MOUSE, key, mouse, True ) clear_all(console, game_map.entities, player) recompute_fov = False mouse_action = handle_mouse(mouse) action = input_scheme.value.handle_key(key, game_state) left_click = mouse_action.get("left_click") right_click = mouse_action.get("right_click") direction = action.get("direction") inventory = action.get("inventory") index = action.get("index") select = action.get("select") pickup = action.get("pickup") drop = action.get("drop") use = action.get("use") combine = action.get("combine") throw = action.get("throw") look = action.get("look") wait = action.get("wait") restart = action.get("restart") exit_action = action.get("exit") fullscreen = action.get("fullscreen") color_scheme_input = action.get("color_scheme") input_scheme_input = action.get("input_scheme") player_results = {} player_acted = False do_use = False combine_target = None do_target = False if left_click and game_state is GameStates.TARGETING: key_cursor = get_mouse_tile( tcod.console_get_width(console), tcod.console_get_height(console), player.x, player.y, *left_click, ) do_target = True if right_click: if game_state is GameStates.TARGETING: game_state = previous_game_state throwing = None looking = False else: mouse_tile = get_mouse_tile( tcod.console_get_width(console), tcod.console_get_height(console), player.x, player.y, *right_click, ) look_message = get_look_message( *mouse_tile, game_map, fov_map, player ) if look_message: message_log.add_message(look_message) if direction: if game_state is GameStates.PLAYER_TURN: move = action.get("move") # face = action.get('face') dx, dy = direction # moved = False if move: if player.move(dx, dy, game_map, face=False): player_acted = True recompute_fov = True entities_at_tile = game_map.get_entities_at_tile( player.x, player.y ) entities_at_tile.remove(player) if entities_at_tile: entity_str = join_list( [ entity.indefinite_name for entity in entities_at_tile ] ) message_log.add_message( Message( f"You see {entity_str}.", tcod.light_gray, ) ) # moved = True else: blocking_entities = game_map.get_entities_at_tile( player.x + dx, player.y + dy, True ) if blocking_entities: target = blocking_entities[0] attack_results = player.fighter.attack_entity( target.fighter, is_player=True ) player_results.update(attack_results) player_acted = True # moved = True elif ( game_map.get_tile( player.x + dx, player.y + dy, raw=True ) is STAIRS ): dungeon_level = game_map.dungeon_level + 1 configuration = LEVEL_CONFIGURATIONS.get( dungeon_level ) if not configuration: game_state = GameStates.VICTORY else: game_map = GameMap(game_map.dungeon_level + 1) # player.fighter.base_max_hp += 10 player.fighter.hp = player.fighter.max_hp start_tile = configuration.get("start_tile") if start_tile: ( player.x, player.y, ) = game_map.find_open_tile( tile_type=start_tile ) else: ( player.x, player.y, ) = game_map.find_open_tile() game_map.entities.append(player) recompute_fov = True fov_map = game_map.generate_fov_map() memory = [ [False for y in range(game_map.height)] for x in range(game_map.width) ] player_acted = False tcod.console_clear(console) # In the event that the player moves into a wall, do not adjust # facing # if face and (not move or moved): # player.sight.face(atan2(dy, dx)) # player_acted = True # recompute_fov = True elif game_state is GameStates.INVENTORY: dy = direction[1] menu_selection += dy if menu_selection < 0: menu_selection = len(inventory_options) - 1 elif menu_selection >= len(inventory_options): menu_selection = 0 elif game_state is GameStates.TARGETING: # Moves the key_cursor in the given direction key_cursor = move_cursor(key_cursor, *direction) if inventory: menu_selection = 0 inventory_options = construct_inventory_options(player) if game_state is GameStates.INVENTORY: game_state = previous_game_state elif game_state is GameStates.PLAYER_TURN: previous_game_state = game_state game_state = GameStates.INVENTORY # is not None check is required since 0 evaluates to False if index is not None: if game_state is GameStates.INVENTORY: menu_selection = max(0, min(len(inventory_options) - 1, index)) if combining and len(inventory_options): combine_target = player.container.get_item( inventory_options[menu_selection] ) if select: if game_state is GameStates.INVENTORY: if combining and menu_selection < len(inventory_options): combine_target = player.container.get_item( inventory_options[menu_selection] ) else: do_use = True elif game_state is GameStates.TARGETING: do_target = True if pickup and game_state is GameStates.PLAYER_TURN: entities_at_tile = game_map.get_entities_at_tile( player.x, player.y ) for entity in entities_at_tile: if entity.item: player_results.update(player.container.add_item(entity)) player_acted = True break else: message_log.add_message( Message("There is nothing here to pick up.", tcod.yellow) ) if drop and game_state is GameStates.INVENTORY: if menu_selection < len(inventory_options): item = player.container.get_item( inventory_options[menu_selection] ) player.container.items.remove(item) if player.slots.is_equipped(item): player.slots.toggle_equip(item) item.x = player.x item.y = player.y game_map.entities.append(item) message_log.add_message( Message( f"You drop {item.definite_name}.", tcod.light_blue, ) ) player_acted = True if use and game_state is GameStates.INVENTORY: do_use = True if combine and game_state is GameStates.INVENTORY: if menu_selection < len(inventory_options): selected_item = player.container.get_item( inventory_options[menu_selection] ) if not combining: combining = selected_item else: combine_target = selected_item previous_game_state = GameStates.PLAYER_TURN if throw and game_state is GameStates.INVENTORY: if menu_selection < len(inventory_options): throwing = player.container.get_item( inventory_options[menu_selection] ) previous_game_state = GameStates.PLAYER_TURN game_state = GameStates.TARGETING key_cursor = (player.x, player.y) message_log.add_message( Message( "Left-click or navigate to a tile to throw. " "Right-click or escape to cancel.", tcod.light_gray, ) ) if look and game_state is not GameStates.TARGETING: previous_game_state = game_state game_state = GameStates.TARGETING looking = True key_cursor = (player.x, player.y) message_log.add_message( Message( "Select a tile to look at. Escape to cancel.", tcod.light_gray, ) ) if wait and game_state is GameStates.PLAYER_TURN: if viewing_map: game_map = GameMap(game_map.dungeon_level + 1) player.x = int(game_map.width / 2) player.y = int(game_map.height / 2) else: player_acted = True if restart and game_state is GameStates.PLAYER_DEAD: return True if exit_action: if game_state is GameStates.INVENTORY: game_state = previous_game_state combining = None elif game_state is GameStates.TARGETING: game_state = previous_game_state throwing = None looking = False elif exit_queued: return False else: exit_queued = True message_log.add_message( Message( "Press escape again to quit the game.", tcod.light_gray ) ) if fullscreen: tcod.console_set_fullscreen(not tcod.console_is_fullscreen()) if color_scheme_input: color_scheme = cycle_scheme( color_scheme, ColorSchemes, color_scheme_input ) options["color_scheme"] = color_scheme.value.name message_log.add_message( Message( "Color Scheme: " + color_scheme.value.name, tcod.light_gray ) ) if input_scheme_input: input_scheme = cycle_scheme( input_scheme, InputSchemes, input_scheme_input ) options["input_scheme"] = input_scheme.value.name message_log.add_message( Message( "Input Scheme: " + input_scheme.value.name, tcod.light_gray ) ) # Process actions with multiple triggers if do_use: if menu_selection < len(inventory_options): use_results = player.container.get_item( inventory_options[menu_selection] ).item.use(player, game_map) player_results.update(use_results) player_acted = True if combine_target: if combining is combine_target: message_log.add_message( Message( "An item cannot be combined with itself.", tcod.yellow ) ) else: result = None if combining.item.combine_function: result = combining.item.use( player, game_map, combining=True, combine_target=combine_target, ) if result: player_results.update(result) player_acted = True else: if combining.item.combine_function: result = combining.item.use( player, game_map, combining=True, combine_target=combining, ) if result: player_results.update(result) player_acted = True else: message_log.add_message( Message( "These items cannot be combined.", tcod.yellow ) ) combining = None game_state = previous_game_state if do_target: if looking: look_message = get_look_message( *key_cursor, game_map, fov_map, player ) if look_message: message_log.add_message(look_message) game_state = previous_game_state looking = False elif ( throwing and (player.x, player.y) != key_cursor and tcod.map_is_in_fov(fov_map, *key_cursor) and game_map.is_tile_open(*key_cursor, check_entities=False) ): if player.slots.is_equipped(throwing): player.slots.toggle_equip(throwing) throw_results = throwing.item.use( player, game_map, throwing=True, target_x=key_cursor[0], target_y=key_cursor[1], ) player_results.update(throw_results) game_state = previous_game_state throwing = None player_acted = True if player_acted: player_results.update(player.update_status_effects()) if player.fighter.max_hp < previous_max_hp: player.fighter.hp = max( 1, player.fighter.hp - (previous_max_hp - player.fighter.max_hp), ) previous_max_hp = player.fighter.max_hp exit_queued = False # Process player turn results attack_message = player_results.get("attack_message") pickup_message = player_results.get("pickup_message") use_message = player_results.get("use_message") effect_message = player_results.get("effect_message") new_messages = [ attack_message, pickup_message, use_message, effect_message, ] recompute_fov = recompute_fov or player_results.get("recompute_fov") update_fov_map = player_results.get("update_fov_map") dead_entities = player_results.get("dead") next_level = player_results.get("next_level") item_obtained = player_results.get("item_obtained") item_moved = player_results.get("item_moved") item_consumed = player_results.get("item_consumed") or item_moved for message in new_messages: if message: message_log.add_message(message) if update_fov_map: fov_map = game_map.generate_fov_map() if dead_entities: for dead_entity in dead_entities: if dead_entity == player: message = player.kill(is_player=True) previous_game_state = GameStates.PLAYER_DEAD game_state = GameStates.PLAYER_DEAD else: message = dead_entity.kill() message_log.add_message(message) if next_level: # Partially copied from code under "direction" game_map = GameMap(game_map.dungeon_level + 1) player.x, player.y = game_map.find_open_tile() game_map.entities.append(player) recompute_fov = True fov_map = game_map.generate_fov_map() memory = [ [False for y in range(game_map.height)] for x in range(game_map.width) ] tcod.console_clear(console) if item_obtained and item_obtained in game_map.entities: game_map.entities.remove(item_obtained) if item_consumed or item_moved: if isinstance(item_consumed, list): for item in item_consumed: player.container.items.remove(item) if player.slots.is_equipped(item): player.slots.toggle_equip(item) else: player.container.items.remove(item_consumed) if player.slots.is_equipped(item_consumed): player.slots.toggle_equip(item_consumed) if item_moved: item_moved.x = player_results.get("item_x") item_moved.y = player_results.get("item_y") if item_moved not in game_map.entities: game_map.entities.append(item_moved) if player_acted: game_state = GameStates.ENEMY_TURN if game_state is GameStates.ENEMY_TURN: enemy_fov_map = game_map.generate_fov_map_with_entities() for entity in game_map.entities: if entity.ai: enemy_results = entity.ai.act( game_map, player, enemy_fov_map, tcod.map_is_in_fov(fov_map, entity.x, entity.y), ) # Process enemy turn results attack_message = enemy_results.get("attack_message") dead_entities = enemy_results.get("dead") if attack_message: message_log.add_message(attack_message) if dead_entities: for dead_entity in dead_entities: if dead_entity == player: message = player.kill(is_player=True) message_log.add_message(message) game_state = GameStates.PLAYER_DEAD break message = dead_entity.kill() message_log.add_message(message) if game_state is GameStates.PLAYER_DEAD: break else: game_state = GameStates.PLAYER_TURN
def render_all( console, panel, bar_width, message_log, game_map, player, fov_map, memory, color_scheme, game_state, mouse, menu_selection=0, key_cursor=None, inventory_options=None, viewing_map=False, ): # Screen dimensions screen_width = tcod.console_get_width(console) screen_height = tcod.console_get_height(console) # The center coordinates of the screen center_x = int(screen_width / 2) center_y = int(screen_height / 2) if key_cursor: camera_x = key_cursor[0] camera_y = key_cursor[1] else: camera_x = player.x camera_y = player.y # The map coordinates of the top left character displayed on the screen top_left_x = camera_x - center_x top_left_y = camera_y - center_y if game_state is GameStates.VICTORY: tcod.console_clear(console) tcod.console_print_ex( console, center_x, center_y, tcod.BKGND_DEFAULT, tcod.CENTER, "You Win!", ) tcod.console_blit(console, 0, 0, screen_width, screen_height, 0, 0, 0) return # Draw all visible and remembered tiles for x in range(screen_width): for y in range(screen_height): tile_x = x + top_left_x tile_y = y + top_left_y if game_map.contains(tile_x, tile_y) and (viewing_map or memory[tile_x][tile_y]): tile = game_map.get_tile(tile_x, tile_y, value=False) foreground = color_scheme.foreground.get(tile) background = color_scheme.background.get(tile) if viewing_map or tcod.map_is_in_fov(fov_map, tile_x, tile_y): if color_scheme.allow_fade and not viewing_map: foreground = apply_fov_gradient( foreground, distance(player.x, player.y, tile_x, tile_y), player.sight.fov_radius, ) background = apply_fov_gradient( background, distance(player.x, player.y, tile_x, tile_y), player.sight.fov_radius, ) else: foreground = color_scheme.get_memory_color(foreground) background = color_scheme.get_memory_color(background) tcod.console_set_default_foreground(console, foreground) tcod.console_put_char(console, x, y, tile.value.character) tcod.console_set_char_background(console, x, y, background, tcod.BKGND_SET) else: tcod.console_set_default_foreground( console, color_scheme.foreground[None]) tcod.console_put_char(console, x, y, " ") tcod.console_set_char_background( console, x, y, color_scheme.background[None], tcod.BKGND_SET, ) # Sort entities by their render order ordered_entities = sorted(game_map.entities, key=lambda i: i.render_order.value, reverse=True) # Draw all visible entities for entity in ordered_entities: if viewing_map or tcod.map_is_in_fov(fov_map, entity.x, entity.y): tcod.console_set_default_foreground(console, entity.color) tcod.console_put_char( console, entity.x - top_left_x, entity.y - top_left_y, entity.char, tcod.BKGND_NONE, ) if key_cursor: tcod.console_set_default_foreground(console, tcod.white) tcod.console_put_char(console, center_x, center_y, "X", tcod.BKGND_NONE) tcod.console_blit(console, 0, 0, screen_width, screen_height, 0, 0, 0) if viewing_map: return # Panel dimensions panel_width = tcod.console_get_width(panel) panel_height = tcod.console_get_height(panel) panel_y = screen_height - panel_height tcod.console_set_default_background(panel, tcod.black) tcod.console_clear(panel) cursor_x = mouse.cx + top_left_x cursor_y = mouse.cy + top_left_y if tcod.map_is_in_fov(fov_map, cursor_x, cursor_y): entities_at_cursor = game_map.get_entities_at_tile(cursor_x, cursor_y) if entities_at_cursor: names = join_list( [entity.indefinite_name for entity in entities_at_cursor]) if len(names) > screen_width - 2: names = (str(len(entities_at_cursor)) + " entities (right-click to list all)") tcod.console_set_default_foreground(panel, tcod.light_gray) tcod.console_print_ex(panel, 1, 0, tcod.BKGND_NONE, tcod.LEFT, names) # Print the game messages, one line at a time message_y = 1 for i in range( max(0, len(message_log.messages) - panel_height + 1), max(0, len(message_log.messages)), ): message = message_log.messages[i] tcod.console_set_default_foreground(panel, message.color) tcod.console_print_ex( panel, message_log.x, message_y, tcod.BKGND_NONE, tcod.LEFT, message.text, ) message_y += 1 render_bar( panel, 1, 1, bar_width, "HP", player.fighter.hp, player.fighter.max_hp, tcod.red, tcod.darker_red, ) tcod.console_print_ex( panel, 1, 2, tcod.BKGND_NONE, tcod.LEFT, f"Attack: {player.fighter.attack}", ) tcod.console_print_ex( panel, 1, 3, tcod.BKGND_NONE, tcod.LEFT, f"Defense: {player.fighter.defense}", ) tcod.console_print_ex( panel, 1, 4, tcod.BKGND_NONE, tcod.LEFT, f"Damage: {player.fighter.damage}", ) tcod.console_set_default_foreground(panel, tcod.yellow) tcod.console_print_ex( panel, 1, 5, tcod.BKGND_NONE, tcod.LEFT, f"Dungeon Level: {game_map.dungeon_level}", ) tcod.console_blit(panel, 0, 0, panel_width, panel_height, 0, 0, panel_y) if game_state == GameStates.INVENTORY: item_count = len(player.container.items) capacity = player.container.capacity inventory_menu( console, f"Inventory ({item_count}/{capacity})\n", player, 50, screen_width, screen_height, menu_selection, inventory_options, )