def __init__(self, name, text, options, width, height):
     Console.__init__(self, width, height)
     self.name = name
     self.text = text
     self.options = options
     self.max_options_len = 26
     self.letter_index = ord('a')
Beispiel #2
0
 def draw_log(self, root_console: tdl.Console):
     log_width = root_console.width
     log_height = 20
     log_y = root_console.height - 3 - log_height
     log = tdl.Console(log_width, log_height)
     log.set_colors((255, 255, 255), (50, 50, 50))
     log.clear()
     log.set_mode('scroll')
     for entry in self._log:
         log.set_colors(fg=entry[1])
         log.print_str(entry[0])
         log.print_str('\n')
     root_console.blit(log, 0, log_y)
Beispiel #3
0
def state(game: GameData, root_console: tdl.Console) -> GameState:

    root_console.clear()
    game.build_map()
    text = f'Dungeon Level {game.current_level}'
    root_console.draw_str(int(root_console.width / 2) - int(len(text) / 2), 15, text, fg=(255, 255, 0), bg=None)

    tdl.flush()

    user_input = wait_for_keys([
        'ESCAPE',
        'ENTER',
        'SPACE'
    ])

    if user_input == 'ESCAPE':
        return GameState.CLOSE

    return GameState.ROOM
Beispiel #4
0
def state(game: GameData, root_console: tdl.Console) -> GameState:
    root_console.clear()
    text = f'You have died. Would you like to play again?'
    root_console.draw_str(root_console.width / 2 - len(text) / 2, 10, text)
    text = 'Y/N'
    root_console.draw_str(root_console.width / 2 - len(text) / 2, 12, text)
    tdl.flush()
    choice = wait_for_keys(['ESCAPE', 'y', 'n'])
    if choice == 'ESCAPE' or choice == 'n':
        # quit game
        return GameState.CLOSE
    root_console.clear()
    tdl.flush()
    return GameState.GAME_RESTART
Beispiel #5
0
def travel_direction(direction, root_console: tdl.Console):
    """
    self-contained "animation" of moving through a hallway in a certain direction
    """
    start_x = 0
    start_y = 0
    end_x = 0
    end_y = 0

    room_draw_width = 9
    room_draw_height = 9

    top = 5
    left = 5

    for x in range(0, room_draw_width):
        for y in range(0, room_draw_height):
            tile = ' '
            if direction == 'north' or direction == 'south':
                if x == math.floor(room_draw_width / 2):
                    tile = '.'
                if x == math.floor(room_draw_width / 2) - 1 or x == math.floor(
                        room_draw_width / 2) + 1:
                    tile = '#'
            elif direction == 'east' or direction == 'west':
                if y == math.floor(room_draw_height / 2):
                    tile = '.'
                if y == math.floor(
                        room_draw_height / 2) - 1 or y == math.floor(
                            room_draw_height / 2) + 1:
                    tile = '#'

            root_console.draw_char(left + x,
                                   top + y,
                                   tile,
                                   bg=None,
                                   fg=(255, 255, 255))

    if direction == 'north' or direction == 'south':
        start_x = math.floor(room_draw_width / 2)
        end_x = start_x
    elif direction == 'east' or direction == 'west':
        start_y = math.floor(room_draw_height / 2)
        end_y = start_y

    if direction == 'north':
        start_y = room_draw_height - 1
    elif direction == 'south':
        end_y = room_draw_height - 1
    elif direction == 'east':
        end_x = room_draw_width - 1
    elif direction == 'west':
        start_x = room_draw_width - 1

    x = start_x
    y = start_y
    while x != end_x or y != end_y:
        root_console.draw_char(left + x,
                               top + y,
                               '.',
                               bg=None,
                               fg=(255, 255, 255))

        if direction == 'north':
            y -= 1
        elif direction == 'south':
            y += 1
        elif direction == 'east':
            x += 1
        elif direction == 'west':
            x -= 1

        root_console.draw_char(left + x,
                               top + y,
                               '@',
                               bg=None,
                               fg=(255, 255, 255))
        tdl.flush()
        time.sleep(.1)
