예제 #1
0
    def collect_resource_node(self, node: ResourceNode,
                              new_energy: int) -> "State":
        """
        Creates a new State that has the given ResourceNode collected.
        :param node:
        :param new_energy: How much energy you should have when collecting this resource
        :return:
        """

        if not node.can_collect(self.patches, self.resources):
            raise ValueError(
                "Trying to collect an uncollectable node'{}'".format(node))

        new_resources = copy.copy(self.resources)
        add_resource_gain_to_current_resources(
            node.resource_gain_on_collect(self.patches, self.resources),
            new_resources)

        energy = new_energy
        if _energy_tank_difference(new_resources, self.resources,
                                   self.resource_database) > 0:
            energy = self._energy_for(new_resources)

        return State(new_resources, self.collected_resource_nodes + (node, ),
                     energy, self.node, self.patches, self,
                     self.resource_database)
예제 #2
0
def calculate_starting_state(game: GameDescription,
                             patches: GamePatches) -> "State":
    starting_node = game.world_list.resolve_teleporter_connection(
        patches.starting_location)
    initial_resources = copy.copy(patches.starting_items)

    if isinstance(starting_node, PlayerShipNode):
        initial_resources[starting_node.resource()] = 1

    initial_game_state = game.initial_states.get("Default")
    if initial_game_state is not None:
        add_resource_gain_to_current_resources(initial_game_state,
                                               initial_resources)

    starting_state = State(
        initial_resources,
        (),
        99 +
        (100 * initial_resources.get(game.resource_database.energy_tank, 0)),
        starting_node,
        patches,
        None,
        game.resource_database,
        game.world_list,
    )

    # Being present with value 0 is troublesome since this dict is used for a simplify_requirements later on
    keys_to_remove = [
        resource for resource, quantity in initial_resources.items()
        if quantity == 0
    ]
    for resource in keys_to_remove:
        del initial_resources[resource]

    return starting_state
예제 #3
0
def add_artifacts(
    resource_database: ResourceDatabase,
    mode: LayoutArtifactMode,
) -> PoolResults:
    """
    :param resource_database:
    :param mode
    :return:
    """
    item_pool: List[PickupEntry] = []
    initial_resources: CurrentResources = {}

    artifacts_to_place = mode.value

    for i in range(artifacts_to_place):
        item_pool.append(pickup_creator.create_artifact(i, resource_database))

    first_automatic_artifact = artifacts_to_place

    for automatic_artifact in range(first_automatic_artifact, 12):
        resource_info.add_resource_gain_to_current_resources(
            pickup_creator.create_artifact(automatic_artifact,
                                           resource_database).all_resources,
            initial_resources)

    return PoolResults(item_pool, {}, initial_resources)
예제 #4
0
파일: state.py 프로젝트: dyceron/randovania
def add_pickup_to_state(state: State, pickup: PickupEntry):
    """
    Modifies inplace the given state, adding the resources of the given pickup
    :param state:
    :param pickup:
    :return:
    """
    add_resource_gain_to_current_resources(pickup.resource_gain(state.resources, force_lock=True), state.resources)
예제 #5
0
    def assign_pickup_resources(self, pickup: PickupEntry) -> "State":
        new_resources = copy.copy(self.resources)
        add_resource_gain_to_current_resources(
            pickup.resource_gain(self.resources), new_resources)

        energy = self.energy
        if _energy_tank_difference(new_resources, self.resources,
                                   self.resource_database) > 0:
            energy = self._energy_for(new_resources)

        return State(new_resources, self.collected_resource_nodes, energy,
                     self.node, self.patches, self, self.resource_database)
예제 #6
0
    def state_for_current_configuration(self) -> Optional[State]:
        state = self._initial_state.copy()
        state.node = self._actions[-1]

        for pickup, quantity in self._collected_pickups.items():
            for _ in range(quantity):
                add_pickup_to_state(state, pickup)

        for node in self._collected_nodes:
            add_resource_gain_to_current_resources(node.resource_gain_on_collect(state.patches, state.resources),
                                                   state.resources)

        return state
예제 #7
0
    def assign_pickup_to_index(self, pickup: PickupEntry,
                               index: PickupIndex) -> "State":
        new_patches = self.patches.assign_new_pickups([(index, pickup)])
        new_resources = copy.copy(self.resources)

        if index in self.resources:
            add_resource_gain_to_current_resources(
                pickup.resource_gain(self.resources), new_resources)

        energy = self.energy
        if _energy_tank_difference(new_resources, self.resources,
                                   self.resource_database) > 0:
            energy = _energy_for(new_resources, self.resource_database)

        return State(new_resources, self.collected_resource_nodes, energy,
                     self.node, new_patches, self, self.resource_database)
