def test_gate_assignment_for_configuration_all_random( echoes_resource_database): # Setup violet = find_resource_info_with_long_name(echoes_resource_database.item, "Violet Translator") emerald = find_resource_info_with_long_name(echoes_resource_database.item, "Emerald Translator") configuration = MagicMock() configuration.translator_configuration.translator_requirement = { TranslatorGate(index): LayoutTranslatorRequirement.RANDOM for index in [1, 15, 23] } rng = MagicMock() rng.choice.side_effect = [ LayoutTranslatorRequirement.EMERALD, LayoutTranslatorRequirement.VIOLET, LayoutTranslatorRequirement.EMERALD, ] # Run results = base_patches_factory.gate_assignment_for_configuration( configuration, echoes_resource_database, rng) # Assert assert results == { TranslatorGate(1): emerald, TranslatorGate(15): violet, TranslatorGate(23): emerald, }
def create_tracker(self): self.delete_tracker() game_enum = RandovaniaGame.PRIME2 resource_database = default_database.resource_database_for(game_enum) with get_data_path().joinpath(f"gui_assets/tracker/{game_enum.value}.json").open("r") as tracker_details_file: tracker_details = json.load(tracker_details_file) for element in tracker_details["elements"]: text_template = "" if "image_path" in element: image_path = get_data_path().joinpath(element["image_path"]) pixmap = QPixmap(str(image_path)) label = ClickableLabel(self.inventory_group, paint_with_opacity(pixmap, 0.3), paint_with_opacity(pixmap, 1.0)) label.set_checked(False) label.set_ignore_mouse_events(True) elif "label" in element: label = QLabel(self.inventory_group) label.setAlignment(Qt.AlignCenter) text_template = element["label"] else: raise ValueError(f"Invalid element: {element}") resources = [ find_resource_info_with_long_name(resource_database.item, resource_name) for resource_name in element["resources"] ] self._tracker_elements.append(Element(label, resources, text_template)) self.inventory_layout.addWidget(label, element["row"], element["column"])
def __init__(self, game_connection: GameConnection, options: Options): super().__init__() self.setupUi(self) self.game_connection = game_connection common_qt_lib.set_default_window_icon(self) self.game_data = data_reader.decode_data( default_data.decode_default_prime2()) self._energy_tank_item = find_resource_info_with_long_name( self.game_data.resource_database.item, "Energy Tank") self._item_to_label: Dict[ItemResourceInfo, ClickableLabel] = {} self._labels_for_keys = [] self.create_tracker() self.game_connection_setup = GameConnectionSetup( self, self.game_connection_tool, self.connection_status_label, self.game_connection, options) self.force_update_button.setEnabled(not options.tracking_inventory) self.force_update_button.clicked.connect(self.on_force_update_button) self._update_timer = QTimer(self) self._update_timer.setInterval(100) self._update_timer.timeout.connect(self._on_timer_update) self._update_timer.setSingleShot(True)
def list_paths_with_resource_logic(args): gd = load_game_description(args) resource = None for resource_type in gd.resource_database: try: resource = find_resource_info_with_long_name( resource_type, args.resource) break except MissingResource: continue if resource is None: print(f"A resource named {args.resource} was not found.") raise SystemExit(1) _list_paths_with_resource(gd, args.print_only_area, resource, None)
def logic_bootstrap( configuration: LayoutConfiguration, game: GameDescription, patches: GamePatches, ) -> Tuple[GameDescription, State]: """ Core code for starting a new Logic/State. :param configuration: :param game: :param patches: :return: """ # global state for easy printing functions debug._gd = game game = copy.deepcopy(game) starting_state = calculate_starting_state(game, patches) if configuration.trick_level_configuration.global_level == LayoutTrickLevel.MINIMAL_RESTRICTIONS: major_items_config = configuration.major_items_configuration _add_minimal_restrictions_initial_resources( starting_state.resources, game.resource_database, major_items_config.progressive_grapple, major_items_config.progressive_suit, ) difficulty_level, static_resources = static_resources_for_layout_logic( configuration.trick_level_configuration, game.resource_database) add_resources_into_another(starting_state.resources, static_resources) add_resources_into_another( starting_state.resources, _create_vanilla_translator_resources(game.resource_database, configuration.elevators)) starting_state.resources[ game.resource_database.difficulty_resource] = difficulty_level # All version differences are patched out from the game starting_state.resources[find_resource_info_with_long_name( game.resource_database.version, "NTSC")] = 1 game.patch_requirements(starting_state.resources, configuration.damage_strictness.value) return game, starting_state
def __init__(self, game_connection: GameConnection): super().__init__() self.setupUi(self) self.game_connection = game_connection common_qt_lib.set_default_window_icon(self) self.game_data = data_reader.decode_data( default_data.decode_default_prime2()) self._energy_tank_item = find_resource_info_with_long_name( self.game_data.resource_database.item, "Energy Tank") self._item_to_label: Dict[ItemResourceInfo, ClickableLabel] = {} self._labels_for_keys = [] self.create_tracker() self._update_timer = QTimer(self) self._update_timer.setInterval(100) self._update_timer.timeout.connect(self._on_timer_update) self._update_timer.setSingleShot(True) self.game_connection.StatusUpdated.connect(self._game_status_updated)
def test_gate_assignment_for_configuration_all_emerald( echoes_resource_database): # Setup emerald = find_resource_info_with_long_name(echoes_resource_database.item, "Emerald Translator") indices = [1, 15, 23] configuration = MagicMock() configuration.translator_configuration.translator_requirement = { TranslatorGate(index): LayoutTranslatorRequirement.EMERALD for index in indices } rng = MagicMock() # Run results = base_patches_factory.gate_assignment_for_configuration( configuration, echoes_resource_database, rng) # Assert assert results == {TranslatorGate(index): emerald for index in indices}
def static_resources_for_layout_logic( configuration: TrickLevelConfiguration, resource_database: ResourceDatabase, ) -> CurrentResources: """ :param configuration: :param resource_database: :return: """ static_resources = {} for trick in resource_database.trick: static_resources[trick] = configuration.level_for_trick( trick).as_number # Room Rando room_rando = find_resource_info_with_long_name(resource_database.misc, "Room Randomizer") static_resources[room_rando] = 0 return static_resources
def _create_vanilla_translator_resources( resource_database: ResourceDatabase, elevators: LayoutElevators, ) -> CurrentResources: """ :param resource_database: :param translator_configuration: :return: """ events = [ ("Vanilla GFMC Compound Translator Gate", False), ("Vanilla Torvus Temple Translator Gate", False), ("Vanilla Great Temple Emerald Translator Gate", elevators == LayoutElevators.VANILLA), ] return { find_resource_info_with_long_name(resource_database.misc, name): 1 if active else 0 for name, active in events }
def test_bit_pack_pickup_entry(has_convert: bool, echoes_resource_database): # Setup name = "Some Random Name" if has_convert: convert_resources = (ResourceConversion( find_resource_info_with_long_name(echoes_resource_database.item, "Morph Ball"), find_resource_info_with_long_name(echoes_resource_database.item, "Item Percentage")), ) else: convert_resources = () pickup = PickupEntry( name=name, model_index=26, item_category=ItemCategory.TEMPLE_KEY, broad_category=ItemCategory.KEY, resources=(ConditionalResources( "Morph Ball", None, ( (find_resource_info_with_long_name( echoes_resource_database.item, "Morph Ball"), 2), (find_resource_info_with_long_name( echoes_resource_database.item, "Item Percentage"), 5), ), ), ConditionalResources( "Grapple Beam", find_resource_info_with_long_name( echoes_resource_database.item, "Morph Ball"), ((find_resource_info_with_long_name( echoes_resource_database.item, "Grapple Beam"), 3), ), )), convert_resources=convert_resources) # Run encoded = bitpacking.pack_value( BitPackPickupEntry(pickup, echoes_resource_database)) decoder = BitPackDecoder(encoded) decoded = BitPackPickupEntry.bit_pack_unpack(decoder, name, echoes_resource_database) # Assert assert pickup == decoded
def _create_vanilla_translator_resources( resource_database: ResourceDatabase, translator_configuration: TranslatorConfiguration, ) -> CurrentResources: """ :param resource_database: :param translator_configuration: :return: """ events = [ ("Vanilla GFMC Compound Translator Gate", not translator_configuration.fixed_gfmc_compound), ("Vanilla Torvus Temple Translator Gate", not translator_configuration.fixed_torvus_temple), ("Vanilla Great Temple Emerald Translator Gate", not translator_configuration.fixed_great_temple), ] return { find_resource_info_with_long_name(resource_database.trick, name): 1 if active else 0 for name, active in events }
def read_node(self, data: Dict) -> Node: name: str = data["name"] heal: bool = data["heal"] node_type: int = data["node_type"] self.generic_index += 1 if node_type == 0: return GenericNode(name, heal, self.generic_index) elif node_type == 1: return DockNode( name, heal, self.generic_index, data["dock_index"], DockConnection(data["connected_area_asset_id"], data["connected_dock_index"]), self.dock_weakness_database.get_by_type_and_index( DockType(data["dock_type"]), data["dock_weakness_index"])) elif node_type == 2: return PickupNode(name, heal, self.generic_index, PickupIndex(data["pickup_index"]), data["major_location"]) elif node_type == 3: instance_id = data["teleporter_instance_id"] destination_world_asset_id = data["destination_world_asset_id"] destination_area_asset_id = data["destination_area_asset_id"] return TeleporterNode( name, heal, self.generic_index, instance_id, AreaLocation(destination_world_asset_id, destination_area_asset_id), data["keep_name_when_vanilla"], data["editable"], ) elif node_type == 4: return EventNode( name, heal, self.generic_index, self.resource_database.get_by_type_and_index( ResourceType.EVENT, data["event_index"])) elif node_type == 5: return TranslatorGateNode( name, heal, self.generic_index, TranslatorGate(data["gate_index"]), find_resource_info_with_long_name(self.resource_database.item, "Scan Visor")) elif node_type == 6: lore_type = list(LoreType)[data["lore_type"]] if lore_type == LoreType.LUMINOTH_LORE: required_translator = self.resource_database.get_item( data["extra"]) else: required_translator = None if lore_type in { LoreType.LUMINOTH_WARRIOR, LoreType.SKY_TEMPLE_KEY_HINT }: hint_index = data["extra"] else: hint_index = None return LogbookNode( name, heal, self.generic_index, data["string_asset_id"], find_resource_info_with_long_name(self.resource_database.item, "Scan Visor"), lore_type, required_translator, hint_index) else: raise Exception("Unknown node type: {}".format(node_type))
def _patches_with_data(request, echoes_game_data, echoes_item_database): game = data_reader.decode_data(echoes_game_data) data = { "starting_location": "Temple Grounds/Landing Site", "starting_items": {}, "elevators": { "Temple Grounds/Temple Transport C": "Great Temple/Temple Transport C", "Temple Grounds/Transport to Agon Wastes": "Agon Wastes/Transport to Temple Grounds", "Temple Grounds/Transport to Torvus Bog": "Torvus Bog/Transport to Temple Grounds", "Temple Grounds/Temple Transport B": "Great Temple/Temple Transport B", "Temple Grounds/Transport to Sanctuary Fortress": "Sanctuary Fortress/Transport to Temple Grounds", "Temple Grounds/Temple Transport A": "Great Temple/Temple Transport A", "Great Temple/Temple Transport A": "Temple Grounds/Temple Transport A", "Great Temple/Temple Transport C": "Temple Grounds/Temple Transport C", "Great Temple/Temple Transport B": "Temple Grounds/Temple Transport B", "Sky Temple Grounds/Sky Temple Gateway": "Sky Temple/Sky Temple Energy Controller", "Sky Temple/Sky Temple Energy Controller": "Sky Temple Grounds/Sky Temple Gateway", "Agon Wastes/Transport to Temple Grounds": "Temple Grounds/Transport to Agon Wastes", "Agon Wastes/Transport to Torvus Bog": "Torvus Bog/Transport to Agon Wastes", "Agon Wastes/Transport to Sanctuary Fortress": "Sanctuary Fortress/Transport to Agon Wastes", "Torvus Bog/Transport to Temple Grounds": "Temple Grounds/Transport to Torvus Bog", "Torvus Bog/Transport to Agon Wastes": "Agon Wastes/Transport to Torvus Bog", "Torvus Bog/Transport to Sanctuary Fortress": "Sanctuary Fortress/Transport to Torvus Bog", "Sanctuary Fortress/Transport to Temple Grounds": "Temple Grounds/Transport to Sanctuary Fortress", "Sanctuary Fortress/Transport to Agon Wastes": "Agon Wastes/Transport to Sanctuary Fortress", "Sanctuary Fortress/Transport to Torvus Bog": "Torvus Bog/Transport to Sanctuary Fortress", "Sanctuary Fortress/Aerie": "Sanctuary Fortress/Aerie Transport Station", "Sanctuary Fortress/Aerie Transport Station": "Sanctuary Fortress/Aerie", }, "translators": {}, "locations": {}, "hints": {}, "_locations_internal": "", } patches = game.create_game_patches() locations = collections.defaultdict(dict) for world, area, node in game.world_list.all_worlds_areas_nodes: if node.is_resource_node and isinstance(node, PickupNode): world_name = world.dark_name if area.in_dark_aether else world.name locations[world_name][game.world_list.node_name(node)] = game_patches_serializer._ETM_NAME data["locations"] = { world: { area: item for area, item in sorted(locations[world].items()) } for world in sorted(locations.keys()) } if request.param.get("starting_item"): item_name = request.param.get("starting_item") patches = patches.assign_extra_initial_items({ find_resource_info_with_long_name(game.resource_database.item, item_name): 1, }) data["starting_items"][item_name] = 1 if request.param.get("elevator"): elevator_id, elevator_source = request.param.get("elevator") elevator_connection = copy.copy(patches.elevator_connection) elevator_connection[elevator_id] = game.starting_location patches = dataclasses.replace(patches, elevator_connection=elevator_connection) data["elevators"][elevator_source] = "Temple Grounds/Landing Site" if request.param.get("translator"): gates = {} for index, gate_name, translator in request.param.get("translator"): gates[TranslatorGate(index)] = find_resource_info_with_long_name(game.resource_database.item, translator) data["translators"][gate_name] = translator patches = patches.assign_gate_assignment(gates) if request.param.get("pickup"): data["_locations_internal"], pickup_name = request.param.get("pickup") pickup = pickup_creator.create_major_item(echoes_item_database.major_items[pickup_name], MajorItemState(), True, game.resource_database, None, False) patches = patches.assign_new_pickups([(PickupIndex(5), pickup)]) data["locations"]["Temple Grounds"]['Transport to Agon Wastes/Pickup (Missile)'] = pickup_name if request.param.get("hint"): asset, hint = request.param.get("hint") patches = patches.assign_hint(LogbookAsset(asset), Hint.from_json(hint)) data["hints"][str(asset)] = hint return data, patches
def _get_command_visor(self) -> ItemResourceInfo: return find_resource_info_with_long_name(self.resource_database.item, "Command Visor")
def _patches_with_data(request, echoes_game_data, echoes_item_database): game = data_reader.decode_data(echoes_game_data) data = { "starting_location": "Temple Grounds/Landing Site", "starting_items": {}, "elevators": { "Temple Grounds/Temple Transport C": "Great Temple/Temple Transport C", "Temple Grounds/Transport to Agon Wastes": "Agon Wastes/Transport to Temple Grounds", "Temple Grounds/Transport to Torvus Bog": "Torvus Bog/Transport to Temple Grounds", "Temple Grounds/Temple Transport B": "Great Temple/Temple Transport B", "Temple Grounds/Sky Temple Gateway": "Great Temple/Sky Temple Energy Controller", "Temple Grounds/Transport to Sanctuary Fortress": "Sanctuary Fortress/Transport to Temple Grounds", "Temple Grounds/Temple Transport A": "Great Temple/Temple Transport A", "Great Temple/Temple Transport A": "Temple Grounds/Temple Transport A", "Great Temple/Temple Transport C": "Temple Grounds/Temple Transport C", "Great Temple/Temple Transport B": "Temple Grounds/Temple Transport B", "Great Temple/Sky Temple Energy Controller": "Temple Grounds/Sky Temple Gateway", "Agon Wastes/Transport to Temple Grounds": "Temple Grounds/Transport to Agon Wastes", "Agon Wastes/Transport to Torvus Bog": "Torvus Bog/Transport to Agon Wastes", "Agon Wastes/Transport to Sanctuary Fortress": "Sanctuary Fortress/Transport to Agon Wastes", "Torvus Bog/Transport to Temple Grounds": "Temple Grounds/Transport to Torvus Bog", "Torvus Bog/Transport to Agon Wastes": "Agon Wastes/Transport to Torvus Bog", "Torvus Bog/Transport to Sanctuary Fortress": "Sanctuary Fortress/Transport to Torvus Bog", "Sanctuary Fortress/Transport to Temple Grounds": "Temple Grounds/Transport to Sanctuary Fortress", "Sanctuary Fortress/Transport to Agon Wastes": "Agon Wastes/Transport to Sanctuary Fortress", "Sanctuary Fortress/Transport to Torvus Bog": "Torvus Bog/Transport to Sanctuary Fortress" }, "translators": {}, "locations": { world.name: { game.world_list.node_name(node): "Nothing" for node in world.all_nodes if node.is_resource_node and isinstance(node, PickupNode) } for world in sorted(game.world_list.worlds, key=lambda w: w.name) }, "hints": {}, "_locations_internal": "", } patches = GamePatches.with_game(game) if request.param.get("starting_item"): item_name = request.param.get("starting_item") patches = patches.assign_extra_initial_items({ find_resource_info_with_long_name(game.resource_database.item, item_name): 1, }) data["starting_items"][item_name] = 1 if request.param.get("elevator"): elevator_id, elevator_source = request.param.get("elevator") elevator_connection = copy.copy(patches.elevator_connection) elevator_connection[elevator_id] = game.starting_location patches = dataclasses.replace(patches, elevator_connection=elevator_connection) data["elevators"][elevator_source] = "Temple Grounds/Landing Site" if request.param.get("translator"): gates = {} for index, gate_name, translator in request.param.get("translator"): gates[TranslatorGate(index)] = find_resource_info_with_long_name( game.resource_database.item, translator) data["translators"][gate_name] = translator patches = patches.assign_gate_assignment(gates) if request.param.get("pickup"): data["_locations_internal"], pickup_name = request.param.get("pickup") pickup = pickup_creator.create_major_item( echoes_item_database.major_items[pickup_name], MajorItemState(), True, game.resource_database, None, False) patches = patches.assign_new_pickups([(PickupIndex(5), pickup)]) data["locations"]["Temple Grounds"][ 'Transport to Agon Wastes/Pickup (Missile)'] = pickup_name if request.param.get("hint"): asset, hint = request.param.get("hint") patches = patches.assign_hint(LogbookAsset(asset), Hint.from_json(hint)) data["hints"][str(asset)] = hint return data, patches
def decode_single(player_index: int, num_players: int, game_modifications: dict, configuration: LayoutConfiguration) -> GamePatches: """ Decodes a dict created by `serialize` back into a GamePatches. :param game_modifications: :param player_index: :param num_players: :param configuration: :return: """ game = data_reader.decode_data(configuration.game_data) game_specific = dataclasses.replace( game.game_specific, energy_per_tank=configuration.energy_per_tank, beam_configurations=configuration.beam_configuration.create_game_specific(game.resource_database)) world_list = game.world_list # Starting Location starting_location = _area_name_to_area_location(world_list, game_modifications["starting_location"]) # Initial items starting_items = { find_resource_info_with_long_name(game.resource_database.item, resource_name): quantity for resource_name, quantity in game_modifications["starting_items"].items() } # Elevators elevator_connection = {} for source_name, target_name in game_modifications["elevators"].items(): source_area = _area_name_to_area_location(world_list, source_name) target_area = _area_name_to_area_location(world_list, target_name) potential_source_nodes = [ node for node in world_list.area_by_area_location(source_area).nodes if isinstance(node, TeleporterNode) ] assert len(potential_source_nodes) == 1 source_node = potential_source_nodes[0] elevator_connection[source_node.teleporter_instance_id] = target_area # Translator Gates translator_gates = { _find_gate_with_name(gate_name): find_resource_info_with_long_name(game.resource_database.item, resource_name) for gate_name, resource_name in game_modifications["translators"].items() } # Pickups target_name_re = re.compile(r"(.*) for Player \d+") index_to_pickup_name = {} for world_name, world_data in game_modifications["locations"].items(): for area_node_name, target_name in world_data.items(): if target_name == _ETM_NAME: continue pickup_name_match = target_name_re.match(target_name) if pickup_name_match is not None: pickup_name = pickup_name_match.group(1) else: pickup_name = target_name node = world_list.node_from_name(f"{world_name}/{area_node_name}") assert isinstance(node, PickupNode) index_to_pickup_name[node.pickup_index] = pickup_name decoder = BitPackDecoder(base64.b64decode(game_modifications["_locations_internal"].encode("utf-8"), validate=True)) pickup_assignment = dict(BitPackPickupEntryList.bit_pack_unpack(decoder, { "index_mapping": index_to_pickup_name, "num_players": num_players, "database": game.resource_database, }).value) # Hints hints = {} for asset_id, hint in game_modifications["hints"].items(): hints[LogbookAsset(int(asset_id))] = Hint.from_json(hint) return GamePatches( player_index=player_index, pickup_assignment=pickup_assignment, # PickupAssignment elevator_connection=elevator_connection, # Dict[int, AreaLocation] dock_connection={}, # Dict[Tuple[int, int], DockConnection] dock_weakness={}, # Dict[Tuple[int, int], DockWeakness] translator_gates=translator_gates, starting_items=starting_items, # ResourceGainTuple starting_location=starting_location, # AreaLocation hints=hints, game_specific=game_specific, )
def decode(game_modifications: dict, configuration: LayoutConfiguration) -> GamePatches: """ Decodes a dict created by `serialize` back into a GamePatches. :param game_modifications: :param configuration: :return: """ game = data_reader.decode_data(configuration.game_data) world_list = game.world_list # Starting Location starting_location = _area_name_to_area_location( world_list, game_modifications["starting_location"]) # Initial items starting_items = { find_resource_info_with_long_name(game.resource_database.item, resource_name): quantity for resource_name, quantity in game_modifications["starting_items"].items() } # Elevators elevator_connection = {} for source_name, target_name in game_modifications["elevators"].items(): source_area = _area_name_to_area_location(world_list, source_name) target_area = _area_name_to_area_location(world_list, target_name) potential_source_nodes = [ node for node in world_list.area_by_area_location(source_area).nodes if isinstance(node, TeleporterNode) ] assert len(potential_source_nodes) == 1 source_node = potential_source_nodes[0] elevator_connection[source_node.teleporter_instance_id] = target_area # Translator Gates translator_gates = { _find_gate_with_name(gate_name): find_resource_info_with_long_name(game.resource_database.item, resource_name) for gate_name, resource_name in game_modifications["translators"].items() } # Pickups pickup_assignment = {} decoder = BitPackDecoder( base64.b64decode( game_modifications["_locations_internal"].encode("utf-8"), validate=True)) for world_name, world_data in game_modifications["locations"].items(): for area_node_name, pickup_name in world_data.items(): if pickup_name == "Nothing": continue node = world_list.node_from_name(f"{world_name}/{area_node_name}") assert isinstance(node, PickupNode) pickup = BitPackPickupEntry.bit_pack_unpack( decoder, pickup_name, game.resource_database) pickup_assignment[node.pickup_index] = pickup # Hints hints = {} for asset_id, hint in game_modifications["hints"].items(): hints[LogbookAsset(int(asset_id))] = Hint.from_json(hint) return GamePatches( pickup_assignment=pickup_assignment, # PickupAssignment elevator_connection=elevator_connection, # Dict[int, AreaLocation] dock_connection={}, # Dict[Tuple[int, int], DockConnection] dock_weakness={}, # Dict[Tuple[int, int], DockWeakness] translator_gates=translator_gates, starting_items=starting_items, # ResourceGainTuple starting_location=starting_location, # AreaLocation hints=hints, )
def find_resource(name: str): return find_resource_info_with_long_name( self.game_data.resource_database.item, name)
def _get_scan_visor(self) -> ItemResourceInfo: return find_resource_info_with_long_name(self.resource_database.item, "Scan Visor")
def decode_single(player_index: int, all_pools: Dict[int, PoolResults], game: GameDescription, game_modifications: dict, configuration: LayoutConfiguration) -> GamePatches: """ Decodes a dict created by `serialize` back into a GamePatches. :param player_index: :param all_pools: :param game: :param game_modifications: :param configuration: :return: """ game_specific = base_patches_factory.create_game_specific( configuration, game) world_list = game.world_list initial_pickup_assignment = all_pools[player_index].assignment # Starting Location starting_location = _area_name_to_area_location( world_list, game_modifications["starting_location"]) # Initial items starting_items = { find_resource_info_with_long_name(game.resource_database.item, resource_name): quantity for resource_name, quantity in game_modifications["starting_items"].items() } # Elevators elevator_connection = {} for source_name, target_name in game_modifications["elevators"].items(): source_area = _area_name_to_area_location(world_list, source_name) target_area = _area_name_to_area_location(world_list, target_name) potential_source_nodes = [ node for node in world_list.area_by_area_location(source_area).nodes if isinstance(node, TeleporterNode) ] assert len(potential_source_nodes) == 1 source_node = potential_source_nodes[0] elevator_connection[source_node.teleporter_instance_id] = target_area # Translator Gates translator_gates = { _find_gate_with_name(gate_name): find_resource_info_with_long_name(game.resource_database.item, resource_name) for gate_name, resource_name in game_modifications["translators"].items() } # Pickups target_name_re = re.compile(r"(.*) for Player (\d+)") pickup_assignment: PickupAssignment = {} for world_name, world_data in game_modifications["locations"].items(): for area_node_name, target_name in world_data.items(): if target_name == _ETM_NAME: continue pickup_name_match = target_name_re.match(target_name) if pickup_name_match is not None: pickup_name = pickup_name_match.group(1) target_player = int(pickup_name_match.group(2)) else: pickup_name = target_name target_player = 0 node = world_list.node_from_name(f"{world_name}/{area_node_name}") assert isinstance(node, PickupNode) if node.pickup_index in initial_pickup_assignment: pickup = initial_pickup_assignment[node.pickup_index] if (pickup_name, target_player) != (pickup.name, player_index): raise ValueError( f"{area_node_name} should be vanilla based on configuration" ) elif pickup_name != _ETM_NAME: configuration_item_pool = all_pools[target_player].pickups pickup = _find_pickup_with_name(configuration_item_pool, pickup_name) configuration_item_pool.remove(pickup) else: pickup = None if pickup is not None: pickup_assignment[node.pickup_index] = PickupTarget( pickup, target_player) # Hints hints = {} for asset_id, hint in game_modifications["hints"].items(): hints[LogbookAsset(int(asset_id))] = Hint.from_json(hint) return GamePatches( player_index=player_index, pickup_assignment=pickup_assignment, # PickupAssignment elevator_connection=elevator_connection, # Dict[int, AreaLocation] dock_connection={}, # Dict[Tuple[int, int], DockConnection] dock_weakness={}, # Dict[Tuple[int, int], DockWeakness] translator_gates=translator_gates, starting_items=starting_items, # ResourceGainTuple starting_location=starting_location, # AreaLocation hints=hints, game_specific=game_specific, )