diff --git a/BaseClasses.py b/BaseClasses.py index 5fa3b769..16a06005 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -16,7 +16,6 @@ import NetUtils import Options import Utils - class Group(TypedDict, total=False): name: str game: str @@ -760,169 +759,9 @@ class CollectionState(): found += self.prog_items[item_name, player] return found - def can_buy_unlimited(self, item: str, player: int) -> bool: - return any(shop.region.player == player and shop.has_unlimited(item) and shop.region.can_reach(self) for - shop in self.multiworld.shops) - - def can_buy(self, item: str, player: int) -> bool: - return any(shop.region.player == player and shop.has(item) and shop.region.can_reach(self) for - shop in self.multiworld.shops) - def item_count(self, item: str, player: int) -> int: return self.prog_items[item, player] - def has_triforce_pieces(self, count: int, player: int) -> bool: - return self.item_count('Triforce Piece', player) + self.item_count('Power Star', player) >= count - - def has_crystals(self, count: int, player: int) -> bool: - found: int = 0 - for crystalnumber in range(1, 8): - found += self.prog_items[f"Crystal {crystalnumber}", player] - if found >= count: - return True - return False - - def can_lift_rocks(self, player: int): - return self.has('Power Glove', player) or self.has('Titans Mitts', player) - - def bottle_count(self, player: int) -> int: - return min(self.multiworld.difficulty_requirements[player].progressive_bottle_limit, - self.count_group("Bottles", player)) - - def has_hearts(self, player: int, count: int) -> int: - # Warning: This only considers items that are marked as advancement items - return self.heart_count(player) >= count - - def heart_count(self, player: int) -> int: - # Warning: This only considers items that are marked as advancement items - diff = self.multiworld.difficulty_requirements[player] - return min(self.item_count('Boss Heart Container', player), diff.boss_heart_container_limit) \ - + self.item_count('Sanctuary Heart Container', player) \ - + min(self.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4 \ - + 3 # starting hearts - - def can_lift_heavy_rocks(self, player: int) -> bool: - return self.has('Titans Mitts', player) - - def can_extend_magic(self, player: int, smallmagic: int = 16, - fullrefill: bool = False): # This reflects the total magic Link has, not the total extra he has. - basemagic = 8 - if self.has('Magic Upgrade (1/4)', player): - basemagic = 32 - elif self.has('Magic Upgrade (1/2)', player): - basemagic = 16 - if self.can_buy_unlimited('Green Potion', player) or self.can_buy_unlimited('Blue Potion', player): - if self.multiworld.item_functionality[player] == 'hard' and not fullrefill: - basemagic = basemagic + int(basemagic * 0.5 * self.bottle_count(player)) - elif self.multiworld.item_functionality[player] == 'expert' and not fullrefill: - basemagic = basemagic + int(basemagic * 0.25 * self.bottle_count(player)) - else: - basemagic = basemagic + basemagic * self.bottle_count(player) - return basemagic >= smallmagic - - def can_kill_most_things(self, player: int, enemies: int = 5) -> bool: - return (self.has_melee_weapon(player) - or self.has('Cane of Somaria', player) - or (self.has('Cane of Byrna', player) and (enemies < 6 or self.can_extend_magic(player))) - or self.can_shoot_arrows(player) - or self.has('Fire Rod', player) - or (self.has('Bombs (10)', player) and enemies < 6)) - - def can_shoot_arrows(self, player: int) -> bool: - if self.multiworld.retro_bow[player]: - return (self.has('Bow', player) or self.has('Silver Bow', player)) and self.can_buy('Single Arrow', player) - return self.has('Bow', player) or self.has('Silver Bow', player) - - def can_get_good_bee(self, player: int) -> bool: - cave = self.multiworld.get_region('Good Bee Cave', player) - return ( - self.has_group("Bottles", player) and - self.has('Bug Catching Net', player) and - (self.has('Pegasus Boots', player) or (self.has_sword(player) and self.has('Quake', player))) and - cave.can_reach(self) and - self.is_not_bunny(cave, player) - ) - - def can_retrieve_tablet(self, player: int) -> bool: - return self.has('Book of Mudora', player) and (self.has_beam_sword(player) or - (self.multiworld.swordless[player] and - self.has("Hammer", player))) - - def has_sword(self, player: int) -> bool: - return self.has('Fighter Sword', player) \ - or self.has('Master Sword', player) \ - or self.has('Tempered Sword', player) \ - or self.has('Golden Sword', player) - - def has_beam_sword(self, player: int) -> bool: - return self.has('Master Sword', player) or self.has('Tempered Sword', player) or self.has('Golden Sword', - player) - - def has_melee_weapon(self, player: int) -> bool: - return self.has_sword(player) or self.has('Hammer', player) - - def has_fire_source(self, player: int) -> bool: - return self.has('Fire Rod', player) or self.has('Lamp', player) - - def can_melt_things(self, player: int) -> bool: - return self.has('Fire Rod', player) or \ - (self.has('Bombos', player) and - (self.multiworld.swordless[player] or - self.has_sword(player))) - - def can_avoid_lasers(self, player: int) -> bool: - return self.has('Mirror Shield', player) or self.has('Cane of Byrna', player) or self.has('Cape', player) - - def is_not_bunny(self, region: Region, player: int) -> bool: - if self.has('Moon Pearl', player): - return True - - return region.is_light_world if self.multiworld.mode[player] != 'inverted' else region.is_dark_world - - def can_reach_light_world(self, player: int) -> bool: - if True in [i.is_light_world for i in self.reachable_regions[player]]: - return True - return False - - def can_reach_dark_world(self, player: int) -> bool: - if True in [i.is_dark_world for i in self.reachable_regions[player]]: - return True - return False - - def has_misery_mire_medallion(self, player: int) -> bool: - return self.has(self.multiworld.required_medallions[player][0], player) - - def has_turtle_rock_medallion(self, player: int) -> bool: - return self.has(self.multiworld.required_medallions[player][1], player) - - def can_boots_clip_lw(self, player: int) -> bool: - if self.multiworld.mode[player] == 'inverted': - return self.has('Pegasus Boots', player) and self.has('Moon Pearl', player) - return self.has('Pegasus Boots', player) - - def can_boots_clip_dw(self, player: int) -> bool: - if self.multiworld.mode[player] != 'inverted': - return self.has('Pegasus Boots', player) and self.has('Moon Pearl', player) - return self.has('Pegasus Boots', player) - - def can_get_glitched_speed_lw(self, player: int) -> bool: - rules = [self.has('Pegasus Boots', player), any([self.has('Hookshot', player), self.has_sword(player)])] - if self.multiworld.mode[player] == 'inverted': - rules.append(self.has('Moon Pearl', player)) - return all(rules) - - def can_superbunny_mirror_with_sword(self, player: int) -> bool: - return self.has('Magic Mirror', player) and self.has_sword(player) - - def can_get_glitched_speed_dw(self, player: int) -> bool: - rules = [self.has('Pegasus Boots', player), any([self.has('Hookshot', player), self.has_sword(player)])] - if self.multiworld.mode[player] != 'inverted': - rules.append(self.has('Moon Pearl', player)) - return all(rules) - - def can_bomb_clip(self, region: Region, player: int) -> bool: - return self.is_not_bunny(region, player) and self.has('Pegasus Boots', player) - def collect(self, item: Item, event: bool = False, location: Optional[Location] = None) -> bool: if location: self.locations_checked.add(location) @@ -958,12 +797,6 @@ class Region: exits: List[Entrance] locations: List[Location] dungeon: Optional[Dungeon] = None - shop: Optional = None - - # LttP specific. TODO: move to a LttPRegion - # will be set after making connections. - is_light_world: bool = False - is_dark_world: bool = False def __init__(self, name: str, player: int, multiworld: MultiWorld, hint: Optional[str] = None): self.name = name diff --git a/worlds/alttp/Bosses.py b/worlds/alttp/Bosses.py index 5f915a33..51615ddc 100644 --- a/worlds/alttp/Bosses.py +++ b/worlds/alttp/Bosses.py @@ -4,7 +4,7 @@ from typing import Optional, Union, List, Tuple, Callable, Dict from BaseClasses import Boss from Fill import FillError from .Options import LTTPBosses as Bosses - +from .StateHelpers import can_shoot_arrows, can_extend_magic, can_get_good_bee, has_sword, has_beam_sword, has_melee_weapon, has_fire_source def BossFactory(boss: str, player: int) -> Optional[Boss]: if boss in boss_table: @@ -16,33 +16,33 @@ def BossFactory(boss: str, player: int) -> Optional[Boss]: def ArmosKnightsDefeatRule(state, player: int) -> bool: # Magic amounts are probably a bit overkill return ( - state.has_melee_weapon(player) or - state.can_shoot_arrows(player) or - (state.has('Cane of Somaria', player) and state.can_extend_magic(player, 10)) or - (state.has('Cane of Byrna', player) and state.can_extend_magic(player, 16)) or - (state.has('Ice Rod', player) and state.can_extend_magic(player, 32)) or - (state.has('Fire Rod', player) and state.can_extend_magic(player, 32)) or + has_melee_weapon(state, player) or + can_shoot_arrows(state, player) or + (state.has('Cane of Somaria', player) and can_extend_magic(state, player, 10)) or + (state.has('Cane of Byrna', player) and can_extend_magic(state, player, 16)) or + (state.has('Ice Rod', player) and can_extend_magic(state, player, 32)) or + (state.has('Fire Rod', player) and can_extend_magic(state, player, 32)) or state.has('Blue Boomerang', player) or state.has('Red Boomerang', player)) def LanmolasDefeatRule(state, player: int) -> bool: return ( - state.has_melee_weapon(player) or + has_melee_weapon(state, player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Cane of Somaria', player) or state.has('Cane of Byrna', player) or - state.can_shoot_arrows(player)) + can_shoot_arrows(state, player)) def MoldormDefeatRule(state, player: int) -> bool: - return state.has_melee_weapon(player) + return has_melee_weapon(state, player) def HelmasaurKingDefeatRule(state, player: int) -> bool: # TODO: technically possible with the hammer - return state.has_sword(player) or state.can_shoot_arrows(player) + return has_sword(state, player) or can_shoot_arrows(state, player) def ArrghusDefeatRule(state, player: int) -> bool: @@ -51,28 +51,28 @@ def ArrghusDefeatRule(state, player: int) -> bool: # TODO: ideally we would have a check for bow and silvers, which combined with the # hookshot is enough. This is not coded yet because the silvers that only work in pyramid feature # makes this complicated - if state.has_melee_weapon(player): + if has_melee_weapon(state, player): return True - return ((state.has('Fire Rod', player) and (state.can_shoot_arrows(player) or state.can_extend_magic(player, + return ((state.has('Fire Rod', player) and (can_shoot_arrows(state, player) or can_extend_magic(state, player, 12))) or # assuming mostly gitting two puff with one shot - (state.has('Ice Rod', player) and (state.can_shoot_arrows(player) or state.can_extend_magic(player, 16)))) + (state.has('Ice Rod', player) and (can_shoot_arrows(state, player) or can_extend_magic(state, player, 16)))) def MothulaDefeatRule(state, player: int) -> bool: return ( - state.has_melee_weapon(player) or - (state.has('Fire Rod', player) and state.can_extend_magic(player, 10)) or + has_melee_weapon(state, player) or + (state.has('Fire Rod', player) and can_extend_magic(state, player, 10)) or # TODO: Not sure how much (if any) extend magic is needed for these two, since they only apply # to non-vanilla locations, so are harder to test, so sticking with what VT has for now: - (state.has('Cane of Somaria', player) and state.can_extend_magic(player, 16)) or - (state.has('Cane of Byrna', player) and state.can_extend_magic(player, 16)) or - state.can_get_good_bee(player) + (state.has('Cane of Somaria', player) and can_extend_magic(state, player, 16)) or + (state.has('Cane of Byrna', player) and can_extend_magic(state, player, 16)) or + can_get_good_bee(state, player) ) def BlindDefeatRule(state, player: int) -> bool: - return state.has_melee_weapon(player) or state.has('Cane of Somaria', player) or state.has('Cane of Byrna', player) + return has_melee_weapon(state, player) or state.has('Cane of Somaria', player) or state.has('Cane of Byrna', player) def KholdstareDefeatRule(state, player: int) -> bool: @@ -81,56 +81,56 @@ def KholdstareDefeatRule(state, player: int) -> bool: state.has('Fire Rod', player) or ( state.has('Bombos', player) and - (state.has_sword(player) or state.multiworld.swordless[player]) + (has_sword(state, player) or state.multiworld.swordless[player]) ) ) and ( - state.has_melee_weapon(player) or - (state.has('Fire Rod', player) and state.can_extend_magic(player, 20)) or + has_melee_weapon(state, player) or + (state.has('Fire Rod', player) and can_extend_magic(state, player, 20)) or ( state.has('Fire Rod', player) and state.has('Bombos', player) and state.multiworld.swordless[player] and - state.can_extend_magic(player, 16) + can_extend_magic(state, player, 16) ) ) ) def VitreousDefeatRule(state, player: int) -> bool: - return state.can_shoot_arrows(player) or state.has_melee_weapon(player) + return can_shoot_arrows(state, player) or has_melee_weapon(state, player) def TrinexxDefeatRule(state, player: int) -> bool: if not (state.has('Fire Rod', player) and state.has('Ice Rod', player)): return False return state.has('Hammer', player) or state.has('Tempered Sword', player) or state.has('Golden Sword', player) or \ - (state.has('Master Sword', player) and state.can_extend_magic(player, 16)) or \ - (state.has_sword(player) and state.can_extend_magic(player, 32)) + (state.has('Master Sword', player) and can_extend_magic(state, player, 16)) or \ + (has_sword(state, player) and can_extend_magic(state, player, 32)) def AgahnimDefeatRule(state, player: int) -> bool: - return state.has_sword(player) or state.has('Hammer', player) or state.has('Bug Catching Net', player) + return has_sword(state, player) or state.has('Hammer', player) or state.has('Bug Catching Net', player) def GanonDefeatRule(state, player: int) -> bool: if state.multiworld.swordless[player]: return state.has('Hammer', player) and \ - state.has_fire_source(player) and \ + has_fire_source(state, player) and \ state.has('Silver Bow', player) and \ - state.can_shoot_arrows(player) + can_shoot_arrows(state, player) - can_hurt = state.has_beam_sword(player) - common = can_hurt and state.has_fire_source(player) + can_hurt = has_beam_sword(state, player) + common = can_hurt and has_fire_source(state, player) # silverless ganon may be needed in anything higher than no glitches if state.multiworld.logic[player] != 'noglitches': # need to light torch a sufficient amount of times return common and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or ( - state.has('Silver Bow', player) and state.can_shoot_arrows(player)) or - state.has('Lamp', player) or state.can_extend_magic(player, 12)) + state.has('Silver Bow', player) and can_shoot_arrows(state, player)) or + state.has('Lamp', player) or can_extend_magic(state, player, 12)) else: - return common and state.has('Silver Bow', player) and state.can_shoot_arrows(player) + return common and state.has('Silver Bow', player) and can_shoot_arrows(state, player) boss_table: Dict[str, Tuple[str, Optional[Callable]]] = { diff --git a/worlds/alttp/ItemPool.py b/worlds/alttp/ItemPool.py index f7326092..80028c3f 100644 --- a/worlds/alttp/ItemPool.py +++ b/worlds/alttp/ItemPool.py @@ -10,6 +10,7 @@ from worlds.alttp.EntranceShuffle import connect_entrance from Fill import FillError from worlds.alttp.Items import ItemFactory, GetBeemizerItem from worlds.alttp.Options import smallkey_shuffle, compass_shuffle, bigkey_shuffle, map_shuffle +from .StateHelpers import has_triforce_pieces, has_melee_weapon # This file sets the item pools for various modes. Timed modes and triforce hunt are enforced first, and then extra items are specified per mode to fill in the remaining space. # Some basic items that various modes require are placed here, including pendants and crystals. Medallion requirements for the two relevant entrances are also decided. @@ -286,7 +287,7 @@ def generate_itempool(world): region = world.get_region('Light World', player) loc = ALttPLocation(player, "Murahdahla", parent=region) - loc.access_rule = lambda state: state.has_triforce_pieces(state.multiworld.treasure_hunt_count[player], player) + loc.access_rule = lambda state: has_triforce_pieces(state, player) region.locations.append(loc) world.clear_location_cache() @@ -327,7 +328,7 @@ def generate_itempool(world): for item in precollected_items: world.push_precollected(ItemFactory(item, player)) - if world.mode[player] == 'standard' and not world.state.has_melee_weapon(player): + if world.mode[player] == 'standard' and not has_melee_weapon(world.state, player): if "Link's Uncle" not in placed_items: found_sword = False found_bow = False diff --git a/worlds/alttp/OverworldGlitchRules.py b/worlds/alttp/OverworldGlitchRules.py index 705db7e7..f6c3ec8d 100644 --- a/worlds/alttp/OverworldGlitchRules.py +++ b/worlds/alttp/OverworldGlitchRules.py @@ -4,6 +4,7 @@ Helper functions to deliver entrance/exit/region sets to OWG rules. from BaseClasses import Entrance +from .StateHelpers import can_lift_heavy_rocks, can_boots_clip_lw, can_boots_clip_dw, can_get_glitched_speed_dw def get_sword_required_superbunny_mirror_regions(): """ @@ -169,7 +170,7 @@ def get_boots_clip_exits_dw(inverted, player): yield ('Ganons Tower Ascent', 'Dark Death Mountain (West Bottom)', 'Dark Death Mountain (Top)') # This only gets you to the GT entrance yield ('Dark Death Mountain Glitched Bridge', 'Dark Death Mountain (West Bottom)', 'Dark Death Mountain (Top)') yield ('Turtle Rock (Top) Clip Spot', 'Dark Death Mountain (Top)', 'Turtle Rock (Top)') - yield ('Ice Palace Clip', 'South Dark World', 'Dark Lake Hylia Central Island', lambda state: state.can_boots_clip_dw(player) and state.has('Flippers', player)) + yield ('Ice Palace Clip', 'South Dark World', 'Dark Lake Hylia Central Island', lambda state: can_boots_clip_dw(state, player) and state.has('Flippers', player)) else: yield ('Dark Desert Teleporter Clip Spot', 'Dark Desert', 'Dark Desert Ledge') @@ -203,7 +204,7 @@ def get_mirror_offset_spots_lw(player): Mirror shenanigans placing a mirror portal with a broken camera """ yield ('Death Mountain Offset Mirror', 'Death Mountain', 'Light World') - yield ('Death Mountain Offset Mirror (Houlihan Exit)', 'Death Mountain', 'Hyrule Castle Ledge', lambda state: state.has('Magic Mirror', player) and state.can_boots_clip_dw(player) and state.has('Moon Pearl', player)) + yield ('Death Mountain Offset Mirror (Houlihan Exit)', 'Death Mountain', 'Hyrule Castle Ledge', lambda state: state.has('Magic Mirror', player) and can_boots_clip_dw(state, player) and state.has('Moon Pearl', player)) @@ -255,11 +256,11 @@ def overworld_glitch_connections(world, player): def overworld_glitches_rules(world, player): # Boots-accessible locations. - set_owg_connection_rules(player, world, get_boots_clip_exits_lw(world.mode[player] == 'inverted'), lambda state: state.can_boots_clip_lw(player)) - set_owg_connection_rules(player, world, get_boots_clip_exits_dw(world.mode[player] == 'inverted', player), lambda state: state.can_boots_clip_dw(player)) + set_owg_connection_rules(player, world, get_boots_clip_exits_lw(world.mode[player] == 'inverted'), lambda state: can_boots_clip_lw(state, player)) + set_owg_connection_rules(player, world, get_boots_clip_exits_dw(world.mode[player] == 'inverted', player), lambda state: can_boots_clip_dw(state, player)) # Glitched speed drops. - set_owg_connection_rules(player, world, get_glitched_speed_drops_dw(world.mode[player] == 'inverted'), lambda state: state.can_get_glitched_speed_dw(player)) + set_owg_connection_rules(player, world, get_glitched_speed_drops_dw(world.mode[player] == 'inverted'), lambda state: can_get_glitched_speed_dw(state, player)) # Dark Death Mountain Ledge Clip Spot also accessible with mirror. if world.mode[player] != 'inverted': add_alternate_rule(world.get_entrance('Dark Death Mountain Ledge Clip Spot', player), lambda state: state.has('Magic Mirror', player)) @@ -267,20 +268,20 @@ def overworld_glitches_rules(world, player): # Mirror clip spots. if world.mode[player] != 'inverted': set_owg_connection_rules(player, world, get_mirror_clip_spots_dw(), lambda state: state.has('Magic Mirror', player)) - set_owg_connection_rules(player, world, get_mirror_offset_spots_dw(), lambda state: state.has('Magic Mirror', player) and state.can_boots_clip_lw(player)) + set_owg_connection_rules(player, world, get_mirror_offset_spots_dw(), lambda state: state.has('Magic Mirror', player) and can_boots_clip_lw(state, player)) else: - set_owg_connection_rules(player, world, get_mirror_offset_spots_lw(player), lambda state: state.has('Magic Mirror', player) and state.can_boots_clip_dw(player)) + set_owg_connection_rules(player, world, get_mirror_offset_spots_lw(player), lambda state: state.has('Magic Mirror', player) and can_boots_clip_dw(state, player)) # Regions that require the boots and some other stuff. if world.mode[player] != 'inverted': - world.get_entrance('Turtle Rock Teleporter', player).access_rule = lambda state: (state.can_boots_clip_lw(player) or state.can_lift_heavy_rocks(player)) and state.has('Hammer', player) + world.get_entrance('Turtle Rock Teleporter', player).access_rule = lambda state: (can_boots_clip_lw(state, player) or can_lift_heavy_rocks(state, player)) and state.has('Hammer', player) add_alternate_rule(world.get_entrance('Waterfall of Wishing', player), lambda state: state.has('Moon Pearl', player) or state.has('Pegasus Boots', player)) else: add_alternate_rule(world.get_entrance('Waterfall of Wishing Cave', player), lambda state: state.has('Moon Pearl', player)) - world.get_entrance('Dark Desert Teleporter', player).access_rule = lambda state: (state.has('Flute', player) or state.has('Pegasus Boots', player)) and state.can_lift_heavy_rocks(player) - add_alternate_rule(world.get_entrance('Catfish Exit Rock', player), lambda state: state.can_boots_clip_dw(player)) - add_alternate_rule(world.get_entrance('East Dark World Broken Bridge Pass', player), lambda state: state.can_boots_clip_dw(player)) + world.get_entrance('Dark Desert Teleporter', player).access_rule = lambda state: (state.has('Flute', player) or state.has('Pegasus Boots', player)) and can_lift_heavy_rocks(state, player) + add_alternate_rule(world.get_entrance('Catfish Exit Rock', player), lambda state: can_boots_clip_dw(state, player)) + add_alternate_rule(world.get_entrance('East Dark World Broken Bridge Pass', player), lambda state: can_boots_clip_dw(state, player)) # Zora's Ledge via waterwalk setup. add_alternate_rule(world.get_location('Zora\'s Ledge', player), lambda state: state.has('Pegasus Boots', player)) diff --git a/worlds/alttp/Rules.py b/worlds/alttp/Rules.py index a31641d6..ff35c305 100644 --- a/worlds/alttp/Rules.py +++ b/worlds/alttp/Rules.py @@ -2,17 +2,24 @@ import collections import logging from typing import Iterator, Set -from worlds.alttp import OverworldGlitchRules -from BaseClasses import MultiWorld, Entrance -from worlds.alttp.Items import ItemFactory, progression_items, item_name_groups, item_table -from worlds.alttp.OverworldGlitchRules import overworld_glitches_rules, no_logic_rules -from worlds.alttp.Regions import location_table -from worlds.alttp.UnderworldGlitchRules import underworld_glitches_rules -from worlds.alttp.Bosses import GanonDefeatRule -from worlds.generic.Rules import set_rule, add_rule, forbid_item, add_item_rule, item_in_locations, \ - item_name -from worlds.alttp.Options import smallkey_shuffle -from worlds.alttp.Regions import LTTPRegionType +from BaseClasses import Entrance, MultiWorld +from worlds.generic.Rules import (add_item_rule, add_rule, forbid_item, + item_in_locations, item_name, set_rule) + +from . import OverworldGlitchRules +from .Bosses import GanonDefeatRule +from .Items import ItemFactory, item_name_groups, item_table, progression_items +from .Options import smallkey_shuffle +from .OverworldGlitchRules import no_logic_rules, overworld_glitches_rules +from .Regions import LTTPRegionType, location_table +from .StateHelpers import (can_extend_magic, can_kill_most_things, + can_lift_heavy_rocks, can_lift_rocks, + can_melt_things, can_retrieve_tablet, + can_shoot_arrows, has_beam_sword, has_crystals, + has_fire_source, has_hearts, + has_misery_mire_medallion, has_sword, has_turtle_rock_medallion, + has_triforce_pieces) +from .UnderworldGlitchRules import underworld_glitches_rules def set_rules(world): @@ -76,7 +83,7 @@ def set_rules(world): if world.goal[player] == 'bosses': # require all bosses to beat ganon - add_rule(world.get_location('Ganon', player), lambda state: state.can_reach('Master Sword Pedestal', 'Location', player) and state.has('Beat Agahnim 1', player) and state.has('Beat Agahnim 2', player) and state.has_crystals(7, player)) + add_rule(world.get_location('Ganon', player), lambda state: state.can_reach('Master Sword Pedestal', 'Location', player) and state.has('Beat Agahnim 1', player) and state.has('Beat Agahnim 2', player) and has_crystals(state, 7, player)) elif world.goal[player] == 'ganon': # require aga2 to beat ganon add_rule(world.get_location('Ganon', player), lambda state: state.has('Beat Agahnim 2', player)) @@ -101,7 +108,7 @@ def set_rules(world): set_trock_key_rules(world, player) - set_rule(ganons_tower, lambda state: state.has_crystals(state.multiworld.crystals_needed_for_gt[player], player)) + set_rule(ganons_tower, lambda state: has_crystals(state, state.multiworld.crystals_needed_for_gt[player], player)) if world.mode[player] != 'inverted' and world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']: add_rule(world.get_entrance('Ganons Tower', player), lambda state: state.multiworld.get_entrance('Ganons Tower Ascent', player).can_reach(state), 'or') @@ -199,7 +206,7 @@ def global_rules(world, player): set_rule(world.get_location('Dark Blacksmith Ruins', player), lambda state: state.has('Return Smith', player)) set_rule(world.get_location('Purple Chest', player), lambda state: state.has('Pick Up Purple Chest', player)) # Can S&Q with chest - set_rule(world.get_location('Ether Tablet', player), lambda state: state.can_retrieve_tablet(player)) + set_rule(world.get_location('Ether Tablet', player), lambda state: can_retrieve_tablet(state, player)) set_rule(world.get_location('Master Sword Pedestal', player), lambda state: state.has('Red Pendant', player) and state.has('Blue Pendant', player) and state.has('Green Pendant', player)) set_rule(world.get_location('Missing Smith', player), lambda state: state.has('Get Frog', player) and state.can_reach('Blacksmiths Hut', 'Region', player)) # Can't S&Q with smith @@ -212,11 +219,11 @@ def global_rules(world, player): set_rule(world.get_location('Spike Cave', player), lambda state: - state.has('Hammer', player) and state.can_lift_rocks(player) and - ((state.has('Cape', player) and state.can_extend_magic(player, 16, True)) or + state.has('Hammer', player) and can_lift_rocks(state, player) and + ((state.has('Cape', player) and can_extend_magic(state, player, 16, True)) or (state.has('Cane of Byrna', player) and - (state.can_extend_magic(player, 12, True) or - (state.multiworld.can_take_damage[player] and (state.has('Pegasus Boots', player) or state.has_hearts(player, 4)))))) + (can_extend_magic(state, player, 12, True) or + (state.multiworld.can_take_damage[player] and (state.has('Pegasus Boots', player) or has_hearts(state, player, 4)))))) ) set_rule(world.get_location('Hookshot Cave - Top Right', player), lambda state: state.has('Hookshot', player)) @@ -232,11 +239,11 @@ def global_rules(world, player): set_rule(world.get_entrance('Sewers Back Door', player), lambda state: state._lttp_has_key('Small Key (Hyrule Castle)', player)) set_rule(world.get_entrance('Agahnim 1', player), - lambda state: state.has_sword(player) and state._lttp_has_key('Small Key (Agahnims Tower)', player, 2)) + lambda state: has_sword(state, player) and state._lttp_has_key('Small Key (Agahnims Tower)', player, 2)) - set_rule(world.get_location('Castle Tower - Room 03', player), lambda state: state.can_kill_most_things(player, 8)) + set_rule(world.get_location('Castle Tower - Room 03', player), lambda state: can_kill_most_things(state, player, 8)) set_rule(world.get_location('Castle Tower - Dark Maze', player), - lambda state: state.can_kill_most_things(player, 8) and state._lttp_has_key('Small Key (Agahnims Tower)', + lambda state: can_kill_most_things(state, player, 8) and state._lttp_has_key('Small Key (Agahnims Tower)', player)) set_rule(world.get_location('Eastern Palace - Big Chest', player), @@ -248,14 +255,14 @@ def global_rules(world, player): set_rule(ep_prize, lambda state: state.has('Big Key (Eastern Palace)', player) and ep_prize.parent_region.dungeon.boss.can_defeat(state)) if not world.enemy_shuffle[player]: - add_rule(ep_boss, lambda state: state.can_shoot_arrows(player)) - add_rule(ep_prize, lambda state: state.can_shoot_arrows(player)) + add_rule(ep_boss, lambda state: can_shoot_arrows(state, player)) + add_rule(ep_prize, lambda state: can_shoot_arrows(state, player)) set_rule(world.get_location('Desert Palace - Big Chest', player), lambda state: state.has('Big Key (Desert Palace)', player)) set_rule(world.get_location('Desert Palace - Torch', player), lambda state: state.has('Pegasus Boots', player)) set_rule(world.get_entrance('Desert Palace East Wing', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player)) - set_rule(world.get_location('Desert Palace - Prize', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player) and state.has('Big Key (Desert Palace)', player) and state.has_fire_source(player) and state.multiworld.get_location('Desert Palace - Prize', player).parent_region.dungeon.boss.can_defeat(state)) - set_rule(world.get_location('Desert Palace - Boss', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player) and state.has('Big Key (Desert Palace)', player) and state.has_fire_source(player) and state.multiworld.get_location('Desert Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state)) + set_rule(world.get_location('Desert Palace - Prize', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player) and state.has('Big Key (Desert Palace)', player) and has_fire_source(state, player) and state.multiworld.get_location('Desert Palace - Prize', player).parent_region.dungeon.boss.can_defeat(state)) + set_rule(world.get_location('Desert Palace - Boss', player), lambda state: state._lttp_has_key('Small Key (Desert Palace)', player) and state.has('Big Key (Desert Palace)', player) and has_fire_source(state, player) and state.multiworld.get_location('Desert Palace - Boss', player).parent_region.dungeon.boss.can_defeat(state)) # logic patch to prevent placing a crystal in Desert that's required to reach the required keys if not (world.smallkey_shuffle[player] and world.bigkey_shuffle[player]): @@ -264,7 +271,7 @@ def global_rules(world, player): set_rule(world.get_entrance('Tower of Hera Small Key Door', player), lambda state: state._lttp_has_key('Small Key (Tower of Hera)', player) or item_name(state, 'Tower of Hera - Big Key Chest', player) == ('Small Key (Tower of Hera)', player)) set_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: state.has('Big Key (Tower of Hera)', player)) set_rule(world.get_location('Tower of Hera - Big Chest', player), lambda state: state.has('Big Key (Tower of Hera)', player)) - set_rule(world.get_location('Tower of Hera - Big Key Chest', player), lambda state: state.has_fire_source(player)) + set_rule(world.get_location('Tower of Hera - Big Key Chest', player), lambda state: has_fire_source(state, player)) if world.accessibility[player] != 'locations': set_always_allow(world.get_location('Tower of Hera - Big Key Chest', player), lambda state, item: item.name == 'Small Key (Tower of Hera)' and item.player == player) @@ -292,18 +299,18 @@ def global_rules(world, player): set_rule(world.get_location('Skull Woods - Big Chest', player), lambda state: state.has('Big Key (Skull Woods)', player) or item_name(state, 'Skull Woods - Big Chest', player) == ('Big Key (Skull Woods)', player)) if world.accessibility[player] != 'locations': set_always_allow(world.get_location('Skull Woods - Big Chest', player), lambda state, item: item.name == 'Big Key (Skull Woods)' and item.player == player) - set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player) and state.has_sword(player)) # sword required for curtain + set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player) and has_sword(state, player)) # sword required for curtain - set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.can_melt_things(player)) + set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: can_melt_things(state, player)) set_rule(world.get_location('Ice Palace - Big Chest', player), lambda state: state.has('Big Key (Ice Palace)', player)) - set_rule(world.get_entrance('Ice Palace (Kholdstare)', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player) and state.has('Big Key (Ice Palace)', player) and (state._lttp_has_key('Small Key (Ice Palace)', player, 2) or (state.has('Cane of Somaria', player) and state._lttp_has_key('Small Key (Ice Palace)', player, 1)))) + set_rule(world.get_entrance('Ice Palace (Kholdstare)', player), lambda state: can_lift_rocks(state, player) and state.has('Hammer', player) and state.has('Big Key (Ice Palace)', player) and (state._lttp_has_key('Small Key (Ice Palace)', player, 2) or (state.has('Cane of Somaria', player) and state._lttp_has_key('Small Key (Ice Palace)', player, 1)))) set_rule(world.get_entrance('Ice Palace (East)', player), lambda state: (state.has('Hookshot', player) or ( item_in_locations(state, 'Big Key (Ice Palace)', player, [('Ice Palace - Spike Room', player), ('Ice Palace - Big Key Chest', player), ('Ice Palace - Map Chest', player)]) and state._lttp_has_key('Small Key (Ice Palace)', player))) and (state.multiworld.can_take_damage[player] or state.has('Hookshot', player) or state.has('Cape', player) or state.has('Cane of Byrna', player))) - set_rule(world.get_entrance('Ice Palace (East Top)', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) + set_rule(world.get_entrance('Ice Palace (East Top)', player), lambda state: can_lift_rocks(state, player) and state.has('Hammer', player)) - set_rule(world.get_entrance('Misery Mire Entrance Gap', player), lambda state: (state.has('Pegasus Boots', player) or state.has('Hookshot', player)) and (state.has_sword(player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Hammer', player) or state.has('Cane of Somaria', player) or state.can_shoot_arrows(player))) # need to defeat wizzrobes, bombs don't work ... + set_rule(world.get_entrance('Misery Mire Entrance Gap', player), lambda state: (state.has('Pegasus Boots', player) or state.has('Hookshot', player)) and (has_sword(state, player) or state.has('Fire Rod', player) or state.has('Ice Rod', player) or state.has('Hammer', player) or state.has('Cane of Somaria', player) or can_shoot_arrows(state, player))) # need to defeat wizzrobes, bombs don't work ... set_rule(world.get_location('Misery Mire - Big Chest', player), lambda state: state.has('Big Key (Misery Mire)', player)) - set_rule(world.get_location('Misery Mire - Spike Chest', player), lambda state: (state.multiworld.can_take_damage[player] and state.has_hearts(player, 4)) or state.has('Cane of Byrna', player) or state.has('Cape', player)) + set_rule(world.get_location('Misery Mire - Spike Chest', player), lambda state: (state.multiworld.can_take_damage[player] and has_hearts(state, player, 4)) or state.has('Cane of Byrna', player) or state.has('Cape', player)) set_rule(world.get_entrance('Misery Mire Big Key Door', player), lambda state: state.has('Big Key (Misery Mire)', player)) # you can squander the free small key from the pot by opening the south door to the north west switch room, locking you out of accessing a color switch ... # big key gives backdoor access to that from the teleporter in the north west @@ -314,8 +321,8 @@ def global_rules(world, player): item_name(state, 'Misery Mire - Compass Chest', player) in [('Big Key (Misery Mire)', player)]) or ( item_name(state, 'Misery Mire - Big Key Chest', player) in [('Big Key (Misery Mire)', player)])) else state._lttp_has_key('Small Key (Misery Mire)', player, 3)) - set_rule(world.get_location('Misery Mire - Compass Chest', player), lambda state: state.has_fire_source(player)) - set_rule(world.get_location('Misery Mire - Big Key Chest', player), lambda state: state.has_fire_source(player)) + set_rule(world.get_location('Misery Mire - Compass Chest', player), lambda state: has_fire_source(state, player)) + set_rule(world.get_location('Misery Mire - Big Key Chest', player), lambda state: has_fire_source(state, player)) set_rule(world.get_entrance('Misery Mire (Vitreous)', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('Turtle Rock Entrance Gap', player), lambda state: state.has('Cane of Somaria', player)) @@ -335,10 +342,10 @@ def global_rules(world, player): set_rule(world.get_entrance('Turtle Rock (Trinexx)', player), lambda state: state._lttp_has_key('Small Key (Turtle Rock)', player, 4) and state.has('Big Key (Turtle Rock)', player) and state.has('Cane of Somaria', player)) if not world.enemy_shuffle[player]: - set_rule(world.get_entrance('Palace of Darkness Bonk Wall', player), lambda state: state.can_shoot_arrows(player)) + set_rule(world.get_entrance('Palace of Darkness Bonk Wall', player), lambda state: can_shoot_arrows(state, player)) set_rule(world.get_entrance('Palace of Darkness Hammer Peg Drop', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('Palace of Darkness Bridge Room', player), lambda state: state._lttp_has_key('Small Key (Palace of Darkness)', player, 1)) # If we can reach any other small key door, we already have back door access to this area - set_rule(world.get_entrance('Palace of Darkness Big Key Door', player), lambda state: state._lttp_has_key('Small Key (Palace of Darkness)', player, 6) and state.has('Big Key (Palace of Darkness)', player) and state.can_shoot_arrows(player) and state.has('Hammer', player)) + set_rule(world.get_entrance('Palace of Darkness Big Key Door', player), lambda state: state._lttp_has_key('Small Key (Palace of Darkness)', player, 6) and state.has('Big Key (Palace of Darkness)', player) and can_shoot_arrows(state, player) and state.has('Hammer', player)) set_rule(world.get_entrance('Palace of Darkness (North)', player), lambda state: state._lttp_has_key('Small Key (Palace of Darkness)', player, 4)) set_rule(world.get_location('Palace of Darkness - Big Chest', player), lambda state: state.has('Big Key (Palace of Darkness)', player)) @@ -399,9 +406,9 @@ def global_rules(world, player): lambda state: state.has('Big Key (Ganons Tower)', player)) else: set_rule(world.get_entrance('Ganons Tower Big Key Door', player), - lambda state: state.has('Big Key (Ganons Tower)', player) and state.can_shoot_arrows(player)) + lambda state: state.has('Big Key (Ganons Tower)', player) and can_shoot_arrows(state, player)) set_rule(world.get_entrance('Ganons Tower Torch Rooms', player), - lambda state: state.has_fire_source(player) and state.multiworld.get_entrance('Ganons Tower Torch Rooms', player).parent_region.dungeon.bosses['middle'].can_defeat(state)) + lambda state: has_fire_source(state, player) and state.multiworld.get_entrance('Ganons Tower Torch Rooms', player).parent_region.dungeon.bosses['middle'].can_defeat(state)) set_rule(world.get_location('Ganons Tower - Pre-Moldorm Chest', player), lambda state: state._lttp_has_key('Small Key (Ganons Tower)', player, 3)) set_rule(world.get_entrance('Ganons Tower Moldorm Door', player), @@ -412,12 +419,12 @@ def global_rules(world, player): ganon = world.get_location('Ganon', player) set_rule(ganon, lambda state: GanonDefeatRule(state, player)) if world.goal[player] in ['ganontriforcehunt', 'localganontriforcehunt']: - add_rule(ganon, lambda state: state.has_triforce_pieces(state.multiworld.treasure_hunt_count[player], player)) + add_rule(ganon, lambda state: has_triforce_pieces(state, player)) elif world.goal[player] == 'ganonpedestal': add_rule(world.get_location('Ganon', player), lambda state: state.can_reach('Master Sword Pedestal', 'Location', player)) else: - add_rule(ganon, lambda state: state.has_crystals(state.multiworld.crystals_needed_for_ganon[player], player)) - set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop + add_rule(ganon, lambda state: has_crystals(state, state.multiworld.crystals_needed_for_ganon[player], player)) + set_rule(world.get_entrance('Ganon Drop', player), lambda state: has_beam_sword(state, player)) # need to damage ganon to get tiles to drop set_rule(world.get_location('Flute Activation Spot', player), lambda state: state.has('Flute', player)) @@ -426,51 +433,51 @@ def default_rules(world, player): """Default world rules when world state is not inverted.""" # overworld requirements set_rule(world.get_entrance('Kings Grave', player), lambda state: state.has('Pegasus Boots', player)) - set_rule(world.get_entrance('Kings Grave Outer Rocks', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Kings Grave Inner Rocks', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Kings Grave Outer Rocks', player), lambda state: can_lift_heavy_rocks(state, player)) + set_rule(world.get_entrance('Kings Grave Inner Rocks', player), lambda state: can_lift_heavy_rocks(state, player)) set_rule(world.get_entrance('Kings Grave Mirror Spot', player), lambda state: state.has('Moon Pearl', player) and state.has('Magic Mirror', player)) # Caution: If king's grave is releaxed at all to account for reaching it via a two way cave's exit in insanity mode, then the bomb shop logic will need to be updated (that would involve create a small ledge-like Region for it) set_rule(world.get_entrance('Bonk Fairy (Light)', player), lambda state: state.has('Pegasus Boots', player)) set_rule(world.get_entrance('Lumberjack Tree Tree', player), lambda state: state.has('Pegasus Boots', player) and state.has('Beat Agahnim 1', player)) set_rule(world.get_entrance('Bonk Rock Cave', player), lambda state: state.has('Pegasus Boots', player)) set_rule(world.get_entrance('Desert Palace Stairs', player), lambda state: state.has('Book of Mudora', player)) - set_rule(world.get_entrance('Sanctuary Grave', player), lambda state: state.can_lift_rocks(player)) - set_rule(world.get_entrance('20 Rupee Cave', player), lambda state: state.can_lift_rocks(player)) - set_rule(world.get_entrance('50 Rupee Cave', player), lambda state: state.can_lift_rocks(player)) - set_rule(world.get_entrance('Death Mountain Entrance Rock', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Sanctuary Grave', player), lambda state: can_lift_rocks(state, player)) + set_rule(world.get_entrance('20 Rupee Cave', player), lambda state: can_lift_rocks(state, player)) + set_rule(world.get_entrance('50 Rupee Cave', player), lambda state: can_lift_rocks(state, player)) + set_rule(world.get_entrance('Death Mountain Entrance Rock', player), lambda state: can_lift_rocks(state, player)) set_rule(world.get_entrance('Bumper Cave Entrance Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Flute Spot 1', player), lambda state: state.has('Activated Flute', player)) - set_rule(world.get_entrance('Lake Hylia Central Island Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Dark Desert Teleporter', player), lambda state: state.has('Activated Flute', player) and state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('East Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has('Moon Pearl', player)) # bunny cannot use hammer - set_rule(world.get_entrance('South Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has('Moon Pearl', player)) # bunny cannot use hammer - set_rule(world.get_entrance('Kakariko Teleporter', player), lambda state: ((state.has('Hammer', player) and state.can_lift_rocks(player)) or state.can_lift_heavy_rocks(player)) and state.has('Moon Pearl', player)) # bunny cannot lift bushes + set_rule(world.get_entrance('Lake Hylia Central Island Teleporter', player), lambda state: can_lift_heavy_rocks(state, player)) + set_rule(world.get_entrance('Dark Desert Teleporter', player), lambda state: state.has('Activated Flute', player) and can_lift_heavy_rocks(state, player)) + set_rule(world.get_entrance('East Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and can_lift_rocks(state, player) and state.has('Moon Pearl', player)) # bunny cannot use hammer + set_rule(world.get_entrance('South Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and can_lift_rocks(state, player) and state.has('Moon Pearl', player)) # bunny cannot use hammer + set_rule(world.get_entrance('Kakariko Teleporter', player), lambda state: ((state.has('Hammer', player) and can_lift_rocks(state, player)) or can_lift_heavy_rocks(state, player)) and state.has('Moon Pearl', player)) # bunny cannot lift bushes set_rule(world.get_location('Flute Spot', player), lambda state: state.has('Shovel', player)) set_rule(world.get_entrance('Bat Cave Drop Ledge', player), lambda state: state.has('Hammer', player)) set_rule(world.get_location('Zora\'s Ledge', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Waterfall of Wishing', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_location('Frog', player), lambda state: state.can_lift_heavy_rocks(player)) # will get automatic moon pearl requirement + set_rule(world.get_location('Frog', player), lambda state: can_lift_heavy_rocks(state, player)) # will get automatic moon pearl requirement set_rule(world.get_location('Potion Shop', player), lambda state: state.has('Mushroom', player)) - set_rule(world.get_entrance('Desert Palace Entrance (North) Rocks', player), lambda state: state.can_lift_rocks(player)) - set_rule(world.get_entrance('Desert Ledge Return Rocks', player), lambda state: state.can_lift_rocks(player)) # should we decide to place something that is not a dungeon end up there at some point - set_rule(world.get_entrance('Checkerboard Cave', player), lambda state: state.can_lift_rocks(player)) - set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has_beam_sword(player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle + set_rule(world.get_entrance('Desert Palace Entrance (North) Rocks', player), lambda state: can_lift_rocks(state, player)) + set_rule(world.get_entrance('Desert Ledge Return Rocks', player), lambda state: can_lift_rocks(state, player)) # should we decide to place something that is not a dungeon end up there at some point + set_rule(world.get_entrance('Checkerboard Cave', player), lambda state: can_lift_rocks(state, player)) + set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or has_beam_sword(state, player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle set_rule(world.get_entrance('Top of Pyramid', player), lambda state: state.has('Beat Agahnim 1', player)) set_rule(world.get_entrance('Old Man Cave Exit (West)', player), lambda state: False) # drop cannot be climbed up set_rule(world.get_entrance('Broken Bridge (West)', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('Broken Bridge (East)', player), lambda state: state.has('Hookshot', player)) - set_rule(world.get_entrance('East Death Mountain Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Fairy Ascension Rocks', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('East Death Mountain Teleporter', player), lambda state: can_lift_heavy_rocks(state, player)) + set_rule(world.get_entrance('Fairy Ascension Rocks', player), lambda state: can_lift_heavy_rocks(state, player)) set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: state.has('Mirror', player)) # can erase block set_rule(world.get_entrance('Death Mountain (Top)', player), lambda state: state.has('Hammer', player)) - set_rule(world.get_entrance('Turtle Rock Teleporter', player), lambda state: state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) + set_rule(world.get_entrance('Turtle Rock Teleporter', player), lambda state: can_lift_heavy_rocks(state, player) and state.has('Hammer', player)) set_rule(world.get_entrance('East Death Mountain (Top)', player), lambda state: state.has('Hammer', player)) - set_rule(world.get_entrance('Catfish Exit Rock', player), lambda state: state.can_lift_rocks(player)) - set_rule(world.get_entrance('Catfish Entrance Rock', player), lambda state: state.can_lift_rocks(player)) - set_rule(world.get_entrance('Northeast Dark World Broken Bridge Pass', player), lambda state: state.has('Moon Pearl', player) and (state.can_lift_rocks(player) or state.has('Hammer', player) or state.has('Flippers', player))) - set_rule(world.get_entrance('East Dark World Broken Bridge Pass', player), lambda state: state.has('Moon Pearl', player) and (state.can_lift_rocks(player) or state.has('Hammer', player))) + set_rule(world.get_entrance('Catfish Exit Rock', player), lambda state: can_lift_rocks(state, player)) + set_rule(world.get_entrance('Catfish Entrance Rock', player), lambda state: can_lift_rocks(state, player)) + set_rule(world.get_entrance('Northeast Dark World Broken Bridge Pass', player), lambda state: state.has('Moon Pearl', player) and (can_lift_rocks(state, player) or state.has('Hammer', player) or state.has('Flippers', player))) + set_rule(world.get_entrance('East Dark World Broken Bridge Pass', player), lambda state: state.has('Moon Pearl', player) and (can_lift_rocks(state, player) or state.has('Hammer', player))) set_rule(world.get_entrance('South Dark World Bridge', player), lambda state: state.has('Hammer', player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('Bonk Fairy (Dark)', player), lambda state: state.has('Moon Pearl', player) and state.has('Pegasus Boots', player)) set_rule(world.get_entrance('West Dark World Gap', player), lambda state: state.has('Moon Pearl', player) and state.has('Hookshot', player)) @@ -478,12 +485,12 @@ def default_rules(world, player): set_rule(world.get_entrance('Hyrule Castle Ledge Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Hyrule Castle Main Gate', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: (state.has('Moon Pearl', player) and state.has('Flippers', player) or state.has('Magic Mirror', player))) # Overworld Bunny Revival - set_rule(world.get_location('Bombos Tablet', player), lambda state: state.can_retrieve_tablet(player)) + set_rule(world.get_location('Bombos Tablet', player), lambda state: can_retrieve_tablet(state, player)) set_rule(world.get_entrance('Dark Lake Hylia Drop (South)', player), lambda state: state.has('Moon Pearl', player) and state.has('Flippers', player)) # ToDo any fake flipper set up? set_rule(world.get_entrance('Dark Lake Hylia Ledge Fairy', player), lambda state: state.has('Moon Pearl', player)) # bomb required - set_rule(world.get_entrance('Dark Lake Hylia Ledge Spike Cave', player), lambda state: state.can_lift_rocks(player) and state.has('Moon Pearl', player)) + set_rule(world.get_entrance('Dark Lake Hylia Ledge Spike Cave', player), lambda state: can_lift_rocks(state, player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has('Moon Pearl', player)) - set_rule(world.get_entrance('Village of Outcasts Heavy Rock', player), lambda state: state.has('Moon Pearl', player) and state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Village of Outcasts Heavy Rock', player), lambda state: state.has('Moon Pearl', player) and can_lift_heavy_rocks(state, player)) set_rule(world.get_entrance('Hype Cave', player), lambda state: state.has('Moon Pearl', player)) # bomb required set_rule(world.get_entrance('Brewery', player), lambda state: state.has('Moon Pearl', player)) # bomb required set_rule(world.get_entrance('Thieves Town', player), lambda state: state.has('Moon Pearl', player)) # bunny cannot pull @@ -497,26 +504,26 @@ def default_rules(world, player): set_rule(world.get_entrance('Lake Hylia Central Island Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('East Dark World River Pier', player), lambda state: state.has('Moon Pearl', player) and state.has('Flippers', player)) set_rule(world.get_entrance('Graveyard Ledge Mirror Spot', player), lambda state: state.has('Moon Pearl', player) and state.has('Magic Mirror', player)) - set_rule(world.get_entrance('Bumper Cave Entrance Rock', player), lambda state: state.has('Moon Pearl', player) and state.can_lift_rocks(player)) + set_rule(world.get_entrance('Bumper Cave Entrance Rock', player), lambda state: state.has('Moon Pearl', player) and can_lift_rocks(state, player)) set_rule(world.get_entrance('Bumper Cave Ledge Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Bat Cave Drop Ledge Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Dark World Hammer Peg Cave', player), lambda state: state.has('Moon Pearl', player) and state.has('Hammer', player)) - set_rule(world.get_entrance('Village of Outcasts Eastern Rocks', player), lambda state: state.has('Moon Pearl', player) and state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Peg Area Rocks', player), lambda state: state.has('Moon Pearl', player) and state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Village of Outcasts Eastern Rocks', player), lambda state: state.has('Moon Pearl', player) and can_lift_heavy_rocks(state, player)) + set_rule(world.get_entrance('Peg Area Rocks', player), lambda state: state.has('Moon Pearl', player) and can_lift_heavy_rocks(state, player)) set_rule(world.get_entrance('Village of Outcasts Pegs', player), lambda state: state.has('Moon Pearl', player) and state.has('Hammer', player)) set_rule(world.get_entrance('Grassy Lawn Pegs', player), lambda state: state.has('Moon Pearl', player) and state.has('Hammer', player)) set_rule(world.get_entrance('Bumper Cave Exit (Top)', player), lambda state: state.has('Cape', player)) set_rule(world.get_entrance('Bumper Cave Exit (Bottom)', player), lambda state: state.has('Cape', player) or state.has('Hookshot', player)) set_rule(world.get_entrance('Skull Woods Final Section', player), lambda state: state.has('Fire Rod', player) and state.has('Moon Pearl', player)) # bunny cannot use fire rod - set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has('Moon Pearl', player) and state.has_sword(player) and state.has_misery_mire_medallion(player)) # sword required to cast magic (!) + set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has('Moon Pearl', player) and has_sword(state, player) and has_misery_mire_medallion(state, player)) # sword required to cast magic (!) set_rule(world.get_entrance('Desert Ledge (Northeast) Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Desert Ledge Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Desert Palace Stairs Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Desert Palace Entrance (North) Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Spectacle Rock Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) - set_rule(world.get_entrance('Hookshot Cave', player), lambda state: state.can_lift_rocks(player) and state.has('Moon Pearl', player)) + set_rule(world.get_entrance('Hookshot Cave', player), lambda state: can_lift_rocks(state, player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('East Death Mountain (Top) Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Mimic Cave Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) @@ -525,7 +532,7 @@ def default_rules(world, player): set_rule(world.get_entrance('Isolated Ledge Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Superbunny Cave Exit (Bottom)', player), lambda state: False) # Cannot get to bottom exit from top. Just exists for shuffling set_rule(world.get_entrance('Floating Island Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) - set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has('Moon Pearl', player) and state.has_sword(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword required to cast magic (!) + set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has('Moon Pearl', player) and has_sword(state, player) and has_turtle_rock_medallion(state, player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword required to cast magic (!) set_rule(world.get_entrance('Pyramid Hole', player), lambda state: state.has('Beat Agahnim 2', player) or world.open_pyramid[player].to_bool(world, player)) @@ -545,12 +552,12 @@ def inverted_rules(world, player): set_rule(world.get_entrance('Potion Shop Pier', player), lambda state: state.has('Flippers', player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('Light World Pier', player), lambda state: state.has('Flippers', player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('Kings Grave', player), lambda state: state.has('Pegasus Boots', player) and state.has('Moon Pearl', player)) - set_rule(world.get_entrance('Kings Grave Outer Rocks', player), lambda state: state.can_lift_heavy_rocks(player) and state.has('Moon Pearl', player)) - set_rule(world.get_entrance('Kings Grave Inner Rocks', player), lambda state: state.can_lift_heavy_rocks(player) and state.has('Moon Pearl', player)) + set_rule(world.get_entrance('Kings Grave Outer Rocks', player), lambda state: can_lift_heavy_rocks(state, player) and state.has('Moon Pearl', player)) + set_rule(world.get_entrance('Kings Grave Inner Rocks', player), lambda state: can_lift_heavy_rocks(state, player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('Potion Shop Inner Bushes', player), lambda state: state.has('Moon Pearl', player)) set_rule(world.get_entrance('Potion Shop Outer Bushes', player), lambda state: state.has('Moon Pearl', player)) - set_rule(world.get_entrance('Potion Shop Outer Rock', player), lambda state: state.can_lift_rocks(player) and state.has('Moon Pearl', player)) - set_rule(world.get_entrance('Potion Shop Inner Rock', player), lambda state: state.can_lift_rocks(player) and state.has('Moon Pearl', player)) + set_rule(world.get_entrance('Potion Shop Outer Rock', player), lambda state: can_lift_rocks(state, player) and state.has('Moon Pearl', player)) + set_rule(world.get_entrance('Potion Shop Inner Rock', player), lambda state: can_lift_rocks(state, player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('Graveyard Cave Inner Bushes', player), lambda state: state.has('Moon Pearl', player)) set_rule(world.get_entrance('Graveyard Cave Outer Bushes', player), lambda state: state.has('Moon Pearl', player)) set_rule(world.get_entrance('Secret Passage Inner Bushes', player), lambda state: state.has('Moon Pearl', player)) @@ -560,23 +567,23 @@ def inverted_rules(world, player): set_rule(world.get_entrance('Lumberjack Tree Tree', player), lambda state: state.has('Pegasus Boots', player) and state.has('Moon Pearl', player) and state.has('Beat Agahnim 1', player)) set_rule(world.get_entrance('Bonk Rock Cave', player), lambda state: state.has('Pegasus Boots', player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('Desert Palace Stairs', player), lambda state: state.has('Book of Mudora', player)) # bunny can use book - set_rule(world.get_entrance('Sanctuary Grave', player), lambda state: state.can_lift_rocks(player) and state.has('Moon Pearl', player)) - set_rule(world.get_entrance('20 Rupee Cave', player), lambda state: state.can_lift_rocks(player) and state.has('Moon Pearl', player)) - set_rule(world.get_entrance('50 Rupee Cave', player), lambda state: state.can_lift_rocks(player) and state.has('Moon Pearl', player)) - set_rule(world.get_entrance('Death Mountain Entrance Rock', player), lambda state: state.can_lift_rocks(player) and state.has('Moon Pearl', player)) + set_rule(world.get_entrance('Sanctuary Grave', player), lambda state: can_lift_rocks(state, player) and state.has('Moon Pearl', player)) + set_rule(world.get_entrance('20 Rupee Cave', player), lambda state: can_lift_rocks(state, player) and state.has('Moon Pearl', player)) + set_rule(world.get_entrance('50 Rupee Cave', player), lambda state: can_lift_rocks(state, player) and state.has('Moon Pearl', player)) + set_rule(world.get_entrance('Death Mountain Entrance Rock', player), lambda state: can_lift_rocks(state, player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('Bumper Cave Entrance Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Lake Hylia Central Island Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) - set_rule(world.get_entrance('Dark Lake Hylia Central Island Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Dark Desert Teleporter', player), lambda state: state.has('Activated Flute', player) and state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('East Dark World Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has('Moon Pearl', player)) # bunny cannot use hammer - set_rule(world.get_entrance('South Dark World Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has('Moon Pearl', player)) # bunny cannot use hammer - set_rule(world.get_entrance('West Dark World Teleporter', player), lambda state: ((state.has('Hammer', player) and state.can_lift_rocks(player)) or state.can_lift_heavy_rocks(player)) and state.has('Moon Pearl', player)) + set_rule(world.get_entrance('Dark Lake Hylia Central Island Teleporter', player), lambda state: can_lift_heavy_rocks(state, player)) + set_rule(world.get_entrance('Dark Desert Teleporter', player), lambda state: state.has('Activated Flute', player) and can_lift_heavy_rocks(state, player)) + set_rule(world.get_entrance('East Dark World Teleporter', player), lambda state: state.has('Hammer', player) and can_lift_rocks(state, player) and state.has('Moon Pearl', player)) # bunny cannot use hammer + set_rule(world.get_entrance('South Dark World Teleporter', player), lambda state: state.has('Hammer', player) and can_lift_rocks(state, player) and state.has('Moon Pearl', player)) # bunny cannot use hammer + set_rule(world.get_entrance('West Dark World Teleporter', player), lambda state: ((state.has('Hammer', player) and can_lift_rocks(state, player)) or can_lift_heavy_rocks(state, player)) and state.has('Moon Pearl', player)) set_rule(world.get_location('Flute Spot', player), lambda state: state.has('Shovel', player) and state.has('Moon Pearl', player)) set_rule(world.get_location('Zora\'s Ledge', player), lambda state: state.has('Flippers', player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('Waterfall of Wishing Cave', player), lambda state: state.has('Flippers', player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('Northeast Light World Return', player), lambda state: state.has('Flippers', player) and state.has('Moon Pearl', player)) - set_rule(world.get_location('Frog', player), lambda state: state.can_lift_heavy_rocks(player) and (state.has('Moon Pearl', player) or state.has('Beat Agahnim 1', player)) or (state.can_reach('Light World', 'Region', player) and state.has('Magic Mirror', player))) # Need LW access using Mirror or Portal + set_rule(world.get_location('Frog', player), lambda state: can_lift_heavy_rocks(state, player) and (state.has('Moon Pearl', player) or state.has('Beat Agahnim 1', player)) or (state.can_reach('Light World', 'Region', player) and state.has('Magic Mirror', player))) # Need LW access using Mirror or Portal set_rule(world.get_location('Missing Smith', player), lambda state: state.has('Get Frog', player) and state.can_reach('Blacksmiths Hut', 'Region', player)) # Can't S&Q with smith set_rule(world.get_location('Blacksmith', player), lambda state: state.has('Return Smith', player)) set_rule(world.get_location('Magic Bat', player), lambda state: state.has('Magic Powder', player) and state.has('Moon Pearl', player)) @@ -591,52 +598,52 @@ def inverted_rules(world, player): set_rule(world.get_entrance('North Fairy Cave Drop', player), lambda state: state.has('Moon Pearl', player)) set_rule(world.get_entrance('Lost Woods Hideout Drop', player), lambda state: state.has('Moon Pearl', player)) set_rule(world.get_location('Potion Shop', player), lambda state: state.has('Mushroom', player) and (state.can_reach('Potion Shop Area', 'Region', player))) # new inverted region, need pearl for bushes or access to potion shop door/waterfall fairy - set_rule(world.get_entrance('Desert Palace Entrance (North) Rocks', player), lambda state: state.can_lift_rocks(player) and state.has('Moon Pearl', player)) - set_rule(world.get_entrance('Desert Ledge Return Rocks', player), lambda state: state.can_lift_rocks(player) and state.has('Moon Pearl', player)) # should we decide to place something that is not a dungeon end up there at some point - set_rule(world.get_entrance('Checkerboard Cave', player), lambda state: state.can_lift_rocks(player) and state.has('Moon Pearl', player)) + set_rule(world.get_entrance('Desert Palace Entrance (North) Rocks', player), lambda state: can_lift_rocks(state, player) and state.has('Moon Pearl', player)) + set_rule(world.get_entrance('Desert Ledge Return Rocks', player), lambda state: can_lift_rocks(state, player) and state.has('Moon Pearl', player)) # should we decide to place something that is not a dungeon end up there at some point + set_rule(world.get_entrance('Checkerboard Cave', player), lambda state: can_lift_rocks(state, player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('Hyrule Castle Secret Entrance Drop', player), lambda state: state.has('Moon Pearl', player)) set_rule(world.get_entrance('Old Man Cave Exit (West)', player), lambda state: False) # drop cannot be climbed up set_rule(world.get_entrance('Broken Bridge (West)', player), lambda state: state.has('Hookshot', player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('Broken Bridge (East)', player), lambda state: state.has('Hookshot', player) and state.has('Moon Pearl', player)) - set_rule(world.get_entrance('Dark Death Mountain Teleporter (East Bottom)', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Fairy Ascension Rocks', player), lambda state: state.can_lift_heavy_rocks(player) and state.has('Moon Pearl', player)) + set_rule(world.get_entrance('Dark Death Mountain Teleporter (East Bottom)', player), lambda state: can_lift_heavy_rocks(state, player)) + set_rule(world.get_entrance('Fairy Ascension Rocks', player), lambda state: can_lift_heavy_rocks(state, player) and state.has('Moon Pearl', player)) set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: state.has('Mirror', player)) # can erase block set_rule(world.get_entrance('Death Mountain (Top)', player), lambda state: state.has('Hammer', player) and state.has('Moon Pearl', player)) - set_rule(world.get_entrance('Dark Death Mountain Teleporter (East)', player), lambda state: state.can_lift_heavy_rocks(player) and state.has('Hammer', player) and state.has('Moon Pearl', player)) # bunny cannot use hammer + set_rule(world.get_entrance('Dark Death Mountain Teleporter (East)', player), lambda state: can_lift_heavy_rocks(state, player) and state.has('Hammer', player) and state.has('Moon Pearl', player)) # bunny cannot use hammer set_rule(world.get_entrance('East Death Mountain (Top)', player), lambda state: state.has('Hammer', player) and state.has('Moon Pearl', player)) # bunny can not use hammer - set_rule(world.get_entrance('Catfish Entrance Rock', player), lambda state: state.can_lift_rocks(player)) - set_rule(world.get_entrance('Northeast Dark World Broken Bridge Pass', player), lambda state: ((state.can_lift_rocks(player) or state.has('Hammer', player)) or state.has('Flippers', player))) - set_rule(world.get_entrance('East Dark World Broken Bridge Pass', player), lambda state: (state.can_lift_rocks(player) or state.has('Hammer', player))) + set_rule(world.get_entrance('Catfish Entrance Rock', player), lambda state: can_lift_rocks(state, player)) + set_rule(world.get_entrance('Northeast Dark World Broken Bridge Pass', player), lambda state: ((can_lift_rocks(state, player) or state.has('Hammer', player)) or state.has('Flippers', player))) + set_rule(world.get_entrance('East Dark World Broken Bridge Pass', player), lambda state: (can_lift_rocks(state, player) or state.has('Hammer', player))) set_rule(world.get_entrance('South Dark World Bridge', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('Bonk Fairy (Dark)', player), lambda state: state.has('Pegasus Boots', player)) set_rule(world.get_entrance('West Dark World Gap', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_location('Bombos Tablet', player), lambda state: state.can_retrieve_tablet(player)) + set_rule(world.get_location('Bombos Tablet', player), lambda state: can_retrieve_tablet(state, player)) set_rule(world.get_entrance('Dark Lake Hylia Drop (South)', player), lambda state: state.has('Flippers', player)) # ToDo any fake flipper set up? set_rule(world.get_entrance('Dark Lake Hylia Ledge Pier', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('Dark Lake Hylia Ledge Spike Cave', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Dark Lake Hylia Ledge Spike Cave', player), lambda state: can_lift_rocks(state, player)) set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has('Flippers', player)) # Fake Flippers set_rule(world.get_entrance('Dark Lake Hylia Shallows', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('Village of Outcasts Heavy Rock', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Village of Outcasts Heavy Rock', player), lambda state: can_lift_heavy_rocks(state, player)) set_rule(world.get_entrance('East Dark World Bridge', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('Lake Hylia Central Island Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('East Dark World River Pier', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('Bumper Cave Entrance Rock', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Bumper Cave Entrance Rock', player), lambda state: can_lift_rocks(state, player)) set_rule(world.get_entrance('Bumper Cave Ledge Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Hammer Peg Area Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Dark World Hammer Peg Cave', player), lambda state: state.has('Hammer', player)) - set_rule(world.get_entrance('Village of Outcasts Eastern Rocks', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Peg Area Rocks', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Village of Outcasts Eastern Rocks', player), lambda state: can_lift_heavy_rocks(state, player)) + set_rule(world.get_entrance('Peg Area Rocks', player), lambda state: can_lift_heavy_rocks(state, player)) set_rule(world.get_entrance('Village of Outcasts Pegs', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('Grassy Lawn Pegs', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('Bumper Cave Exit (Top)', player), lambda state: state.has('Cape', player)) set_rule(world.get_entrance('Bumper Cave Exit (Bottom)', player), lambda state: state.has('Cape', player) or state.has('Hookshot', player)) set_rule(world.get_entrance('Skull Woods Final Section', player), lambda state: state.has('Fire Rod', player)) - set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player)) # sword required to cast magic (!) + set_rule(world.get_entrance('Misery Mire', player), lambda state: has_sword(state, player) and has_misery_mire_medallion(state, player)) # sword required to cast magic (!) - set_rule(world.get_entrance('Hookshot Cave', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Hookshot Cave', player), lambda state: can_lift_rocks(state, player)) set_rule(world.get_entrance('East Death Mountain Mirror Spot (Top)', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Death Mountain (Top) Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) @@ -646,7 +653,7 @@ def inverted_rules(world, player): set_rule(world.get_entrance('Dark Death Mountain Ledge Mirror Spot (West)', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Laser Bridge Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) set_rule(world.get_entrance('Floating Island Mirror Spot', player), lambda state: state.has('Magic Mirror', player)) - set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_sword(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword required to cast magic (!) + set_rule(world.get_entrance('Turtle Rock', player), lambda state: has_sword(state, player) and has_turtle_rock_medallion(state, player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword required to cast magic (!) # new inverted spots set_rule(world.get_entrance('Post Aga Teleporter', player), lambda state: state.has('Beat Agahnim 1', player)) @@ -687,7 +694,7 @@ def inverted_rules(world, player): def no_glitches_rules(world, player): """""" if world.mode[player] == 'inverted': - set_rule(world.get_entrance('Zoras River', player), lambda state: state.has('Moon Pearl', player) and (state.has('Flippers', player) or state.can_lift_rocks(player))) + set_rule(world.get_entrance('Zoras River', player), lambda state: state.has('Moon Pearl', player) and (state.has('Flippers', player) or can_lift_rocks(state, player))) set_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: state.has('Moon Pearl', player) and state.has('Flippers', player)) # can be fake flippered to set_rule(world.get_entrance('Lake Hylia Island Pier', player), lambda state: state.has('Moon Pearl', player) and state.has('Flippers', player)) # can be fake flippered to set_rule(world.get_entrance('Lake Hylia Warp', player), lambda state: state.has('Moon Pearl', player) and state.has('Flippers', player)) # can be fake flippered to @@ -698,7 +705,7 @@ def no_glitches_rules(world, player): set_rule(world.get_entrance('Dark Lake Hylia Ledge Drop', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('East Dark World Pier', player), lambda state: state.has('Flippers', player)) else: - set_rule(world.get_entrance('Zoras River', player), lambda state: state.has('Flippers', player) or state.can_lift_rocks(player)) + set_rule(world.get_entrance('Zoras River', player), lambda state: state.has('Flippers', player) or can_lift_rocks(state, player)) set_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: state.has('Flippers', player)) # can be fake flippered to set_rule(world.get_entrance('Hobo Bridge', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has('Moon Pearl', player) and state.has('Flippers', player)) @@ -820,19 +827,19 @@ def open_rules(world, player): def swordless_rules(world, player): - set_rule(world.get_entrance('Agahnim 1', player), lambda state: (state.has('Hammer', player) or state.has('Fire Rod', player) or state.can_shoot_arrows(player) or state.has('Cane of Somaria', player)) and state._lttp_has_key('Small Key (Agahnims Tower)', player, 2)) + set_rule(world.get_entrance('Agahnim 1', player), lambda state: (state.has('Hammer', player) or state.has('Fire Rod', player) or can_shoot_arrows(state, player) or state.has('Cane of Somaria', player)) and state._lttp_has_key('Small Key (Agahnims Tower)', player, 2)) set_rule(world.get_entrance('Skull Woods Torch Room', player), lambda state: state._lttp_has_key('Small Key (Skull Woods)', player, 3) and state.has('Fire Rod', player)) # no curtain set_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.has('Fire Rod', player) or state.has('Bombos', player)) #in swordless mode bombos pads are present in the relevant parts of ice palace set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop if world.mode[player] != 'inverted': set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle - set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has('Moon Pearl', player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!) - set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has('Moon Pearl', player) and state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!) + set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has('Moon Pearl', player) and has_turtle_rock_medallion(state, player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!) + set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has('Moon Pearl', player) and has_misery_mire_medallion(state, player)) # sword not required to use medallion for opening in swordless (!) else: # only need ddm access for aga tower in inverted - set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!) - set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!) + set_rule(world.get_entrance('Turtle Rock', player), lambda state: has_turtle_rock_medallion(state, player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!) + set_rule(world.get_entrance('Misery Mire', player), lambda state: has_misery_mire_medallion(state, player)) # sword not required to use medallion for opening in swordless (!) def add_connection(parent_name, target_name, entrance_name, world, player): @@ -1083,7 +1090,7 @@ def set_big_bomb_rules(world, player): # returning via the eastern and southern teleporters needs the same items, so we use the southern teleporter for out routing. # crossing preg bridge already requires hammer so we just add the gloves to the requirement def southern_teleporter(state): - return state.can_lift_rocks(player) and cross_peg_bridge(state) + return can_lift_rocks(state, player) and cross_peg_bridge(state) # the basic routes assume you can reach eastern light world with the bomb. # you can then use the southern teleporter, or (if you have beaten Aga1) the hyrule castle gate warp @@ -1110,13 +1117,13 @@ def set_big_bomb_rules(world, player): #1. Mirror and basic routes #2. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl # -> (Mitts and CPB) or (M and BR) - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and cross_peg_bridge(state)) or (state.has('Magic Mirror', player) and basic_routes(state))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (can_lift_heavy_rocks(state, player) and cross_peg_bridge(state)) or (state.has('Magic Mirror', player) and basic_routes(state))) elif bombshop_entrance.name == 'Bumper Cave (Bottom)': #1. Mirror and Lift rock and basic_routes #2. Mirror and Flute and basic routes (can make difference if accessed via insanity or w/ mirror from connector, and then via hyrule castle gate, because no gloves are needed in that case) #3. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl # -> (Mitts and CPB) or (((G or Flute) and M) and BR)) - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and cross_peg_bridge(state)) or (((state.can_lift_rocks(player) or state.has('Flute', player)) and state.has('Magic Mirror', player)) and basic_routes(state))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (can_lift_heavy_rocks(state, player) and cross_peg_bridge(state)) or (((can_lift_rocks(state, player) or state.has('Flute', player)) and state.has('Magic Mirror', player)) and basic_routes(state))) elif bombshop_entrance.name in Southern_DW_entrances: #1. Mirror and enter via gate: Need mirror and Aga1 #2. cross peg bridge: Need hammer and moon pearl @@ -1144,7 +1151,7 @@ def set_big_bomb_rules(world, player): elif bombshop_entrance.name == 'Fairy Ascension Cave (Bottom)': # Same as East_LW_DM_entrances except navigation without BR requires Mitts # -> Flute and ((M and Hookshot and Mitts) or BR) - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Activated Flute', player) and ((state.has('Magic Mirror', player) and state.has('Hookshot', player) and state.can_lift_heavy_rocks(player)) or basic_routes(state))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Activated Flute', player) and ((state.has('Magic Mirror', player) and state.has('Hookshot', player) and can_lift_heavy_rocks(state, player)) or basic_routes(state))) elif bombshop_entrance.name in Castle_ledge_entrances: # 1. mirror on pyramid to castle ledge, grab bomb, return through mirror spot: Needs mirror # 2. flute then basic routes @@ -1160,7 +1167,7 @@ def set_big_bomb_rules(world, player): # 1. Lift rock then basic_routes # 2. flute then basic_routes # -> (Flute or G) and BR - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Activated Flute', player) or state.can_lift_rocks(player)) and basic_routes(state)) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Activated Flute', player) or can_lift_rocks(state, player)) and basic_routes(state)) elif bombshop_entrance.name == 'Graveyard Cave': # 1. flute then basic routes # 2. (has west dark world access) use existing mirror spot (required Pearl), mirror again off ledge @@ -1176,13 +1183,13 @@ def set_big_bomb_rules(world, player): # 2. walk down by hammering peg: needs hammer and pearl # 3. mirror and basic routes # -> (P and (H or Gloves)) or (M and BR) - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Moon Pearl', player) and (state.has('Hammer', player) or state.can_lift_rocks(player))) or (state.has('Magic Mirror', player) and basic_routes(state))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Moon Pearl', player) and (state.has('Hammer', player) or can_lift_rocks(state, player))) or (state.has('Magic Mirror', player) and basic_routes(state))) elif bombshop_entrance.name == 'Kings Grave': # same as the Normal_LW_entrances case except that the pre-existing mirror is only possible if you have mitts # (because otherwise mirror was used to reach the grave, so would cancel a pre-existing mirror spot) # to account for insanity, must consider a way to escape without a cave for basic_routes # -> (M and Mitts) or ((Mitts or Flute or (M and P and West Dark World access)) and BR) - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and state.has('Magic Mirror', player)) or ((state.can_lift_heavy_rocks(player) or state.has('Activated Flute', player) or (state.can_reach('West Dark World', 'Region', player) and state.has('Moon Pearl', player) and state.has('Magic Mirror', player))) and basic_routes(state))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (can_lift_heavy_rocks(state, player) and state.has('Magic Mirror', player)) or ((can_lift_heavy_rocks(state, player) or state.has('Activated Flute', player) or (state.can_reach('West Dark World', 'Region', player) and state.has('Moon Pearl', player) and state.has('Magic Mirror', player))) and basic_routes(state))) elif bombshop_entrance.name == 'Waterfall of Wishing': # same as the Normal_LW_entrances case except in insanity it's possible you could be here without Flippers which # means you need an escape route of either Flippers or Flute @@ -1329,7 +1336,7 @@ def set_inverted_big_bomb_rules(world, player): elif bombshop_entrance.name in Northern_DW_entrances: # You can just fly with the Flute, you can take a long walk with Mitts and Hammer, # or you can leave a Mirror portal nearby and then walk to the castle to Mirror again. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Activated Flute', player) or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has('Magic Mirror', player) and state.can_reach('Light World', 'Region', player))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Activated Flute', player) or (can_lift_heavy_rocks(state, player) and state.has('Hammer', player)) or (state.has('Magic Mirror', player) and state.can_reach('Light World', 'Region', player))) elif bombshop_entrance.name in Southern_DW_entrances: # This is the same as north DW without the Mitts rock present. add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Hammer', player) or state.has('Activated Flute', player) or (state.has('Magic Mirror', player) and state.can_reach('Light World', 'Region', player))) @@ -1341,22 +1348,22 @@ def set_inverted_big_bomb_rules(world, player): add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Activated Flute', player) or (state.has('Magic Mirror', player) and state.can_reach('Light World', 'Region', player))) elif bombshop_entrance.name in LW_bush_entrances: # These entrances are behind bushes in LW so you need either Pearl or the tools to solve NDW bomb shop locations. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Magic Mirror', player) and (state.has('Activated Flute', player) or state.has('Moon Pearl', player) or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Magic Mirror', player) and (state.has('Activated Flute', player) or state.has('Moon Pearl', player) or (can_lift_heavy_rocks(state, player) and state.has('Hammer', player)))) elif bombshop_entrance.name == 'Village of Outcasts Shop': # This is mostly the same as NDW but the Mirror path requires the Pearl, or using the Hammer - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Activated Flute', player) or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has('Magic Mirror', player) and state.can_reach('Light World', 'Region', player) and (state.has('Moon Pearl', player) or state.has('Hammer', player)))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Activated Flute', player) or (can_lift_heavy_rocks(state, player) and state.has('Hammer', player)) or (state.has('Magic Mirror', player) and state.can_reach('Light World', 'Region', player) and (state.has('Moon Pearl', player) or state.has('Hammer', player)))) elif bombshop_entrance.name == 'Bumper Cave (Bottom)': # This is mostly the same as NDW but the Mirror path requires being able to lift a rock. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Activated Flute', player) or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has('Magic Mirror', player) and state.can_lift_rocks(player) and state.can_reach('Light World', 'Region', player))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Activated Flute', player) or (can_lift_heavy_rocks(state, player) and state.has('Hammer', player)) or (state.has('Magic Mirror', player) and can_lift_rocks(state, player) and state.can_reach('Light World', 'Region', player))) elif bombshop_entrance.name == 'Old Man Cave (West)': # The three paths back are Mirror and DW walk, Mirror and Flute, or LW walk and then Mirror. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Magic Mirror', player) and ((state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.can_lift_rocks(player) and state.has('Moon Pearl', player)) or state.has('Activated Flute', player))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Magic Mirror', player) and ((can_lift_heavy_rocks(state, player) and state.has('Hammer', player)) or (can_lift_rocks(state, player) and state.has('Moon Pearl', player)) or state.has('Activated Flute', player))) elif bombshop_entrance.name == 'Dark World Potion Shop': # You either need to Flute to 5 or cross the rock/hammer choice pass to the south. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Activated Flute', player) or state.has('Hammer', player) or state.can_lift_rocks(player)) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Activated Flute', player) or state.has('Hammer', player) or can_lift_rocks(state, player)) elif bombshop_entrance.name == 'Kings Grave': # Either lift the rock and walk to the castle to Mirror or Mirror immediately and Flute. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Activated Flute', player) or state.can_lift_heavy_rocks(player)) and state.has('Magic Mirror', player)) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Activated Flute', player) or can_lift_heavy_rocks(state, player)) and state.has('Magic Mirror', player)) elif bombshop_entrance.name == 'Waterfall of Wishing': # You absolutely must be able to swim to return it from here. add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Flippers', player) and state.has('Moon Pearl', player) and state.has('Magic Mirror', player)) @@ -1421,7 +1428,7 @@ def set_bunny_rules(world: MultiWorld, player: int, inverted: bool): if region.name == 'Swamp Palace (Entrance)': # Need to 0hp revive - not in logic return lambda state: state.has('Moon Pearl', player) if region.name == 'Tower of Hera (Bottom)': # Need to hit the crystal switch - return lambda state: state.has('Magic Mirror', player) and state.has_sword(player) or state.has('Moon Pearl', player) + return lambda state: state.has('Magic Mirror', player) and has_sword(state, player) or state.has('Moon Pearl', player) if region.name in OverworldGlitchRules.get_invalid_bunny_revival_dungeons(): return lambda state: state.has('Magic Mirror', player) or state.has('Moon Pearl', player) if region.type == LTTPRegionType.Dungeon: @@ -1459,7 +1466,7 @@ def set_bunny_rules(world: MultiWorld, player: int, inverted: bool): # For glitch rulesets, establish superbunny and revival rules. if world.logic[player] in ['minorglitches', 'owglitches', 'hybridglitches', 'nologic'] and entrance.name not in OverworldGlitchRules.get_invalid_bunny_revival_dungeons(): if region.name in OverworldGlitchRules.get_sword_required_superbunny_mirror_regions(): - possible_options.append(lambda state: path_to_access_rule(new_path, entrance) and state.has('Magic Mirror', player) and state.has_sword(player)) + possible_options.append(lambda state: path_to_access_rule(new_path, entrance) and state.has('Magic Mirror', player) and has_sword(state, player)) elif (region.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_regions() or location is not None and location.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_locations()): possible_options.append(lambda state: path_to_access_rule(new_path, entrance) and state.has('Magic Mirror', player) and state.has('Pegasus Boots', player)) diff --git a/worlds/alttp/StateHelpers.py b/worlds/alttp/StateHelpers.py new file mode 100644 index 00000000..33cea8fb --- /dev/null +++ b/worlds/alttp/StateHelpers.py @@ -0,0 +1,137 @@ +from .SubClasses import LTTPRegion +from BaseClasses import CollectionState + +def is_not_bunny(state: CollectionState, region: LTTPRegion, player: int) -> bool: + if state.has('Moon Pearl', player): + return True + + return region.is_light_world if state.multiworld.mode[player] != 'inverted' else region.is_dark_world + +def can_bomb_clip(state: CollectionState, region: LTTPRegion, player: int) -> bool: + return is_not_bunny(state, region, player) and state.has('Pegasus Boots', player) + +def can_buy_unlimited(state: CollectionState, item: str, player: int) -> bool: + return any(shop.region.player == player and shop.has_unlimited(item) and shop.region.can_reach(state) for + shop in state.multiworld.shops) + +def can_buy(state: CollectionState, item: str, player: int) -> bool: + return any(shop.region.player == player and shop.has(item) and shop.region.can_reach(state) for + shop in state.multiworld.shops) + +def can_shoot_arrows(state: CollectionState, player: int) -> bool: + if state.multiworld.retro_bow[player]: + return (state.has('Bow', player) or state.has('Silver Bow', player)) and can_buy(state, 'Single Arrow', player) + return state.has('Bow', player) or state.has('Silver Bow', player) + +def has_triforce_pieces(state: CollectionState, player: int) -> bool: + count = state.multiworld.treasure_hunt_count[player] + return state.item_count('Triforce Piece', player) + state.item_count('Power Star', player) >= count + +def has_crystals(state: CollectionState, count: int, player: int) -> bool: + found = state.count_group("Crystals", player) + return found >= count + +def can_lift_rocks(state: CollectionState, player: int): + return state.has('Power Glove', player) or state.has('Titans Mitts', player) + +def can_lift_heavy_rocks(state: CollectionState, player: int) -> bool: + return state.has('Titans Mitts', player) + +def bottle_count(state: CollectionState, player: int) -> int: + return min(state.multiworld.difficulty_requirements[player].progressive_bottle_limit, + state.count_group("Bottles", player)) + +def has_hearts(state: CollectionState, player: int, count: int) -> int: + # Warning: This only considers items that are marked as advancement items + return heart_count(state, player) >= count + +def heart_count(state: CollectionState, player: int) -> int: + # Warning: This only considers items that are marked as advancement items + diff = state.multiworld.difficulty_requirements[player] + return min(state.item_count('Boss Heart Container', player), diff.boss_heart_container_limit) \ + + state.item_count('Sanctuary Heart Container', player) \ + + min(state.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4 \ + + 3 # starting hearts + +def can_extend_magic(state: CollectionState, player: int, smallmagic: int = 16, + fullrefill: bool = False): # This reflects the total magic Link has, not the total extra he has. + basemagic = 8 + if state.has('Magic Upgrade (1/4)', player): + basemagic = 32 + elif state.has('Magic Upgrade (1/2)', player): + basemagic = 16 + if can_buy_unlimited(state, 'Green Potion', player) or can_buy_unlimited(state, 'Blue Potion', player): + if state.multiworld.item_functionality[player] == 'hard' and not fullrefill: + basemagic = basemagic + int(basemagic * 0.5 * bottle_count(state, player)) + elif state.multiworld.item_functionality[player] == 'expert' and not fullrefill: + basemagic = basemagic + int(basemagic * 0.25 * bottle_count(state, player)) + else: + basemagic = basemagic + basemagic * bottle_count(state, player) + return basemagic >= smallmagic + +def can_kill_most_things(state: CollectionState, player: int, enemies: int = 5) -> bool: + return (has_melee_weapon(state, player) + or state.has('Cane of Somaria', player) + or (state.has('Cane of Byrna', player) and (enemies < 6 or can_extend_magic(state, player))) + or can_shoot_arrows(state, player) + or state.has('Fire Rod', player) + or (state.has('Bombs (10)', player) and enemies < 6)) + +def can_get_good_bee(state: CollectionState, player: int) -> bool: + cave = state.multiworld.get_region('Good Bee Cave', player) + return ( + state.has_group("Bottles", player) and + state.has('Bug Catching Net', player) and + (state.has('Pegasus Boots', player) or (has_sword(state, player) and state.has('Quake', player))) and + cave.can_reach(state) and + is_not_bunny(state, cave, player) + ) + +def can_retrieve_tablet(state: CollectionState, player: int) -> bool: + return state.has('Book of Mudora', player) and (has_beam_sword(state, player) or + (state.multiworld.swordless[player] and + state.has("Hammer", player))) + +def has_sword(state: CollectionState, player: int) -> bool: + return state.has('Fighter Sword', player) \ + or state.has('Master Sword', player) \ + or state.has('Tempered Sword', player) \ + or state.has('Golden Sword', player) + +def has_beam_sword(state: CollectionState, player: int) -> bool: + return state.has('Master Sword', player) or state.has('Tempered Sword', player) or state.has('Golden Sword', + player) + +def has_melee_weapon(state: CollectionState, player: int) -> bool: + return has_sword(state, player) or state.has('Hammer', player) + +def has_fire_source(state: CollectionState, player: int) -> bool: + return state.has('Fire Rod', player) or state.has('Lamp', player) + +def can_melt_things(state: CollectionState, player: int) -> bool: + return state.has('Fire Rod', player) or \ + (state.has('Bombos', player) and + (state.multiworld.swordless[player] or + has_sword(state, player))) + +def has_misery_mire_medallion(state: CollectionState, player: int) -> bool: + return state.has(state.multiworld.required_medallions[player][0], player) + +def has_turtle_rock_medallion(state: CollectionState, player: int) -> bool: + return state.has(state.multiworld.required_medallions[player][1], player) + +def can_boots_clip_lw(state: CollectionState, player: int) -> bool: + if state.multiworld.mode[player] == 'inverted': + return state.has('Pegasus Boots', player) and state.has('Moon Pearl', player) + return state.has('Pegasus Boots', player) + +def can_boots_clip_dw(state: CollectionState, player: int) -> bool: + if state.multiworld.mode[player] != 'inverted': + return state.has('Pegasus Boots', player) and state.has('Moon Pearl', player) + return state.has('Pegasus Boots', player) + +def can_get_glitched_speed_dw(state: CollectionState, player: int) -> bool: + rules = [state.has('Pegasus Boots', player), any([state.has('Hookshot', player), has_sword(state, player)])] + if state.multiworld.mode[player] != 'inverted': + rules.append(state.has('Moon Pearl', player)) + return all(rules) \ No newline at end of file diff --git a/worlds/alttp/SubClasses.py b/worlds/alttp/SubClasses.py index 50f3ca47..5fc2aa0b 100644 --- a/worlds/alttp/SubClasses.py +++ b/worlds/alttp/SubClasses.py @@ -4,7 +4,6 @@ from enum import IntEnum from BaseClasses import Location, Item, ItemClassification, Region, MultiWorld - class ALttPLocation(Location): game: str = "A Link to the Past" crystal: bool @@ -81,6 +80,12 @@ class LTTPRegionType(IntEnum): class LTTPRegion(Region): type: LTTPRegionType + # will be set after making connections. + is_light_world: bool = False + is_dark_world: bool = False + + shop: Optional = None + def __init__(self, name: str, type_: LTTPRegionType, hint: str, player: int, multiworld: MultiWorld): super().__init__(name, player, multiworld, hint) self.type = type_ diff --git a/worlds/alttp/UnderworldGlitchRules.py b/worlds/alttp/UnderworldGlitchRules.py index f7e77367..f3d78e36 100644 --- a/worlds/alttp/UnderworldGlitchRules.py +++ b/worlds/alttp/UnderworldGlitchRules.py @@ -1,6 +1,8 @@ from BaseClasses import Entrance +from .SubClasses import LTTPRegion from worlds.generic.Rules import set_rule, add_rule +from .StateHelpers import can_bomb_clip, has_sword, has_beam_sword, has_fire_source, can_melt_things, has_misery_mire_medallion # We actually need the logic to properly "mark" these regions as Light or Dark world. # Therefore we need to make these connections during the normal link_entrances stage, rather than during set_rules. @@ -46,9 +48,9 @@ def dungeon_reentry_rules(world, player, clip: Entrance, dungeon_region: str, du if dungeon_entrance.name == 'Skull Woods Final Section': set_rule(clip, lambda state: False) # entrance doesn't exist until you fire rod it from the other side elif dungeon_entrance.name == 'Misery Mire': - add_rule(clip, lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player)) # open the dungeon + add_rule(clip, lambda state: has_sword(state, player) and has_misery_mire_medallion(state, player)) # open the dungeon elif dungeon_entrance.name == 'Agahnims Tower': - add_rule(clip, lambda state: state.has('Cape', player) or state.has_beam_sword(player) or state.has('Beat Agahnim 1', player)) # kill/bypass barrier + add_rule(clip, lambda state: state.has('Cape', player) or has_beam_sword(state, player) or state.has('Beat Agahnim 1', player)) # kill/bypass barrier # Then we set a restriction on exiting the dungeon, so you can't leave unless you got in normally. add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state)) elif not fix_fake_worlds: # full, dungeonsfull; fixed dungeon exits, but no fake worlds fix @@ -66,21 +68,21 @@ def underworld_glitches_rules(world, player): # Ice Palace Entrance Clip # This is the easiest one since it's a simple internal clip. Just need to also add melting to freezor chest since it's otherwise assumed. - add_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: state.can_bomb_clip(world.get_region('Ice Palace (Entrance)', player), player), combine='or') - add_rule(world.get_location('Ice Palace - Freezor Chest', player), lambda state: state.can_melt_things(player)) + add_rule(world.get_entrance('Ice Palace Entrance Room', player), lambda state: can_bomb_clip(state, world.get_region('Ice Palace (Entrance)', player), player), combine='or') + add_rule(world.get_location('Ice Palace - Freezor Chest', player), lambda state: can_melt_things(state, player)) # Kiki Skip kikiskip = world.get_entrance('Kiki Skip', player) - set_rule(kikiskip, lambda state: state.can_bomb_clip(kikiskip.parent_region, player)) + set_rule(kikiskip, lambda state: can_bomb_clip(state, kikiskip.parent_region, player)) dungeon_reentry_rules(world, player, kikiskip, 'Palace of Darkness (Entrance)', 'Palace of Darkness Exit') # Mire -> Hera -> Swamp # Using mire keys on other dungeon doors mire = world.get_region('Misery Mire (West)', player) - mire_clip = lambda state: state.can_reach('Misery Mire (West)', 'Region', player) and state.can_bomb_clip(mire, player) and state.has_fire_source(player) - hera_clip = lambda state: state.can_reach('Tower of Hera (Top)', 'Region', player) and state.can_bomb_clip(world.get_region('Tower of Hera (Top)', player), player) + mire_clip = lambda state: state.can_reach('Misery Mire (West)', 'Region', player) and can_bomb_clip(state, mire, player) and has_fire_source(state, player) + hera_clip = lambda state: state.can_reach('Tower of Hera (Top)', 'Region', player) and can_bomb_clip(state, world.get_region('Tower of Hera (Top)', player), player) add_rule(world.get_entrance('Tower of Hera Big Key Door', player), lambda state: mire_clip(state) and state.has('Big Key (Misery Mire)', player), combine='or') add_rule(world.get_entrance('Swamp Palace Small Key Door', player), lambda state: mire_clip(state), combine='or') add_rule(world.get_entrance('Swamp Palace (Center)', player), lambda state: mire_clip(state) or hera_clip(state), combine='or') diff --git a/worlds/alttp/__init__.py b/worlds/alttp/__init__.py index 5f33b152..4121dbad 100644 --- a/worlds/alttp/__init__.py +++ b/worlds/alttp/__init__.py @@ -23,6 +23,7 @@ from .Rules import set_rules from .Shops import create_shops, ShopSlotFill, ShopType, price_rate_display, price_type_display_name from .SubClasses import ALttPItem, LTTPRegionType from worlds.AutoWorld import World, WebWorld, LogicMixin +from .StateHelpers import can_buy_unlimited lttp_logger = logging.getLogger("A Link to the Past") @@ -673,5 +674,5 @@ class ALttPLogic(LogicMixin): if self.multiworld.logic[player] == 'nologic': return True if self.multiworld.smallkey_shuffle[player] == smallkey_shuffle.option_universal: - return self.can_buy_unlimited('Small Key (Universal)', player) + return can_buy_unlimited(self, 'Small Key (Universal)', player) return self.prog_items[item, player] >= count