Beispiel #6
0
def state(game: GameData, root_console: tdl.Console) -> GameState:

    player: Character = game.the_player

    # create panels
    inventory_panel = tdl.Console(int((root_console.width - 2) * .7), 30)
    inventory_panel.set_colors(fg=(220, 220, 220), bg=(0, 0, 50))
    equipped_panel = tdl.Console(int((root_console.width - 2) * .3), 30)
    equipped_panel.set_colors(fg=(220, 220, 220), bg=(0, 30, 0))
    # height = root height - heading x2 - space between panels - equipment panel height
    tooltip_panel = tdl.Console(root_console.width - 2, root_console.height - 34)
    tooltip_panel.set_colors(fg=(255, 255, 255), bg=(10, 10, 10))
    tooltip_panel.set_mode('scroll')

    selected_index = 0
    current_top = 0
    max_lines = inventory_panel.height

    while True:
        # clear console
        root_console.clear()

        # titles
        root_console.draw_str(1, 0, 'INVENTORY', fg=(255, 255, 0), bg=None)
        root_console.draw_str(int((root_console.width - 2) * .7 + 2), 0, 'EQUIPPED', fg=(255, 255, 0), bg=None)
        root_console.draw_str(1, 32, 'TOOLTIP', fg=(200, 200, 200), bg=None)

        # inventory list panel
        inventory_panel.clear()

        text_y = 0
        text_x = 0
        for index, item in enumerate(player.inventory.items[current_top:current_top+max_lines]):
            text_color = (200, 200, 200)
            item_text = f'{str(item)}'
            if index + current_top == selected_index:
                text_color = (255, 255, 0)
                if item.is_equipable():
                    if item == player.weapon or item == player.armor:
                        # unequip
                        item_text = f'{item_text}  [U]nequip'
                    else:
                        # equip
                        item_text = f'{item_text}  [E]quip'
                if item.can_activate_outside_encounter():
                    item_text = f'{item_text}  [A]ctivate'
                if item != player.weapon and item != player.armor:
                    item_text = f'{item_text}  [D]rop'
            inventory_panel.draw_str(text_x, text_y, item_text, fg=text_color)
            text_y += 1

        # draw panel
        root_console.blit(inventory_panel, 1, 1)

        # equipped panel
        equipped_panel.clear()
        # TODO: handle long lines of text
        equipped_panel.draw_str(1, 1, 'WEAPON:')
        equipped_panel.draw_str(2, 2, f'{player.weapon.name if player.weapon else "EMPTY"}')
        equipped_panel.draw_str(1, 4, 'ARMOR:')
        equipped_panel.draw_str(2, 5, f'{player.armor.name if player.armor else "EMPTY"}')

        # combat stats
        equipped_panel.draw_str(1, 8, 'STATS:')
        equipped_panel.draw_str(2, 9, f'HIT CHANCE: {player.combined_hit_chance}%')
        equipped_panel.draw_str(2, 10, f'CRIT CHANCE: {player.combined_crit_chance}%')
        equipped_panel.draw_str(2, 11, f'DAMAGE: {player.min_damage}-{player.max_damage}')
        equipped_panel.draw_str(2, 12, f'BLOCK: {player.combined_block}')

        root_console.blit(equipped_panel, 2 + inventory_panel.width, 1)

        # draw tooltip
        tooltip_panel.clear()
        if selected_index < len(player.inventory.items):
            selected_item: Item = player.inventory.items[selected_index]
            if selected_item.description:
                tooltip_panel.move(0, 0)
                tooltip_panel.print_str(selected_item.description)
            # show weapon stats
            if isinstance(selected_item, Weapon):
                as_weapon: Weapon = selected_item
                tooltip_panel.print_str('\n\n')
                damage = f'+{as_weapon.damage}'
                hit = f'{"+" if as_weapon.hit_chance_modifier > 0 else ""}{as_weapon.hit_chance_modifier}'
                crit = f'+{as_weapon.crit_chance_modifier}'
                tooltip_panel.print_str(f'DAMAGE: {damage}    HIT: {hit}    CRIT: {crit}')
                # TODO: show information about prefix and suffix
            # show armor stats
            if isinstance(selected_item, Armor):
                as_armor: Armor = selected_item
                tooltip_panel.print_str('\n\n')
                tooltip_panel.print_str(f'BLOCK: +{as_armor.block}')
        root_console.blit(tooltip_panel, 1, 33)

        tdl.flush()

        user_input = wait_for_keys([
            'ESCAPE',
            'UP',
            'DOWN',
            'ENTER',
            'i',
            'u',
            'e',
            'a',
            'd'
        ])

        if user_input == 'UP':
            # scroll selection up and scroll list up if needed
            if current_top <= 0 and selected_index <= 0:
                # TODO: maybe loop around to bottom of list?
                continue
            selected_index -= 1
            if selected_index < current_top:
                current_top = selected_index

        elif user_input == 'DOWN':
            # scroll selection down and scroll list down if needed
            if selected_index >= len(player.inventory.items) - 1:
                # TODO: maybe loop around to top of list?
                continue

            selected_index += 1
            if selected_index >= current_top + max_lines:
                current_top = selected_index

        elif user_input == 'd' and selected_index < len(player.inventory.items):
            # drop the item from inventory
            # TODO: add item to room like treasure?
            selected_item: Item = player.inventory.items[selected_index]
            if player.is_item_equipped(selected_item):
                continue
            game.log(f'Dropped {selected_item.name} to the floor. Gone Forever.')
            player.inventory.remove_item(selected_item)
            if selected_index >= len(player.inventory.items):
                selected_index = max(0, len(player.inventory.items) - 1)

        elif user_input == 'a' and selected_index < len(player.inventory.items):
            item_to_use: Item = player.inventory.items[selected_index]
            if item_to_use.can_activate_outside_encounter() and item_to_use.can_activate_on_player():
                result_text = item_to_use.activate(player)
                if result_text:
                    player.inventory.remove_item(item_to_use)
                    game.log(result_text, fg=(255, 255, 0))

        elif user_input == 'u' and selected_index < len(player.inventory.items):
            item_to_unequip: Item = player.inventory.items[selected_index]
            if not player.is_item_equipped(item_to_unequip):
                continue
            response = player.unequip_item(item_to_unequip)
            if response:
                game.log(response)

        elif user_input == 'e' and selected_index < len(player.inventory.items):
            item_to_equip: Item = player.inventory.items[selected_index]
            if not item_to_equip.is_equipable() or player.is_item_equipped(item_to_equip):
                continue
            for_log = player.equip_item(item_to_equip)
            if for_log:
                game.log(for_log)

        elif user_input == 'ESCAPE' or user_input == 'i':
            return GameState.ROOM

    return GameState.ROOM
