Updating logic and features to better match VT26

Updates include:

* Allow acticating tablets with hammer
* Remove 1/4 magic from the normal mode pool
* Incorporate OHKO compatible cape/cane-of-byrna requirements
* Upgrade Mirror Shield, Progressive Shield, and Bug Catching Net to
qualify as advancement items
* Allow Mirror shield as alternative to cape/byrna for Laser Bridge
* Prevent Waterfall Fairy from upgrading Boomerang or Shield
* Change PoD key logic to match VT26.
* Add restrictions on small key placement (to match VT26). I think these
restrictions may be redundant, but I'm adding them just in case any of
them are not.
This commit is contained in:
Kevin Cathcart 2017-10-14 14:45:59 -04:00
parent fcc97cef81
commit 9c6d649996
6 changed files with 51 additions and 22 deletions

View File

@ -372,6 +372,18 @@ class CollectionState(object):
else:
self.prog_items.append('Power Glove')
changed = True
elif 'Shield' in item.name:
if self.has('Mirror Shield'):
pass
elif self.has('Red Shield'):
self.prog_items.append('Mirror Shield')
changed = True
elif self.has('Blue Shield'):
self.prog_items.append('Red Shield')
changed = True
else:
self.prog_items.append('Blue Shield')
changed = True
elif event or item.advancement:
self.prog_items.append(item.name)

View File

@ -36,8 +36,7 @@ if __name__ == '__main__':
Agahnim\'s Tower barrier can be destroyed with
hammer. Misery Mire and Turtle Rock can be opened
without a sword. Hammer damages Ganon. Ether and
Bombos Tablet are unreachable but contain trash items
always.
Bombos Tablet can be activated with Hammer (and Book).
''')
parser.add_argument('--goal', default='ganon', const='ganon', nargs='?', choices=['ganon', 'pedestal', 'dungeons', 'triforcehunt', 'crystals'],
help='''\

View File

@ -85,9 +85,9 @@ item_table = {'Bow': (True, False, None, 0x0B, 'You have\nchosen the\narcher cla
'Red Boomerang': (False, True, None, 0x2A, 'No matter what\nyou do, red\nreturns to you', 'and the badmarang', None, None, None, None),
'Blue Shield': (False, True, None, 0x04, 'Now you can\ndefend against\npebbles!', 'and the stone blocker', None, None, None, None),
'Red Shield': (False, True, None, 0x05, 'Now you can\ndefend against\nfireballs!', 'and the shot blocker', None, None, None, None),
'Mirror Shield': (False, True, None, 0x06, 'Now you can\ndefend against\nlasers!', 'and the laser blocker', None, None, None, None),
'Progressive Shield': (False, True, None, 0x5F, 'have a better\nblocker in\nfront of you', 'and the new shield', None, None, None, None),
'Bug Catching Net': (False, True, None, 0x21, 'Let\'s catch\nsome bees and\nfaeries!', 'and the bee catcher', None, None, None, None),
'Mirror Shield': (True, False, None, 0x06, 'Now you can\ndefend against\nlasers!', 'and the laser blocker', None, None, None, None),
'Progressive Shield': (True, False, None, 0x5F, 'have a better\nblocker in\nfront of you', 'and the new shield', None, None, None, None),
'Bug Catching Net': (True, False, None, 0x21, 'Let\'s catch\nsome bees and\nfaeries!', 'and the bee catcher', None, None, None, None),
'Cane of Byrna': (True, False, None, 0x18, 'Use this to\nbecome\ninvincible!', 'and the bad cane', None, None, None, None),
'Boss Heart Container': (False, False, None, 0x3E, 'Maximum health\nincreased!\nYeah!', 'and the love', None, None, None, None),
'Sanctuary Heart Container': (False, False, None, 0x3F, 'Maximum health\nincreased!\nYeah!', 'and the love', None, None, None, None),

View File

@ -158,9 +158,7 @@ def generate_itempool(world):
['Arrows (10)'] * 5 + ['Bombs (3)'] * 10)
if world.mode == 'swordless':
world.push_item('Ether Tablet', ItemFactory('Rupees (20)'), False)
world.push_item('Bombos Tablet', ItemFactory('Rupees (20)'), False)
world.itempool.extend(ItemFactory(['Rupees (20)', 'Rupees (20)']))
world.itempool.extend(ItemFactory(['Rupees (20)'] * 4))
elif world.mode == 'standard':
world.push_item('Uncle', ItemFactory('Progressive Sword'), False)
world.get_location('Uncle').event = True
@ -184,10 +182,7 @@ def generate_itempool(world):
world.treasure_hunt_icon = 'Triforce Piece'
world.itempool.extend(ItemFactory(['Triforce Piece'] * 30))
if random.randint(0, 3) == 0:
world.itempool.append(ItemFactory('Magic Upgrade (1/4)'))
else:
world.itempool.append(ItemFactory('Magic Upgrade (1/2)'))
world.itempool.append(ItemFactory('Magic Upgrade (1/2)'))
# shuffle medallions
mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)]

