Beispiel #1
0
    def patch_resource_database(
            self, db: ResourceDatabase,
            configuration: BaseConfiguration) -> ResourceDatabase:
        base_damage_reduction = db.base_damage_reduction
        damage_reductions = copy.copy(db.damage_reductions)
        requirement_template = copy.copy(db.requirement_template)

        suits = [db.get_item_by_name("Varia Suit")]
        if configuration.heat_protection_only_varia:
            requirement_template["Heat-Resisting Suit"] = ResourceRequirement(
                db.get_item_by_name("Varia Suit"), 1, False)
        else:
            suits.extend([
                db.get_item_by_name("Gravity Suit"),
                db.get_item_by_name("Phazon Suit")
            ])

        reductions = [DamageReduction(None, configuration.heat_damage / 10.0)]
        reductions.extend([DamageReduction(suit, 0) for suit in suits])
        damage_reductions[db.get_by_type_and_index(ResourceType.DAMAGE,
                                                   "HeatDamage1")] = reductions

        if configuration.progressive_damage_reduction:
            base_damage_reduction = self.prime1_progressive_damage_reduction
        else:
            base_damage_reduction = self.prime1_absolute_damage_reduction

        return dataclasses.replace(db,
                                   damage_reductions=damage_reductions,
                                   base_damage_reduction=base_damage_reduction,
                                   requirement_template=requirement_template)
Beispiel #2
0
def create_stk_hints(
    all_patches: Dict[int, GamePatches],
    players_config: PlayersConfiguration,
    resource_database: ResourceDatabase,
    namer: HintNamer,
    hide_area: bool,
) -> list:
    """
    Creates the string patches entries that changes the Sky Temple Gateway hint scans with hints for where
    the STK actually are.
    :param all_patches:
    :param players_config:
    :param resource_database:
    :param namer:
    :param hide_area: Should the hint include only the world?
    :return:
    """
    resulting_hints = guaranteed_item_hint.create_guaranteed_hints_for_resources(
        all_patches,
        players_config,
        namer,
        hide_area,
        [
            resource_database.get_item(index)
            for index in echoes_items.SKY_TEMPLE_KEY_ITEMS
        ],
        True,
    )
    return [
        create_simple_logbook_hint(
            _SKY_TEMPLE_KEY_SCAN_ASSETS[key_number],
            resulting_hints[resource_database.get_item(key_index)],
        ) for key_number, key_index in enumerate(
            echoes_items.SKY_TEMPLE_KEY_ITEMS)
    ]
def create_energy_cell(
    cell_index: int,
    resource_database: ResourceDatabase,
) -> PickupEntry:
    ENERGY_CELL_CATEGORY = ItemCategory(name="energy_cell",
                                        long_name="",
                                        hint_details=("an ", "energy cell"),
                                        is_major=False,
                                        is_key=True)

    return PickupEntry(
        name=f"Energy Cell {cell_index + 1}",
        progression=((resource_database.get_item(
            corruption_items.ENERGY_CELL_ITEMS[cell_index]), 1), ),
        extra_resources=(
            (resource_database.get_item(
                corruption_items.ENERGY_CELL_TOTAL_ITEM), 1),
            (resource_database.item_percentage, 1),
        ),
        model=PickupModel(
            game=resource_database.game_enum,
            name=corruption_items.ENERGY_CELL_MODEL,
        ),
        item_category=ENERGY_CELL_CATEGORY,
        broad_category=GENERIC_KEY_CATEGORY,
        probability_offset=0.25,
    )
Beispiel #4
0
 def create_resource_lock(self, resource_database: ResourceDatabase) -> Optional[ResourceLock]:
     if self.unlocked_by is not None:
         return ResourceLock(
             locked_by=resource_database.get_item(self.unlocked_by),
             item_to_lock=resource_database.get_item(self.items[0]),
             temporary_item=resource_database.get_item(self.temporary),
         )
     return None
