def add_elevator_connections_to_patches(self, configuration: EchoesConfiguration, rng: Random, patches: GamePatches) -> GamePatches: elevator_connection = copy.copy(patches.elevator_connection) elevators = configuration.elevators if not elevators.is_vanilla: if rng is None: raise MissingRng("Elevator") world_list = filtered_database.game_description_for_layout(configuration).world_list elevator_db = elevator_distributor.create_elevator_database( world_list, elevators.editable_teleporters) if elevators.mode in {TeleporterShuffleMode.TWO_WAY_RANDOMIZED, TeleporterShuffleMode.TWO_WAY_UNCHECKED}: connections = elevator_distributor.two_way_elevator_connections( rng=rng, elevator_database=elevator_db, between_areas=elevators.mode == TeleporterShuffleMode.TWO_WAY_RANDOMIZED ) else: connections = elevator_distributor.one_way_elevator_connections( rng=rng, elevator_database=elevator_db, target_locations=elevators.valid_targets, replacement=elevators.mode != TeleporterShuffleMode.ONE_WAY_ELEVATOR, ) elevator_connection.update(connections) for teleporter, destination in elevators.static_teleporters.items(): elevator_connection[teleporter] = destination return dataclasses.replace(patches, elevator_connection=elevator_connection)
def update_content(self, configuration: BaseConfiguration, all_patches: dict[int, GamePatches], players: PlayersConfiguration): self.tree_widget.clear() self.tree_widget.setColumnCount(2) self.tree_widget.setHeaderLabels(["Source", "Destination"]) world_list = filtered_database.game_description_for_layout(configuration).world_list patches = all_patches[players.player_index] per_world: dict[str, dict[str, str]] = collections.defaultdict(dict) for source, destination_loc in patches.all_elevator_connections(): source_world = world_list.world_by_area_location(source.identifier.area_identifier) source_name = elevators.get_elevator_or_area_name(self.game_enum, world_list, source.identifier.area_identifier, True) per_world[source_world.name][source_name] = elevators.get_elevator_or_area_name(self.game_enum, world_list, destination_loc, True) for world_name, world_contents in iterate_key_sorted(per_world): world_item = QtWidgets.QTreeWidgetItem(self.tree_widget) world_item.setText(0, world_name) world_item.setExpanded(True) for source_name, destination in iterate_key_sorted(world_contents): area_item = QtWidgets.QTreeWidgetItem(world_item) area_item.setText(0, source_name) area_item.setText(1, destination) self.tree_widget.resizeColumnToContents(0) self.tree_widget.resizeColumnToContents(1)
def __init__( self, parent: QWidget, window_manager: WindowManager, preset: Preset, ): super().__init__(parent) self.setupUi(self) set_default_window_icon(self) self._window_manager = window_manager self._game_description = filtered_database.game_description_for_layout( preset.configuration) database = self._game_description.resource_database trick_level = preset.configuration.trick_level if trick_level.minimal_logic: trick_usage_description = "Minimal Logic" else: trick_usage_description = ", ".join( sorted( f"{trick.long_name} ({trick_level.level_for_trick(trick).long_name})" for trick in database.trick if trick_level.has_specific_level_for_trick(trick))) # setup self.area_list_label.linkActivated.connect( self._on_click_link_to_data_editor) self.setWindowTitle("{} for preset {}".format(self.windowTitle(), preset.name)) self.title_label.setText(self.title_label.text().format( trick_levels=trick_usage_description)) # connect self.button_box.accepted.connect(self.button_box_close) self.button_box.rejected.connect(self.button_box_close) if trick_level.minimal_logic: return # Update trick_resources = self._game_description.game.generator.bootstrap.trick_resources_for_configuration( trick_level, database) lines = [] for world in sorted(self._game_description.world_list.worlds, key=lambda it: it.name): for area in sorted(world.areas, key=lambda it: it.name): used_tricks = _check_used_tricks(area, trick_resources, database) if used_tricks: lines.append( f'<p><a href="data-editor://{world.correct_name(area.in_dark_aether)}/{area.name}">' f'{world.correct_name(area.in_dark_aether)} - {area.name}</a>' f'<br />{"<br />".join(used_tricks)}</p>') self.area_list_label.setText("".join(lines))
def test_node_index_multiple_games(default_prime_preset): default_config = default_prime_preset.configuration assert isinstance(default_config, PrimeConfiguration) alt_config = dataclasses.replace(default_config, items_every_room=True) alt_game = filtered_database.game_description_for_layout(alt_config) default_game = filtered_database.game_description_for_layout( default_config) all_nodes_default = default_game.world_list.all_nodes all_nodes_alt = alt_game.world_list.all_nodes for node in alt_game.world_list.iterate_nodes(): assert all_nodes_alt[node.node_index] is node for node in default_game.world_list.iterate_nodes(): assert all_nodes_default[node.node_index] is node
def serialize_single(player_index: int, num_players: int, patches: GamePatches) -> dict: """ Encodes a given GamePatches into a JSON-serializable dict. :param player_index: :param num_players: :param patches: :return: """ game = filtered_database.game_description_for_layout(patches.configuration) world_list = game.world_list dock_weakness_to_type = {} for dock_type, weaknesses in game.dock_weakness_database.weaknesses.items( ): for weakness in weaknesses.values(): dock_weakness_to_type[weakness] = dock_type result = { # This field helps schema migrations, if nothing else "game": patches.configuration.game.value, "starting_location": patches.starting_location.as_string, "starting_items": { resource_info.long_name: quantity for resource_info, quantity in patches.starting_items.as_resource_gain() }, "teleporters": { source.identifier.as_string: connection.as_string for source, connection in patches.all_elevator_connections() }, "dock_weakness": { dock.identifier.as_string: { "type": dock_weakness_to_type[weakness].short_name, "name": weakness.name, } for dock, weakness in patches.all_dock_weaknesses() }, "configurable_nodes": { identifier.as_string: data_writer.write_requirement(requirement) for identifier, requirement in patches.configurable_nodes.items() }, "locations": { key: value for key, value in _pickup_assignment_to_item_locations( world_list, patches.pickup_assignment, num_players).items() }, "hints": { identifier.as_string: hint.as_json for identifier, hint in patches.hints.items() } } return result
def update_content(self, configuration: BaseConfiguration, all_patches: dict[int, GamePatches], players: PlayersConfiguration): self.tree_widget.clear() self.tree_widget.setColumnCount(3) self.tree_widget.setHeaderLabels(["Hint", "Pickup", "In-Game Text"]) game = filtered_database.game_description_for_layout(configuration) world_list = game.world_list patches = all_patches[players.player_index] per_world: dict[str, dict[str, tuple[str, str]]] = collections.defaultdict(dict) namer = DreadHintNamer(all_patches, players) exporter = HintExporter(namer, random.Random(0), ["A joke hint."]) for identifier, hint in patches.hints.items(): node = game.world_list.node_by_identifier(identifier) source_world = world_list.nodes_to_world(node) source_name = world_list.node_name(node) hint_text = exporter.create_message_for_hint( hint, all_patches, players, False) # FIXME: tell the room name instead of the pickup name if hint.target is None: hinted_pickup = "" else: target = patches.pickup_assignment.get(hint.target) if target is None: hinted_pickup = "Nothing" else: hinted_pickup = target.pickup.name if players.is_multiworld: hinted_pickup = f"{players.player_names[target.player]}'s {hinted_pickup}" per_world[source_world.name][source_name] = (hint_text, hinted_pickup) for world_name, world_contents in iterate_key_sorted(per_world): world_item = QtWidgets.QTreeWidgetItem(self.tree_widget) world_item.setText(0, world_name) world_item.setExpanded(True) for source_name, content in iterate_key_sorted(world_contents): area_item = QtWidgets.QTreeWidgetItem(world_item) area_item.setText(0, source_name) area_item.setText(1, content[1]) area_item.setText(2, content[0]) self.tree_widget.resizeColumnToContents(0) self.tree_widget.resizeColumnToContents(1) self.tree_widget.resizeColumnToContents(3)
async def create_player_pool(rng: Random, configuration: BaseConfiguration, player_index: int, num_players: int, rng_required: bool = True) -> PlayerPool: game = filtered_database.game_description_for_layout( configuration).get_mutable() game_generator = game.game.generator game.resource_database = game_generator.bootstrap.patch_resource_database( game.resource_database, configuration) base_patches = game_generator.base_patches_factory.create_base_patches( configuration, rng, game, num_players > 1, player_index=player_index, rng_required=rng_required) base_patches = dock_weakness_distributor.distribute_pre_fill_weaknesses( base_patches) base_patches = await game_generator.hint_distributor.assign_pre_filler_hints( base_patches, PreFillParams( rng, configuration, game, num_players > 1, ), rng_required=rng_required) pool_results = pool_creator.calculate_pool_results( configuration, game.resource_database, base_patches, rng, rng_required=rng_required) target_assignment = [(index, PickupTarget(pickup, player_index)) for index, pickup in pool_results.assignment.items()] patches = base_patches.assign_new_pickups( target_assignment).assign_extra_initial_items( pool_results.initial_resources.as_resource_gain()) return PlayerPool( game=game, game_generator=game_generator, configuration=configuration, patches=patches, pickups=pool_results.pickups, )
def update_content(self, configuration: BaseConfiguration, all_patches: dict[int, GamePatches], players: PlayersConfiguration): patches = all_patches[players.player_index] pickup_names = { pickup.pickup.name for pickup in patches.pickup_assignment.values() } game_description = filtered_database.game_description_for_layout( configuration) self._create_pickup_spoilers(game_description) starting_area = game_description.world_list.area_by_area_location( patches.starting_location) extra_items = item_names.additional_starting_items( configuration, game_description.resource_database, patches.starting_items) self.spoiler_starting_location_label.setText( "Starting Location: {}".format( game_description.world_list.area_name(starting_area))) self.spoiler_starting_items_label.setText( "Random Starting Items: {}".format( ", ".join(extra_items) if extra_items else "None")) self._update_show_all_button_state() self.pickup_spoiler_pickup_combobox.clear() self.pickup_spoiler_pickup_combobox.addItem("None") for pickup_name in sorted(pickup_names): self.pickup_spoiler_pickup_combobox.addItem(pickup_name) for pickup_button in self.pickup_spoiler_buttons: pickup_target = patches.pickup_assignment.get( pickup_button.pickup_index) pickup_button.target_player = None if pickup_target is not None: pickup_button.item_name = pickup_target.pickup.name if players.is_multiworld: pickup_button.target_player = pickup_target.player pickup_button.player_names = players.player_names else: pickup_button.item_name = "Nothing" if not pickup_button.item_is_hidden: pickup_button.setText(pickup_button.item_name)
def setup_resolver(configuration: BaseConfiguration, patches: GamePatches) -> Tuple[State, Logic]: set_attempts(0) game = filtered_database.game_description_for_layout( configuration).get_mutable() bootstrap = game.game.generator.bootstrap game.resource_database = bootstrap.patch_resource_database( game.resource_database, configuration) new_game, starting_state = bootstrap.logic_bootstrap( configuration, game, patches) logic = Logic(new_game, configuration) starting_state.resources.add_self_as_requirement_to_resources = True return starting_state, logic
def __init__(self, description: LayoutDescription, players_config: PlayersConfiguration, cosmetic_patches: BaseCosmeticPatches): self.description = description self.players_config = players_config self.cosmetic_patches = cosmetic_patches self.item_db = default_database.item_database_for_game( self.game_enum()) self.patches = description.all_patches[players_config.player_index] self.configuration = description.get_preset( players_config.player_index).configuration self.rng = Random( description.get_seed_for_player(players_config.player_index)) self.game = filtered_database.game_description_for_layout( self.configuration)
def run_bootstrap(preset: Preset): game = filtered_database.game_description_for_layout(preset.configuration).get_mutable() generator = game.game.generator derived_nodes.create_derived_nodes(game) game.resource_database = generator.bootstrap.patch_resource_database(game.resource_database, preset.configuration) permalink = GeneratorParameters( seed_number=15000, spoiler=True, presets=[preset], ) patches = generator.base_patches_factory.create_base_patches(preset.configuration, Random(15000), game, False, player_index=0) _, state = generator.bootstrap.logic_bootstrap(preset.configuration, game, patches) return game, state, permalink
def calculate_pool_item_count(layout: BaseConfiguration) -> Tuple[int, int]: """ Calculate how many pickups are needed for given layout, with how many spots are there. :param layout: :return: """ game_description = filtered_database.game_description_for_layout(layout) num_pickup_nodes = game_description.world_list.num_pickup_nodes pool_pickups, pool_assignment, _ = calculate_pool_results(layout, game_description.resource_database, rng_required=False) min_starting_items = layout.major_items_configuration.minimum_random_starting_items pool_count = len(pool_pickups) + len(pool_assignment) maximum_size = num_pickup_nodes + min_starting_items return pool_count, maximum_size
def decode( game_modifications: List[dict], layout_configurations: Dict[int, BaseConfiguration], ) -> Dict[int, GamePatches]: all_games = { index: filtered_database.game_description_for_layout(configuration) for index, configuration in layout_configurations.items() } all_pools = { index: pool_creator.calculate_pool_results(configuration, all_games[index].resource_database) for index, configuration in layout_configurations.items() } return { index: decode_single(index, all_pools, all_games[index], modifications, layout_configurations[index]) for index, modifications in enumerate(game_modifications) }
async def create_player_pool(rng: Random, configuration: BaseConfiguration, player_index: int, num_players: int, rng_required: bool = True) -> PlayerPool: game = filtered_database.game_description_for_layout(configuration).get_mutable() derived_nodes.create_derived_nodes(game) game_generator = game.game.generator game.resource_database = game_generator.bootstrap.patch_resource_database(game.resource_database, configuration) base_patches = game_generator.base_patches_factory.create_base_patches(configuration, rng, game, num_players > 1, player_index=player_index, rng_required=rng_required) base_patches = await game_generator.hint_distributor.assign_pre_filler_hints( base_patches, PreFillParams( rng, configuration, game, num_players > 1, ), rng_required=rng_required ) item_pool, pickup_assignment, initial_items = pool_creator.calculate_pool_results(configuration, game.resource_database, base_patches, rng, rng_required=rng_required) target_assignment = { index: PickupTarget(pickup, player_index) for index, pickup in pickup_assignment.items() } patches = base_patches.assign_pickup_assignment(target_assignment).assign_extra_initial_items(initial_items) return PlayerPool( game=game, game_generator=game_generator, configuration=configuration, patches=patches, pickups=item_pool, )
def create_message_for_hint( self, hint: Hint, all_patches: dict[int, GamePatches], players_config: PlayersConfiguration, with_color: bool, ) -> str: patches = all_patches[players_config.player_index] if hint.hint_type == HintType.JOKE: return self.namer.format_joke(self.create_joke_hint(), with_color) elif hint.hint_type == HintType.RED_TEMPLE_KEY_SET: return create_temple_key_hint(all_patches, players_config.player_index, hint.dark_temple, self.namer, with_color) else: assert hint.hint_type == HintType.LOCATION configuration = all_patches[ players_config.player_index].configuration pickup_target = patches.pickup_assignment.get(hint.target) phint = pickup_hint.create_pickup_hint( patches.pickup_assignment, filtered_database.game_description_for_layout( configuration).world_list, hint.precision.item, pickup_target, players_config, hint.precision.include_owner, ) return self.namer.format_location_hint( configuration.game, phint, hint, with_color, )
def update_content(self, configuration: BaseConfiguration, all_patches: dict[int, GamePatches], players: PlayersConfiguration): self.tree_widget.clear() self.tree_widget.setColumnCount(2) self.tree_widget.setHeaderLabels(["Gate", "Requirement"]) game = filtered_database.game_description_for_layout(configuration) world_list = game.world_list patches = all_patches[players.player_index] gate_index_to_name, identifier_to_gate = gate_data() resource_db = game.resource_database items_by_id = { item.extra["item_id"]: item.long_name for item in resource_db.item if "item_id" in item.extra } per_world: dict[str, dict[str, str]] = collections.defaultdict(dict) for source_loc, requirement in patches.configurable_nodes.items(): source_world = world_list.world_by_area_location(source_loc.area_identifier) source_name = gate_index_to_name[identifier_to_gate[source_loc]] index = patch_data_factory.translator_index_for_requirement(requirement) per_world[source_world.name][source_name] = items_by_id[index] for world_name, world_contents in iterate_key_sorted(per_world): world_item = QtWidgets.QTreeWidgetItem(self.tree_widget) world_item.setText(0, world_name) world_item.setExpanded(True) for source_name, destination in iterate_key_sorted(world_contents): area_item = QtWidgets.QTreeWidgetItem(world_item) area_item.setText(0, source_name) area_item.setText(1, destination) self.tree_widget.resizeColumnToContents(0) self.tree_widget.resizeColumnToContents(1)
async def resolve( configuration: BaseConfiguration, patches: GamePatches, status_update: Optional[Callable[[str], None]] = None) -> Optional[State]: if status_update is None: status_update = _quiet_print game = filtered_database.game_description_for_layout( configuration).get_mutable() derived_nodes.create_derived_nodes(game) bootstrap = game.game.generator.bootstrap game.resource_database = bootstrap.patch_resource_database( game.resource_database, configuration) event_pickup.replace_with_event_pickups(game) new_game, starting_state = bootstrap.logic_bootstrap( configuration, game, patches) logic = Logic(new_game, configuration) starting_state.resources["add_self_as_requirement_to_resources"] = 1 debug.log_resolve_start() return await advance_depth(starting_state, logic, status_update)
def __init__(self, patches: GamePatches, distance_painter: Callable[[str, bool], str]): self.world_list = filtered_database.game_description_for_layout(patches.configuration).world_list self.patches = patches self.distance_painter = distance_painter