コード例 #1
0
ファイル: map_generator.py プロジェクト: oeN/forest
 def spawn_monsters(self, gamemap: GameMap) -> None:
     monsters = randint(0, 3)
     for _ in range(monsters):
         x, y = self.random_coordinates()
         if gamemap.is_blocked(x, y):
             continue
         # TODO: add code for random enemy type
         spawn_entity(entities_db.monsters['orc'](), gamemap[x, y])
コード例 #2
0
ファイル: map_generator.py プロジェクト: oeN/forest
 def spawn_items(self, gamemap: GameMap) -> None:
     items = randint(0, 2)
     for _ in range(items):
         x, y = self.random_coordinates()
         if gamemap.is_blocked(x, y):
             continue
         # TODO: add code for random enemy type
         spawn_entity(entities_db.items['healing_potiont'](), gamemap[x, y])
コード例 #3
0
    def move_towards(self, game_map: GameMap, entities, x_target: int, y_target: int):
        dx = x_target - self.x
        dy = y_target - self.y
        distance = math.sqrt(dx ** 2 + dy ** 2)

        # Normalize
        dx = int(round(dx / distance))
        dy = int(round(dy / distance))

        if not (game_map.is_blocked(self.x + dx, self.y + dy) or
                    Entity.get_blocking_entity_at_location(entities, self.x + dx, self.y + dy)):
            self.move(dx, dy)
コード例 #4
0
 def push(self, game_map: GameMap, x_delta: int, y_delta: int):
     """
     Attempts to push this in a direction.
     Returns true if push was successful.
     """
     if self.pushable:
         x_new = self.x + x_delta
         y_new = self.y + y_delta
         if not game_map.is_blocked(x_new, y_new):
             self.move(x_delta, y_delta)
             return True
     return False
