diff --git a/BaseClasses.py b/BaseClasses.py index de3ad469..2fb9b3ea 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2895,6 +2895,10 @@ class Item(object): def compass(self): return self.type == 'Compass' + @property + def event(self): + return self.type == 'Event' + @property def dungeon(self): if not self.smallkey and not self.bigkey and not self.map and not self.compass: diff --git a/CLI.py b/CLI.py index 8711f649..62163b28 100644 --- a/CLI.py +++ b/CLI.py @@ -89,6 +89,7 @@ def parse_cli(argv, no_defaults=False): parser.add_argument('--count', default=defval(int(settings["count"]) if settings["count"] != "" and settings["count"] is not None else 1), help="\n".join(fish.translate("cli", "help", "count")), type=int) parser.add_argument('--tries', default=defval(int(settings["tries"]) if settings["tries"] != "" and settings["tries"] is not None else 1), help="\n".join(fish.translate("cli", "help", "tries")), type=int) parser.add_argument('--customitemarray', default={}, help=argparse.SUPPRESS) + parser.add_argument('--skip_money_balance', action="store_true", help=argparse.SUPPRESS) # included for backwards compatibility parser.add_argument('--multi', default=defval(settings["multi"]), type=lambda value: min(max(int(value), 1), 255)) diff --git a/Fill.py b/Fill.py index 2b920c9f..35c19d7d 100644 --- a/Fill.py +++ b/Fill.py @@ -252,6 +252,7 @@ def verify_spot_to_fill(location, item_to_place, max_exp_state, single_player_pl or (world.algorithm == 'vanilla_fill' and item_to_place.is_near_dungeon_item(world)))) \ or valid_dungeon_placement(item_to_place, location, world): return location + if item_to_place.smallkey or item_to_place.bigkey or item_to_place.prize: location.item = None location.event = False @@ -311,7 +312,9 @@ def valid_dungeon_placement(item, location, world): dungeon = check_dungeon if dungeon: layout = world.dungeon_layouts[location.player][dungeon.name] - if not is_dungeon_item(item, world) or item.player != location.player: + if item.event: + return True + elif not is_dungeon_item(item, world) or item.player != location.player: if item.prize and item.is_near_dungeon_item(world): return item.dungeon_object == dungeon and layout.free_items > 0 return layout.free_items > 0 diff --git a/ItemList.py b/ItemList.py index 7ccead35..0d2ebebf 100644 --- a/ItemList.py +++ b/ItemList.py @@ -372,6 +372,13 @@ def generate_itempool(world, player): world.push_precollected(ItemFactory(item, player)) if world.mode[player] == 'standard' and not world.state.has_blunt_weapon(player): + if world.customizer: + placements = world.customizer.get_placements() + if placements: + custom_uncle = placements.get(player, {}).get("Link's Uncle") + if custom_uncle: + placed_items["Link's Uncle"] = custom_uncle + if "Link's Uncle" not in placed_items: found_sword = False found_bow = False @@ -1669,12 +1676,15 @@ def fill_specific_items(world): item_to_place, event_flag = get_item_and_event_flag(item, world, player, dungeon_pool, prize_set, prize_pool) if item_to_place: - world.push_item(loc, item_to_place, False) - loc.locked = True - track_outside_keys(item_to_place, loc, world) - track_dungeon_items(item_to_place, loc, world) - loc.event = (event_flag or item_to_place.advancement - or item_to_place.bigkey or item_to_place.smallkey) + if not loc.item: + world.push_item(loc, item_to_place, False) + loc.locked = True + track_outside_keys(item_to_place, loc, world) + track_dungeon_items(item_to_place, loc, world) + loc.event = (event_flag or item_to_place.advancement + or item_to_place.bigkey or item_to_place.smallkey) + elif loc.item != item_to_place: + logging.getLogger('').warning("Failed to place item %s at location %s because it already contained %s", item_to_place, loc.name, loc.item) else: raise Exception(f'Did not find "{item}" in item pool to place at "{location}"') advanced_placements = world.customizer.get_advanced_placements() @@ -1802,7 +1812,7 @@ def shuffle_event_items(world, player): continue break else: - raise FillError(f'Unable to place followers: {", ".join(list(map(lambda d: d.hint_text, follower_locations)))}') + raise FillError(f'Unable to place followers: {", ".join(list(map(lambda f: f.name, pickup_items)))}') def get_item_and_event_flag(item, world, player, dungeon_pool, prize_set, prize_pool): diff --git a/Main.py b/Main.py index f2c19eff..b1aaa1c4 100644 --- a/Main.py +++ b/Main.py @@ -335,7 +335,7 @@ def main(args, seed=None, fish=None): for player in range(1, world.players+1): if world.shopsanity[player]: customize_shops(world, player) - if args.algorithm in ['balanced', 'equitable']: + if not args.skip_money_balance and args.algorithm in ['balanced', 'equitable']: balance_money_progression(world) ensure_good_items(world, True) @@ -572,7 +572,7 @@ def init_world(args, fish): world.collection_rate = args.collection_rate.copy() world.colorizepots = args.colorizepots.copy() world.aga_randomness = args.aga_randomness.copy() - world.money_balance = args.money_balance.copy() + world.money_balance = {player: int(args.money_balance[player]) for player in range(1, world.players + 1)} # custom settings - these haven't been promoted to full settings yet in_progress_settings = ['force_enemy'] @@ -1288,6 +1288,8 @@ def create_playthrough(world): for location in sphere: if world.goal[location.player] == 'completionist': continue # every location for that player is required + if location.item.type == "SmallKey": + continue # we remove the item at location and check if game is still beatable logging.getLogger('').debug('Checking if %s (Player %d) is required to beat the game.', location.item.name, location.item.player) old_item = location.item diff --git a/vanilla_placements.yaml b/vanilla_placements.yaml new file mode 100644 index 00000000..f3bba118 --- /dev/null +++ b/vanilla_placements.yaml @@ -0,0 +1,239 @@ +item_pool_adjust: + 1: + "Bottle (Random)": -4 + "Bottle": 4 + "Lamp": 1 + "Bombs (10)": -1 + "Bombs (3)": 1 +placements: + 1: + "Master Sword Pedestal": Master Sword + "Mushroom": Mushroom + "Ether Tablet": Ether + "Spectacle Rock": Piece of Heart + "Old Man": Magic Mirror + "Floating Island": Piece of Heart + "King Zora": Flippers + "Zora's Ledge": Piece of Heart + "Bottle Merchant": Bottle + "Maze Race": Piece of Heart + "Flute Spot": Ocarina + "Hobo": Bottle + "Desert Ledge": Piece of Heart + "Bombos Tablet": Bombos + "Lake Hylia Island": Piece of Heart + "Purple Chest": Bottle + "Sunken Treasure": Piece of Heart + "Bumper Cave Ledge": Piece of Heart + "Catfish": Quake + "Pyramid": Piece of Heart + "Digging Game": Piece of Heart + "Stumpy": Shovel + "Lost Woods Hideout": Piece of Heart + "Lumberjack Tree": Piece of Heart + "Spectacle Rock Cave": Piece of Heart + "Spiral Cave": Rupees (20) + "Mimic Cave": Piece of Heart + "Paradox Cave Lower - Far Left": Rupees (20) + "Paradox Cave Lower - Left": Rupees (20) + "Paradox Cave Lower - Right": Rupees (20) + "Paradox Cave Lower - Far Right": Rupees (20) + "Paradox Cave Lower - Middle": Rupees (20) + "Paradox Cave Upper - Left": Bombs (3) + "Paradox Cave Upper - Right": Arrows (10) + "Waterfall Fairy - Left": Red Boomerang + "Waterfall Fairy - Right": Red Shield + "Bonk Rock Cave": Piece of Heart + "Graveyard Cave": Piece of Heart + "King's Tomb": Cape + "Potion Shop": Magic Powder + "Kakariko Well - Left": Rupees (20) + "Kakariko Well - Middle": Rupees (20) + "Kakariko Well - Right": Rupees (20) + "Kakariko Well - Bottom": Bombs (3) + "Kakariko Well - Top": Piece of Heart + "Blind's Hideout - Left": Rupees (20) + "Blind's Hideout - Right": Rupees (20) + "Blind's Hideout - Far Left": Rupees (20) + "Blind's Hideout - Far Right": Rupees (20) + "Blind's Hideout - Top": Piece of Heart + "Chicken House": Arrows (10) + "Sick Kid": Bug Catching Net + "Kakariko Tavern": Bottle + "Link's Uncle": Fighter Sword + "Secret Passage": Blue Shield + "Sahasrahla's Hut - Left": Rupees (50) + "Sahasrahla's Hut - Middle": Bombs (3) + "Sahasrahla's Hut - Right": Rupees (50) + "Sahasrahla": Pegasus Boots + "Blacksmith": Tempered Sword + "Magic Bat": Magic Upgrade (1/2) + "Library": Book of Mudora + "Link's House": Lamp + "Checkerboard Cave": Piece of Heart + "Aginah's Cave": Piece of Heart + "Cave 45": Piece of Heart + "Mini Moldorm Cave - Far Left": Bombs (3) + "Mini Moldorm Cave - Left": Rupees (20) + "Mini Moldorm Cave - Right": Rupees (20) + "Mini Moldorm Cave - Far Right": Arrows (10) + "Mini Moldorm Cave - Generous Guy": Rupees (300) + "Ice Rod Cave": Ice Rod + "Floodgate Chest": Bombs (3) + "Spike Cave": Cane of Byrna + "Hookshot Cave - Bottom Right": Rupees (50) + "Hookshot Cave - Top Right": Rupees (50) + "Hookshot Cave - Top Left": Rupees (50) + "Hookshot Cave - Bottom Left": Rupees (50) + "Superbunny Cave - Top": Bombs (3) + "Superbunny Cave - Bottom": Rupees (50) + "Chest Game": Piece of Heart + "C-Shaped House": Rupees (300) + "Brewery": Rupees (300) + "Pyramid Fairy - Left": Golden Sword + "Pyramid Fairy - Right": Silver Arrows + "Peg Cave": Piece of Heart + "Mire Shed - Left": Piece of Heart + "Mire Shed - Right": Arrows (10) + "Hype Cave - Top": Rupees (20) + "Hype Cave - Middle Right": Rupees (20) + "Hype Cave - Middle Left": Rupees (20) + "Hype Cave - Bottom": Rupees (20) + "Hype Cave - Generous Guy": Rupees (300) + "Hyrule Castle - Map Chest": Map (Escape) + "Hyrule Castle - Boomerang Chest": Blue Boomerang + "Hyrule Castle - Zelda's Chest": Lamp + "Sewers - Dark Cross": Small Key (Escape) + "Sewers - Secret Room - Left": Bombs (3) + "Sewers - Secret Room - Middle": Rupees (300) + "Sewers - Secret Room - Right": Arrows (10) + "Sanctuary": Sanctuary Heart Container + "Eastern Palace - Cannonball Chest": Rupees (100) + "Eastern Palace - Map Chest": Map (Eastern Palace) + "Eastern Palace - Compass Chest": Compass (Eastern Palace) + "Eastern Palace - Big Chest": Bow + "Eastern Palace - Big Key Chest": Big Key (Eastern Palace) + "Eastern Palace - Boss": Boss Heart Container + "Eastern Palace - Prize": Green Pendant + "Desert Palace - Compass Chest": Compass (Desert Palace) + "Desert Palace - Big Key Chest": Big Key (Desert Palace) + "Desert Palace - Map Chest": Map (Desert Palace) + "Desert Palace - Torch": Small Key (Desert Palace) + "Desert Palace - Big Chest": Power Glove + "Desert Palace - Boss": Boss Heart Container + "Desert Palace - Prize": Blue Pendant + "Tower of Hera - Map Chest": Map (Tower of Hera) + "Tower of Hera - Basement Cage": Small Key (Tower of Hera) + "Tower of Hera - Big Key Chest": Big Key (Tower of Hera) + "Tower of Hera - Compass Chest": Compass (Tower of Hera) + "Tower of Hera - Big Chest": Moon Pearl + "Tower of Hera - Boss": Boss Heart Container + "Tower of Hera - Prize": Red Pendant + "Castle Tower - Room 03": Small Key (Agahnims Tower) + "Castle Tower - Dark Maze": Small Key (Agahnims Tower) + "Palace of Darkness - Shooter Room": Small Key (Palace of Darkness) + "Palace of Darkness - The Arena - Bridge": Small Key (Palace of Darkness) + "Palace of Darkness - The Arena - Ledge": Small Key (Palace of Darkness) + "Palace of Darkness - Map Chest": Map (Palace of Darkness) + "Palace of Darkness - Stalfos Basement": Small Key (Palace of Darkness) + "Palace of Darkness - Big Key Chest": Big Key (Palace of Darkness) + "Palace of Darkness - Dark Maze - Top": Rupees (20) + "Palace of Darkness - Dark Maze - Bottom": Rupees (5) + "Palace of Darkness - Big Chest": Hammer + "Palace of Darkness - Compass Chest": Compass (Palace of Darkness) + "Palace of Darkness - Dark Basement - Left": Single Arrow + "Palace of Darkness - Dark Basement - Right": Small Key (Palace of Darkness) + "Palace of Darkness - Harmless Hellway": Small Key (Palace of Darkness) + "Palace of Darkness - Boss": Boss Heart Container + "Palace of Darkness - Prize": Crystal 3 + "Thieves' Town - Map Chest": Map (Thieves Town) + "Thieves' Town - Ambush Chest": Rupees (20) + "Thieves' Town - Compass Chest": Compass (Thieves Town) + "Thieves' Town - Big Key Chest": Big Key (Thieves Town) + "Thieves' Town - Boss": Boss Heart Container + "Thieves' Town - Prize": Crystal 4 + "Thieves' Town - Attic": Bombs (3) + "Thieves' Town - Blind's Cell": Small Key (Thieves Town) + "Thieves' Town - Big Chest": Titans Mitts + "Skull Woods - Map Chest": Map (Skull Woods) + "Skull Woods - Big Chest": Fire Rod + "Skull Woods - Pinball Room": Small Key (Skull Woods) + "Skull Woods - Pot Prison": Small Key (Skull Woods) + "Skull Woods - Compass Chest": Compass (Skull Woods) + "Skull Woods - Big Key Chest": Big Key (Skull Woods) + "Skull Woods - Bridge Room": Small Key (Skull Woods) + "Skull Woods - Boss": Boss Heart Container + "Skull Woods - Prize": Crystal 7 + "Swamp Palace - Entrance": Small Key (Swamp Palace) + "Swamp Palace - Map Chest": Map (Swamp Palace) + "Swamp Palace - Big Chest": Hookshot + "Swamp Palace - Compass Chest": Compass (Swamp Palace) + "Swamp Palace - Big Key Chest": Big Key (Swamp Palace) + "Swamp Palace - West Chest": Rupees (20) + "Swamp Palace - Flooded Room - Left": Rupees (20) + "Swamp Palace - Flooded Room - Right": Rupees (20) + "Swamp Palace - Waterfall Room": Rupees (20) + "Swamp Palace - Boss": Boss Heart Container + "Swamp Palace - Prize": Crystal 1 + "Ice Palace - Compass Chest": Compass (Ice Palace) + "Ice Palace - Big Key Chest": Big Key (Ice Palace) + "Ice Palace - Spike Room": Small Key (Ice Palace) + "Ice Palace - Map Chest": Map (Ice Palace) + "Ice Palace - Freezor Chest": Bombs (3) + "Ice Palace - Iced T Room": Small Key (Ice Palace) + "Ice Palace - Big Chest": Blue Mail + "Ice Palace - Boss": Boss Heart Container + "Ice Palace - Prize": Crystal 5 + "Misery Mire - Main Lobby": Small Key (Misery Mire) + "Misery Mire - Big Chest": Cane of Somaria + "Misery Mire - Map Chest": Map (Misery Mire) + "Misery Mire - Spike Chest": Small Key (Misery Mire) + "Misery Mire - Bridge Chest": Small Key (Misery Mire) + "Misery Mire - Compass Chest": Compass (Misery Mire) + "Misery Mire - Big Key Chest": Big Key (Misery Mire) + "Misery Mire - Boss": Boss Heart Container + "Misery Mire - Prize": Crystal 6 + "Turtle Rock - Compass Chest": Compass (Turtle Rock) + "Turtle Rock - Roller Room - Left": Map (Turtle Rock) + "Turtle Rock - Roller Room - Right": Small Key (Turtle Rock) + "Turtle Rock - Chain Chomps": Small Key (Turtle Rock) + "Turtle Rock - Big Key Chest": Big Key (Turtle Rock) + "Turtle Rock - Big Chest": Mirror Shield + "Turtle Rock - Crystaroller Room": Small Key (Turtle Rock) + "Turtle Rock - Eye Bridge - Bottom Left": Small Key (Turtle Rock) + "Turtle Rock - Eye Bridge - Bottom Right": Rupees (20) + "Turtle Rock - Eye Bridge - Top Left": Rupees (5) + "Turtle Rock - Eye Bridge - Top Right": Rupee (1) + "Turtle Rock - Boss": Boss Heart Container + "Turtle Rock - Prize": Crystal 2 + "Ganons Tower - Bob's Torch": Small Key (Ganons Tower) + "Ganons Tower - Hope Room - Left": Arrows (10) + "Ganons Tower - Hope Room - Right": Bombs (3) + "Ganons Tower - Big Chest": Red Mail + "Ganons Tower - Bob's Chest": Arrows (10) + "Ganons Tower - Tile Room": Small Key (Ganons Tower) + "Ganons Tower - Compass Room - Top Left": Compass (Ganons Tower) + "Ganons Tower - Compass Room - Top Right": Rupee (1) + "Ganons Tower - Compass Room - Bottom Left": Rupees (20) + "Ganons Tower - Compass Room - Bottom Right": Arrows (10) + "Ganons Tower - Map Chest": Map (Ganons Tower) + "Ganons Tower - Firesnake Room": Small Key (Ganons Tower) + "Ganons Tower - DMs Room - Top Left": Bombs (3) + "Ganons Tower - DMs Room - Top Right": Arrows (10) + "Ganons Tower - DMs Room - Bottom Left": Bombs (3) + "Ganons Tower - DMs Room - Bottom Right": Rupees (20) + "Ganons Tower - Randomizer Room - Top Left": Arrows (10) + "Ganons Tower - Randomizer Room - Top Right": Arrows (10) + "Ganons Tower - Randomizer Room - Bottom Left": Bombs (3) + "Ganons Tower - Randomizer Room - Bottom Right": Bombs (3) + "Ganons Tower - Big Key Room - Left": Arrows (10) + "Ganons Tower - Big Key Room - Right": Bombs (3) + "Ganons Tower - Big Key Chest": Big Key (Ganons Tower) + "Ganons Tower - Mini Helmasaur Room - Left": Bombs (3) + "Ganons Tower - Mini Helmasaur Room - Right": Bombs (3) + "Ganons Tower - Pre-Moldorm Chest": Small Key (Ganons Tower) + "Ganons Tower - Validation Chest": Rupees (5) +medallions: + 1: + "Misery Mire": Ether + "Turtle Rock": Quake