Beispiel #7
0
def state(game: GameData, root_console: tdl.Console) -> GameState:

    root_console.clear()
    draw_player_status_bar(game.the_player, 1, root_console.height - 2,
                           root_console)

    # draw the room
    if game.current_room:
        # odd number of tiles makes "centering" easier
        room_draw_width = game.room_draw_width
        room_draw_height = game.room_draw_height

        top = 5
        left = 5
        center_x = int(math.floor(room_draw_width / 2))
        center_y = int(math.floor(room_draw_height / 2))

        for x in range(0, room_draw_width):
            for y in range(0, room_draw_height):
                tile = '.'
                if x == 0 or y == 0 or x == room_draw_width - 1 or y == room_draw_height - 1:
                    tile = '#'
                if game.current_room.north and x == math.floor(
                        room_draw_width / 2) and y == 0:
                    tile = '.'
                if game.current_room.south and x == math.floor(
                        room_draw_width / 2) and y == room_draw_height - 1:
                    tile = '.'
                if game.current_room.west and x == 0 and y == math.floor(
                        room_draw_height / 2):
                    tile = '.'
                if game.current_room.east and x == room_draw_width - 1 and y == math.floor(
                        room_draw_height / 2):
                    tile = '.'
                root_console.draw_char(left + x,
                                       top + y,
                                       tile,
                                       bg=None,
                                       fg=(255, 255, 255))

        # draw the player in the room
        p_x = math.floor(room_draw_width / 2)
        p_y = math.floor(room_draw_height / 2)
        if game.previous_room:
            if game.previous_room == game.current_room.north:
                # came from the north
                p_y = 2
            elif game.previous_room == game.current_room.south:
                # came from the south
                p_y = room_draw_height - 3
            elif game.previous_room == game.current_room.east:
                # came from the east
                p_x = room_draw_width - 3
            elif game.previous_room == game.current_room.west:
                # came from the west
                p_x = 2
        root_console.draw_char(left + p_x,
                               top + p_y,
                               '@',
                               bg=None,
                               fg=(255, 255, 255))

        # draw encounter
        if game.current_room.encounter and not game.current_room.encountered:
            if game.current_room.encounter == EncounterType.MONSTER:
                root_console.draw_char(left + center_x,
                                       top + center_y,
                                       game.current_room.monster.tile,
                                       bg=None,
                                       fg=(255, 255, 255))

            elif game.current_room.encounter == EncounterType.STAIRS:
                game.log(
                    'You find stairs to the next level! Press [ENTER] to advance.',
                    (0, 200, 235))

    # draw info bar
    info_con = tdl.Console(int(root_console.width / 2),
                           root_console.height - 21)
    info_con.set_colors(fg=(220, 220, 220), bg=(0, 0, 50))
    info_con.clear()
    left = 1
    top = 1
    info_con.draw_str(left, top, 'MOVE:')
    top += 1
    info_con.draw_str(left, top, '[UP] North')
    top += 1
    info_con.draw_str(left, top, '[DOWN] South')
    top += 1
    info_con.draw_str(left, top, '[RIGHT] East')
    top += 1
    info_con.draw_str(left, top, '[LEFT] West')
    top += 2
    if len(game.the_player.get_explore_skills()) > 0:
        info_con.draw_str(left, top, 'SKILLS:')
        top += 1
        for index, skill in enumerate(game.the_player.get_explore_skills(), 1):
            skill_text = f'{skill.name}'
            if not skill.ready():
                skill_text = f'{skill_text} ({skill.cooldown_left} turns)'

            info_con.draw_str(left, top, f'[{index}] {skill_text}')
            top += 1
        top += 1
    info_con.draw_str(left, top, 'MENU:')
    top += 1
    info_con.draw_str(left, top, '[m] Map')
    top += 1
    info_con.draw_str(left, top, '[i] Inventory')
    top += 1
    info_con.draw_str(left, top, '[c] Character')
    top += 1
    info_con.draw_str(left, top, '[ESC] Quit')
    root_console.blit(info_con, int(root_console.width / 2), 0)

    # draw the log
    game.draw_log(root_console)

    tdl.flush()

    user_input = wait_for_keys([
        'ESCAPE', 'UP', 'DOWN', 'LEFT', 'RIGHT', 'ENTER', 'i', 'm', 'c', '1',
        '2'
    ])

    if user_input == 'UP':
        return GameState.MOVE_NORTH

    elif user_input == 'DOWN':
        return GameState.MOVE_SOUTH

    elif user_input == 'LEFT':
        return GameState.MOVE_WEST

    elif user_input == 'RIGHT':
        return GameState.MOVE_EAST

    elif user_input == 'ESCAPE':
        return GameState.CLOSE

    elif user_input == 'm':
        return GameState.MAP

    elif user_input == 'i':
        return GameState.INVENTORY

    elif user_input == 'c':
        return GameState.CHARACTER_SHEET

    elif user_input == 'ENTER':
        return GameState.NEXT_LEVEL

    elif user_input == '1' and len(game.the_player.get_explore_skills()) > 0:
        # User skill 1
        skill: ActiveSkill = game.the_player.get_explore_skills()[0]
        if skill.ready():
            if skill.activate(game):
                if skill.uses_turn:
                    game.advance_turn()

    elif user_input == '2' and len(game.the_player.get_explore_skills()) > 1:
        # User skill 2
        skill: ActiveSkill = game.the_player.get_explore_skills()[1]
        if skill.ready():
            if skill.activate(game):
                if skill.uses_turn:
                    game.advance_turn()

    return GameState.ROOM
