Esempio n. 1
0
def play_game():
    screen_width = 70
    screen_height = 45

    bar_width = 20

    panel_horiz_height = 10
    panel_horiz_y = screen_height - panel_horiz_height
    panel_horiz = libtcod.console_new(screen_width, panel_horiz_height)

    panel_vert_height = panel_horiz_y
    panel_vert_y = 0
    panel_vert = libtcod.console_new(48, panel_vert_height)

    message_x = 1
    message_width = screen_width - 1
    message_height = panel_horiz_height - 2

    map_width = 47
    map_height = panel_horiz_y

    room_max_size = 8
    room_min_size = 3
    max_rooms = 30

    fov_algorithm = 0
    fov_light_walls = True
    fov_radius = 10

    max_monsters_per_room = 3
    max_items_per_room = 2

    colors = {
        'dark_wall': libtcod.Color(0, 0, 100),
        'dark_ground': libtcod.Color(50, 50, 150),
        'light_wall': libtcod.Color(130, 110, 50),
        'light_ground': libtcod.Color(200, 180, 50)
    }

    fighter_component = Fighter(hp=100, defense=1, power=2)
    inventory_component = Inventory(26)
    level_component = Level()
    equipment_component = Equipment()

    player = Entity(0, 0, '@', libtcod.white, 'Player', blocks=True, render_order=RenderOrder.ACTOR,
                    fighter=fighter_component, inventory=inventory_component, level=level_component,
                    equipment=equipment_component)

    entities = [player]

    equippable_component = Equippable(EquipmentSlots.MAIN_HAND, power_bonus=2)
    dagger = Entity(0, 0, '-', libtcod.sky, 'Dagger', equippable=equippable_component)
    player.inventory.add_item(dagger)
    player.equipment.toggle_equip(dagger)

    libtcod.console_set_custom_font(consts.FONT, libtcod.FONT_LAYOUT_ASCII_INROW)

    libtcod.console_init_root(screen_width, screen_height, 'libtcod tutorial revised', False)

    con = libtcod.console_new(screen_width, screen_height)

    game_map = GameMap(map_width, map_height)
    game_map.make_map(max_rooms, room_min_size, room_max_size, map_width, map_height, player, entities)

    fov_recompute = True

    fov_map = initialize_fov(game_map)

    message_log = MessageLog(message_x, message_width, message_height)

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

    game_state = GameStates.PLAYERS_TURN
    previous_game_state = game_state

    targeting_item = None

    while not libtcod.console_is_window_closed():
        libtcod.sys_check_for_event(libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, key, mouse)

        if fov_recompute:
            recompute_fov(fov_map, player.x, player.y, fov_radius, fov_light_walls, fov_algorithm)

        render_all(con, panel_vert, panel_horiz, entities, player, game_map, fov_map, fov_recompute, message_log,
                   screen_width, screen_height,
                   bar_width, panel_horiz_height, panel_horiz_y, panel_vert_height, panel_vert_y, mouse, colors,
                   game_state)

        fov_recompute = False

        libtcod.console_flush()

        clear_all(con, entities)

        action = handle_keys(key, game_state)
        mouse_action = handle_mouse(mouse)

        move = action.get('move')
        exit = action.get('exit')
        pickup = action.get('pickup')
        fullscreen = action.get('fullscreen')
        show_inventory = action.get('show_inventory')
        inventory_index = action.get('inventory_index')
        drop_inventory = action.get('drop_inventory')
        take_stairs = action.get('take_stairs')
        level_up = action.get('level_up')
        show_character_screen = action.get('show_character_screen')
        wait = action.get('wait')

        left_click = mouse_action.get('left_click')
        right_click = mouse_action.get('right_click')

        player_turn_results = []

        if move and game_state == GameStates.PLAYERS_TURN:
            dx, dy = move
            destination_x = player.x + dx
            destination_y = player.y + dy

            if not game_map.is_blocked(player.x + dx, player.y + dy):
                if not game_map.is_blocked(destination_x, destination_y):
                    target = get_blocking_entities_at_location(entities, destination_x, destination_y)

                    if target:
                        attack_results = player.fighter.attack(target)
                        player_turn_results.extend(attack_results)
                    else:
                        player.move(dx, dy)

                        fov_recompute = True
                    game_state = GameStates.ENEMY_TURN

        elif wait:
            message_log.add_message(Message('You wait for a moment.', libtcod.yellow))
            game_state = GameStates.ENEMY_TURN

        elif pickup and game_state == GameStates.PLAYERS_TURN:
            for entity in entities:
                if entity.item and entity.x == player.x and entity.y == player.y:
                    pickup_results = player.inventory.add_item(entity)
                    player_turn_results.extend(pickup_results)

                    break
            else:
                message_log.add_message(Message('There is nothing here to pick up.', libtcod.yellow))

        if show_inventory:
            previous_game_state = game_state
            game_state = GameStates.SHOW_INVENTORY

        if drop_inventory:
            previous_game_state = game_state
            game_state = GameStates.DROP_INVENTORY

        if inventory_index is not None and previous_game_state != GameStates.PLAYER_DEAD and inventory_index < len(
                player.inventory.items):
            item = player.inventory.items[inventory_index]
            if game_state == GameStates.SHOW_INVENTORY:
                player_turn_results.extend(player.inventory.use(item, entities=entities, fov_map=fov_map))
            elif game_state == GameStates.DROP_INVENTORY:
                player_turn_results.extend(player.inventory.drop_item(item))

        if take_stairs and game_state == GameStates.PLAYERS_TURN:
            for entity in entities:
                if entity.stairs and entity.x == player.x and entity.y == player.y:
                    entities = game_map.next_floor(player, message_log, max_rooms, room_min_size, room_max_size,
                                                   map_width, map_height)
                    fov_map = initialize_fov(game_map)
                    fov_recompute = True
                    libtcod.console_clear(con)

                    break
            else:
                message_log.add_message(Message('There are no stairs here.', libtcod.yellow))

        if level_up:
            if level_up == 'hp':
                player.fighter.base_max_hp += 20
                player.fighter.hp += 20
            elif level_up == 'str':
                player.fighter.base_power += 1
            elif level_up == 'def':
                player.fighter.base_defense += 1

            game_state = previous_game_state

        if show_character_screen:
            previous_game_state = game_state
            game_state = GameStates.CHARACTER_SCREEN

        if game_state == GameStates.TARGETING:
            if left_click:
                target_x, target_y = left_click

                item_use_results = player.inventory.use(targeting_item, entities=entities, fov_map=fov_map,
                                                        target_x=target_x, target_y=target_y)
                player_turn_results.extend(item_use_results)
            elif right_click:
                player_turn_results.append({'targeting_cancelled': True})

        if exit:
            if game_state in (GameStates.SHOW_INVENTORY, GameStates.DROP_INVENTORY, GameStates.CHARACTER_SCREEN):
                game_state = previous_game_state
            elif game_state == GameStates.TARGETING:
                player_turn_results.append({'targeting_cancelled': True})
            else:
                return True

        if fullscreen:
            libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen())

        for player_turn_result in player_turn_results:
            message = player_turn_result.get('message')
            dead_entity = player_turn_result.get('dead')
            item_added = player_turn_result.get('item_added')
            item_consumed = player_turn_result.get('consumed')
            item_dropped = player_turn_result.get('item_dropped')
            targeting = player_turn_result.get('targeting')
            targeting_cancelled = player_turn_result.get('targeting_cancelled')
            xp = player_turn_result.get('xp')
            equip = player_turn_result.get('equip')

            if message:
                message_log.add_message(message)

            if dead_entity:
                if dead_entity == player:
                    message, game_state = kill_player(dead_entity)
                else:
                    message = kill_monster(dead_entity)

                message_log.add_message(message)

            if item_added:
                entities.remove(item_added)

                game_state = GameStates.ENEMY_TURN

            if item_consumed:
                game_state = GameStates.ENEMY_TURN

            if targeting:
                previous_game_state = GameStates.PLAYERS_TURN
                game_state = GameStates.TARGETING

                targeting_item = targeting

                message_log.add_message(targeting_item.item.targeting_message)

            if targeting_cancelled:
                game_state = previous_game_state

                message_log.add_message(Message('Targeting cancelled'))

            if xp:
                leveled_up = player.level.add_xp(xp)
                message_log.add_message(Message('You gain {0} experience points.'.format(xp)))

                if leveled_up:
                    message_log.add_message(Message(
                        'Your battle skills grow stronger! You reached level {0}'.format(
                            player.level.current_level) + '!', libtcod.yellow))
                    previous_game_state = game_state
                    game_state = GameStates.LEVEL_UP

            if item_dropped:
                entities.append(item_dropped)

                game_state = GameStates.ENEMY_TURN

            if equip:
                equip_results = player.equipment.toggle_equip(equip)

                for equip_result in equip_results:
                    equipped = equip_result.get('equipped')
                    dequipped = equip_result.get('dequipped')

                    if equipped:
                        message_log.add_message(Message('You equipped the {0}'.format(equipped.name)))

                    if dequipped:
                        message_log.add_message(Message('You dequipped the {0}'.format(dequipped.name)))

                game_state = GameStates.ENEMY_TURN

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

                    for enemy_turn_result in enemy_turn_results:
                        message = enemy_turn_result.get('message')
                        dead_entity = enemy_turn_result.get('dead')

                        if message:
                            message_log.add_message(message)

                        if dead_entity:
                            if dead_entity == player:
                                message, game_state = kill_player(dead_entity)
                            else:
                                message = kill_monster(dead_entity)

                            message_log.add_message(message)

                            if game_state == GameStates.PLAYER_DEAD:
                                break

                    if game_state == GameStates.PLAYER_DEAD:
                        break
            else:
                game_state = GameStates.PLAYERS_TURN
