class Engine:
    game_map: GameMap
    game_world: GameWorld

    def __init__(self, player: Actor):
        self.message_log = MessageLog()
        self.mouse_location = (0, 0)
        self.player = player
        self.player.encountered = True

    def handle_enemy_turns(self) -> None:
        for entity in set(self.game_map.actors) - {self.player}:
            if entity.ai:
                try:
                    entity.ai.perform()
                except exceptions.Impossible:
                    pass  # Ignore impossible action exceptions from AI.

    def update_fov(self) -> None:
        """Recompute the visible area based on the players point of view."""
        self.game_map.visible[:] = compute_fov(
            self.game_map.tiles["transparent"],
            (self.player.x, self.player.y),
            radius=8,
        )
        # If a tile is "visible" it should be added to "explored".
        self.game_map.explored |= self.game_map.visible

    def render(self, console: Console) -> None:
        self.game_map.render(console)

        self.message_log.render(console=console,
                                x=21,
                                y=45,
                                width=40,
                                height=5)

        render_functions.render_bar(
            console=console,
            current_value=self.player.fighter.hp,
            maximum_value=self.player.fighter.max_hp,
            total_width=20,
        )

        render_functions.render_dungeon_level(
            console=console,
            dungeon_level=self.game_world.current_floor,
            location=(0, 47),
        )

        render_functions.render_names_at_mouse_location(console=console,
                                                        x=21,
                                                        y=44,
                                                        engine=self)

    def save_as(self, filename: str) -> None:
        """Save this Engine instance as a compressed file."""
        save_data = lzma.compress(pickle.dumps(self))
        with open(filename, "wb") as f:
            f.write(save_data)
示例#2
0
class Engine:
	game_map: GameMap

	def __init__(self, player: Actor):
		self.event_handler: EventHandler = MainGameEventHandler(self)
		self.message_log = MessageLog()
		self.mouse_location = (0, 0)
		self.player = player

	def handle_enemy_turns(self) -> None:
		for entity in set(self.game_map.actors) - {self.player}:
			if entity.ai:
				entity.ai.perform()

	def update_fov(self) -> None:
		self.game_map.visible[:] = compute_fov(
			self.game_map.tiles["transparent"],
			(self.player.x, self.player.y),
			radius=8,
		)
		self.game_map.explored |= self.game_map.visible

	def render(self, console: Console) -> None:
		self.game_map.render(console)

		self.message_log.render(console=console, x=22, y=45, width=59, height=5)

		render_bar(
			console=console,
			current_value=self.player.fighter.hp,
			maximum_value=self.player.fighter.max_hp,
			total_width=20,
		)

		render_names_at_mouse_location(console=console, x=22, y=44, engine=self)
示例#3
0
class Engine:
    def __init__(self, game_map: GameMap, player: Actor):
        self.event_handler: EventHandler = MainGameEventHandler(self)
        self.game_map = game_map
        self.message_log = MessageLog(x=21, y=45, width=60, height=4)
        self.mouse_location = (0, 0)
        self.player = player
        self.update_fov()

    def handle_enemy_turns(self) -> None:
        for entity in self.game_map.actors - {self.player}:
            if entity.ai is not None and not isinstance(entity.ai, DeadAI):
                action = entity.ai.take_turn(self, self.player)

                if action is None:
                    continue

                action.perform()

        check_for_dead_entities(self)

    def update_fov(self) -> None:
        """Recompute the visible area based on the players point of view."""
        self.game_map.visible[:] = compute_fov(
            self.game_map.tiles["transparent"],
            (self.player.x, self.player.y),
            radius=5,
        )
        # If a tile is "visible" it should be added to "explored".
        self.game_map.explored |= self.game_map.visible

    def render(self, console: Console, context: Context) -> None:
        self.game_map.render(console)

        render_menu(console=console,
                    map_height=self.game_map.height,
                    menu_width=100)

        self.message_log.render(console=console)

        # If the current event handler is the Inventory handler, show the inventory screen.
        if isinstance(self.event_handler, InventoryEventHandler):
            render_inventory_menu(console=console, engine=self)

        render_bar(console=console,
                   character=self.player,
                   current_value=self.player.fighter.hp,
                   maximum_value=self.player.fighter.max_hp,
                   total_width=20)

        render_names_at_mouse_location(console=console,
                                       x=21,
                                       y=44,
                                       engine=self)

        context.present(console)

        console.clear()
示例#4
0
class Engine:
    game_map: GameMap
    game_world: GameWorld

    def __init__(self, player: Actor):
        self.message_log = MessageLog()
        self.mouse_location = (
            0, 0)  # здесь ханится информация о местонахождении мыши
        self.player = player

    def handle_enemy_turns(self) -> None:
        for entity in set(self.game_map.actors) - {self.player}:
            if entity.ai:
                try:
                    entity.ai.perform()
                except exceptions.Impossible:
                    pass  # Игнорирует исключения, возникающие из-за невозможных действий AI.

    def update_fov(self) -> None:
        """Пересчитывает видимую область на основе местонахождения героя."""
        self.game_map.visible[:] = compute_fov(
            self.game_map.tiles["transparent"],
            (self.player.x, self.player.y),
            radius=
            8,  # радиус отвечает за то, насколько обширным будет поле зрения
        )
        # Если плитка - "visible", она должны быть добавлена к "explored".
        self.game_map.explored |= self.game_map.visible  # добавляет увиденные плитки к исследованным
        #(любая плитка, которая была увидена, автоматически считается исследованной)

    def render(self, console: Console) -> None:
        self.game_map.render(console)

        self.message_log.render(console=console,
                                x=21,
                                y=47,
                                width=45,
                                height=7)

        render_functions.render_bar(
            console=console,
            current_value=self.player.fighter.hp,
            maximum_value=self.player.fighter.max_hp,
            total_width=20,
        )

        render_functions.render_spaceship_level(
            console=console,
            dungeon_level=self.game_world.current_floor,
            location=(0, 49),
        )

        render_functions.render_names_at_mouse_location(console=console,
                                                        x=21,
                                                        y=46,
                                                        engine=self)
        render_functions.render_help(console=console, location=(68, 47))