Beispiel #8
0
def state(game: GameData, root_console: tdl.Console) -> GameState:

    # minimum points for stats:
    min_power = 1
    min_block = 1
    min_health = 6

    # values
    starting_power = min_power
    starting_block = min_block
    starting_health = min_health
    name = ''
    name_max_length = 12

    # points to spend
    points_left = 8

    active = 'name'

    normal_color = (255, 255, 255)
    active_color = (255, 255, 0)

    while True:
        root_console.clear()

        # page title
        root_console.draw_str(1, 0, 'CREATE YOUR CHARACTER')

        left = 1
        top = 2

        # Player Name
        root_console.draw_str(
            left,
            top,
            f'NAME: {name}',
            fg=active_color if active == 'name' else normal_color)

        # Power
        top += 2
        power_text = f'POWER: {starting_power}'
        if starting_power > min_power:
            power_text = f'{power_text}  [LEFT] Decrease'
        if points_left > 0:
            power_text = f'{power_text}  [RIGHT] Increase'
        root_console.draw_str(
            left,
            top,
            power_text,
            fg=active_color if active == 'power' else normal_color)

        # Block
        top += 2
        block_text = f'BLOCK: {starting_block}'
        if starting_block > min_block:
            block_text = f'{block_text}  [LEFT] Decrease'
        if points_left > 0:
            block_text = f'{block_text}  [RIGHT] Increase'
        root_console.draw_str(
            left,
            top,
            block_text,
            fg=active_color if active == 'block' else normal_color)

        # Health
        top += 2
        health_text = f'HEALTH: {starting_health}'
        if starting_health > min_health:
            health_text = f'{health_text}  [LEFT] Decrease'
        if points_left > 0:
            health_text = f'{health_text}  [RIGHT] Increase'
        root_console.draw_str(
            left,
            top,
            health_text,
            fg=active_color if active == 'health' else normal_color)

        # show remaining points
        top += 4
        root_console.draw_str(left,
                              top,
                              f'POINTS REMAINING: {points_left}',
                              fg=(255, 0, 0))

        # Help
        top += 4
        root_console.draw_str(left, top,
                              '*Use UP/DOWN to change active field.')

        tdl.flush()

        if active == 'name':
            alpha = list('qwertyuiopasdfghjklzxcvbnm')
            alpha.extend(
                ['ESCAPE', 'DOWN', 'UP', 'SPACE', 'BACKSPACE', 'ENTER', '1'])
            user_input = wait_for_keys(alpha)

            if user_input == 'ESCAPE':
                # quit the game
                return GameState.CLOSE

            elif user_input == 'ENTER':
                # start game with current stats
                break

            elif user_input == '1':
                # TODO: remove this shortcut
                name = 'Player'
                starting_health = 10
                starting_power = 4
                starting_block = 2
                break

            elif user_input == 'UP':
                active = 'health'

            elif user_input == 'DOWN':
                active = 'power'

            elif user_input == 'BACKSPACE':
                if len(name) > 0:
                    name = name[:-1]

            elif len(name) < name_max_length:
                if user_input == 'SPACE':
                    name += ' '

                else:
                    name += user_input

        elif active == 'power':
            user_input = wait_for_keys(
                ['ESCAPE', 'DOWN', 'UP', 'LEFT', 'RIGHT', 'ENTER'])

            if user_input == 'ESCAPE':
                # quit the game
                return GameState.CLOSE

            elif user_input == 'ENTER':
                # start game with current stats
                break

            elif user_input == 'UP':
                active = 'name'

            elif user_input == 'DOWN':
                active = 'block'

            elif user_input == 'LEFT' and starting_power > min_power:
                starting_power -= 1
                points_left += 1

            elif user_input == 'RIGHT' and points_left > 0:
                starting_power += 1
                points_left -= 1

        elif active == 'block':
            user_input = wait_for_keys(
                ['ESCAPE', 'DOWN', 'UP', 'LEFT', 'RIGHT', 'ENTER'])

            if user_input == 'ESCAPE':
                # quit the game
                return GameState.CLOSE
            elif user_input == 'ENTER':
                # start game with current stats
                break

            elif user_input == 'UP':
                active = 'power'

            elif user_input == 'DOWN':
                active = 'health'

            elif user_input == 'LEFT' and starting_block > min_block:
                starting_block -= 1
                points_left += 1

            elif user_input == 'RIGHT' and points_left > 0:
                starting_block += 1
                points_left -= 1

        elif active == 'health':
            user_input = wait_for_keys(
                ['ESCAPE', 'DOWN', 'UP', 'LEFT', 'RIGHT', 'ENTER'])

            if user_input == 'ESCAPE':
                # quit the game
                return GameState.CLOSE

            elif user_input == 'ENTER':
                # start game with current stats
                break

            elif user_input == 'UP':
                active = 'block'

            elif user_input == 'DOWN':
                active = 'name'

            elif user_input == 'LEFT' and starting_health > min_health:
                starting_health -= 1
                points_left += 1

            elif user_input == 'RIGHT' and points_left > 0:
                starting_health += 1
                points_left -= 1

    # make the player

    game.the_player = create_player(name, starting_power, starting_block,
                                    starting_health)

    # start game
    return GameState.ROOM