Beispiel #5
0
 def prime1_absolute_damage_reduction(self, db: ResourceDatabase, current_resources: ResourceCollection):
     if current_resources[db.get_item_by_name("Phazon Suit")] > 0:
         return 0.5
     elif current_resources[db.get_item_by_name("Gravity Suit")] > 0:
         return 0.8
     elif current_resources[db.get_item_by_name("Varia Suit")] > 0:
         return 0.9
     else:
         return 1
Beispiel #6
0
 def create_game_specific(self, resource_database: ResourceDatabase) -> EchoesBeamConfiguration:
     return EchoesBeamConfiguration(
         item=resource_database.get_item(self.item_index),
         ammo_a=resource_database.get_item(self.ammo_a) if self.ammo_a >= 0 else None,
         ammo_b=resource_database.get_item(self.ammo_b) if self.ammo_b >= 0 else None,
         uncharged_cost=self.uncharged_cost,
         charged_cost=self.charged_cost,
         combo_missile_cost=self.combo_missile_cost,
         combo_ammo_cost=self.combo_ammo_cost,
     )
def wrap(db: ResourceDatabase, data):
    if isinstance(data, dict):
        return {
            db.get_item(key): value
            for key, value in data.items()
        }
    else:
        return [
            (db.get_item(key), value)
            for key, value in data
        ]
Beispiel #8
0
def read_beam_configuration(data: Dict, resource_database: ResourceDatabase,
                            ) -> EchoesBeamConfiguration:
    return EchoesBeamConfiguration(
        item=resource_database.get_item(data["item_index"]),
        ammo_a=resource_database.get_item(data["ammo_a"]) if data["ammo_a"] is not None else None,
        ammo_b=resource_database.get_item(data["ammo_b"]) if data["ammo_b"] is not None else None,
        uncharged_cost=data["uncharged_cost"],
        charged_cost=data["charged_cost"],
        combo_missile_cost=data["combo_missile_cost"],
        combo_ammo_cost=data["combo_ammo_cost"],
    )
Beispiel #9
0
 def patch_resource_database(
         self, db: ResourceDatabase,
         configuration: BaseConfiguration) -> ResourceDatabase:
     damage_reductions = copy.copy(db.damage_reductions)
     damage_reductions[db.get_by_type_and_index(
         ResourceType.DAMAGE, "DarkWorld1")] = [
             DamageReduction(None, configuration.varia_suit_damage / 6.0),
             DamageReduction(db.get_item_by_name("Dark Suit"),
                             configuration.dark_suit_damage / 6.0),
             DamageReduction(db.get_item_by_name("Light Suit"), 0.0),
         ]
     return dataclasses.replace(db, damage_reductions=damage_reductions)
Beispiel #10
0
    def event_resources_for_configuration(self, configuration: BaseConfiguration,
                                          resource_database: ResourceDatabase,
                                          ) -> ResourceGain:
        assert isinstance(configuration, DreadConfiguration)

        if configuration.hanubia_shortcut_no_grapple:
            for name in ["s080_shipyard:default:grapplepulloff1x2_000", "s080_shipyard:default:grapplepulloff1x2"]:
                yield resource_database.get_event(name), 1

        if configuration.hanubia_easier_path_to_itorash:
            yield resource_database.get_event("s080_shipyard:default:grapplepulloff1x2_001"), 1

        if configuration.x_starts_released:
            yield resource_database.get_event("ElunReleaseX"), 1
Beispiel #11
0
def read_resource_database(game: RandovaniaGame,
                           data: Dict) -> ResourceDatabase:
    reader = ResourceReader()

    item = read_dict(data["items"], reader.read_item_resource_info)
    db = ResourceDatabase(
        game_enum=game,
        item=item,
        event=reader.read_resource_info_array(data["events"],
                                              ResourceType.EVENT),
        trick=read_dict(data["tricks"], reader.read_trick_resource_info),
        damage=reader.read_resource_info_array(data["damage"],
                                               ResourceType.DAMAGE),
        version=reader.read_resource_info_array(data["versions"],
                                                ResourceType.VERSION),
        misc=reader.read_resource_info_array(data["misc"], ResourceType.MISC),
        requirement_template={},
        damage_reductions={},
        energy_tank_item_index=data["energy_tank_item_index"],
        item_percentage_index=data["item_percentage_index"],
        multiworld_magic_item_index=data["multiworld_magic_item_index"])
    db.requirement_template.update(
        read_requirement_templates(data["requirement_template"], db))
    db.damage_reductions.update(
        read_resource_reductions_dict(data["damage_reductions"], db))
    return db
