def process(self):
        tcod.sys_wait_for_event(mask=tcod.EVENT_ANY,
                                k=self.key,
                                m=self.mouse,
                                flush=False)

        if self.key.vk == tcod.KEY_ESCAPE:
            self.scene.action = {'exit': True}
        else:
            self.scene.action = {}
Beispiel #2
0
 def get_input(self):
     tcod.sys_wait_for_event(tcod.EVENT_KEY_PRESS | tcod.EVENT_MOUSE, key, mouse, False)
     if key.vk and key.vk not in (tcod.KEY_TEXT, tcod.KEY_CHAR):
         return Input(InputType.KEY, key.vk, key)
     if key.vk and key.vk == tcod.KEY_TEXT:
         return Input(InputType.CHAR, key.text, key)
     if key.vk and key.vk == tcod.KEY_CHAR and (key.lctrl or key.rctrl):
         return Input(InputType.CHAR, chr(key.c), key)
     if mouse:
         return Input(InputType.MOUSE, mouse, key)
     return None
    def process(self):
        tcod.sys_wait_for_event(mask=tcod.EVENT_ANY,
                                k=self.key,
                                m=self.mouse,
                                flush=False)

        x, y = self.mouse.cx, self.mouse.cy
        if self.key.vk == tcod.KEY_ESCAPE:
            self.scene.action = {'exit': True}
        if self.mouse.lbutton_pressed:
            self.scene.action['left_click'] = (x, y)
        if self.mouse.rbutton_pressed:
            self.scene.action['right_click'] = (x, y)
        self.scene.mouse = self.mouse
Beispiel #4
0
 def wait_for_event(self, mask):
     event_type = tcod.sys_wait_for_event(mask, self.current_key_event,
                                          self.current_mouse_event)
     self.current_event = events.event_from(event_type,
                                            self.current_key_event,
                                            self.current_mouse_event)
     return self.current_event
    def process(self):
        tcod.sys_wait_for_event(mask=tcod.EVENT_ANY,
                                k=self.key,
                                m=self.mouse,
                                flush=False)

        if tcod.EVENT_KEY_PRESS and self.key.pressed:
            index = self.key.c - ord('a')
            if index >= 0:
                self.scene.action = {'inventory_index': str(index)}
            elif self.key.vk == tcod.KEY_ENTER and self.key.lalt:
                self.scene.action = {'fullscreen': True}
            elif self.key.vk == tcod.KEY_ESCAPE:
                self.scene.action = {'exit': True}
            else:
                self.scene.action = {}
    def process(self):
        tcod.sys_wait_for_event(mask=tcod.EVENT_ANY,
                                k=self.key,
                                m=self.mouse,
                                flush=False)

        if tcod.EVENT_KEY_PRESS and self.key.pressed:
            if self.key.c == ord('a'):
                self.scene.action = {'level_up': 'hp'}
            elif self.key.c == ord('b'):
                self.scene.action = {'level_up': 'str'}
            elif self.key.c == ord('c'):
                self.scene.action = {'level_up': 'def'}
            elif self.key.vk == tcod.KEY_ENTER and self.key.lalt:
                self.scene.action = {'fullscreen': True}
            else:
                self.scene.action = {}
    def process(self):
        tcod.sys_wait_for_event(mask=tcod.EVENT_ANY,
                                k=self.key,
                                m=self.mouse,
                                flush=False)

        if tcod.EVENT_KEY_PRESS and self.key.pressed:
            if self.key.c == ord('a'):
                self.scene.action = {'new_game': True}
            elif self.key.c == ord('b'):
                self.scene.action = {'load_game': True}
            elif self.key.vk == tcod.KEY_ENTER and self.key.lalt:
                self.scene.action = {'fullscreen': True}
            elif self.key.c == ord('c') or self.key.vk == tcod.KEY_ESCAPE:
                self.scene.action = {'exit': True}
            else:
                self.scene.action = {}