예제 #8
0
def add_major_items(
    resource_database: ResourceDatabase,
    major_items_configuration: MajorItemsConfiguration,
    ammo_configuration: AmmoConfiguration,
) -> PoolResults:
    """

    :param resource_database:
    :param major_items_configuration:
    :param ammo_configuration:
    :return:
    """

    item_pool: List[PickupEntry] = []
    new_assignment: Dict[PickupIndex, PickupEntry] = {}
    initial_resources: CurrentResources = {}

    for item, state in major_items_configuration.items_state.items():
        if len(item.ammo_index) != len(state.included_ammo):
            raise InvalidConfiguration(
                "Item {0.name} uses {0.ammo_index} as ammo, but there's only {1} values in included_ammo"
                .format(item, len(state.included_ammo)))

        ammo, locked_ammo = _find_ammo_for(item.ammo_index, ammo_configuration)

        if state.include_copy_in_original_location:
            if item.original_index is None:
                raise InvalidConfiguration(
                    "Item {0.name} does not exist in the original game, cannot use state {1}"
                    .format(item, state), )
            new_assignment[item.original_index] = create_major_item(
                item, state, True, resource_database, ammo, locked_ammo)

        for _ in range(state.num_shuffled_pickups):
            item_pool.append(
                create_major_item(item, state, True, resource_database, ammo,
                                  locked_ammo))

        for _ in range(state.num_included_in_starting_items):
            add_resource_gain_to_current_resources(
                create_major_item(item, state, False, resource_database, ammo,
                                  locked_ammo).all_resources,
                initial_resources)

    return PoolResults(item_pool, new_assignment, initial_resources)
예제 #9
0
파일: state.py 프로젝트: dyceron/randovania
    def assign_pickups_resources(self, pickups: Iterator[PickupEntry]) -> "State":
        new_resources = copy.copy(self.resources)
        for pickup in pickups:
            add_resource_gain_to_current_resources(pickup.resource_gain(self.resources, force_lock=True), new_resources)

        energy = self.energy
        if _energy_tank_difference(new_resources, self.resources, self.resource_database) > 0:
            energy = self._energy_for(new_resources)

        return State(
            new_resources,
            self.collected_resource_nodes,
            energy,
            self.node,
            self.patches,
            self,
            self.game_data,
        )
예제 #10
0
def test_add_resource_gain_to_current_resources_convert(blank_pickup):
    # Setup
    resource_a = ItemResourceInfo("A", "A", 10, None)
    resource_b = ItemResourceInfo("B", "B", 10, None)

    pickup = dataclasses.replace(
        blank_pickup,
        progression=(),
        resource_lock=ResourceLock(resource_b, resource_b, resource_a),
        unlocks_resource=True,
    )
    current_resources = {resource_a: 5}

    # Run
    add_resource_gain_to_current_resources(
        pickup.resource_gain(current_resources), current_resources)

    # Assert
    assert current_resources == {resource_a: 0, resource_b: 5}
예제 #11
0
def test_add_resource_gain_to_current_resources_convert():
    # Setup
    resource_a = SimpleResourceInfo(1, "A", "A", ResourceType.ITEM)
    resource_b = SimpleResourceInfo(2, "B", "B", ResourceType.ITEM)

    pickup = PickupEntry(name="P1",
                         model_index=1,
                         item_category=ItemCategory.SUIT,
                         resources=(ConditionalResources(None, None, ()), ),
                         convert_resources=(ResourceConversion(
                             resource_a, resource_b), ))
    current_resources = {resource_a: 5}

    # Run
    add_resource_gain_to_current_resources(
        pickup.resource_gain(current_resources), current_resources)

    # Assert
    assert current_resources == {resource_a: 0, resource_b: 5}