def gate_assignment_for_configuration(configuration: EchoesConfiguration,
                                      resource_database: ResourceDatabase,
                                      rng: Random,
                                      ) -> GateAssignment:
    """
    :param configuration:
    :param resource_database:
    :param rng:
    :return:
    """

    all_choices = list(LayoutTranslatorRequirement)
    all_choices.remove(LayoutTranslatorRequirement.RANDOM)
    all_choices.remove(LayoutTranslatorRequirement.RANDOM_WITH_REMOVED)
    without_removed = copy.copy(all_choices)
    without_removed.remove(LayoutTranslatorRequirement.REMOVED)
    random_requirements = {LayoutTranslatorRequirement.RANDOM, LayoutTranslatorRequirement.RANDOM_WITH_REMOVED}

    result = {}
    for gate, requirement in configuration.translator_configuration.translator_requirement.items():
        if requirement in random_requirements:
            if rng is None:
                raise MissingRng("Translator")
            requirement = rng.choice(all_choices if requirement == LayoutTranslatorRequirement.RANDOM_WITH_REMOVED
                                     else without_removed)

        result[gate] = resource_database.get_by_type_and_index(ResourceType.ITEM, requirement.item_index)

    return result
Beispiel #13
0
def create_artifact(artifact_index: int,
                    minimum_progression: int,
                    resource_database: ResourceDatabase,
                    ) -> PickupEntry:
    ARTIFACT_CATEGORY = ItemCategory(
        name="artifact",
        long_name="",
        hint_details=("an ", "artifact"),
        is_major=False,
        is_key=True
    )

    return PickupEntry(
        name=prime_items.ARTIFACT_NAMES[artifact_index],
        progression=(
            (resource_database.get_item(prime_items.ARTIFACT_ITEMS[artifact_index]), 1),
        ),
        model=PickupModel(
            game=resource_database.game_enum,
            name=prime_items.ARTIFACT_MODEL[artifact_index],
        ),
        item_category=ARTIFACT_CATEGORY,
        broad_category=GENERIC_KEY_CATEGORY,
        probability_offset=0.25,
        required_progression=minimum_progression,
    )
Beispiel #14
0
def _create_resource_type_combo(
        current_resource_type: ResourceType, parent: QWidget,
        resource_database: ResourceDatabase) -> QComboBox:
    """

    :param current_resource_type:
    :param parent:
    :return:
    """
    resource_type_combo = QComboBox(parent)

    for resource_type in iterate_enum(ResourceType):
        try:
            count_elements = len(resource_database.get_by_type(resource_type))
        except ValueError:
            count_elements = 0

        if count_elements == 0:
            continue

        resource_type_combo.addItem(resource_type.name, resource_type)
        if resource_type is current_resource_type:
            resource_type_combo.setCurrentIndex(resource_type_combo.count() -
                                                1)

    return resource_type_combo
Beispiel #15
0
def test_area_with_invalid_connections():
    # Setup
    db = ResourceDatabase(RandovaniaGame.METROID_PRIME_ECHOES, [], [], [], [], [], [], {}, {}, 0, 0, 0)
    reader = WorldReader(db, None)
    reader.current_world_name = "World"

    with pytest.raises(MissingResource) as e:
        reader.read_area("Broken Area", {
            "extra": {},
            "nodes": {
                "A": {
                    "heal": True, "coordinates": None, "node_type": "generic",
                    "connections": {}, "extra": {}, "layers": ["default"], "description": "",
                },
                "Broken": {
                    "heal": True, "coordinates": None, "node_type": "generic",
                    "layers": ["default"], "extra": {}, "description": "",
                    "connections": {
                        "A": {
                            "type": "resource",
                            "data": {
                                "type": "items",
                                "name": "Dark",
                                "amount": 1,
                                "negate": False
                            }
                        }
                    }
                },
            }
        })

    assert str(e.value) == ("In area Broken Area, connection from Broken to A got error: "
                            "items Resource with short_name 'Dark' not found in 0 resources")
