def test_state_change_to_starting(skip_qtbot, echoes_item_database, echoes_resource_database): item = [ item for item in echoes_item_database.major_items.values() if not item.required ][0] state = MajorItemState( include_copy_in_original_location=False, num_shuffled_pickups=1, num_included_in_starting_items=0, included_ammo=(), ) # Run popup = ItemConfigurationPopup(None, item, state, echoes_resource_database) skip_qtbot.addWidget(popup) skip_qtbot.mouseClick(popup.starting_radio, QtCore.Qt.LeftButton) # Assert assert popup.state == MajorItemState( include_copy_in_original_location=False, num_shuffled_pickups=0, num_included_in_starting_items=1, included_ammo=(), )
def test_state_required(skip_qtbot, echoes_item_database, echoes_resource_database): item = [ item for item in echoes_item_database.major_items.values() if item.required ][0] state = MajorItemState( include_copy_in_original_location=False, num_shuffled_pickups=1, num_included_in_starting_items=0, included_ammo=(), ) # Run popup = ItemConfigurationWidget(None, item, state, echoes_resource_database) skip_qtbot.addWidget(popup) # Assert assert popup.state == MajorItemState( include_copy_in_original_location=False, num_shuffled_pickups=0, num_included_in_starting_items=1, included_ammo=(), )
def from_json(cls, value: dict, game: RandovaniaGame) -> "MajorItemsConfiguration": item_database = default_database.item_database_for_game(game) items_state = {} for name, item in item_database.major_items.items(): if name in value["items_state"]: state = MajorItemState.from_json(value["items_state"][name]) else: state = MajorItemState() items_state[item] = state default_items = {} for category, options in item_database.default_items.items(): default_items[category] = item_database.major_items[ value["default_items"][category.value]] if default_items[category] not in options: raise ValueError( f"Category {category} has {default_items[category]} as default item, " f"but that's not a valid option.") return cls( items_state=items_state, default_items=default_items, minimum_random_starting_items=value[ "minimum_random_starting_items"], maximum_random_starting_items=value[ "maximum_random_starting_items"], )
def change_progressive(self, is_progressive: bool): with self._editor as editor: if is_progressive: non_progressive_state = MajorItemState() progressive_state = MajorItemState(num_shuffled_pickups=len(self.non_progressive_items)) else: non_progressive_state = MajorItemState(num_shuffled_pickups=1) progressive_state = MajorItemState() new_states = {self.progressive_item: progressive_state} for item in self.non_progressive_items: new_states[item] = non_progressive_state editor.major_items_configuration = editor.major_items_configuration.replace_states(new_states)
def state(self) -> MajorItemState: if self.included_box.isChecked(): return MajorItemState( include_copy_in_original_location=self.vanilla_radio.isChecked(), num_shuffled_pickups=self.shuffled_spinbox.value() if self.shuffled_radio.isChecked() else 0, num_included_in_starting_items=1 if self.starting_radio.isChecked() else 0, included_ammo=self.included_ammo, ) else: return MajorItemState( include_copy_in_original_location=False, num_shuffled_pickups=0, num_included_in_starting_items=0, included_ammo=self.included_ammo, )
def test_create_seeker_launcher( ammo_quantity: int, ammo_requires_major_item: bool, echoes_item_database, echoes_resource_database, ): # Setup missile = echoes_resource_database.get_item(44) missile_launcher = echoes_resource_database.get_item(73) seeker_launcher = echoes_resource_database.get_item(26) temporary = echoes_resource_database.get_item(71) state = MajorItemState( include_copy_in_original_location=False, num_shuffled_pickups=0, num_included_in_starting_items=0, included_ammo=(ammo_quantity, ), ) # Run result = randovania.generator.item_pool.pickup_creator.create_major_item( echoes_item_database.major_items["Seeker Launcher"], state, True, echoes_resource_database, echoes_item_database.ammo["Missile Expansion"], ammo_requires_major_item) # Assert locked_conditional = ( ConditionalResources("Seeker Launcher", None, resources=( (seeker_launcher, 1), (temporary, ammo_quantity), (echoes_resource_database.item_percentage, 1), )), ConditionalResources("Seeker Launcher", missile_launcher, resources=( (seeker_launcher, 1), (missile, ammo_quantity), (echoes_resource_database.item_percentage, 1), )), ) normal_resources = (ConditionalResources( "Seeker Launcher", None, resources=( (seeker_launcher, 1), (missile, ammo_quantity), (echoes_resource_database.item_percentage, 1), )), ) assert result == PickupEntry( name="Seeker Launcher", resources=locked_conditional if ammo_requires_major_item else normal_resources, model_index=25, item_category=ItemCategory.MISSILE, broad_category=ItemCategory.MISSILE_RELATED, )
def bit_pack_unpack(cls, decoder: BitPackDecoder, metadata) -> "MajorItemsConfiguration": reference: MajorItemsConfiguration = metadata["reference"] num_items = decoder.decode_single(len(reference.items_state)) indices_with_custom = { decoder.decode_single(len(reference.items_state)) for _ in range(num_items) } items_state = {} for index, item in enumerate(reference.items_state.keys()): if index in indices_with_custom: items_state[item] = MajorItemState.bit_pack_unpack( decoder, item) else: items_state[item] = reference.items_state[item] minimum, maximum = decoder.decode(RANDOM_STARTING_ITEMS_LIMIT, RANDOM_STARTING_ITEMS_LIMIT) return cls(items_state, minimum_random_starting_items=minimum, maximum_random_starting_items=maximum)
def _on_default_item_updated(self, category: ItemCategory, combo: QtWidgets.QComboBox, _): with self._editor as editor: new_config = editor.major_items_configuration new_config = new_config.replace_default_item(category, combo.currentData()) new_config = new_config.replace_state_for_item(combo.currentData(), MajorItemState(num_included_in_starting_items=1)) editor.major_items_configuration = new_config
def bit_pack_unpack(cls, decoder: BitPackDecoder, metadata) -> "MajorItemsConfiguration": from randovania.game_description import default_database item_database = default_database.default_prime2_item_database() progressive_suit = bitpacking.decode_bool(decoder) progressive_grapple = bitpacking.decode_bool(decoder) progressive_launcher = bitpacking.decode_bool(decoder) default = MajorItemsConfiguration.default() num_items = decoder.decode_single(len(default.items_state)) indices_with_custom = { decoder.decode_single(len(default.items_state)) for _ in range(num_items) } items_state = {} for index, item in enumerate(item_database.major_items.values()): if index in indices_with_custom: items_state[item] = MajorItemState.bit_pack_unpack( decoder, item) else: items_state[item] = default.items_state[item] minimum, maximum = decoder.decode(RANDOM_STARTING_ITEMS_LIMIT, RANDOM_STARTING_ITEMS_LIMIT) return cls(items_state, progressive_suit=progressive_suit, progressive_grapple=progressive_grapple, progressive_launcher=progressive_launcher, minimum_random_starting_items=minimum, maximum_random_starting_items=maximum)
def from_json(cls, value: dict, item_database: ItemDatabase) -> "MajorItemsConfiguration": items_state = {} for name, item in item_database.major_items.items(): if name in value["items_state"]: state = MajorItemState.from_json(value["items_state"][name]) else: state = MajorItemState() items_state[item] = state return cls( items_state=items_state, minimum_random_starting_items=value[ "minimum_random_starting_items"], maximum_random_starting_items=value[ "maximum_random_starting_items"], )
def test_create_pickup_for(percentage: bool, echoes_resource_database): # Setup item_a = echoes_resource_database.get_item(10) item_b = echoes_resource_database.get_item(15) item_c = echoes_resource_database.get_item(18) ammo_a = echoes_resource_database.get_item(40) ammo_b = echoes_resource_database.get_item(42) major_item = MajorItem( name="The Item", item_category=ItemCategory.MORPH_BALL, broad_category=ItemCategory.MORPH_BALL_RELATED, model_name="SuperModel", progression=(10, 15, 18), ammo_index=(40, 42), required=False, original_index=None, probability_offset=5, ) state = MajorItemState( include_copy_in_original_location=False, num_shuffled_pickups=0, num_included_in_starting_items=0, included_ammo=(10, 20), ) if percentage: extra_resources = ( (ammo_a, 10), (ammo_b, 20), (echoes_resource_database.item_percentage, 1), ) else: extra_resources = ( (ammo_a, 10), (ammo_b, 20), ) # Run result = randovania.generator.item_pool.pickup_creator.create_major_item(major_item, state, percentage, echoes_resource_database, None, False) # Assert assert result == PickupEntry( name="The Item", model=PickupModel(echoes_resource_database.game_enum, "SuperModel"), progression=( (item_a, 1), (item_b, 1), (item_c, 1), ), extra_resources=extra_resources, item_category=ItemCategory.MORPH_BALL, broad_category=ItemCategory.MORPH_BALL_RELATED, probability_offset=5, respects_lock=False, )
def test_decode(state_with_data): # Setup item, data, expected = state_with_data # Run decoder = BitPackDecoder(data) result = MajorItemState.bit_pack_unpack(decoder, item) # Assert assert result == expected
def test_pickup_scan_for_progressive_suit(echoes_item_database, echoes_resource_database): # Setup progressive_suit = echoes_item_database.major_items["Progressive Suit"] pickup = pickup_creator.create_major_item(progressive_suit, MajorItemState(), False, echoes_resource_database, None, False) # Run result = patcher_file._pickup_scan(pickup) # Assert assert result == "Progressive Suit. Provides the following in order: Dark Suit, Light Suit"
def _create_state(self, *, include_copy_in_original_location=False, num_shuffled_pickups=0, num_included_in_starting_items=0, ): return MajorItemState( include_copy_in_original_location=include_copy_in_original_location, num_shuffled_pickups=num_shuffled_pickups, num_included_in_starting_items=num_included_in_starting_items, included_ammo=self.included_ammo, )
def _state_with_data(request): item = MajorItem( name="Item Name", item_category=ItemCategory(request.param.get("category", "visor")), model_index=0, progression=(), ammo_index=request.param.get("ammo_index", ()), required=True, original_index=None, probability_offset=0, ) return item, request.param["encoded"], MajorItemState.from_json(request.param["json"])
def get_default_major_items_configurations() -> MajorItemsConfiguration: item_database = default_prime2_item_database() with get_data_path().joinpath("item_database", "default_state", "major-items.json").open() as open_file: data = json.load(open_file) return MajorItemsConfiguration( items_state={ item_database.major_items[name]: MajorItemState.from_json(state_data) for name, state_data in data["items_state"].items() } )
def _change_progressive_launcher(self, has_progressive: bool): with self._options as options: major_configuration = options.major_items_configuration # Progressive launcher is added twice to the list so it's value is doubled. total = sum(major_configuration.items_state[item].included_ammo[0] for item in [self._missile_launcher, self._seeker_launcher, self._progressive_launcher, self._progressive_launcher]) // 2 if has_progressive: launcher_state = MajorItemState(included_ammo=(0,)) seekers_state = MajorItemState(included_ammo=(0,)) progressive_state = MajorItemState(num_shuffled_pickups=2, included_ammo=(total,)) else: launcher_state = MajorItemState(num_shuffled_pickups=1, included_ammo=(total,)) seekers_state = MajorItemState(num_shuffled_pickups=1, included_ammo=(total,)) progressive_state = MajorItemState(included_ammo=(0,)) major_configuration = major_configuration.replace_states({ self._missile_launcher: launcher_state, self._seeker_launcher: seekers_state, self._progressive_launcher: progressive_state, }) options.major_items_configuration = major_configuration
def _create_major_item_boxes(self, item_database: ItemDatabase, resource_database: ResourceDatabase): for major_item in item_database.major_items.values(): if major_item.required or major_item.item_category == ItemCategory.ENERGY_TANK: continue category_box, category_layout, elements = self._boxes_for_category[major_item.item_category] widget = ItemConfigurationWidget(None, major_item, MajorItemState(), resource_database) widget.Changed.connect(partial(self._on_major_item_updated, widget)) row = category_layout.rowCount() category_layout.addWidget(widget, row, 0, 1, -1) elements[major_item] = widget
def test_get_single_hud_text_all_major_items(echoes_item_database, echoes_resource_database): memo_data = default_prime2_memo_data() # Run for item in echoes_item_database.major_items.values(): pickup = pickup_creator.create_major_item(item, MajorItemState(), False, echoes_resource_database, None, False) result = patcher_file._get_all_hud_text(pickup, memo_data) for i, progression in enumerate(pickup.resources): assert progression.name in result[i] assert result for line in result: assert len(line) > 10 assert isinstance(line, str)
def test_create_seeker_launcher(ammo_quantity: int, ammo_requires_major_item: bool, echoes_item_database, echoes_resource_database, ): # Setup missile = echoes_resource_database.get_item(44) missile_launcher = echoes_resource_database.get_item(73) seeker_launcher = echoes_resource_database.get_item(26) temporary = echoes_resource_database.get_item(71) state = MajorItemState( include_copy_in_original_location=False, num_shuffled_pickups=0, num_included_in_starting_items=0, included_ammo=(ammo_quantity,), ) # Run result = randovania.generator.item_pool.pickup_creator.create_major_item( echoes_item_database.major_items["Seeker Launcher"], state, True, echoes_resource_database, echoes_item_database.ammo["Missile Expansion"], ammo_requires_major_item ) # Assert assert result == PickupEntry( name="Seeker Launcher", progression=( (seeker_launcher, 1), ), extra_resources=( (missile, ammo_quantity), (echoes_resource_database.item_percentage, 1), ), model=PickupModel(echoes_resource_database.game_enum, "SeekerLauncher"), item_category=ItemCategory.MISSILE, broad_category=ItemCategory.MISSILE_RELATED, respects_lock=ammo_requires_major_item, resource_lock=ResourceLock( locked_by=missile_launcher, temporary_item=temporary, item_to_lock=missile, ), )
def from_json(cls, value: dict, item_database: ItemDatabase) -> "MajorItemsConfiguration": default = cls.default() items_state = copy.copy(default.items_state) for name, state_data in value["items_state"].items(): items_state[item_database.major_items[ name]] = MajorItemState.from_json(state_data) return cls( items_state=items_state, progressive_suit=value["progressive_suit"], progressive_grapple=value["progressive_grapple"], minimum_random_starting_items=value[ "minimum_random_starting_items"], maximum_random_starting_items=value[ "maximum_random_starting_items"], )
def test_get_single_hud_text_all_major_items(echoes_item_database, echoes_resource_database): memo_data = default_database.default_prime2_memo_data() # Run for item in echoes_item_database.major_items.values(): pickup = pickup_creator.create_major_item(item, MajorItemState(), False, echoes_resource_database, None, False) result = pickup_exporter._get_all_hud_text( pickup_exporter._conditional_resources_for_pickup(pickup), memo_data) for i, progression in enumerate(pickup.progression): assert progression[0].long_name in result[i] assert result for line in result: assert len(line) > 10 assert isinstance(line, str)
def test_create_missile_launcher(ammo_quantity: int, echoes_item_database, echoes_resource_database): # Setup missile = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 44) missile_launcher = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 73) temporary = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 71) state = MajorItemState( include_copy_in_original_location=False, num_shuffled_pickups=0, num_included_in_starting_items=0, included_ammo=(ammo_quantity, ), ) # Run result = randovania.generator.item_pool.pickup_creator.create_major_item( echoes_item_database.major_items["Missile Launcher"], state, True, echoes_resource_database, echoes_item_database.ammo["Missile Expansion"], True) # Assert assert result == PickupEntry( name="Missile Launcher", resources=(ConditionalResources( "Missile Launcher", None, resources=( (missile_launcher, 1), (missile, ammo_quantity), (echoes_resource_database.item_percentage, 1), )), ), convert_resources=(ResourceConversion(source=temporary, target=missile), ), model_index=24, item_category=ItemCategory.MISSILE, )
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}
def bit_pack_unpack(cls, decoder: BitPackDecoder, metadata) -> "MajorItemsConfiguration": reference: MajorItemsConfiguration = metadata["reference"] num_items = decoder.decode_single(len(reference.items_state)) indices_with_custom = { decoder.decode_single(len(reference.items_state)) for _ in range(num_items) } items_state = {} for index, item in enumerate(reference.items_state.keys()): if index in indices_with_custom: items_state[item] = MajorItemState.bit_pack_unpack( decoder, item) else: items_state[item] = reference.items_state[item] # default_items default_items = {} for category in reference.default_items.keys(): all_major = [ major for major in reference.items_state.keys() if major.item_category == category ] default_items[category] = decoder.decode_element(all_major) # random starting items minimum, maximum = decoder.decode(RANDOM_STARTING_ITEMS_LIMIT, RANDOM_STARTING_ITEMS_LIMIT) return cls(items_state, default_items=default_items, minimum_random_starting_items=minimum, maximum_random_starting_items=maximum)
def _change_progressive_grapple(self, has_progressive: bool): with self._editor as options: major_configuration = options.major_items_configuration if has_progressive: grapple_state = MajorItemState() screw_state = MajorItemState() progressive_state = MajorItemState(num_shuffled_pickups=2) else: grapple_state = MajorItemState(num_shuffled_pickups=1) screw_state = MajorItemState(num_shuffled_pickups=1) progressive_state = MajorItemState() major_configuration = major_configuration.replace_states({ self._grapple_beam: grapple_state, self._screw_attack: screw_state, self._progressive_grapple: progressive_state, }) options.major_items_configuration = major_configuration
def _change_progressive_suit(self, has_progressive: bool): with self._editor as options: major_configuration = options.major_items_configuration if has_progressive: dark_suit_state = MajorItemState() light_suit_state = MajorItemState() progressive_suit_state = MajorItemState(num_shuffled_pickups=2) else: dark_suit_state = MajorItemState(num_shuffled_pickups=1) light_suit_state = MajorItemState(num_shuffled_pickups=1) progressive_suit_state = MajorItemState() major_configuration = major_configuration.replace_states({ self._dark_suit: dark_suit_state, self._light_suit: light_suit_state, self._progressive_suit: progressive_suit_state, }) options.major_items_configuration = major_configuration
def test_blank_as_json(): assert MajorItemState().as_json == {}
def test_blank_from_json(): assert MajorItemState.from_json({}) == MajorItemState()
def test_create_pickup_for(percentage: bool, has_convert: bool, echoes_resource_database): # Setup item_a = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 10) item_b = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 15) item_c = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 18) ammo_a = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 40) ammo_b = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 42) temporary_a = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 71) temporary_b = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 72) major_item = MajorItem( name="The Item", item_category=ItemCategory.MORPH_BALL, model_index=1337, progression=(10, 15, 18), ammo_index=(40, 42), converts_indices=(71, 72) if has_convert else (), required=False, original_index=None, probability_offset=5, ) state = MajorItemState( include_copy_in_original_location=False, num_shuffled_pickups=0, num_included_in_starting_items=0, included_ammo=(10, 20), ) def _create_resources(item): if percentage: return ( (item, 1), (ammo_a, 10), (ammo_b, 20), (echoes_resource_database.item_percentage, 1), ) else: return ( (item, 1), (ammo_a, 10), (ammo_b, 20), ) # Run result = randovania.generator.item_pool.pickup_creator.create_major_item( major_item, state, percentage, echoes_resource_database, None, False) # Assert assert result == PickupEntry( name="The Item", model_index=1337, resources=( ConditionalResources( name="Dark Visor", item=None, resources=_create_resources(item_a), ), ConditionalResources( name="Morph Ball", item=item_a, resources=_create_resources(item_b), ), ConditionalResources( name="Morph Ball Bomb", item=item_b, resources=_create_resources(item_c), ), ), convert_resources=( ResourceConversion(source=temporary_a, target=ammo_a), ResourceConversion(source=temporary_b, target=ammo_b), ) if has_convert else (), item_category=ItemCategory.MORPH_BALL, probability_offset=5, )