From bb9176891dcf0a5fa359c31ed3b851d2fe61a00c Mon Sep 17 00:00:00 2001 From: jungleChief87 Date: Thu, 14 Aug 2025 22:00:03 -0500 Subject: [PATCH 1/2] support for random additional starting characters and key items --- worlds/sadx/ItemPool.py | 11 ++++++++++- worlds/sadx/Options.py | 17 +++++++++++++++++ worlds/sadx/StartingSetup.py | 7 +++++-- worlds/sadx/__init__.py | 3 ++- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/worlds/sadx/ItemPool.py b/worlds/sadx/ItemPool.py index 7a33aa4e89e3..932785076205 100644 --- a/worlds/sadx/ItemPool.py +++ b/worlds/sadx/ItemPool.py @@ -7,7 +7,7 @@ from .CharacterUtils import get_playable_character_item, is_character_playable, are_character_upgrades_randomized, \ get_character_upgrades_item from .Enums import Character -from .Items import filler_item_table +from .Items import filler_item_table, key_item_table from .Names import ItemName, LocationName from .Options import SonicAdventureDXOptions from .StartingSetup import StarterSetup @@ -24,6 +24,8 @@ def __init__(self, emblem_count_progressive=0, emblem_count_non_progressive=0, f def create_sadx_items(world: World, starter_setup: StarterSetup, options: SonicAdventureDXOptions): item_names = get_item_names(options, starter_setup) + starting_key_items = world.random.choices(key_item_table, k=options.starting_overworld_key_items.value) + # Remove the items that are already in the starting inventory for item in world.options.start_inventory: for _ in range(world.options.start_inventory[item]): @@ -70,6 +72,13 @@ def create_sadx_items(world: World, starter_setup: StarterSetup, options: SonicA world.multiworld.push_precollected(world.create_item(get_playable_character_item(starter_setup.character))) + # Setups for random starting items + for character in starter_setup.additional_starting_characters_list: + world.multiworld.push_precollected(world.create_item(get_playable_character_item(character))) + + for item in starting_key_items: + world.multiworld.push_precollected(world.create_item(item.name)) + world.multiworld.itempool += itempool return item_distribution diff --git a/worlds/sadx/Options.py b/worlds/sadx/Options.py index b6474b54fca8..e8078eb4ef23 100644 --- a/worlds/sadx/Options.py +++ b/worlds/sadx/Options.py @@ -159,6 +159,19 @@ class GuaranteedStartingChecks(Range): range_end = 10 default = 2 +class AdditionalStartingCharacters(Range): + """Adds extra characters to start inventory randomly based on set value.""" + display_name = "Additional Starting Character(s)" + range_start = 1 + range_end = 5 + default = 0 + +class StartingOverworldKeyItems(Range): + """Grants a number of items that allow extra overworld access at random.""" + display_name = "Starting Overworld Key Items" + range_start = 1 + range_end = 15 + default = 0 class EntranceRandomizer(Toggle): """ @@ -775,6 +788,8 @@ class SonicAdventureDXOptions(PerGameCommonOptions): starting_location: StartingLocationOption random_starting_location_per_character: RandomStartingLocationPerCharacter guaranteed_starting_checks: GuaranteedStartingChecks + additional_starting_characters: AdditionalStartingCharacters + starting_overworld_key_items: StartingOverworldKeyItems entrance_randomizer: EntranceRandomizer level_entrance_plando: LevelEntrancePlando @@ -891,6 +906,8 @@ class SonicAdventureDXOptions(PerGameCommonOptions): StartingLocationOption, RandomStartingLocationPerCharacter, GuaranteedStartingChecks, + AdditionalStartingCharacters, + StartingOverworldKeyItems, EntranceRandomizer, LevelEntrancePlando, SendDeathLinkChance, diff --git a/worlds/sadx/StartingSetup.py b/worlds/sadx/StartingSetup.py index 25742743e4c4..d53e46dfcb41 100644 --- a/worlds/sadx/StartingSetup.py +++ b/worlds/sadx/StartingSetup.py @@ -27,14 +27,13 @@ class StarterSetup: area: Area = None charactersWithArea: List[CharacterArea] = field(default_factory=list) level_mapping: dict[Area, Area] = field(default_factory=dict) - + additional_starting_characters_list: List[Character] = field(default_factory=list) def get_starting_area(self, character: Character) -> Area: for char_area in self.charactersWithArea: if char_area.character == character: return char_area.area return self.area - def generate_early_sadx(world: World, options: SonicAdventureDXOptions) -> StarterSetup: validate_settings(options) @@ -96,6 +95,10 @@ def generate_early_sadx(world: World, options: SonicAdventureDXOptions) -> Start used_areas.add(area) starter_setup.charactersWithArea.append(CharacterArea(character, area)) + for character_slot in range(0, options.additional_starting_characters.value): + if character_slot < len(possible_characters) - 1: + starter_setup.additional_starting_characters_list.append(possible_characters[character_slot+1]) + return starter_setup diff --git a/worlds/sadx/__init__.py b/worlds/sadx/__init__.py index 6cea5b008feb..50a8322b72c4 100644 --- a/worlds/sadx/__init__.py +++ b/worlds/sadx/__init__.py @@ -118,7 +118,8 @@ def generate_early(self): self.options.random_starting_location_per_character.value = passthrough[ "RandomStartingLocationPerCharacter"] self.options.guaranteed_starting_checks.value = passthrough["GuaranteedStartingChecks"] - + self.options.additional_starting_characters = passthrough["AdditionalStartingCharacters"] + self.options.starting_overworld_key_items = passthrough["StartingOverworldKeyItems"] self.options.chao_egg_checks.value = passthrough["SecretChaoEggs"] self.options.chao_races_checks.value = passthrough["ChaoRacesChecks"] self.options.chao_races_levels_to_access_percentage.value = passthrough[ From 66e73900a4aad127d2e85ee5ab2b6bfd9f46daf3 Mon Sep 17 00:00:00 2001 From: jungleChief87 Date: Wed, 20 Aug 2025 13:04:14 -0500 Subject: [PATCH 2/2] fixes for duplicates anf added failsafes --- worlds/sadx/ItemPool.py | 16 ++++++++++------ worlds/sadx/StartingSetup.py | 9 ++++++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/worlds/sadx/ItemPool.py b/worlds/sadx/ItemPool.py index 932785076205..414b983898f0 100644 --- a/worlds/sadx/ItemPool.py +++ b/worlds/sadx/ItemPool.py @@ -7,7 +7,7 @@ from .CharacterUtils import get_playable_character_item, is_character_playable, are_character_upgrades_randomized, \ get_character_upgrades_item from .Enums import Character -from .Items import filler_item_table, key_item_table +from .Items import filler_item_table from .Names import ItemName, LocationName from .Options import SonicAdventureDXOptions from .StartingSetup import StarterSetup @@ -23,9 +23,7 @@ def __init__(self, emblem_count_progressive=0, emblem_count_non_progressive=0, f def create_sadx_items(world: World, starter_setup: StarterSetup, options: SonicAdventureDXOptions): item_names = get_item_names(options, starter_setup) - - starting_key_items = world.random.choices(key_item_table, k=options.starting_overworld_key_items.value) - + # Remove the items that are already in the starting inventory for item in world.options.start_inventory: for _ in range(world.options.start_inventory[item]): @@ -75,8 +73,8 @@ def create_sadx_items(world: World, starter_setup: StarterSetup, options: SonicA # Setups for random starting items for character in starter_setup.additional_starting_characters_list: world.multiworld.push_precollected(world.create_item(get_playable_character_item(character))) - - for item in starting_key_items: + + for item in starter_setup.starting_key_items: world.multiworld.push_precollected(world.create_item(item.name)) world.multiworld.itempool += itempool @@ -140,7 +138,13 @@ def get_item_names(options: SonicAdventureDXOptions, starter_setup: StarterSetup ] item_names.remove(get_playable_character_item(starter_setup.character)) + + for character in starter_setup.additional_starting_characters_list: + item_names.remove(get_playable_character_item(character)) + for item in starter_setup.starting_key_items: + item_names.remove(item.name) + return item_names diff --git a/worlds/sadx/StartingSetup.py b/worlds/sadx/StartingSetup.py index d53e46dfcb41..a6a64aae1123 100644 --- a/worlds/sadx/StartingSetup.py +++ b/worlds/sadx/StartingSetup.py @@ -13,6 +13,7 @@ field_emblem_location_table, boss_location_table, capsule_location_table, mission_location_table from .Logic import area_connections, chao_egg_location_table, enemy_location_table, fish_location_table from .Options import SonicAdventureDXOptions +from .Items import key_item_table @dataclass @@ -28,6 +29,7 @@ class StarterSetup: charactersWithArea: List[CharacterArea] = field(default_factory=list) level_mapping: dict[Area, Area] = field(default_factory=dict) additional_starting_characters_list: List[Character] = field(default_factory=list) + starting_key_items: List[str] = field(default_factory=list) def get_starting_area(self, character: Character) -> Area: for char_area in self.charactersWithArea: if char_area.character == character: @@ -98,7 +100,12 @@ def generate_early_sadx(world: World, options: SonicAdventureDXOptions) -> Start for character_slot in range(0, options.additional_starting_characters.value): if character_slot < len(possible_characters) - 1: starter_setup.additional_starting_characters_list.append(possible_characters[character_slot+1]) - + + if options.starting_overworld_key_items.value > len(key_item_table): + starter_setup.starting_key_items = world.random.sample(key_item_table, k=len(key_item_table)) + else: + starter_setup.starting_key_items = world.random.sample(key_item_table, k=options.starting_overworld_key_items.value) + return starter_setup