diff --git a/BaseClasses.py b/BaseClasses.py index b47e7e51..dd7b24e2 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -127,6 +127,7 @@ class World(object): set_player_attr('glitch_boots', True) set_player_attr('progression_balancing', True) set_player_attr('local_items', set()) + set_player_attr('non_local_items', set()) set_player_attr('triforce_pieces_available', 30) set_player_attr('triforce_pieces_required', 20) set_player_attr('shop_shuffle', 'off') diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index 1a7f2ed3..f4e04fbd 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -266,6 +266,8 @@ def parse_arguments(argv, no_defaults=False): help='Specifies a list of items that will be in your starting inventory (separated by commas)') parser.add_argument('--local_items', default=defval(''), help='Specifies a list of items that will not spread across the multiworld (separated by commas)') + parser.add_argument('--non_local_items', default=defval(''), + help='Specifies a list of items that will not spread across the multiworld (separated by commas)') parser.add_argument('--custom', default=defval(False), help='Not supported.') parser.add_argument('--customitemarray', default=defval(False), help='Not supported.') parser.add_argument('--accessibility', default=defval('items'), const='items', nargs='?', choices=['items', 'locations', 'none'], help='''\ @@ -376,7 +378,7 @@ def parse_arguments(argv, no_defaults=False): 'shuffle', 'crystals_ganon', 'crystals_gt', 'open_pyramid', 'timer', 'countdown_start_time', 'red_clock_time', 'blue_clock_time', 'green_clock_time', 'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory', - 'local_items', 'retro', 'accessibility', 'hints', 'beemizer', + 'local_items', 'non_local_items', 'retro', 'accessibility', 'hints', 'beemizer', 'shufflebosses', 'enemy_shuffle', 'enemy_health', 'enemy_damage', 'shufflepots', 'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'heartbeep', "skip_progression_balancing", "triforce_pieces_available", diff --git a/Main.py b/Main.py index 84f449fc..a8221298 100644 --- a/Main.py +++ b/Main.py @@ -10,7 +10,7 @@ import zlib import concurrent.futures from BaseClasses import World, CollectionState, Item, Region, Location, Shop -from Items import ItemFactory +from Items import ItemFactory, item_table from Regions import create_regions, create_shops, mark_light_world_regions, lookup_vanilla_location_to_entrance from InvertedRegions import create_inverted_regions, mark_dark_world_regions from EntranceShuffle import link_entrances, link_inverted_entrances @@ -110,7 +110,13 @@ def main(args, seed=None): item = ItemFactory(tok.strip(), player) if item: world.push_precollected(item) - world.local_items[player] = {item.strip() for item in args.local_items[player].split(',')} + # item in item_table gets checked in mystery, but not CLI - so we double-check here + world.local_items[player] = {item.strip() for item in args.local_items[player].split(',') if + item.strip() in item_table} + world.non_local_items[player] = {item.strip() for item in args.local_items[player].split(',') if + item.strip() in item_table} + # items can't be both local and non-local, prefer local + world.non_local_items[player] -= world.local_items[player] world.triforce_pieces_available[player] = max(world.triforce_pieces_available[player], world.triforce_pieces_required[player]) diff --git a/Mystery.py b/Mystery.py index 27a29759..2e1b29e2 100644 --- a/Mystery.py +++ b/Mystery.py @@ -490,6 +490,17 @@ def roll_settings(weights): ret.local_items = ",".join(ret.local_items) + ret.non_local_items = set() + for item_name in weights.get('non_local_items', []): + items = item_name_groups.get(item_name, {item_name}) + for item in items: + if item in item_table: + ret.non_local_items.add(item) + else: + raise Exception(f"Could not force item {item} to be world-local, as it was not recognized.") + + ret.non_local_items = ",".join(ret.non_local_items) + if 'rom' in weights: romweights = weights['rom'] diff --git a/Rules.py b/Rules.py index 6baeeb96..04889a0f 100644 --- a/Rules.py +++ b/Rules.py @@ -179,6 +179,10 @@ def locality_rules(world, player): for location in world.get_locations(): if location.player != player: forbid_items_for_player(location, world.local_items[player], player) + if world.non_local_items[player]: + for location in world.get_locations(): + if location.player == player: + forbid_items_for_player(location, world.non_local_items[player], player) non_crossover_items = (item_name_groups["Small Keys"] | item_name_groups["Big Keys"] | progression_items) - { diff --git a/playerSettings.yaml b/playerSettings.yaml index 4760173f..469a96de 100644 --- a/playerSettings.yaml +++ b/playerSettings.yaml @@ -253,6 +253,11 @@ green_clock_time: # For all timer modes, the amount of time in minutes to gain o # - "Small Keys" # - "Big Keys" # Can be uncommented to use it +# non_local_items: # Force certain items to appear outside your world only, always across the multiworld. Recognizes some group names, like "Swords" +# - "Moon Pearl" +# - "Small Keys" +# - "Big Keys" +# Can be uncommented to use it # startinventory: # Begin the file with the listed items/upgrades # Pegasus Boots: on # Bomb Upgrade (+10): 4