Beispiel #16
0
def test_area_with_invalid_connections():
    # Setup
    db = ResourceDatabase([], [], [], [], [], [], [], {})
    reader = WorldReader(db, None)

    with pytest.raises(MissingResource) as e:
        reader.read_area({
            "name": "Broken Area",
            "nodes": [
                {"name": "A", "heal": True, "node_type": "generic", "connections": {}},
                {"name": "Broken", "heal": True, "node_type": "generic", "connections": {
                    "A": {
                        "type": "resource",
                        "data": {
                            "type": 0,
                            "index": 1,
                            "amount": 1,
                            "negate": False
                        }
                    }
                }},
            ]
        })

    assert str(e.value) == ("In area Broken Area, connection from Broken to A got error: "
                            "ResourceType.ITEM Resource with index 1 not found in []")
def gate_assignment_for_configuration(
    configuration: LayoutConfiguration,
    resource_database: ResourceDatabase,
    rng: Random,
) -> GateAssignment:
    """
    :param configuration:
    :param resource_database:
    :param rng:
    :return:
    """

    choices = list(LayoutTranslatorRequirement)
    choices.remove(LayoutTranslatorRequirement.RANDOM)

    result = {}
    for gate, requirement in configuration.translator_configuration.translator_requirement.items(
    ):
        if requirement == LayoutTranslatorRequirement.RANDOM:
            if rng is None:
                raise MissingRng("Translator")
            requirement = rng.choice(choices)

        result[gate] = resource_database.get_by_type_and_index(
            ResourceType.ITEM, requirement.item_index)

    return result
Beispiel #18
0
def create_ammo_expansion(
    ammo: Ammo,
    ammo_count: Sequence[int],
    requires_major_item: bool,
    resource_database: ResourceDatabase,
) -> PickupEntry:
    """
    Creates a Pickup for an expansion of the given ammo.
    :param ammo:
    :param ammo_count:
    :param requires_major_item:
    :param resource_database:
    :return:
    """
    resources = [(resource_database.get_item(item), count)
                 for item, count in zip(ammo.items, ammo_count)]

    if resource_database.item_percentage is not None:
        resources.append((resource_database.item_percentage, 1))

    return PickupEntry(
        name=ammo.name,
        progression=(),
        extra_resources=tuple(resources),
        model=PickupModel(
            game=resource_database.game_enum,
            name=ammo.model_name,
        ),
        item_category=ammo.item_category,
        broad_category=ammo.broad_category,
        respects_lock=requires_major_item,
        resource_lock=ammo.create_resource_lock(resource_database),
        probability_multiplier=2,
    )
Beispiel #19
0
def create_sky_temple_key(
    key_number: int,
    resource_database: ResourceDatabase,
) -> PickupEntry:
    """

    :param key_number:
    :param resource_database:
    :return:
    """
    SKY_TEMPLE_KEY_CATEGORY = ItemCategory(name="sky_temple_key",
                                           long_name="",
                                           hint_details=("a ",
                                                         "Sky Temple Key"),
                                           is_major=False,
                                           is_key=True)

    return PickupEntry(
        name="Sky Temple Key {}".format(key_number + 1),
        progression=((resource_database.get_item(
            echoes_items.SKY_TEMPLE_KEY_ITEMS[key_number]), 1), ),
        model=PickupModel(
            game=resource_database.game_enum,
            name=echoes_items.SKY_TEMPLE_KEY_MODEL,
        ),
        item_category=SKY_TEMPLE_KEY_CATEGORY,
        broad_category=GENERIC_KEY_CATEGORY,
        probability_offset=3,
    )
