def bit_pack_unpack(cls, decoder: BitPackDecoder, database: ResourceDatabase) -> PickupEntry: helper = DatabaseBitPackHelper(database) name = bitpacking.decode_string(decoder) model = PickupModel( game=RandovaniaGame.bit_pack_unpack(decoder, {}), name=bitpacking.decode_string(decoder), ) item_category = ItemCategory.bit_pack_unpack(decoder, {}) broad_category = ItemCategory.bit_pack_unpack(decoder, {}) progression = bitpacking.decode_tuple(decoder, helper.decode_resource_quantity) extra_resources = bitpacking.decode_tuple(decoder, helper.decode_resource_quantity) unlocks_resource = bitpacking.decode_bool(decoder) resource_lock = None if bitpacking.decode_bool(decoder): resource_lock = helper.decode_resource_lock(decoder) respects_lock = bitpacking.decode_bool(decoder) probability_offset = BitPackFloat.bit_pack_unpack(decoder, _PROBABILITY_OFFSET_META) probability_multiplier = BitPackFloat.bit_pack_unpack(decoder, _PROBABILITY_MULTIPLIER_META) return PickupEntry( name=name, model=model, item_category=item_category, broad_category=broad_category, progression=progression, extra_resources=extra_resources, unlocks_resource=unlocks_resource, resource_lock=resource_lock, respects_lock=respects_lock, probability_offset=probability_offset, probability_multiplier=probability_multiplier, )
def bit_pack_unpack(cls, decoder: BitPackDecoder, name: str, database: ResourceDatabase) -> PickupEntry: model_index = decoder.decode_single(255) probability_offset = BitPackFloat.bit_pack_unpack( decoder, _PROBABILITY_OFFSET_META) probability_multiplier = BitPackFloat.bit_pack_unpack( decoder, _PROBABILITY_MULTIPLIER_META) item_category = ItemCategory.bit_pack_unpack(decoder, {}) broad_category = ItemCategory.bit_pack_unpack(decoder, {}) has_name = bitpacking.decode_bool(decoder) num_conditional = decoder.decode_single( MAXIMUM_PICKUP_CONDITIONAL_RESOURCES) + 1 conditional_resources = [] for i in range(num_conditional): item_name = None # TODO: get the first resource name if i > 0: item_dependency = decoder.decode_element(database.item) else: item_dependency = None resources = [] for _ in range(decoder.decode_single(MAXIMUM_PICKUP_RESOURCES + 1)): resource = decoder.decode_element(database.item) quantity = decoder.decode_single(255) resources.append((resource, quantity)) if has_name: if bitpacking.decode_bool(decoder): item_name = name else: item_name = resources[0][0].long_name conditional_resources.append( ConditionalResources( name=item_name, item=item_dependency, resources=tuple(resources), )) num_convert = decoder.decode_single(MAXIMUM_PICKUP_CONVERSION + 1) convert_resources = [] for i in range(num_convert): source = decoder.decode_element(database.item) target = decoder.decode_element(database.item) convert_resources.append(ResourceConversion(source, target)) return PickupEntry( name=name, model_index=model_index, item_category=item_category, broad_category=broad_category, resources=tuple(conditional_resources), convert_resources=tuple(convert_resources), probability_offset=probability_offset, probability_multiplier=probability_multiplier, )
def _state_with_data(request): item = MajorItem( name="Item Name", item_category=ItemCategory(request.param.get("category", "visor")), broad_category=ItemCategory( request.param.get("broad_category", "visor")), model_name="Model Name", 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 from_json(cls, name: str, value: dict) -> "MajorItem": return cls( name=name, item_category=ItemCategory(value["item_category"]), broad_category=ItemCategory(value["broad_category"]), model_name=value["model_name"], progression=tuple(value["progression"]), ammo_index=tuple(value.get("ammo", [])), unlocks_ammo=value.get("unlocks_ammo", False), required=value.get("required", False), original_index=PickupIndex(value["original_index"]) if "original_index" in value else None, probability_offset=value["probability_offset"], probability_multiplier=value["probability_multiplier"], warning=value.get("warning"), )
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, )
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, )
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 read_database( major_items_data: Dict, ammo_data: Dict, ) -> ItemDatabase: """ :param major_items_data: :param ammo_data: :return: """ major_items = { name: MajorItem.from_json(name, value) for name, value in major_items_data["items"].items() } ammo = { name: Ammo.from_json(name, value) for name, value in ammo_data.items() } default_items = { ItemCategory(category_name): tuple(major_items[item_name] for item_name in value) for category_name, value in major_items_data["default_items"].items() } return ItemDatabase(major_items, ammo, default_items)
def generic_item_category() -> ItemCategory: return ItemCategory( name="generic", long_name="Generic Item Category", hint_details=("an ", "unspecified item"), is_major=False )
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, )
def read_database(database_data: Dict, game: RandovaniaGame) -> ItemDatabase: """ :param database_data: :param game: :return: """ migrations.migrate_current(database_data) item_categories = { name: ItemCategory.from_json(name, value) for name, value in database_data["item_categories"].items() } major_items = { name: MajorItem.from_json(name, value, game, item_categories) for name, value in database_data["items"].items() } ammo = { name: Ammo.from_json(name, value, game, item_categories) for name, value in database_data["ammo"].items() } default_items = { item_categories[category_name]: tuple(major_items[item_name] for item_name in value) for category_name, value in database_data["default_items"].items() } return ItemDatabase(item_categories, major_items, ammo, default_items)
def _decode_item_category(decoder: BitPackDecoder) -> ItemCategory: return ItemCategory(name=bitpacking.decode_string(decoder), long_name=bitpacking.decode_string(decoder), hint_details=(bitpacking.decode_string(decoder), bitpacking.decode_string(decoder)), is_major=bitpacking.decode_bool(decoder), is_key=bitpacking.decode_bool(decoder))
def from_json(cls, name: str, value: dict) -> "Ammo": return cls( name=name, maximum=value["maximum"], models=tuple(value["models"]), items=tuple(value["items"]), broad_category=ItemCategory(value["broad_category"]), unlocked_by=value.get("unlocked_by"), temporaries=tuple(value.get("temporaries", [])), )
def from_json(cls, name: str, value: dict) -> "MajorItem": return cls( name=name, item_category=ItemCategory(value["item_category"]), model_index=value["model_index"], progression=tuple(value["progression"]), ammo_index=tuple(value.get("ammo", [])), converts_indices=tuple(value.get("converts_indices", [])), required=value.get("required", False), original_index=PickupIndex(value["original_index"]) if "original_index" in value else None, probability_offset=value["probability_offset"], )
"model_name": self.model_name, "items": frozen_lib.unwrap(self.items), "broad_category": self.broad_category.name, "extra": frozen_lib.unwrap(self.extra), } if self.unlocked_by is not None: result["temporary"] = self.temporary result["unlocked_by"] = self.unlocked_by return result @property def item_category(self) -> ItemCategory: return AMMO_ITEM_CATEGORY 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 AMMO_ITEM_CATEGORY = ItemCategory( name="expansion", long_name="", hint_details=("an ", "expansion"), is_major=False )
def test_create_pickup_for(percentage: bool, echoes_item_database, echoes_resource_database, generic_item_category): # Setup item_a = echoes_resource_database.get_item("DarkVisor") item_b = echoes_resource_database.get_item("MorphBall") item_c = echoes_resource_database.get_item("Bombs") ammo_a = echoes_resource_database.get_item("EnergyTank") ammo_b = echoes_resource_database.get_item("DarkAmmo") less_generic_item_category = ItemCategory(name="the_category", long_name="The Category", hint_details=("a ", " wonderful item"), is_major=True) major_item = MajorItem( game=echoes_resource_database.game_enum, name="The Item", item_category=less_generic_item_category, broad_category=generic_item_category, model_name="SuperModel", progression=("DarkVisor", "MorphBall", "Bombs"), default_starting_count=0, default_shuffled_count=1, ammo_index=("EnergyTank", "DarkAmmo"), must_be_starting=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 = 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=less_generic_item_category, broad_category=generic_item_category, probability_offset=5, respects_lock=False, )