Beispiel #8
0
def menu_loop(wait_for=None,
              cancel_with_escape=True,
              sort_by: Union[int, str] = 'string'):
    """
    The loop waits for a key input.
    If wait_for is an integer, it waits for a key that corresponds to an integer in range of (0, wait_for)
    If wait_for is a list of characters or tcod key codes it waits for a key that corresponds to one of the characters or key codes.

    :param wait_for:
    :type wait_for: int or list
    :return:
    :rtype: int or str
    """
    key = tcod.Key()

    while True:
        tcod.sys_wait_for_event(tcod.EVENT_KEY_PRESS, key, '', False)
        char = chr(key.c)
        if key.vk == tcod.KEY_ESCAPE and cancel_with_escape:
            return None
        # elif type(wait_for) is dict:
        #     if char.lower() in wait_for.keys():
        #         return wait_for[char]
        elif key.vk == tcod.KEY_KPENTER:
            return None
        elif isinstance(wait_for,
                        int):  # If menu is waiting to receive an index
            if isinstance(sort_by, str):
                index = ord(char) - ord('a')
                if 0 <= index < wait_for:
                    return index
            elif isinstance(sort_by, int):
                index = int(key.c - ord('1'))
                if 0 <= index < wait_for:
                    return index
        elif isinstance(wait_for,
                        list):  # If menu is waiting for a specific key input
            if char.lower() in wait_for:
                return char
            if key.vk in wait_for:
                return key.vk
    def process(self):
        # tcod.sys_check_for_event(
        tcod.sys_wait_for_event(mask=tcod.EVENT_ANY,
                                k=self.key,
                                m=self.mouse,
                                flush=False)

        user_input = Key(
            vk=self.key.vk,
            ch=chr(self.key.c),
            alt=(self.key.lalt or self.key.ralt),
            ctrl=(self.key.lctrl or self.key.lctrl),
            meta=(self.key.lmeta or self.key.rmeta),
            shift=self.key.shift,
            pressed=self.key.pressed,
        )

        if tcod.EVENT_KEY_PRESS and user_input in self.key_code:
            self.scene.action = self.key_code[user_input]
        else:
            self.scene.action = {}
        self.scene.mouse = self.mouse
Beispiel #10
0
def main():
    screen_width = 80
    screen_height = 80

    libtcod.console_set_custom_font(
        'arial10x10.png',
        libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD)
    libtcod.console_init_root(screen_width,
                              screen_height,
                              'Image',
                              fullscreen=False)
    con = libtcod.console_new(screen_width, screen_height)

    n = 20
    backgr = 0.7
    im = Image.open(
        'D:\\(un)important\\python\\3year\\project\\other\\test4.png')

    rgbim = im.convert('RGB')
    rgbim.thumbnail((n, n), Image.ANTIALIAS)

    for x in range(rgbim.size[0]):
        for y in range(rgbim.size[1]):
            r, g, b = rgbim.getpixel((x, y))
            backcolor = libtcod.Color(int(r * backgr), int(g * backgr),
                                      int(b * backgr))
            libtcod.console_set_char_background(con, x, y, backcolor,
                                                libtcod.BKGND_SET)
            ch = get_char(x, y, rgbim)
            libtcod.console_set_default_foreground(
                con, libtcod.Color(int(r), int(g), int(b)))
            libtcod.console_put_char(con, x, y, ch, libtcod.BKGND_NONE)

    libtcod.console_blit(con, 0, 0, screen_width, screen_height, 0, 0, 0)
    libtcod.console_flush()

    key = libtcod.Key()
    mouse = libtcod.Mouse()
    libtcod.sys_wait_for_event(libtcod.EVENT_KEY_PRESS, key, mouse, True)