Beispiel #20
0
def create_dark_temple_key(
    key_number: int,
    temple_index: int,
    resource_database: ResourceDatabase,
) -> PickupEntry:
    """
    Creates a Dark Temple Key
    :param key_number:
    :param temple_index: The index of the temple: Dark Agon, Dark Torvus, Hive Temple
    :param resource_database:
    :return:
    """
    TEMPLE_KEY_CATEGORY = ItemCategory(name="temple_key",
                                       long_name="",
                                       hint_details=("a ", "red Temple Key"),
                                       is_major=False,
                                       is_key=True)

    return PickupEntry(
        name=echoes_items.DARK_TEMPLE_KEY_NAMES[temple_index].format(
            key_number + 1),
        progression=((resource_database.get_item(
            echoes_items.DARK_TEMPLE_KEY_ITEMS[temple_index][key_number]),
                      1), ),
        model=PickupModel(
            game=resource_database.game_enum,
            name=echoes_items.DARK_TEMPLE_KEY_MODEL,
        ),
        item_category=TEMPLE_KEY_CATEGORY,
        broad_category=GENERIC_KEY_CATEGORY,
        probability_offset=3,
    )
 def with_data(cls, database: ResourceDatabase, resource_type: ResourceType,
               requirement_name: str, amount: int,
               negate: bool) -> ResourceRequirement:
     return cls.create(
         database.get_by_type_and_index(resource_type, requirement_name),
         amount,
         negate,
     )
Beispiel #22
0
 def damage(self, current_resources: CurrentResources,
            database: ResourceDatabase) -> int:
     if self.resource.resource_type == ResourceType.DAMAGE:
         return ceil(
             database.get_damage_reduction(self.resource, current_resources)
             * self.amount)
     else:
         return 0
Beispiel #23
0
def _starting_items_value_for(resource_database: ResourceDatabase,
                              starting_items: CurrentResources, index: str) -> Union[bool, int]:
    item = resource_database.get_item(index)
    value = starting_items.get(item, 0)
    if item.max_capacity > 1:
        return value
    else:
        return value > 0
Beispiel #24
0
    def __init__(self, parent: QWidget, item: MajorItem, starting_state: MajorItemState,
                 resources_database: ResourceDatabase):
        super().__init__(parent)
        self.setupUi(self)
        set_default_window_icon(self)
        self._item = item

        self.setWindowTitle(f"Item: {item.name}")
        self.item_name_label.setText(item.name)

        # connect
        self.excluded_radio.toggled.connect(self._on_select_excluded)
        self.vanilla_radio.toggled.connect(self._on_select_vanilla)
        self.starting_radio.toggled.connect(self._on_select_starting)
        self.shuffled_radio.toggled.connect(self._on_select_shuffled)
        self.shuffled_spinbox.valueChanged.connect(self._on_shuffled_value)
        self.provided_ammo_spinbox.valueChanged.connect(self._on_shuffled_value)

        # Update
        self.vanilla_radio.setEnabled(item.original_index is not None)
        self.shuffled_radio.setEnabled(item.model_index is not None)

        if not self.vanilla_radio.isEnabled():
            self.vanilla_radio.setToolTip(
                "This item does not exist in the original game, so there's no vanilla location.")

        if not self.shuffled_radio.isEnabled():
            self.shuffled_radio.setToolTip(
                "There's currently no available model and/or logic for this item to be shuffled.")

        # At least one radio should be selected
        for radio in (self.shuffled_radio, self.starting_radio, self.vanilla_radio):
            if radio.isEnabled():
                radio.setChecked(True)
                break

        if item.ammo_index:
            self.provided_ammo_label.setText(
                "<html><head/><body><p>Provided Ammo ({})</p></body></html>".format(
                    " and ".join(
                        resources_database.get_item(ammo_index).long_name
                        for ammo_index in item.ammo_index
                    )
                )
            )
        else:
            self.provided_ammo_label.hide()
            self.provided_ammo_spinbox.hide()

        if self._item.required:
            self.item_name_label.setToolTip(
                "This item is necessary for the game to function properly and can't be removed.")
            self.setEnabled(False)
            self.state = self._create_state(num_included_in_starting_items=1)
        else:
            if self._item.warning is not None:
                self.item_name_label.setToolTip(self._item.warning)
            self.state = starting_state