Esempio n. 2
0
class Rogue(tcod.event.EventDispatch):
    def __init__(self):
        self.recompute = True
        self.game_map = None
        self.map_console = tcod.console.Console(CONFIG.get('map_width'),
                                                CONFIG.get('map_height'), 'F')
        self.info_console = tcod.console.Console(
            CONFIG.get('map_width'), CONFIG.get('info_panel_height'), 'F')
        self.message_console = tcod.console.Console(
            CONFIG.get('map_width'), CONFIG.get('message_panel_height'), 'F')
        self.menu_console = tcod.console.Console(CONFIG.get('map_width'),
                                                 CONFIG.get('map_height'), 'F')
        self.game_state = GameStates.PLAYER_TURN
        self.previous_game_state = None
        self.message_log = None
        self.motion = tcod.event.MouseMotion()
        self.lbut = self.mbut = self.rbut = 0
        self.quest_request = None
        self.using_item = None
        self.last_debug_entity = None

    def start_fresh_game(self):
        logging.basicConfig(
            filename=
            f'{resource_path("log")}/{datetime.datetime.now().strftime("%Y%m%d%H%M%S")}.log',
            filemode='w',
            format='%(asctime)s - %(message)s',
            datefmt='%d-%b-%y %H:%M:%S',
            level=CONFIG.get('logging_level'))

        pubsub.pubsub = pubsub.PubSub()

        self.message_log = MessageLog(CONFIG.get('message_width'),
                                      CONFIG.get('message_height'))
        pubsub.pubsub.subscribe(
            pubsub.Subscription(self.message_log, pubsub.PubSubTypes.MESSAGE,
                                pubsub.add_to_messages))

        self.player = create_player()

        self.game_map = GameMap()
        self.game_map.create_floor(self.player)

        self.start_game()

    def start_game(self):
        self.update_fov()

        quest.active_quests = []

        self.game_state = GameStates.PLAYER_TURN
        self.previous_game_state = None

        self.message_log.add_message(
            Message('Let\'s get ready to rock and/or roll!', tcod.yellow))

    def on_enter(self):
        tcod.sys_set_fps(60)

    def update_fov(self):
        self.game_map.current_level.compute_fov(
            self.player.x,
            self.player.y,
            algorithm=self.player.fov.fov_algorithm,
            radius=self.player.fov.fov_radius,
            light_walls=self.player.fov.fov_light_walls)

        if self.player.sleep:
            self.game_map.current_level.npc_fov = tcod.map.compute_fov(
                self.game_map.current_level.transparent,
                pov=(self.player.x, self.player.y),
                algorithm=tcod.FOV_RESTRICTIVE,
                light_walls=True,
                radius=10)

        else:
            self.game_map.current_level.npc_fov = self.game_map.current_level.fov

        if not CONFIG.get('debug'):
            where_fov = np.where(self.game_map.current_level.fov[:])
            self.game_map.current_level.explored[where_fov] = True
        else:
            self.game_map.current_level.fov[:] = True
            self.game_map.current_level.explored[:] = True

    def on_draw(self):
        #---------------------------------------------------------------------
        # Recompute the player's field of view.
        #---------------------------------------------------------------------
        self.update_fov()

        #---------------------------------------------------------------------
        # Render and display the dungeon and its inhabitates.
        #---------------------------------------------------------------------
        self.game_map.current_level.render(self.map_console)

        if not CONFIG.get('debug'):
            self.game_map.current_level.render_torch(
                self.player.x, self.player.y, self.player.fov.fov_radius,
                self.map_console)

        if CONFIG.get('debug') and self.game_map.current_level.within_bounds(
                self.motion.tile.x, self.motion.tile.y):
            for entity in self.game_map.current_level.entities.get_entities_in_position(
                (self.motion.tile.x, self.motion.tile.y)):
                if entity.movement:
                    dijkstra = calculate_dijkstra(self.game_map,
                                                  [(entity.x, entity.y)],
                                                  avoid_entity=self.player)
                    self.game_map.current_level.render_dijkstra(
                        dijkstra, self.map_console)

                    if entity.ai:
                        path = entity.ai.tree.namespace.get("path")
                        target = entity.ai.tree.namespace.get("target")
                        if path or target:
                            self.game_map.current_level.render_entity_detail(
                                path, target, self.map_console)
                    if not (entity == self.last_debug_entity):
                        entity.debug()
                        self.last_debug_entity = entity

        #---------------------------------------------------------------------
        # Render infomation panels.
        #---------------------------------------------------------------------
        render_info_console(self.info_console, self.player, self.game_map)
        render_message_console(self.message_console, self.message_log)

        #---------------------------------------------------------------------
        # Blit the subconsoles to the main console and flush all rendering.
        #---------------------------------------------------------------------
        root_console.clear(fg=COLORS.get('console_background'))

        self.map_console.blit(root_console, 0, 0, 0, 0, self.map_console.width,
                              self.map_console.height)

        under_mouse_text = get_names_under_mouse(self.motion.tile.x,
                                                 self.motion.tile.y,
                                                 self.game_map.current_level)
        text_height = root_console.get_height_rect(1, 0,
                                                   root_console.width - 2, 10,
                                                   under_mouse_text)

        root_console.print_box(
            1,
            CONFIG.get('info_panel_y') - text_height - 1,
            root_console.width - 2,
            text_height,
            under_mouse_text,
            fg=tcod.white,
            bg=None,
            alignment=tcod.LEFT,
        )

        self.info_console.blit(root_console, 0, CONFIG.get('info_panel_y'), 0,
                               0, CONFIG.get('full_screen_width'),
                               CONFIG.get('info_panel_height'))
        self.message_console.blit(root_console, 0,
                                  CONFIG.get('message_panel_y'), 0, 0,
                                  CONFIG.get('full_screen_width'),
                                  CONFIG.get('message_panel_height'))

        if self.game_state in MENU_STATES:
            #---------------------------------------------------------------------
            # Render any menus.
            #---------------------------------------------------------------------
            exclude = []
            if self.using_item:
                exclude.append(self.using_item)

            self.menu_console = render_menu_console(self.game_state,
                                                    self.player,
                                                    self.quest_request,
                                                    exclude)

            self.menu_console.blit(root_console, 0, 0, 0, 0,
                                   CONFIG.get('full_screen_width'),
                                   CONFIG.get('full_screen_height'))

    def ev_keydown(self, event: tcod.event.KeyDown):
        #---------------------------------------------------------------------
        # Get key input from the self.player.
        #---------------------------------------------------------------------
        input_result = handle_keys(event, self.game_state)

        if (len(input_result) == 0):
            if CONFIG.get('debug'):
                #logging.info("No corresponding result for key press.")
                pass
            return

        action, action_value = unpack_single_key_dict(input_result)
        self.process_turn(action, action_value)

    def ev_mousemotion(self, event: tcod.event.MouseMotion):
        self.motion = event

    def ev_mousebuttondown(self, event: tcod.event.MouseButtonDown):
        input_type = None
        if event.button == tcod.event.BUTTON_LEFT:
            self.lbut = True
            input_type = InputTypes.TARGETING
        elif event.button == tcod.event.BUTTON_MIDDLE:
            self.mbut = True
        elif event.button == tcod.event.BUTTON_RIGHT:
            self.rbut = True
            input_type = InputTypes.EXIT

        self.process_turn(input_type, (event.tile.x, event.tile.y))

    def ev_mousebuttonup(self, event: tcod.event.MouseButtonUp):
        if event.button == tcod.event.BUTTON_LEFT:
            self.lbut = False
        elif event.button == tcod.event.BUTTON_MIDDLE:
            self.mbut = False
        elif event.button == tcod.event.BUTTON_RIGHT:
            self.rbut = False

    def ev_quit(self, event: tcod.event.Quit):
        raise SystemExit()

    def game_actions(self, action, action_value):
        if action == InputTypes.GAME_EXIT:
            self.game_state = GameStates.GAME_EXIT
            return True

        if action == InputTypes.GAME_SAVE:
            #FIXME: Saves don't work
            #save_game(self.player, self.game_map, message_log, self.game_state, pubsub.pubsub)
            return True

        if action == InputTypes.GAME_RESTART:
            self.start_fresh_game()
            return True

        if action == InputTypes.GAME_RESET:
            self.game_map.first_floor(self.player)
            self.start_game()
            return True

        if action == InputTypes.RELOAD_LEVEL:
            self.game_map.next_floor(self.player)
            self.update_fov()
            return True

        return False

    def change_state_action(self, action, action_value):
        if action == InputTypes.CHARACTER_SCREEN:
            self.previous_game_state = self.game_state
            self.game_state = GameStates.CHARACTER_SCREEN
            return True

        if action == InputTypes.INVENTORY_DROP:
            self.previous_game_state = self.game_state
            self.game_state = GameStates.INVENTORY_DROP
            return True

        if action == InputTypes.INVENTORY_EXAMINE:
            self.previous_game_state = self.game_state
            self.game_state = GameStates.INVENTORY_EXAMINE
            return True

        if action == InputTypes.INVENTORY_THROW:
            self.previous_game_state = self.game_state
            self.game_state = GameStates.INVENTORY_THROW
            return True

        if action == InputTypes.INVENTORY_USE:
            self.previous_game_state = self.game_state
            self.game_state = GameStates.INVENTORY_USE
            return True

        if action == InputTypes.LEVEL_UP:
            self.player.level.level_up_stats(action_value)
            self.game_state = self.previous_game_state

        #This needs to come after leveling up or we get stuck in a loop
        if not (self.game_state
                in MENU_STATES) and self.player.level.can_level_up():
            self.previous_game_state = self.game_state
            self.game_state = GameStates.LEVEL_UP
            return True

        if action == InputTypes.QUEST_LIST:
            self.previous_game_state = self.game_state
            self.game_state = GameStates.QUEST_LIST
            return True

        return False

    def debug_actions(self, action, action_value):
        if action == InputTypes.DEBUG_ON:
            CONFIG.update({'debug': True})
            self.update_fov()

        if action == InputTypes.DEBUG_OFF:
            CONFIG.update({'debug': False})
            self.update_fov()

    def menu_actions(self, action, action_value):
        pass

    def quest_actions(self, action, action_value):
        if action == InputTypes.QUEST_RESPONSE:
            if action_value:
                self.quest_request.owner.start_quest(self.game_map)
                self.message_log.add_message(
                    Message(f"Started quest: {self.quest_request.title}",
                            tcod.yellow))
            self.quest_request = None
            self.game_state = self.previous_game_state

        if (action == InputTypes.QUEST_INDEX
                and self.previous_game_state != GameStates.GAME_OVER
                and action_value < len(quest.active_quests)):
            self.message_log.add_message(
                quest.active_quests[action_value].status())
            self.game_state = self.previous_game_state

    def player_actions(self, action, action_value):
        self.player.energy.take_action()

        player_turn_results = []

        player_on_turn_results = self.player.on_turn(self.game_map)
        self.process_results_stack(self.player, player_on_turn_results)

        if action == InputTypes.SLEEP:
            if not self.player.sleep:
                self.player.add_component(Sleep(), 'sleep')
                self.player.sleep.start()
                self.update_fov()
                pubsub.pubsub.add_message(
                    pubsub.Publish(None,
                                   pubsub.PubSubTypes.MESSAGE,
                                   message=Message('You have gone asleep.',
                                                   COLORS.get('effect_text'))))

        if self.player.health.dead:
            self.game_state = GameStates.GAME_OVER
        elif self.player.sleep:
            finished = self.player.sleep.on_turn(self.game_map)
            if finished:
                pubsub.pubsub.add_message(
                    pubsub.Publish(None,
                                   pubsub.PubSubTypes.MESSAGE,
                                   message=Message('You have woken up.',
                                                   COLORS.get('effect_text'))))
                self.update_fov()

            self.game_state = GameStates.ENEMY_TURN
        elif action == InputTypes.WAIT:
            self.game_state = GameStates.ENEMY_TURN
        elif action == InputTypes.MOVE:
            dx, dy = action_value

            if self.game_map.current_level.accessible_tile(
                    self.player.x + dx, self.player.y + dy):
                if self.game_map.current_level.blocked[self.player.x + dx,
                                                       self.player.y + dy]:
                    targets = self.game_map.current_level.entities.get_entities_in_position(
                        (self.player.x + dx, self.player.y + dy))

                    targets_in_render_order = sorted(
                        targets,
                        key=lambda x: x.render_order.value,
                        reverse=True)
                    target = targets_in_render_order[0]

                    if target.interaction.interaction_type == Interactions.QUESTGIVER:
                        quest_results = target.questgiver.talk(self.player)
                        player_turn_results.extend(quest_results)
                    elif target.interaction.interaction_type == Interactions.DOOR:
                        if target.locked:
                            can_unlock = False

                            if target.locked.requires_key:
                                all_keys = self.player.inventory.search(
                                    name='key')
                                for key_to_check in all_keys:
                                    if key_to_check.unlock.unlocks == target.uuid:
                                        can_unlock = True
                                        player_turn_results.extend([{
                                            ResultTypes.DISCARD_ITEM:
                                            key_to_check
                                        }])
                                        break
                            else:
                                can_unlock = True

                            if can_unlock:
                                target.locked.toggle()
                                self.game_map.current_level.update_entity_position(
                                    target)
                                self.update_fov()

                                message = Message(
                                    f"You have unlocked the {target.name}.",
                                    tcod.yellow)
                                player_turn_results.extend([{
                                    ResultTypes.MESSAGE:
                                    message
                                }])
                            else:
                                message = Message(
                                    f"The {target.name} is locked.",
                                    tcod.yellow)
                                player_turn_results.extend([{
                                    ResultTypes.MESSAGE:
                                    message
                                }])
                    elif target.interaction.interaction_type == Interactions.FOE:
                        if target.health and not target.health.dead:
                            attack_results = self.player.offence.attack(
                                target, self.game_map)
                            player_turn_results.extend(attack_results)
                else:
                    self.player.movement.move(dx, dy,
                                              self.game_map.current_level)
                    player_turn_results.extend(
                        quest.check_quest_for_location(self.player))

                    self.update_fov()

                self.game_state = GameStates.ENEMY_TURN
        elif action == InputTypes.PICKUP:
            entities = self.game_map.current_level.entities.get_entities_in_position(
                (self.player.x, self.player.y))
            pickup = False
            for entity in entities:
                if entity.item:
                    if entity.identifiable and identified_items.get(
                            entity.base_name):
                        entity.identifiable.identified = True
                    player_turn_results.extend([{
                        ResultTypes.ADD_ITEM_TO_INVENTORY:
                        entity
                    }])
                    pickup = True
            if not pickup:
                message = Message('There is nothing here to pick up.',
                                  tcod.yellow)
                player_turn_results.extend([{ResultTypes.MESSAGE: message}])
        elif action == InputTypes.DOWN_LEVEL:
            self.game_map.next_floor(self.player)
            self.update_fov()
            message = Message(
                'You take a moment to rest and recover your strength.',
                tcod.light_violet)
            player_turn_results.extend([{ResultTypes.MESSAGE: message}])

            #continue
            return
        elif action == InputTypes.TAKE_STAIRS:
            stair_state = self.game_map.check_for_stairs(
                self.player.x, self.player.y)
            if stair_state == StairOption.GODOWN:
                self.game_map.next_floor(self.player)
                self.update_fov()
                message = Message(
                    'You take a moment to rest and recover your strength.',
                    tcod.light_violet)
                player_turn_results.extend([{ResultTypes.MESSAGE: message}])

                #continue
                return
            elif stair_state == StairOption.GOUP:
                self.game_map.previous_floor(self.player)
                self.update_fov()

                return
            elif stair_state == StairOption.EXIT:
                self.game_state = GameStates.GAME_PAUSED
            else:
                message = Message('There are no stairs here.', tcod.yellow)
                player_turn_results.extend([{ResultTypes.MESSAGE: message}])

        self.process_results_stack(self.player, player_turn_results)

        pubsub.pubsub.process_queue(self.game_map)

    def npc_actions(self):
        self.game_map.current_level.clear_paths()
        for entity in self.game_map.current_level.entities:
            if entity == self.player:
                continue
            entity.energy.increase_energy()
            if entity.energy.take_action():
                if entity.level and entity.level.can_level_up():
                    entity.level.random_level_up(1)
                enemy_turn_results = entity.on_turn(self.game_map)
                self.process_results_stack(entity, enemy_turn_results)
                enemy_turn_results.clear()

                if entity.health and entity.health.dead:
                    entity.death.decompose(self.game_map)
                elif entity.ai:
                    # Enemies move and attack if possible.
                    enemy_turn_results.extend(
                        entity.ai.take_turn(self.game_map))

                    self.process_results_stack(entity, enemy_turn_results)
                    enemy_turn_results.clear()

                pubsub.pubsub.process_queue(self.game_map)

    def process_turn(self, action, action_value):
        player_turn_results = []

        if self.game_actions(action, action_value):
            return

        if self.change_state_action(action, action_value):
            return

        self.debug_actions(action, action_value)

        self.quest_actions(action, action_value)

        if action == InputTypes.EXIT:
            if self.game_state in CANCEL_STATES:
                self.game_state = self.previous_game_state
                self.using_item = None
                if self.game_state == GameStates.QUEST_ONBOARDING:
                    player_turn_results.append(
                        {ResultTypes.QUEST_CANCELLED: True})
            else:
                self.previous_game_state = self.game_state
                self.game_state = GameStates.GAME_PAUSED
                return

        if (action == InputTypes.TARGETING
                and self.game_state == GameStates.TARGETING):
            target_x, target_y = action_value

            player_turn_results.extend(
                self.using_item.usable.use(game_map=self.game_map,
                                           user=self.player,
                                           target_x=target_x,
                                           target_y=target_y))

        if (action == InputTypes.INVENTORY_INDEX
                and self.previous_game_state != GameStates.GAME_OVER
                and action_value < len(self.player.inventory.items)):

            items = self.player.inventory.items.copy()

            if self.using_item:
                items.remove(self.using_item)

            item = items[action_value]

            if self.game_state == GameStates.INVENTORY_USE:
                if item.usable:
                    self.using_item = item
                    player_turn_results.extend(
                        item.usable.use(self.game_map, self.player))
                else:
                    player_turn_results.extend([{ResultTypes.EQUIP: item}])

            elif self.game_state == GameStates.INVENTORY_SELECT:
                player_turn_results.extend(
                    self.using_item.usable.use(self.game_map, self.player,
                                               item))
                self.using_item = None
            elif self.game_state == GameStates.INVENTORY_DROP:
                player_turn_results.extend(
                    self.player.inventory.drop_item(item))
            elif self.game_state == GameStates.INVENTORY_EXAMINE:
                player_turn_results.extend(
                    self.player.inventory.examine_item(item))

        self.process_results_stack(self.player, player_turn_results)

        pubsub.pubsub.process_queue(self.game_map)

        #-------------------------------------------------------------------
        # Player takes their turn.
        #-------------------------------------------------------------------
        if (self.game_state == GameStates.PLAYER_TURN
                or self.game_state == GameStates.PLAYER_SLEEP):
            self.player_actions(action, action_value)

        if (self.game_state in INPUT_STATES
                or self.game_state == GameStates.GAME_OVER):
            return

        #-------------------------------------------------------------------
        # NPCs take their turns.
        #-------------------------------------------------------------------
        self.npc_actions()

        self.player.energy.increase_energy()
        if self.player.energy.can_act:
            if self.player.sleep:
                self.game_state = GameStates.PLAYER_SLEEP
            else:
                if not self.game_state in INPUT_STATES:
                    self.game_state = GameStates.PLAYER_TURN
        else:
            if not self.game_state in INPUT_STATES:
                self.game_state = GameStates.ENEMY_TURN

        if not self.game_state == GameStates.PLAYER_TURN:
            sleep(CONFIG.get('time_between_enemy_turns'))

        #---------------------------------------------------------------------
        # And done...so broadcast a tick
        #---------------------------------------------------------------------
        pubsub.pubsub.add_message(pubsub.Publish(None,
                                                 pubsub.PubSubTypes.TICK))

        pubsub.pubsub.process_queue(self.game_map)

    def process_results_stack(self, entity, turn_results):
        #----------------------------------------------------------------------
        # Process the results stack
        #......................................................................
        # We are done processing inputs, and may have some results on
        # the entity turn stack. Process the stack by popping off the top
        # result from the queue. There are many different possible results,
        # so each is handled with a dedicated handler.
        #
        # Note: Handling a result may result in other results being added to
        # the stack, so we continually process the results stack until it is
        # empty.
        #----------------------------------------------------------------------
        while turn_results != []:
            # Sort the turn results stack by the priority order.
            turn_results = sorted(
                flatten_list_of_dictionaries(turn_results),
                key=lambda d: get_key_from_single_key_dict(d))

            result = turn_results.pop()
            result_type, result_data = unpack_single_key_dict(result)

            # Handle a simple message.
            if result_type == ResultTypes.MESSAGE:
                message = result_data
                pubsub.pubsub.add_message(
                    pubsub.Publish(None,
                                   pubsub.PubSubTypes.MESSAGE,
                                   message=message))

            if result_type == ResultTypes.FOV_RECOMPUTE:
                self.update_fov()

            if result_type == ResultTypes.END_TURN:
                self.game_state = GameStates.ENEMY_TURN

            if result_type == ResultTypes.EARN_XP:
                if result_data['xp'] > 0:
                    result_data['earner'].level.add_xp(result_data['xp'])
                    message = Message(
                        f"{result_data['earner'].name} gained {result_data['xp']} xp",
                        COLORS.get('success_text'),
                        target=result_data['earner'],
                        type=MessageType.EVENT)
                    turn_results.extend([{ResultTypes.MESSAGE: message}])

            # Handle death.
            if result_type == ResultTypes.DEAD_ENTITY:
                self.game_state = result_data['dead'].death.npc_death(
                    self.game_map)
                if entity == result_data['dead']:
                    turn_results = []
                if result_data['attacker'] and result_data['attacker'].ai:
                    result_data['attacker'].ai.remove_target(
                        target=result_data['dead'])
                result_data['dead'].deregister_turn_all()

            if result_type == ResultTypes.TARGET_ITEM_IN_INVENTORY:
                self.game_state = GameStates.INVENTORY_SELECT

            if result_type == ResultTypes.CANCEL_TARGET_ITEM_IN_INVENTORY:
                self.using_item = None
                self.game_state = GameStates.PLAYER_TURN

            # Add an item to the inventory, and remove it from the game map.
            if result_type == ResultTypes.ADD_ITEM_TO_INVENTORY:
                turn_results.extend(entity.inventory.add_item(result_data))
                self.game_state = GameStates.ENEMY_TURN
            # Remove consumed items from inventory
            if result_type == ResultTypes.DISCARD_ITEM:
                entity.inventory.remove_item(result_data)
                self.game_state = GameStates.ENEMY_TURN
                self.using_item = None

            # Remove dropped items from inventory and place on the map
            if result_type == ResultTypes.DROP_ITEM_FROM_INVENTORY:
                self.game_map.current_level.add_entity(result_data)
                message = Message(
                    f"{entity.name} dropped the {result_data.name}",
                    COLORS.get('success_text'),
                    target=entity,
                    type=MessageType.EVENT)
                turn_results.extend([{ResultTypes.MESSAGE: message}])
                self.game_state = GameStates.ENEMY_TURN

            if result_type == ResultTypes.EQUIP:
                equip_results = entity.equipment.toggle_equip(result_data)

                for equip_result in equip_results:
                    equipped = equip_result.get('equipped')
                    dequipped = equip_result.get('dequipped')

                    if equipped:
                        message = Message(
                            f"{entity.name} equipped the {equipped.name}",
                            target=entity,
                            type=MessageType.EVENT)

                    if dequipped:
                        message = Message(
                            f"{entity.name} dequipped the {dequipped.name}",
                            target=entity,
                            type=MessageType.EVENT)

                    turn_results.extend([{ResultTypes.MESSAGE: message}])

                self.game_state = GameStates.ENEMY_TURN
            if result_type == ResultTypes.QUEST_ONBOARDING:
                self.quest_request = result_data
                self.previous_game_state = self.game_state
                self.game_state = GameStates.QUEST_ONBOARDING

            if result_type == ResultTypes.QUEST_CANCELLED:
                pass

            if result_type == ResultTypes.SET_POSITION:
                npc, point = result_data
                npc.movement.place(point.x, point.y,
                                   self.game_map.current_level)
            # Handle a move towards action.  Move towards a target.
            if result_type == ResultTypes.MOVE_TOWARDS:
                npc, target_x, target_y = result_data
                npc.movement.attempt_move(Point(target_x, target_y),
                                          self.game_map)
            # Handle a move towards action.  Move towards a target following a particular path.
            if result_type == ResultTypes.MOVE_WITH_PATH:
                npc, path = result_data
                self.game_map.current_level.paths.append(path)
                npc.movement.attempt_move(Point(path[0][0], path[0][1]),
                                          self.game_map)
            # Handle a move random adjacent action.  Move to a random adjacent
            # square.
            if result_type == ResultTypes.MOVE_RANDOM_ADJACENT:
                npc = result_data
                npc.movement.move_to_random_adjacent(self.game_map)

            if result_type == ResultTypes.MOVE_FORCE:
                target, dx, dy, damage = result_data
                if damage > 0 and not target.movement.move(
                        dx, dy, self.game_map.current_level):
                    damage_results, total_damage = target.health.take_damage(
                        damage)
                    msg_text = '{0} crashes into the wall and takes {1} hit points damage.'
                    message = Message(
                        msg_text.format(target.name, str(total_damage)),
                        COLORS.get('damage_text'))
                    turn_results.extend([{ResultTypes.MESSAGE: message}])
                    turn_results.extend(damage_results)

            # Add a new entity to the game.
            if result_type == ResultTypes.ADD_ENTITY:
                self.game_map.current_level.add_entity(result_data)
            # Remove an entity from the game.
            if result_type == ResultTypes.REMOVE_ENTITY:
                self.game_map.current_level.remove_entity(result_data)
            if result_type == ResultTypes.TARGETING:
                self.previous_game_state = self.game_state
                self.game_state = GameStates.TARGETING

            if result_type == ResultTypes.COMMON_IDENT:
                identified_items[result_data] = True