Beispiel #11
0
    def get_action(self, g, key, mouse):
        # Capture new user input
        # Deprecated since version 9.3: Use the tcod.event.get function to check for events.
        # tcod.sys_check_for_event(
            # mask=tcod.EVENT_KEY_PRESS | tcod.EVENT_MOUSE,
            # k=key,
            # m=mouse
        # )

        # Flush False - returns 2 key events
        # tcod.Key(pressed=True, vk=tcod.KEY_CHAR, c=ord('h'))
        # tcod.Key(pressed=True, vk=tcod.KEY_TEXT, text='h')

        # Flush True: returns just this
        # tcod.Key(pressed=True, vk=tcod.KEY_CHAR, c=ord('h'))


        # Nothing is waiting in the action queue - collect more actions
        tcod.sys_wait_for_event(
            mask=tcod.EVENT_KEY_PRESS | tcod.EVENT_MOUSE,
            k=key,
            m=mouse,
            flush=True
        )

        # Get keyboard/mouse input
        key_char = input_handling.process_tcod_input(key)

        action = input_handling.handle_keys(g.state, key_char)
        mouse_action = input_handling.handle_mouse(g.state, mouse)

        if mouse_action:
            # Mouse action will take priority over keys (for now)
            # log.debug('mouse_action: {}'.format(mouse_action))

            action = mouse_action

        return action
Beispiel #12
0
#!/usr/bin/env python3

import tcod

WIDTH, HEIGHT = 80, 60

key = tcod.Key()
mouse = tcod.Mouse()

with tcod.console_init_root(WIDTH, HEIGHT, 'tcod events example',
                            renderer=tcod.RENDERER_SDL) as console:
    tcod.sys_set_fps(24)
    while not tcod.console_is_window_closed():
        ev = tcod.sys_wait_for_event(tcod.EVENT_ANY, key, mouse, False)
        if ev & tcod.EVENT_KEY:
            console.blit(console, 0, 0, 0, 1, WIDTH, HEIGHT - 2)
            console.print_(0, HEIGHT - 3, repr(key))
            print(key)
        if ev & tcod.EVENT_MOUSE_MOVE:
            console.rect(0, HEIGHT - 1, WIDTH, 1, True)
            console.print_(0, HEIGHT - 1, repr(mouse))
            print(mouse)
        elif ev & tcod.EVENT_MOUSE:
            console.blit(console, 0, 0, 0, 1, WIDTH, HEIGHT - 2)
            console.print_(0, HEIGHT - 3, repr(mouse))
            print(mouse)
        tcod.console_flush()
Beispiel #13
0
 def wait_for_event(self, mask):
     event_type = tcod.sys_wait_for_event(mask, self.current_key_event, self.current_mouse_event)
     self.current_event = events.event_from(event_type, self.current_key_event, self.current_mouse_event)
     return self.current_event
Beispiel #14
0


# Initialize the root console in a context.
with tcod.console_init_root(WIDTH, HEIGHT, 'tcod-test', False) as root_console:
    MM = MonsterManager(root_console)
    tcod.sys_set_fps(24)
    state = 'main' # set back to a main menu when we get there.
    
    while not tcod.console_is_window_closed():
        tcod.console_clear(root_console) # clear screen between frames.

        # if state is 'start' blit the opening screen asking the player to start a new game.
        if(state == 'start'):
            tcod.console_flush()
            ev = tcod.sys_wait_for_event(tcod.EVENT_ANY, key, None, True)
            if ev & tcod.EVENT_KEY:
                state = 'main'
        
        # if state is 'main' blit the map and the player on the screen as well as the noise level.
        elif(state == 'main'):
            # figure out what level we are on and use that data to display.
            try:
                _level = full_dungeon_stack[current_level]
            except KeyError:
                full_dungeon_stack[current_level] = dungeon_level(current_level)
                _level = full_dungeon_stack[current_level]
            _map = _level.map
            _creatures = _level.creatures
            _objects = _level.objects