def _starting_items_value_for(resource_database: ResourceDatabase,
                              starting_items: ResourceCollection,
                              index: str) -> Union[bool, int]:
    item = resource_database.get_item(index)
    value = starting_items[item]
    if item.max_capacity > 1:
        return value
    else:
        return value > 0
Beispiel #26
0
def read_resource_reductions_dict(
    data: List[Dict],
    db: ResourceDatabase,
) -> Dict[SimpleResourceInfo, List[DamageReduction]]:
    return {
        db.get_by_type_and_index(ResourceType.DAMAGE, item["name"]):
        read_damage_reductions(item["reductions"], db.item)
        for item in data
    }
Beispiel #27
0
 def with_data(cls,
               database: ResourceDatabase,
               resource_type: ResourceType,
               requirement_index: int,
               amount: int,
               negate: bool) -> "IndividualRequirement":
     return cls(
         database.get_by_type_and_index(resource_type, requirement_index),
         amount,
         negate)
def create_ammo_expansion(
    ammo: Ammo,
    ammo_count: List[int],
    requires_major_item: bool,
    resource_database: ResourceDatabase,
) -> PickupEntry:
    """
    Creates a Pickup for an expansion of the given ammo.
    :param ammo:
    :param ammo_count:
    :param requires_major_item:
    :param resource_database:
    :return:
    """
    resources = [(resource_database.get_item(item), count)
                 for item, count in zip(ammo.items, ammo_count)]
    resources.append((resource_database.item_percentage, 1))

    if ammo.unlocked_by is not None and requires_major_item:
        temporary_resources = [
            (resource_database.get_item(item), count)
            for item, count in zip(ammo.temporaries, ammo_count)
        ]
        temporary_resources.append((resource_database.item_percentage, 1))

        conditional_resources = (
            ConditionalResources(temporary_resources[0][0].long_name, None,
                                 tuple(temporary_resources)),
            ConditionalResources(ammo.name,
                                 resource_database.get_item(ammo.unlocked_by),
                                 tuple(resources)),
        )
    else:
        conditional_resources = (ConditionalResources(None, None,
                                                      tuple(resources)), )

    return PickupEntry(
        name=ammo.name,
        resources=conditional_resources,
        model_index=ammo.models[0],  # TODO: use a random model
        item_category=ItemCategory.EXPANSION,
        broad_category=ammo.broad_category,
    )
Beispiel #29
0
 def prime1_progressive_damage_reduction(self, db: ResourceDatabase, current_resources: ResourceCollection):
     num_suits = sum(current_resources[db.get_item_by_name(suit)]
                     for suit in ["Varia Suit", "Gravity Suit", "Phazon Suit"])
     if num_suits >= 3:
         return 0.5
     elif num_suits == 2:
         return 0.8
     elif num_suits == 1:
         return 0.9
     else:
         return 1
Beispiel #30
0
def read_resource_database(data: Dict) -> ResourceDatabase:
    item = read_resource_info_array(data["items"], ResourceType.ITEM)
    return ResourceDatabase(
        item=item,
        event=read_resource_info_array(data["events"], ResourceType.EVENT),
        trick=read_resource_info_array(data["tricks"], ResourceType.TRICK),
        damage=read_damage_resource_info_array(data["damage"], item),
        version=read_resource_info_array(data["versions"], ResourceType.VERSION),
        misc=read_resource_info_array(data["misc"], ResourceType.MISC),
        difficulty=read_resource_info_array(data["difficulty"], ResourceType.DIFFICULTY),
    )