def suitable_for_door(dungeon_level, position): current_terrain = dungeon_level.get_tile_or_unknown(position).get_terrain() if current_terrain.has("is_solid") or current_terrain.has("is_chasm"): return False up_terrain = dungeon_level.get_tile_or_unknown( geo.add_2d(position, direction.UP)).get_terrain() down_terrain = dungeon_level.get_tile_or_unknown( geo.add_2d(position, direction.DOWN)).get_terrain() left_terrain = dungeon_level.get_tile_or_unknown( geo.add_2d(position, direction.LEFT)).get_terrain() right_terrain = dungeon_level.get_tile_or_unknown( geo.add_2d(position, direction.RIGHT)).get_terrain() # No door next to other door or chasm, it looks silly. terrains = [up_terrain, down_terrain, left_terrain, right_terrain] if any([ terrain.has("is_door") or terrain.has("is_chasm") for terrain in terrains ]): return False # Are there any walls to connect with the door? up = up_terrain.has("is_solid") down = down_terrain.has("is_solid") left = left_terrain.has("is_solid") right = right_terrain.has("is_solid") return ((up and down and not (left or right)) or (left and right and not (up or down)))
def draw_everything(self, camera, tile_matrix): for y in range(self.height): for x in range(self.width): position = (x, y) tile_position = geo.add_2d(position, camera.camera_offset) the_tile = get_tile_or_unknown(tile_position, tile_matrix) the_tile.draw_seen(self.console, position)
def put_tile_and_surrounding_tiles_on_fire(dungeon_level, position, min_fire_time, max_fire_time, game_state): fire = new_fire_cloud(game_state, random.randrange(min_fire_time, max_fire_time)) fire.mover.replace_move(position, dungeon_level) for d in direction.DIRECTIONS: point = geometry.add_2d(d, position) fire = new_fire_cloud(game_state, random.randrange(min_fire_time, max_fire_time)) fire.mover.replace_move(point, dungeon_level)
def _before_act(self): neighbours = [geometry.add_2d(offset, self.parent.position.value) for offset in direction.AXIS_DIRECTIONS] random.shuffle(neighbours) for neighbour in neighbours: if self.point_has_flammable(neighbour) and self.try_spread_to_position(neighbour): break return gametime.single_turn
def position_can_have_web(self, dungeon_level, position): surrounding_tiles = [dungeon_level.get_tile_or_unknown(geometry.add_2d(position, d)) for d in direction.DIRECTIONS] surrounding_solid_terrains = len([tile for tile in surrounding_tiles if tile.get_terrain().has("is_solid")]) surrounding_dungeon_features = len([tile for tile in surrounding_tiles if tile.get_dungeon_feature()]) return surrounding_dungeon_features + surrounding_solid_terrains > 2
def _split_slime(self, split_direction): if self._slime.health.hp.value < 3: return new_slime = self._slime.clone_function.value(self._slime.game_state.value) if new_slime.mover.try_move_roll_over(geometry.add_2d(self._slime.position.value, split_direction), self._slime.dungeon_level.value): health = self._slime.health.hp.value / 2 self._slime.health.hp.value = health new_slime.health.hp.value = health new_slime.monster_actor_state.value = MonsterActorState.HUNTING
def _get_walkable_neighbors(entity, position, dungeon_level): result_positions = [] for direction_ in direction.DIRECTIONS: neighbor_position = geo.add_2d(position, direction_) try: neighbor = dungeon_level.get_tile(neighbor_position) if entity.mover.can_pass_terrain(neighbor.get_terrain()): result_positions.append(neighbor_position) except IndexError: pass return result_positions
def dfs_tunnler(dungeon_level, start_position, min_length, max_length, tile_brush, end_condition_func, direction_list=None): position = start_position direction_ = random.sample(direction_list, 1)[0] while not end_condition_func(): direction_ = direction.turn_left_or_right(direction_) length = random.randint(min_length, max_length) tile_brush.apply_brush(dungeon_level, position) for _ in range(length): position = geo.add_2d(position, direction_) tile_brush.apply_brush(dungeon_level, position)
def player_status_menu(player): split_width = 27 content_padding = (2, 2) player_status_stack_panel = gui.StackPanelVertical(geo.add_2d((0, 0), content_padding), alignment=gui.StackPanelVertical.ALIGN_LEFT, vertical_space=1) player_status_stack_panel_row_1 = gui.StackPanelHorizontal((0, 0), alignment=gui.StackPanelHorizontal.ALIGN_TOP, horizontal_space=1) player_status_stack_panel.append(player_status_stack_panel_row_1) player_status_stack_panel_row_1.append(gui.BigSymbolUIElement((0, 0), player.graphic_char)) player_description_stack = gui.StackPanelVertical((0, 0), alignment=gui.StackPanelVertical.ALIGN_LEFT, vertical_space=1) player_status_stack_panel_row_1.append(player_description_stack) player_description_stack.append(gui.TextBox(player.description.name, (2, 0), colors.WHITE)) player_description_stack.append(gui.TextBox(player.race.value + "\n" + player.job.value, (2, 0), colors.WHITE)) player_description_stack.append(gui.new_player_hp_bar(12, player.health.hp)) player_description_stack.append(gui.new_player_sanity_bar(12, Counter(10, 10))) player_status_stack_panel_row_2 = gui.StackPanelHorizontal((0, 0), alignment=gui.StackPanelHorizontal.ALIGN_TOP, horizontal_space=3) player_status_stack_panel.append(player_status_stack_panel_row_2) player_status_stack_panel_row_2.append(new_player_status_stack(player, 8)) player_status_stack_panel_row_2.append(new_player_weapon_table(player, 8)) description_card = gui.new_item_description_card() power_list = get_power_list(player, description_card) if len(power_list.menu_items) > 0: player_status_stack_panel.append(gui.VerticalSpace(1)) player_status_stack_panel.append(power_list) bg_rect_height = (player_status_stack_panel.total_height + 4) bg_rect = rectfactory.center_of_screen_rect(split_width, bg_rect_height) player_status_stack_panel.offset = geo.add_2d(bg_rect.top_left, (2, 2)) styled_bg_rect = get_menu_background(bg_rect, style.rogue_classic_theme.rect_style) dock = gui.UIDock(rectfactory.full_screen_rect()) dock.bottom = description_card return state.UIState(gui.UIElementList([styled_bg_rect, player_status_stack_panel, dock]))
def _get_point_behind_unless_solid(self, enemy_position, distance, dungeon_level): """ Gets point right behind me seen from my enemy. """ right_behind_direction = geometry.other_side_of_point_direction(enemy_position, self.parent.position.value) last_result = self.parent.position.value for _ in range(distance): result = geometry.add_2d(right_behind_direction, last_result) if position_is_solid(result, dungeon_level): return last_result last_result = result return result
def suitable_for_door(dungeon_level, position): current_terrain = dungeon_level.get_tile_or_unknown(position).get_terrain() if current_terrain.has("is_solid") or current_terrain.has("is_chasm"): return False up_terrain = dungeon_level.get_tile_or_unknown(geo.add_2d(position, direction.UP)).get_terrain() down_terrain = dungeon_level.get_tile_or_unknown(geo.add_2d(position, direction.DOWN)).get_terrain() left_terrain = dungeon_level.get_tile_or_unknown(geo.add_2d(position, direction.LEFT)).get_terrain() right_terrain = dungeon_level.get_tile_or_unknown(geo.add_2d(position, direction.RIGHT)).get_terrain() # No door next to other door or chasm, it looks silly. terrains = [up_terrain, down_terrain, left_terrain, right_terrain] if any([terrain.has("is_door") or terrain.has("is_chasm") for terrain in terrains]): return False # Are there any walls to connect with the door? up = up_terrain.has("is_solid") down = down_terrain.has("is_solid") left = left_terrain.has("is_solid") right = right_terrain.has("is_solid") return ((up and down and not (left or right)) or (left and right and not (up or down)))
def _on_successful_step(self): position = self.parent.position.value surrounding_points = [geometry.add_2d(d, position) for d in direction.DIRECTIONS] for p in surrounding_points: entities = self.parent.dungeon_level.value.get_tile_or_unknown(p).get_entities() if len(entities) > 0: entity = entities[0] for e in entity.get_children_with_tag(trigger.ENEMY_STEPPING_NEXT_TO_ME_TRIGGER_TAG): if e.can_trigger(source_entity=entity, target_entity=self.parent): e.trigger(source_entity=entity, target_entity=self.parent) for e in self.parent.get_children_with_tag(trigger.STEP_NEXT_TO_ENEMY_TRIGGER_TAG): if e.can_trigger(source_entity=self.parent, target_entity=entity): e.trigger(source_entity=self.parent, target_entity=entity)
def _get_point_behind_unless_solid(self, enemy_position, distance, dungeon_level): """ Gets point right behind me seen from my enemy. """ right_behind_direction = geometry.other_side_of_point_direction( enemy_position, self.parent.position.value) last_result = self.parent.position.value for _ in range(distance): result = geometry.add_2d(right_behind_direction, last_result) if position_is_solid(result, dungeon_level): return last_result last_result = result return result
def spread(self): minimal_cloud_size = 2 if self.parent.density.value < minimal_cloud_size: self.parent.mover.try_remove_from_dungeon() return gametime.single_turn density_per_tile = max(self.parent.density.value / 4, 1) neighbours = [geometry.add_2d(offset, self.parent.position.value) for offset in direction.AXIS_DIRECTIONS] random.shuffle(neighbours) for neighbour in neighbours: self._float_to_position(neighbour, density_per_tile) if self.parent.density.value < density_per_tile: break if self.parent.density.value < minimal_cloud_size: break
def cellular_automata(dungeon_level): for y in range(dungeon_level.height): for x in range(dungeon_level.width): position = (x, y) neighbors = [geo.add_2d(position, direction_) for direction_ in direction.DIRECTIONS] solid_neighbors = 0 for point in neighbors: if (not dungeon_level.has_tile(point) or dungeon_level.get_tile(point).get_terrain().has("is_solid")): solid_neighbors += 1 _apply_cellular_automata_rule_on_tile(dungeon_level, position, solid_neighbors)
def after_tick(self, time): self.time_to_next_attempt -= time if self.time_to_next_attempt > 0: return my_position = self.parent.position.value chance = self.web_chance_per_turn / float(len(direction.DIRECTIONS)) dungeon_level = self.parent.dungeon_level.value for d in direction.DIRECTIONS: point = geometry.add_2d(my_position, d) if (random.random() < chance and dungeon_level and len(dungeon_level.get_tile_or_unknown(point).get_entities()) == 0 and self.position_can_have_web(dungeon_level, point)): web = new_spider_web() web.mover.try_move(point, dungeon_level) self.time_to_next_attempt = self.time_interval
def try_step_left_or_right(self, next_point): step_direction = geometry.sub_2d(next_point, self.parent.position.value) if step_direction not in direction.DIRECTIONS: return False alternate_directions = [direction.turn_slight_left(step_direction), direction.turn_slight_right(step_direction)] random.shuffle(alternate_directions) for d in alternate_directions: new_position = geometry.add_2d(d, self.parent.position.value) terrain = self.parent.dungeon_level.value.get_tile_or_unknown(new_position).get_terrain() if self.parent.mover.can_pass_terrain(terrain): energy_spent = self.parent.stepper.try_move_or_bump(new_position) if energy_spent > 0: return energy_spent return 0
def _blink(self, entity): sight = entity.sight_radius.value possible_destinations = [] for x in range(-sight, sight + 1): for y in range(-sight, sight + 1): if x == 0 and y == 0: continue p = geometry.add_2d((x, y), entity.position.value) if entity.dungeon_mask.can_see_point(p): possible_destinations.append(p) random.shuffle(possible_destinations) for position in possible_destinations: is_safe = (entity.dungeon_level.value.get_tile_or_unknown(position).get_terrain().has("is_floor") or entity.status_flags.has_status(StatusFlags.FLYING)) if is_safe and entity.mover.try_move(position): break
def _blink(self, entity): sight = entity.sight_radius.value possible_destinations = [] for x in range(-sight, sight + 1): for y in range(-sight, sight + 1): if x == 0 and y == 0: continue p = geometry.add_2d((x, y), entity.position.value) if entity.dungeon_mask.can_see_point(p): possible_destinations.append(p) random.shuffle(possible_destinations) for position in possible_destinations: is_safe = (entity.dungeon_level.value.get_tile_or_unknown( position).get_terrain().has("is_floor") or entity.status_flags.has_status(StatusFlags.FLYING)) if is_safe and entity.mover.try_move(position): break
def cellular_automata(dungeon_level): for y in range(dungeon_level.height): for x in range(dungeon_level.width): position = (x, y) neighbors = [ geo.add_2d(position, direction_) for direction_ in direction.DIRECTIONS ] solid_neighbors = 0 for point in neighbors: if (not dungeon_level.has_tile(point) or dungeon_level.get_tile(point).get_terrain().has( "is_solid")): solid_neighbors += 1 _apply_cellular_automata_rule_on_tile(dungeon_level, position, solid_neighbors)
def try_move_roll_over(self, new_position, new_dungeon_level=None): """ Tries to move parent to new position. Or an adjacent tile if it is already occupied. Returns true if it is successful, false otherwise. """ if self.try_move(new_position, new_dungeon_level): return True # Do not shuffle public constants! directions = list(direction.DIRECTIONS) random.shuffle(directions) for d in directions: destination = geometry.add_2d(d, new_position) if self.try_move(destination, new_dungeon_level): return True return False
def trigger(self, **kwargs): #message = messenger.ENTITY_EXPLODES % {"target_entity": self.parent.description.name} source_entity = kwargs[action.SOURCE_ENTITY] target_position = kwargs[action.TARGET_POSITION] game_state = kwargs[action.GAME_STATE] explosion = new_explosion_cloud(game_state, 1) explosion.graphic_char.color_fg = colors.YELLOW explosion.mover.replace_move(target_position, source_entity.dungeon_level.value) for d in direction.DIRECTIONS: if d in direction.AXIS_DIRECTIONS: color = colors.ORANGE else: color = colors.RED point = geometry.add_2d(d, target_position) explosion = new_explosion_cloud(game_state, 1) explosion.graphic_char.color_fg = color explosion.mover.replace_move(point, source_entity.dungeon_level.value)
def random_explosion(dungeon_level, start_pos, tile_brush, end_condition_func, move_list=None): if move_list is None: move_list = direction.DIRECTIONS position = start_pos visited = set() unvisited_positions = set() while not end_condition_func(): tile_brush.apply_brush(dungeon_level, position) visited.add(position) neighbors = set([geo.add_2d(position, _direction) for _direction in move_list]) unvisited_neighbors = neighbors - visited unvisited_positions = unvisited_positions | unvisited_neighbors if len(unvisited_positions) >= 1: position = random.sample(unvisited_positions, 1)[0] else: break
def after_tick(self, time): self.time_to_next_burn_attempt -= time if self.time_to_next_burn_attempt > 0: return my_position = self.parent.position.value chance = self.fire_chance_per_turn / float(len(direction.DIRECTIONS)) for d in direction.DIRECTIONS: point = geometry.add_2d(my_position, d) dungeon_level = self.parent.dungeon_level.value if not dungeon_level: break fire = new_fire_cloud(self.parent.game_state.value, random.randrange(6, 10)) if (random.random() < chance and len(dungeon_level.get_tile_or_unknown(point).get_entities()) == 0 and fire.mover.can_move(point, dungeon_level)): animate_flight(self.parent.game_state.value, [my_position, point], fire.graphic_char.icon, fire.graphic_char.color_fg) fire.mover.try_move(point, dungeon_level) self.time_to_next_burn_attempt = self.time_interval
def __init__(self, state_stack, message, width=settings.MINIMUM_WIDTH * 0.8, max_height=settings.MINIMUM_HEIGHT * 0.8): self.message = message self._width = width self.max_height = max_height self._state_stack = state_stack margin = style.interface_theme.margin self.text_stack_panel = gui.StackPanelVertical((-1, -1), vertical_space=1) self.text_stack_panel.append(gui.TextBoxWrap(message, (0, 0), colors.GRAY, self.width, max_height)) self.text_stack_panel.append(gui.TextBoxWrap(messenger.PRESS_ENTER_TO_ACCEPT, (0, 0), colors.LIGHT_ORANGE, self.width, max_height)) self.text_stack_panel.update() rect = rectfactory.ratio_of_screen_rect(self.text_stack_panel.width + margin[0] * 2, self.text_stack_panel.height + margin[1] * 2, 0.5, 0.3) self.text_stack_panel.offset = geo.add_2d(rect.top_left, (2, 2)) self.bg_rectangle = gui.StyledRectangle(rect, style.interface_theme.rect_style) self.result = False
def random_explosion(dungeon_level, start_pos, tile_brush, end_condition_func, move_list=None): if move_list is None: move_list = direction.DIRECTIONS position = start_pos visited = set() unvisited_positions = set() while not end_condition_func(): tile_brush.apply_brush(dungeon_level, position) visited.add(position) neighbors = set( [geo.add_2d(position, _direction) for _direction in move_list]) unvisited_neighbors = neighbors - visited unvisited_positions = unvisited_positions | unvisited_neighbors if len(unvisited_positions) >= 1: position = random.sample(unvisited_positions, 1)[0] else: break
def _keep_player_at_distance(self): player = self.get_player_if_seen() if player is None: return False if not self.parent.monster_actor_state.value == MonsterActorState.HUNTING: return False current_distance_to_optimal = self.distance_to_optimal_distance(self.parent.position.value, player.position.value) if current_distance_to_optimal == 0: return True for d in direction.get_shuffled_directions(): possible_step = geo.add_2d(d, self.parent.position.value) if (self.distance_to_optimal_distance(possible_step, player.position.value) < current_distance_to_optimal): energy_used = self.parent.stepper.try_step_in_direction(d) if energy_used > 0: self.newly_spent_energy += energy_used return True return False
def draw(self, offset=geo.zero2d()): real_offset = geo.int_2d(geo.add_2d(geo.add_2d(self.offset, offset), self.margin)) self._item_stack_panel.draw(real_offset)
def try_step_in_direction(self, direction): return self.try_move_or_bump(geometry.add_2d(self.parent.position.value, direction))
def apply_brush(self, dungeon_level, position): shape = random.sample(self.shapes, 1)[0] for point in shape: dungeon_position = geo.add_2d(point, position) self.tile_modifier.modify(dungeon_level, dungeon_position)
def apply_brush(self, dungeon_level, position): for point in self.shape: dungeon_position = geo.add_2d(point, position) self.tile_modifier.modify(dungeon_level, dungeon_position)
def generate_dungeon_exploded_rooms(depth, rooms, room_area, rectangle_room_chance): aprox_room_radius = math.sqrt(room_area) * 1.2 room_distance = aprox_room_radius grid_side = int(max(rooms / 2 + 1, math.sqrt(rooms + 1) + 1)) triangle_points = shapegenerator.triangle_points(room_distance, grid_side, grid_side) room_positions = random.sample(triangle_points, rooms) minor_room_positions = set() room_graph = graph.Graph() corridors_points = set() for room_position in room_positions: room_graph.add_point(room_position) while not room_graph.is_connected(): edge = random.sample(room_positions, 2) while room_graph.has_edge(edge[0], edge[1]): edge = random.sample(room_positions, 2) room_graph.add_edge(edge[0], edge[1]) mid_point = random.sample(shapegenerator.get_opposite_rectangle_corners(edge[0], edge[1]), 1)[0] minor_room_positions.add(mid_point) corridor = shapegenerator.three_point_rectangle_draw(edge[0], mid_point, edge[1]) corridors_points.update(corridor) # Corridor and small corner room shape generation open_points = corridors_points used_roms_positions = [] for position in room_positions: if random.random() > rectangle_room_chance: used_roms_positions.append(position) if rng.coin_flip(): room_points = shapegenerator.random_explosion(position, room_area, direction.AXIS_DIRECTIONS) else: room_points = shapegenerator.fractal_rectangle(position, 3, 3) open_points.update(room_points) for position in minor_room_positions: room_points = shapegenerator.random_explosion(position, room_area / 4, direction.AXIS_DIRECTIONS) open_points.update(room_points) open_points = shapegenerator.smooth_shape(open_points) open_points = shapegenerator.smooth_shape(open_points) possible_door_points = set() for position in set(room_positions) - set(used_roms_positions): width = random.randrange(3, 8) height = random.randrange(3, 8) offset_x = random.randrange(width) offset_y = random.randrange(height) top_left_corner = geo.sub_2d(position, (offset_x, offset_y)) bottom_right_corner = geo.add_2d(top_left_corner, (width, height)) room_points = shapegenerator.get_rectangle_shape(top_left_corner, bottom_right_corner) surrounding_points = shapegenerator.get_rectangle_shape(geo.sub_2d(top_left_corner, (1, 1)), geo.add_2d(bottom_right_corner, (1, 1))) - room_points open_points.update(room_points) possible_door_points |= surrounding_points # Redraw corridors to make sure no dead rooms appear. open_points.update(corridors_points) plant_points = set() if rng.coin_flip() or rng.coin_flip(): for room_position in room_positions: if rng.coin_flip() and rng.coin_flip(): continue room_x, room_y = room_position variance = 7 chasm_start_point = (random.randrange(room_x - variance, room_x + variance), random.randrange(room_y - variance, room_y + variance)) plant_points.update(shapegenerator.random_explosion(chasm_start_point, room_area * 0.2, direction.AXIS_DIRECTIONS)) # Chasm shape generation chasm_points = set() for room_position in room_positions: room_x, room_y = room_position variance = 10 chasm_start_point = (random.randrange(room_x - variance, room_x + variance), random.randrange(room_y - variance, room_y + variance)) chasm_points.update(shapegenerator.random_explosion(chasm_start_point, room_area * 0.8, direction.AXIS_DIRECTIONS)) chasm_points = shapegenerator.smooth_shape(chasm_points) chasm_points = shapegenerator.smooth_shape(chasm_points) # Normalize Points to dungeon frame = (2, 2) # Just to be safe we won't try to draw outside Dungeon. level_shape = shapegenerator.Shape(open_points) plant_shape = shapegenerator.Shape(plant_points) chasm_shape = shapegenerator.Shape(chasm_points) possible_door_shape = shapegenerator.Shape(possible_door_points) dungeon_rect = shapegenerator.Shape(open_points | chasm_points).calc_rect() dungeon_rect_with_frame = dungeon_rect.expanded_by(frame) normalized_chasm_points = chasm_shape.offset_points(geo.sub_2d(frame, dungeon_rect.top_left)) normalized_plant_points = plant_shape.offset_points(geo.sub_2d(frame, dungeon_rect.top_left)) normalized_possible_door_points = possible_door_shape.offset_points(geo.sub_2d(frame, dungeon_rect.top_left)) normalized_open_points = level_shape.offset_points(geo.sub_2d(frame, dungeon_rect.top_left)) # Apply shapes to dungeon dungeon_level = get_full_wall_dungeon(dungeon_rect_with_frame.width, dungeon_rect_with_frame.height, depth) brush = SinglePointBrush(ReplaceComponent(terrain.Chasm)) apply_brush_to_points(dungeon_level, normalized_chasm_points, brush) brush = SinglePointBrush(ReplaceComponent(terrain.Floor)) apply_brush_to_points(dungeon_level, normalized_open_points, brush) brush = SinglePointBrush(ReplaceComponent(dungeonfeature.new_plant)) apply_brush_to_terrains_with_flag(dungeon_level, normalized_plant_points, brush, terrain.Floor.FLOOR_FLAG) door_brush = DoorIfSuitableBrush() apply_brush_to_points(dungeon_level, normalized_possible_door_points, door_brush) feature_positions = random.sample(normalized_open_points, 4) place_up_down_stairs(dungeon_level, feature_positions[0], feature_positions[1]) _place_feature_replace_terrain_with_floor(dungeonfeature.new_fountain(), dungeon_level, feature_positions[2]) if rng.coin_flip(): _place_feature_replace_terrain_with_floor(dungeonfeature.new_blood_fountain(), dungeon_level, feature_positions[3]) return dungeon_level
def place_up_down_stairs_at_center(dungeon_level): center = (dungeon_level.width / 2, dungeon_level.height / 2) next_to_center = geo.add_2d(center, (0, 1)) place_up_down_stairs(dungeon_level, center, next_to_center) return True
def get_tiles_surrounding_position(self, position): return [self.get_tile_or_unknown(geo.add_2d(offset, position)) for offset in direction.AXIS_DIRECTIONS]
def try_move_or_bump(self, _): new_position = geometry.add_2d(random.choice(direction.DIRECTIONS), self.parent.position.value) return super(RandomStepper, self).try_move_or_bump(new_position)