4
Rom.py
View File

@ -252,12 +252,16 @@ def patch_rom(world, rom, hashtable, beep='normal', sprite=None):
202, 33, 98, 255, 38, 131, 187, 35, 250, 195, 35, 250, 187, 43, 250, 195, 43, 250, 187, 83, 250, 195, 83, 250, 176, 160, 61, 152, 19, 192, 152, 82,
192, 136, 0, 96, 144, 0, 96, 232, 0, 96, 240, 0, 96, 152, 202, 192, 216, 202, 192, 216, 19, 192, 216, 82, 192, 252, 189, 133, 253, 29, 135, 255,
255, 255, 255, 240, 255, 128, 46, 97, 14, 129, 14, 255, 255])
# set Waterfall fairy prizes to be disappointing
rom.write_byte(0x348DB, 0x3A) # Red Boomerang becomes Red Boomerang
rom.write_byte(0x348EB, 0x05) # Blue Shield becomes Blue Shield
# set swordless mode settings
rom.write_byte(0x18003F, 0x01 if world.mode == 'swordless' else 0x00) # hammer can harm ganon
rom.write_byte(0x180040, 0x01 if world.mode == 'swordless' else 0x00) # open curtains
rom.write_byte(0x180041, 0x01 if world.mode == 'swordless' else 0x00) # swordless medallions
rom.write_byte(0x180043, 0xFF if world.mode == 'swordless' else 0x00) # starting sword for link
rom.write_byte(0x180044, 0x01 if world.mode == 'swordless' else 0x00) # hammer activates tablets
# set up clocks for timed modes
if world.clock_mode == 'off':

View File