示例#5
0
class Engine:
    game_map: GameMap
    game_world: GameWorld

    def __init__(self, player: Actor):
        self.message_log = MessageLog()
        self.mouse_location = (0, 0)
        self.player = player

    def handle_enemy_turns(self) -> None:
        for entity in set(self.game_map.actors) - {self.player}:
            if entity.ai:
                try:
                    entity.ai.perform()
                except exceptions.Impossible:
                    pass

    def update_fov(self) -> None:
        self.game_map.visible[:] = compute_fov(
            self.game_map.tiles["transparent"],
            (self.player.x, self.player.y),
            radius=8,
        )
        self.game_map.explored |= self.game_map.visible

    def render(self, console: Console) -> None:
        self.game_map.render(console)

        self.message_log.render(console=console,
                                x=21,
                                y=45,
                                width=40,
                                height=5)

        render_functions.render_bar(
            console=console,
            current_value=self.player.fighter.hp,
            maximum_value=self.player.fighter.max_hp,
            total_width=20,
        )

        render_functions.render_dungeon_level(
            console=console,
            dungeon_level=self.game_world.current_floor,
            location=(0, 47),
        )

        render_functions.render_names_at_mouse_location(console=console,
                                                        x=21,
                                                        y=44,
                                                        engine=self)

    def save_as(self, filename: str) -> None:
        save_data = lzma.compress(pickle.dumps(self))
        with open(filename, "wb") as f:
            f.write(save_data)
示例#6
0
class Engine:
    game_map: GameMap

    def __init__(self, player: Actor):
        self.event_handler: EventHandler = MainGameEventHandle(self)
        self.message_log = MessageLog()
        self.mouse_location = (0, 0)
        self.player = player

    def handle_enemy_turns(self) -> None:
        for entity in set(self.game_map.actors) - {self.player}:
            if entity.ai:
                try:
                    entity.ai.perform()
                except exceptions.Impossible:
                    pass  # Ignore impossible action exceptions from AI.

    def update_fov(self) -> None:
        """Recompute the visible area based on the players point of view"""
        self.game_map.visible[:] = compute_fov(
            self.game_map.tiles["transparent"],
            (self.player.x, self.player.y),
            radius=8,
        )
        # if a tile is visible it should be added to explored
        self.game_map.explored |= self.game_map.visible

    def render(self, console: Console) -> None:
        self.game_map.render(console)

        self.message_log.render(console=console,
                                x=21,
                                y=45,
                                width=40,
                                height=5)

        render_bar(
            console=console,
            current_value=self.player.fighter.hp,
            maximum_value=self.player.fighter.max_hp,
            total_width=20,
        )

        render_names_at_mouse_location(console=console,
                                       x=21,
                                       y=44,
                                       engine=self)
示例#7
0
class Engine:
    game_map: GameMap

    def __init__(self, player: Actor):
        self.event_handler: EventHandler = MainGameEventHandler(self)
        self.message_log = MessageLog()
        self.mouse_location = (0, 0)
        self.player = player

    def handle_enemy_turns(self) -> None:
        for entity in set(self.game_map.actors) - {self.player}:
            if entity.ai:
                entity.ai.perform()

    def update_fov(self) -> None:
        """Recompute the visible area based on the players point of view."""
        self.game_map.visible[:] = compute_fov(
            self.game_map.tiles["transparent"],
            (self.player.x, self.player.y),
            radius=8,
        )
        # If a tile is "visible" it should be added to "explored".
        self.game_map.explored |= self.game_map.visible

    def render(self, console: Console) -> None:
        self.game_map.render(console)

        render_ui_box(console=console, engine=self)

        self.message_log.render(console=console,
                                x=23,
                                y=45,
                                width=75,
                                height=3)

        render_bar(
            console=console,
            current_value=self.player.fighter.hp,
            maximum_value=self.player.fighter.max_hp,
            total_width=20,
        )

        render_names_at_mouse_location(console=console, x=2, y=43, engine=self)