Beispiel #9
0
def state(game: GameData, root_console: tdl.Console) -> GameState:

    player: Character = game.the_player

    while True:
        root_console.clear()

        # page title
        root_console.draw_str(1, 0, 'CHARACTER')

        left = 1
        top = 2

        # basic player info
        root_console.draw_str(left, top, f'NAME: {player.name}')
        top += 1
        root_console.draw_str(left, top,
                              f'HP: {player.health}/{player.max_health}')
        top += 1
        root_console.draw_str(
            left, top,
            f'LEVEL {player.level}  ({player.experience}/{player.experience_to_next_level}XP)'
        )
        top += 1
        root_console.draw_str(left, top, f'FOOD: {player.food}')

        # base stats
        top += 3
        root_console.draw_str(left, top, f'POWER: {player.power}')
        top += 1
        root_console.draw_str(left, top, f'BLOCK: {player.block}')

        # combat stats
        top += 3
        root_console.draw_str(left, top,
                              f'HIT CHANCE: {player.combined_hit_chance}%')
        top += 1
        root_console.draw_str(left, top,
                              f'CRIT CHANCE: {player.combined_crit_chance}%')
        top += 1
        root_console.draw_str(
            left, top, f'DAMAGE: {player.min_damage}-{player.max_damage}')

        # level-up point application
        top += 5
        root_console.draw_str(left,
                              top,
                              f'UNSPENT POINTS: {player.unspent_points}',
                              fg=(255, 255, 0))
        if player.unspent_points > 0:
            top += 1
            root_console.draw_str(left + 2, top,
                                  f'[1] INCREASE MAX HEALTH (+1)')
            top += 1
            root_console.draw_str(left + 2, top, f'[2] INCREASE POWER (+1)')
            top += 1
            root_console.draw_str(left + 2, top, f'[3] INCREASE BLOCK (+1)')

        tdl.flush()

        user_input = wait_for_keys(
            ['ESCAPE', 'LEFT', 'RIGHT', 'DOWN', 'UP', 'c', '1', '2', '3'])

        if user_input == 'ESCAPE' or user_input == 'c':
            return GameState.ROOM

        elif player.unspent_points > 0:
            if user_input == '1':
                # increase max health
                player.max_health += 1
                player.spend_points(1)
            elif user_input == '2':
                # increase power
                player.power += 1
                player.spend_points(1)
            elif user_input == '3':
                # increase block
                player.block += 1
                player.spend_points(1)

        else:
            continue
