def handle_keys(key): # if key is None: # if terminal.has_input(): # key = terminal.read() if key == terminal.TK_H or key == terminal.TK_KP_4 or key == terminal.TK_LEFT: return {"move": Point(-1, 0)} elif key == terminal.TK_L or key == terminal.TK_KP_6 or key == terminal.TK_RIGHT: return {"move": Point(1, 0)} elif key == terminal.TK_K or key == terminal.TK_KP_8 or key == terminal.TK_UP: return {"move": Point(0, -1)} elif key == terminal.TK_J or key == terminal.TK_KP_2 or key == terminal.TK_DOWN: return {"move": Point(0, 1)} elif key == terminal.TK_Y or key == terminal.TK_KP_7: return {"move": Point(-1, -1)} elif key == terminal.TK_U or key == terminal.TK_KP_9: return {"move": Point(1, -1)} elif key == terminal.TK_B or key == terminal.TK_KP_1: return {"move": Point(-1, 1)} elif key == terminal.TK_N or key == terminal.TK_KP_3: return {"move": Point(1, 1)} elif key == terminal.TK_PERIOD or key == terminal.TK_KP_0 or key == terminal.TK_KP_PERIOD: return {"move": Point(0, 0)} elif key == terminal.TK_ESCAPE or key == terminal.TK_CLOSE: return {"exit": True} elif key == terminal.TK_R: return {"rebuild": True} return {}
def __init__(self, player: Entity, width: int = None, height: int = None): if width is None: width: int = CAMERA_WIDTH if height is None: height: int = CAMERA_HEIGHT super(Camera, self).__init__(position=Point(0, 0), width=width, height=height) self.center = player.position self.player: Entity = player self._fov_update: bool = True self.ui_panel: Panel = Panel(position=Point(0, 0), width=width, height=height - 5)
def points(self) -> Set[Point]: points: Set[Point] = set() for i in range(self.height): for j in range(self.width): point: Point = Point(x=j, y=i) points.add(point) return points
def new_game(cls) -> Game: game = cls() game.map_generator: Optional[MapGenerator] = MapGenerator( map_width=CONSTANTS.map_width, map_height=CONSTANTS.map_height) game.current_state: Optional[GameStates] = GameStates.PLAYER_TURN game.previous_state: Optional[GameStates] = GameStates.PLAYER_TURN fighter_component: Fighter = Fighter( hp=CONSTANTS.player_hp, defense=CONSTANTS.player_defense, power=CONSTANTS.player_power, ) inventory_component: Inventory = Inventory(capacity=26) level_component = Level() equipment_component = Equipment() player: Optional[Entity] = Entity(position=Point(0, 0), char="@", color=Colors.WHITE, name="Player", blocks=True, render_order=RenderLayer.ACTOR, fighter=fighter_component, inventory=inventory_component, level=level_component, equipment=equipment_component) game.player: Optional[Entity] = player game.entities: Optional[List[Entity]] = [player] equippable_component = Equippable(slot=EquipmentSlots.MAIN_HAND, power_bonus=2) dagger = Entity(position=Point(0, 0), char="-", color=Colors.SKY, name="Dagger", equippable=equippable_component) game.player.inventory.add_item(dagger) game.player.equipment.toggle_equip(dagger) game.message_log: Optional[MessageLog] = MessageLog( x=CONSTANTS.message_x, width=CONSTANTS.message_width, height=CONSTANTS.message_height, ) game.game_running: bool = True game.camera: Optional[Camera] = Camera(player=game.player) return game
def __iter__(self) -> Iterator["camera_view"]: camera_view = namedtuple("camera_view", ["x", "y", "map_point"]) for i in range(self.height): for j in range(self.width): view = camera_view(x=j, y=i, map_point=Point(x=self.x + j, y=self.y + i)) yield view
def initialize_fov(game_map: GameMap): for y in range(game_map.height): for x in range(game_map.width): point = Point(x, y) tile: Tile = game_map.tile(point) assert game_map.transparent[x, y] == tile.transparent assert game_map.walkable[x, y] == tile.walkable
def center(self) -> Point: x = round(self.width / 2, 0) y = round(self.height / 2, 0) return Point(int(x), int(y))
def bottom_right(self) -> Point: return Point(self.x + self.width - 1, self.y + self.height - 1)
def bottom_left(self) -> Point: return Point(self.x, self.y + self.height - 1)
def top_right(self, value: Point): if value != self.top_right: self.position = Point(value.x - self.width, value.y)
def top_right(self) -> Point: return Point(self.x + self.width - 1, self.y)
def player() -> Entity: e: Entity = Entity(position=Point(x=3, y=3), char="@", color=Color.WHITE) return e
def play_game(game: Game): game.fov_map: tcod.map.Map = initialize_fov(game.game_map) game.game_state: GameStates = GameStates.PLAYER_TURN game.previous_state: GameStates = game.game_state game.camera: Camera = Camera(player=game.player, width=CONSTANTS.camera_width, height=CONSTANTS.camera_height) game.camera.fov_update: bool = True targeting_item: Optional[Entity] = None mouse_position: Point = Point(x=blt.state(blt.TK_MOUSE_X) // 2, y=blt.state(blt.TK_MOUSE_Y) // 2) while game.game_running: if game.camera.fov_update: recompute_fov( fov_map=game.fov_map, point=game.player.position, radius=CONSTANTS.fov_radius, light_walls=CONSTANTS.fov_light_walls, algorithm=CONSTANTS.fov_algorithm, ) render_all( entities=game.entities, player=game.player, game_map=game.game_map, fov_map=game.fov_map, camera=game.camera, message_log=game.message_log, ui_panel=CONSTANTS.ui_panel, bar_width=CONSTANTS.bar_width, mouse_position=mouse_position, game_state=game.game_state, ) game.camera.fov_update = False if blt.has_input(): terminal_input: int = blt.read() mouse_position: Point = Point(x=blt.state(blt.TK_MOUSE_X) // 2, y=blt.state(blt.TK_MOUSE_Y) // 2) action: dict = handle_keys(key=terminal_input, game_state=game.game_state) mouse_action: dict = handle_mouse(key=terminal_input) movement: Optional[Point] = action.get("move") wait: bool = action.get("wait", False) pickup: bool = action.get("pickup", False) show_inventory: bool = action.get("show_inventory", False) drop_inventory: bool = action.get("drop_inventory", False) inventory_index: Optional[int] = action.get("inventory_index") take_stairs: bool = action.get("take_stairs", False) level_up: str = action.get("level_up") show_character_screen: bool = action.get("show_character_screen", False) exit_action: bool = action.get("exit", False) left_click: Point = mouse_action.get("left_click") right_click: Point = mouse_action.get("right_click") player_turn_results: List = [] if movement and game.game_state == GameStates.PLAYER_TURN: destination = game.player.position + movement if not game.game_map.is_blocked(destination): target = get_blocking_entities_at_location( entities=game.entities, destination=destination) if target: attack_results = game.player.fighter.attack( target=target) player_turn_results.extend(attack_results) else: game.player.move(movement) game.camera.recenter() game.change_state(GameStates.ENEMY_TURN) elif wait: game.change_state(GameStates.ENEMY_TURN) elif pickup and game.game_state == GameStates.PLAYER_TURN: for entity in game.entities: if entity.item and entity.position == game.player.position: pickup_results = game.player.inventory.add_item(entity) player_turn_results.extend(pickup_results) break else: game.message_log.add_message( Message("There is nothing here to pick up.")) if show_inventory: game.change_state(GameStates.SHOW_INVENTORY) if drop_inventory: game.change_state(GameStates.DROP_INVENTORY) if (inventory_index is not None and game.previous_state != GameStates.PLAYER_DEAD and inventory_index < len(game.player.inventory.items)): item = game.player.inventory.items[inventory_index] if game.game_state == GameStates.SHOW_INVENTORY: player_turn_results.extend( game.player.inventory.use(item, entities=game.entities, fov_map=game.fov_map)) elif game.game_state == GameStates.DROP_INVENTORY: player_turn_results.extend( game.player.inventory.drop_item(item)) if take_stairs and game.game_state == GameStates.PLAYER_TURN: for entity in game.entities: if entity.stairs and entity.position == game.player.position: game.next_floor() game.fov_map = initialize_fov(game.game_map) game.camera.fov_update = True break else: game.message_log.add_message( Message("There are no stairs here.", Colors.YELLOW)) if level_up: if level_up == "hp": game.player.fighter.base_max_hp += 20 game.player.fighter.hp += 20 elif level_up == "str": game.player.fighter.base_power += 1 elif level_up == "dex": game.player.fighter.base_defense += 1 game.change_state(game.previous_state) if show_character_screen: game.change_state(GameStates.CHARACTER_SCREEN) if game.game_state == GameStates.TARGETING: if left_click: target_position: Point = game.camera.map_point(left_click) item_use_results = game.player.inventory.use( targeting_item, entities=game.entities, fov_map=game.fov_map, target_position=target_position, ) player_turn_results.extend(item_use_results) elif right_click: player_turn_results.append({"targeting_cancelled": True}) if exit_action: if game.game_state in (GameStates.SHOW_INVENTORY, GameStates.DROP_INVENTORY, GameStates.CHARACTER_SCREEN): game.change_state(GameStates.PLAYER_TURN) elif game.game_state == GameStates.TARGETING: player_turn_results.append({"targeting_cancelled": True}) else: game.save_game() game.game_running = False for player_turn_result in player_turn_results: message: Optional[Message] = player_turn_result.get("message") dead_entity: Optional[Entity] = player_turn_result.get("dead") item_added: Optional[Entity] = player_turn_result.get( "item_added") item_consumed: Optional[Entity] = player_turn_result.get( "consumed") item_dropped: Optional[Entity] = player_turn_result.get( "item_dropped") equip: Optional[Entity] = player_turn_result.get("equip") targeting: Optional[Entity] = player_turn_result.get( "targeting") xp: Optional[int] = player_turn_result.get("xp") targeting_cancelled: bool = player_turn_result.get( "targeting_cancelled", False) if message: game.message_log.add_message(message) if targeting_cancelled: game.game_state = game.previous_state game.message_log.add_message( Message("Targeting cancelled")) if xp: leveled_up = game.player.level.add_xp(xp=xp) game.message_log.add_message( Message(f"You gain {xp} experience points.")) if leveled_up: game.message_log.add_message( Message( f"Your battle skills grow stronger! You reached level {game.player.level.current_level}!", Colors.YELLOW)) game.change_state(GameStates.LEVEL_UP) if dead_entity: if dead_entity == game.player: message = kill_player(player=dead_entity) game.change_state(GameStates.PLAYER_DEAD) else: message = kill_monster(monster=dead_entity) game.message_log.add_message(message) if item_added: game.entities.remove(item_added) game.change_state(GameStates.ENEMY_TURN) game.camera.fov_update = True if item_consumed: game.change_state(GameStates.ENEMY_TURN) if targeting: game.change_state(GameStates.TARGETING) targeting_item: Entity = targeting game.message_log.add_message( targeting_item.item.targeting_message) if item_dropped: game.entities.append(item_dropped) game.change_state(GameStates.ENEMY_TURN) if equip: equip_results = game.player.equipment.toggle_equip(equip) for equip_result in equip_results: equipped: Optional[Entity] = equip_result.get( "equipped") dequipped: Optional[Entity] = equip_result.get( "dequipped") if equipped: game.message_log.add_message( Message(f"You equipped the {equipped.name}")) if dequipped: game.message_log.add_message( Message(f"You dequipped the {dequipped.name}")) game.change_state(GameStates.ENEMY_TURN) if game.game_state == GameStates.ENEMY_TURN: for entity in game.entities: if entity.ai: enemy_turn_results = entity.ai.take_turn( target=game.player, fov_map=game.fov_map, game_map=game.game_map, entities=game.entities, ) for enemy_turn_result in enemy_turn_results: message: Optional[Message] = enemy_turn_result.get( "message") dead_entity = enemy_turn_result.get("dead") if message: game.message_log.add_message(message) if dead_entity: if dead_entity == game.player: message = kill_player(player=dead_entity) game.change_state(GameStates.PLAYER_DEAD) else: message = kill_monster(monster=dead_entity) game.message_log.add_message(message) if game.game_state == GameStates.PLAYER_DEAD: break else: game.change_state(GameStates.PLAYER_TURN)
def view(self) -> dict: view = {} for x, y, point in iter(self): view[point] = Point(x, y) return view
def offset(self) -> Point: return Point(self.x_offset, self.y_offset)
def top_left(self) -> Point: return Point(self.x, self.y)
def position(self) -> Point: return Point(self.x - self.x_offset, self.y - self.y_offset)
def goblin() -> Entity: e: Entity = Entity(position=Point(x=5, y=5), char="g", color=Color.LIGHT_GREEN) return e
def map_points(self) -> Set[Point]: return { Point(x=j, y=i) for i in range(self.top, self.bottom + 1) for j in range(self.left, self.right + 1) }
def x(self, value: int): new_position = Point(self.x + value, self.y) self.position = new_position
def y(self, value): new_position = Point(self.x, self.y + value) self.position = new_position
def __iter__(self): for i in range(self.height): for j in range(self.width): yield Point(x=self.x + j, y=self.y + i)
def camera_view(self): view_point = namedtuple("view_point", ["x", "y", "map_point"]) for row, i in enumerate(range(self.top, self.bottom)): for col, j in enumerate(range(self.left, self.right)): yield view_point(x=col, y=row, map_point=Point(x=j, y=i))