Exemplo n.º 1
0
    def assign_pickup_to_starting_items(self, pickup: PickupEntry) -> "State":
        pickup_resources = ResourceCollection.from_resource_gain(
            self.resource_database,
            pickup.resource_gain(self.resources, force_lock=True))

        # Make sure there's no item percentage on starting items
        if self.resource_database.item_percentage is not None:
            pickup_resources.remove_resource(
                self.resource_database.item_percentage)

        new_resources = self.resources.duplicate()
        new_resources.add_resource_gain(pickup_resources.as_resource_gain())
        new_patches = self.patches.assign_extra_initial_items(
            pickup_resources.as_resource_gain())

        tank_delta = _energy_tank_difference(new_resources, self.resources,
                                             self.resource_database)

        return State(
            new_resources,
            self.collected_resource_nodes,
            self.energy + tank_delta * self.game_data.energy_per_tank,
            self.node,
            new_patches,
            self,
            self.game_data,
        )
Exemplo n.º 2
0
def find_area_errors(game: GameDescription, area: Area) -> Iterator[str]:
    nodes_with_paths_in = set()
    for node in area.nodes:
        nodes_with_paths_in.update(area.connections[node].keys())

        for error in find_node_errors(game, node):
            yield f"{area.name} - {error}"

        if node in area.connections.get(node, {}):
            yield f"{area.name} - Node '{node.name}' has a connection to itself"

    if area.default_node is not None and area.node_with_name(area.default_node) is None:
        yield f"{area.name} has default node {area.default_node}, but no node with that name exists"

    # elif area.default_node is not None:
    #     nodes_with_paths_in.add(area.node_with_name(area.default_node))

    for node in area.nodes:
        if isinstance(node, (TeleporterNode, DockNode)) or area.connections[node]:
            continue

        # FIXME: cannot implement this for PickupNodes because their resource gain depends on GamePatches
        if isinstance(node, EventNode):
            # if this node would satisfy the victory condition, it does not need outgoing connections
            current = ResourceCollection.from_resource_gain(game.resource_database, node.resource_gain_on_collect(None))
            if game.victory_condition.satisfied(current, 0, game.resource_database):
                continue

        if node in nodes_with_paths_in:
            yield f"{area.name} - '{node.name}': Node has paths in, but no connections out."
Exemplo n.º 3
0
def find_locations_that_gives_items(
    target_items: List[ItemResourceInfo],
    all_patches: Dict[int, GamePatches],
    player: int,
) -> dict[ItemResourceInfo, list[tuple[int, PickupLocation]]]:
    result: dict[ItemResourceInfo, list[tuple[int, PickupLocation]]] = {
        item: []
        for item in target_items
    }

    for other_player, patches in all_patches.items():
        for pickup_index, target in patches.pickup_assignment.items():
            if target.player != player:
                continue

            # TODO: iterate over all tiers of progression
            db = patches.game.resource_database
            resources = ResourceCollection.from_resource_gain(
                db, target.pickup.resource_gain(ResourceCollection()))
            for resource, quantity in resources.as_resource_gain():
                if quantity > 0 and resource in result:
                    result[resource].append(
                        (other_player,
                         PickupLocation(patches.configuration.game,
                                        pickup_index)))

    return result
Exemplo n.º 4
0
def test_assign_extra_initial_items_to_empty(empty_patches, items):
    db = empty_patches.game.resource_database
    items = wrap(db, items)

    # Run
    new_patches = empty_patches.assign_extra_initial_items(items)

    # Assert
    assert new_patches.starting_items == ResourceCollection.from_resource_gain(db, items)
Exemplo n.º 5
0
def test_logbook_node_requirements_to_leave(logbook_node,
                                            empty_patches):
    # Setup
    has_translator, translator, node = logbook_node
    db = empty_patches.game.resource_database

    def ctx(resources):
        return NodeContext(empty_patches, resources, db, empty_patches.game.world_list)

    # Run
    to_leave = node.requirement_to_leave(ctx({}))

    # Assert
    rc2 = ResourceCollection.from_resource_gain(db, [])
    rc3 = ResourceCollection.from_resource_gain(db, [(translator, 1)])

    assert to_leave.satisfied(rc2, 99, None) != has_translator
    assert to_leave.satisfied(rc3, 99, None)
