def test_connections_from_dock_blast_shield(empty_patches): # Setup trivial = Requirement.trivial() req_1 = ResourceRequirement.simple( SimpleResourceInfo(0, "Ev1", "Ev1", ResourceType.EVENT)) req_2 = ResourceRequirement.simple( SimpleResourceInfo(1, "Ev2", "Ev2", ResourceType.EVENT)) dock_type = DockType("Type", "Type", frozendict()) weak_1 = DockWeakness(0, "Weak 1", frozendict(), req_1, None) weak_2 = DockWeakness(1, "Weak 2", frozendict(), trivial, DockLock(DockLockType.FRONT_BLAST_BACK_BLAST, req_2)) node_1_identifier = NodeIdentifier.create("W", "Area 1", "Node 1") node_2_identifier = NodeIdentifier.create("W", "Area 2", "Node 2") node_1 = DockNode(node_1_identifier, 0, False, None, "", ("default", ), {}, dock_type, node_2_identifier, weak_1, None, None) node_1_lock = DockLockNode.create_from_dock(node_1, 1) node_2 = DockNode(node_2_identifier, 2, False, None, "", ("default", ), {}, dock_type, node_1_identifier, weak_2, None, None) node_2_lock = DockLockNode.create_from_dock(node_2, 3) area_1 = Area("Area 1", None, True, [node_1, node_1_lock], {}, {}) area_2 = Area("Area 2", None, True, [node_2, node_2_lock], {}, {}) world = World("W", [area_1, area_2], {}) world_list = WorldList([world]) world_list.ensure_has_node_cache() game_mock = MagicMock() game_mock.world_list = world_list patches = dataclasses.replace(empty_patches, game=game_mock) context = NodeContext( patches=patches, current_resources=ResourceCollection(), database=patches.game.resource_database, node_provider=world_list, ) # Run result_1 = list(node_1.connections_from(context)) result_2 = list(node_2.connections_from(context)) # Assert simple = ResourceRequirement.simple assert result_1 == [ (node_2, RequirementAnd( [req_1, simple(NodeResourceInfo.from_node(node_2, context))])), (node_1_lock, RequirementAnd([trivial, req_2])), ] assert result_2 == [ (node_1, simple(NodeResourceInfo.from_node(node_2, context))), (node_2_lock, req_2), ]
def test_connections_from_dock_blast_shield(empty_patches): # Setup trivial = Requirement.trivial() req_1 = ResourceRequirement( SimpleResourceInfo("Ev1", "Ev1", ResourceType.EVENT), 1, False) req_2 = ResourceRequirement( SimpleResourceInfo("Ev2", "Ev2", ResourceType.EVENT), 1, False) dock_type = DockType("Type", "Type", frozendict()) weak_1 = DockWeakness("Weak 1", frozendict(), req_1, None) weak_2 = DockWeakness("Weak 2", frozendict(), trivial, DockLock(DockLockType.FRONT_BLAST_BACK_BLAST, req_2)) node_1_identifier = NodeIdentifier.create("W", "Area 1", "Node 1") node_2_identifier = NodeIdentifier.create("W", "Area 2", "Node 2") node_1 = DockNode(node_1_identifier, False, None, "", ("default", ), {}, dock_type, node_2_identifier, weak_1, None, None) node_1_lock = DockLockNode.create_from_dock(node_1) node_2 = DockNode(node_2_identifier, False, None, "", ("default", ), {}, dock_type, node_1_identifier, weak_2, None, None) node_2_lock = DockLockNode.create_from_dock(node_2) area_1 = Area("Area 1", None, True, [node_1, node_1_lock], {}, {}) area_2 = Area("Area 2", None, True, [node_2, node_2_lock], {}, {}) world = World("W", [area_1, area_2], {}) world_list = WorldList([world]) context = NodeContext( patches=empty_patches, current_resources={}, database=None, node_provider=world_list, ) # Run result_1 = list(node_1.connections_from(context)) result_2 = list(node_2.connections_from(context)) # Assert assert result_1 == [ (node_2, RequirementAnd([req_1, ResourceRequirement.simple(node_2_identifier)])), (node_1_lock, RequirementAnd([trivial, req_2])), ] assert result_2 == [ (node_1, ResourceRequirement.simple(node_2_identifier)), (node_2_lock, req_2), ]
def move_node_from_area_to_area(self, old_area: Area, new_area: Area, node: Node): assert node in old_area.nodes if new_area.node_with_name(node.name) is not None: raise ValueError(f"New area {new_area.name} already contains a node named {node.name}") old_world = self.game.world_list.world_with_area(old_area) new_world = self.game.world_list.world_with_area(new_area) self.remove_node(old_area, node) self.add_node(new_area, node) self.replace_references_to_node_identifier( NodeIdentifier.create(old_world.name, old_area.name, node.name), NodeIdentifier.create(new_world.name, new_area.name, node.name), )
def test_add_elevator_connections_to_patches_vanilla(echoes_game_description, skip_final_bosses: bool, default_echoes_configuration, echoes_game_patches): # Setup patches_factory = echoes_game_description.game.generator.base_patches_factory expected = dataclasses.replace(echoes_game_patches, elevator_connection=echoes_game_description.get_default_elevator_connection()) if skip_final_bosses: node_ident = NodeIdentifier.create("Temple Grounds", "Sky Temple Gateway", "Teleport to Great Temple - Sky Temple Energy Controller") expected.elevator_connection[node_ident] = AreaIdentifier("Temple Grounds", "Credits") config = default_echoes_configuration config = dataclasses.replace(config, elevators=dataclasses.replace(config.elevators, skip_final_bosses=skip_final_bosses)) # Run result = patches_factory.add_elevator_connections_to_patches( config, Random(0), echoes_game_patches) # Assert assert result == expected
def replace_references_to_area_identifier(self, old_identifier: AreaIdentifier, new_identifier: AreaIdentifier): if old_identifier == new_identifier: return for world in self.game.world_list.worlds: for area in world.areas: for i in range(len(area.nodes)): node = area.nodes[i] new_node = None if isinstance(node, TeleporterNode): if node.default_connection == old_identifier: new_node = dataclasses.replace( node, identifier=node.identifier.renamed( node.name.replace(old_identifier.area_name, new_identifier.area_name) ), default_connection=new_identifier, ) elif isinstance(node, DockNode): if node.default_connection.area_identifier == old_identifier: new_node = dataclasses.replace( node, identifier=node.identifier.renamed( node.name.replace(old_identifier.area_name, new_identifier.area_name), ), default_connection=NodeIdentifier( area_identifier=new_identifier, node_name=node.default_connection.node_name, ), ) if new_node is not None: self.replace_node(area, node, new_node)
def static_teleporters(self) -> Dict[NodeIdentifier, AreaIdentifier]: static = {} if self.skip_final_bosses: if self.game == RandovaniaGame.METROID_PRIME: crater = NodeIdentifier.create( "Tallon Overworld", "Artifact Temple", "Teleport to Impact Crater - Crater Impact Point") static[crater] = AreaIdentifier("End of Game", "Credits") elif self.game == RandovaniaGame.METROID_PRIME_ECHOES: gateway = NodeIdentifier.create( "Temple Grounds", "Sky Temple Gateway", "Teleport to Great Temple - Sky Temple Energy Controller") static[gateway] = AreaIdentifier("Temple Grounds", "Credits") else: raise ValueError( f"Unsupported skip_final_bosses and {self.game}") return static
def from_json(cls, value: dict) -> "TranslatorConfiguration": default = cls.default() params = copy.copy(value) translator_requirement = copy.copy(default.translator_requirement) for key, item in params.pop("translator_requirement").items(): translator_requirement[NodeIdentifier.from_string(key)] = LayoutTranslatorRequirement(item) return cls(translator_requirement, **params)
def _logbook_node(request, blank_game_description): has_translator = request.param translator = blank_game_description.resource_database.get_item("BlueKey") node = blank_game_description.world_list.node_by_identifier(NodeIdentifier.create( "Intro", "Hint Room", "Hint with Translator" if has_translator else "Hint no Translator", )) assert isinstance(node, LogbookNode) return has_translator, translator, node
def _pickup_node(): return PickupNode( pickup_index=PickupIndex(1), major_location=True, identifier=NodeIdentifier.create("W", "A", "Pickup (Ultra Beam)"), heal=False, location=None, layers=("default", ), description="", extra={}, )
def test_sort_resource_requirement(): resources = [ NodeIdentifier.create("World", "Area", "Node"), PickupIndex(10), _make_resource("Resource"), TrickResourceInfo("Trick", "Trick", "Long Description"), ItemResourceInfo("Item", "Item", 1), ] # Assert resources has an entry for every type of ResourceInfo assert {type(it) for it in resources} == set(ResourceInfo.__args__) assert len(resources) == len(ResourceInfo.__args__) requirements = [ResourceRequirement.simple(it) for it in resources] result = sorted(requirements) assert result == list(reversed(requirements))
def test_one_way_elevator_connections(echoes_game_description, replacement, expected): # Setup rng = random.Random(5000) target_locations = [AreaIdentifier(f"w{i}", f"a{i}") for i in range(6)] elevators = [ ElevatorHelper(NodeIdentifier.create(f"w{i}", f"a{i}", f"n{i}"), AreaIdentifier(f"w{i}", f"a{i}")) for i in range(6) ] database = tuple(elevators) # Run result = elevator_distributor.one_way_elevator_connections( rng, database, target_locations, replacement) # Assert assert result == expected
def test_two_way_elevator_connections_unchecked(): # Setup rng = random.Random(5000) elevators = [ ElevatorHelper(NodeIdentifier.create(f"w{i}", f"a{i}", f"n{i}"), AreaIdentifier(f"w{i}", f"a{i}")) for i in range(6) ] database = tuple(elevators) # Run result = elevator_distributor.two_way_elevator_connections( rng, database, False) # Assert assert result == { NodeIdentifier.create("w0", "a0", "n0"): AreaIdentifier("w4", "a4"), NodeIdentifier.create("w1", "a1", "n1"): AreaIdentifier("w2", "a2"), NodeIdentifier.create("w2", "a2", "n2"): AreaIdentifier("w1", "a1"), NodeIdentifier.create("w3", "a3", "n3"): AreaIdentifier("w5", "a5"), NodeIdentifier.create("w4", "a4", "n4"): AreaIdentifier("w0", "a0"), NodeIdentifier.create("w5", "a5", "n5"): AreaIdentifier("w3", "a3"), }
def apply_previous_state(self, previous_state: Optional[dict]) -> bool: if previous_state is None: return False starting_location = None needs_starting_location = len( self.game_configuration.starting_location.locations) > 1 configurable_nodes = {} try: pickup_name_to_pickup = { pickup.name: pickup for pickup in self._collected_pickups.keys() } quantity_to_change = { pickup_name_to_pickup[pickup_name]: quantity for pickup_name, quantity in previous_state["collected_pickups"].items() } previous_actions = [ self.game_description.world_list.node_by_identifier( NodeIdentifier.from_string(identifier)) for identifier in previous_state["actions"] ] if needs_starting_location: starting_location = AreaIdentifier.from_json( previous_state["starting_location"]) teleporters: Dict[NodeIdentifier, Optional[AreaIdentifier]] = { NodeIdentifier.from_json(item["teleporter"]): (AreaIdentifier.from_json(item["data"]) if item["data"] is not None else None) for item in previous_state["elevators"] } if self.game_configuration.game == RandovaniaGame.METROID_PRIME_ECHOES: configurable_nodes = { NodeIdentifier.from_string(identifier): (LayoutTranslatorRequirement(item) if item is not None else None) for identifier, item in previous_state["configurable_nodes"].items() } except (KeyError, AttributeError): return False self.setup_starting_location(starting_location) for teleporter, area_location in teleporters.items(): combo = self._elevator_id_to_combo[teleporter] if area_location is None: combo.setCurrentIndex(0) continue for i in range(combo.count()): if area_location == combo.itemData(i): combo.setCurrentIndex(i) break for identifier, requirement in configurable_nodes.items(): combo = self._translator_gate_to_combo[identifier] for i in range(combo.count()): if requirement == combo.itemData(i): combo.setCurrentIndex(i) break self.bulk_change_quantity(quantity_to_change) self._add_new_actions(previous_actions) world_list = self.game_description.world_list state = self.state_for_current_configuration() self.focus_on_world(world_list.nodes_to_world(state.node)) self.focus_on_area(world_list.nodes_to_area(state.node)) return True
def ni(w: str, a: str, n: str, tw: str, ta: str): elevator_connection[NodeIdentifier.create(w, a, n)] = AreaIdentifier(tw, ta)
def test_create_pickup_list(model_style: PickupModelStyle, empty_patches, generic_item_category, blank_resource_db): # Setup has_scan_text = model_style in {PickupModelStyle.ALL_VISIBLE, PickupModelStyle.HIDE_MODEL} rng = Random(5000) model_0 = MagicMock(spec=PickupModel) model_1 = MagicMock(spec=PickupModel) model_2 = MagicMock(spec=PickupModel) useless_model = PickupModel( game=RandovaniaGame.METROID_PRIME_ECHOES, name="EnergyTransferModule", ) useless_resource = ItemResourceInfo(0, "Useless", "Useless", 10) resource_a = ItemResourceInfo(1, "A", "A", 10) resource_b = ItemResourceInfo(2, "B", "B", 10) pickup_a = PickupEntry("P-A", model_1, generic_item_category, generic_item_category, progression=((resource_a, 1),), ) pickup_b = PickupEntry("P-B", model_2, generic_item_category, generic_item_category, progression=((resource_b, 1), (resource_a, 5)), ) pickup_c = PickupEntry("P-C", model_2, AMMO_ITEM_CATEGORY, generic_item_category, progression=tuple(), extra_resources=((resource_b, 2), (resource_a, 1)), unlocks_resource=True, resource_lock=ResourceLock(resource_a, resource_a, useless_resource)) useless_pickup = PickupEntry("P-Useless", model_0, USELESS_ITEM_CATEGORY, USELESS_ITEM_CATEGORY, progression=((useless_resource, 1),)) patches = empty_patches.assign_new_pickups([ (PickupIndex(0), PickupTarget(pickup_a, 0)), (PickupIndex(2), PickupTarget(pickup_b, 0)), (PickupIndex(3), PickupTarget(pickup_a, 0)), (PickupIndex(4), PickupTarget(pickup_c, 0)), ]) creator = pickup_exporter.PickupExporterSolo(pickup_exporter.GenericAcquiredMemo()) world_list = MagicMock() world_list.iterate_nodes.return_value = [ PickupNode(NodeIdentifier.create("World", "Area", f"Name {i}"), i, False, None, "", ("default",), {}, PickupIndex(i), False) for i in range(5) ] # Run result = pickup_exporter.export_all_indices( patches, PickupTarget(useless_pickup, 0), world_list, rng, model_style, PickupModelDataSource.ETM, creator, pickup_creator.create_visual_etm(), ) # Assert assert len(result) == 5 assert result[0] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(0), scan_text="P-A" if has_scan_text else "Unknown item", hud_text=["A acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!'], conditional_resources=[ConditionalResources("A", None, ((resource_a, 1),))], conversion=[], model=model_1 if model_style == PickupModelStyle.ALL_VISIBLE else useless_model, other_player=False, original_pickup=pickup_a, ) assert result[1] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(1), scan_text="P-Useless" if has_scan_text else "Unknown item", hud_text=["Useless acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!'], conditional_resources=[ConditionalResources("Useless", None, ((useless_resource, 1),))], conversion=[], model=model_0 if model_style == PickupModelStyle.ALL_VISIBLE else useless_model, other_player=False, original_pickup=useless_pickup, ) assert result[2] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(2), scan_text="P-B. Provides the following in order: B, A" if has_scan_text else "Unknown item", hud_text=["B acquired!", "A acquired!"] if model_style != PickupModelStyle.HIDE_ALL else [ 'Unknown item acquired!', 'Unknown item acquired!'], conditional_resources=[ ConditionalResources("B", None, ((resource_b, 1),)), ConditionalResources("A", resource_b, ((resource_a, 5),)), ], conversion=[], model=model_2 if model_style == PickupModelStyle.ALL_VISIBLE else useless_model, other_player=False, original_pickup=pickup_b, ) assert result[3] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(3), scan_text="P-A" if has_scan_text else "Unknown item", hud_text=["A acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!'], conditional_resources=[ConditionalResources("A", None, ((resource_a, 1),))], conversion=[], model=model_1 if model_style == PickupModelStyle.ALL_VISIBLE else useless_model, other_player=False, original_pickup=pickup_a, ) assert result[4] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(4), scan_text="P-C. Provides 2 B and 1 A" if has_scan_text else "Unknown item", hud_text=["P-C acquired!"] if model_style != PickupModelStyle.HIDE_ALL else ['Unknown item acquired!'], conditional_resources=[ConditionalResources("P-C", None, ( (resource_b, 2), (resource_a, 1), ))], conversion=[ResourceConversion(source=useless_resource, target=resource_a)], model=model_2 if model_style == PickupModelStyle.ALL_VISIBLE else useless_model, other_player=False, original_pickup=pickup_c, )
def _create_new_dock(self, location: NodeLocation, target_area: Area): current_area = self.current_area target_identifier = self.world_list.identifier_for_area(target_area) source_identifier = self.world_list.identifier_for_area(current_area) dock_weakness = self.game_description.dock_weakness_database.default_weakness source_name_base = integrity_check.base_dock_name_raw( dock_weakness[0], dock_weakness[1], target_identifier) target_name_base = integrity_check.base_dock_name_raw( dock_weakness[0], dock_weakness[1], source_identifier) source_count = len( integrity_check.docks_with_same_base_name(current_area, source_name_base)) if source_count != len( integrity_check.docks_with_same_base_name( target_area, target_name_base)): raise ValueError( f"Expected {target_area.name} to also have {source_count} " f"docks with name {target_name_base}") if source_count > 0: source_name = f"{source_name_base} ({source_count + 1})" target_name = f"{target_name_base} ({source_count + 1})" else: source_name = source_name_base target_name = target_name_base new_node_this_area_identifier = NodeIdentifier( self.world_list.identifier_for_area(current_area), source_name) new_node_other_area_identifier = NodeIdentifier( self.world_list.identifier_for_area(target_area), target_name) new_node_this_area = DockNode( identifier=new_node_this_area_identifier, node_index=self.editor.new_node_index(), heal=False, location=location, description="", layers=("default", ), extra={}, dock_type=dock_weakness[0], default_connection=new_node_other_area_identifier, default_dock_weakness=dock_weakness[1], override_default_open_requirement=None, override_default_lock_requirement=None, ) new_node_other_area = DockNode( identifier=new_node_other_area_identifier, node_index=self.editor.new_node_index(), heal=False, location=location, description="", layers=("default", ), extra={}, dock_type=dock_weakness[0], default_connection=new_node_this_area_identifier, default_dock_weakness=dock_weakness[1], override_default_open_requirement=None, override_default_lock_requirement=None, ) self.editor.add_node(current_area, new_node_this_area) self.editor.add_node(target_area, new_node_other_area) if source_count == 1: self.editor.rename_node( current_area, current_area.node_with_name(source_name_base), f"{source_name_base} (1)", ) self.editor.rename_node( target_area, target_area.node_with_name(target_name_base), f"{target_name_base} (1)", ) self.on_select_area(new_node_this_area)
def read_node(self, name: str, data: Dict) -> Node: try: location = None if data["coordinates"] is not None: location = location_from_json(data["coordinates"]) generic_args = { "identifier": NodeIdentifier.create(self.current_world_name, self.current_area_name, name), "heal": data["heal"], "location": location, "description": data["description"], "layers": tuple(data["layers"]), "extra": frozen_lib.wrap(data["extra"]), } node_type: int = data["node_type"] if node_type == "generic": return GenericNode(**generic_args) elif node_type == "dock": return DockNode( **generic_args, dock_type=self.dock_weakness_database.find_type( data["dock_type"]), default_connection=NodeIdentifier.from_json( data["default_connection"]), default_dock_weakness=self.dock_weakness_database. get_by_weakness( data["dock_type"], data["default_dock_weakness"], ), override_default_open_requirement=read_optional_requirement( data["override_default_open_requirement"], self.resource_database), override_default_lock_requirement=read_optional_requirement( data["override_default_lock_requirement"], self.resource_database), ) elif node_type == "pickup": return PickupNode( **generic_args, pickup_index=PickupIndex(data["pickup_index"]), major_location=data["major_location"], ) elif node_type == "teleporter": return TeleporterNode( **generic_args, default_connection=AreaIdentifier.from_json( data["destination"]), keep_name_when_vanilla=data["keep_name_when_vanilla"], editable=data["editable"], ) elif node_type == "event": return EventNode( **generic_args, event=self.resource_database.get_by_type_and_index( ResourceType.EVENT, data["event_name"])) elif node_type == "configurable_node": return ConfigurableNode(**generic_args, ) elif node_type == "logbook": lore_type = LoreType(data["lore_type"]) if lore_type == LoreType.REQUIRES_ITEM: required_translator = self.resource_database.get_item( data["extra"]["translator"]) else: required_translator = None if lore_type in { LoreType.SPECIFIC_PICKUP, LoreType.SKY_TEMPLE_KEY_HINT }: hint_index = data["extra"]["hint_index"] else: hint_index = None return LogbookNode( **generic_args, string_asset_id=data["string_asset_id"], scan_visor=self._get_scan_visor(), lore_type=lore_type, required_translator=required_translator, hint_index=hint_index, ) elif node_type == "player_ship": return PlayerShipNode( **generic_args, is_unlocked=read_requirement(data["is_unlocked"], self.resource_database), item_to_summon=self._get_command_visor(), ) else: raise Exception(f"Unknown type: {node_type}") except Exception as e: raise Exception(f"In node {name}, got error: {e}")
def test_create_pickup_list_random_data_source(has_memo_data: bool, empty_patches, generic_item_category): # Setup rng = Random(5000) resource_b = ItemResourceInfo(0, "B", "B", 10) model_1 = MagicMock(spec=PickupModel) model_2 = MagicMock(spec=PickupModel) useless_model = PickupModel(game=RandovaniaGame.METROID_PRIME_CORRUPTION, name="Useless") pickup_a = PickupEntry("A", model_1, generic_item_category, generic_item_category, progression=tuple()) pickup_b = PickupEntry("B", model_2, generic_item_category, generic_item_category, progression=((resource_b, 1), (resource_b, 1))) pickup_c = PickupEntry("C", model_2, generic_item_category, generic_item_category, progression=tuple()) useless_pickup = PickupEntry("Useless", useless_model, USELESS_ITEM_CATEGORY, USELESS_ITEM_CATEGORY, progression=tuple()) patches = empty_patches.assign_new_pickups([ (PickupIndex(0), PickupTarget(pickup_a, 0)), (PickupIndex(2), PickupTarget(pickup_b, 0)), (PickupIndex(3), PickupTarget(pickup_a, 0)), (PickupIndex(4), PickupTarget(pickup_c, 0)), ]) if has_memo_data: memo_data = { "A": "This is an awesome item A", "B": "This is B. It is good.", "C": "What a nice day to have a C", "Useless": "Try again next time", } else: memo_data = { name: "{} acquired!".format(name) for name in ("A", "B", "C", "Useless") } creator = pickup_exporter.PickupExporterSolo(memo_data) world_list = MagicMock() world_list.iterate_nodes.return_value = [ PickupNode(NodeIdentifier.create("W", "A", f"Name {i}"), i, False, None, "", ("default",), {}, PickupIndex(i), False) for i in range(5) ] # Run result = pickup_exporter.export_all_indices( patches, PickupTarget(useless_pickup, 0), world_list, rng, PickupModelStyle.HIDE_ALL, PickupModelDataSource.RANDOM, creator, pickup_creator.create_visual_etm(), ) # Assert assert len(result) == 5 assert result[0] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(0), scan_text="A", hud_text=[memo_data["A"]], conditional_resources=[ConditionalResources("A", None, ())], conversion=[], model=model_1, other_player=False, original_pickup=pickup_a, ) assert result[1] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(1), scan_text="A", hud_text=[memo_data["A"]], conditional_resources=[ConditionalResources("Useless", None, ())], conversion=[], model=model_1, other_player=False, original_pickup=useless_pickup, ) assert result[2] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(2), scan_text="C", hud_text=[memo_data["C"], memo_data["C"]], conditional_resources=[ ConditionalResources("B", None, ((resource_b, 1),)), ConditionalResources("B", resource_b, ((resource_b, 1),)), ], conversion=[], model=model_2, other_player=False, original_pickup=pickup_b, ) assert result[3] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(3), scan_text="B", hud_text=[memo_data["B"]], conditional_resources=[ConditionalResources("A", None, ())], conversion=[], model=model_2, other_player=False, original_pickup=pickup_a, ) assert result[4] == pickup_exporter.ExportedPickupDetails( index=PickupIndex(4), scan_text="A", hud_text=[memo_data["A"]], conditional_resources=[ConditionalResources("C", None, ())], conversion=[], model=model_1, other_player=False, original_pickup=pickup_c, )
def add(world: str, area: str, node: str, target_world: str, target_area: str): elevator_connection.append(( wl.get_teleporter_node(NodeIdentifier.create(world, area, node)), AreaIdentifier(target_world, target_area), ))
def decode_single(player_index: int, all_pools: Dict[int, PoolResults], game: GameDescription, game_modifications: dict, configuration: BaseConfiguration) -> 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: """ world_list = game.world_list weakness_db = game.dock_weakness_database if game_modifications["game"] != game.game.value: raise ValueError( f"Expected '{game.game.value}', got '{game_modifications['game']}'" ) initial_pickup_assignment = all_pools[player_index].assignment # Starting Location starting_location = AreaIdentifier.from_string( 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: ElevatorConnection = { NodeIdentifier.from_string(source_name): AreaIdentifier.from_string(target_name) for source_name, target_name in game_modifications["teleporters"].items() } # Dock Weakness dock_weakness: dict[NodeIdentifier, DockWeakness] = { NodeIdentifier.from_string(source_name): weakness_db.get_by_weakness( weakness_data["type"], weakness_data["name"], ) for source_name, weakness_data in game_modifications["dock_weakness"].items() } # Configurable Nodes configurable_nodes = { NodeIdentifier.from_string(identifier): data_reader.read_requirement(requirement, game.resource_database) for identifier, requirement in game_modifications["configurable_nodes"].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 typing.cast(dict[str, str], 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)) - 1 else: pickup_name = target_name target_player = 0 node_identifier = NodeIdentifier.create( world_name, *area_node_name.split("/", 1)) node = world_list.node_by_identifier(node_identifier) 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 identifier_str, hint in game_modifications["hints"].items(): hints[NodeIdentifier.from_string(identifier_str)] = Hint.from_json( hint) return GamePatches( player_index=player_index, configuration=configuration, pickup_assignment=pickup_assignment, # PickupAssignment elevator_connection=elevator_connection, # ElevatorConnection dock_connection={}, # Dict[Tuple[int, int], DockConnection] dock_weakness=dock_weakness, configurable_nodes=configurable_nodes, starting_items=starting_items, # ResourceGainTuple starting_location=starting_location, # AreaIdentifier hints=hints, )
def _patches_with_data(request, echoes_game_description, echoes_game_patches, echoes_item_database): game = echoes_game_description db = game.resource_database data = { "game": echoes_game_description.game.value, "starting_location": "Temple Grounds/Landing Site", "starting_items": {}, "teleporters": { "Temple Grounds/Temple Transport C/Elevator to Great Temple - Temple Transport C": "Great Temple/Temple Transport C", "Temple Grounds/Transport to Agon Wastes/Elevator to Agon Wastes - Transport to Temple Grounds": "Agon Wastes/Transport to Temple Grounds", "Temple Grounds/Transport to Torvus Bog/Elevator to Torvus Bog - Transport to Temple Grounds": "Torvus Bog/Transport to Temple Grounds", "Temple Grounds/Temple Transport B/Elevator to Great Temple - Temple Transport B": "Great Temple/Temple Transport B", "Temple Grounds/Transport to Sanctuary Fortress/Elevator to Sanctuary Fortress - Transport to Temple Grounds": "Sanctuary Fortress/Transport to Temple Grounds", "Temple Grounds/Temple Transport A/Elevator to Great Temple - Temple Transport A": "Great Temple/Temple Transport A", "Great Temple/Temple Transport A/Elevator to Temple Grounds - Temple Transport A": "Temple Grounds/Temple Transport A", "Great Temple/Temple Transport C/Elevator to Temple Grounds - Temple Transport C": "Temple Grounds/Temple Transport C", "Great Temple/Temple Transport B/Elevator to Temple Grounds - Temple Transport B": "Temple Grounds/Temple Transport B", "Temple Grounds/Sky Temple Gateway/Teleport to Great Temple - Sky Temple Energy Controller": "Great Temple/Sky Temple Energy Controller", "Great Temple/Sky Temple Energy Controller/Teleport to Temple Grounds - Sky Temple Gateway": "Temple Grounds/Sky Temple Gateway", "Agon Wastes/Transport to Temple Grounds/Elevator to Temple Grounds - Transport to Agon Wastes": "Temple Grounds/Transport to Agon Wastes", "Agon Wastes/Transport to Torvus Bog/Elevator to Torvus Bog - Transport to Agon Wastes": "Torvus Bog/Transport to Agon Wastes", "Agon Wastes/Transport to Sanctuary Fortress/Elevator to Sanctuary Fortress - Transport to Agon Wastes": "Sanctuary Fortress/Transport to Agon Wastes", "Torvus Bog/Transport to Temple Grounds/Elevator to Temple Grounds - Transport to Torvus Bog": "Temple Grounds/Transport to Torvus Bog", "Torvus Bog/Transport to Agon Wastes/Elevator to Agon Wastes - Transport to Torvus Bog": "Agon Wastes/Transport to Torvus Bog", "Torvus Bog/Transport to Sanctuary Fortress/Elevator to Sanctuary Fortress - Transport to Torvus Bog": "Sanctuary Fortress/Transport to Torvus Bog", "Sanctuary Fortress/Transport to Temple Grounds/Elevator to Temple Grounds - Transport to Sanctuary Fortress": "Temple Grounds/Transport to Sanctuary Fortress", "Sanctuary Fortress/Transport to Agon Wastes/Elevator to Agon Wastes - Transport to Sanctuary Fortress": "Agon Wastes/Transport to Sanctuary Fortress", "Sanctuary Fortress/Transport to Torvus Bog/Elevator to Torvus Bog - Transport to Sanctuary Fortress": "Torvus Bog/Transport to Sanctuary Fortress", "Sanctuary Fortress/Aerie/Elevator to Sanctuary Fortress - Aerie Transport Station": "Sanctuary Fortress/Aerie Transport Station", "Sanctuary Fortress/Aerie Transport Station/Elevator to Sanctuary Fortress - Aerie": "Sanctuary Fortress/Aerie", }, "dock_weakness": {}, "configurable_nodes": {}, "locations": {}, "hints": {} } patches = dataclasses.replace(echoes_game_patches, player_index=0) 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([ (db.get_item_by_name(item_name), 1), ]) data["starting_items"][item_name] = 1 if request.param.get("elevator"): teleporter: NodeIdentifier = request.param.get("elevator") patches = patches.assign_elevators([ (game.world_list.get_teleporter_node(teleporter), game.starting_location), ]) data["teleporters"][ teleporter.as_string] = "Temple Grounds/Landing Site" if request.param.get("configurable_nodes"): gates = [] for identifier, translator in request.param.get("configurable_nodes"): requirement = ResourceRequirement.simple(db.get_item(translator)) gates.append((NodeIdentifier.from_string(identifier), requirement)) data["configurable_nodes"][ identifier] = data_writer.write_requirement(requirement) patches = patches.assign_node_configuration(gates) if request.param.get("pickup"): 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), PickupTarget(pickup, 0))]) data["locations"]["Temple Grounds"][ 'Transport to Agon Wastes/Pickup (Missile)'] = pickup_name if request.param.get("hint"): identifier, hint = request.param.get("hint") patches = patches.assign_hint(NodeIdentifier.from_string(identifier), Hint.from_json(hint)) data["hints"][identifier] = hint return data, patches
from randovania.layout import game_patches_serializer from randovania.layout.base.major_item_state import MajorItemState from randovania.layout.base.trick_level_configuration import TrickLevelConfiguration from randovania.layout.generator_parameters import GeneratorParameters from randovania.network_common.pickup_serializer import BitPackPickupEntry @pytest.fixture(params=[ {}, { "starting_item": "Morph Ball" }, { "elevator": NodeIdentifier.create( "Temple Grounds", "Transport to Agon Wastes", "Elevator to Agon Wastes - Transport to Temple Grounds") }, { "configurable_nodes": [("Agon Wastes/Mining Plaza/Translator Gate", "Cobalt"), ("Torvus Bog/Great Bridge/Translator Gate", "Emerald")] }, { "pickup": "Morph Ball Bomb" }, { "hint": [ 'Torvus Bog/Catacombs/Lore Scan', { "hint_type": "location", "dark_temple": None,
def add(world: str, area: str, node: str, target_world: str, target_area: str): elevator_connection[NodeIdentifier.create(world, area, node)] = AreaIdentifier(target_world, target_area)
def _a(world, area, instance_id=None): if instance_id is not None: return NodeIdentifier.create(world, area, instance_id).as_json return AreaIdentifier(world, area).as_json
def _create_identifier(self, node_name: str): return NodeIdentifier.create(self.current_world.name, self.current_area.name, node_name)
async def test_add_default_hints_to_patches(echoes_game_description, empty_patches, is_multiworld): # Setup layout_configuration = MagicMock() layout_configuration.game = RandovaniaGame.METROID_PRIME_ECHOES rng = MagicMock() hint_distributor = EchoesHintDistributor() def _light_suit_location_hint(number: int): return Hint( HintType.LOCATION, PrecisionPair(HintLocationPrecision.LIGHT_SUIT_LOCATION, HintItemPrecision.DETAILED, include_owner=False), PickupIndex(number)) def _guardian_hint(number: int): return Hint( HintType.LOCATION, PrecisionPair(HintLocationPrecision.GUARDIAN, HintItemPrecision.DETAILED, include_owner=False), PickupIndex(number)) def _keybearer_hint(number: int): return Hint( HintType.LOCATION, PrecisionPair(HintLocationPrecision.KEYBEARER, HintItemPrecision.BROAD_CATEGORY, include_owner=True), PickupIndex(number)) expected = { # Keybearer 'Temple Grounds/Landing Site/Keybearer Corpse (M-Dhe)': _keybearer_hint(11), 'Temple Grounds/Industrial Site/Keybearer Corpse (J-Fme)': _keybearer_hint(15), 'Temple Grounds/Storage Cavern A/Keybearer Corpse (D-Isl)': _keybearer_hint(19), # Agon 'Agon Wastes/Central Mining Station/Keybearer Corpse (J-Stl)': _keybearer_hint(45), 'Agon Wastes/Main Reactor/Keybearer Corpse (B-Stl)': _keybearer_hint(53), # Torvus 'Torvus Bog/Torvus Lagoon/Keybearer Corpse (S-Dly)': _keybearer_hint(68), 'Torvus Bog/Catacombs/Keybearer Corpse (G-Sch)': _keybearer_hint(91), # Sanctuary 'Sanctuary Fortress/Sanctuary Entrance/Keybearer Corpse (S-Jrs)': _keybearer_hint(117), 'Sanctuary Fortress/Dynamo Works/Keybearer Corpse (C-Rch)': _keybearer_hint(106), # Locations with hints 'Sanctuary Fortress/Sanctuary Energy Controller/Lore Scan': _light_suit_location_hint(24), 'Sanctuary Fortress/Main Gyro Chamber/Lore Scan': _guardian_hint(43), 'Sanctuary Fortress/Watch Station/Lore Scan': _guardian_hint(79), 'Sanctuary Fortress/Main Research/Lore Scan': _guardian_hint(115), # Dark Temple hints 'Sanctuary Fortress/Hall of Combat Mastery/Lore Scan': Hint(HintType.RED_TEMPLE_KEY_SET, None, dark_temple=HintDarkTemple.AGON_WASTES), 'Sanctuary Fortress/Sanctuary Entrance/Lore Scan': Hint(HintType.RED_TEMPLE_KEY_SET, None, dark_temple=HintDarkTemple.TORVUS_BOG), 'Torvus Bog/Catacombs/Lore Scan': Hint(HintType.RED_TEMPLE_KEY_SET, None, dark_temple=HintDarkTemple.SANCTUARY_FORTRESS), # Jokes 'Torvus Bog/Gathering Hall/Lore Scan': Hint(HintType.JOKE, None), 'Torvus Bog/Training Chamber/Lore Scan': Hint(HintType.JOKE, None), } expected = { NodeIdentifier.from_string(ident_s): hint for ident_s, hint in expected.items() } # Run result = await hint_distributor.assign_pre_filler_hints( empty_patches, prefill=PreFillParams( rng=rng, configuration=layout_configuration, game=echoes_game_description, is_multiworld=is_multiworld, ), ) # Assert rng.shuffle.assert_has_calls([call(ANY), call(ANY)]) assert result.hints == expected
rng, database, False) # Assert assert result == { NodeIdentifier.create("w0", "a0", "n0"): AreaIdentifier("w4", "a4"), NodeIdentifier.create("w1", "a1", "n1"): AreaIdentifier("w2", "a2"), NodeIdentifier.create("w2", "a2", "n2"): AreaIdentifier("w1", "a1"), NodeIdentifier.create("w3", "a3", "n3"): AreaIdentifier("w5", "a5"), NodeIdentifier.create("w4", "a4", "n4"): AreaIdentifier("w0", "a0"), NodeIdentifier.create("w5", "a5", "n5"): AreaIdentifier("w3", "a3"), } @pytest.mark.parametrize(["replacement", "expected"], [ (False, { NodeIdentifier.create("w0", "a0", "n0"): AreaIdentifier("w1", "a1"), NodeIdentifier.create("w1", "a1", "n1"): AreaIdentifier("w2", "a2"), NodeIdentifier.create("w2", "a2", "n2"): AreaIdentifier("w3", "a3"), NodeIdentifier.create("w3", "a3", "n3"): AreaIdentifier("w5", "a5"), NodeIdentifier.create("w4", "a4", "n4"): AreaIdentifier("w0", "a0"), NodeIdentifier.create("w5", "a5", "n5"): AreaIdentifier("w4", "a4"), }), (True, { NodeIdentifier.create("w0", "a0", "n0"): AreaIdentifier("w2", "a2"), NodeIdentifier.create("w1", "a1", "n1"): AreaIdentifier("w3", "a3"), NodeIdentifier.create("w2", "a2", "n2"): AreaIdentifier("w4", "a4"), NodeIdentifier.create("w3", "a3", "n3"): AreaIdentifier("w2", "a2"), NodeIdentifier.create("w4", "a4", "n4"): AreaIdentifier("w5", "a5"), NodeIdentifier.create("w5", "a5", "n5"): AreaIdentifier("w3", "a3"), }), ])