From 4c33bf72ffc436adcf815f9fdf19f74a24af5079 Mon Sep 17 00:00:00 2001 From: AmazingAmpharos Date: Wed, 14 Mar 2018 13:28:52 -0500 Subject: [PATCH 1/6] Smith softlock fix Now correctly requires Moon Pearl. --- Rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rules.py b/Rules.py index b96c7fc3..b0cd6424 100644 --- a/Rules.py +++ b/Rules.py @@ -114,7 +114,7 @@ def global_rules(world): set_rule(world.get_location('Zora\'s Ledge'), lambda state: state.has('Flippers')) set_rule(world.get_entrance('Waterfall of Wishing'), lambda state: state.has('Flippers')) # can be fake flippered into, but is in weird state inside that might prevent you from doing things. Can be improved in future Todo - set_rule(world.get_location('Blacksmith'), lambda state: state.can_lift_heavy_rocks() and state.can_reach('West Dark World')) # Can S&Q with smith + set_rule(world.get_location('Blacksmith'), lambda state: state.can_lift_heavy_rocks() and state.can_reach('West Dark World') and state.has_Pearl()) # Can S&Q with smith set_rule(world.get_location('Magic Bat'), lambda state: state.has('Magic Powder')) set_rule(world.get_location('Sick Kid'), lambda state: state.has_bottle()) set_rule(world.get_location('Library'), lambda state: state.has_Boots()) From 7ebe4f9d93a100d82c41663497a0e3663690abb5 Mon Sep 17 00:00:00 2001 From: AmazingAmpharos Date: Wed, 14 Mar 2018 13:30:11 -0500 Subject: [PATCH 2/6] High difficulty updates Should implement the myriad changes v29 will have to Hard+. This assumes silvers do not exist in Expert which is ambiguous. --- ItemList.py | 66 ++++++++++++++++++++++++++--------------------------- Rom.py | 30 +++++++++++++++++++----- 2 files changed, 56 insertions(+), 40 deletions(-) diff --git a/ItemList.py b/ItemList.py index f59ec964..37643865 100644 --- a/ItemList.py +++ b/ItemList.py @@ -10,15 +10,15 @@ from Dungeons import get_dungeon_item_pool #Some basic items that various modes require are placed here, including pendants and crystals. Medallion requirements for the two relevant entrances are also decided. alwaysitems = ['Bombos', 'Book of Mudora', 'Bow', 'Cane of Somaria', 'Ether', 'Fire Rod', 'Flippers', 'Ocarina', 'Hammer', 'Hookshot', 'Ice Rod', 'Lamp', - 'Cape', 'Magic Powder', 'Mushroom', 'Pegasus Boots', 'Quake', 'Shovel', 'Bug Catching Net', 'Cane of Byrna'] + 'Cape', 'Magic Powder', 'Mushroom', 'Pegasus Boots', 'Quake', 'Shovel', 'Bug Catching Net', 'Cane of Byrna', 'Blue Boomerang', 'Red Boomerang'] progressivegloves = ['Progressive Glove'] * 2 basicgloves = ['Power Glove', 'Titans Mitts'] normalbottles = ['Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Bottle (Blue Potion)', 'Bottle (Fairy)', 'Bottle (Bee)', 'Bottle (Good Bee)'] hardbottles = ['Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Bottle (Blue Potion)', 'Bottle (Bee)', 'Bottle (Good Bee)'] -normalbaseitems = (['Blue Boomerang', 'Red Boomerang', 'Silver Arrows', 'Magic Upgrade (1/2)'] + ['Rupees (300)'] * 4 + - ['Single Arrow', 'Sanctuary Heart Container', 'Arrow Upgrade (+10)', 'Bomb Upgrade (+10)'] + ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 24) +normalbaseitems = (['Silver Arrows', 'Magic Upgrade (1/2)', 'Single Arrow', 'Sanctuary Heart Container', 'Arrow Upgrade (+10)', 'Bomb Upgrade (+10)'] + + ['Rupees (300)'] * 4 + ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 24) normalfirst15extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Arrow Upgrade (+5)'] * 6 + ['Bomb Upgrade (+5)'] * 6 normalsecond15extra = ['Bombs (3)'] * 10 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupee (1)'] normalthird10extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 3 + ['Arrows (10)', 'Rupee (1)', 'Rupees (5)'] @@ -26,8 +26,8 @@ normalfourth5extra = ['Arrows (10)'] * 2 + ['Rupees (20)'] * 2 + ['Rupees (5)'] normalfinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2 -easybaseitems = (['Blue Boomerang', 'Red Boomerang', 'Sanctuary Heart Container'] + ['Rupees (300)'] * 4 + ['Magic Upgrade (1/2)'] * 2 + ['Lamp'] * 2 + - ['Silver Arrows'] * 2 + ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 12) +easybaseitems = (['Sanctuary Heart Container'] + ['Rupees (300)'] * 4 + ['Magic Upgrade (1/2)'] * 2 + ['Lamp'] * 2 + ['Silver Arrows'] * 2 + + ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 12) easyextra = ['Piece of Heart'] * 12 + ['Rupees (300)'] easylimitedextra = ['Boss Heart Container'] * 3 # collapsing down the 12 pieces of heart easyfirst15extra = ['Rupees (100)', 'Arrow Upgrade (+10)', 'Bomb Upgrade (+10)'] + ['Arrow Upgrade (+5)'] * 6 + ['Bomb Upgrade (+5)'] * 6 @@ -36,29 +36,27 @@ easythird5extra = ['Rupees (50)'] * 2 + ['Bombs (3)'] * 2 + ['Arrows (10)'] easyfinal25extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 14 + ['Rupee (1)'] + ['Arrows (10)'] * 4 + ['Rupees (5)'] * 2 easytimedotherextra = ['Red Clock'] * 5 -hardbaseitems = (['Silver Arrows', 'Single Arrow', 'Single Bomb'] + ['Rupees (300)'] + ['Rupees (100)'] * 3 + ['Rupees (50)'] * 5 + ['Bombs (3)'] * 5 + - ['Boss Heart Container'] * 5 + ['Piece of Heart'] * 24) -hardfirst20extra = ['Single Bomb'] * 7 + ['Rupees (5)'] * 8 + ['Rupee (1)'] * 2 + ['Rupees (20)'] * 2 + ['Arrows (10)'] -hardsecond10extra = ['Rupees (5)'] * 7 + ['Rupee (1)'] * 3 -hardthird10extra = ['Arrows (10)'] * 4 + ['Rupees (20)'] * 3 + ['Single Bomb'] * 3 -hardfourth10extra = ['Rupees (5)'] * 3 + ['Single Arrow'] * 5 + ['Single Bomb'] * 2 -hardfinal20extra = ['Single Bomb'] * 4 + ['Rupees (5)'] * 2 + ['Single Arrow'] * 14 +hardbaseitems = ['Silver Arrows', 'Single Arrow'] + ['Rupees (300)'] * 4 + ['Boss Heart Container'] * 6 + ['Piece of Heart'] * 20 + ['Rupees (5)'] * 7 + ['Bombs (3)'] * 5 +hardfirst20extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Bombs (3)'] * 5 + ['Rupees (5)'] * 10 + ['Arrows (10)', 'Rupee (1)'] +hardsecond10extra = ['Rupees (5)'] * 5 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupee (1)'] +hardthird10extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 3 + ['Rupees (5)'] * 3 +hardfourth10extra = ['Arrows (10)'] * 2 + ['Rupees (20)'] * 7 + ['Rupees (5)'] +hardfinal20extra = ['Rupees (20)'] * 18 + ['Rupees (5)'] * 2 -expertbaseitems = (['Single Arrow', 'Rupees (300)', 'Rupees (100)', 'Bombs (3)', 'Arrows (10)'] + ['Rupees (50)'] * 4 + ['Rupees (5)'] * 5 + - ['Rupees (20)'] * 3 + ['Single Bomb'] * 10 + ['Piece of Heart'] * 24) -expertfirst15extra = ['Single Bomb'] * 7 + ['Rupees (20)'] * 3 + ['Single Arrow'] * 5 -expertsecond15extra = ['Single Bomb'] * 6 + ['Single Arrow'] * 4 + ['Rupee (1)'] * 5 -expertthird10extra = ['Rupees (5)'] * 3 + ['Single Bomb'] * 3 + ['Rupees (20)'] * 2 + ['Single Arrow'] * 2 -expertfourth5extra = ['Rupees (5)'] * 2 + ['Single Arrow'] * 3 -expertfinal25extra = ['Single Bomb'] * 4 + ['Rupees (20)'] * 3 + ['Single Arrow'] * 18 +expertbaseitems = (['Rupees (300)'] * 4 + ['Single Arrow', 'Boss Heart Container', 'Rupee (1)'] + ['Piece of Heart'] * 20 + ['Rupees (5)'] * 3 + + ['Bombs (3)'] * 10 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupees (20)'] * 2) +expertfirst15extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Rupees (5)'] * 12 +expertsecond15extra = ['Rupees (5)'] * 10 + ['Rupees (20)'] * 5 +expertthird10extra = ['Rupees (50)'] * 4 + ['Rupees (5)'] * 2 + ['Arrows (10)'] * 3 + ['Rupee (1)'] +expertfourth5extra = ['Rupees (5)'] * 5 +expertfinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2 -insanebaseitems = (['Bombs (3)', 'Arrows (10)'] + ['Rupees (50)'] * 4 + ['Rupees (5)'] * 10 + ['Rupees (300)'] * 5 + ['Rupees (100)'] * 4 + - ['Rupee (1)'] * 8 + ['Rupees (20)'] * 4 + ['Single Bomb'] * 8 + ['Single Arrow'] * 6) -insanefirst15extra = ['Single Bomb'] * 5 + ['Single Arrow'] * 4 + ['Rupee (1)'] * 5 + ['Rupees (20)'] -insanesecond15extra = ['Single Bomb'] * 5 + ['Single Arrow'] * 5 + ['Rupee (1)'] * 5 -insanethird10extra = ['Single Bomb'] * 4 + ['Single Arrow'] * 3 + ['Rupee (1)'] * 3 -insanefourth5extra = ['Single Bomb'] + ['Single Arrow'] * 2 + ['Rupee (1)'] * 2 -insanefinal25extra = ['Single Bomb'] * 2 + ['Single Arrow'] * 10 + ['Rupee (1)'] * 7 + ['Rupees (20)'] * 6 +insanebaseitems = ['Rupees (300)'] * 4 + ['Single Arrow'] + ['Rupees (5)'] * 24 + ['Bombs (3)'] * 10 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupee (1)'] + ['Rupees (20)'] * 5 +insanefirst15extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Rupees (5)'] * 12 +insanesecond15extra = ['Rupees (5)'] * 10 + ['Rupees (20)'] * 5 +insanethird10extra = ['Rupees (50)'] * 4 + ['Rupees (5)'] * 2 + ['Arrows (10)'] * 3 + ['Rupee (1)'] +insanefourth5extra = ['Rupees (5)'] * 5 +insanefinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2 Difficulty = namedtuple('Difficulty', ['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield', @@ -150,15 +148,15 @@ difficulties = { progressive_sword_limit = 3, progressive_shield_limit = 2, progressive_armor_limit = 1, - progressive_bottle_limit = 2, + progressive_bottle_limit = 4, ), 'expert': Difficulty( baseitems = expertbaseitems, bottles = hardbottles, bottle_count = 4, - same_bottle = True, - progressiveshield = [], - basicshield = [], + same_bottle = False, + progressiveshield = ['Progressive Shield'] * 3, + basicshield = ['Progressive Shield'] * 3, #only the first one will upgrade, making this equivalent to two blue shields progressivearmor = [], basicarmor = [], swordless = ['Rupees (20)'] * 3 + ['Silver Arrows'], @@ -171,15 +169,15 @@ difficulties = { conditional_extras = no_conditonal_extras, extras = [expertfirst15extra, expertsecond15extra, expertthird10extra, expertfourth5extra, expertfinal25extra], progressive_sword_limit = 2, - progressive_shield_limit = 0, + progressive_shield_limit = 1, progressive_armor_limit = 0, - progressive_bottle_limit = 1, + progressive_bottle_limit = 4, ), 'insane': Difficulty( baseitems = insanebaseitems, bottles = hardbottles, bottle_count = 4, - same_bottle = True, + same_bottle = False, progressiveshield = [], basicshield = [], progressivearmor = [], @@ -196,7 +194,7 @@ difficulties = { progressive_sword_limit = 2, progressive_shield_limit = 0, progressive_armor_limit = 0, - progressive_bottle_limit = 1, + progressive_bottle_limit = 4, ), } diff --git a/Rom.py b/Rom.py index ba75d0a9..372d8db1 100644 --- a/Rom.py +++ b/Rom.py @@ -411,7 +411,7 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None): # Powdered Fairies Prize rom.write_byte(0x36DD0, 0xD8) # One Heart # potion heal amount - rom.write_byte(0x180084, 0x28) # Five Hearts + rom.write_byte(0x180084, 0x38) # Seven Hearts # potion magic restore amount rom.write_byte(0x180085, 0x40) # Half Magic #Cape magic cost @@ -423,6 +423,10 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None): overflow_replacement = GREEN_TWENTY_RUPEES # Rupoor negative value rom.write_int16_to_rom(0x180036, world.rupoor_cost) + # Set stun items + rom.write_byte(0x180180, 0x01) # Hookshot only + # Make silver arrows only usable against Ganon + rom.write_byte(0x180181, 0x01) #Make Blue Shield more expensive rom.write_bytes(0xF73D2, [0xFC, 0xFF]) rom.write_bytes(0xF73DA, [0x04, 0x00]) @@ -443,20 +447,24 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None): rom.write_byte(0xF723F, 0xE7) elif world.difficulty == 'expert': # Powdered Fairies Prize - rom.write_byte(0x36DD0, 0x79) # Bees + rom.write_byte(0x36DD0, 0xD8) # One Heart # potion heal amount rom.write_byte(0x180084, 0x08) # One Heart # potion magic restore amount rom.write_byte(0x180085, 0x20) # Quarter Magic #Cape magic cost - rom.write_bytes(0x3ADA7, [0x02, 0x02, 0x02]) + rom.write_bytes(0x3ADA7, [0x01, 0x01, 0x01]) # Byrna Invulnerability: off rom.write_byte(0x18004F, 0x00) #Disable catching fairies rom.write_byte(0x34FD6, 0x80) overflow_replacement = GREEN_TWENTY_RUPEES # Rupoor negative value - rom.write_int16_to_rom(0x180036, 20) + rom.write_int16_to_rom(0x180036, world.rupoor_cost) + # Set stun items + rom.write_byte(0x180180, 0x00) # Nothing + # Make silver arrows only usable against Ganon + rom.write_byte(0x180181, 0x01) #Make Blue Shield more expensive rom.write_bytes(0xF73D2, [0xFC, 0xFF]) rom.write_bytes(0xF73DA, [0x04, 0x00]) @@ -483,14 +491,18 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None): # potion magic restore amount rom.write_byte(0x180085, 0x00) # No healing #Cape magic cost - rom.write_bytes(0x3ADA7, [0x02, 0x02, 0x02]) + rom.write_bytes(0x3ADA7, [0x01, 0x01, 0x01]) # Byrna Invulnerability: off rom.write_byte(0x18004F, 0x00) #Disable catching fairies rom.write_byte(0x34FD6, 0x80) overflow_replacement = GREEN_TWENTY_RUPEES # Rupoor negative value - rom.write_int16_to_rom(0x180036, 9999) + rom.write_int16_to_rom(0x180036, world.rupoor_cost) + # Set stun items + rom.write_byte(0x180180, 0x00) # Nothing + # Make silver arrows only usable against Ganon + rom.write_byte(0x180181, 0x01) #Make Blue Shield more expensive rom.write_bytes(0xF73D2, [0xFC, 0xFF]) rom.write_bytes(0xF73DA, [0x04, 0x00]) @@ -522,6 +534,12 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None): rom.write_byte(0x18004F, 0x01) #Enable catching fairies rom.write_byte(0x34FD6, 0xF0) + # Rupoor negative value + rom.write_int16_to_rom(0x180036, world.rupoor_cost) + # Set stun items + rom.write_byte(0x180180, 0x03) # All standard items + # Make silver arrows freely usable + rom.write_byte(0x180181, 0x00) #Set overflow items for progressive equipment if world.goal == 'triforcehunt': overflow_replacement = TRIFORCE_PIECE From 28d4ce09974b135cf5a48615d848aeabfd0f2a0e Mon Sep 17 00:00:00 2001 From: AmazingAmpharos Date: Wed, 14 Mar 2018 13:31:36 -0500 Subject: [PATCH 3/6] Beginnings of Retro Mode This just adds a GUI/command line option to set the variable into world for retro mode and puts the universal key item into the list of defined items. None of the functionality is yet present. --- BaseClasses.py | 4 +++- EntranceRandomizer.py | 4 ++++ Gui.py | 5 +++++ Items.py | 1 + Main.py | 6 +++--- Plando.py | 2 +- 6 files changed, 17 insertions(+), 5 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 7c61ac96..1c065af2 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -7,7 +7,7 @@ from collections import OrderedDict class World(object): - def __init__(self, shuffle, logic, mode, difficulty, timer, progressive, goal, algorithm, place_dungeon_items, check_beatable_only, shuffle_ganon, quickswap, fastmenu, disable_music, keysanity, custom, customitemarray): + def __init__(self, shuffle, logic, mode, difficulty, timer, progressive, goal, algorithm, place_dungeon_items, check_beatable_only, shuffle_ganon, quickswap, fastmenu, disable_music, keysanity, retro, custom, customitemarray): self.shuffle = shuffle self.logic = logic self.mode = mode @@ -56,6 +56,7 @@ class World(object): self.fastmenu = fastmenu self.disable_music = disable_music self.keysanity = keysanity + self.retro = retro self.custom = custom self.customitemarray = customitemarray self.can_take_damage = True @@ -278,6 +279,7 @@ class World(object): markbool(self.check_beatable_only) markbool(self.shuffle_ganon) markbool(self.keysanity) + markbool(self.retro) assert id_value_max <= 0xFFFFFFFF return id_value diff --git a/EntranceRandomizer.py b/EntranceRandomizer.py index bc6d8451..0d91223e 100755 --- a/EntranceRandomizer.py +++ b/EntranceRandomizer.py @@ -165,6 +165,10 @@ def start(): Keys (and other dungeon items) are no longer restricted to their dungeons, but can be anywhere ''', action='store_true') + parser.add_argument('--retro', help='''\ + Keys are universal, shooting arrows costs rupees, + and a few other little things make this more like Zelda-1. + ''', action='store_true') parser.add_argument('--custom', default=False, help='Not supported.') parser.add_argument('--customitemarray', default=False, help='Not supported.') parser.add_argument('--nodungeonitems', help='''\ diff --git a/Gui.py b/Gui.py index 85cc0c4c..c33fe5e2 100755 --- a/Gui.py +++ b/Gui.py @@ -62,6 +62,8 @@ def guiMain(args=None): quickSwapCheckbutton = Checkbutton(checkBoxFrame, text="Enabled L/R Item quickswapping", variable=quickSwapVar) keysanityVar = IntVar() keysanityCheckbutton = Checkbutton(checkBoxFrame, text="Keysanity (keys anywhere)", variable=keysanityVar) + retroVar = IntVar() + retroCheckbutton = Checkbutton(checkBoxFrame, text="Retro mode (universal keys)", variable=retroVar) dungeonItemsVar = IntVar() dungeonItemsCheckbutton = Checkbutton(checkBoxFrame, text="Place Dungeon Items (Compasses/Maps)", onvalue=0, offvalue=1, variable=dungeonItemsVar) beatableOnlyVar = IntVar() @@ -78,6 +80,7 @@ def guiMain(args=None): suppressRomCheckbutton.pack(expand=True, anchor=W) quickSwapCheckbutton.pack(expand=True, anchor=W) keysanityCheckbutton.pack(expand=True, anchor=W) + retroCheckbutton.pack(expand=True, anchor=W) dungeonItemsCheckbutton.pack(expand=True, anchor=W) beatableOnlyCheckbutton.pack(expand=True, anchor=W) disableMusicCheckbutton.pack(expand=True, anchor=W) @@ -262,6 +265,7 @@ def guiMain(args=None): guiargs.create_spoiler = bool(createSpoilerVar.get()) guiargs.suppress_rom = bool(suppressRomVar.get()) guiargs.keysanity = bool(keysanityVar.get()) + guiargs.retro = bool(retroVar.get()) guiargs.nodungeonitems = bool(dungeonItemsVar.get()) guiargs.beatableonly = bool(beatableOnlyVar.get()) guiargs.quickswap = bool(quickSwapVar.get()) @@ -978,6 +982,7 @@ def guiMain(args=None): createSpoilerVar.set(int(args.create_spoiler)) suppressRomVar.set(int(args.suppress_rom)) keysanityVar.set(args.keysanity) + retroVar.set(args.retro) if args.nodungeonitems: dungeonItemsVar.set(int(not args.nodungeonitems)) beatableOnlyVar.set(int(args.beatableonly)) diff --git a/Items.py b/Items.py index 97b9c605..e8a88c66 100644 --- a/Items.py +++ b/Items.py @@ -157,6 +157,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 'You have\nchosen the\narcher cla 'Big Key (Ganons Tower)': (False, False, 'BigKey', 0x92, 'A big key to the evil tower', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again'), 'Compass (Ganons Tower)': (False, True, 'Compass', 0x82, 'Now you can find Agahnim!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again'), 'Map (Ganons Tower)': (False, True, 'Map', 0x72, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again'), + 'Small Key (Universal)': (False, False, 'SmallKey', 0xAF, 'A small key for any door', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again'), 'Nothing': (False, False, None, 0x5A, 'Some Hot Air', 'and the Nothing', 'the zen kid', 'outright theft', 'shroom theft', 'empty boy is bored again'), 'Beat Agahnim 1': (True, False, 'Event', None, None, None, None, None, None, None), 'Beat Agahnim 2': (True, False, 'Event', None, None, None, None, None, None, None)} diff --git a/Main.py b/Main.py index cb22a945..98cfa0e9 100644 --- a/Main.py +++ b/Main.py @@ -39,7 +39,7 @@ def main(args, seed=None): start = time.clock() # initialize the world - world = World(args.shuffle, args.logic, args.mode, args.difficulty, args.timer, args.progressive, args.goal, args.algorithm, not args.nodungeonitems, args.beatableonly, args.shuffleganon, args.quickswap, args.fastmenu, args.disablemusic, args.keysanity, args.custom, args.customitemarray) + world = World(args.shuffle, args.logic, args.mode, args.difficulty, args.timer, args.progressive, args.goal, args.algorithm, not args.nodungeonitems, args.beatableonly, args.shuffleganon, args.quickswap, args.fastmenu, args.disablemusic, args.keysanity, args.retro, args.custom, args.customitemarray) logger = logging.getLogger('') if seed is None: random.seed(None) @@ -111,7 +111,7 @@ def main(args, seed=None): else: sprite = None - outfilebase = 'ER_%s_%s-%s-%s%s_%s-%s%s%s%s_%s' % (world.logic, world.difficulty, world.mode, world.goal, "" if world.timer in ['none', 'display'] else "-" + world.timer, world.shuffle, world.algorithm, "-keysanity" if world.keysanity else "", "-prog_" + world.progressive if world.progressive in ['off', 'random'] else "", "-shuffleganon" if world.shuffle_ganon else "", world.seed) + outfilebase = 'ER_%s_%s-%s-%s%s_%s-%s%s%s%s_%s' % (world.logic, world.difficulty, world.mode, world.goal, "" if world.timer in ['none', 'display'] else "-" + world.timer, world.shuffle, world.algorithm, "-keysanity" if world.keysanity else "", "-retro" if world.retro else "", "-prog_" + world.progressive if world.progressive in ['off', 'random'] else "", world.seed) if not args.suppress_rom: if args.jsonout: @@ -139,7 +139,7 @@ def gt_filler(world): def copy_world(world): # ToDo: Not good yet - ret = World(world.shuffle, world.logic, world.mode, world.difficulty, world.timer, world.progressive, world.goal, world.algorithm, world.place_dungeon_items, world.check_beatable_only, world.shuffle_ganon, world.quickswap, world.fastmenu, world.disable_music, world.keysanity, world.custom, world.customitemarray) + ret = World(world.shuffle, world.logic, world.mode, world.difficulty, world.timer, world.progressive, world.goal, world.algorithm, world.place_dungeon_items, world.check_beatable_only, world.shuffle_ganon, world.quickswap, world.fastmenu, world.disable_music, world.keysanity, world.retro, world.custom, world.customitemarray) ret.required_medallions = list(world.required_medallions) ret.swamp_patch_required = world.swamp_patch_required ret.ganon_at_pyramid = world.ganon_at_pyramid diff --git a/Plando.py b/Plando.py index 941a8606..f2f68873 100755 --- a/Plando.py +++ b/Plando.py @@ -33,7 +33,7 @@ def main(args): start_time = time.clock() # initialize the world - world = World('vanilla', 'noglitches', 'standard', 'normal', 'none', 'on', 'ganon', 'freshness', False, False, False, args.quickswap, args.fastmenu, args.disablemusic, False, False, None) + world = World('vanilla', 'noglitches', 'standard', 'normal', 'none', 'on', 'ganon', 'freshness', False, False, False, args.quickswap, args.fastmenu, args.disablemusic, False, False, False, None) logger = logging.getLogger('') hasher = hashlib.md5() From f701aefa2832f13e42a500f185850b3660754e81 Mon Sep 17 00:00:00 2001 From: AmazingAmpharos Date: Thu, 15 Mar 2018 16:23:02 -0500 Subject: [PATCH 4/6] More of Retro Mode Implement correct Retro Mode item pools. Set up most of the key logic to handle retro mode (still needs shop access) Set ROM flags appropriately. TODO: Support Retro Mode with custom item pools, deal with shops in general, deal with Bow paired with arrow requirements, correct Expert item pool for silvers, test older fill algorithms with retro mode, deal with the new Sahas/Bomb Shop reveal map info ROM flags. --- BaseClasses.py | 9 +++- Dungeons.py | 12 ++++-- ItemList.py | 31 ++++++++++---- Items.py | 2 +- Rom.py | 14 ++++-- Rules.py | 114 ++++++++++++++++++++++++------------------------- 6 files changed, 107 insertions(+), 75 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 1c065af2..44679095 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -44,7 +44,7 @@ class World(object): self.aga_randomness = True self.lock_aga_door_in_escape = False self.fix_trock_doors = self.shuffle != 'vanilla' - self.save_and_quite_from_boss = False + self.save_and_quit_from_boss = False self.check_beatable_only = check_beatable_only self.fix_skullwoods_exit = self.shuffle not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'] self.fix_palaceofdarkness_exit = self.shuffle not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'] @@ -377,6 +377,13 @@ class CollectionState(object): return item in self.prog_items return self.item_count(item) >= count + def has_key(self, item, count=1): + if self.world.retro: + return True #FIXME: This needs to check for shop access to a small key shop + if count == 1: + return item in self.prog_items + return self.item_count(item) >= count + def item_count(self, item): return len([pritem for pritem in self.prog_items if pritem == item]) diff --git a/Dungeons.py b/Dungeons.py index 8be5eba8..7ec6f576 100644 --- a/Dungeons.py +++ b/Dungeons.py @@ -7,7 +7,7 @@ from Items import ItemFactory def create_dungeons(world): def make_dungeon(name, dungeon_regions, big_key, small_keys, dungeon_items): - dungeon = Dungeon(name, dungeon_regions, big_key, small_keys, dungeon_items) + dungeon = Dungeon(name, dungeon_regions, big_key, [] if world.retro else small_keys, dungeon_items) for region in dungeon.regions: world.get_region(region).dungeon = dungeon return dungeon @@ -34,7 +34,10 @@ def fill_dungeons(world): all_state_base = world.get_all_state() - world.push_item(world.get_location('Skull Woods - Pinball Room'), ItemFactory('Small Key (Skull Woods)'), False) + if world.retro: + world.push_item(world.get_location('Skull Woods - Pinball Room'), ItemFactory('Small Key (Universal)'), False) + else: + world.push_item(world.get_location('Skull Woods - Pinball Room'), ItemFactory('Small Key (Skull Woods)'), False) world.get_location('Skull Woods - Pinball Room').event = True dungeons = [(list(dungeon.regions), dungeon.big_key, list(dungeon.small_keys), list(dungeon.dungeon_items)) for dungeon in world.dungeons] @@ -110,7 +113,10 @@ def fill_dungeons_restrictive(world, shuffled_locations): all_state_base = world.get_all_state() skull_woods_big_chest = world.get_location('Skull Woods - Pinball Room') - world.push_item(skull_woods_big_chest, ItemFactory('Small Key (Skull Woods)'), False) + if world.retro: + world.push_item(skull_woods_big_chest, ItemFactory('Small Key (Universal)'), False) + else: + world.push_item(skull_woods_big_chest, ItemFactory('Small Key (Skull Woods)'), False) skull_woods_big_chest.event = True shuffled_locations.remove(skull_woods_big_chest) diff --git a/ItemList.py b/ItemList.py index 37643865..0f1ebf3d 100644 --- a/ItemList.py +++ b/ItemList.py @@ -62,7 +62,7 @@ Difficulty = namedtuple('Difficulty', ['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield', 'basicshield', 'progressivearmor', 'basicarmor', 'swordless', 'progressivesword', 'basicsword', 'timedohko', 'timedother', - 'triforcehunt', 'triforce_pieces_required', 'conditional_extras', + 'triforcehunt', 'triforce_pieces_required', 'retro', 'conditional_extras', 'extras', 'progressive_sword_limit', 'progressive_shield_limit', 'progressive_armor_limit', 'progressive_bottle_limit']) @@ -76,7 +76,7 @@ def easy_conditional_extras(timer, _goal, _mode, pool, placed_items): return easytimedotherextra return [] -def no_conditonal_extras(*_args): +def no_conditional_extras(*_args): return [] @@ -97,7 +97,8 @@ difficulties = { timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, triforcehunt = ['Triforce Piece'] * 30, triforce_pieces_required = 20, - conditional_extras = no_conditonal_extras, + retro = ['Small Key (Universal)'] * 18 + ['Rupees (20)'] * 10, + conditional_extras = no_conditional_extras, extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 4, progressive_shield_limit = 3, @@ -120,6 +121,7 @@ difficulties = { timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 5, # +5 more Red Clocks if there is room triforcehunt = ['Triforce Piece'] * 30, triforce_pieces_required = 20, + retro = ['Small Key (Universal)'] * 28, conditional_extras = easy_conditional_extras, extras = [easyextra, easyfirst15extra, easysecond10extra, easythird5extra, easyfinal25extra], progressive_sword_limit = 4, @@ -143,7 +145,8 @@ difficulties = { timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, triforcehunt = ['Triforce Piece'] * 30, triforce_pieces_required = 20, - conditional_extras = no_conditonal_extras, + retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15, + conditional_extras = no_conditional_extras, extras = [hardfirst20extra, hardsecond10extra, hardthird10extra, hardfourth10extra, hardfinal20extra], progressive_sword_limit = 3, progressive_shield_limit = 2, @@ -166,7 +169,8 @@ difficulties = { timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, triforcehunt = ['Triforce Piece'] * 30, triforce_pieces_required = 20, - conditional_extras = no_conditonal_extras, + retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15, + conditional_extras = no_conditional_extras, extras = [expertfirst15extra, expertsecond15extra, expertthird10extra, expertfourth5extra, expertfinal25extra], progressive_sword_limit = 2, progressive_shield_limit = 1, @@ -189,7 +193,8 @@ difficulties = { timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, triforcehunt = ['Triforce Piece'] * 30, triforce_pieces_required = 20, - conditional_extras = no_conditonal_extras, + retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15, + conditional_extras = no_conditional_extras, extras = [insanefirst15extra, insanesecond15extra, insanethird10extra, insanefourth5extra, insanefinal25extra], progressive_sword_limit = 2, progressive_shield_limit = 0, @@ -218,7 +223,7 @@ def generate_itempool(world): (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.customitemarray) world.rupoor_cost = min(world.customitemarray[67], 9999) else: - (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode) + (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.retro) world.itempool = ItemFactory(pool) for (location, item) in placed_items: world.push_item(location, ItemFactory(item), False) @@ -281,7 +286,7 @@ def fill_prizes(world, attempts=15): -def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode): +def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, retro): pool = [] placed_items = [] clock_mode = None @@ -380,6 +385,12 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode): if goal == 'pedestal': placed_items.append(('Master Sword Pedestal', 'Triforce')) + if retro: + pool = [item.replace('Single Arrow','Rupees (5)') for item in pool] + pool = [item.replace('Arrows (10)','Rupees (5)') for item in pool] + pool = [item.replace('Arrow Upgrade (+5)','Rupees (5)') for item in pool] + pool = [item.replace('Arrow Upgrade (+10)','Rupees (5)') for item in pool] + pool.extend(diff.retro) return (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, customitemarray): @@ -532,13 +543,15 @@ def test(): for mode in ['open', 'standard', 'swordless']: for progressive in ['on', 'off']: for shuffle in ['full', 'insane']: - out = get_pool_core(progressive, shuffle, difficulty, timer, goal, mode) + out = get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, retro) count = len(out[0]) + len(out[1]) correct_count = total_items_to_place if goal in ['pedestal']: # pedestal goals generate one extra item correct_count += 1 + if retro: + correct_count += 28 assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode)) diff --git a/Items.py b/Items.py index e8a88c66..473f3fe9 100644 --- a/Items.py +++ b/Items.py @@ -157,7 +157,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 'You have\nchosen the\narcher cla 'Big Key (Ganons Tower)': (False, False, 'BigKey', 0x92, 'A big key to the evil tower', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again'), 'Compass (Ganons Tower)': (False, True, 'Compass', 0x82, 'Now you can find Agahnim!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again'), 'Map (Ganons Tower)': (False, True, 'Map', 0x72, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again'), - 'Small Key (Universal)': (False, False, 'SmallKey', 0xAF, 'A small key for any door', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again'), + 'Small Key (Universal)': (False, True, None, 0xAF, 'A small key for any door', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again'), 'Nothing': (False, False, None, 0x5A, 'Some Hot Air', 'and the Nothing', 'the zen kid', 'outright theft', 'shroom theft', 'empty boy is bored again'), 'Beat Agahnim 1': (True, False, 'Event', None, None, None, None, None, None, None), 'Beat Agahnim 2': (True, False, 'Event', None, None, None, None, None, None, None)} diff --git a/Rom.py b/Rom.py index 372d8db1..c2ffef56 100644 --- a/Rom.py +++ b/Rom.py @@ -744,9 +744,7 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None): rom.write_byte(0x1800A0, 0x01) # return to light world on s+q without mirror rom.write_byte(0x1800A1, 0x01) # enable overworld screen transition draining for water level inside swamp rom.write_byte(0x180174, 0x01 if world.fix_fake_world else 0x00) - rom.write_byte(0x180175, 0x00) # Arrow mode: normal - rom.write_int16_to_rom(0x180176, 0) # Wood Arrow Cost (rupee arrow mode) - rom.write_int16_to_rom(0x180178, 0) # Silver Arrow Cost (rupee arrow mode) + rom.write_byte(0x18017E, 0x01) # Fairy fountains only trade in bottles rom.write_byte(0x180034, 0x0A) # starting max bombs rom.write_byte(0x180035, 30) # starting max arrows for x in range(0x183000, 0x18304F): @@ -787,11 +785,19 @@ def patch_rom(world, rom, hashtable, beep='normal', color='red', sprite=None): rom.write_byte(0x18003C, 0x00) rom.write_byte(0x180045, 0xFF if world.keysanity else 0x00) # free roaming items in menu + rom.write_byte(0x180172, 0x01 if world.retro else 0x00) # universal keys + rom.write_byte(0x180175, 0x01 if world.retro else 0x00) # rupee bow + rom.write_byte(0x180176, 0x0A if world.retro else 0x00) # wood arrow cost + rom.write_byte(0x180178, 0x32 if world.retro else 0x00) # silver arrow cost + rom.write_byte(0x301FC, 0xDA if world.retro else 0xE1) # rupees replace arrows under pots + rom.write_bytes(0xECB4E, [0xA9, 0x00, 0xEA, 0xEA] if world.retro else [0xAF, 0x77, 0xF3, 0x7E]) # Thief steals rupees instead of arrows + rom.write_bytes(0xF0D96, [0xA9, 0x00, 0xEA, 0xEA] if world.retro else [0xAF, 0x77, 0xF3, 0x7E]) # Pikit steals rupees instead of arrows + rom.write_bytes(0xEDA5, [0x35, 0x41] if world.retro else [0x43, 0x44]) # Chest game gives rupees instead of arrows digging_game_rng = random.randint(1, 30) # set rng for digging game rom.write_byte(0x180020, digging_game_rng) rom.write_byte(0xEFD95, digging_game_rng) rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills - rom.write_byte(0x180042, 0x01 if world.save_and_quite_from_boss else 0x00) # Allow Save and Quite after boss kill + rom.write_byte(0x180042, 0x01 if world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill # remove shield from uncle rom.write_bytes(0x6D253, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E]) diff --git a/Rules.py b/Rules.py index b0cd6424..fcbd2b2a 100644 --- a/Rules.py +++ b/Rules.py @@ -125,8 +125,8 @@ def global_rules(world): set_rule(world.get_location('Master Sword Pedestal'), lambda state: state.has('Red Pendant') and state.has('Blue Pendant') and state.has('Green Pendant')) set_rule(world.get_location('Sahasrahla'), lambda state: state.has('Green Pendant')) set_rule(world.get_entrance('Agahnims Tower'), lambda state: state.has('Cape') or state.has_beam_sword() or state.has('Beat Agahnim 1')) # barrier gets removed after killing agahnim, relevant for entrance shuffle - set_rule(world.get_entrance('Agahnim 1'), lambda state: state.has_sword() and state.has('Small Key (Agahnims Tower)', 2)) - set_rule(world.get_location('Castle Tower - Dark Maze'), lambda state: state.has('Small Key (Agahnims Tower)')) + set_rule(world.get_entrance('Agahnim 1'), lambda state: state.has_sword() and state.has_key('Small Key (Agahnims Tower)', 2)) + set_rule(world.get_location('Castle Tower - Dark Maze'), lambda state: state.has_key('Small Key (Agahnims Tower)')) set_rule(world.get_entrance('Top of Pyramid'), lambda state: state.has('Beat Agahnim 1')) set_rule(world.get_entrance('Old Man Cave Exit (West)'), lambda state: False) # drop cannot be climbed up set_rule(world.get_entrance('Broken Bridge (West)'), lambda state: state.has('Hookshot')) @@ -212,8 +212,8 @@ def global_rules(world): set_rule(world.get_entrance('Turtle Rock'), lambda state: state.has_Pearl() and state.has_sword() and state.has_turtle_rock_medallion() and state.can_reach('Turtle Rock (Top)', 'Region')) # sword required to cast magic (!) set_rule(world.get_location('Mimic Cave'), lambda state: state.has('Hammer')) - set_rule(world.get_entrance('Sewers Door'), lambda state: state.has('Small Key (Escape)')) - set_rule(world.get_entrance('Sewers Back Door'), lambda state: state.has('Small Key (Escape)')) + set_rule(world.get_entrance('Sewers Door'), lambda state: state.has_key('Small Key (Escape)')) + set_rule(world.get_entrance('Sewers Back Door'), lambda state: state.has_key('Small Key (Escape)')) set_rule(world.get_location('Eastern Palace - Big Chest'), lambda state: state.has('Big Key (Eastern Palace)')) set_rule(world.get_location('Eastern Palace - Armos Knights'), lambda state: state.has('Bow') and state.has('Big Key (Eastern Palace)')) @@ -223,10 +223,10 @@ def global_rules(world): set_rule(world.get_location('Desert Palace - Big Chest'), lambda state: state.has('Big Key (Desert Palace)')) set_rule(world.get_location('Desert Palace - Torch'), lambda state: state.has_Boots()) - set_rule(world.get_entrance('Desert Palace East Wing'), lambda state: state.has('Small Key (Desert Palace)')) - set_rule(world.get_location('Desert Palace - Prize'), lambda state: state.has('Small Key (Desert Palace)') and state.has('Big Key (Desert Palace)') and state.has_fire_source() and + set_rule(world.get_entrance('Desert Palace East Wing'), lambda state: state.has_key('Small Key (Desert Palace)')) + set_rule(world.get_location('Desert Palace - Prize'), lambda state: state.has_key('Small Key (Desert Palace)') and state.has('Big Key (Desert Palace)') and state.has_fire_source() and (state.has_blunt_weapon() or state.has('Fire Rod') or state.has('Ice Rod') or state.has('Bow'))) - set_rule(world.get_location('Desert Palace - Lanmolas'), lambda state: state.has('Small Key (Desert Palace)') and state.has('Big Key (Desert Palace)') and state.has_fire_source() and + set_rule(world.get_location('Desert Palace - Lanmolas'), lambda state: state.has_key('Small Key (Desert Palace)') and state.has('Big Key (Desert Palace)') and state.has_fire_source() and (state.has_blunt_weapon() or state.has('Fire Rod') or state.has('Ice Rod') or state.has('Bow'))) for location in ['Desert Palace - Lanmolas', 'Desert Palace - Big Chest']: forbid_item(world.get_location(location), 'Big Key (Desert Palace)') @@ -234,7 +234,7 @@ def global_rules(world): for location in ['Desert Palace - Lanmolas', 'Desert Palace - Big Key Chest', 'Desert Palace - Compass Chest']: forbid_item(world.get_location(location), 'Small Key (Desert Palace)') - set_rule(world.get_entrance('Tower of Hera Small Key Door'), lambda state: state.has('Small Key (Tower of Hera)') or item_name(state, 'Tower of Hera - Big Key Chest') == 'Small Key (Tower of Hera)') + set_rule(world.get_entrance('Tower of Hera Small Key Door'), lambda state: state.has_key('Small Key (Tower of Hera)') or item_name(state, 'Tower of Hera - Big Key Chest') == 'Small Key (Tower of Hera)') set_rule(world.get_entrance('Tower of Hera Big Key Door'), lambda state: state.has('Big Key (Tower of Hera)')) set_rule(world.get_location('Tower of Hera - Big Chest'), lambda state: state.has('Big Key (Tower of Hera)')) set_rule(world.get_location('Tower of Hera - Big Key Chest'), lambda state: state.has_fire_source()) @@ -243,11 +243,11 @@ def global_rules(world): set_rule(world.get_location('Tower of Hera - Prize'), lambda state: state.has_blunt_weapon()) for location in ['Tower of Hera - Moldorm', 'Tower of Hera - Big Chest', 'Tower of Hera - Compass Chest']: forbid_item(world.get_location(location), 'Big Key (Tower of Hera)') - for location in ['Tower of Hera - Big Key Chest']: - forbid_item(world.get_location(location), 'Small Key (Tower of Hera)') +# for location in ['Tower of Hera - Big Key Chest']: +# forbid_item(world.get_location(location), 'Small Key (Tower of Hera)') set_rule(world.get_entrance('Swamp Palace Moat'), lambda state: state.has('Flippers') and state.can_reach('Dam')) - set_rule(world.get_entrance('Swamp Palace Small Key Door'), lambda state: state.has('Small Key (Swamp Palace)')) + set_rule(world.get_entrance('Swamp Palace Small Key Door'), lambda state: state.has_key('Small Key (Swamp Palace)')) set_rule(world.get_entrance('Swamp Palace (Center)'), lambda state: state.has('Hammer')) set_rule(world.get_location('Swamp Palace - Big Chest'), lambda state: state.has('Big Key (Swamp Palace)') or item_name(state, 'Swamp Palace - Big Chest') == 'Big Key (Swamp Palace)') set_always_allow(world.get_location('Swamp Palace - Big Chest'), lambda state, item: item.name == 'Big Key (Swamp Palace)') @@ -258,29 +258,29 @@ def global_rules(world): forbid_item(world.get_location(location), 'Big Key (Swamp Palace)') set_rule(world.get_entrance('Thieves Town Big Key Door'), lambda state: state.has('Big Key (Thieves Town)')) - set_rule(world.get_entrance('Blind Fight'), lambda state: state.has('Small Key (Thieves Town)') and (state.has_blunt_weapon() or state.has('Cane of Somaria') or state.has('Cane of Byrna'))) - set_rule(world.get_location('Thieves\' Town - Big Chest'), lambda state: (state.has('Small Key (Thieves Town)') or item_name(state, 'Thieves\' Town - Big Chest') == 'Small Key (Thieves Town)') and state.has('Hammer')) + set_rule(world.get_entrance('Blind Fight'), lambda state: state.has_key('Small Key (Thieves Town)') and (state.has_blunt_weapon() or state.has('Cane of Somaria') or state.has('Cane of Byrna'))) + set_rule(world.get_location('Thieves\' Town - Big Chest'), lambda state: (state.has_key('Small Key (Thieves Town)') or item_name(state, 'Thieves\' Town - Big Chest') == 'Small Key (Thieves Town)') and state.has('Hammer')) set_always_allow(world.get_location('Thieves\' Town - Big Chest'), lambda state, item: item.name == 'Small Key (Thieves Town)' and state.has('Hammer')) - set_rule(world.get_location('Thieves\' Town - Attic'), lambda state: state.has('Small Key (Thieves Town)')) + set_rule(world.get_location('Thieves\' Town - Attic'), lambda state: state.has_key('Small Key (Thieves Town)')) for location in ['Thieves\' Town - Attic', 'Thieves\' Town - Big Chest', 'Thieves\' Town - Blind\'s Cell', 'Thieves Town - Blind']: forbid_item(world.get_location(location), 'Big Key (Thieves Town)') for location in ['Thieves\' Town - Attic', 'Thieves Town - Blind']: forbid_item(world.get_location(location), 'Small Key (Thieves Town)') - set_rule(world.get_entrance('Skull Woods First Section South Door'), lambda state: state.has('Small Key (Skull Woods)')) - set_rule(world.get_entrance('Skull Woods First Section (Right) North Door'), lambda state: state.has('Small Key (Skull Woods)')) - set_rule(world.get_entrance('Skull Woods First Section West Door'), lambda state: state.has('Small Key (Skull Woods)', 2)) # ideally would only be one key, but we may have spent thst key already on escaping the right section - set_rule(world.get_entrance('Skull Woods First Section (Left) Door to Exit'), lambda state: state.has('Small Key (Skull Woods)', 2)) + set_rule(world.get_entrance('Skull Woods First Section South Door'), lambda state: state.has_key('Small Key (Skull Woods)')) + set_rule(world.get_entrance('Skull Woods First Section (Right) North Door'), lambda state: state.has_key('Small Key (Skull Woods)')) + set_rule(world.get_entrance('Skull Woods First Section West Door'), lambda state: state.has_key('Small Key (Skull Woods)', 2)) # ideally would only be one key, but we may have spent thst key already on escaping the right section + set_rule(world.get_entrance('Skull Woods First Section (Left) Door to Exit'), lambda state: state.has_key('Small Key (Skull Woods)', 2)) set_rule(world.get_location('Skull Woods - Big Chest'), lambda state: state.has('Big Key (Skull Woods)') or item_name(state, 'Skull Woods - Big Chest') == 'Big Key (Skull Woods)') set_always_allow(world.get_location('Skull Woods - Big Chest'), lambda state, item: item.name == 'Big Key (Skull Woods)') - set_rule(world.get_entrance('Skull Woods Torch Room'), lambda state: state.has('Small Key (Skull Woods)', 3) and state.has('Fire Rod') and state.has_sword()) # sword required for curtain + set_rule(world.get_entrance('Skull Woods Torch Room'), lambda state: state.has_key('Small Key (Skull Woods)', 3) and state.has('Fire Rod') and state.has_sword()) # sword required for curtain for location in ['Skull Woods - Mothula']: forbid_item(world.get_location(location), 'Small Key (Skull Woods)') set_rule(world.get_entrance('Ice Palace Entrance Room'), lambda state: state.has('Fire Rod') or (state.has('Bombos') and state.has_sword())) set_rule(world.get_location('Ice Palace - Big Chest'), lambda state: state.has('Big Key (Ice Palace)')) - set_rule(world.get_entrance('Ice Palace (Kholdstare)'), lambda state: state.can_lift_rocks() and state.has('Hammer') and state.has('Big Key (Ice Palace)') and (state.has('Small Key (Ice Palace)', 2) or (state.has('Cane of Somaria') and state.has('Small Key (Ice Palace)', 1)))) - set_rule(world.get_entrance('Ice Palace (East)'), lambda state: (state.has('Hookshot') or (item_in_locations(state, 'Big Key (Ice Palace)', ['Ice Palace - Spike Room', 'Ice Palace - Big Key Chest', 'Ice Palace - Map Chest']) and state.has('Small Key (Ice Palace)'))) and (state.world.can_take_damage or state.has('Hookshot') or state.has('Cape') or state.has('Cane of Byrna'))) + set_rule(world.get_entrance('Ice Palace (Kholdstare)'), lambda state: state.can_lift_rocks() and state.has('Hammer') and state.has('Big Key (Ice Palace)') and (state.has_key('Small Key (Ice Palace)', 2) or (state.has('Cane of Somaria') and state.has_key('Small Key (Ice Palace)', 1)))) + set_rule(world.get_entrance('Ice Palace (East)'), lambda state: (state.has('Hookshot') or (item_in_locations(state, 'Big Key (Ice Palace)', ['Ice Palace - Spike Room', 'Ice Palace - Big Key Chest', 'Ice Palace - Map Chest']) and state.has_key('Small Key (Ice Palace)'))) and (state.world.can_take_damage or state.has('Hookshot') or state.has('Cape') or state.has('Cane of Byrna'))) set_rule(world.get_entrance('Ice Palace (East Top)'), lambda state: state.can_lift_rocks() and state.has('Hammer')) for location in ['Ice Palace - Big Chest', 'Ice Palace - Kholdstare']: forbid_item(world.get_location(location), 'Big Key (Ice Palace)') @@ -291,12 +291,12 @@ def global_rules(world): set_rule(world.get_entrance('Misery Mire Big Key Door'), lambda state: state.has('Big Key (Misery Mire)')) # 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 - set_rule(world.get_location('Misery Mire - Map Chest'), lambda state: state.has('Small Key (Misery Mire)', 1) or state.has('Big Key (Misery Mire)')) + set_rule(world.get_location('Misery Mire - Map Chest'), lambda state: state.has_key('Small Key (Misery Mire)', 1) or state.has('Big Key (Misery Mire)')) # in addition, you can open the door to the map room before getting access to a color switch, so this is locked behing 2 small keys or the big key... - set_rule(world.get_location('Misery Mire - Main Lobby'), lambda state: state.has('Small Key (Misery Mire)', 2) or state.has('Big Key (Misery Mire)')) + set_rule(world.get_location('Misery Mire - Main Lobby'), lambda state: state.has_key('Small Key (Misery Mire)', 2) or state.has_key('Big Key (Misery Mire)')) # we can place a small key in the West wing iff it also contains/blocks the Big Key, as we cannot reach and softlock with the basement key door yet - set_rule(world.get_entrance('Misery Mire (West)'), lambda state: state.has('Small Key (Misery Mire)', 2) if ((item_name(state, 'Misery Mire - Compass Chest') in ['Big Key (Misery Mire)']) or - (item_name(state, 'Misery Mire - Big Key Chest') in ['Big Key (Misery Mire)'])) else state.has('Small Key (Misery Mire)', 3)) + set_rule(world.get_entrance('Misery Mire (West)'), lambda state: state.has_key('Small Key (Misery Mire)', 2) if ((item_name(state, 'Misery Mire - Compass Chest') in ['Big Key (Misery Mire)']) or + (item_name(state, 'Misery Mire - Big Key Chest') in ['Big Key (Misery Mire)'])) else state.has_key('Small Key (Misery Mire)', 3)) set_rule(world.get_location('Misery Mire - Compass Chest'), lambda state: state.has_fire_source()) set_rule(world.get_location('Misery Mire - Big Key Chest'), lambda state: state.has_fire_source()) set_rule(world.get_entrance('Misery Mire (Vitreous)'), lambda state: state.has('Cane of Somaria') and (state.has('Bow') or state.has_blunt_weapon())) @@ -311,31 +311,31 @@ def global_rules(world): set_rule(world.get_location('Turtle Rock - Big Chest'), lambda state: state.has('Big Key (Turtle Rock)') and (state.has('Cane of Somaria') or state.has('Hookshot'))) set_rule(world.get_entrance('Turtle Rock (Big Chest) (North)'), lambda state: state.has('Cane of Somaria') or state.has('Hookshot')) set_rule(world.get_entrance('Turtle Rock Big Key Door'), lambda state: state.has('Big Key (Turtle Rock)')) - set_rule(world.get_entrance('Turtle Rock Dark Room Staircase'), lambda state: state.has('Small Key (Turtle Rock)', 3)) + set_rule(world.get_entrance('Turtle Rock Dark Room Staircase'), lambda state: state.has_key('Small Key (Turtle Rock)', 3)) set_rule(world.get_entrance('Turtle Rock (Dark Room) (North)'), lambda state: state.has('Cane of Somaria')) set_rule(world.get_entrance('Turtle Rock (Dark Room) (South)'), lambda state: state.has('Cane of Somaria')) set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Left'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield')) set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Right'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield')) set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield')) set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield')) - set_rule(world.get_entrance('Turtle Rock (Trinexx)'), lambda state: state.has('Small Key (Turtle Rock)', 4) and state.has('Big Key (Turtle Rock)') and state.has('Cane of Somaria') and state.has('Fire Rod') and state.has('Ice Rod') and + set_rule(world.get_entrance('Turtle Rock (Trinexx)'), lambda state: state.has_key('Small Key (Turtle Rock)', 4) and state.has('Big Key (Turtle Rock)') and state.has('Cane of Somaria') and state.has('Fire Rod') and state.has('Ice Rod') and (state.has('Hammer') or state.has_beam_sword() or (state.has_sword() and state.can_extend_magic(32)))) set_trock_key_rules(world) set_rule(world.get_entrance('Palace of Darkness Bonk Wall'), lambda state: state.has('Bow')) set_rule(world.get_entrance('Palace of Darkness Hammer Peg Drop'), lambda state: state.has('Hammer')) - set_rule(world.get_entrance('Palace of Darkness Bridge Room'), lambda state: state.has('Small Key (Palace of Darkness)', 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'), lambda state: state.has('Small Key (Palace of Darkness)', 6) and state.has('Big Key (Palace of Darkness)') and state.has('Bow') and state.has('Hammer')) - set_rule(world.get_entrance('Palace of Darkness (North)'), lambda state: state.has('Small Key (Palace of Darkness)', 4)) + set_rule(world.get_entrance('Palace of Darkness Bridge Room'), lambda state: state.has_key('Small Key (Palace of Darkness)', 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'), lambda state: state.has_key('Small Key (Palace of Darkness)', 6) and state.has('Big Key (Palace of Darkness)') and state.has('Bow') and state.has('Hammer')) + set_rule(world.get_entrance('Palace of Darkness (North)'), lambda state: state.has_key('Small Key (Palace of Darkness)', 4)) set_rule(world.get_location('Palace of Darkness - Big Chest'), lambda state: state.has('Big Key (Palace of Darkness)')) - set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase'), lambda state: state.has('Small Key (Palace of Darkness)', 6) or (item_name(state, 'Palace of Darkness - Big Key Chest') in ['Small Key (Palace of Darkness)'] and state.has('Small Key (Palace of Darkness)', 3))) - set_always_allow(world.get_location('Palace of Darkness - Big Key Chest'), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and state.has('Small Key (Palace of Darkness)', 5)) + set_rule(world.get_entrance('Palace of Darkness Big Key Chest Staircase'), lambda state: state.has_key('Small Key (Palace of Darkness)', 6) or (item_name(state, 'Palace of Darkness - Big Key Chest') in ['Small Key (Palace of Darkness)'] and state.has_key('Small Key (Palace of Darkness)', 3))) + set_always_allow(world.get_location('Palace of Darkness - Big Key Chest'), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and state.has_key('Small Key (Palace of Darkness)', 5)) - set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door'), lambda state: state.has('Small Key (Palace of Darkness)', 6) or (item_name(state, 'Palace of Darkness - Harmless Hellway') in ['Small Key (Palace of Darkness)'] and state.has('Small Key (Palace of Darkness)', 4))) - set_always_allow(world.get_location('Palace of Darkness - Harmless Hellway'), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and state.has('Small Key (Palace of Darkness)', 5)) - set_rule(world.get_entrance('Palace of Darkness Maze Door'), lambda state: state.has('Small Key (Palace of Darkness)', 6)) + set_rule(world.get_entrance('Palace of Darkness Spike Statue Room Door'), lambda state: state.has_key('Small Key (Palace of Darkness)', 6) or (item_name(state, 'Palace of Darkness - Harmless Hellway') in ['Small Key (Palace of Darkness)'] and state.has_key('Small Key (Palace of Darkness)', 4))) + set_always_allow(world.get_location('Palace of Darkness - Harmless Hellway'), lambda state, item: item.name == 'Small Key (Palace of Darkness)' and state.has_key('Small Key (Palace of Darkness)', 5)) + set_rule(world.get_entrance('Palace of Darkness Maze Door'), lambda state: state.has_key('Small Key (Palace of Darkness)', 6)) # these key rules are conservative, you might be able to get away with more lenient rules randomizer_room_chests = ['Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right', 'Ganons Tower - Randomizer Room - Bottom Left', 'Ganons Tower - Randomizer Room - Bottom Right'] @@ -345,25 +345,25 @@ def global_rules(world): set_rule(world.get_entrance('Ganons Tower (Tile Room)'), lambda state: state.has('Cane of Somaria')) set_rule(world.get_entrance('Ganons Tower (Hookshot Room)'), lambda state: state.has('Hammer')) - set_rule(world.get_entrance('Ganons Tower (Map Room)'), lambda state: state.has('Small Key (Ganons Tower)', 4) or (item_name(state, 'Ganons Tower - Map Chest') in ['Big Key (Ganons Tower)', 'Small Key (Ganons Tower)'] and state.has('Small Key (Ganons Tower)', 3))) - set_always_allow(world.get_location('Ganons Tower - Map Chest'), lambda state, item: item.name == 'Small Key (Ganons Tower)' and state.has('Small Key (Ganons Tower)', 3)) + set_rule(world.get_entrance('Ganons Tower (Map Room)'), lambda state: state.has_key('Small Key (Ganons Tower)', 4) or (item_name(state, 'Ganons Tower - Map Chest') in ['Big Key (Ganons Tower)', 'Small Key (Ganons Tower)'] and state.has_key('Small Key (Ganons Tower)', 3))) + set_always_allow(world.get_location('Ganons Tower - Map Chest'), lambda state, item: item.name == 'Small Key (Ganons Tower)' and state.has_key('Small Key (Ganons Tower)', 3)) # It is possible to need more than 2 keys to get through this entance if you spend keys elsewhere. We reflect this in the chest requirements. # However we need to leave these at the lower values to derive that with 3 keys it is always possible to reach Bob and Ice Armos. - set_rule(world.get_entrance('Ganons Tower (Double Switch Room)'), lambda state: state.has('Small Key (Ganons Tower)', 2)) + set_rule(world.get_entrance('Ganons Tower (Double Switch Room)'), lambda state: state.has_key('Small Key (Ganons Tower)', 2)) # It is possible to need more than 3 keys .... - set_rule(world.get_entrance('Ganons Tower (Firesnake Room)'), lambda state: state.has('Small Key (Ganons Tower)', 3)) + set_rule(world.get_entrance('Ganons Tower (Firesnake Room)'), lambda state: state.has_key('Small Key (Ganons Tower)', 3)) #The actual requirements for these rooms to avoid key-lock - set_rule(world.get_location('Ganons Tower - Firesnake Room'), lambda state: state.has('Small Key (Ganons Tower)', 3) or (item_in_locations(state, 'Big Key (Ganons Tower)', randomizer_room_chests) and state.has('Small Key (Ganons Tower)', 2))) + set_rule(world.get_location('Ganons Tower - Firesnake Room'), lambda state: state.has_key('Small Key (Ganons Tower)', 3) or (item_in_locations(state, 'Big Key (Ganons Tower)', randomizer_room_chests) and state.has_key('Small Key (Ganons Tower)', 2))) for location in randomizer_room_chests: - set_rule(world.get_location(location), lambda state: state.has('Small Key (Ganons Tower)', 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', randomizer_room_chests) and state.has('Small Key (Ganons Tower)', 3))) + set_rule(world.get_location(location), lambda state: state.has_key('Small Key (Ganons Tower)', 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', randomizer_room_chests) and state.has_key('Small Key (Ganons Tower)', 3))) # Once again it is possible to need more than 3 keys... - set_rule(world.get_entrance('Ganons Tower (Tile Room) Key Door'), lambda state: state.has('Small Key (Ganons Tower)', 3) and state.has('Fire Rod')) + set_rule(world.get_entrance('Ganons Tower (Tile Room) Key Door'), lambda state: state.has_key('Small Key (Ganons Tower)', 3) and state.has('Fire Rod')) # Actual requirements for location in compass_room_chests: - set_rule(world.get_location(location), lambda state: state.has('Fire Rod') and (state.has('Small Key (Ganons Tower)', 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', compass_room_chests) and state.has('Small Key (Ganons Tower)', 3)))) + set_rule(world.get_location(location), lambda state: state.has('Fire Rod') and (state.has_key('Small Key (Ganons Tower)', 4) or (item_in_locations(state, 'Big Key (Ganons Tower)', compass_room_chests) and state.has_key('Small Key (Ganons Tower)', 3)))) set_rule(world.get_location('Ganons Tower - Big Chest'), lambda state: state.has('Big Key (Ganons Tower)')) set_rule(world.get_location('Ganons Tower - Big Key Room - Left'), lambda state: state.has('Bow') or state.has_blunt_weapon()) @@ -371,8 +371,8 @@ def global_rules(world): set_rule(world.get_location('Ganons Tower - Big Key Room - Right'), lambda state: state.has('Bow') or state.has_blunt_weapon()) set_rule(world.get_entrance('Ganons Tower Big Key Door'), lambda state: state.has('Big Key (Ganons Tower)') and state.has('Bow')) set_rule(world.get_entrance('Ganons Tower Torch Rooms'), lambda state: state.has_fire_source()) - set_rule(world.get_location('Ganons Tower - Pre-Moldorm Chest'), lambda state: state.has('Small Key (Ganons Tower)', 3)) - set_rule(world.get_entrance('Ganons Tower Moldorm Door'), lambda state: state.has('Small Key (Ganons Tower)', 4)) + set_rule(world.get_location('Ganons Tower - Pre-Moldorm Chest'), lambda state: state.has_key('Small Key (Ganons Tower)', 3)) + set_rule(world.get_entrance('Ganons Tower Moldorm Door'), lambda state: state.has_key('Small Key (Ganons Tower)', 4)) set_rule(world.get_entrance('Ganons Tower Moldorm Gap'), lambda state: state.has('Hookshot') and state.has_blunt_weapon()) set_rule(world.get_location('Agahnim 2'), lambda state: state.has_sword() or state.has('Hammer') or state.has('Bug Catching Net')) set_rule(world.get_entrance('Pyramid Hole'), lambda state: state.has('Beat Agahnim 2')) @@ -447,8 +447,8 @@ def open_rules(world): forbid_item(world.get_location('Hyrule Castle - Boomerang Chest'), 'Small Key (Escape)') forbid_item(world.get_location('Hyrule Castle - Zelda\'s Chest'), 'Small Key (Escape)') - set_rule(world.get_location('Hyrule Castle - Boomerang Chest'), lambda state: state.has('Small Key (Escape)')) - set_rule(world.get_location('Hyrule Castle - Zelda\'s Chest'), lambda state: state.has('Small Key (Escape)')) + set_rule(world.get_location('Hyrule Castle - Boomerang Chest'), lambda state: state.has_key('Small Key (Escape)')) + set_rule(world.get_location('Hyrule Castle - Zelda\'s Chest'), lambda state: state.has_key('Small Key (Escape)')) def swordless_rules(world): @@ -459,12 +459,12 @@ def swordless_rules(world): open_rules(world) set_rule(world.get_entrance('Agahnims Tower'), lambda state: state.has('Cape') or state.has('Hammer') or state.has('Beat Agahnim 1')) # barrier gets removed after killing agahnim, relevant for entrance shuffle - set_rule(world.get_entrance('Agahnim 1'), lambda state: (state.has('Hammer') or (state.has('Bug Catching Net') and (state.has('Fire Rod') or state.has('Bow') or state.has('Cane of Somaria')))) and state.has('Small Key (Agahnims Tower)', 2)) + set_rule(world.get_entrance('Agahnim 1'), lambda state: (state.has('Hammer') or (state.has('Bug Catching Net') and (state.has('Fire Rod') or state.has('Bow') or state.has('Cane of Somaria')))) and state.has_key('Small Key (Agahnims Tower)', 2)) set_rule(world.get_location('Ether Tablet'), lambda state: state.has('Book of Mudora') and state.has('Hammer')) set_rule(world.get_location('Bombos Tablet'), lambda state: state.has('Book of Mudora') and state.has('Hammer') and state.has_Mirror()) set_rule(world.get_entrance('Misery Mire'), lambda state: state.has_Pearl() and state.has_misery_mire_medallion()) # sword not required to use medallion for opening in swordless (!) set_rule(world.get_entrance('Turtle Rock'), lambda state: state.has_Pearl() and state.has_turtle_rock_medallion() and state.can_reach('Turtle Rock (Top)', 'Region')) # sword not required to use medallion for opening in swordless (!) - set_rule(world.get_entrance('Skull Woods Torch Room'), lambda state: state.has('Small Key (Skull Woods)', 3) and state.has('Fire Rod')) # no curtain + set_rule(world.get_entrance('Skull Woods Torch Room'), lambda state: state.has_key('Small Key (Skull Woods)', 3) and state.has('Fire Rod')) # no curtain set_rule(world.get_entrance('Ice Palace Entrance Room'), lambda state: state.has('Fire Rod') or state.has('Bombos')) #in swordless mode bombos pads are present in the relevant parts of ice palace set_rule(world.get_location('Agahnim 2'), lambda state: state.has('Hammer') or state.has('Bug Catching Net')) set_rule(world.get_location('Ganon'), lambda state: state.has('Hammer') and state.has_fire_source() and state.has('Silver Arrows') and state.has('Bow') and state.has('Crystal 1') and state.has('Crystal 2') @@ -475,7 +475,7 @@ def swordless_rules(world): def standard_rules(world): for loc in ['Sanctuary','Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle', 'Sewers - Secret Room - Right']: - add_rule(world.get_location(loc), lambda state: state.can_kill_most_things() and state.has('Small Key (Escape)')) + add_rule(world.get_location(loc), lambda state: state.can_kill_most_things() and state.has_key('Small Key (Escape)')) # easiest way to enforce key placement not relevant for open set_rule(world.get_location('Sewers - Dark Cross'), lambda state: state.can_kill_most_things()) @@ -500,18 +500,18 @@ def set_trock_key_rules(world): # if we have backdoor access we can waste a key on the trinexx door, then have no lamp to reverse traverse the maze room. We simply require an additional key just to be super safe then. The backdoor access to the chest is otherwise free if not can_reach_back: - set_rule(world.get_entrance('Turtle Rock Pokey Room'), lambda state: state.has('Small Key (Turtle Rock)', 1)) + set_rule(world.get_entrance('Turtle Rock Pokey Room'), lambda state: state.has_key('Small Key (Turtle Rock)', 1)) else: - set_rule(world.get_entrance('Turtle Rock Pokey Room'), lambda state: state.has('Small Key (Turtle Rock)', 2)) + set_rule(world.get_entrance('Turtle Rock Pokey Room'), lambda state: state.has_key('Small Key (Turtle Rock)', 2)) # if we have front access this transition is useless. If we don't, it's a dead end so cannot hold any small keys - set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (South)'), lambda state: state.has('Small Key (Turtle Rock)', 4)) + set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (South)'), lambda state: state.has_key('Small Key (Turtle Rock)', 4)) # this is just the pokey room with one more key if not can_reach_back: - set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (North)'), lambda state: state.has('Small Key (Turtle Rock)', 2)) + set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (North)'), lambda state: state.has_key('Small Key (Turtle Rock)', 2)) else: - set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (North)'), lambda state: state.has('Small Key (Turtle Rock)', 3)) + set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (North)'), lambda state: state.has_key('Small Key (Turtle Rock)', 3)) # the most complicated one def tr_big_key_chest_keys_needed(state): @@ -527,8 +527,8 @@ def set_trock_key_rules(world): # otherwise we could potentially have opened every other door already, so we need all 4 keys. return 4 - set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has('Small Key (Turtle Rock)', tr_big_key_chest_keys_needed(state))) - set_always_allow(world.get_location('Turtle Rock - Big Key Chest'), lambda state, item: item.name == 'Small Key (Turtle Rock)' and state.has('Small Key (Turtle Rock)', 3)) + set_rule(world.get_location('Turtle Rock - Big Key Chest'), lambda state: state.has_key('Small Key (Turtle Rock)', tr_big_key_chest_keys_needed(state))) + set_always_allow(world.get_location('Turtle Rock - Big Key Chest'), lambda state, item: item.name == 'Small Key (Turtle Rock)' and state.has_key('Small Key (Turtle Rock)', 3)) # set big key restrictions non_big_key_locations = ['Turtle Rock - Big Chest', 'Turtle Rock - Trinexx'] From f607cc2522e8758ab64cce19d9b4005703828b42 Mon Sep 17 00:00:00 2001 From: AmazingAmpharos Date: Thu, 15 Mar 2018 16:53:42 -0500 Subject: [PATCH 5/6] Custom item pool support for retro mode Allows the placement of Universal Keys (which probably don't do much outside of retro mode) and handles total item counts properly. --- Gui.py | 10 +++++++++- ItemList.py | 8 ++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Gui.py b/Gui.py index c33fe5e2..5ea97546 100755 --- a/Gui.py +++ b/Gui.py @@ -280,7 +280,7 @@ def guiMain(args=None): int(redmailVar.get()), int(progmailVar.get()), int(halfmagicVar.get()), int(quartermagicVar.get()), int(bcap5Var.get()), int(bcap10Var.get()), int(acap5Var.get()), int(acap10Var.get()), int(arrow1Var.get()), int(arrow10Var.get()), int(bomb1Var.get()), int(bomb3Var.get()), int(rupee1Var.get()), int(rupee5Var.get()), int(rupee20Var.get()), int(rupee50Var.get()), int(rupee100Var.get()), int(rupee300Var.get()), int(rupoorVar.get()), int(blueclockVar.get()), int(greenclockVar.get()), int(redclockVar.get()), int(triforcepieceVar.get()), int(triforcecountVar.get()), - int(triforceVar.get()), int(rupoorcostVar.get())] + int(triforceVar.get()), int(rupoorcostVar.get()), int(universalkeyVar.get())] guiargs.rom = romVar.get() guiargs.jsonout = None guiargs.sprite = sprite @@ -938,6 +938,14 @@ def guiMain(args=None): redclockLabel.pack(anchor=W, side=LEFT, padx=(0,14)) redclockEntry.pack(anchor=E) + universalkeyFrame = Frame(itemList5) + universalkeyLabel = Label(universalkeyFrame, text='Universal Key') + universalkeyVar = StringVar(value='0') + universalkeyEntry = Entry(universalkeyFrame, textvariable=universalkeyVar, width=3, validate='all', vcmd=vcmd) + universalkeyFrame.pack() + universalkeyLabel.pack(anchor=W, side=LEFT, padx=(0,57)) + universalkeyEntry.pack(anchor=E) + triforcepieceFrame = Frame(itemList5) triforcepieceLabel = Label(triforcepieceFrame, text='Triforce Piece') triforcepieceVar = StringVar(value='0') diff --git a/ItemList.py b/ItemList.py index 0f1ebf3d..f95f9cb6 100644 --- a/ItemList.py +++ b/ItemList.py @@ -220,7 +220,7 @@ def generate_itempool(world): # set up item pool if world.custom: - (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.customitemarray) + (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.retro, world.customitemarray) world.rupoor_cost = min(world.customitemarray[67], 9999) else: (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle, world.difficulty, world.timer, world.goal, world.mode, world.retro) @@ -393,7 +393,7 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, retro): pool.extend(diff.retro) return (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) -def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, customitemarray): +def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, retro, customitemarray): pool = [] placed_items = [] clock_mode = None @@ -410,6 +410,7 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, c for x in range(0, 65): itemtotal = itemtotal + customitemarray[x] itemtotal = itemtotal + customitemarray[66] + itemtotal = itemtotal + customitemarray[68] pool.extend(['Bow'] * customitemarray[0]) pool.extend(['Silver Arrows']* customitemarray[1]) @@ -472,6 +473,7 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, c pool.extend(['Red Clock'] * customitemarray[63]) pool.extend(['Triforce Piece'] * customitemarray[64]) pool.extend(['Triforce'] * customitemarray[66]) + pool.extend(['Small Key (Universal)'] * customitemarray[68]) diff = difficulties[difficulty] @@ -530,6 +532,8 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, c pool.extend(['Magic Mirror'] * customitemarray[22]) pool.extend(['Moon Pearl'] * customitemarray[28]) + if retro: + itemtotal = itemtotal - 28 # Corrects for small keys not being in item pool in Retro Mode if itemtotal < total_items_to_place: pool.extend(['Nothing'] * (total_items_to_place - itemtotal)) From a6ec622e7abd9791290cc12894567a65d1ef7186 Mon Sep 17 00:00:00 2001 From: AmazingAmpharos Date: Fri, 16 Mar 2018 12:26:14 -0500 Subject: [PATCH 6/6] Final v29 tweaks -Makes Retro Mode and Standard Mode compatible. -Replaces a Bombs (3) with Bombs (10) in all modes. -Introduces Silver Arrows to Expert Mode. --- ItemList.py | 37 ++++++++++++++++++++++++------------- Items.py | 1 + 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/ItemList.py b/ItemList.py index f95f9cb6..488ac33c 100644 --- a/ItemList.py +++ b/ItemList.py @@ -20,7 +20,7 @@ hardbottles = ['Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Bottle normalbaseitems = (['Silver Arrows', 'Magic Upgrade (1/2)', 'Single Arrow', 'Sanctuary Heart Container', 'Arrow Upgrade (+10)', 'Bomb Upgrade (+10)'] + ['Rupees (300)'] * 4 + ['Boss Heart Container'] * 10 + ['Piece of Heart'] * 24) normalfirst15extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Arrow Upgrade (+5)'] * 6 + ['Bomb Upgrade (+5)'] * 6 -normalsecond15extra = ['Bombs (3)'] * 10 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupee (1)'] +normalsecond15extra = ['Bombs (3)'] * 9 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupee (1)'] + ['Bombs (10)'] normalthird10extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 3 + ['Arrows (10)', 'Rupee (1)', 'Rupees (5)'] normalfourth5extra = ['Arrows (10)'] * 2 + ['Rupees (20)'] * 2 + ['Rupees (5)'] normalfinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2 @@ -31,27 +31,27 @@ easybaseitems = (['Sanctuary Heart Container'] + ['Rupees (300)'] * 4 + ['Magic easyextra = ['Piece of Heart'] * 12 + ['Rupees (300)'] easylimitedextra = ['Boss Heart Container'] * 3 # collapsing down the 12 pieces of heart easyfirst15extra = ['Rupees (100)', 'Arrow Upgrade (+10)', 'Bomb Upgrade (+10)'] + ['Arrow Upgrade (+5)'] * 6 + ['Bomb Upgrade (+5)'] * 6 -easysecond10extra = ['Bombs (3)'] * 8 + ['Rupee (1)', 'Rupees (50)'] +easysecond10extra = ['Bombs (3)'] * 7 + ['Rupee (1)', 'Rupees (50)', 'Bombs (10)'] easythird5extra = ['Rupees (50)'] * 2 + ['Bombs (3)'] * 2 + ['Arrows (10)'] easyfinal25extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 14 + ['Rupee (1)'] + ['Arrows (10)'] * 4 + ['Rupees (5)'] * 2 easytimedotherextra = ['Red Clock'] * 5 -hardbaseitems = ['Silver Arrows', 'Single Arrow'] + ['Rupees (300)'] * 4 + ['Boss Heart Container'] * 6 + ['Piece of Heart'] * 20 + ['Rupees (5)'] * 7 + ['Bombs (3)'] * 5 +hardbaseitems = ['Silver Arrows', 'Single Arrow', 'Bombs (10)'] + ['Rupees (300)'] * 4 + ['Boss Heart Container'] * 6 + ['Piece of Heart'] * 20 + ['Rupees (5)'] * 7 + ['Bombs (3)'] * 4 hardfirst20extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Bombs (3)'] * 5 + ['Rupees (5)'] * 10 + ['Arrows (10)', 'Rupee (1)'] hardsecond10extra = ['Rupees (5)'] * 5 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupee (1)'] hardthird10extra = ['Rupees (50)'] * 4 + ['Rupees (20)'] * 3 + ['Rupees (5)'] * 3 hardfourth10extra = ['Arrows (10)'] * 2 + ['Rupees (20)'] * 7 + ['Rupees (5)'] hardfinal20extra = ['Rupees (20)'] * 18 + ['Rupees (5)'] * 2 -expertbaseitems = (['Rupees (300)'] * 4 + ['Single Arrow', 'Boss Heart Container', 'Rupee (1)'] + ['Piece of Heart'] * 20 + ['Rupees (5)'] * 3 + - ['Bombs (3)'] * 10 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupees (20)'] * 2) +expertbaseitems = (['Rupees (300)'] * 4 + ['Single Arrow', 'Silver Arrows', 'Boss Heart Container', 'Rupee (1)', 'Bombs (10)'] + ['Piece of Heart'] * 20 + ['Rupees (5)'] * 2 + + ['Bombs (3)'] * 9 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupees (20)'] * 2) expertfirst15extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Rupees (5)'] * 12 expertsecond15extra = ['Rupees (5)'] * 10 + ['Rupees (20)'] * 5 expertthird10extra = ['Rupees (50)'] * 4 + ['Rupees (5)'] * 2 + ['Arrows (10)'] * 3 + ['Rupee (1)'] expertfourth5extra = ['Rupees (5)'] * 5 expertfinal25extra = ['Rupees (20)'] * 23 + ['Rupees (5)'] * 2 -insanebaseitems = ['Rupees (300)'] * 4 + ['Single Arrow'] + ['Rupees (5)'] * 24 + ['Bombs (3)'] * 10 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupee (1)'] + ['Rupees (20)'] * 5 +insanebaseitems = ['Rupees (300)'] * 4 + ['Single Arrow', 'Bombs (10)', 'Rupee (1)'] + ['Rupees (5)'] * 24 + ['Bombs (3)'] * 9 + ['Rupees (50)'] * 2 + ['Arrows (10)'] * 2 + ['Rupees (20)'] * 5 insanefirst15extra = ['Rupees (100)', 'Rupees (300)', 'Rupees (50)'] + ['Rupees (5)'] * 12 insanesecond15extra = ['Rupees (5)'] * 10 + ['Rupees (20)'] * 5 insanethird10extra = ['Rupees (50)'] * 4 + ['Rupees (5)'] * 2 + ['Arrows (10)'] * 3 + ['Rupee (1)'] @@ -97,7 +97,7 @@ difficulties = { timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, triforcehunt = ['Triforce Piece'] * 30, triforce_pieces_required = 20, - retro = ['Small Key (Universal)'] * 18 + ['Rupees (20)'] * 10, + retro = ['Small Key (Universal)'] * 17 + ['Rupees (20)'] * 10, conditional_extras = no_conditional_extras, extras = [normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra], progressive_sword_limit = 4, @@ -121,7 +121,7 @@ difficulties = { timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 5, # +5 more Red Clocks if there is room triforcehunt = ['Triforce Piece'] * 30, triforce_pieces_required = 20, - retro = ['Small Key (Universal)'] * 28, + retro = ['Small Key (Universal)'] * 27, conditional_extras = easy_conditional_extras, extras = [easyextra, easyfirst15extra, easysecond10extra, easythird5extra, easyfinal25extra], progressive_sword_limit = 4, @@ -145,7 +145,7 @@ difficulties = { timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, triforcehunt = ['Triforce Piece'] * 30, triforce_pieces_required = 20, - retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15, + retro = ['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 15, conditional_extras = no_conditional_extras, extras = [hardfirst20extra, hardsecond10extra, hardthird10extra, hardfourth10extra, hardfinal20extra], progressive_sword_limit = 3, @@ -162,14 +162,14 @@ difficulties = { basicshield = ['Progressive Shield'] * 3, #only the first one will upgrade, making this equivalent to two blue shields progressivearmor = [], basicarmor = [], - swordless = ['Rupees (20)'] * 3 + ['Silver Arrows'], + swordless = ['Rupees (20)'] * 4, progressivesword = ['Progressive Sword'] * 3, basicsword = ['Fighter Sword', 'Master Sword', 'Master Sword'], timedohko = ['Green Clock'] * 20 + ['Red Clock'] * 5, timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, triforcehunt = ['Triforce Piece'] * 30, triforce_pieces_required = 20, - retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15, + retro = ['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 15, conditional_extras = no_conditional_extras, extras = [expertfirst15extra, expertsecond15extra, expertthird10extra, expertfourth5extra, expertfinal25extra], progressive_sword_limit = 2, @@ -193,7 +193,7 @@ difficulties = { timedother = ['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10, triforcehunt = ['Triforce Piece'] * 30, triforce_pieces_required = 20, - retro = ['Small Key (Universal)'] * 13 + ['Rupees (5)'] * 15, + retro = ['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 15, conditional_extras = no_conditional_extras, extras = [insanefirst15extra, insanesecond15extra, insanethird10extra, insanefourth5extra, insanefinal25extra], progressive_sword_limit = 2, @@ -391,6 +391,11 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, retro): pool = [item.replace('Arrow Upgrade (+5)','Rupees (5)') for item in pool] pool = [item.replace('Arrow Upgrade (+10)','Rupees (5)') for item in pool] pool.extend(diff.retro) + if mode == 'standard': + key_location = random.choice(['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross']) + placed_items.append((key_location, 'Small Key (Universal)')) + else: + pool.extend(['Small Key (Universal)']) return (pool, placed_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, retro, customitemarray): @@ -473,7 +478,6 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, r pool.extend(['Red Clock'] * customitemarray[63]) pool.extend(['Triforce Piece'] * customitemarray[64]) pool.extend(['Triforce'] * customitemarray[66]) - pool.extend(['Small Key (Universal)'] * customitemarray[68]) diff = difficulties[difficulty] @@ -519,9 +523,16 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, r placed_items.append(('Link\'s Uncle', 'Progressive Sword')) pool.extend(['Fighter Sword'] * customitemarray[32]) pool.extend(['Progressive Sword'] * max((customitemarray[36] - 1), 0)) + if retro: + key_location = random.choice(['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross']) + placed_items.append((key_location, 'Small Key (Universal)')) + pool.extend(['Small Key (Universal)'] * max((customitemarray[68] - 1), 0)) + else: + pool.extend(['Small Key (Universal)'] * customitemarray[68]) else: pool.extend(['Fighter Sword'] * customitemarray[32]) pool.extend(['Progressive Sword'] * customitemarray[36]) + pool.extend(['Small Key (Universal)'] * customitemarray[68]) if shuffle == 'insanity_legacy': placed_items.append(('Link\'s House', 'Magic Mirror')) diff --git a/Items.py b/Items.py index 473f3fe9..55cc26e2 100644 --- a/Items.py +++ b/Items.py @@ -78,6 +78,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 'You have\nchosen the\narcher cla 'Arrow Upgrade (+5)': (False, False, None, 0x53, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again'), 'Single Bomb': (False, False, None, 0x27, 'I make things\ngo BOOM! But\njust once.', 'and the explosion', 'the bomb-holding kid', 'firecracker for sale', 'blend fungus into bomb', '\'splosion boy explodes again'), 'Bombs (3)': (False, False, None, 0x28, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again'), + 'Bombs (10)': (False, False, None, 0x31, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again'), 'Bomb Upgrade (+10)': (False, False, None, 0x52, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again'), 'Bomb Upgrade (+5)': (False, False, None, 0x51, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again'), 'Blue Mail': (False, True, None, 0x22, 'Now you\'re a\nblue elf!', 'and the banana hat', 'the protected kid', 'banana hat for sale', 'the clothing store', 'tailor boy banana hatted again'),