示例#8
0
class Engine:
    game_map: Game_Map
    game_world: GameWorld

    def __init__(self, player: Actor):
        self.event_handler: EventHandler = IntroScreen(self)
        self.mode = "idle"
        self.player = player
        self.message_log = MessageLog()
        self.mouse_location = (0, 0)
        self.scorekeeper = ScoreKeeper()
        self.effect = None

    # @property
    # def task(self, console: Console, motivation: int, T_energy: int, special: bool):
    #     return render_task(console, motivation, special)

    #change color of game_map
    def rerender(self):
        if self.mode == "idle":
            self.mode = "seek"
            self.player.color = colors.salmon
            for entity in set(self.game_map.actors) - {self.player}:
                entity.color = colors.welcome_text

            #no. of times entered seek mode is part of score
            self.scorekeeper.seek_mode += 1

        else:
            self.mode = "idle"
            if self.effect:
                self.effect.deactivate
            self.player.color = colors.v_dark_purp
            for entity in set(self.game_map.actors) - {self.player}:
                entity.color = colors.bar_filled
        self.message_log.add_message(self.mode, colors.lite_blue)

    def others_handleturn(self) -> None:
        for entity in set(self.game_map.actors) - {self.player}:
            if entity.ai:
                try:
                    entity.ai.perform()
                except exceptions.Impossible:
                    pass  # Ignore impossible action exceptions from AI.

    def update_fov(self) -> None:
        if self.effect:
            if self.effect.type == "blindness":
                self.game_map.visible[:] = compute_fov(
                    self.game_map.tiles["transparent"],
                    (self.player.x, self.player.y),
                    radius=1,
                )

                #do not add visible tiles to explored
            elif self.effect.type == "clarity":
                self.game_map.visible[:] = compute_fov(
                    self.game_map.tiles["transparent"],
                    (self.player.x, self.player.y),
                    radius=1000,
                )
            else:
                self.game_map.visible[:] = compute_fov(
                    self.game_map.tiles["transparent"],
                    (self.player.x, self.player.y),
                    radius=8,
                )

        else:
            self.game_map.visible[:] = compute_fov(
                self.game_map.tiles["transparent"],
                (self.player.x, self.player.y),
                radius=8,
            )

            self.game_map.explored |= self.game_map.visible  #visible tiles get added to explore

    #energy degenerates as you play
    def decrease_energy(self) -> None:
        if random() > 0.5:  #will bring this down to 50/50, just to test game
            self.player.fighter.energy -= 1
            self.scorekeeper.score += 1
            if self.mode == "seek":
                self.scorekeeper.seek_energy_spent += 1

    def render(self, console: Console) -> None:
        self.game_map.render(console)
        self.message_log.render(console=console,
                                x=17,
                                y=30,
                                width=15,
                                height=5)
        render_bar(console=console,
                   current_value=self.player.fighter.energy,
                   maximum_value=self.player.fighter.max_energy,
                   total_width=15)
        if self.effect:
            render_effect(console=console, type=self.effect.type)
        render_names_at_mouse_location(console=console, x=2, y=4, engine=self)