@ -53,6 +53,13 @@ def add_lamp_requirement(spot):
def forbid_item(location, item):
old_rule = location.item_rule
location.item_rule = lambda i: i.name != item and old_rule(i)
def item_in_locations(state, item, locations):
for location in locations:
loc=state.world.get_location(location)
if loc.item is not None and loc.item.name == item:
return True
return False
def global_rules(world):
@ -168,7 +175,7 @@ def global_rules(world):
set_rule(world.get_entrance('Dark Death Mountain Ascend (Bottom)'), lambda state: state.has_Pearl())
set_rule(world.get_entrance('Cave Shop (Dark Death Mountain)'), lambda state: state.has_Pearl()) # just for save bunny algo for now
set_rule(world.get_entrance('Dark Death Mountain Ascend Exit (Bottom)'), lambda state: False) # Cannot get to bottom exit from top. Just exists for shuffling
set_rule(world.get_location('[cave-055] Spike Cave'), lambda state: state.has('Hammer') and state.can_lift_rocks() and (state.has('Cane of Byrna') or state.has('Cape')))
set_rule(world.get_location('[cave-055] Spike Cave'), lambda state: state.has('Hammer') and state.can_lift_rocks() and (state.has('Cane of Byrna') or state.has('Cape')) and (state.has('Bottle') or state.has('Half Magic') or state.has('Quarter Magic')))
set_rule(world.get_location('[cave-056] Hookshot Cave [top right chest]'), lambda state: state.has('Hookshot'))
set_rule(world.get_location('[cave-056] Hookshot Cave [top left chest]'), lambda state: state.has('Hookshot'))
set_rule(world.get_location('[cave-056] Hookshot Cave [bottom right chest]'), lambda state: state.has('Hookshot') or state.has('Pegasus Boots'))
@ -195,6 +202,8 @@ def global_rules(world):
(state.has_blunt_weapon() or state.has('Fire Rod') or state.has('Ice Rod') or state.has('Bow')))
for location in ['Lanmolas - Heart Container', '[dungeon-L2-B1] Desert Palace - Big Chest']:
forbid_item(world.get_location(location), 'Big Key (Desert Palace)')
for location in ['Lanmolas - Heart Container', '[dungeon-L2-B1] Desert Palace - Big Key Room', '[dungeon-L2-B1] Desert Palace - Compass Room']:
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)'))
set_rule(world.get_entrance('Tower of Hera Big Key Door'), lambda state: state.has('Big Key (Tower of Hera)'))
@ -204,6 +213,8 @@ def global_rules(world):
set_rule(world.get_location('Moldorm - Pendant'), lambda state: state.has_blunt_weapon())
for location in ['Moldorm - Heart Container', '[dungeon-L3-1F] Tower of Hera - Big Chest', '[dungeon-L3-1F] Tower of Hera - 4F [small chest]']:
forbid_item(world.get_location(location), 'Big Key (Tower of Hera)')
for location in ['[dungeon-L3-1F] Tower of Hera - Basement']:
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)'))
@ -221,18 +232,20 @@ def global_rules(world):
set_rule(world.get_location('[dungeon-D4-1F] Thieves Town - Room above Boss'), lambda state: state.has('Small Key (Thieves Town)'))
for location in ['[dungeon-D4-1F] Thieves Town - Room above Boss', '[dungeon-D4-B2] Thieves Town - Big Chest', '[dungeon-D4-B2] Thieves Town - Chest next to Blind', 'Blind - Heart Container']:
forbid_item(world.get_location(location), 'Big Key (Thieves Town)')
for location in ['[dungeon-D4-1F] Thieves Town - Room above Boss', '[dungeon-D4-B2] Thieves Town - Big Chest', 'Blind - Heart Container']:
forbid_item(world.get_location(location), 'Small Key (Thieves Town)')
set_rule(world.get_location('[dungeon-D3-B1] Skull Woods - Big Chest'), lambda state: state.has('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
for location in ['[dungeon-D3-B1] Skull Woods - Big Chest']:
forbid_item(world.get_location(location), 'Big Key (Skull Woods)')
for location in ['Mothula - Heart Container']:
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('[dungeon-D5-B5] 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 (state.has('Small Key (Ice Palace)', 1) and ((state.world.get_location('[dungeon-D5-B3] Ice Palace - Spike Room').item is not None and state.world.get_location('[dungeon-D5-B3] Ice Palace - Spike Room').item.name in ['Big Key (Ice Palace)']) or
(state.world.get_location('[dungeon-D5-B1] Ice Palace - Big Key Room').item is not None and state.world.get_location('[dungeon-D5-B1] Ice Palace - Big Key Room').item.name in ['Big Key (Ice Palace)']) or
(state.world.get_location('[dungeon-D5-B2] Ice Palace - Map Room').item is not None and state.world.get_location('[dungeon-D5-B2] Ice Palace - Map Room').item.name in ['Big Key (Ice Palace)'])))) # if you do ipbj and waste SKs in the basement, you have to BJ over the hookshot room to fix your mess potentially. This seems fair
set_rule(world.get_entrance('Ice Palace (East)'), lambda state: (state.has('Hookshot') or (item_in_locations(state,'Big Key (Ice Palace)',['[dungeon-D5-B3] Ice Palace - Spike Room','[dungeon-D5-B1] Ice Palace - Big Key Room','[dungeon-D5-B2] Ice Palace - Map Room']) and state.has('Small Key (Ice Palace)')) or state.has('Small Key (Ice Palace)',2)) and (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 ['[dungeon-D5-B5] Ice Palace - Big Chest', 'Kholdstare - Heart Container']:
forbid_item(world.get_location(location), 'Big Key (Ice Palace)')
@ -266,10 +279,10 @@ def global_rules(world):
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) (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('[dungeon-D7-B2] Turtle Rock - Eye Bridge Room [bottom left chest]'), lambda state: state.has('Cane of Byrna') or state.has('Cape'))
set_rule(world.get_location('[dungeon-D7-B2] Turtle Rock - Eye Bridge Room [bottom right chest]'), lambda state: state.has('Cane of Byrna') or state.has('Cape'))
set_rule(world.get_location('[dungeon-D7-B2] Turtle Rock - Eye Bridge Room [top left chest]'), lambda state: state.has('Cane of Byrna') or state.has('Cape'))
set_rule(world.get_location('[dungeon-D7-B2] Turtle Rock - Eye Bridge Room [top right chest]'), lambda state: state.has('Cane of Byrna') or state.has('Cape'))
set_rule(world.get_location('[dungeon-D7-B2] Turtle Rock - Eye Bridge Room [bottom left chest]'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield'))
set_rule(world.get_location('[dungeon-D7-B2] Turtle Rock - Eye Bridge Room [bottom right chest]'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield'))
set_rule(world.get_location('[dungeon-D7-B2] Turtle Rock - Eye Bridge Room [top left chest]'), lambda state: state.has('Cane of Byrna') or state.has('Cape') or state.has('Mirror Shield'))
set_rule(world.get_location('[dungeon-D7-B2] Turtle Rock - Eye Bridge Room [top right chest]'), 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
(state.has('Hammer') or state.has_beam_sword() or state.has('Bottle') or state.has('Half Magic') or state.has('Quarter Magic')))
set_trock_key_rules(world)
@ -285,6 +298,8 @@ def global_rules(world):
set_rule(world.get_location('[dungeon-D1-1F] Dark Palace - Big Chest'), lambda state: state.has('Big Key (Palace of Darkness)'))
for location in ['[dungeon-D1-1F] Dark Palace - Big Chest', 'Helmasaur - Heart Container']:
forbid_item(world.get_location(location), 'Big Key (Palace of Darkness)')
for location in ['[dungeon-D1-1F] Dark Palace - Big Chest', '[dungeon-D1-1F] Dark Palace - Maze Room [top chest]', '[dungeon-D1-1F] Dark Palace - Maze Room [bottom chest]']:
forbid_item(world.get_location(location), 'Small Key (Palace of Darkness)')
# these key rules are conservative, you might be able to get away with more lenient rules
set_rule(world.get_location('[dungeon-A2-1F] Ganons Tower - Torch'), lambda state: state.has_Boots())
@ -382,8 +397,8 @@ def open_rules(world):
def swordless_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'))
set_rule(world.get_location('Ether Tablet'), lambda state: True) # will have fixed rupee drop, unobtainable
set_rule(world.get_location('Bombos Tablet'), lambda state: True) # will have fixed rupee drop, unobtainable
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
@ -438,6 +453,10 @@ def set_trock_key_rules(world):
for location in non_big_key_locations:
forbid_item(world.get_location(location), 'Big Key (Turtle Rock)')
# small key restriction
for location in ['Trinexx - Heart Container']:
forbid_item(world.get_location(location), 'Small Key (Turtle Rock)')
def set_big_bomb_rules(world):