예제 #12
0
def add_sky_temple_key_distribution_logic(
    resource_database: ResourceDatabase,
    mode: LayoutSkyTempleKeyMode,
) -> PoolResults:
    """
    Adds the given Sky Temple Keys to the item pool
    :param resource_database:
    :param mode:
    :return:
    """

    item_pool: List[PickupEntry] = []
    new_assignment: Dict[PickupIndex, PickupEntry] = {}
    initial_resources: CurrentResources = {}

    if mode == LayoutSkyTempleKeyMode.ALL_BOSSES or mode == LayoutSkyTempleKeyMode.ALL_GUARDIANS:
        locations_to_place = _GUARDIAN_INDICES[:]
        if mode == LayoutSkyTempleKeyMode.ALL_BOSSES:
            locations_to_place += _SUB_GUARDIAN_INDICES

        for key_number, location in enumerate(locations_to_place):
            new_assignment[location] = create_sky_temple_key(
                key_number, resource_database)
        first_automatic_key = len(locations_to_place)

    else:
        keys_to_place = mode.value
        if not isinstance(keys_to_place, int):
            raise InvalidConfiguration(
                "Unknown Sky Temple Key mode: {}".format(mode))

        for key_number in range(keys_to_place):
            item_pool.append(
                create_sky_temple_key(key_number, resource_database))
        first_automatic_key = keys_to_place

    for automatic_key_number in range(first_automatic_key, 9):
        add_resource_gain_to_current_resources(
            create_sky_temple_key(automatic_key_number,
                                  resource_database).all_resources,
            initial_resources)

    return PoolResults(item_pool, new_assignment, initial_resources)
예제 #13
0
    def calculate_starting_state(self, game: GameDescription,
                                 patches: GamePatches,
                                 configuration: BaseConfiguration) -> "State":
        starting_node = game.world_list.resolve_teleporter_connection(
            patches.starting_location)
        initial_resources = copy.copy(patches.starting_items)

        starting_energy, energy_per_tank = self.energy_config(configuration)

        if starting_node.is_resource_node:
            assert isinstance(starting_node, ResourceNode)
            add_resource_gain_to_current_resources(
                starting_node.resource_gain_on_collect(
                    NodeContext(
                        patches,
                        initial_resources,
                        game.resource_database,
                        game.world_list,
                    )),
                initial_resources,
            )

        initial_game_state = game.initial_states.get("Default")
        if initial_game_state is not None:
            add_resource_gain_to_current_resources(initial_game_state,
                                                   initial_resources)

        starting_state = State(
            initial_resources, (), None, starting_node, patches, None,
            StateGameData(game.resource_database, game.world_list,
                          energy_per_tank, starting_energy))

        # Being present with value 0 is troublesome since this dict is used for a simplify_requirements later on
        keys_to_remove = [
            resource for resource, quantity in initial_resources.items()
            if quantity == 0
        ]
        for resource in keys_to_remove:
            del initial_resources[resource]

        return starting_state
예제 #14
0
    async def _patches_for_pickup(self, provider_name: str, pickup: PickupEntry):
        inventory_resources: CurrentResources = {
            item: inv_item.capacity
            for item, inv_item in self._inventory.items()
        }
        conditional = pickup.conditional_for_resources(inventory_resources)
        if conditional.name is not None:
            item_name = conditional.name
        else:
            item_name = pickup.name

        resources_to_give = {}
        add_resource_gain_to_current_resources(conditional.resources, inventory_resources)
        add_resource_gain_to_current_resources(conditional.resources, resources_to_give)
        add_resource_gain_to_current_resources(pickup.conversion_resource_gain(inventory_resources),
                                               resources_to_give)

        # Ignore item% for received items
        resources_to_give.pop(self.game.resource_database.item_percentage, None)

        patches = [
            all_prime_dol_patches.adjust_item_amount_and_capacity_patch(self.patches.powerup_functions,
                                                                        item.index, delta)
            for item, delta in resources_to_give.items()
        ]
        return patches, f"Received {item_name} from {provider_name}."
예제 #15
0
    def state_for_current_configuration(self) -> Optional[State]:
        state = self._initial_state.copy()
        if self._actions:
            state.node = self._actions[-1]

        for teleporter, combo in self._elevator_id_to_combo.items():
            assert combo.currentData() is not None
            state.patches.elevator_connection[teleporter] = combo.currentData()

        for gate, item in self._translator_gate_to_combo.items():
            state.patches.translator_gates[gate] = item.currentData()

        for pickup, quantity in self._collected_pickups.items():
            for _ in range(quantity):
                add_pickup_to_state(state, pickup)

        for node in self._collected_nodes:
            add_resource_gain_to_current_resources(
                node.resource_gain_on_collect(state.patches, state.resources),
                state.resources)

        return state
