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 __init__( self, player: Actor, ): self.message_log = MessageLog() self.mouse_location = (0, 0) self.player = player
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()
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)
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)
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
def __init__( self, player: Actor, ): self.event_handler: EventHandler = MainGameEventHandler(self) self.message_log = MessageLog() self.mouse_location = (0, 0) self.player = player
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()
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))
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)
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 __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 __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 __init__(self, player: Actor): self.PLAYER = player self.message_log = MessageLog() self.eventhandler: event_handlers.EventHandler = event_handlers.MainGameEventHandler( self) self.mouse_position = (0, 0) self.render_engine = RenderEngine()
def get_game_variables(): player = Entity(0, 0, '@', tcod.black, 'Player', True, render_order=RenderOrder.ACTOR, inventory=component("INVENTORY"), equipment=component("EQUIPMENT"), purse=component("PURSE"), fighter=component("PLAYER")) entities = [player] game_map = GameMap(CONFIG.get('MAP_WIDTH'), CONFIG.get('MAP_HEIGHT')) game_map.make_map( CONFIG.get('MAX_ROOMS'), CONFIG.get('ROOM_MIN_SIZE'), CONFIG.get('ROOM_MAX_SIZE'), player, entities, CONFIG.get('MAX_MONSTERS'), CONFIG.get('MAX_ITEMS'), component) message_log = MessageLog(CONFIG.get("MESSAGE_X"), CONFIG.get("MESSAGE_WIDTH"), CONFIG.get("MESSAGE_HEIGHT")) game_state = GameStates.INSTRUCTIONS return player, entities, game_map, message_log, game_state
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)
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)
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
def __init__(self, player: Starship, filename: str, easy_aim: bool, easy_navigation: bool, easy_warp:bool, torpedo_warning: bool, crash_warning: bool ): self.message_log = MessageLog() self.mouse_location = (0, 0) self.player = player self.screen_width = CONFIG_OBJECT.screen_width self.screen_height = CONFIG_OBJECT.screen_height self.easy_aim = easy_aim self.easy_navigation = easy_navigation self.easy_warp = easy_warp self.torpedo_warning = torpedo_warning self.crash_warning = crash_warning self.filename = filename self.lookup_table:Dict[Coords,Tuple[Coords]] = {}
def main(): tdl.set_font('terminal16x16.png', greyscale=True, altLayout=False) # Load the font from a png. tdl.set_fps(100) map_width = 20 map_height = 20 room_width = 30 room_height = 30 screen_width = room_width + 22 screen_height = room_height + 22 root_console = tdl.init(screen_width, screen_height, title='7DRL 2019') top_panel_console = tdl.Console(screen_width, 10) view_port_console = tdl.Console(room_width, room_height) bottom_panel_console = tdl.Console(screen_width, 10) message_log = MessageLog(0, 0, screen_width, 9) entities = [] game_map = GameMap(map_width, map_height) sword_stats = Weapon(2, 10) player_weapon = Item(game_map, "0x0", 0, 0, "Sword", "|", (255, 255, 255), weapon=sword_stats) player_stats = Fighter(hits=10, left_hand=player_weapon) player = Actor(game_map, "2x2", 15, 10, "Player", "@", (255, 255, 255), fighter=player_stats) entities.append(player) generate_map(game_map, entities, player) all_consoles = [ root_console, view_port_console, bottom_panel_console, top_panel_console ] fov_algorithm = "BASIC" fov_light_walls = True fov_radius = 50 fov_recompute = True game_state = GameStates.PLAYER_TURN while not tdl.event.is_window_closed(): if fov_recompute: # Compute the field of view to show changes. game_map.rooms[player.map_x][player.map_y]\ .compute_fov(player.room_x, player.room_y, fov=fov_algorithm, radius=fov_radius, light_walls=fov_light_walls, sphere=True) render_all(all_consoles, game_map, entities, player, fov_recompute, message_log) tdl.flush() clear_all(view_port_console, entities) fov_recompute = False for event in tdl.event.get(): if event.type == 'KEYUP': user_input = event break else: user_input = None if not user_input: continue action = handle_keys(user_input) move = action.get('move') exit_game = action.get('exit_game') select_hand = action.get('select_hand') drop_item = action.get('drop_item') pickup_item = action.get('pickup_item') shuffle_rooms = action.get('shuffle_rooms') player_turn_results = [] if shuffle_rooms: message = game_map.shuffle_rooms(player, entities) message_log.add_message(message) fov_recompute = True # TODO at the moment these functions are doing all the leg work and player_turn_results isn't used. Rectify. if select_hand and game_state == GameStates.PLAYER_TURN: player.fighter.selected_hand = select_hand fov_recompute = True if drop_item and game_state == GameStates.PLAYER_TURN: message = player.fighter.drop_item(game_map, entities) message_log.add_message(message) game_state = GameStates.ENEMY_TURN fov_recompute = True if pickup_item and game_state == GameStates.PLAYER_TURN: message = player.fighter.pickup_item(entities) message_log.add_message(message) game_state = GameStates.ENEMY_TURN fov_recompute = True if move and game_state == GameStates.PLAYER_TURN: dx, dy = move destination_room_x = player.room_x + dx destination_room_y = player.room_y + dy if destination_room_x < 0: dx = 0 if player.map_x - 1 < 0: player.map_x = map_width - 1 else: player.map_x -= 1 player.room_x = room_width - 1 if destination_room_x == room_width: destination_room_x -= 1 dx = 0 if player.map_x + 1 > map_width - 1: player.map_x = 0 else: player.map_x += 1 player.room_x = 0 if destination_room_y < 0: dy = 0 if player.map_y - 1 < 0: player.map_y = map_height - 1 else: player.map_y -= 1 player.room_y = room_height - 1 if destination_room_y == room_height: destination_room_y -= 1 dy = 0 if player.map_y + 1 > map_height - 1: player.map_y = 0 else: player.map_y += 1 player.room_y = 0 if game_map.rooms[player.map_x][player.map_y].walkable[ destination_room_x, destination_room_y]: target = get_blocking_entities_at_location( entities, player.map_x, player.map_y, destination_room_x, destination_room_y) if target: # Combat here attack_results = player.fighter.attack(target) player_turn_results.extend( attack_results ) # Add the result of the last turn to the list fov_recompute = True else: player.move(dx, dy) # Or just move player.set_current_room(game_map) # TODO: Bug with the new room layouts - some cause change of room to break. print("MapPos: ", player.map_x, ",", player.map_y, end=" / ") print("RoomPos: ", player.room_x, ",", player.room_y) fov_recompute = True game_state = GameStates.ENEMY_TURN # Switch over the enemy's turn. if exit_game: return True for player_turn_result in player_turn_results: message = player_turn_result.get("message") # Pull any messages dead_entity = player_turn_result.get( "dead") # Or anything that died if message: message_log.add_message( message ) # Add the message (if any) to the message log to print on screen. if dead_entity: # If anything died... if dead_entity == player: message, game_state = kill_player(dead_entity) # Game over else: message = kill_monster( dead_entity ) # Print a death message for monster, add exp message_log.add_message(message) # Print messages to screen. player_turn_results.clear() # Clear ready for next turn. if game_state == GameStates.ENEMY_TURN: for entity in entities: if entity.map_x == player.map_x and entity.map_y == player.map_y: if entity.ai: # If the entity has some intelligence (monsters, npc) enemy_turn_results = entity.ai.take_turn( player, game_map, entities) for enemy_turn_result in enemy_turn_results: # Same deal as the player turns 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 enemy_turn_results.clear() if game_state == GameStates.PLAYER_DEAD: break else: game_state = GameStates.PLAYER_TURN
if 'dialog' in o: dialog = o['dialog'] else: dialog = 'default' return NPC(o['x'], o['y'], o['char'], o['color'], name, dialog) ##TODO: abstract, automate init terminal.open() terminal.printf(1, 1, 'Hello, world!') terminal.refresh() terminal.set("window: size=" + str(screen_width) + "x" + str(screen_height) + ";") run = True ml = MessageLog(dialog_width, dialog_height) test_count = 0 game_objects = [] dialog_entities = [] player = GameObject(3, 3, '@', 'red', "Hero", True) player.inventory = {} ##Keep track of which direction player is pointing, start up. player.last_dx = 0 player.last_dy = -1 game_objects.append(player) add_to_inventory(player.inventory, InventoryItem("Goblet")) add_to_inventory(player.inventory, InventoryItem("Replacement Plugs", 11))
def __init__(self, player: Actor): self.message_log = MessageLog() self.mouse_location = ( 0, 0) # здесь ханится информация о местонахождении мыши self.player = player
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)
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)
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)
def __init__(self, player: Actor): self.message_log = MessageLog() self.player = player self.graphics_component = GraphicsFrame(self)
game_map = GameMap(SCREEN_WIDTH, SCREEN_HEIGHT) game_map.generate(player, entities, max_monsters_per_room=3, room_max_size=12, room_min_size=6, max_rooms=75) game_map.genMainTunnels() game_map.genDoors() fov_algorithm = 0 fov_light_walls = True fov_radius = 10 fov_recompute = True message_log = MessageLog(22, SCREEN_WIDTH - 22, PANEL_HEIGHT) fov_map = initialize_fov(game_map) game_state = GameStates.PLAYERS_TURN colors = { 'dark_wall': tcod.Color(2, 2, 2), 'dark_ground': tcod.Color(50, 50, 50), 'light_wall': tcod.Color(120, 120, 120), 'light_ground': tcod.Color(200, 200, 200), 'black': tcod.Color(200, 200, 200) } def main():
def __init__(self, player: Actor): self.message_log = MessageLog() self.mouse_location = (0, 0) self.player = player self.player.encountered = True
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)
def __init__(self, player: Actor): self.message_log: MessageLog = MessageLog() self.mouse_location: Tuple[int, int] = (0, 0) self.player: Actor = player