Esempio n. 3
0
class Engine:
    def __init__(self, iteration, agent=None):
        self.agent = agent
        self.game_ended = False
        self.path = None
        self.game_result = None
        self.iteration = iteration
        self.root_console = None
        self.panel = None
        self.player = None
        self.entities = None
        self.game_map = None
        self.fov_recompute = False
        self.fov_map = None
        self.message_log = None
        self.game_state = None
        self.previous_game_state = None
        # self.power_difference = None
        # self.defence_difference = None
        self.current_enemy = None

    # Set the game up and play the game loop
    def play(self):
        libtcod.console_init_root(SCREEN_WIDTH, SCREEN_HEIGHT,
                                  'Moria - {0}'.format(self.iteration), False,
                                  libtcod.RENDERER_SDL2, "F", False)
        self.root_console = libtcodconsole.Console(SCREEN_WIDTH, SCREEN_HEIGHT)
        self.panel = libtcodconsole.Console(SCREEN_WIDTH, PANEL_HEIGHT)
        fighter_component = Fighter(hp=PLAYER_MAX_HEALTH,
                                    defence=PLAYER_DEFENCE,
                                    power=PLAYER_POWER)
        inventory_component = Inventory(26)
        equipment_component = Equipment()
        coin_pouch = CoinPouch(0)
        self.player = Entity(0,
                             0,
                             '@',
                             libtcod.white,
                             "Player",
                             blocks=True,
                             render_order=RenderOrder.ACTOR,
                             fighter=fighter_component,
                             inventory=inventory_component,
                             equipment=equipment_component,
                             coin_pouch=coin_pouch)
        self.entities = [self.player]

        self.game_map = GameMap()
        self.game_map.make_map(self.player, self.entities)
        self.fov_recompute = True
        self.fov_map = initialize_fov(self.game_map)

        self.message_log = MessageLog()

        self.game_state = GameStates.PLAYERS_TURN
        self.previous_game_state = self.game_state

        self.init_game()

        while not self.game_ended and self.player is not None:
            if self.agent is not None:
                print("Shop has", len(self.game_map.shop.items))
                self.agent.action = None
                self.run_agent(self.player, self.agent, self.fov_map)

            self.current_enemy = None

            self.run_game()

        return self.game_result

    # The agent decides their turn
    def run_agent(self, player, agent, fov_map):
        agent.take_turn(self, player, self.game_map, fov_map, self.game_state)

    # Initialise the game
    def init_game(self):
        libtcodconsole.default_fg = libtcod.white

        if self.fov_recompute:
            recompute_fov(self.fov_map, self.player.x, self.player.y)

        render_all(self.root_console, self.panel, self.entities, self.player,
                   self.game_map, self.fov_map, self.fov_recompute,
                   self.message_log, self.game_state)
        self.fov_recompute = False

        libtcod.console_flush()

    # Run the games turn
    def run_game(self):
        libtcodconsole.default_fg = libtcod.white

        if self.fov_recompute:
            recompute_fov(self.fov_map, self.player.x, self.player.y)

        render_all(self.root_console, self.panel, self.entities, self.player,
                   self.game_map, self.fov_map, self.fov_recompute,
                   self.message_log, self.game_state)
        self.fov_recompute = False

        libtcod.console_flush()
        if self.agent is None:
            action = {'pass': False}
            for event in libtcodevent.get():

                if event.type == "QUIT":
                    return
                if event.type == "KEYDOWN":
                    action = handle_keys(event.sym, self.game_state)
        else:
            if self.player.equipment.head is not None:
                head_name = self.player.equipment.head.name
            else:
                head_name = "Nothing"
            if self.player.equipment.body is not None:
                body_name = self.player.equipment.body.name
            else:
                body_name = "Nothing"
            if self.player.equipment.main_hand is not None:
                main_name = self.player.equipment.main_hand.name
            else:
                main_name = "Nothing"
            if self.player.equipment.off_hand is not None:
                off_name = self.player.equipment.off_hand.name
            else:
                off_name = "Nothing"

            print(
                "Head: {0}   Body: {1}   Main hand: {2}   Off hand: {3}   Coins: {4}"
                .format(head_name, body_name, main_name, off_name,
                        self.player.coin_pouch.get_amount()))
            action = self.agent.get_action()

        self.handle_action(action)

    # Deal with the players chosen action whether human or CPU
    def handle_action(self, action):
        # print(action)
        pass_action = action.get('pass')
        move = action.get('move')
        pickup = action.get('pickup')
        show_inventory = action.get('show_inventory')
        menu_index = action.get('menu_index')
        drop_inventory = action.get('drop_inventory')
        show_shop = action.get('show_shop')
        take_stairs = action.get('take_stairs')
        exit_action = action.get('exit')
        fullscreen = action.get('fullscreen')
        player_turn_results = []

        if pass_action:
            self.game_state = GameStates.ENEMY_TURN

        if move and self.game_state == GameStates.PLAYERS_TURN:
            dx, dy = move

            destination_x = self.player.x + dx
            destination_y = self.player.y + dy
            if not self.game_map.is_blocked(destination_x, destination_y):
                target = get_blocking_entities_at_location(
                    self.entities, destination_x, destination_y)

                if target:
                    attack_results = self.player.fighter.attack(target)
                    self.current_enemy = target
                    player_turn_results.extend(attack_results)
                else:
                    self.player.move(dx, dy)
                    self.fov_recompute = True

            self.game_state = GameStates.ENEMY_TURN

        elif pickup and self.game_state == GameStates.PLAYERS_TURN:
            for entity in self.entities:
                if entity.item and entity.x == self.player.x and entity.y == self.player.y:
                    pickup_results = self.player.inventory.add_item(entity)
                    player_turn_results.extend(pickup_results)

                    break
            else:
                self.message_log.add_message(
                    Message('There is nothing here to pick up.',
                            libtcod.yellow))

        if show_inventory:
            self.previous_game_state = self.game_state
            self.game_state = GameStates.SHOW_INVENTORY

        if drop_inventory:
            self.previous_game_state = self.game_state
            self.game_state = GameStates.DROP_INVENTORY

        if show_shop:
            if self.game_map.tiles[self.player.x][self.player.y].shop:
                self.previous_game_state = self.game_state
                self.game_state = GameStates.SHOW_SHOP

        if take_stairs:
            for entity in self.entities:
                if entity.stairs and entity.x == self.player.x and entity.y == self.player.y:
                    if entity.char == DOWN_STAIRS_CHAR:
                        self.entities = self.game_map.next_floor(
                            self.player, self.message_log)
                    else:
                        self.entities = self.game_map.previous_floor(
                            self.player, self.message_log)

                    self.fov_map = initialize_fov(self.game_map)
                    self.fov_recompute = True
                    self.root_console.clear()

                    break
            else:
                self.message_log.add_message(
                    Message('There are no stairs here.', libtcod.yellow))

        if menu_index is not None and self.game_state == GameStates.SHOW_SHOP and menu_index < len(
                GameMap.shop.items):
            item = GameMap.shop.items[menu_index]
            player_turn_results.extend(GameMap.shop.buy(item, self.player))
            # player_turn_results.extend(self.player.inventory.use(item, entities=self.entities, fov_map=self.fov_map))

        if menu_index is not None and self.previous_game_state != GameStates.PLAYER_DEAD and menu_index < len(
                self.player.inventory.items):
            item = self.player.inventory.items[menu_index]
            if self.game_state == GameStates.SHOW_INVENTORY:
                player_turn_results.extend(
                    self.player.inventory.use(item,
                                              entities=self.entities,
                                              fov_map=self.fov_map))
            elif self.game_state == GameStates.DROP_INVENTORY:
                player_turn_results.extend(
                    self.player.inventory.drop_item(item))

        if self.game_state == GameStates.TARGETING:
            # TODO Make a menu of the visible opponents to attack
            pass

        if exit_action:
            if self.game_state in (GameStates.SHOW_INVENTORY,
                                   GameStates.DROP_INVENTORY,
                                   GameStates.SHOW_SHOP):
                self.game_state = self.previous_game_state
            else:
                return True

        if fullscreen:
            libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen())

        for player_turn_result in player_turn_results:
            message = player_turn_result.get('message')
            dead_entity = player_turn_result.get('dead')
            item_added = player_turn_result.get('item_added')
            item_consumed = player_turn_result.get('consumed')
            equip = player_turn_result.get('equip')
            item_dropped = player_turn_result.get('item_dropped')
            targeting = player_turn_result.get('targeting')

            if message:
                self.message_log.add_message(message)

            if dead_entity:
                if dead_entity == self.player:
                    message, self.game_state = kill_player(
                        dead_entity, self.game_map)
                    self.game_result = 0
                else:
                    # self.power_difference = self.player.fighter.power - dead_entity.fighter.power
                    # self.defence_difference = self.player.fighter.defence - dead_entity.fighter.defence
                    # self.current_enemy = dead_entity
                    message, self.game_ended = kill_monster(
                        dead_entity, self.player)
                    if self.game_ended:
                        self.game_result = 1

                self.message_log.add_message(message)

            if item_added:
                if item_added in self.entities:
                    self.entities.remove(item_added)

                self.game_state = GameStates.ENEMY_TURN

            if item_consumed:
                self.game_state = GameStates.ENEMY_TURN

            if equip:
                equip_results = self.player.equipment.toggle_equip(equip)
                for result in equip_results:
                    equipped = result.get('equipped')
                    dequipped = result.get('dequipped')

                    if equipped:
                        self.message_log.add_message(
                            Message('You equipped the {0}'.format(
                                equipped.name)))
                    if dequipped:
                        self.message_log.add_message(
                            Message('You dequipped the {0}'.format(
                                dequipped.name)))

                self.game_state = GameStates.ENEMY_TURN

            if item_dropped:
                self.entities.append(item_dropped)

                self.game_state = GameStates.ENEMY_TURN

            if targeting:
                self.previous_game_state = GameStates.PLAYERS_TURN
                self.game_state = GameStates.TARGETING

                targeting_item = targeting

                self.message_log.add_message(
                    targeting_item.item.targeting_message)

        if self.game_state == GameStates.ENEMY_TURN:
            for entity in self.entities:
                if entity.ai:
                    enemy_turn_results = entity.ai.take_turn(
                        self.player, self.fov_map, self.game_map,
                        self.entities)

                    for enemy_turn_result in enemy_turn_results:
                        message = enemy_turn_result.get('message')
                        dead_entity = enemy_turn_result.get('dead')

                        if message:
                            self.message_log.add_message(message)

                        if dead_entity:
                            if dead_entity == self.player:
                                message, self.game_state = kill_player(
                                    dead_entity, self.game_map)
                            else:
                                message, self.game_ended = kill_monster(
                                    dead_entity, self.player)
                                if self.game_ended:
                                    self.game_result = 1

                            self.message_log.add_message(message)

                            if self.game_state == GameStates.PLAYER_DEAD:
                                self.game_ended = True
                                self.game_result = 0
                                break

                    if self.game_state == GameStates.PLAYER_DEAD:
                        self.game_ended = True
                        break
            else:
                self.game_state = GameStates.PLAYERS_TURN

    def end_game(self):
        self.game_ended = True

    def get_entities(self):
        return self.entities