Beispiel #10
0
def state(game: GameData, root_console: tdl.Console) -> GameState:

    root_console.clear()
    tdl.flush()

    room_size = 5
    spacing = 0

    the_map = game.current_map

    total_width = (room_size + (spacing * 2)) * the_map.size[0]
    total_height = (room_size + (spacing * 2)) * the_map.size[1]

    map_console = tdl.Console(total_width, total_height)

    legend_scheme = {
        EncounterType.MONSTER: (180, 0, 0),
        EncounterType.SHRINE: (255, 255, 200),
        EncounterType.TREASURE: (200, 200, 0),
        EncounterType.TRAP: (149, 108, 53),
        EncounterType.STAIRS: (0, 200, 235),
        EncounterType.EMPTY: (255, 255, 255)
    }

    # legend
    x = root_console.width - 20
    y = 1
    root_console.draw_str(x, y, 'LEGEND', bg=None, fg=(255, 255, 255))
    y += 1
    root_console.draw_str(x,
                          y,
                          '# Shrine',
                          bg=None,
                          fg=legend_scheme[EncounterType.SHRINE])
    y += 1
    root_console.draw_str(x,
                          y,
                          '# Treasure',
                          bg=None,
                          fg=legend_scheme[EncounterType.TREASURE])
    y += 1
    root_console.draw_str(x,
                          y,
                          '# Monster',
                          bg=None,
                          fg=legend_scheme[EncounterType.MONSTER])
    y += 1
    root_console.draw_str(x,
                          y,
                          '# Trap',
                          bg=None,
                          fg=legend_scheme[EncounterType.TRAP])
    y += 1
    root_console.draw_str(x,
                          y,
                          '# Stairs',
                          bg=None,
                          fg=legend_scheme[EncounterType.STAIRS])
    y += 1
    root_console.draw_str(x,
                          y,
                          '# Empty',
                          bg=None,
                          fg=legend_scheme[EncounterType.EMPTY])
    y += 1
    root_console.draw_str(x, y, '@ Player', bg=None, fg=(255, 255, 255))

    # bottom bar
    root_console.draw_rect(0,
                           root_console.height - 3,
                           root_console.width,
                           3,
                           None,
                           bg=(200, 200, 200))
    root_console.draw_str(1,
                          root_console.height - 2,
                          '[ESC] back',
                          fg=(0, 0, 0),
                          bg=None)
    root_console.draw_str(15,
                          root_console.height - 2,
                          '[Arrow Keys] scroll map',
                          fg=(0, 0, 0),
                          bg=None)
    root_console.draw_str(50,
                          root_console.height - 2,
                          '[Enter] center map',
                          fg=(0, 0, 0),
                          bg=None)

    map_center_x = game.current_room.x * (room_size + spacing) + spacing
    map_center_y = game.current_room.y * (room_size + spacing) + spacing

    # draw the rooms
    while True:
        map_console.clear()
        for room in [room for room in the_map.rooms if room.discovered]:
            x = room.x * (room_size + spacing) + spacing
            y = room.y * (room_size + spacing) + spacing

            for xx in range(x, x + room_size):
                for yy in range(y, y + room_size):
                    t = '.'
                    if xx == x or xx == x + room_size - 1 or yy == y or yy == y + room_size - 1:
                        t = '#'
                    if room.north and yy == y and xx == x + 2:
                        t = '.'
                    if room.south and yy == y + room_size - 1 and xx == x + 2:
                        t = '.'
                    if room.east and yy == y + 2 and xx == x + room_size - 1:
                        t = '.'
                    if room.west and yy == y + 2 and xx == x:
                        t = '.'

                    color = (255, 255, 255)

                    if room.encounter and room.encounter in legend_scheme:
                        color = legend_scheme[room.encounter]

                    map_console.draw_char(xx, yy, t, bg=None, fg=color)

            if room == game.current_room:
                map_console.draw_char(x + int(room_size / 2),
                                      y + int(room_size / 2),
                                      game.the_player.tile,
                                      fg=(255, 255, 255))

        src_x = max(map_center_x - 15, 0)
        src_y = max(map_center_y - 15, 0)
        root_console.draw_rect(0, 0, 30, 30, ' ', bg=(50, 50, 50))
        root_console.blit(map_console, 0, 0, 30, 30, src_x, src_y, 1.0, 0.0)

        tdl.flush()
        response = wait_for_keys([
            'ESCAPE', 'SPACE', 'ENTER', 'm', 'UP', 'DOWN', 'LEFT', 'RIGHT', '0'
        ])
        if response in ['ESCAPE', 'SPACE', 'm']:
            break
        # scroll the map
        if response == 'UP':
            if map_center_y > 0:
                map_center_y -= 1
        elif response == 'DOWN':
            if map_center_y < total_height - 1:
                map_center_y += 1
        elif response == 'LEFT':
            if map_center_x > 0:
                map_center_x -= 1
        elif response == 'RIGHT':
            if map_center_x < total_width - 1:
                map_center_x += 1
        elif response == 'ENTER':
            # recenter the map
            map_center_x = game.current_room.x * (room_size +
                                                  spacing) + spacing
            map_center_y = game.current_room.y * (room_size +
                                                  spacing) + spacing
        elif response == '0':
            # TODO: remove this cheat
            # reveal entire map
            for room in the_map.rooms:
                room.discovered = True

    return GameState.ROOM
Beispiel #11
0
def state_usable_inventory(game: GameData, root_console: tdl.Console):
    # Used for player-inventory interaction during the combat

    the_player = game.the_player
    # get user's inventory that is allowed to be used in combat
    available_items = [item for item in the_player.inventory.items if item.can_activate_in_combat()]

    # set up console
    inventory_console = tdl.Console(int(root_console.width / 2), root_console.height - 23)
    inventory_console.set_colors(fg=(200, 200, 200), bg=(0, 0, 90))

    selected_index = 0
    current_top = 0

    while True:
        inventory_console.clear()
        inventory_console.draw_str(1, 0, 'COMBAT ITEMS:')

        text_y = 2
        text_x = 1
        counter = 1
        max_lines = 14

        if len(available_items) > 0:
            for index, item in enumerate(available_items[current_top:current_top+max_lines]):
                text_color = (200, 200, 200)
                if index + current_top == selected_index:
                    text_color = (255, 255, 0)
                inventory_console.draw_str(text_x, text_y, f'{str(item)}', fg=text_color)
                text_y += 1
                counter += 1
        else:
            inventory_console.draw_str(text_x, text_y, 'YOU HAVE NO ITEMS THAT CAN BE USED')

        # blit to root and flush
        root_console.blit(inventory_console, int(root_console.width / 2), 0)
        tdl.flush()

        user_input = wait_for_keys(['ENTER', 'ESCAPE', 'SPACE', 'UP', 'DOWN'])
        if user_input in ('ENTER', 'SPACE', ):
            if len(available_items) == 0:
                continue
            item_to_use: Item = available_items[selected_index]
            result_text = None
            if item_to_use.can_activate_on_player():
                result_text = item_to_use.activate(the_player)
            elif item_to_use.can_activate_on_monster():
                result_text = item_to_use.activate(game.current_room.monster)
            if result_text:
                the_player.inventory.remove_item(item_to_use)
                game.log(result_text, fg=(255, 255, 0))
            break

        elif user_input == 'ESCAPE':
            # nevermind
            return False

        elif user_input == 'UP':
            # scroll selection up and scroll list up if needed
            if current_top <= 0 and selected_index <= 0:
                # TODO: maybe loop around to bottom of list?
                continue
            selected_index -= 1
            if selected_index < current_top:
                current_top = selected_index

        elif user_input == 'DOWN':
            # scroll selection down and scroll list down if needed
            if selected_index >= len(available_items) - 1:
                # TODO: maybe loop around to top of list?
                continue

            selected_index += 1
            if selected_index >= current_top + max_lines:
                current_top = selected_index

    return True