示例#9
0
文件: engine.py 项目: Sunnigen/rlkivy
class Engine:
    game_map: GameMap
    game_world: GameWorld
    graphics_component: GraphicsFrame
    mouse_location: Tuple[int, int] = (0, 0)
    map_location: Tuple[int, int] = (0, 0)
    temp_location: Tuple[int, int] = None

    def __init__(self, player: Actor):
        self.message_log = MessageLog()
        self.player = player
        self.graphics_component = GraphicsFrame(self)

    def add_graphics_component(self, root_widget: Widget) -> None:
        if not self.graphics_component:
            self.graphics_component = GraphicsFrame(self)

        if self.graphics_component in root_widget.children:
            return

        root_widget.add_widget(self.graphics_component)

    def handle_enemy_turns(self) -> None:
        for entity in set(self.game_map.actors) - {self.player}:
            if entity.ai:
                try:
                    entity.ai.perform()
                except exceptions.Impossible:
                    pass  # Ignore impossible action exceptions from AI.

    def normalize_mouse_pos(self, root_widget: Widget) -> None:
        # Changes the mouse location from floats, to tile coordinates on screen
        x, y = root_widget.mouse_location
        y -= 14  # Magic number offset from nearest multiple of 12
        tile_size = 32
        self.update_mouse_location(int(x // tile_size), int(y // tile_size))

    def update_mouse_location(self, x: int, y: int) -> None:
        self.mouse_location = x, y
        # Update mouse location on map
        self.update_map_location(x, y)

    def get_mouse_location(self) -> Tuple[int, int]:
        return self.mouse_location

    def update_temp_location(self, x: int, y: int) -> None:
        self.temp_location = x, y

    def get_temp_location(self) -> Tuple[int, int]:
        return self.temp_location

    def disable_temp_location(self) -> None:
        self.temp_location = None

    def update_map_location(self, x: int, y: int) -> None:
        y_offset = int(self.graphics_component.game_window.y / 32)
        map_x_offset = 10
        map_y_offset = 10

        # Check if Game Window is Centered on Player or on Temporary Location
        if self.temp_location:
            origin_x, origin_y = self.temp_location
        else:
            origin_x, origin_y = self.player.x, self.player.y

        self.map_location = x + origin_x - map_x_offset, y + origin_y - map_y_offset - y_offset

    def get_map_location(self) -> Tuple[int, int]:
        return self.map_location

    def update_fov(self) -> None:
        """Recompute the visible area based on the players point of view."""
        self.game_map.visible[:] = compute_fov(
            self.game_map.tiles["transparent"],
            (self.player.x, self.player.y),
            radius=8,
        )
        # If a tile is "visible" it should be added to "explored".
        self.game_map.explored |= self.game_map.visible

    def render(self, root_widget: Widget, dt: float) -> None:
        self.normalize_mouse_pos(root_widget)
        self.graphics_component.render(root_widget, dt)
        self.message_log.render(root_widget=root_widget, engine=self, height=5)

    def disconnect_graphics_component(self) -> None:
        self.graphics_component = None

    def save_as(self, filename: str) -> None:
        """Save this Engine instance as a compressed file."""
        self.disconnect_graphics_component()
        save_data = lzma.compress(pickle.dumps(self))
        with open(filename, "wb") as f:
            f.write(save_data)
示例#10
0
class Engine:
    def __init__(self, player: Actor):
        """
        Vars:
            player_path:
                Uses deque data structure to save player's path when the player uses mouse driven movements.
            player_dir:
                Uses tuple to save player's action direction(dx,dy) when the player uses mouse driven actions.
            actors_in_sight, items_in_sight:
                Set of actors/items that are currently in player's visible area.
            prev_actors_in_sight, prev_items_in_sight:
                Set of actors/items that was in player's visible area one turn ago.
            context:
                The actual window screen that shows the game.
            game_map:
                Current gamemap that player is in.
            world:
                Collection of entire gamemaps created.
        """
        self.event_handler: EventHandler = MainGameEventHandler(self)
        self.message_log = MessageLog(engine=self)
        self.mouse_location = (0, 0)
        self.mouse_dir = (1, 1)
        self.player = player
        self.player_path = deque([])
        self.player_dir = None
        self.actors_in_sight = set()
        self.items_in_sight = set()
        self.prev_actors_in_sight = set()
        self.prev_items_in_sight = set()
        self.game_turn = 0
        self.config = None  # Set from initialization
        self.console = None
        self.context = None
        self.camera = None
        self.world = {}
        self.game_map: GameMap = None
        self.depth: int = 0

    @property
    def mouse_relative_location(self):
        x, y = self.camera.get_relative_coordinate(
            abs_x=self.mouse_location[0], abs_y=self.mouse_location[1])
        return x, y

    def handle_world(self, turn_pass: bool) -> None:
        """
        Handles things that happens regardless of player's will.
        This function will run after every in-game events.
        However most of its parts will not function unless the game world's time has passed.

        Args:
            turn_pass:
                This indicates whether the time has passed after player's input.
                If this is False, world's time will not pass as well.
                NOTE To stop a game world time except for the player, you should set turn_pass to False.
        """
        if turn_pass:
            self.player.spend_action_point()
            self.time_pass()
            self.handle_enemy_turns()
            self.handle_semiactor_turns()
            self.handle_actor_states()
            self.handle_item_states()
            self.handle_gamemap_states()
            self.update_fov()
            self.update_enemy_fov()
            self.update_entity_in_sight()

    def time_pass(self) -> None:
        while self.player.action_point < 60:
            for entity in set(self.game_map.actors) - {self.player}:
                entity.gain_action_point()
            for entity in set(self.game_map.semiactors):
                entity.gain_action_point()
            self.player.gain_action_point()
        self.game_turn += 1

    def handle_enemy_turns(self) -> None:
        for entity in set(self.game_map.actors) - {self.player}:
            if entity.ai and not entity.actor_state.is_dead:
                while entity.action_point >= 60:
                    try:
                        entity.ai.perform()
                    except exceptions.Impossible:
                        pass  # Ignore impossible action exceptions from AI.
                    entity.spend_action_point()

    def handle_item_states(self) -> None:
        """
        Handle things about items that should be updated every turn.
        e.g. Rotting foods, burning items
        """
        for item in set(self.game_map.items):
            if item.item_state.is_burning:
                item.item_state.burn()
            if item.edible:
                item.edible.time_pass()
        for entity in set(
                self.game_map.actors
        ):  # NOTE: If this cause too much performance issues, change the code so that the game only checks player's inventory.
            for item in entity.inventory.items:
                if item.item_state.is_burning:
                    item.item_state.burn(
                        owner=entity
                    )  # NOTE: The fireproof checking of the inventory happens during the ignition of the fire. (at rule.py)
                if item.edible:
                    item.edible.time_pass()

    def handle_actor_states(self) -> None:
        """
        Handle things about actors that should be updated every turn.
        e.g. burning monsters

        When something should be handled immediately, this isn't the place to do it.
        e.g. electrical shock
        """
        for actor in set(self.game_map.actors):
            # Bug prevention
            if actor.is_dead:
                print(
                    "ERROR :: THE ACTOR IS DEAD BUT HANDLE_ACTOR_STATES() IS STILL RUNNING."
                )

            ### Unique status effects ###
            # Burning
            if actor.actor_state.is_burning != [0, 0, 0, 0]:
                actor.actor_state.actor_burn()
            # Paralyzed
            if actor.actor_state.is_paralyzing != [0, 0]:
                actor.actor_state.actor_paralyzing()
            # Confusion
            if actor.actor_state.is_confused != [0, 0]:
                actor.actor_state.actor_confused()
            # Completly frozen
            if actor.actor_state.is_frozen != [0, 0, 0]:
                actor.actor_state.actor_frozen()
            # Freezing
            if actor.actor_state.is_freezing != [0, 0, 0, 0, 0]:
                actor.actor_state.actor_freeze()
            # Bleeding
            if actor.actor_state.is_bleeding != [0, 0, 0]:
                actor.actor_state.actor_bleed()
            # Poisoned
            if actor.actor_state.is_poisoned != [0, 0, 0, 0]:
                actor.actor_state.actor_poisoned()
            # Water related status effects NOTE: Drowning status effect is handled seperately
            if actor.actor_state.is_submerged:
                actor.actor_state.actor_submerged()
                if not actor.actor_state.is_underwater and actor.actor_state.was_submerged:
                    # actor moved from deep water to shallow water
                    actor.status.reset_bonuses(
                        ["bonus_dexterity", "bonus_agility"])
            elif actor.actor_state.was_submerged:
                # actor is completely out of water
                actor.status.reset_bonuses(
                    ["bonus_dexterity", "bonus_agility"])
            # Drowning
            if actor.actor_state.is_drowning != [0, 0]:
                actor.actor_state.actor_drowning()

            ### Regular status effects ###
            # Health point recovering
            if actor.actor_state.heal_wounds:
                actor.actor_state.actor_heal_wounds()
            # Hunger
            if actor.actor_state.hunger >= 0:
                actor.actor_state.actor_gets_hungry()

    def handle_semiactor_turns(self) -> None:
        """
        Handle semiactors' actions.
        NOTE: Semiactor's lifetime is handled in rule.perform(). 
        This includes deleting semiactors after there lifetime, and decreasing the lifetime every turn.
        """
        current_entities = self.game_map.entities
        current_semiactors = []

        for entity in current_entities:
            if isinstance(entity, SemiActor) and entity.is_active:
                current_semiactors.append(entity)

        for entity in set(current_semiactors):
            if entity.rule:
                if entity.do_action:
                    while entity.action_point >= 60:
                        try:
                            entity.rule.perform()
                        except exceptions.Impossible:
                            print(
                                f"DEBUG::HANDLE_SEMIACTOR_TURNS() - IMPOSSIBLE ACTION WAS TRIED FROM THE SEMIACTOR{entity.name}"
                            )
                            pass
                        entity.spend_action_point()

            # If the semiactor has walkable component, set its previous_entity to None
            # so that it can be triggered again when it is stepped on by the same entity.
            if entity.walkable:
                # Check if the entity has previous_entity variables. (Some trap semiactors might not have this variable when it's unnecessary.)
                try:
                    if entity.walkable.previous_entity:
                        entity.walkable.previous_entity = None
                except AttributeError:
                    pass

    def handle_gamemap_states(self) -> None:
        """
        Handles changes that occurs on gamemap.
        This functions should run once every player's turn.
        e.g. monster regeneration
        """
        self.game_map.respawn_monsters()

    def generate_new_dungeon(self, depth=1) -> GameMap:
        """Generate new dungeon and return as gamemap object"""
        # Set temporary context, console values to prevent cffi error
        temp_console, temp_context = self.console, self.context
        self.console, self.context = None, None

        # Select biome
        biome = choose_biome()

        # Generate dungeon
        new_dungeon = generate_dungeon(
            biome=biome,
            engine=self,
            depth=depth,
        )

        # Get console, context info back.
        self.console, self.context = temp_console, temp_context
        return new_dungeon

    def update_entity_in_sight(self, is_initialization=False) -> None:
        """
        Update informations about entities that are currently in player's sight.

        Args:
            is_initialization:
                When this function is called the first time right before the main game loop begins, this parameter is set to True.
        """

        # Copy the old data to prev_ variables (shallow copy)
        self.prev_actors_in_sight = copy.copy(self.actors_in_sight)
        self.prev_items_in_sight = copy.copy(self.items_in_sight)

        # Get new data
        for entity in self.game_map.entities:
            if self.game_map.visible[entity.x, entity.y]:
                if isinstance(entity, Actor):
                    self.actors_in_sight.add(entity)
                elif isinstance(entity, Item):
                    self.items_in_sight.add(entity)

        # If the function is called for the first time, copy data and set prev_ variables again since it was set to nothing before.
        if is_initialization:
            self.prev_actors_in_sight = copy.copy(self.actors_in_sight)
            self.prev_items_in_sight = copy.copy(self.items_in_sight)

    def update_fov(self) -> None:
        """Recompute the visible area based on the players point of view."""
        temp_vision = copy.copy(self.game_map.tiles["transparent"])

        for entity in self.game_map.entities:
            if entity.blocks_sight:
                temp_vision[entity.x, entity.y] = False

        self.game_map.visible[:] = compute_fov(
            temp_vision,
            (self.player.x, self.player.y),
            radius=self.player.status.changed_status["eyesight"],
        )

        # If a tile is "visible" it should be added to "explored".
        self.game_map.explored |= self.game_map.visible

    def update_enemy_fov(self, is_initialization: bool = False) -> None:
        """
        Recomputes the vision of actors besides player.
        This function is called every turn, but the actual update might not be called every turn due to perf. issues.
        """
        for actor in set(self.game_map.actors):
            # initialize actors vision
            if is_initialization:
                if actor.ai:
                    actor.ai.vision = np.full(
                        (self.game_map.width, self.game_map.height),
                        fill_value=False,
                        order="F")
                    actor.ai.update_vision()

            ## The game will not update every actor's vision every turn due to performance issues
            # actor.ai.update_vision()

    def set_player_path(self,
                        dest_x: int,
                        dest_y: int,
                        ignore_unexplored: bool = True,
                        ignore_dangerous_tiles: bool = True,
                        ignore_blocking_entities: bool = True,
                        ignore_semiactors: bool = True) -> None:
        """
        This function sets the player's path when mouse movement is used.

        ### FLOW
        # Set the cost of certain tiles to 1(True) if the tile satisfies the following conditions.
        # explored == True, walkable == True, safe_to_walk == True
        ###
        """
        # Copy the walkable array. (All walkable tiles are set to 1)
        cost = np.array(self.game_map.tiles["walkable"], dtype=np.int8)

        # 1. Set unexplored tiles' cost to 0
        # TODO : using the numpy mask might increase the performance here.
        if ignore_unexplored:
            not_explored_coordinates = zip(*np.where(
                self.game_map.explored[:, :] == False))

            for cor in not_explored_coordinates:
                # Even if the position clicked if currently unexplored, the game will generate path and allow player to move to that tile
                # UNLESS the path contains other unexplored tiles.
                # This is due to convenience, and other touch/click driven moving roguelikes like pixel dungeon uses similar mechanics.
                # TODO: This mechanics can be improved.
                if cor[0] == dest_x and cor[1] == dest_y:
                    continue
                cost[cor] = 0

        # 2. Set safe_to_walk=False tiles' cost to 0
        if ignore_dangerous_tiles:
            dangerous_coordinates = zip(*np.where(
                self.game_map.tiles["safe_to_walk"][:, :] == False))

            for cor in dangerous_coordinates:
                # If the player is already on a dangerous tile, ignore same types of tile and exclude them from "dangerous coordinates".
                # The reaseon for this is convenience.
                # For example, when the player is in the middle of ocean, the player is most likely to be standing on the deep water tile.(which is considered "dangerous tile")
                # When player click somewhere else to get out of the ocean, the game will not generate any path because the player is surrounded by dangerous tiles(deep water).
                # However by excluding all deep water tiles from "dangerous tile" temporarily, the player can now get out of the ocean by clicking somewhere else.
                if self.game_map.tiles[cor]["tile_id"] == self.game_map.tiles[
                        self.player.x, self.player.y]["tile_id"]:
                    continue

                cost[cor] = 0

        # 3. Check for entities
        for parent in self.game_map.entities:
            if ignore_blocking_entities:
                # If there is any entity(that blocks) on the tile, try to exclude that tile from path generation if possible.
                # NOTE: However, we ignore the entity that is at the destination of the mouse click.
                if parent.blocks_movement and cost[parent.x, parent.y]:
                    if parent.x != dest_x or parent.y != dest_y:
                        cost[parent.x, parent.y] += 100

            if ignore_semiactors:
                # Exclude tiles that has a dangerous semiactor on it.
                if isinstance(parent, SemiActor):
                    if (parent.x != dest_x
                            or parent.y != dest_y) and not parent.safe_to_move:
                        cost[parent.x, parent.y] = 0

        # Create a graph from the cost array and pass that graph to a new pathfinder.
        graph = SimpleGraph(cost=cost, cardinal=2, diagonal=3, greed=1)
        pathfinder = Pathfinder(graph)

        # Set start position
        pathfinder.add_root((self.player.x, self.player.y))

        # Compute the path to the destination and remove the starting point.
        path: List[List[int]] = pathfinder.path_to(
            (dest_x, dest_y))[1:].tolist()

        # Save player paths
        for index in path:
            self.player_path.appendleft((index[0], index[1]))

    def do_player_queue_actions(self) -> None:
        """
        The game will automatically do an action for the player based on the player_path, player_dir information.
        """
        ### A. If player path exist (=if the player clicked a tile that is at least 2 tiles away)
        if self.player_path:

            # Check if there is any new actor spotted in sight
            if self.prev_actors_in_sight != self.actors_in_sight:
                # If so, stop the movement.
                # TODO: Add an option to not stop?
                # TODO: Add a feature to stop ONLY if the actor is hostile to player?
                self.update_entity_in_sight(
                )  # update actors only when it's necessary
                self.player_path = deque([])
                return False

            dest_xy = self.player_path[-1]
            dx = dest_xy[0] - self.player.x
            dy = dest_xy[1] - self.player.y

            # Check for semiactors
            collided_semiactor = self.game_map.get_semiactor_at_location(
                dest_xy[0], dest_xy[1])
            if collided_semiactor:
                if collided_semiactor.entity_id == "closed_door":
                    # We do not pop from the path, because the player has to first open the door and then move to the tile.
                    BumpAction(self.player, dx, dy).perform()
                    return True
            if self.game_map.get_actor_at_location(dest_xy[0], dest_xy[1]):
                # If the monster is in the way, the game automatically attacks it.
                # After the attack the game will delete the player's path.
                BumpAction(self.player, dx, dy).perform()
                self.player_path = deque([])
                return True
            else:
                # If something unexpectedly blocks the way, perform BumpAction and delete all paths.
                #NOTE: This part of the code is meant to prevent unexpected circumstances crashing the game.
                # So be aware that issues can be ignored here.
                try:
                    BumpAction(self.player, dx, dy).perform()
                    self.player_path.pop()
                    return True
                except Exception as e:
                    print(f"DEBUG::{e}")
                    self.player_path = deque([])
                    return False

        ### B. If the player clicked one of nearby tiles (including the tile player is at)
        elif self.player_dir:

            # B-1. Clicked the tile that player is currently at
            if self.player_dir == (0, 0):
                # Check for descending stairs
                if self.game_map.tiles[
                        self.player.x,
                        self.player.y]["tile_id"] == "descending_stair":
                    try:
                        DescendAction(entity=self.player).perform()
                        self.player_dir = None
                        return True
                    except exceptions.Impossible as exc:
                        self.message_log.add_message(exc.args[0],
                                                     color.impossible)
                        self.player_dir = None
                        return False
                    except:
                        traceback.print_exc()
                        self.message_log.add_message(traceback.format_exc(),
                                                     color.error)
                        self.player_dir = None
                        return False
                # Check for ascending stairs
                elif self.game_map.tiles[
                        self.player.x,
                        self.player.y]["tile_id"] == "ascending_stair":
                    try:
                        AscendAction(entity=self.player).perform()
                        self.player_dir = None
                        return True
                    except exceptions.Impossible as exc:
                        self.message_log.add_message(exc.args[0],
                                                     color.impossible)
                        self.player_dir = None
                        return False
                    except:
                        traceback.print_exc()
                        self.message_log.add_message(traceback.format_exc(),
                                                     color.error)
                        self.player_dir = None
                        return False
                # If there is no stair, check for items.
                # TODO: What if the item is dropped on the stair tile?
                else:
                    try:
                        PickupAction(entity=self.player).perform()
                        self.player_dir = None
                        return True
                    except exceptions.Impossible as exc:
                        self.message_log.add_message(exc.args[0],
                                                     color.impossible)
                        self.player_dir = None
                        return False
                    except:
                        traceback.print_exc()
                        self.message_log.add_message(traceback.format_exc(),
                                                     color.error)
                        self.player_dir = None
                        return False

            # B-2. Clicked one of the nearby tile
            else:
                try:
                    BumpAction(self.player, self.player_dir[0],
                               self.player_dir[1]).perform()
                    self.player_dir = None
                    return True
                except exceptions.Impossible as exc:
                    self.message_log.add_message(exc.args[0], color.impossible)
                    self.player_dir = None
                    return False
                except:
                    traceback.print_exc()
                    self.message_log.add_message(traceback.format_exc(),
                                                 color.error)
                    self.player_dir = None
                    return False

        return True  # Turn passes after every actions like normal

    def refresh_screen(self) -> None:
        """Refresh current console and apply it to current context."""
        self.render(self.console)
        self.context.present(self.console)

    def render_playerinfo(self,
                          console: Console,
                          gui_x: int,
                          gui_y: int,
                          draw_frame: bool = False) -> None:
        """
        Handles the GUI about players status.
        """
        render_character_name(console=console,
                              x=gui_x,
                              y=gui_y,
                              character=self.player)

        render_health_bar(
            console=console,
            x=gui_x,
            y=gui_y + 2,
            current_value=self.player.status.hp,
            maximum_value=self.player.status.max_hp,
            total_width=26,
        )

        render_mana_bar(
            console=console,
            x=gui_x,
            y=gui_y + 3,
            current_value=self.player.status.mp,
            maximum_value=self.player.status.max_mp,
            total_width=26,
        )

        render_character_status(console=console,
                                x=gui_x,
                                y=gui_y + 5,
                                character=self.player)

        render_character_state(console=console,
                               x=gui_x,
                               y=gui_y + 14,
                               character=self.player)

        if draw_frame:
            # If the border goes across the game screen it will not be displayed.
            # TODO: Most of the values are hard-coded.

            # border for status gui
            console.draw_frame(x=gui_x - 1,
                               y=gui_y - 1,
                               width=28,
                               height=15,
                               title="Player Status",
                               clear=False,
                               fg=(255, 255, 255),
                               bg=(0, 0, 0))
            # border for state gui
            console.draw_frame(x=gui_x - 1,
                               y=gui_y + 14,
                               width=28,
                               height=8,
                               title="Status Effect",
                               clear=False,
                               fg=(255, 255, 255),
                               bg=(0, 0, 0))

    def render_gui(self, console: Console) -> None:
        """
        Handles rendering all the graphical user interfaces.
        """
        # TODO: Most of the values are hard-coded.

        self.message_log.render(console=console,
                                x=1,
                                y=48,
                                width=70,
                                height=9,
                                draw_frame=True)
        render_gameinfo(console=console,
                        x=1,
                        y=58,
                        depth=self.depth,
                        game_turn=self.game_turn)
        self.render_playerinfo(console=console,
                               gui_x=73,
                               gui_y=1,
                               draw_frame=True)

    def render(self, console: Console) -> None:
        """
        Handles rendering everything from the game.
        """
        self.camera.adjust()
        self.camera.render(console, draw_frame=True)
        self.render_gui(console=console)

        render_names_at_mouse_location(console=console, x=3, y=46, engine=self)
示例#11
0
class Engine:
    game_maps: List[GameMap]

    def __init__(self,
                 player: Actor):
        self.event_handler: EventHandler = MainGameEventHandler(self)
        self.message_log = MessageLog()
        self.mouse_location = (0, 0)
        self.player = player
        self.map_index = 0
        self.game_maps = []
        self.turn_queue = TurnQueue()
        # For the sake of laziness
        self.schedule = self.turn_queue.schedule

    def handle_enemy_turns(self) -> None:
        while True:
            # print(self.turn_queue)
            ticket = self.turn_queue.next()

            entity_to_act = ticket.value
            if entity_to_act is self.player:
                return
            elif entity_to_act.ai:
                # I took out the Impossible exception part...
                entity_to_act.ai.perform()


    @property
    def game_map(self) -> Optional[GameMap]:
        """ Maps are accessed by using an index. """
        try:
            return self.game_maps[self.map_index]
        except IndexError:
            return None

    @game_map.setter
    def game_map(self, new_map) -> None:
        """ Either we switch to an already existing map, or we add it to the list. """
        try:
            map_index = self.game_maps.index(new_map)
        except ValueError:  # game not in list already
            self.game_maps.append(new_map)
            map_index = len(self.game_maps) - 1
        self.map_index = map_index

    def update_fov(self) -> None:
        """ Recompute visible area based on player's point of view. """
        self.game_map.visible[:] = compute_fov(
            transparency=self.game_map.tiles["transparent"],
            pov=(self.player.x, self.player.y),
            radius=8,
        )
        # If a tile is visible, it should be marked as explored.
        self.game_map.explored |= self.game_map.visible

    def render(self, console: Console) -> None:
        self.game_map.render(console)

        self.message_log.render(console=console, x=21, y=45, width=40, height=5)

        render_bar(
            console=console,
            current_value=self.player.fighter.hp,
            maximum_value=self.player.fighter.max_hp,
            total_width=20,
        )

        render_names_at_mouse_location(console=console, x=21, y=44, engine=self)
示例#12
0
文件: engine.py 项目: voynix/7drl
class Engine:
    game_map: GameMap
    game_world: GameWorld

    def __init__(self, player: Actor, viewport_width: int, viewport_height: int):
        self.message_log = MessageLog()
        self.mouse_location = (0, 0)  # in viewport-space
        self.player = player
        self.viewport_width = viewport_width
        self.viewport_height = viewport_height
        self.debug_mode = False

    def handle_enemy_turns(self) -> None:
        for entity in set(self.game_map.actors) - {self.player}:
            if entity.ai:
                try:
                    entity.ai.perform()
                except exceptions.Impossible:
                    pass  # ignore impossible actions

    def update_fov(self) -> None:
        """Recompute the visible area based on the player POV"""
        self.game_map.visible[:] = compute_fov(
            self.game_map.tiles["transparent"],
            (self.player.x, self.player.y),
            radius=PLAYER_FOV_RADIUS,
        )
        self.game_map.explored |= self.game_map.visible

    def render(self, console: Console) -> None:
        self.game_map.render(console, self.viewport_width, self.viewport_height)

        self.message_log.render(console=console, x=21, y=45, width=40, height=5)

        render_functions.render_bar(
            console=console,
            current_value=self.player.fighter.hp,
            maximum_value=self.player.fighter.max_hp,
            total_width=20,
        )

        render_functions.render_dungeon_level(
            console=console,
            dungeon_level=self.game_world.current_floor,
            location=(0, 47),
        )

        if self.debug_mode:
            render_functions.render_graphics_debug(
                console=console,
                player=self.player,
                game_map=self.game_map,
                location=(0, 48),
            )

        render_functions.render_names_at_mouse_location(
            console=console, x=21, y=44, engine=self
        )

    def save_as(self, filename: str) -> None:
        save_data = lzma.compress(pickle.dumps(self))
        with open(filename, "wb") as f:
            f.write(save_data)
示例#13
0
class Engine:
    game_map: GameMap
    game_world: GameWorld

    def __init__(self, player: Actor):
        """ Engine sørger for game logic

         Args:
            entities (Set[Entity]): *Unordered list of unique items.*
            event_handler (EventHandler): Gi'r jo sig selv.
            player (Entity): Godt nok, med lykke og held.
        """
        self.message_log = MessageLog()
        self.mouse_location = (0, 0)
        self.player = player

    def handle_enemy_turns(self):
        for entity in set(self.game_map.actors) - {self.player}:
            if entity.ai:
                try:
                    entity.ai.perform()
                except exceptions.Impossible:
                    pass

    def update_fov(self):
        """ Opdater `game_map` baseret på spillerens FOV"""
        self.game_map.visible[:] = compute_fov(
            self.game_map.tiles['transparent'],
            (self.player.x, self.player.y),
            radius=8,
        )
        # Hvis en `tile` er synlig, sæt den til `explored`
        self.game_map.explored |= self.game_map.visible

    def render(self, console: Console):
        """Tegner konsolen"""
        self.game_map.render(console)

        self.message_log.render(
            console=console,
            x=21,
            y=45,
            width=40,
            height=5,
        )

        render_functions.render_bar(
            console=console,
            current_value=self.player.fighter.hp,
            maximum_value=self.player.fighter.max_hp,
            total_width=20,
        )

        render_functions.render_dungeon_level(
            console=console,
            dungeon_level=self.game_world.current_floor,
            location=(0, 47),
        )

        render_functions.render_names_at_mouse_location(console,
                                                        x=21,
                                                        y=44,
                                                        engine=self)

    def save_as(self, filename: str) -> None:
        """Save this Engine instance as a compressed file."""
        save_data = lzma.compress(pickle.dumps(self))
        with open(filename, 'wb') as f:
            f.write(save_data)
示例#14
0
class Engine:
    game_map: GameMap
    game_world: GameWorld

    def __init__(
        self, player: Actor,
            camera_width: int,
            camera_height: int,
            map_width: int,
            map_height: int
    ):
        self.message_log = MessageLog()
        self.mouse_location = (0, 0)
        self.player = player
        self.camera = Camera(0, 0, camera_width, camera_height, map_width, map_height)
        self.camera.update(player)

    def handle_enemy_turns(self) -> None:
        for entity in set(self.game_map.actors) - {self.player}:
            if entity.ai:
                try:
                    entity.ai.perform()
                except exceptions.Impossible:
                    pass  # Ignore impossible action exceptions from AI.

    def toggle_fullscreen(self) -> None:
        import g
        if g.context is not None:
            if not g.context.sdl_window_p:
                return
            fullscreen = tcod.lib.SDL_GetWindowFlags(g.context.sdl_window_p) & (
                    tcod.lib.SDL_WINDOW_FULLSCREEN | tcod.lib.SDL_WINDOW_FULLSCREEN_DESKTOP
            )
            tcod.lib.SDL_SetWindowFullscreen(
                g.context.sdl_window_p,
                0 if fullscreen else tcod.lib.SDL_WINDOW_FULLSCREEN_DESKTOP,
            )
        else:
            print("No global context set, cannot toggle fullscreen")

    def update_fov(self) -> None:
        """Recompute the visible area based on the players point of view."""
        self.game_map.visible[:] = compute_fov(
            self.game_map.tiles["transparent"],
            (self.player.x, self.player.y),
            radius=8,
        )
        # If a tile is "visible" it should be added to "explored".
        self.game_map.explored |= self.game_map.visible

    def render(self, console: Console) -> None:
        self.game_map.render(console, self.camera)

        self.message_log.render(console=console, x=21, y=45, width=40, height=5)

        render_functions.render_bar(
            console=console,
            current_value=self.player.fighter.hp,
            maximum_value=self.player.fighter.max_hp,
            total_width=20,
        )

        render_functions.render_dungeon_level(
                console=console,
                dungeon_level=self.game_world.current_floor,
                location=(0, 47),
                )

        render_functions.render_names_at_mouse_location(
            console=console, x=21, y=44, engine=self
        )

    def save_as(self, filename: str) -> None:
        """Save this Engine instance as a compressed file."""
        save_data = lzma.compress(pickle.dumps(self))
        with open(filename, "wb") as f:
            f.write(save_data)