Esempio n. 4
0
def main():
    constants = get_constants()

    libtcod.console_set_custom_font(
        'tiledfont.png',
        libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD, 32, 10)

    libtcod.console_init_root(constants['screen_width'],
                              constants['screen_height'],
                              constants['window_title'], False)

    # load the custom font rows
    load_customfont()

    # assign the custom font rows numbers to text (for easier calling when defining entities with custom tiles)
    # defining tiles (rather than numbers)
    wall_tile = 257  #note: see render_functions for where the wall and floor tiles are defined, these are not used.
    floor_tile = 256
    player_tile = 258
    quiz_tile = 259
    exam_tile = 260
    healingpotion_tile = 261
    sword_tile = 263
    shield_tile = 264
    stairsdown_tile = 265
    dagger_tile = 266

    fighter_component = Fighter(hp=30, defense=1, power=3, name='Guardian')
    inventory_component = Inventory(8)
    level_component = Level()
    equipment_component = Equipment()
    player = Entity(0,
                    0,
                    player_tile,
                    libtcod.white,
                    'Guardian',
                    blocks=True,
                    render_order=RenderOrder.ACTOR,
                    fighter=fighter_component,
                    inventory=inventory_component,
                    level=level_component,
                    equipment=equipment_component)
    entities = [player]

    equippable_component = Equippable(EquipmentSlots.MAIN_HAND, power_bonus=1)
    dagger = Entity(0,
                    0,
                    dagger_tile,
                    libtcod.white,
                    'Elemental Absorber + 1',
                    equippable=equippable_component)
    player.inventory.add_item(dagger)
    player.equipment.toggle_equip(dagger)

    equippable_component = Equippable(EquipmentSlots.OFF_HAND,
                                      max_hp_bonus=5,
                                      defense_bonus=0)
    shield = Entity(0,
                    0,
                    shield_tile,
                    libtcod.white,
                    'Elemental Deflector + 0',
                    equippable=equippable_component)
    player.inventory.add_item(shield)
    player.equipment.toggle_equip(shield)
    player.fighter.hp += shield.equippable.max_hp_bonus

    con = libtcod.console.Console(constants['screen_width'],
                                  constants['screen_height'])
    panel = libtcod.console.Console(constants['screen_width'],
                                    constants['panel_height'])

    game_map = GameMap(constants['map_width'], constants['map_height'])
    game_map.make_map(constants['max_rooms'], constants['room_min_size'],
                      constants['room_max_size'], constants['map_width'],
                      constants['map_height'], player, entities)

    fov_recompute = True

    fov_map = initialize_fov(game_map)

    message_log = MessageLog(constants['message_x'],
                             constants['message_width'],
                             constants['message_height'])

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

    game_state = GameStates.PLAYERS_TURN
    previous_game_state = game_state

    # Welcome the player
    message_log.add_message(
        Message(
            'Welcome, Guardian, to the elemental kingdom of Empyria! Aquire all the elements...or die trying! Fearsome foes await in the deepest depths of Empyria, where many Guardians have disappeared...',
            libtcod.white))

    while not libtcod.console_is_window_closed():
        libtcod.sys_check_for_event(
            libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, key, mouse)

        if fov_recompute:
            recompute_fov(fov_map, player.x, player.y, constants['fov_radius'],
                          constants['fov_light_walls'],
                          constants['fov_algorithm'])

        render_all(con, panel, entities, player, game_map, fov_map,
                   fov_recompute, message_log, constants['screen_width'],
                   constants['screen_height'], constants['bar_width'],
                   constants['panel_height'], constants['panel_y'], mouse,
                   constants['colors'], game_state)

        fov_recompute = False

        libtcod.console_flush()

        clear_all(con, entities)

        action = handle_keys(key, game_state)

        move = action.get('move')
        pickup = action.get('pickup')
        show_inventory = action.get('show_inventory')
        drop_inventory = action.get('drop_inventory')
        inventory_index = action.get('inventory_index')
        take_stairs = action.get('take_stairs')
        level_up = action.get('level_up')
        show_character_screen = action.get('show_character_screen')
        wait = action.get('wait')
        exit = action.get('exit')
        fullscreen = action.get('fullscreen')

        player_turn_results = []

        if move and game_state == GameStates.PLAYERS_TURN:
            dx, dy = move
            destination_x = player.x + dx
            destination_y = player.y + dy

            if not game_map.is_blocked(destination_x, destination_y):
                target = get_blocking_entities_at_location(
                    entities, destination_x, destination_y)

                if target:
                    attack_results = player.fighter.attack(target)
                    player_turn_results.extend(attack_results)
                    fov_recompute = True
                else:
                    player.move(dx, dy)

                    fov_recompute = True

                game_state = GameStates.ENEMY_TURN

        elif pickup and game_state == GameStates.PLAYERS_TURN:
            for entity in entities:
                if entity.item and entity.x == player.x and entity.y == player.y:
                    pickup_results = player.inventory.add_item(entity)
                    player_turn_results.extend(pickup_results)

                    break

            else:
                message_log.add_message(
                    Message('There is nothing here to pick up.',
                            libtcod.yellow))

        if show_inventory:
            previous_game_state = game_state
            game_state = GameStates.SHOW_INVENTORY

        if drop_inventory:
            previous_game_state = game_state
            game_state = GameStates.DROP_INVENTORY

        if inventory_index is not None and previous_game_state != GameStates.PLAYER_DEAD and inventory_index < len(
                player.inventory.items):
            item = player.inventory.items[inventory_index]

            if game_state == GameStates.SHOW_INVENTORY:
                player_turn_results.extend(player.inventory.use(item))
            elif game_state == GameStates.DROP_INVENTORY:
                player_turn_results.extend(player.inventory.drop_item(item))

        if take_stairs and game_state == GameStates.PLAYERS_TURN:
            for entity in entities:
                if entity.stairs and entity.x == player.x and entity.y == player.y:
                    entities = game_map.next_floor(player, message_log,
                                                   constants)
                    fov_map = initialize_fov(game_map)
                    fov_recompute = True
                    con.clear()

                    break
            else:
                message_log.add_message(
                    Message(
                        'You search around, but the stairs are nowhere to be seen.',
                        libtcod.yellow))

        if level_up:
            if level_up == 'hp':
                player.fighter.base_max_hp += 30
                player.fighter.hp += 30
            elif level_up == 'str':
                player.fighter.base_power += 5
            elif level_up == 'def':
                player.fighter.base_defense += 1

            game_state = previous_game_state

        if show_character_screen:
            previous_game_state = game_state
            game_state = GameStates.CHARACTER_SCREEN

        if wait == True:
            game_state = GameStates.ENEMY_TURN
            fov_recompute = True

        if exit:
            if game_state in (GameStates.SHOW_INVENTORY,
                              GameStates.DROP_INVENTORY,
                              GameStates.CHARACTER_SCREEN):
                game_state = previous_game_state
            else:
                return True

        if fullscreen:
            libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen())

        for player_turn_result in player_turn_results:
            message = player_turn_result.get('message')
            dead_entity = player_turn_result.get('dead')
            item_added = player_turn_result.get('item_added')
            item_consumed = player_turn_result.get('consumed')
            item_pick_gold = player_turn_result.get('got_gold')
            item_dropped = player_turn_result.get('item_dropped')
            equip = player_turn_result.get('equip')

            if message:
                message_log.add_message(message)

            if dead_entity:
                if dead_entity == player:
                    message, game_state = kill_player(dead_entity)
                else:
                    # Using xp to increase player's stats, "absorbing" the element
                    if dead_entity.fighter.name == 'Corrupted Guardian':
                        previous_game_state = game_state
                        game_state = GameStates.WIN
                        message_log.add_message(
                            Message(
                                'You have brought peace to Empyria and vanquished the Corrputed Guardian! Congratulations!',
                                libtcod.yellow))

                    if dead_entity.fighter.element == 'blank':
                        player.fighter.blank_element += dead_entity.fighter.xp
                        message = kill_monster(dead_entity)
                        if player.fighter.blank_element > player.fighter.max_blank_element:
                            player.fighter.blank_element = player.fighter.max_blank_element
                    elif dead_entity.fighter.element == 'fire':
                        player.fighter.fire_element += dead_entity.fighter.xp
                        message = kill_monster(dead_entity)
                        if player.fighter.fire_element > player.fighter.max_fire_element:
                            player.fighter.fire_element = player.fighter.max_fire_element
                    elif dead_entity.fighter.element == 'air':
                        player.fighter.air_element += dead_entity.fighter.xp
                        message = kill_monster(dead_entity)
                        if player.fighter.air_element > player.fighter.max_air_element:
                            player.fighter.air_element = player.fighter.max_air_element
                    elif dead_entity.fighter.element == 'ice':
                        player.fighter.ice_element += dead_entity.fighter.xp
                        message = kill_monster(dead_entity)
                        if player.fighter.ice_element > player.fighter.max_ice_element:
                            player.fighter.ice_element = player.fighter.max_ice_element
                    elif dead_entity.fighter.element == 'lightning':
                        player.fighter.lightning_element += dead_entity.fighter.xp
                        message = kill_monster(dead_entity)
                        if player.fighter.lightning_element > player.fighter.max_lightning_element:
                            player.fighter.lightning_element = player.fighter.max_lightning_element
                    elif dead_entity.fighter.element == 'earth':
                        player.fighter.earth_element += dead_entity.fighter.xp
                        message = kill_monster(dead_entity)
                        if player.fighter.earth_element > player.fighter.max_earth_element:
                            player.fighter.earth_element = player.fighter.max_earth_element
                    elif dead_entity.fighter.element == 'psychic':
                        player.fighter.psychic_element += dead_entity.fighter.xp
                        message = kill_monster(dead_entity)
                        if player.fighter.psychic_element > player.fighter.max_psychic_element:
                            player.fighter.psychic_element = player.fighter.max_psychic_element
                    elif dead_entity.fighter.element == 'water':
                        player.fighter.water_element += dead_entity.fighter.xp
                        message = kill_monster(dead_entity)
                        if player.fighter.water_element > player.fighter.max_water_element:
                            player.fighter.water_element = player.fighter.max_water_element

                message_log.add_message(message)

            if item_added:
                entities.remove(item_added)

                game_state = GameStates.ENEMY_TURN

            if item_consumed:
                game_state = GameStates.ENEMY_TURN

            # if item_pick_gold:
            # entities.remove(item_gold)

            # game_state = GameStates.ENEMY_TURN

            if item_dropped:
                entities.append(item_dropped)

                game_state = GameStates.ENEMY_TURN

            if equip:
                equip_results = player.equipment.toggle_equip(equip)

                for equip_result in equip_results:
                    equipped = equip_result.get('equipped')
                    dequipped = equip_result.get('dequipped')

                    if equipped:
                        message_log.add_message(
                            Message('You equipped the {0}.'.format(
                                equipped.name)))

                    if dequipped:
                        message_log.add_message(
                            Message('You dequipped the {0}.'.format(
                                dequipped.name)))

                game_state = GameStates.ENEMY_TURN

            xp = player_turn_result.get('xp')

            if xp:
                # if not GameStates.WIN:
                leveled_up = player.level.add_xp(xp)
                message_log.add_message(
                    Message('You gained {0} exp!'.format(xp)))
                if (player.fighter.blank_element
                        == player.fighter.max_blank_element
                        and player.fighter.fire_element
                        == player.fighter.max_fire_element
                        and player.fighter.air_element
                        == player.fighter.max_air_element
                        and player.fighter.ice_element
                        == player.fighter.max_ice_element
                        and player.fighter.lightning_element
                        == player.fighter.max_lightning_element
                        and player.fighter.earth_element
                        == player.fighter.max_earth_element
                        and player.fighter.psychic_element
                        == player.fighter.max_psychic_element
                        and player.fighter.water_element
                        == player.fighter.max_water_element):
                    message_log.add_message(
                        Message(
                            'You have collected all of the elements and are now a true Elemental Guardian! You won! Or did you...?',
                            libtcod.yellow))

                if leveled_up:

                    # if player.level.current_level == 5:
                    # previous_game_state = game_state
                    # game_state = GameStates.WIN
                    # message_log.add_message(Message('You have collected 180 exp! You won!', libtcod.yellow))
                    # else:
                    message_log.add_message(
                        Message(
                            'Level up! You are now level {0}'.format(
                                player.level.current_level) + '!',
                            libtcod.yellow))
                    previous_game_state = game_state
                    game_state = GameStates.LEVEL_UP

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

                    for enemy_turn_result in enemy_turn_results:
                        message = enemy_turn_result.get('message')
                        dead_entity = enemy_turn_result.get('dead')

                        if message:
                            message_log.add_message(message)

                        if dead_entity:
                            if dead_entity == player:
                                message, game_state = kill_player(dead_entity)
                            else:
                                message = kill_monster(dead_entity)

                            message_log.add_message(message)

                            if game_state == GameStates.PLAYER_DEAD:
                                break

                    if game_state == GameStates.PLAYER_DEAD:
                        break

            else:
                game_state = GameStates.PLAYERS_TURN

            fov_recompute = True
