コード例 #1
0
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)
コード例 #2
0
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)
コード例 #3
0
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)
コード例 #4
0
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)
コード例 #5
0
ファイル: render.py プロジェクト: Maugrift/GeneriCrawl
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,
        )
コード例 #6
0
    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
コード例 #7
0
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
コード例 #8
0
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
コード例 #9
0
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()
コード例 #10
0
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()
コード例 #11
0
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
コード例 #12
0
ファイル: render.py プロジェクト: Maugrift/GeneriCrawl
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,
        )