예제 #16
0
    def state_for_current_configuration(self) -> Optional[State]:
        state = self._initial_state.copy()
        if self._actions:
            state.node = self._actions[-1]

        for teleporter, combo in self._elevator_id_to_combo.items():
            state.patches.elevator_connection[teleporter] = combo.currentData()

        for gate, item in self._translator_gate_to_combo.items():
            scan_visor = self.game_description.resource_database.get_item(
                "Scan")

            requirement: Optional[
                LayoutTranslatorRequirement] = item.currentData()
            if requirement is None:
                translator_req = Requirement.impossible()
            else:
                translator = self.game_description.resource_database.get_item(
                    requirement.item_name)
                translator_req = ResourceRequirement(translator, 1, False)

            state.patches.configurable_nodes[gate] = RequirementAnd([
                ResourceRequirement(scan_visor, 1, False),
                translator_req,
            ])

        for pickup, quantity in self._collected_pickups.items():
            for _ in range(quantity):
                add_pickup_to_state(state, pickup)

        for node in self._collected_nodes:
            add_resource_gain_to_current_resources(
                node.resource_gain_on_collect(state.node_context()),
                state.resources)

        return state
예제 #17
0
    def _resources_to_give_for_pickup(
            self, pickup: PickupEntry,
            inventory: Inventory) -> Tuple[str, CurrentResources]:
        inventory_resources: CurrentResources = {
            item: inv_item.capacity
            for item, inv_item in inventory.items()
        }
        conditional = pickup.conditional_for_resources(inventory_resources)
        if conditional.name is not None:
            item_name = conditional.name
        else:
            item_name = pickup.name

        resources_to_give = {}

        if pickup.respects_lock and not pickup.unlocks_resource and (
                pickup.resource_lock is not None and inventory_resources.get(
                    pickup.resource_lock.locked_by, 0) == 0):
            pickup_resources = list(
                pickup.resource_lock.convert_gain(conditional.resources))
            item_name = f"Locked {item_name}"
        else:
            pickup_resources = conditional.resources

        add_resource_gain_to_current_resources(pickup_resources,
                                               inventory_resources)
        add_resource_gain_to_current_resources(pickup_resources,
                                               resources_to_give)
        add_resource_gain_to_current_resources(
            pickup.conversion_resource_gain(inventory_resources),
            resources_to_give)

        # Ignore item% for received items
        if self.game.resource_database.item_percentage is not None:
            resources_to_give.pop(self.game.resource_database.item_percentage,
                                  None)

        return item_name, resources_to_give
예제 #18
0
def test_missile_expansion_before_launcher(include_before, echoes_item_database, echoes_resource_database):
    # Setup
    ammo = echoes_item_database.ammo["Missile Expansion"]
    major_item = echoes_item_database.major_items["Missile Launcher"]

    missile = echoes_resource_database.get_item(ammo.items[0])
    missile_launcher = echoes_resource_database.get_item(major_item.progression[0])
    temporary = echoes_resource_database.get_item(ammo.temporary)
    percent = echoes_resource_database.item_percentage

    # Run
    expansion = pickup_creator.create_ammo_expansion(ammo, [5], True, echoes_resource_database)
    launcher = pickup_creator.create_major_item(
        major_item, MajorItemState(included_ammo=(5,)),
        True, echoes_resource_database, ammo, True
    )

    resources = {}

    if include_before:
        # Ammo Expansion
        add_resource_gain_to_current_resources(expansion.resource_gain(resources, force_lock=True), resources)
        assert resources == {percent: 1, temporary: 5}

    # Add Launcher
    add_resource_gain_to_current_resources(launcher.resource_gain(resources, force_lock=True), resources)
    if include_before:
        assert resources == {percent: 2, temporary: 0, missile_launcher: 1, missile: 10}
    else:
        assert resources == {percent: 1, temporary: 0, missile_launcher: 1, missile: 5}

    # Ammo Expansion
    add_resource_gain_to_current_resources(expansion.resource_gain(resources, force_lock=True), resources)
    if include_before:
        assert resources == {percent: 3, temporary: 0, missile_launcher: 1, missile: 15}
    else:
        assert resources == {percent: 2, temporary: 0, missile_launcher: 1, missile: 10}
예제 #19
0
def _add_pickup_to_resources(pickup: PickupEntry,
                             inventory: CurrentResources) -> CurrentResources:
    return add_resource_gain_to_current_resources(
        pickup.resource_gain(inventory), copy.copy(inventory))