Beispiel #15
0
def main_loop(root_con, key, mouse, current_view, game_world, player, game_map,
              entities, close_entities, mlog):

    render_args = None
    game_state = GameStates.PLAYER_TURN

    while not tcod.console_is_window_closed():

        tcod.sys_wait_for_event(tcod.EVENT_KEY_PRESS, key, mouse, True)

        action = handle_keys(key, movement_settings)

        if game_state == GameStates.PLAYER_TURN:

            action_move = action.get('move')
            action_exit = action.get('exit')
            action_fullscreen = action.get('fullscreen')
            action_pass = action.get('pass')
            action_save = action.get('save')

            if action_save:
                print("Saving...")
                return {
                    'save': (game_world, game_map, player, entities,
                             close_entities, mlog)
                }

            player_turn_results = []

            if action_move:
                dx, dy = action_move
                """ Check if player entered new chunk """

                pos_in_chunk_x, pos_in_chunk_y = utils.get_pos_in_chunk(
                    player.x, player.y)

                if utils.enter_new_chunk(pos_in_chunk_x + dx,
                                         pos_in_chunk_y + dy):
                    """
                    Here is code for changing chunks.
                    If player.x, player.y + direction from action_move goes beyond the chunk area,
                    he is changing chunks.
                    We don't have to calculate in which way he went, because get_chunk_pos_from_player_pos does that automatically.
                    We don't have to calculate on what place in the next chunk he will appear, because in the render_functions file and
                    draw entity function, his position is calculated from world pos (0...WORLD_HEIGHT or _WIDTH) to (0...CHUNK_HEIGHT or _WIDTH).
                    Next, we create two sets - close_entities and all_entities. Then, we exclude close entities from all, and add those that are close,
                    to final entities list.
                    Offloaded entities are those who are far and won't chase the player. We are storing them in chunk.objects list.

                    """

                    old_chunk_x, old_chunk_y = game_world.get_chunk_pos_from_player_pos(
                        player.x, player.y)

                    player.x += dx
                    player.y += dy

                    chunk_pos_x, chunk_pos_y = game_world.get_chunk_pos_from_player_pos(
                        player.x, player.y)
                    if game_world.chunks[chunk_pos_x][
                            chunk_pos_y].property == ChunkProperty.END:
                        print("END")

                    # process only these, which are far.

                    close_entities = set([
                        e for e in entities if e.distance_to(player) <
                        constants.DISTANCE_TO_PROCESS_ENTITY and e != player
                    ])
                    all_entities = set([e for e in entities if e != player])
                    to_offload = list(all_entities.difference(close_entities))

                    game_map.offload_chunk(
                        game_world.chunks[old_chunk_x][old_chunk_y], player,
                        list(to_offload))
                    entities = game_map.remove_entities(player, entities)
                    entities += list(close_entities)

                    print(chunk_pos_x, chunk_pos_y)
                    print(
                        game_world.chunks[chunk_pos_x][chunk_pos_y].discovered)

                    # print(
                    #     f"CLOSE ENTITIES: {len(close_entities)}\nALL ENTITIES: {len(all_entities)}\nENTITIES TO OFFLOAD: {len(to_offload)}")

                    # # Make new map
                    game_map = GameMap(
                        constants.MAP_WIDTH, constants.MAP_HEIGHT,
                        game_world.chunks[chunk_pos_x][chunk_pos_y])

                    if not game_world.chunks[chunk_pos_x][
                            chunk_pos_y].discovered:
                        game_map.randomize_sand(chunk_pos_x, chunk_pos_y,
                                                game_world)
                    else:
                        # here we should randomize...
                        new_entities = game_map.restore_chunk(
                            chunk_pos_x, chunk_pos_y, entities, player,
                            game_world)
                        entities = new_entities + list(close_entities)
                    ##

                else:

                    if not game_map.is_blocked(player.x + dx, player.y + dy):

                        target = get_blocking_entities_at_location(
                            entities, player.x + dx, player.y + dy)

                        if target:
                            # do damage etc.
                            attack_results = player.fighter.attack(target)
                            player_turn_results.extend(attack_results)

                        else:
                            player.move(dx, dy)

                game_state = GameStates.ENEMY_TURN

                for player_turn_result in player_turn_results:

                    received_msg = player_turn_result.get('message')
                    received_dead_entity = player_turn_result.get('dead')

                    if received_msg:
                        msg = Message(received_msg, (255, 255, 255))
                        mlog.add_msg(msg)

                    if received_dead_entity:
                        received_dead_entity.fighter.die()
                        msg = Message(
                            f"{received_dead_entity.name.capitalize()} is dead.",
                            constants.COLOR_DARK_RED)
                        mlog.add_msg(msg)

            if action_exit:
                raise SystemExit()

            if action_fullscreen:
                tcod.console_set_fullscreen(not tcod.console_is_fullscreen())

            if action_pass:
                game_state = GameStates.ENEMY_TURN

            current_view.consoles['view_MAP']['args'] = (player, entities,
                                                         game_map)

        if game_state == GameStates.ENEMY_TURN:

            for entity in entities:
                if entity.ai:
                    enemy_turn_results = entity.ai.take_turn(
                        player, game_map, entities)

                    for enemy_turn_result in enemy_turn_results:

                        received_message = enemy_turn_result.get('message')
                        received_dead_entity = enemy_turn_result.get('dead')

                        if received_message:
                            msg = Message(received_message, (255, 255, 255))
                            mlog.add_msg(msg)

                        if received_dead_entity:
                            game_state = GameStates.PLAYER_DEATH
                            # player is dead
                            mlog.add_msg(
                                f"{received_dead_entity.name} is dead.",
                                (255, 255, 255))
                            entity.fighter.die()

            if game_state != GameStates.PLAYER_DEATH:
                game_state = GameStates.PLAYER_TURN

        if game_state == GameStates.PLAYER_DEATH:

            death_console = tcod.console.Console(constants.SCREEN_WIDTH,
                                                 constants.SCREEN_HEIGHT,
                                                 order="F")

            action = handle_keys(key, movement_settings)

            if action.get('exit'):
                raise SystemExit()

            current_view = view.View("death_screen", death_console,
                                     render_functions.render_death_screen,
                                     root_con)

        current_view.render()

        tcod.console_flush()

        tcod.sys_set_fps(60)