Exemplo n.º 6
0
def test_convert_resource_gain_to_current_resources(blank_resource_db, resource_gain, expected):
    # Setup
    db = blank_resource_db
    resource_gain = wrap(db, resource_gain)
    expected = wrap(db, expected)

    # Run
    result = ResourceCollection.from_resource_gain(db, resource_gain)

    # Assert
    assert dict(result.as_resource_gain()) == expected
Exemplo n.º 7
0
def pickups_to_solve_list(pickup_pool: list[PickupEntry],
                          requirement_list: RequirementList, state: State):
    pickups = []

    db = state.resource_database
    resources = state.resources.duplicate()
    pickups_for_this = list(pickup_pool)

    # Check pickups that give less items in total first
    # This means we test for expansions before the major items, in case both give the same resource
    # Useful to get Dark Beam Ammo Expansion instead of Dark Beam.
    pickups_for_this.sort(key=lambda p: sum(
        1 for _ in p.resource_gain(resources, force_lock=True)))

    for individual in sorted(requirement_list.values()):
        if individual.satisfied(resources, state.energy,
                                state.resource_database):
            continue

        # Create another copy of the list so we can remove elements while iterating
        for pickup in list(pickups_for_this):
            new_resources = ResourceCollection.from_resource_gain(
                db, pickup.resource_gain(resources, force_lock=True))
            pickup_progression = ResourceCollection.from_resource_gain(
                db, pickup.progression)
            if new_resources[individual.resource] + pickup_progression[
                    individual.resource] > 0:
                pickups.append(pickup)
                pickups_for_this.remove(pickup)
                resources.add_resource_gain(new_resources.as_resource_gain())

            if individual.satisfied(resources, state.energy,
                                    state.resource_database):
                break

        if not individual.satisfied(resources, state.energy,
                                    state.resource_database):
            return None

    return pickups
Exemplo n.º 8
0
    def _on_filters_changed(self):
        if self.edit_mode:
            game = self.original_game_description
        else:
            game = derived_nodes.remove_inactive_layers(
                self.original_game_description,
                self.layers_editor.selected_layers(),
            )

            resources = self.layers_editor.selected_tricks()
            if resources:
                self._collection_for_filtering = ResourceCollection.from_resource_gain(
                    game.resource_database, resources.items())
            else:
                self._collection_for_filtering = None

        self.update_game(game)
Exemplo n.º 9
0
    def __init__(
        self,
        parent: QWidget,
        window_manager: WindowManager,
        preset: Preset,
    ):

        super().__init__(parent)
        self.setupUi(self)
        set_default_window_icon(self)

        self._window_manager = window_manager
        self._game_description = filtered_database.game_description_for_layout(
            preset.configuration)
        database = self._game_description.resource_database

        trick_level = preset.configuration.trick_level
        if trick_level.minimal_logic:
            trick_usage_description = "Minimal Logic"
        else:
            trick_usage_description = ", ".join(
                sorted(
                    f"{trick.long_name} ({trick_level.level_for_trick(trick).long_name})"
                    for trick in database.trick
                    if trick_level.has_specific_level_for_trick(trick)))

        # setup
        self.area_list_label.linkActivated.connect(
            self._on_click_link_to_data_editor)
        self.setWindowTitle("{} for preset {}".format(self.windowTitle(),
                                                      preset.name))
        self.title_label.setText(self.title_label.text().format(
            trick_levels=trick_usage_description))

        # connect
        self.button_box.accepted.connect(self.button_box_close)
        self.button_box.rejected.connect(self.button_box_close)

        if trick_level.minimal_logic:
            return

        # Update
        bootstrap = self._game_description.game.generator.bootstrap
        trick_resources = ResourceCollection.from_resource_gain(
            database,
            bootstrap.trick_resources_for_configuration(trick_level, database))

        lines = []

        for world in sorted(self._game_description.world_list.worlds,
                            key=lambda it: it.name):
            for area in sorted(world.areas, key=lambda it: it.name):
                used_tricks = _check_used_tricks(area, trick_resources,
                                                 database)
                if used_tricks:
                    lines.append(
                        f'<p><a href="data-editor://{world.correct_name(area.in_dark_aether)}/{area.name}">'
                        f'{world.correct_name(area.in_dark_aether)} - {area.name}</a>'
                        f'<br />{"<br />".join(used_tricks)}</p>')

        self.area_list_label.setText("".join(lines))