Esempio n. 5
0
def main():
    constants = get_constants()

    libtcod.console_set_custom_font(
        'tiledfont.png',
        libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD, 32, 10)

    libtcod.console_init_root(constants['screen_width'],
                              constants['screen_height'],
                              constants['window_title'], False)

    # load the custom font rows
    load_customfont()

    # assign the custom font rows numbers to text (for easier calling when defining entities with custom tiles)
    # defining tiles (rather than numbers)
    wall_tile = 256
    floor_tile = 257
    player_tile = 258
    quiz_tile = 259
    exam_tile = 260
    healingpotion_tile = 261
    sword_tile = 263
    shield_tile = 264
    stairsdown_tile = 265
    dagger_tile = 266

    fighter_component = Fighter(hp=100, defense=1, power=3, name='Student')
    inventory_component = Inventory(26)
    level_component = Level()
    equipment_component = Equipment()
    player = Entity(0,
                    0,
                    player_tile,
                    libtcod.white,
                    'Student',
                    blocks=True,
                    render_order=RenderOrder.ACTOR,
                    fighter=fighter_component,
                    inventory=inventory_component,
                    level=level_component,
                    equipment=equipment_component)
    entities = [player]

    equippable_component = Equippable(EquipmentSlots.MAIN_HAND, power_bonus=1)
    dagger = Entity(0,
                    0,
                    dagger_tile,
                    libtcod.white,
                    'Pencil',
                    equippable=equippable_component)
    player.inventory.add_item(dagger)
    player.equipment.toggle_equip(dagger)

    con = libtcod.console.Console(constants['screen_width'],
                                  constants['screen_height'])
    panel = libtcod.console.Console(constants['screen_width'],
                                    constants['panel_height'])

    game_map = GameMap(constants['map_width'], constants['map_height'])
    game_map.make_map(constants['max_rooms'], constants['room_min_size'],
                      constants['room_max_size'], constants['map_width'],
                      constants['map_height'], player, entities)

    fov_recompute = True

    fov_map = initialize_fov(game_map)

    message_log = MessageLog(constants['message_x'],
                             constants['message_width'],
                             constants['message_height'])

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

    game_state = GameStates.PLAYERS_TURN
    previous_game_state = game_state

    # Welcome the player
    message_log.add_message(
        Message(
            'Welcome, Student, to the College of Doom! Aquire 180 credits to graduate...or die trying!',
            libtcod.white))

    while not libtcod.console_is_window_closed():
        libtcod.sys_check_for_event(
            libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, key, mouse)

        if fov_recompute:
            recompute_fov(fov_map, player.x, player.y, constants['fov_radius'],
                          constants['fov_light_walls'],
                          constants['fov_algorithm'])

        render_all(con, panel, entities, player, game_map, fov_map,
                   fov_recompute, message_log, constants['screen_width'],
                   constants['screen_height'], constants['bar_width'],
                   constants['panel_height'], constants['panel_y'], mouse,
                   constants['colors'], game_state)

        fov_recompute = False

        libtcod.console_flush()

        clear_all(con, entities)

        action = handle_keys(key, game_state)

        move = action.get('move')
        pickup = action.get('pickup')
        show_inventory = action.get('show_inventory')
        drop_inventory = action.get('drop_inventory')
        inventory_index = action.get('inventory_index')
        take_stairs = action.get('take_stairs')
        level_up = action.get('level_up')
        show_character_screen = action.get('show_character_screen')
        wait = action.get('wait')
        exit = action.get('exit')
        fullscreen = action.get('fullscreen')

        player_turn_results = []

        if move and game_state == GameStates.PLAYERS_TURN:
            dx, dy = move
            destination_x = player.x + dx
            destination_y = player.y + dy

            if not game_map.is_blocked(destination_x, destination_y):
                target = get_blocking_entities_at_location(
                    entities, destination_x, destination_y)

                if target:
                    attack_results = player.fighter.attack(target)
                    player_turn_results.extend(attack_results)
                    fov_recompute = True
                else:
                    player.move(dx, dy)

                    fov_recompute = True

                game_state = GameStates.ENEMY_TURN

        elif pickup and game_state == GameStates.PLAYERS_TURN:
            for entity in entities:
                if entity.item and entity.x == player.x and entity.y == player.y:
                    pickup_results = player.inventory.add_item(entity)
                    player_turn_results.extend(pickup_results)

                    break

            else:
                message_log.add_message(
                    Message('There is nothing here to pick up.',
                            libtcod.yellow))

        if show_inventory:
            previous_game_state = game_state
            game_state = GameStates.SHOW_INVENTORY

        if drop_inventory:
            previous_game_state = game_state
            game_state = GameStates.DROP_INVENTORY

        if inventory_index is not None and previous_game_state != GameStates.PLAYER_DEAD and inventory_index < len(
                player.inventory.items):
            item = player.inventory.items[inventory_index]

            if game_state == GameStates.SHOW_INVENTORY:
                player_turn_results.extend(player.inventory.use(item))
            elif game_state == GameStates.DROP_INVENTORY:
                player_turn_results.extend(player.inventory.drop_item(item))

        if take_stairs and game_state == GameStates.PLAYERS_TURN:
            for entity in entities:
                if entity.stairs and entity.x == player.x and entity.y == player.y:
                    entities = game_map.next_floor(player, message_log,
                                                   constants)
                    fov_map = initialize_fov(game_map)
                    fov_recompute = True
                    con.clear()

                    break
            else:
                message_log.add_message(
                    Message(
                        'You search around, but Summer break is nowhere to be seen.',
                        libtcod.yellow))

        if level_up:
            if level_up == 'hp':
                player.fighter.base_max_hp += 20
                player.fighter.hp += 20
            elif level_up == 'str':
                player.fighter.base_power += 1
            elif level_up == 'def':
                player.fighter.base_defense += 1

            game_state = previous_game_state

        if show_character_screen:
            previous_game_state = game_state
            game_state = GameStates.CHARACTER_SCREEN

        if wait == True:
            game_state = GameStates.ENEMY_TURN
            fov_recompute = True

        if exit:
            if game_state in (GameStates.SHOW_INVENTORY,
                              GameStates.DROP_INVENTORY,
                              GameStates.CHARACTER_SCREEN):
                game_state = previous_game_state
            else:
                return True

        if fullscreen:
            libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen())

        for player_turn_result in player_turn_results:
            message = player_turn_result.get('message')
            dead_entity = player_turn_result.get('dead')
            item_added = player_turn_result.get('item_added')
            item_consumed = player_turn_result.get('consumed')
            item_dropped = player_turn_result.get('item_dropped')
            equip = player_turn_result.get('equip')

            if message:
                message_log.add_message(message)

            if dead_entity:
                if dead_entity == player:
                    message, game_state = kill_player(dead_entity)
                else:
                    message = kill_monster(dead_entity)

                message_log.add_message(message)

            if item_added:
                entities.remove(item_added)

                game_state = GameStates.ENEMY_TURN

            if item_consumed:
                game_state = GameStates.ENEMY_TURN

            if item_dropped:
                entities.append(item_dropped)

                game_state = GameStates.ENEMY_TURN

            if equip:
                equip_results = player.equipment.toggle_equip(equip)

                for equip_result in equip_results:
                    equipped = equip_result.get('equipped')
                    dequipped = equip_result.get('dequipped')

                    if equipped:
                        message_log.add_message(
                            Message('You equipped the {0}.'.format(
                                equipped.name)))

                    if dequipped:
                        message_log.add_message(
                            Message('You dequipped the {0}.'.format(
                                dequipped.name)))

                game_state = GameStates.ENEMY_TURN

            xp = player_turn_result.get('xp')

            if xp:
                leveled_up = player.level.add_xp(xp)
                message_log.add_message(
                    Message(
                        'You passed the Final Exam and gained {0} credits!'.
                        format(xp)))

                if leveled_up:
                    if player.level.current_level == 5:
                        previous_game_state = game_state
                        game_state = GameStates.WIN
                        message_log.add_message(
                            Message(
                                'You have completed 180 credits at the College of Doom! You graduated!',
                                libtcod.yellow))
                    else:
                        message_log.add_message(
                            Message(
                                'You made it through another year of school! You move on to school year {0}'
                                .format(player.level.current_level) + '!',
                                libtcod.yellow))
                        previous_game_state = game_state
                        game_state = GameStates.LEVEL_UP

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

                    for enemy_turn_result in enemy_turn_results:
                        message = enemy_turn_result.get('message')
                        dead_entity = enemy_turn_result.get('dead')

                        if message:
                            message_log.add_message(message)

                        if dead_entity:
                            if dead_entity == player:
                                message, game_state = kill_player(dead_entity)
                            else:
                                message = kill_monster(dead_entity)

                            message_log.add_message(message)

                            if game_state == GameStates.PLAYER_DEAD:
                                break

                    if game_state == GameStates.PLAYER_DEAD:
                        break

            else:
                game_state = GameStates.PLAYERS_TURN

            fov_recompute = True