コード例 #5
0
def play_game(con, player, entities, animator: Animator, turn_count: int,
              game_map: GameMap, message_log, game_state, panel, constants):
    target_x, target_y = player.x, player.y
    targeting_item = None
    target_entity = None

    while True:
        fov_algorithm = 2
        fov_light_walls = True
        fov_radius = 20
        fov_recompute = True
        fov_map = initialize_fov(game_map)

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

        render_all(con, panel, entities, animator, player, game_map, fov_map,
                   fov_recompute, message_log, constants['screen_width'],
                   constants['screen_height'], constants['bar_width'],
                   constants['panel_height'], constants['panel_y'], game_state,
                   target_x, target_y, target_entity, turn_count)

        tcod.console_flush()

        clear_all(con, entities)

        player_turn_results = []

        animator.advance_frame()

        # Handle Game State
        if game_state == GameStates.ENEMY_TURN:
            turn_count += 1

            # Generate path map with all static tiles (ignore entities for now)
            path_map = generate_path_map(game_map,
                                         entities=None,
                                         player=player)

            for entity in entities:
                if entity.ai and entity.ai != Player:
                    recompute_walkable(fov_map, game_map, entities, entity)
                    entity_turn_results = entity.ai.take_turn(
                        player, fov_map, game_map, entities, path_map)

                    for entity_turn_result in entity_turn_results:
                        message = entity_turn_result.get('message')
                        dead_entity = entity_turn_result.get('dead')

                        if message:
                            message_log.add_message(message)

                        if dead_entity:
                            if dead_entity == player:
                                message, game_state = kill_player(player)
                            else:
                                message = kill_entity(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.PLAYER_TURN

        # Handle Events
        for event in tcod.event.wait():
            if event.type == "QUIT":
                save_game(player, entities, animator, turn_count, game_map,
                          message_log, game_state)
                raise SystemExit()

            if event.type == "KEYDOWN":
                action: [Action, None] = handle_keys(event.sym, game_state)

                if action is None:
                    continue

                action_type: ActionType = action.action_type

                if action_type == ActionType.EXECUTE:
                    if game_state == GameStates.TARGETING:
                        item_use_results = player.body.use_selected_appendage(
                            entities=entities,
                            fov_map=fov_map,
                            game_map=game_map,
                            target_x=target_x,
                            target_y=target_y)
                        player_turn_results.extend(item_use_results)
                        game_state = GameStates.ENEMY_TURN
                    elif game_state == GameStates.LOOKING:
                        look_results = []

                        looked_at_entities = get_entities_at_location(
                            entities, target_x, target_y)
                        if tcod.map_is_in_fov(fov_map, target_x, target_y):
                            if looked_at_entities:
                                for entity in looked_at_entities:
                                    look_results.extend(
                                        entity.get_description())
                                    target_entity = entity
                            else:
                                if game_map.tiles[target_x][target_y].blocked:
                                    look_results.append({
                                        'message':
                                        Message("You stare at the wall.")
                                    })
                                else:
                                    look_results.append({
                                        'message':
                                        Message("You stare into empty space.")
                                    })
                        else:
                            look_results.append({
                                'message':
                                Message("You can't see that far.")
                            })

                        game_state = GameStates.PLAYER_TURN
                        player_turn_results.extend(look_results)

                if action_type == ActionType.MOVEMENT:
                    dx: int = action.kwargs.get("dx", 0)
                    dy: int = action.kwargs.get("dy", 0)

                    # Player Movement
                    if game_state == GameStates.PLAYER_TURN:
                        destination_x = player.x + dx
                        destination_y = player.y + dy

                        tile_results = game_map.tiles[destination_x][
                            destination_y].overlap_entity(player)
                        player_turn_results.extend(tile_results)

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

                            if target_appendage:
                                if target_appendage.body:
                                    player_fighter = player.body.selected_appendage.fighter

                                    if player_fighter:
                                        target_entity = target_appendage
                                        game_state = GameStates.TARGET_APPENDAGE
                                    else:
                                        player_turn_results.append({
                                            'message':
                                            Message(
                                                "You cannot attack with your {0}."
                                                .format(
                                                    player.body.
                                                    selected_appendage.name),
                                                tcod.yellow)
                                        })
                                elif target_appendage.structure:
                                    structure_interact_results = target_appendage.structure.interact(
                                        player)
                                    player_turn_results.extend(
                                        structure_interact_results)
                            else:
                                player.move(dx, dy)
                        else:
                            player_turn_results.append({
                                'message':
                                Message("You slam yourself into the wall!",
                                        tcod.orange)
                            })

                        if game_state != GameStates.TARGET_APPENDAGE:
                            game_state = GameStates.ENEMY_TURN

                    # Targeting
                    elif game_state in (GameStates.TARGETING,
                                        GameStates.LOOKING):
                        new_x = target_x + dx
                        new_y = target_y + dy
                        if player.distance(new_x, new_y) < targeting_radius:
                            target_x = new_x
                            target_y = new_y

                elif action_type == ActionType.GRAB:
                    for entity in entities:
                        if entity.item and entity.x == player.x and entity.y == player.y:
                            pickup_result = player.body.grab_entity(entity)
                            player_turn_results.extend(pickup_result)
                            break
                    else:
                        player_turn_results.append({
                            'message':
                            Message('You grab at the air', tcod.yellow)
                        })

                    game_state = GameStates.ENEMY_TURN

                elif action_type == ActionType.LOOK:
                    game_state = GameStates.LOOKING
                    target_x, target_y = player.x, player.y
                    targeting_radius = 100

                elif action_type == ActionType.WAIT:
                    player_turn_results.append({
                        'message':
                        Message('You stare blankly into space', tcod.yellow)
                    })
                    game_state = GameStates.ENEMY_TURN

                elif action_type == ActionType.CHOOSE_OPTION:
                    option_index = action.kwargs.get("option_index", None)

                    if game_state == GameStates.SWAP_APPENDAGE:
                        if option_index < len(player.body.appendages):
                            item = player.body.appendages[option_index]
                            swap_results = player.body.select_appendage(item)
                            player_turn_results.extend(swap_results)
                            game_state = GameStates.PLAYER_TURN

                    elif game_state == GameStates.TARGET_APPENDAGE:
                        if option_index < len(target_entity.body.appendages):
                            target_appendage = target_entity.body.appendages[
                                option_index]
                            attack_results = player.body.selected_appendage.fighter.attack_appendage(
                                target_appendage)
                            player_turn_results.extend(attack_results)
                            game_state = GameStates.ENEMY_TURN

                elif action_type == ActionType.INTERACT:
                    for entity in entities:
                        if entity.structure and entity.x == player.x and entity.y == player.y:
                            interact_results = entity.structure.interact(
                                player)
                            player_turn_results.extend(interact_results)
                            break
                    else:
                        if game_state == GameStates.PLAYER_TURN:
                            activate_item_results = player.body.use_selected_appendage(
                                fov_map=fov_map,
                                game_map=game_map,
                                entities=entities)
                            if activate_item_results:
                                player_turn_results.extend(
                                    activate_item_results)
                                game_state = GameStates.ENEMY_TURN

                elif action_type == ActionType.DROP_INVENTORY_ITEM:
                    grabber = player.body.selected_appendage.grabber
                    if grabber:
                        player_turn_results.extend(grabber.drop())
                    # game_state = GameStates.DROP_INVENTORY

                elif action_type == ActionType.SWAP_APPENDAGE:
                    game_state = GameStates.SWAP_APPENDAGE

                elif action_type == ActionType.ESCAPE:
                    if game_state == GameStates.TARGETING:
                        game_state = GameStates.PLAYER_TURN
                    elif game_state == GameStates.PLAYER_TURN:
                        save_game(player, entities, animator, turn_count,
                                  game_map, message_log, game_state)
                        main()
                elif action_type == ActionType.RESTART:
                    main()

        # Process player turn results
        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')
            next_floor = player_turn_result.get('next_floor')

            if message:
                message_log.add_message(message)

            if dead_entity:
                if dead_entity == player:
                    message, game_state = kill_player(player)
                else:
                    message_log.add_message(kill_entity(dead_entity))

            if item_added:
                entities.remove(item_added)
                game_state = GameStates.ENEMY_TURN

            if item_dropped:
                entities.append(item_dropped)

                game_state = GameStates.ENEMY_TURN

            if targeting:
                game_state = GameStates.TARGETING

                targeting_item = targeting
                targeting_radius = targeting.item.targeting_radius
                target_x = player.x
                target_y = player.y

                message_log.add_message(
                    Message("You begin aiming the {0}.".format(
                        targeting.name)))
                # TODO: Replace occurrences of add_message with player_turn_result approach
                # player_turn_results.append({'message': Message("You begin aiming the {0}.".format(targeting.name))})

            if next_floor:
                entities = game_map.next_floor(player, constants)
                fov_map = initialize_fov(game_map)
                tcod.console_clear(con)
コード例 #6
0
class Engine():
    def __init__(self):
        # Set up the game window
        #libtcod.console_set_custom_font('spritesheet.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD)
        libtcod.console_set_custom_font(
            'Winterwing_Curses.png',
            libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_ASCII_INROW)
        libtcod.console_init_root(game_constants.screen_width,
                                  game_constants.screen_height,
                                  'Ascetic of the Cabal',
                                  True,
                                  libtcod.RENDERER_SDL2,
                                  vsync=True)

        # Establish the primary console as well as the detail panel
        self.con = libtcod.console.Console(game_constants.screen_width,
                                           game_constants.screen_height)
        self.panel = libtcod.console.Console(game_constants.screen_width,
                                             game_constants.panel_height)

        # Create references for the player input
        self.key = libtcod.Key()
        self.mouse = libtcod.Mouse()

        self.initialize_game()

    # Initializes a lot of run-specific items. Kept outside of init because it has to be re-run on a restart
    def initialize_game(self):
        # Create and initialize the Message Log
        self.message_log = MessageLog()

        #Initialize the player
        self.player = self.initialize_player()

        # Create a game map and fill it with enemies
        self.build_map()

        self.player_target = None

        # Establish the Game State
        self.game_map.compute_dijkstra_map([self.player], 'player', True)
        self.game_map.compute_dijkstra_map(
            self.entities.get_sublist(lambda x: x.name != "Ascetic"),
            "enemies")
        self.game_state = GameStates.PLAYERS_TURN
        self.previous_game_state = GameStates.PLAYERS_TURN
        self.game_running = True

    # Creates the player object, with all associated defaults
    # This can and probably should be moved to another file
    def initialize_player(self):
        player_components = {
            "Fighter":
            Fighter(hp=300, defense=2, power=5, factions=[Factions.PLAYER]),
            "Inventory":
            Inventory(26),
            "Devotee":
            Devotee(100),
            "StatusContainer":
            StatusContainer()
        }

        player = Entity(int(game_constants.screen_width / 2),
                        int(game_constants.screen_height / 2),
                        '@',
                        libtcod.white,
                        "Ascetic",
                        True,
                        RenderOrder.ACTOR,
                        message_log=self.message_log,
                        state=AIStates.INANIMATE,
                        components=player_components)

        player.get_component("Inventory").equip_item(
            generate_starting_pistol(self.message_log))
        return player

    # Generates a game map and initializes the FOV map of it
    def build_map(self, level=1):
        self.game_map = GameMap(self.message_log, level)
        self.entities = Entities(self.game_map)
        self.game_map.make_map(self.player, self.entities)
        self.entities.insert_entity(self.player)

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

    # Literally 0 recollection what this does
    def grade_map_down(self):
        self.game_map.grade_down()

    def cull_dead(self):
        # Finds every entity in the game world with 0 or less health and kills them
        # Returns true if it kills the player, otherwise false
        player_killed = False
        dead_entities = self.entities.get_sublist(
            lambda x: x.has_component("Fighter") and not x.get_component(
                "Fighter").isAlive() and x.char != '%')

        if dead_entities:
            for dead_entity in dead_entities:
                drop = dead_entity.get_component("Fighter").die()
                if drop:
                    self.entities.insert_entity(drop)
                if dead_entity == self.player:
                    player_killed = True
        return player_killed

    def send_invalid_action_message(self):
        self.message_log.add_message(Message("Can't do that here"),
                                     libtcod.red)

    # Initializes the game's start menu, and captures any player input on that menu and passes it to the appropriate handler
    def start_screen(self):
        show_main_menu = True

        while show_main_menu:
            libtcod.sys_check_for_event(
                libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, self.key,
                self.mouse)
            main_menu(self.con, game_constants.main_menu_background_image)

            libtcod.console_flush()

            action = handle_keys(self.key, GameStates.MAIN_MENU)
            game_type = action.get('game_start')
            exit_game = action.get('action') == 'exit'

            if exit_game:
                return False
            elif game_type == 'from_scratch':
                return True
            elif game_type == 'from_save':
                self.player, self.entities, self.game_map, self.message_log, self.game_state = load_game(
                )
                self.fov_map = initialize_fov(self.game_map)
                self.entities.set_log_all(self.message_log)
                return True

    def main(self):
        # Game Loop
        while self.game_running:
            # Check input streams for an event
            libtcod.sys_check_for_event(
                libtcod.EVENT_KEY_PRESS | libtcod.EVENT_MOUSE, self.key,
                self.mouse)

            # If we need to recompute fov, do so
            if self.fov_recompute:
                recompute_fov(self.fov_map, self.player.x, self.player.y)

            # Render the game world according to current FOV, mark FOV recompute as complete, and flush to console
            render_all(self.con, self.panel, self.entities, self.player,
                       self.game_map, self.fov_map, self.fov_recompute,
                       self.message_log, self.mouse, self.game_state,
                       self.player_target)
            self.fov_recompute = False
            libtcod.console_flush()

            # Interpret the input into a game action
            input = handle_keys(self.key, self.game_state)
            action = input.get('action')

            inventory_item = input.get(
                'inventory_item') if 'inventory_item' in input else None
            dialogue_option = input.get(
                'dialogue_option') if 'dialogue_option' in input else None
            shop_option = input.get(
                'shop_option') if 'shop_option' in input else None
            unequip_item = input.get('slot') if 'slot' in input else None

            # If players turned and it's their turn to move
            if action == 'move' and self.game_state == GameStates.PLAYERS_TURN:
                # Calculate where they should move
                dx, dy = input.get('move')
                destination_x = self.player.x + dx
                destination_y = self.player.y + dy

                # TODO: This is where you hid the noclip check. Fix this for release
                #if not self.game_map.is_blocked(destination_x, destination_y):
                if True:
                    # If they're not about to walk into a wall, check for enemies at the destination
                    potential_collision_list = self.entities.get_sublist(
                        lambda ent: ent.x == destination_x and ent.y ==
                        destination_y and ent.blocks)
                    target = potential_collision_list[
                        0] if potential_collision_list else None
                    if target and target.state == AIStates.HOSTILE:
                        # If there are enemies, attack them
                        self.player.get_component("Fighter").attack(target)
                        self.game_state = GameStates.ENEMY_TURN
                    elif target and target.state == AIStates.FRIENDLY:
                        self.previous_game_state = self.game_state
                        self.player_target = target
                        self.game_state = GameStates.DIALOGUE
                    else:
                        # If there are not enemies, move and mark FOV for recomputation
                        self.player.move(dx, dy, self.game_map)
                        self.fov_recompute = True
                        self.game_map.compute_dijkstra_map([self.player],
                                                           'player', True)
                        self.game_state = GameStates.ENEMY_TURN

            # If the player grabs something, check if there is an object at their feet, and either have them pick it up (if it's an Item) or add it to their wallet (if it's money)
            elif action == 'grab' and self.game_state == GameStates.PLAYERS_TURN:
                for item in self.entities.get_sublist(
                        lambda entity: (entity.has_component("Item") or entity.
                                        has_component("Money")) and entity.x ==
                        self.player.x and entity.y == self.player.y):
                    if item.has_component("Money"):
                        self.player.get_component("Fighter").pick_up_money(
                            item)
                    else:
                        self.player.get_component("Inventory").add_item(item)
                    self.entities.remove_entity(item)
                    self.game_state = GameStates.ENEMY_TURN

            # Open up the inventory menu
            elif action == 'inventory' and inventory_item is None:
                self.previous_game_state = self.game_state
                self.game_state = GameStates.INVENTORY_OPEN

            # Open up the equipped menu
            elif action == 'equipped':
                self.previous_game_state = self.game_state
                self.game_state = GameStates.EQUIPPED_OPEN

            elif action == 'unequip' and self.game_state == GameStates.EQUIPPED_OPEN:
                self.player.get_component("Inventory").unequip_slot(
                    unequip_item)

            # if the player has selected an inventory item to use, get the item object, and equip it if it's vgear, or use it if it's a consumable (like a potion)
            elif inventory_item is not None and self.previous_game_state != GameStates.PLAYER_DEAD and inventory_item < len(
                    self.player.get_component("Inventory").items):
                item_entity = self.player.get_component(
                    "Inventory").items[inventory_item]
                if ItemType(item_entity.get_component(
                        "Item").item_type) != ItemType.NONE:
                    self.player.get_component("Inventory").equip_item(
                        item_entity)
                else:
                    print("In the else")
                    if item_entity.get_component("Item").use(self.player):
                        self.player.get_component("Inventory").remove_item(
                            item_entity)

            # if the player is in dialogue, provide the dialogue option to the target's Character object
            elif dialogue_option is not None:
                dialogue_response = self.player_target.get_component(
                    "Character").talk(dialogue_option)
                if dialogue_response.shop:
                    self.game_state = GameStates.SHOPPING

            # if the player attempts to go down some stairs, make sure they're on stairs, then build a new map and clear the console
            elif action == 'go_down' and self.game_state == GameStates.PLAYERS_TURN:
                stairs_candidates = self.entities.get_sublist(
                    lambda entity: entity.x == self.player.x and entity.y ==
                    self.player.y and entity.has_component("Stairs"))
                if stairs_candidates:
                    self.build_map(
                        stairs_candidates[0].get_component("Stairs").floor)
                    libtcod.console_clear(self.con)

            # Save the game
            elif action == 'save':
                save_game(self.player, self.entities, self.game_map,
                          self.message_log, self.game_state)

            # if the player draws their gun, change to a player shoot state and await gunfire
            elif self.game_state == GameStates.PLAYERS_TURN and action == 'gun':
                if (self.player.get_component("Inventory").slot_filled(
                        "RANGED")):
                    self.previous_game_state = self.game_state
                    self.game_state = GameStates.PLAYER_SHOOT
                    self.message_log.add_message(
                        Message(
                            "Taking aim. Click on your target, or e to holster"
                        ))
                else:
                    self.message_log.add_message(
                        Message("No ranged weapon equipped!"))

            # if the player already has their gun drawn and presses the draw button, holster it instead
            elif self.game_state == GameStates.PLAYER_SHOOT and action == 'holster':
                self.game_state = self.previous_game_state
                self.message_log.add_message(Message("Holstered your weapon"))

            # if the player has their gun drawn and clicks on a target, check if there is line of sight
            # and if so, shoot the target. This sets the AI to hostile if it isn't already (this should be handled by Fighter)
            elif self.game_state == GameStates.PLAYER_SHOOT and self.mouse.lbutton_pressed:
                target = get_shoot_target(self.mouse, self.entities,
                                          self.fov_map)
                if (target):
                    line_of_sight = draw_line((self.player.x, self.player.y),
                                              (target.x, target.y))
                    if not [
                            space for space in line_of_sight
                            if self.game_map.is_blocked(space[0], space[1])
                    ]:
                        self.player.get_component("Fighter").ranged_attack(
                            target)
                        target.state = AIStates.HOSTILE
                        self.game_state = GameStates.ENEMY_TURN
                    else:
                        self.message_log.add_message(
                            Message("You don't have a clear line of sight!"))

            # if the player right clicks something, get open up the inspect menu for that target
            elif self.mouse.rbutton_pressed and self.game_state != GameStates.INSPECT_OPEN:
                target = get_shoot_target(self.mouse, self.entities,
                                          self.fov_map, False)
                if (target):
                    self.player_target = target
                    self.previous_game_state = self.game_state
                    self.game_state = GameStates.INSPECT_OPEN

            # If the player is buying something, they make the purchase
            elif action == 'buy' and shop_option is not None:
                target.get_component("Shop").purchase(shop_option, self.player)

            elif action == 'status':
                self.previous_game_state = self.game_state
                self.game_state = GameStates.STATUS

            # Exit the game
            if action == 'exit' and (self.game_state in [
                    GameStates.INVENTORY_OPEN, GameStates.DIALOGUE,
                    GameStates.EQUIPPED_OPEN, GameStates.SHOPPING,
                    GameStates.INSPECT_OPEN, GameStates.STATUS
            ]):
                self.game_state = self.previous_game_state
            elif action == 'exit':
                return True

            # Set the game to fullscreen
            if action == 'fullscreen':
                libtcod.console_set_fullscreen(
                    not libtcod.console_is_fullscreen())

            # cull_dead returns true if the player is dead, so this conditional calls it to cull the dead, and then
            # checks if the game is over
            if self.cull_dead():
                self.game_state = GameStates.PLAYER_DEAD

            # when it's the AI's turn, find every entity that has AI and move it (if it's hostile)
            if self.game_state == GameStates.ENEMY_TURN:
                for entity in self.entities.get_entity_set():
                    if entity.has_component(
                            "AI") and entity.state == AIStates.HOSTILE:
                        entity.get_component("AI").take_turn(
                            self.player, self.fov_map, self.game_map,
                            self.entities)
                        if self.cull_dead():
                            self.game_state = GameStates.PLAYER_DEAD
                    if entity.has_component("StatusContainer"):
                        entity.get_component("StatusContainer").tick_clocks()
                        for status in entity.get_component(
                                "StatusContainer").get_statuses():
                            status_mapping[status](entity, self.entities,
                                                   self.game_map)

                if self.game_state != GameStates.PLAYER_DEAD:
                    self.player.get_component("StatusContainer").tick_clocks()
                    for status in self.player.get_component(
                            "StatusContainer").get_statuses():
                        status_mapping[status](self.player, self.entities,
                                               self.game_map)
                    self.game_map.compute_dijkstra_map(
                        self.entities.get_sublist(
                            lambda x: x.name != "Ascetic"), "enemies")
                    self.game_state = GameStates.PLAYERS_TURN

            # TODO: need a check somewhere around here to tick condition clocks, and then to apply conditions

            if action == 'restart':
                libtcod.console_clear(self.con)
                self.initialize_game()
コード例 #7
0
ファイル: engine.py プロジェクト: mgedmin/tcod-tutorial
def play_game(player: Entity, entities: List[Entity], game_map: GameMap,
              message_log: MessageLog, game_state: GameStates,
              root_console: tcod.console.Console, con: tcod.console.Console,
              panel: tcod.console.Console) -> None:
    fov_recompute = True

    fov_map = initialize_fov(game_map)

    mouse = tcod.event.Point(-1, -1)

    if player.fighter.hp > 0:
        game_state = GameStates.PLAYERS_TURN
    else:
        game_state = GameStates.PLAYER_DEAD
    previous_game_state = game_state

    targeting_item = None

    while True:
        action: UserAction = {}
        for event in tcod.event.wait(1):
            if event.type == 'QUIT':
                # XXX: what happens if I do this when in the character screen?
                # or inventory? or while targeting?  will the game load fine?
                save_game(player, entities, game_map, message_log, game_state)
                sys.exit()
            elif event.type == 'KEYDOWN':
                action = handle_keys(event, game_state, mouse)
            elif event.type == 'MOUSEMOTION':
                mouse = event.tile
            elif event.type == 'MOUSEBUTTONDOWN':
                mouse = event.tile
                action = handle_mouse(event)
            if action:
                break

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

        target_radius = 0
        if targeting_item and targeting_item.item:
            target_radius = targeting_item.item.function_kwargs.get(
                'radius', 0)

        render_all(
            root_console,
            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,
            target_radius,
        )

        fov_recompute = False

        tcod.console_flush()

        clear_all(con, entities)

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

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

        player_turn_results: ActionResults = []

        if move and game_state == GameStates.PLAYERS_TURN:
            dx, dy = move
            new_x = player.x + dx
            new_y = player.y + dy
            if not game_map.is_blocked(new_x, new_y):
                target = get_blocking_entities_at_location(
                    entities, new_x, new_y)
                if target:
                    player_turn_results.extend(player.fighter.attack(target))
                else:
                    player.move(dx, dy)
                    fov_recompute = True

                game_state = GameStates.ENEMY_TURN

        if wait and game_state == GameStates.PLAYERS_TURN:
            game_state = GameStates.ENEMY_TURN

        if pickup and game_state == GameStates.PLAYERS_TURN:
            for entity in entities:
                if (entity.x == player.x and entity.y == player.y
                        and entity.item):
                    player_turn_results.extend(
                        player.inventory.add_item(entity))
                    break
            else:
                message_log.add_message(
                    Message('There is nothing here to pick up.', tcod.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):
                    save_game(player, entities, game_map, message_log,
                              game_state)
                    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('There are no stairs here.', tcod.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
                player_turn_results.extend(
                    player.inventory.use(targeting_item,
                                         entities=entities,
                                         fov_map=fov_map,
                                         target_x=target_x,
                                         target_y=target_y))
            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:
                save_game(player, entities, game_map, message_log, game_state)
                return

        if fullscreen:
            tcod.console_set_fullscreen(not tcod.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: Optional[Entity] = 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')
            targeting = player_turn_result.get('targeting')
            targeting_cancelled = player_turn_result.get('targeting_cancelled')
            xp = player_turn_result.get('xp')

            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 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(f"You equipped the {equipped.name}."))
                    if dequipped:
                        message_log.add_message(
                            Message(f"You removed the {dequipped.name}."))
                game_state = GameStates.ENEMY_TURN

            if xp:
                leveled_up = player.level.add_xp(xp)
                message_log.add_message(
                    Message(f'You gain {xp} experience points.'))
                if leveled_up:
                    message_log.add_message(
                        Message(
                            f'Your battle skills grow stronger!'
                            f' You reached level {player.level.current_level}!',
                            tcod.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
コード例 #8
0
ファイル: game.py プロジェクト: nobus/rogatut
class Game:
    def __init__(self):
        # Initialization
        libtcodpy.console_set_custom_font('src/arial10x10.png', libtcodpy.FONT_TYPE_GREYSCALE | libtcodpy.FONT_LAYOUT_TCOD)
        libtcodpy.console_init_root(settings.SCREEN_WIDTH, settings.SCREEN_HEIGHT, 'ROGuelike TUTorial', False)
        libtcodpy.sys_set_fps(settings.LIMIT_FPS)
        self.con = libtcodpy.console_new(settings.SCREEN_WIDTH, settings.SCREEN_HEIGHT)

        self.game_map = GameMap(self.con)
        player_x, player_y = self.game_map.get_staring_position()
        # game objects
        self.player = Player(
            'Player',
            self.con,
            player_x, player_y, '@',
            fighter=Fighter(hp=15, defense=5, power=5, death_function=player_death))

        npc_x, npc_y = self.game_map.get_ending_position()
        self.npc = Npc('Trader', self.con, npc_x, npc_y, '@')
        self.objects = [self.npc, self.player]

        self.npcs = [self.npc]
        for monster in self.game_map.place_monsters():
            self.objects.append(monster)

            if hasattr(monster, 'is_selfmoving') and monster.is_selfmoving:
                self.npcs.append(monster)

        self.game_state = 'playing'
        self.player_action = None

    def is_blocked_and_target(self, x, y):
        #first test the map tile
        if self.game_map.is_blocked(x, y):
            return (True, None)

        #now check for any blocking objects
        for obj in self.objects:
            if obj.x == x and obj.y == y:
                return (obj.blocks, obj)

        return (False, None)

    def is_blocked(self, x, y):
        _is_blocked, _ = self.is_blocked_and_target(x, y)
        return _is_blocked

    def render_all(self):
        self.game_map.render(self.player.x, self.player.y)
    
        #draw all objects in the list
        for obj in self.objects:
            obj.draw()
    
        #blit the contents of "con" to the root console
        libtcodpy.console_blit(self.con, 0, 0, settings.SCREEN_WIDTH, settings.SCREEN_HEIGHT, 0, 0, 0)

        #show the player's stats
        libtcodpy.console_set_default_foreground(self.con, libtcodpy.white)
        libtcodpy.console_print_ex(
            self.con,
            1,
            settings.SCREEN_HEIGHT - 2,
            libtcodpy.BKGND_NONE,
            libtcodpy.LEFT,
            f'HP: {str(self.player.fighter.hp)} / {str(self.player.fighter.max_hp)}'
        )

    def clear_objects(self):
        #erase all objects at their old locations, before they move
        for obj in self.objects:
            obj.clear()

    def move_npcs(self):
        # move npc
        for npc in self.npcs:
            npc.move(self.is_blocked)

    def handle_keys(self):
        #key = libtcod.console_check_for_keypress()  #real-time
        key = libtcodpy.console_wait_for_keypress(True)  #turn-based

        #let monsters take their turn
        if self.game_state == 'playing' and self.player_action != 'didnt-take-turn':
            for obj in self.objects:
                if obj.ai:
                    obj.ai.take_turn(self.game_map, self.player)

        if self.game_state == 'playing':
            if key.vk == libtcodpy.KEY_ENTER and key.lalt:
                #Alt+Enter: toggle fullscreen
                libtcodpy.console_set_fullscreen(not libtcodpy.console_is_fullscreen())

            elif key.vk == libtcodpy.KEY_ESCAPE:
                return 'exit'  #exit game

            #movement keys
            if libtcodpy.console_is_key_pressed(libtcodpy.KEY_UP):
                self.game_map.fov_recompute = self.player.move(0, -1, self.is_blocked_and_target)

            elif libtcodpy.console_is_key_pressed(libtcodpy.KEY_DOWN):
                self.game_map.fov_recompute = self.player.move(0, 1, self.is_blocked_and_target)

            elif libtcodpy.console_is_key_pressed(libtcodpy.KEY_LEFT):
                self.game_map.fov_recompute = self.player.move(-1, 0, self.is_blocked_and_target)

            elif libtcodpy.console_is_key_pressed(libtcodpy.KEY_RIGHT):
                self.game_map.fov_recompute = self.player.move(1, 0, self.is_blocked_and_target)

            else:
                return 'didnt-take-turn'

    def run(self):
        # Main loop
        while not libtcodpy.console_is_window_closed():
            self.render_all()
            libtcodpy.console_flush()

            self.clear_objects()
            self.move_npcs()

            #handle keys and exit game if needed
            self.player_action = self.handle_keys()
            if self.player_action == 'exit':
                break
コード例 #9
0
ファイル: main.py プロジェクト: amfl/roguelike-python
def game_loop(t: Terminal, game_map: GameMap, entities, fov_map):
    # For now, the player is simply the first entity.
    player = entities[0]

    closed = False
    frame_count = 0
    fov_recompute = True

    game_state = GameStates.PLAYERS_TURN

    while not closed:
        logger.debug(f'frame: {frame_count}')

        clear_all(t, entities)

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

        # Enemies move before the screen renders
        if game_state == GameStates.ENEMY_TURN:
            for e in entities:
                if e.ai:
                    e.ai.take_turn(player, fov_map, game_map, entities)
            game_state = GameStates.PLAYERS_TURN

        render_all(t, game_map, entities, fov_map, fov_recompute)

        sys.stdout.flush()

        # Read in input for a new turn.
        fov_recompute = False
        inp = t.inkey()
        logger.debug('Key Input: ' + repr(inp))
        action = handle_keys(inp)
        logger.debug('Action: ' + repr(action))

        exit = action.get('exit')
        move = action.get('move')

        # Escape key doesn't work and I don't know why.
        if exit:
            logger.info('Quitting cleanly...')
            closed = True
            return True

        if move and game_state == GameStates.PLAYERS_TURN:
            destination = (
                player.x + move[0],
                player.y + move[1],
            )
            if not game_map.is_blocked(destination[0], destination[1]):
                # Try to push anything that is there
                [
                    e.push(game_map, move[0], move[1]) for e in entities
                    if e.x == destination[0] and e.y == destination[1]
                ]

                blocking_ent = Entity.get_blocking_entity_at_location(
                    entities, destination[0], destination[1])
                if blocking_ent:
                    logger.debug("Bonked into a blocking entity: %s",
                                 blocking_ent.name)
                else:
                    # Update player position
                    player.move(move[0], move[1])
                    fov_recompute = True
            game_state = GameStates.ENEMY_TURN

        frame_count += 1