Beispiel #12
0
def state(game: GameData, root_console: tdl.Console) -> GameState:

    if game.current_room.monster is None:
        monster = game.current_room.monster = create_monster_for_level(game.current_level)
    else:
        monster = game.current_room.monster

    player: Character = game.the_player
    player_x = int(root_console.width / 2)
    monster_x = player_x
    top = 5
    left = 5
    room_draw_width = game.room_draw_width
    room_draw_height = game.room_draw_height
    center_x = int(math.floor(room_draw_width / 2))
    center_y = int(math.floor(room_draw_height / 2))

    # if you flee a monster and come back, it will return to full health
    monster.reset()

    game.log(f'You encounter a {monster.name}!', (255, 0, 0))

    fighting: bool = True
    did_flee: bool = False
    player_used_turn: bool = False

    player_combat_skills = player.get_combat_skills()

    while fighting:

        if player_used_turn:
            # tick status effects
            player.tick_status_effects()
            monster.tick_status_effects()

            # tick skills
            player.tick_skill_cooldowns()
            monster.tick_skill_cooldowns()

        # clear console
        root_console.clear()

        # draw room
        root_console.draw_rect(left, top, room_draw_width, room_draw_height, '.', (255, 255, 255), (0, 0, 0))
        root_console.draw_frame(left, top, room_draw_width, room_draw_height, '#', (255, 255, 255), (0, 0, 0))
        if game.current_room.north:
            root_console.draw_char(left + center_x, top, '.', (255, 255, 255), (0, 0, 0))
        if game.current_room.south:
            root_console.draw_char(left + center_x, top + room_draw_height - 1, '.', (255, 255, 255), (0, 0, 0))
        if game.current_room.west:
            root_console.draw_char(left, top + center_y, '.', (255, 255, 255), (0, 0, 0))
        if game.current_room.east:
            root_console.draw_char(left + room_draw_width - 1, top + center_y, '.', (255, 255, 255), (0, 0, 0))

        # draw player
        p_x = center_x
        p_y = center_y
        if game.previous_room:
            if game.previous_room == game.current_room.north:
                # came from the north
                p_y = 2
            elif game.previous_room == game.current_room.south:
                # came from the south
                p_y = room_draw_height - 3
            elif game.previous_room == game.current_room.east:
                # came from the east
                p_x = room_draw_width - 3
            elif game.previous_room == game.current_room.west:
                # came from the west
                p_x = 2
        root_console.draw_char(left + p_x, top + p_y, player.tile, bg=None, fg=(255, 255, 255))

        # draw monster
        root_console.draw_char(left + center_x, top + center_y, monster.tile, bg=None, fg=(255, 0, 0))

        # draw the log
        game.draw_log(root_console)

        # Draw Monster Information
        monster_y = 0
        root_console.draw_str(monster_x, monster_y, f'{monster.name}')
        monster_y += 1
        root_console.draw_str(monster_x, monster_y, f'HP: {monster.health} / {monster.max_health}')
        monster_y += 1
        for status in monster.active_status_effects:
            # list each status effect
            root_console.draw_str(monster_x, monster_y,
                                  f'({status.status_effect.name} x{status.duration})',
                                  fg=(255, 255, 0))
            monster_y += 1

        # Draw Player Information
        player_y = 10
        root_console.draw_str(player_x, player_y, f'{player.name}')
        player_y += 1
        root_console.draw_str(player_x, player_y, f'HP: {player.health} / {player.max_health}')
        player_y += 1
        for status in player.active_status_effects:
            # list each status effect
            root_console.draw_str(player_x, player_y,
                                  f'({status.status_effect.name} x{status.duration})',
                                  fg=(255, 0, 0))
            player_y += 1

        player_y += 2
        root_console.draw_str(player_x, player_y, '[A] Attack')
        player_y += 1

        if len(player_combat_skills) > 0:
            for index, skill in enumerate(player_combat_skills, 1):
                skill_text = f'{skill.name}'
                if not skill.ready():
                    skill_text = f'{skill_text} ({skill.cooldown_left} turns)'

                root_console.draw_str(player_x, player_y, f'[{index}] {skill_text}')
                player_y += 1

        root_console.draw_str(player_x, player_y + 5, '[U] Use Item')
        root_console.draw_str(player_x, player_y + 6, '[F] Flee')

        tdl.flush()

        # only allow available keys
        available_keys = ['ESCAPE', 'a', 'f', 'u']
        if len(player_combat_skills) >= 1 and player_combat_skills[0].ready():
            available_keys.append('1')
        if len(player_combat_skills) >= 2 and player_combat_skills[1].ready():
            available_keys.append('2')
        user_input = wait_for_keys(available_keys)

        if user_input == 'ESCAPE':
            return GameState.CLOSE

        # will be set to True if the player attempts to flee below
        tried_to_flee: bool = False
        player_used_turn = False

        # ATTACK
        if user_input == 'a':
            game.log(f'You attack the {monster.name}...')
            player_used_turn = True
            results = do_attack(player, monster)
            if not results.dodged:
                game.log(f'...and hit for {results.damage} damage.')
                # if the player's weapon has the "Vampiric" trait, heal the player (maybe)
                if player.weapon and player.weapon.suffix == WeaponSuffix.Vampirism:
                    did_vamp = random.randint(1, 101) <= player.weapon.life_steal_chance
                    vamp_amount = int(math.ceil(player.weapon.life_steal_percent * results.damage))
                    # TODO: decide whether or not to tell the player...
                    player.heal(vamp_amount)
                # if the player's weapon has the "Doom" trait, kill the monster if it only has 1 HP left
                elif player.weapon and player.weapon.suffix == WeaponSuffix.Doom and monster.health == 1:
                    monster.health = 0
                    game.log(f'DOOM has befallen the {monster.name}!')

            else:
                # enemy dodged the attack
                game.log(f'...and miss as the {monster.name} dodges out of the way.')
        # USE SKILL 1
        elif user_input == '1' and len(player_combat_skills) > 0:
            skill: ActiveSkill = player_combat_skills[0]
            if skill.ready():
                if skill.activate(game):
                    player_used_turn = skill.uses_turn
        # USE SKILL 2
        elif user_input == '2' and len(player_combat_skills) > 1:
            skill: ActiveSkill = player_combat_skills[1]
            if skill.ready():
                if skill.activate(game):
                    player_used_turn = skill.uses_turn
        # ATTEMPT TO FLEE THE MONSTER
        elif user_input == 'f':
            player_used_turn = True
            game.log('You attempt to flee.')
            tried_to_flee = True
            chance_to_flee = 80
            roll = random.randrange(0, 100)
            if roll <= chance_to_flee:
                did_flee = True
        # OPEN INVENTORY
        elif user_input == 'u':
            # only uses a turn if the player uses an item
            player_used_turn = state_usable_inventory(game, root_console)

        # if the monster is dead, combat is over
        if monster.health <= 0:
            fighting = False
            game.log(f'You slay the {monster.name}!')
            continue

        # if monster is alive, it takes a turn (assuming the player did a "used_turn" action)
        # monster gets a turn even if the player succeeded at fleeing
        # make sure the monster is ABLE to attack
        if player_used_turn:
            if not monster.has_status_effect(StatusEffectType.Stunned):
                game.log(f'The {monster.name} attacks...', (255, 0, 0))
                if tried_to_flee and player.armor and player.armor.suffix == ArmorSuffix.Fleeing:
                    game.log(f'...and misses.')
                else:
                    results = do_attack(monster, player)
                    if not results.dodged:
                        game.log(f'...and hits for {results.damage} damage')
                    else:
                        game.log(f'...and misses you when you dodge out of the way.')

                if player.health <= 0:
                    fighting = False
                    # cannot flee if you are dead, Jim
                    did_flee = False
                    continue
                elif tried_to_flee and not did_flee:
                    game.log("You failed to run away", (255, 0, 0))
                elif tried_to_flee and did_flee:
                    fighting = False
                    continue
            else:
                game.log(f'The {monster.name} is STUNNED and cannot attack.')

    if did_flee:
        # fleeing does not mark the encounter as complete
        text = f'You run away from the {monster.name}, returning to the previous room.'
        game.log(text)
        game.current_room = game.previous_room
        game.previous_room = None
        return GameState.ROOM

    game.current_room.encountered = True

    # killed the monster! yay!
    if monster.health <= 0:
        experience_gained = monster.experience
        if player.weapon and player.weapon.suffix == WeaponSuffix.Adventuring:
            # 15% boost to XP gain
            experience_gained = int(math.ceil(experience_gained * 1.15))
        text = f'You have killed the {monster.name}! You have been awarded {experience_gained} XP.'
        game.log(text)
        if player.grant_experience(experience_gained):
            # player leveled up!
            game.log(f'LEVEL UP! You have reached level {player.level}!', (255, 255, 0))

        return GameState.ROOM

    # player is dead
    return GameState.GAME_OVER
Beispiel #13
0
def draw_player_status_bar(the_player, x: int, y: int,
                           root_console: tdl.Console):
    """
    Helper function that draws the player's status bar at the bottom
    of the screen.
    """
    root_console.draw_rect(0,
                           root_console.height - 3,
                           root_console.width,
                           3,
                           None,
                           bg=(200, 200, 200))

    name = f'@{the_player.name}'
    food = f'FOOD: {the_player.food}'
    health = f'{the_player.health}/{the_player.max_health}HP'
    level = f'LEVEL {the_player.level}'
    xp = f'{the_player.experience}/{the_player.experience_to_next_level}XP'

    root_console.draw_str(x, y, name, fg=(0, 0, 0), bg=None)

    x += len(name) + 4

    root_console.draw_str(x, y, health, fg=(0, 0, 0), bg=None)

    x += len(health) + 4

    root_console.draw_str(x, y, food, fg=(0, 0, 0), bg=None)

    x += len(food) + 4

    root_console.draw_str(x, y, level, fg=(0, 0, 0), bg=None)

    x += len(level) + 4

    root_console.draw_str(x, y, xp, fg=(0, 0, 0), bg=None)