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:
parent
fcc97cef81
commit
9c6d649996
|
@ -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)
|
||||
|
|
|
@ -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='''\
|
||||
|
|
6
Items.py
6
Items.py
|
@ -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),
|
||||
|
|
9
Main.py
9
Main.py
|
@ -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
4
Rom.py
|
@ -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':
|
||||
|
|
39
Rules.py
39
Rules.py
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue