first pass at owg logic support
This commit is contained in:
parent
636a18cee9
commit
c09fab64f8
|
@ -28,14 +28,17 @@ def parse_arguments(argv, no_defaults=False):
|
|||
|
||||
parser = argparse.ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument('--create_spoiler', help='Output a Spoiler File', action='store_true')
|
||||
parser.add_argument('--logic', default=defval('noglitches'), const='noglitches', nargs='?', choices=['noglitches', 'minorglitches', 'nologic'],
|
||||
parser.add_argument('--logic', default=defval('noglitches'), const='noglitches', nargs='?', choices=['noglitches', 'minorglitches', 'owglitches', 'nologic'],
|
||||
help='''\
|
||||
Select Enforcement of Item Requirements. (default: %(default)s)
|
||||
No Glitches:
|
||||
Minor Glitches: May require Fake Flippers, Bunny Revival
|
||||
and Dark Room Navigation.
|
||||
Overworld Glitches: May require overworld glitches. Starts with
|
||||
boots.
|
||||
No Logic: Distribute items without regard for
|
||||
item requirements.
|
||||
item requirements. Starts with
|
||||
boots
|
||||
''')
|
||||
parser.add_argument('--mode', default=defval('open'), const='open', nargs='?', choices=['standard', 'open', 'inverted'],
|
||||
help='''\
|
||||
|
|
|
@ -2987,7 +2987,8 @@ mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central
|
|||
('Pyramid Drop', 'East Dark World')
|
||||
]
|
||||
|
||||
inverted_mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'),
|
||||
inverted_mandatory_connections = [('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'),
|
||||
('Lake Hylia Island', 'Lake Hylia Island'),
|
||||
('Zoras River', 'Zoras River'),
|
||||
('Kings Grave Outer Rocks', 'Kings Grave Area'),
|
||||
('Kings Grave Inner Rocks', 'Light World'),
|
||||
|
|
2
Gui.py
2
Gui.py
|
@ -193,7 +193,7 @@ def guiMain(args=None):
|
|||
logicFrame = Frame(drowDownFrame)
|
||||
logicVar = StringVar()
|
||||
logicVar.set('noglitches')
|
||||
logicOptionMenu = OptionMenu(logicFrame, logicVar, 'noglitches', 'minorglitches', 'nologic')
|
||||
logicOptionMenu = OptionMenu(logicFrame, logicVar, 'noglitches', 'minorglitches', 'owglitches', 'nologic')
|
||||
logicOptionMenu.pack(side=RIGHT)
|
||||
logicLabel = Label(logicFrame, text='Game logic')
|
||||
logicLabel.pack(side=LEFT)
|
||||
|
|
|
@ -9,7 +9,7 @@ def create_inverted_regions(world, player):
|
|||
["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Kings Grave Outer Rocks', 'Dam',
|
||||
'Inverted Big Bomb Shop', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave',
|
||||
'Blacksmiths Hut', 'Bat Cave Drop Ledge', 'Bat Cave Cave', 'Sick Kids House', 'Hobo Bridge', 'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump',
|
||||
'Lumberjack Tree Tree', 'Lumberjack Tree Cave', 'Mini Moldorm Cave', 'Ice Rod Cave', 'Lake Hylia Central Island Pier',
|
||||
'Lumberjack Tree Tree', 'Lumberjack Tree Cave', 'Mini Moldorm Cave', 'Ice Rod Cave', 'Lake Hylia Central Island Pier', 'Lake Hylia Island',
|
||||
'Bonk Rock Cave', 'Library', 'Two Brothers House (East)', 'Desert Palace Stairs', 'Eastern Palace', 'Master Sword Meadow',
|
||||
'Sanctuary', 'Sanctuary Grave', 'Death Mountain Entrance Rock', 'Light World River Drop',
|
||||
'Elder House (East)', 'Elder House (West)', 'North Fairy Cave', 'North Fairy Cave Drop', 'Lost Woods Gamble', 'Snitch Lady (East)', 'Snitch Lady (West)', 'Tavern (Front)',
|
||||
|
|
34
ItemList.py
34
ItemList.py
|
@ -177,7 +177,7 @@ def generate_itempool(world, player):
|
|||
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.customitemarray)
|
||||
world.rupoor_cost = min(world.customitemarray[69], 9999)
|
||||
else:
|
||||
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player])
|
||||
(pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.logic[player])
|
||||
|
||||
for item in precollected_items:
|
||||
world.push_precollected(ItemFactory(item, player))
|
||||
|
@ -386,7 +386,7 @@ def set_up_shops(world, player):
|
|||
|
||||
#special shop types
|
||||
|
||||
def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro):
|
||||
def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, logic):
|
||||
pool = []
|
||||
placed_items = {}
|
||||
precollected_items = []
|
||||
|
@ -403,6 +403,11 @@ def get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, r
|
|||
def want_progressives():
|
||||
return random.choice([True, False]) if progressive == 'random' else progressive == 'on'
|
||||
|
||||
# provide boots to major glitch dependent seeds
|
||||
if logic in ['owglitches', 'nologic']:
|
||||
precollected_items.append('Pegasus Boots')
|
||||
pool.remove('Pegasus Boots')
|
||||
|
||||
if want_progressives():
|
||||
pool.extend(progressivegloves)
|
||||
else:
|
||||
|
@ -685,19 +690,20 @@ def test():
|
|||
for progressive in ['on', 'off']:
|
||||
for shuffle in ['full', 'insanity_legacy']:
|
||||
for retro in [True, False]:
|
||||
out = get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro)
|
||||
count = len(out[0]) + len(out[1])
|
||||
for logic in ['noglitches', 'owglitches']:
|
||||
out = get_pool_core(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, logic)
|
||||
count = len(out[0]) + len(out[1])
|
||||
|
||||
correct_count = total_items_to_place
|
||||
if goal == 'pedestal' and swords != 'vanilla':
|
||||
# pedestal goals generate one extra item
|
||||
correct_count += 1
|
||||
if retro:
|
||||
correct_count += 28
|
||||
try:
|
||||
assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro))
|
||||
except AssertionError as e:
|
||||
print(e)
|
||||
correct_count = total_items_to_place
|
||||
if goal == 'pedestal' and swords != 'vanilla':
|
||||
# pedestal goals generate one extra item
|
||||
correct_count += 1
|
||||
if retro:
|
||||
correct_count += 28
|
||||
try:
|
||||
assert count == correct_count, "expected {0} items but found {1} items for {2}".format(correct_count, count, (progressive, shuffle, difficulty, timer, goal, mode, swords, retro))
|
||||
except AssertionError as e:
|
||||
print(e)
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
|
|
|
@ -114,10 +114,10 @@ def roll_settings(weights):
|
|||
ret = argparse.Namespace()
|
||||
|
||||
glitches_required = get_choice('glitches_required')
|
||||
if glitches_required not in ['none', 'no_logic']:
|
||||
print("Only NMG and No Logic supported")
|
||||
if glitches_required not in ['none', 'no_logic', 'overworld_glitches']:
|
||||
print("Only NMG, OWG and No Logic supported")
|
||||
glitches_required = 'none'
|
||||
ret.logic = {'none': 'noglitches', 'no_logic': 'nologic'}[glitches_required]
|
||||
ret.logic = {'none': 'noglitches', 'no_logic': 'nologic', 'overworld_glitches': 'owglitches'}[glitches_required]
|
||||
|
||||
item_placement = get_choice('item_placement')
|
||||
# not supported in ER
|
||||
|
|
8
Rom.py
8
Rom.py
|
@ -901,7 +901,7 @@ def patch_rom(world, player, rom, enemized):
|
|||
rom.write_byte(x, 0) # Zero the initial equipment array
|
||||
rom.write_byte(0x18302C, 0x18) # starting max health
|
||||
rom.write_byte(0x18302D, 0x18) # starting current health
|
||||
rom.write_byte(0x183039, 0x68) # starting abilities, bit array
|
||||
ability_flags = 0x68 # starting abilities, bit array; may be modified by precollected items
|
||||
|
||||
for item in world.precollected_items:
|
||||
if item.player != player:
|
||||
|
@ -911,9 +911,15 @@ def patch_rom(world, player, rom, enemized):
|
|||
rom.write_byte(0x183000+0x19, 0x01)
|
||||
rom.write_byte(0x0271A6+0x19, 0x01)
|
||||
rom.write_byte(0x180043, 0x01) # special starting sword byte
|
||||
elif item.name == 'Pegasus Boots':
|
||||
rom.write_byte(0x183015, 0x01)
|
||||
ability_flags |= 0b00000100
|
||||
else:
|
||||
raise RuntimeError("Unsupported pre-collected item: {}".format(item))
|
||||
|
||||
# write abilities after ability flags have been determined
|
||||
rom.write_byte(0x183039, ability_flags)
|
||||
|
||||
rom.write_byte(0x18004A, 0x00 if world.mode[player] != 'inverted' else 0x01) # Inverted mode
|
||||
rom.write_byte(0x18005D, 0x00) # Hammer always breaks barrier
|
||||
rom.write_byte(0x2AF79, 0xD0 if world.mode[player] != 'inverted' else 0xF0) # vortexes: Normal (D0=light to dark, F0=dark to light, 42 = both)
|
||||
|
|
84
Rules.py
84
Rules.py
|
@ -38,6 +38,8 @@ def set_rules(world, player):
|
|||
|
||||
if world.logic[player] == 'noglitches':
|
||||
no_glitches_rules(world, player)
|
||||
elif world.logic[player] == 'owglitches':
|
||||
overworld_glitches_rules(world, player)
|
||||
elif world.logic[player] == 'minorglitches':
|
||||
logging.getLogger('').info('Minor Glitches may be buggy still. No guarantee for proper logic checks.')
|
||||
else:
|
||||
|
@ -357,7 +359,6 @@ def default_rules(world, player):
|
|||
set_rule(world.get_entrance('Kings Grave Mirror Spot', player), lambda state: state.has_Pearl(player) and state.has_Mirror(player))
|
||||
# Caution: If king's grave is releaxed at all to account for reaching it via a two way cave's exit in insanity mode, then the bomb shop logic will need to be updated (that would involve create a small ledge-like Region for it)
|
||||
set_rule(world.get_entrance('Bonk Fairy (Light)', player), lambda state: state.has_Boots(player))
|
||||
set_rule(world.get_entrance('Bat Cave Drop Ledge', player), lambda state: state.has('Hammer', player))
|
||||
set_rule(world.get_entrance('Lumberjack Tree Tree', player), lambda state: state.has_Boots(player) and state.has('Beat Agahnim 1', player))
|
||||
set_rule(world.get_entrance('Bonk Rock Cave', player), lambda state: state.has_Boots(player))
|
||||
set_rule(world.get_entrance('Desert Palace Stairs', player), lambda state: state.has('Book of Mudora', player))
|
||||
|
@ -625,8 +626,10 @@ def no_glitches_rules(world, player):
|
|||
set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player) and (state.has('Hammer', player) or state.can_lift_rocks(player)))
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Ledge Drop', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player))
|
||||
else:
|
||||
set_rule(world.get_entrance('Bat Cave Drop Ledge', player), lambda state: state.has('Hammer', player))
|
||||
set_rule(world.get_entrance('Zoras River', player), lambda state: state.has_Pearl(player) and (state.has('Flippers', player) or state.can_lift_rocks(player)))
|
||||
set_rule(world.get_entrance('Lake Hylia Central Island Pier', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player)) # can be fake flippered to
|
||||
set_rule(world.get_entrance('Lake Hylia Island', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player))
|
||||
set_rule(world.get_entrance('Hobo Bridge', player), lambda state: state.has_Pearl(player) and state.has('Flippers', player))
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Drop (East)', player), lambda state: state.has('Flippers', player))
|
||||
set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.has('Flippers', player) and (state.has('Hammer', player) or state.can_lift_rocks(player)))
|
||||
|
@ -641,7 +644,10 @@ def no_glitches_rules(world, player):
|
|||
set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: False) # no glitches does not require block override
|
||||
set_rule(world.get_entrance('Paradox Cave Bomb Jump', player), lambda state: False)
|
||||
set_rule(world.get_entrance('Skull Woods First Section Bomb Jump', player), lambda state: False)
|
||||
add_conditional_lamps(world, player)
|
||||
|
||||
|
||||
def add_conditional_lamps(world, player):
|
||||
# Light cones in standard depend on which world we actually are in, not which one the location would normally be
|
||||
# We add Lamp requirements only to those locations which lie in the dark world (or everything if open
|
||||
DW_Entrances = ['Bumper Cave (Bottom)', 'Superbunny Cave (Top)', 'Superbunny Cave (Bottom)', 'Hookshot Cave', 'Bumper Cave (Top)', 'Hookshot Cave Back Entrance', 'Dark Death Mountain Ledge (East)',
|
||||
|
@ -690,6 +696,82 @@ def no_glitches_rules(world, player):
|
|||
add_lamp_requirement(world.get_entrance('Throne Room', player), player)
|
||||
|
||||
|
||||
def overworld_glitches_rules(world, player):
|
||||
# spots that are immediately accessible
|
||||
set_rule(world.get_entrance('Hobo Bridge', player), lambda state: True)
|
||||
set_rule(world.get_region('Lake Hylia Central Island', player), lambda state: True)
|
||||
set_rule(world.get_entrance('Zoras River', player), lambda state: True)
|
||||
# lw boots-accessible locations
|
||||
lw_boots_accessible_regions = [
|
||||
'Bat Cave Drop Ledge',
|
||||
'Lake Hylia Island',
|
||||
'Desert Ledge',
|
||||
'Desert Ledge (Northeast)',
|
||||
'Desert Palace Lone Stairs',
|
||||
'Desert Palace Entrance (North) Spot',
|
||||
'Death Mountain',
|
||||
'Death Mountain Return Ledge',
|
||||
'East Death Mountain (Bottom)',
|
||||
'East Death Mountain (Top)',
|
||||
'Death Mountain (Top)',
|
||||
'Spectacle Rock',
|
||||
'Death Mountain Floating Island (Light World)',
|
||||
]
|
||||
# dw boots-accessible regions
|
||||
dw_boots_accessible_regions = [
|
||||
'East Dark World',
|
||||
'Northeast Dark World',
|
||||
'West Dark World',
|
||||
'Hammer Peg Area',
|
||||
'Bumper Cave Ledge',
|
||||
'Dark Desert',
|
||||
'Dark Death Mountain (Top)',
|
||||
'Dark Death Mountain (East Bottom)',
|
||||
'Dark Death Mountain Ledge',
|
||||
'Death Mountain Floating Island (Dark World)',
|
||||
'Turtle Rock (Top)',
|
||||
]
|
||||
# set up boots-accessible regions
|
||||
if world.mode[player] != 'inverted':
|
||||
lw_boots_accessible_regions.append('Cave 45 Ledge')
|
||||
lw_boots_accessible_regions.append('Graveyard Ledge')
|
||||
# couple other random spots
|
||||
set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player) and (state.has_Mirror(player) or state.has_Boots(player)))
|
||||
set_rule(world.get_entrance('Dark Desert Teleporter', player), lambda state: (state.has('Ocarina', player) or state.has_Boots(player)) and state.can_lift_heavy_rocks(player))
|
||||
set_rule(world.get_entrance('South Hyrule Teleporter', player), lambda state: (state.has('Hammer', player) or state.has_Boots(player)) and state.can_lift_rocks(player))
|
||||
set_rule(world.get_location('Zora\'s Ledge', player), lambda state: state.has_Boots(player))
|
||||
add_rule(world.get_entrance('Ganons Tower', player), lambda state: state.has_Boots(player) and state.has_Pearl(player), 'or')
|
||||
needs_boots = lambda state: state.has_Boots(player)
|
||||
needs_boots_and_pearl = lambda state: state.has_Boots(player) and state.has_Pearl(player)
|
||||
for spot in lw_boots_accessible_regions:
|
||||
for location in world.get_region(spot, player).locations:
|
||||
add_rule(world.get_location(location, player), needs_boots_and_pearl if world.mode[player] == 'inverted' else needs_boots, 'or')
|
||||
for spot in dw_boots_accessible_regions:
|
||||
for location in world.get_region(spot, player).locations:
|
||||
add_rule(world.get_location(location, player), needs_boots if world.mode[player] == 'inverted' else needs_boots_and_pearl, 'or')
|
||||
# bunny DMD rules
|
||||
if world.mode[player] != 'inverted':
|
||||
# set up some mirror-accessible dw entrances.
|
||||
boots_and_mirror = lambda state: state.has_Boots(player) and state.has_Mirror(player)
|
||||
add_rule(world.get_entrance('Dark Sanctuary Hint', player), boots_and_mirror, 'or') # should suffice to give us west dark world access
|
||||
for spot in world.get_region('Dark Death Mountain (East Bottom)', player).locations:
|
||||
add_rule(world.get_location(spot, player), boots_and_mirror, 'or')
|
||||
# dw entrances accessible with mirror and hookshot
|
||||
mirror_hookshot_accessible_dw_locations = [
|
||||
'Pyramid Fairy',
|
||||
'Pyramid Entrance',
|
||||
'Pyramid Drop',
|
||||
]
|
||||
mirror_hookshot_accessible_dw_locations.extend(world.get_region('Dark Death Mountain Ledge', player).locations)
|
||||
for spot in mirror_hookshot_accessible_dw_locations:
|
||||
add_rule(world.get_entrance(spot, player), lambda state: state.has_Boots(player) and state.has_Mirror(player) and state.has('Hookshot', player), 'or')
|
||||
# dw entrances accessible with mirror and titans
|
||||
boots_mirror_titans = lambda state: state.has_Boots(player) and state.has_Mirror(player) and state.can_lift_heavy_rocks(player)
|
||||
add_rule(world.get_entrance('Mire Shed', player), boots_mirror_titans, 'or')
|
||||
add_rule(world.get_location('Frog', player), boots_mirror_titans, 'or')
|
||||
add_conditional_lamps(world, player)
|
||||
|
||||
|
||||
def open_rules(world, player):
|
||||
# softlock protection as you can reach the sewers small key door with a guard drop key
|
||||
set_rule(world.get_location('Hyrule Castle - Boomerang Chest', player), lambda state: state.has_key('Small Key (Escape)', player))
|
||||
|
|
Loading…
Reference in New Issue