Example #1
0
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),
    ]
Example #2
0
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),
    ]
Example #3
0
    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),
        )
Example #4
0
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
Example #5
0
    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)
Example #6
0
    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
Example #7
0
    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)
Example #8
0
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
Example #9
0
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={},
    )
Example #10
0
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))
Example #11
0
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
Example #12
0
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"),
    }
Example #13
0
    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
Example #14
0
 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,
    )
Example #16
0
    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)
Example #17
0
    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),
     ))
Example #20
0
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,
    )
Example #21
0
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
Example #22
0
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,
Example #23
0
 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)
Example #24
0
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
Example #25
0
 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
Example #27
0
        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"),
    }),
])