def start(debug=False):
    SETTINGS = SettingsObject("res/settings.json")

    # Start setting up the screens/panels
    # Sets the window dimensions
    window_width = SETTINGS.getSetting("WindowWidth")
    window_height = SETTINGS.getSetting("WindowHeight")
    screen_names = SETTINGS.getSetting("Screens")

    # Initiate the Display
    display = DisplayObject()

    # Puts all screen settings into a dictionary
    for screen_name in screen_names:
        screen_data = SETTINGS.getSetting(screen_name)
        display.panelInfo[screen_name] = screen_data

    # Create the displays:
    map_console = libtcod.console_new(
        display.panelInfo["MapScreen"]["ScreenWidth"],
        display.panelInfo["MapScreen"]["ScreenHeight"])
    hud_console = libtcod.console_new(
        display.panelInfo["HUDScreen"]["ScreenWidth"],
        display.panelInfo["HUDScreen"]["ScreenHeight"])
    quest_console = libtcod.console_new(
        display.panelInfo["QuestScreen"]["ScreenWidth"],
        display.panelInfo["QuestScreen"]["ScreenHeight"])
    toast_console = libtcod.console_new(
        display.panelInfo["ToastScreen"]["ScreenWidth"],
        display.panelInfo["ToastScreen"]["ScreenHeight"])

    # Set the font
    libtcod.console_set_custom_font(
        SETTINGS.getResource("FontFile"),
        libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD)

    # Initialize display
    libtcod.console_init_root(window_width, window_height, 'Roguelike Me',
                              False)

    # Create the game object:
    Game = GameObject(SETTINGS, debug)

    #
    # MAIN LOOP
    #
    key = libtcod.Key()
    mouse = libtcod.Mouse()
    while not libtcod.console_is_window_closed():

        # Turn key press into an action, otherwise return None
        action = processKeyPress(key)

        # Process action if not None
        if action != None:
            if action[0] == 'fullscreen':
                libtcod.console_set_fullscreen(
                    not libtcod.console_is_fullscreen())
            elif action[0] == 'quit' and action[1] == True:
                return True
            else:
                # If the action is meant for the game itself instead of the program,
                # send it forward to the game to update later
                Game.addActionToBacklog(action)
            # Process backlog after keypress if the action did something.
            Game.processBacklog()

        # Get all draw updates from the Game
        toDraw = Game.getToDraw(map_console, hud_console, quest_console,
                                toast_console)

        # Forward all draw updates to the display
        display.setDrawOrders(toDraw)

        # Print to Panels
        display.drawToPanels(True)
        display.renderPanels(map_console, hud_console, quest_console,
                             toast_console)

        # Wait for Keypress
        libtcod.sys_wait_for_event(libtcod.EVENT_KEY_PRESS, key, mouse, True)
