def place_stairs(self): exit = find(lambda room: room.name == 'exit', self.current_level.rooms) if (exit): x, y = exit.center self.down_stairs = Entity(Point(x, y), '>', 'Stairs', COLORS.get('stairs'), render_order=RenderOrder.STAIRS, always_visible=True) self.down_stairs.add_component(Stairs(self.dungeon_level + 1), "stairs") self.current_level.add_entity(self.down_stairs) entrance = find(lambda room: room.name == 'entrance', self.current_level.rooms) if (entrance): x, y = entrance.center self.up_stairs = Entity(Point(x, y), '<', 'Stairs', COLORS.get('stairs'), render_order=RenderOrder.STAIRS, always_visible=True) self.up_stairs.add_component(Stairs(self.dungeon_level - 1), "stairs") self.current_level.add_entity(self.up_stairs)
def find_empty_space(self, distance: int) -> Point: for x in range(distance, self.width - distance): for y in range(distance, self.height - distance): touching = 0 for xi in range(-distance, distance): for yi in range(-distance, distance): if self.dungeon.label(Point(x + xi, y + yi)): touching += 1 if touching == 0: print(f"returning {Point(x, y)}") return Point(x, y) print("returning no point") return Point(-1, -1)
class Direction(CallableEnum): NW = Point(-1, 1) N = Point(0, 1) NE = Point(1, 1) W = Point(-1, 0) E = Point(1, 0) SW = Point(-1, -1) S = Point(0, -1) SE = Point(1, -1) @classmethod def cardinal(cls): """ Yields Point for the cardinal directions (N, E, S, W) :return: generator object of Point for each cardinal directions """ for direction in [cls.N, cls.E, cls.S, cls.W]: yield direction.value @classmethod def every(cls): for direction in cls: yield direction.value @staticmethod def self(): return Point(0, 0)
def tile(self, x: int, y: int) -> Tile: """ :param x: x-coordinate of tile :type x: int :param y: y-coordinate of tile :type y: int :return: Tile at coordinate (x, y) :rtype: Tile """ grids = self.dungeon.grids(Point(x, y)) tile = Tile.from_grid(Point(x, y), grids) return tile
def tick(self, owner, game_map): super().tick(owner, game_map) if not self.target_position or (self.target_position == owner.point): self.target_position = random_walkable_position(game_map, owner) path = self.namespace.get("path") blocking_entity = None if path and len(path) > 1: if not game_map.current_level.blocked[path[1][0], path[1][1]]: path.pop(0) return TreeStates.SUCCESS, [{ ResultTypes.MOVE_WITH_PATH: (owner, path) }] else: if self.target_position == Point(path[1][0], path[1][1]): self.target_position = None return TreeStates.SUCCESS, [] else: blocking_entity = (path[1][0], path[1][1]) path = get_path_to(game_map, (owner.x, owner.y), (self.target_position.x, self.target_position.y), routing_avoid=owner.movement.routing_avoid, blocking_entity=blocking_entity) self.namespace["path"] = path if len(path) < 1: self.target_position = None return TreeStates.SUCCESS, [] return TreeStates.SUCCESS, [{ ResultTypes.MOVE_WITH_PATH: (owner, path) }]
def tick(self, owner, game_map): super().tick(owner, game_map) self.current_time += 1 if self.current_time < self.min_time: return TreeStates.FAILURE, [] if owner.children and not owner.children.can_have_children: return TreeStates.FAILURE, [] if self.current_time > self.max_time or (uniform( 0, 1), (self.current_time / self.max_time)): x, y = random_adjacent((owner.x, owner.y)) if (game_map.current_level.walkable[x, y] and not game_map.current_level.blocked[x, y]): entity = self.maker(Point(x, y)) target = self.namespace.get("target") if target: entity.ai.set_target(target) results = [{ResultTypes.ADD_ENTITY: entity}] if self.hatch: results.append({ResultTypes.REMOVE_ENTITY: owner}) if owner.children: owner.children.addChild(entity) self.current_time = 0 return TreeStates.SUCCESS, results else: logging.info(f"{owner} can't spawn {self.maker} as no room.") return TreeStates.FAILURE, []
def tick(self, owner, game_map): super().tick(owner, game_map) if not self.namespace.get(self.name): raise ValueError(f"{self.name} is not in tree namespace!") point = self.namespace.get(self.name) path = self.namespace.get("path") if path and len(path) > 1 and (Point(path[0][0], path[0][1]) == owner.point): path.pop(0) if not game_map.current_level.blocked[path[0][0], path[0][1]]: return TreeStates.SUCCESS, [{ ResultTypes.MOVE_WITH_PATH: (owner, path) }] path = get_path_to(game_map, (owner.x, owner.y), (point.x, point.y), routing_avoid=owner.movement.routing_avoid) self.namespace["path"] = path if len(path) < 1: self.target_position = None return TreeStates.SUCCESS, [] return TreeStates.SUCCESS, [{ ResultTypes.MOVE_WITH_PATH: (owner, path) }]
def place_entities(self, player: Entity) -> List[Entity]: max_monsters_per_room = self.map_settings["max_monsters_per_room"] self.entities = [player] for room in self.rooms[1:]: number_of_monsters = randint(0, max_monsters_per_room) for i in range(number_of_monsters): x = randint(room.left, room.right) y = randint(room.top, room.bottom) point = Point(x, y) if not any([ entity for entity in self.entities if entity.position == point ]): if randint(0, 100) < 80: monster = Entity(name="goblin", position=point, char=Tiles.GOBLIN, blocks=True) else: monster = Entity(name="orc", position=point, char=Tiles.ORC, blocks=True) self.entities.append(monster) return self.entities
def from_center(cls, center: Point, width: int, height: int): width_offset = (width + 1) // 2 - 1 height_offset = (height + 1) // 2 - 1 top_left = Point(center.x - width_offset, center.y - height_offset) return Rect(position=top_left, width=width, height=height)
def menu( camera_width: int, header: str, options: List[str], width: int, panel_y: int = None, camera: Camera = None ): if len(options) > 26: raise ValueError("Cannot have a menu with more than 26 options.") # Calculate total height for the header (after auto-wrap) and one line per option wrapped_header = textwrap.wrap(text=header, width=width * 2) height = len(options) + len(wrapped_header) if panel_y is None: panel_y = 2 panel = Menu( position=Point(x=2, y=panel_y), width=camera_width * 2, height=height, header=wrapped_header, options=options, ) if camera: camera.clear_view() for i, text_line in enumerate(panel.header): blt.printf(x=panel.x, y=panel.y + i * 2, s=f"{text_line}") letter_index = ord("a") for i, option_text in enumerate(options): text = f"({chr(letter_index + i)}) {option_text}" blt.puts( x=panel.x, y=panel.options_y + i * 2, s=f"{text}", align=blt.TK_ALIGN_LEFT )
def take_turn( self, target: Entity, fov_map: tcod.map.Map, game_map: GameMap, entities: List[Entity], ): results = [] if self.number_of_turns > 0: random_x = self.owner.x + random.randint(0, 2) - 1 random_y = self.owner.y + random.randint(0, 2) - 1 if random_x != self.owner.x and random_y != self.owner.y: self.owner.move_towards( target_position=Point(random_x, random_y), game_map=game_map, entities=entities, ) self.number_of_turns -= 1 else: self.owner.ai = self.previous_ai results.append({ "message": Message(f"The {self.owner.name} is no longer confused!", Colors.RED) }) return results
def move_to_random_adjacent(self, game_map): """Move the owner to a random adjacent tile.""" dx, dy = choice([ (-1, 1), (0, 1), (1, 1), (-1, 0), (1, 0), (-1, -1), (0, -1), (1, -1)]) self.attempt_move(Point(self.owner.x + dx, self.owner.y + dy), game_map)
def grow_maze(self, start: Point, label: TileType = None): """ :param start: :type start: Point :param label: :type label: map_objects.tile.TileType """ if label is None: label = TileType.CORRIDOR tiles = [] # tiles to check last_direction = Point(0, 0) if not self.can_place(start, last_direction): return region = self.new_region() self.place_tile(start, region=region, label=label) self.corridors.append(start) tiles.append(start) while len(tiles) > 0: tile = tiles[-1] # grab last tile # see which neighboring tiles can be carved open_tiles = [] for d in Direction.cardinal(): if self.can_place(tile, d): open_tiles.append(d) if len(open_tiles) > 0: if (last_direction in open_tiles and randint(1, 101) > self.winding_percent): current_direction = last_direction else: # TODO: refactor for random.choice() current_direction = open_tiles[randint( 0, len(open_tiles) - 1)] self.place_tile(tile + current_direction, region=region, label=label) self.corridors.append(tile + current_direction) tiles.append(tile + current_direction) # * 2) last_direction = current_direction else: tiles.remove(tile) last_direction = None
def __init__(self, x: int, y: int, width: int, height: int): super(Room, self).__init__(Point(x, y), width, height) # self.x: int = x # self.y: int = y # self.width: int = width # self.height: int = height self.region: int = None self.connections: list = []
def place_doors(self): doors_tiles = self.current_level.doors current_door_tuples = tuple(zip(doors_tiles[0], doors_tiles[1])) for x, y in current_door_tuples: door = buildings.door(Point(x, y)) self.current_level.add_entity(door)
def get_extents(self, point: Point, direction: Point): if direction == Point(0, 0): return point.NW, point.SE # north elif direction == Point(0, 1): return point.N.NW, point.NE # east elif direction == Point(1, 0): return point.NE, point.E.SE # west elif direction == Point(-1, 0): return point.W.NW, point.SW # south elif direction == Point(0, -1): return point.SW, point.S.SE else: print("get_extents else statement") print(f"Direction not valid: point={point}, direction={direction}")
def test_popluate_map(self, player): point = self.current_level.find_random_open_position() item = equipment.healing_potion(Point(27, 27)) self.current_level.add_entity(item) item2 = equipment.healing_potion(player.point) self.current_level.add_entity(item2) #cube = bestiary.gelatinous_cube(item.point) #self.current_level.add_entity(cube) #npc = bestiary.generate_npc(Species.TROLL, self.dungeon_level) #npc.set_point(Point(26,26)) #self.current_level.add_entity(npc) npc = bestiary.spawn_point(Point(26, 26), Species.TROLL) self.current_level.add_entity(npc) self.place_doors()
def fireball(**kwargs): '''Cast a fireball. Does FIRE damage to all damagable entities in a circle centered on the target point. Parameters ---------- kwargs: caster (Entity): Entity that initiated the spell. game_map (GameMap): Current game map. number_of_dice (int): number of die to roll. radius (int): radius of the spell effect. target_x (int): x co-ordinate. target_y (int): y co-ordinate. type_of_dice (int): Type of die to roll. Returns ------- results (list) Results of casting the spell. ''' caster = kwargs.get('caster') game_map = kwargs.get('game_map') number_of_dice = kwargs.get('number_of_dice') radius = kwargs.get('radius') target_x = kwargs.get('target_x') target_y = kwargs.get('target_y') type_of_dice = kwargs.get('type_of_dice') results = [] if not game_map.current_level.fov[target_x, target_y]: results.append({ ResultTypes.MESSAGE: Message('You cannot target a tile outside your field of view.', COLORS.get('neutral_text')) }) return results results.append({ ResultTypes.MESSAGE: Message( 'The fireball explodes, burning everything within {0} tiles!'. format(radius), COLORS.get('effect_text')) }) #TODO: refactor, this is probably horribly inefficent for entity in game_map.current_level.entities: if entity.point.distance_to(Point( target_x, target_y)) <= radius and entity.health: damage = die_roll(number_of_dice, type_of_dice) damage_results, total_damage = entity.health.take_damage( damage, caster, DamageType.FIRE) results.extend(damage_results) return results
def empty(cls, point=Point(-1, -1)): """ creates an empty Tile that is not passable :param point: x- and y-coordinates for the tile, defaults to -1, -1 if no point is provided :type point: Point :return: returns a tile at x and y of point with the label "EMPTY" and not passable :rtype Tile """ return Tile(point.x, point.y, label=TileType.EMPTY)
def __init__(self, x: int, y: int, *, label: TileType = TileType.EMPTY, passable: bool = False): self.position: Point = Point(x, y) self.label: TileType = label self.passable: bool = passable
def random_walkable_position(game_map, entity): walkable_array = game_map.current_level.make_walkable_array( entity.movement.routing_avoid) walkable_array[entity.x, entity.y] = True open_tiles = np.where(walkable_array == True) listOfCoordinates = list(zip(open_tiles[0], open_tiles[1])) target = random.choice(listOfCoordinates) return Point(target[0], target[1])
class Constants: screen_width: int = 120 * 2 screen_height: int = 45 * 2 window_title: str = "Rogue Trader" map_font: str = "map font: mplus-1p-bold.ttf, size=12, spacing=2x2" ui_font: str = "ui_font font: mplus-1p-bold.ttf, size=10" bar_font: str = "bar font: mplus-1p-bold.ttf, size=6, spacing=2x2" hover_font: str = "hover font: mplus-1p-bold.ttf, size=6" map_width: int = 80 map_height: int = 80 camera_width: int = 65 camera_height: int = 33 bar_width: int = 20 panel_height: int = 12 ui_panel: Rect = Rect(position=Point(x=0, y=68), width=240, height=24) room_min_size: int = 10 room_max_size: int = 6 max_rooms: int = 30 max_monsters: int = 10 min_monsters: int = 5 max_items: int = 50 fov_algorithm: int = 0 fov_light_walls: bool = True fov_radius: int = 10 player_hp: int = 100 player_defense: int = 1 player_power: int = 2 level_up_base: int = 200 level_up_factor: int = 150 @property def message_x(self) -> int: message_x: int = (self.bar_width + 2) * 2 return message_x @property def message_width(self) -> int: message_width: int = self.screen_width - ((self.bar_width - 2) * 2) return message_width @property def panel_y(self) -> int: panel_y: int = self.screen_height - (self.panel_height * 2) return panel_y @property def message_height(self) -> int: panel_height: int = self.panel_height - 1 return panel_height
def handle_mouse(key: int) -> Dict[str, Point]: mouse_position: Point = Point(x=blt.state(blt.TK_MOUSE_X) // 2, y=blt.state(blt.TK_MOUSE_Y) // 2) if key == blt.TK_MOUSE_LEFT: return {"left_click": mouse_position} elif key == blt.TK_MOUSE_RIGHT: return {"right_click": mouse_position} return {}
def translate_point(point, x, y): offsets = [[(-1, -1), (0, -1), (-1, 1)], [(1, 0), (0, 0), (-1, 0)], [(1, -1), (0, 1), (1, 1)]] (a, b) = offsets[x][y] change = Point(point.x + a, point.y + b) return change
def tiles(self) -> Iterator[Tile]: for i in range(self.height): for j in range(self.width): point = Point(x=j, y=i) if self.cave_map[j, i]: label = TileType.CAVE else: label = TileType.FLOOR tile = Tile.from_label(point=point, label=label) yield tile
def display(self): """ iterator that begins at bottom left of the dungeon to display properly :rtype: List[int, int, Point] """ for i in range(self.height - 1, 0, -1): for j in range(self.width): # yield i, j - 1, self.grid[i][j - 1] yield j, i, self.dungeon.tile(Point(j, i)) """
def remove_small_walls(self, cave: List[Point]) -> List[Point]: for i in range(1, self.map_height - 1): for j in range(1, self.map_width - 1): point = Point(x=j, y=i) wall = self.game_map.is_blocked(point) if not wall: continue if self.game_map.count_one_step_neighbors(point) == 1: cave.append(point) self.game_map.place_tile(point=point, tile=Tile.floor(point=point)) return cave
def tile(self, x: int, y: int) -> Tile: """ :param x: x-coordinate of tile :type x: int :param y: y-coordinate of tile :type y: int :return: Tile at coordinate (x, y) :rtype: Tile """ tile = self.dungeon.tile(Point(x, y)) return tile
def build_corridors(self, start_point: Point = None): cells = [] if start_point is None: start_point = Point( x=randint(1, self.width - 2), y=randint(1, self.height - 2), ) # TODO: refactor can_carve attempts = 0 while not self.can_carve(start_point, Direction.self()): attempts += 1 start_point = Point( x=randint(1, self.width - 2), y=randint(1, self.height - 2), ) # TODO: need to remove this hard stop once everything is combined if attempts > 100: break self.carve(point=start_point, region=self.new_region(), label=TileType.CORRIDOR) # add point to corridor list self.corridors.append(start_point) # add point to open cell list cells.append(start_point) while cells: middle = len(cells) // 2 start_point = cells[-1] possible_moves = self.possible_moves(start_point) if possible_moves: point = choice(possible_moves) self.carve(point=point, region=self.current_region, label=TileType.CORRIDOR) self.corridors.append(point) cells.append(point) else: cells.remove(start_point)
def count_one_step_neighbors(self, center: Point) -> int: # TODO: refactor to make more generic # combine with two_step to take a steps parameter and returns multiple values count = 0 for i in [-1, 0, 1]: for j in [-1, 0, 1]: point = Point(x=center.x + j, y=center.y + i) if not self.in_bounds(point=point): continue if self.cave_map[point.x, point.y]: count += 1 return count