Beispiel #17
0
def game_loop(game):
    player = game.player

    targeting_item = None
    debug_spawn = None
    fov_reset = False
    fov_recompute = True
    new_turn = True
    final_turn_results = {}

    key = tcod.Key()
    # mouse = tcod.Mouse()

    game.fov_map = initialize_fov(game)
    recompute_fov(game, player.x, player.y)
    render_all(game, game.fov_map, debug=game.debug['reveal_map'])

    if game.turn == 1 and not game.debug['no_intro']:
        play_intro(game)

    game.state = GameState.PLAYER_ACTIVE
    game.previous_state = game.state

    # TODO new tcod.event system
    # action = False
    # for event in tcod.event.wait():
    #     if event.type == "QUIT":
    #         exit()
    #     elif event.type == "KEYDOWN":
    #         action = handle_keys(event, game.state)

    while not tcod.console_is_window_closed(
    ):  # todo deprecated -> replace with new tcod-system

        if fov_reset:
            game.fov_map = initialize_fov(game)
        if fov_recompute or fov_reset:
            recompute_fov(game, player.x, player.y)
            fov_recompute = False
        render_all(game, game.fov_map, debug=game.debug['reveal_map'])

        if new_turn:
            game.turn += 1
            for ent in game.entities:
                ent.proc_every_turn(game, start=True)
            new_turn = False

        tcod.sys_wait_for_event(tcod.EVENT_KEY_PRESS, key, None,
                                True)  # todo deprecated
        action = handle_keys_legacy(key, game.state)
        # mouse_action = handle_mouse(mouse)

        if action:
            logging.debug(
                f'Processing {action}, last turn results: {final_turn_results}'
            )
            # Process player input into turn results #
            # TODO ideally game state should be set in engine.py, not in the lower-level functions
            player_turn_results = process_player_input(
                action, game, last_turn_results=final_turn_results)
            logging.debug(
                f'Loop starts with turn {game.turn}, player results: {player_turn_results}'
            )
            # The game exits if player turn results returns False #
            if player_turn_results is False:
                break

            # Process turn results #
            # TODO ideally game state should be set in engine.py, not in the lower-level functions
            processed_turn_results = process_turn_results(
                player_turn_results, game, game.fov_map)
            logging.debug(
                f'Turn {game.turn}. State: {game.state}. Processed results: {player_turn_results}'
            )
            fov_recompute = processed_turn_results.get('fov_recompute', False)
            fov_reset = processed_turn_results.get('fov_reset', False)
            level_change = processed_turn_results.get('level_change')
            if targeting_item is None:
                targeting_item = processed_turn_results.get('targeting_item')
            if debug_spawn is None:
                debug_spawn = processed_turn_results.get('debug_spawn')

            # NPCs take turns #
            if game.npc_active:
                new_turn = True
                for ent in game.entities:
                    ent.proc_every_turn(game, start=False)
                game.state = process_npc_actions(
                    game
                )  # set game state to either player turn or player dead
                new_turn = True

            # Level Change #
            if level_change is not None and game.player_active:  # player_active prevents level_change if player was killed while trying to change the level
                initialize_level(level_change, game)

            # Resets variables used for targeting/cursor manipulation #
            if game.player_active:
                targeting_item = None
                debug_spawn = None

            final_turn_results = {
                'targeting_item': targeting_item,
                'debug_spawn': debug_spawn
            }  # carry over turn results to next loop run

            logging.debug(
                f'Ending loop at turn {game.turn}. State: {game.state}. FOV Reset/Recompute: {fov_reset}/{fov_recompute}'
            )
Beispiel #18
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