Flute mode

And some odd fixes
This commit is contained in:
aerinon
2022-09-01 13:59:26 -06:00
parent 3975c6c65d
commit af4f8e5b4b
19 changed files with 79 additions and 51 deletions

View File

@@ -895,7 +895,7 @@ class CollectionState(object):
'Golden Sword', 'Progressive Sword', 'Progressive Glove', 'Silver Arrows', 'Green Pendant', 'Golden Sword', 'Progressive Sword', 'Progressive Glove', 'Silver Arrows', 'Green Pendant',
'Blue Pendant', 'Red Pendant', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Blue Pendant', 'Red Pendant', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5',
'Crystal 6', 'Crystal 7', 'Blue Boomerang', 'Red Boomerang', 'Blue Shield', 'Red Shield', 'Crystal 6', 'Crystal 7', 'Blue Boomerang', 'Red Boomerang', 'Blue Shield', 'Red Shield',
'Mirror Shield', 'Progressive Shield', 'Bug Catching Net', 'Cane of Byrna', 'Mirror Shield', 'Progressive Shield', 'Bug Catching Net', 'Cane of Byrna', 'Ocarina (Activated)',
'Boss Heart Container', 'Sanctuary Heart Container', 'Piece of Heart', 'Magic Upgrade (1/2)', 'Boss Heart Container', 'Sanctuary Heart Container', 'Piece of Heart', 'Magic Upgrade (1/2)',
'Magic Upgrade (1/4)'] 'Magic Upgrade (1/4)']
or item_name.startswith(('Bottle', 'Small Key', 'Big Key')) or item_name.startswith(('Bottle', 'Small Key', 'Big Key'))
@@ -1129,8 +1129,11 @@ class CollectionState(object):
return self.has('Fire Rod', player) or self.has('Lamp', player) return self.has('Fire Rod', player) or self.has('Lamp', player)
def can_flute(self, player): def can_flute(self, player):
if any(map(lambda i: i.name in ['Ocarina', 'Ocarina (Activated)'], self.world.precollected_items)):
return True
lw = self.world.get_region('Light World', player) lw = self.world.get_region('Light World', player)
return self.has('Ocarina', player) and lw.can_reach(self) and self.is_not_bunny(lw, player) return self.has('Ocarina (Activated)', player) or (self.has('Ocarina', player) and lw.can_reach(self)
and self.is_not_bunny(lw, player))
def can_melt_things(self, player): def can_melt_things(self, player):
return self.has('Fire Rod', player) or (self.has('Bombos', player) and self.has_sword(player)) return self.has('Fire Rod', player) or (self.has('Bombos', player) and self.has_sword(player))
@@ -2371,6 +2374,7 @@ class Spoiler(object):
'retro': self.world.retro, 'retro': self.world.retro,
'bombbag': self.world.bombbag, 'bombbag': self.world.bombbag,
'weapons': self.world.swords, 'weapons': self.world.swords,
'flute_mode': self.world.flute_mode,
'goal': self.world.goal, 'goal': self.world.goal,
'shuffle': self.world.shuffle, 'shuffle': self.world.shuffle,
'shuffleganon': self.world.shuffle_ganon, 'shuffleganon': self.world.shuffle_ganon,
@@ -2569,6 +2573,7 @@ class Spoiler(object):
outfile.write(f"Restricted Boss Items: {self.metadata['restricted_boss_items'][player]}\n") outfile.write(f"Restricted Boss Items: {self.metadata['restricted_boss_items'][player]}\n")
outfile.write('Difficulty: %s\n' % self.metadata['item_pool'][player]) outfile.write('Difficulty: %s\n' % self.metadata['item_pool'][player])
outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality'][player]) outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality'][player])
outfile.write(f"Flute Mode: {self.metadata['flute_mode'][player]}\n")
outfile.write(f"Shopsanity: {yn(self.metadata['shopsanity'][player])}\n") outfile.write(f"Shopsanity: {yn(self.metadata['shopsanity'][player])}\n")
outfile.write(f"Bombbag: {yn(self.metadata['bombbag'][player])}\n") outfile.write(f"Bombbag: {yn(self.metadata['bombbag'][player])}\n")
outfile.write(f"Pseudoboots: {yn(self.metadata['pseudoboots'][player])}\n") outfile.write(f"Pseudoboots: {yn(self.metadata['pseudoboots'][player])}\n")
@@ -2873,6 +2878,8 @@ boss_mode = {"none": 0, "simple": 1, "full": 2, "chaos": 3, 'random': 3, 'unique
# byte 10: settings_version # byte 10: settings_version
# byte 11: F???, ???? (flute_mode)
flute_mode = {'normal': 0, 'active': 1}
# additions # additions
# psuedoboots does not effect code # psuedoboots does not effect code
@@ -2917,7 +2924,9 @@ class Settings(object):
(rb_mode[w.restrict_boss_items[p]] << 6) | (algo_mode[w.algorithm] << 3) | (boss_mode[w.boss_shuffle[p]]), (rb_mode[w.restrict_boss_items[p]] << 6) | (algo_mode[w.algorithm] << 3) | (boss_mode[w.boss_shuffle[p]]),
settings_version]) settings_version,
flute_mode[w.flute_mode[p]] << 7])
return base64.b64encode(code, "+-".encode()).decode() return base64.b64encode(code, "+-".encode()).decode()
@staticmethod @staticmethod
@@ -2979,6 +2988,8 @@ class Settings(object):
args.restrict_boss_items[p] = r(rb_mode)[(settings[9] & 0xC0) >> 6] args.restrict_boss_items[p] = r(rb_mode)[(settings[9] & 0xC0) >> 6]
args.algorithm = r(algo_mode)[(settings[9] & 0x38) >> 3] args.algorithm = r(algo_mode)[(settings[9] & 0x38) >> 3]
args.shufflebosses[p] = r(boss_mode)[(settings[9] & 0x07)] args.shufflebosses[p] = r(boss_mode)[(settings[9] & 0x07)]
if len(settings) > 11:
args.flute_mode[p] = r(flute_mode)[(settings[11] & 0x80) >> 7]
class KeyRuleType(FastEnum): class KeyRuleType(FastEnum):

2
CLI.py
View File

@@ -115,6 +115,7 @@ def parse_cli(argv, no_defaults=False):
playerargs = parse_cli(shlex.split(getattr(ret, f"p{player}")), True) playerargs = parse_cli(shlex.split(getattr(ret, f"p{player}")), True)
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
'flute_mode',
'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid', 'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid',
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory', 'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
'usestartinventory', 'bombbag', 'overworld_map', 'restrict_boss_items', 'usestartinventory', 'bombbag', 'overworld_map', 'restrict_boss_items',
@@ -156,6 +157,7 @@ def parse_settings():
"crystals_gt": "7", "crystals_gt": "7",
"crystals_ganon": "7", "crystals_ganon": "7",
"swords": "random", "swords": "random",
'flute_mode': 'normal',
"difficulty": "normal", "difficulty": "normal",
"item_functionality": "normal", "item_functionality": "normal",
"timer": "none", "timer": "none",

View File

@@ -834,7 +834,7 @@ def main_dungeon_pool(dungeon_pool, world, player):
for name in pool: for name in pool:
builder = world.dungeon_layouts[player][name] builder = world.dungeon_layouts[player][name]
region_set = builder.master_sector.region_set() region_set = builder.master_sector.region_set()
builder.bk_required = len(builder.bk_door_proposal) > 0 or any(x in region_set for x in special_bk_regions) builder.bk_required = builder.bk_door_proposal or any(x in region_set for x in special_bk_regions)
dungeon = world.get_dungeon(name, player) dungeon = world.get_dungeon(name, player)
if not builder.bk_required or builder.bk_provided: if not builder.bk_required or builder.bk_provided:
dungeon.big_key = None dungeon.big_key = None
@@ -1793,7 +1793,7 @@ def shuffle_trap_doors(door_type_pools, paths, start_regions_map, world, player)
remaining -= len(custom_trap_doors[dungeon]) remaining -= len(custom_trap_doors[dungeon])
ttl += len(builder.candidates.trap) ttl += len(builder.candidates.trap)
if ttl == 0: if ttl == 0:
return used_doors continue
for dungeon in pool: for dungeon in pool:
builder = world.dungeon_layouts[player][dungeon] builder = world.dungeon_layouts[player][dungeon]
proportion = len(builder.candidates.trap) proportion = len(builder.candidates.trap)
@@ -1853,7 +1853,7 @@ def shuffle_big_key_doors(door_type_pools, used_doors, start_regions_map, world,
remaining -= len(custom_bk_doors[dungeon]) remaining -= len(custom_bk_doors[dungeon])
ttl += len(builder.candidates.big) ttl += len(builder.candidates.big)
if ttl == 0: if ttl == 0:
return used_doors continue
for dungeon in pool: for dungeon in pool:
builder = world.dungeon_layouts[player][dungeon] builder = world.dungeon_layouts[player][dungeon]
proportion = len(builder.candidates.big) proportion = len(builder.candidates.big)
@@ -2004,7 +2004,7 @@ def shuffle_bomb_dash_doors(door_type_pools, used_doors, start_regions_map, worl
remaining_dash -= len(custom_dash_doors[dungeon]) remaining_dash -= len(custom_dash_doors[dungeon])
ttl += len(builder.candidates.bomb_dash) ttl += len(builder.candidates.bomb_dash)
if ttl == 0: if ttl == 0:
return used_doors continue
for dungeon in pool: for dungeon in pool:
builder = world.dungeon_layouts[player][dungeon] builder = world.dungeon_layouts[player][dungeon]
proportion = len(builder.candidates.bomb_dash) proportion = len(builder.candidates.bomb_dash)
@@ -2508,8 +2508,9 @@ def find_small_key_door_candidates(builder, start_regions, used, world, player):
def calc_used_dungeon_items(builder, world, player): def calc_used_dungeon_items(builder, world, player):
base = max(count_reserved_locations(world, player, builder.location_set), 2)
basic_flag = world.doorShuffle[player] == 'basic' basic_flag = world.doorShuffle[player] == 'basic'
base = 0 if basic_flag else 2 # at least 2 items per dungeon, except in basic
base = max(count_reserved_locations(world, player, builder.location_set), base)
if not world.bigkeyshuffle[player]: if not world.bigkeyshuffle[player]:
if builder.bk_required and not builder.bk_provided: if builder.bk_required and not builder.bk_provided:
base += 1 base += 1

View File

@@ -144,6 +144,7 @@ class InitialSram:
'Big Key (Ganons Tower)': (0x366, 0x04), 'Compass (Ganons Tower)': (0x364, 0x04), 'Map (Ganons Tower)': (0x368, 0x04)} 'Big Key (Ganons Tower)': (0x366, 0x04), 'Compass (Ganons Tower)': (0x364, 0x04), 'Map (Ganons Tower)': (0x368, 0x04)}
set_or_table = {'Flippers': (0x356, 1, 0x379, 0x02),'Pegasus Boots': (0x355, 1, 0x379, 0x04), set_or_table = {'Flippers': (0x356, 1, 0x379, 0x02),'Pegasus Boots': (0x355, 1, 0x379, 0x04),
'Shovel': (0x34C, 1, 0x38C, 0x04), 'Ocarina': (0x34C, 3, 0x38C, 0x01), 'Shovel': (0x34C, 1, 0x38C, 0x04), 'Ocarina': (0x34C, 3, 0x38C, 0x01),
'Ocarina (Activated)': (0x34C, 3, 0x38C, 0x01),
'Mushroom': (0x344, 1, 0x38C, 0x20 | 0x08), 'Magic Powder': (0x344, 2, 0x38C, 0x10), 'Mushroom': (0x344, 1, 0x38C, 0x20 | 0x08), 'Magic Powder': (0x344, 2, 0x38C, 0x10),
'Blue Boomerang': (0x341, 1, 0x38C, 0x80), 'Red Boomerang': (0x341, 2, 0x38C, 0x40)} 'Blue Boomerang': (0x341, 1, 0x38C, 0x80), 'Red Boomerang': (0x341, 2, 0x38C, 0x40)}
keys = {'Small Key (Eastern Palace)': [0x37E], 'Small Key (Desert Palace)': [0x37F], keys = {'Small Key (Eastern Palace)': [0x37E], 'Small Key (Desert Palace)': [0x37F],

View File

@@ -267,7 +267,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.bombbag[player], world.customitemarray) (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.bombbag[player], world.customitemarray)
world.rupoor_cost = min(world.customitemarray[player]["rupoorcost"], 9999) world.rupoor_cost = min(world.customitemarray[player]["rupoorcost"], 9999)
else: else:
(pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.doorShuffle[player], world.logic[player]) (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.doorShuffle[player], world.logic[player], world.flute_mode[player] == 'active')
if player in world.pool_adjustment.keys() and not skip_pool_adjustments: if player in world.pool_adjustment.keys() and not skip_pool_adjustments:
amt = world.pool_adjustment[player] amt = world.pool_adjustment[player]
@@ -789,7 +789,8 @@ def add_pot_contents(world, player):
world.itempool.append(ItemFactory(item, player)) world.itempool.append(ItemFactory(item, player))
def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic): def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bombbag,
door_shuffle, logic, flute_activated):
pool = [] pool = []
placed_items = {} placed_items = {}
precollected_items = [] precollected_items = []
@@ -802,6 +803,10 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer,
pool.extend(alwaysitems) pool.extend(alwaysitems)
if flute_activated:
pool.remove('Ocarina')
pool.append('Ocarina (Activated)')
def place_item(loc, item): def place_item(loc, item):
assert loc not in placed_items assert loc not in placed_items
placed_items[loc] = item placed_items[loc] = item

View File

@@ -30,6 +30,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche
'Hookshot': (True, False, None, 0x0A, 250, 'BOING!!!\nBOING!!!\nBOING!!!', 'and the tickle beam', 'tickle-monster kid', 'tickle beam for sale', 'witch and tickle boy', 'beam boy tickles again', 'the Hookshot'), 'Hookshot': (True, False, None, 0x0A, 250, 'BOING!!!\nBOING!!!\nBOING!!!', 'and the tickle beam', 'tickle-monster kid', 'tickle beam for sale', 'witch and tickle boy', 'beam boy tickles again', 'the Hookshot'),
'Magic Mirror': (True, False, None, 0x1A, 250, 'Isn\'t your\nreflection so\npretty?', 'the face reflector', 'the narcissistic kid', 'your face for sale', 'trades looking-glass', 'narcissistic boy is happy again', 'the mirror'), 'Magic Mirror': (True, False, None, 0x1A, 250, 'Isn\'t your\nreflection so\npretty?', 'the face reflector', 'the narcissistic kid', 'your face for sale', 'trades looking-glass', 'narcissistic boy is happy again', 'the mirror'),
'Ocarina': (True, False, None, 0x14, 250, 'Save the duck\nand fly to\nfreedom!', 'and the duck call', 'the duck-call kid', 'duck call for sale', 'duck-calls for trade', 'ocarina boy plays again', 'the flute'), 'Ocarina': (True, False, None, 0x14, 250, 'Save the duck\nand fly to\nfreedom!', 'and the duck call', 'the duck-call kid', 'duck call for sale', 'duck-calls for trade', 'ocarina boy plays again', 'the flute'),
'Ocarina (Activated)': (True, False, None, 0x4A, 250, 'Save the duck\nand fly to\nfreedom!', 'and the duck call', 'the duck-call kid', 'duck call for sale', 'duck-calls for trade', 'ocarina boy plays again', 'the flute'),
'Pegasus Boots': (True, False, None, 0x4B, 250, 'Gotta go fast!', 'and the sprint shoes', 'the running-man kid', 'sprint shoe for sale', 'shrooms for speed', 'gotta-go-fast boy runs again', 'the boots'), 'Pegasus Boots': (True, False, None, 0x4B, 250, 'Gotta go fast!', 'and the sprint shoes', 'the running-man kid', 'sprint shoe for sale', 'shrooms for speed', 'gotta-go-fast boy runs again', 'the boots'),
'Power Glove': (True, False, None, 0x1B, 100, 'Now you can\nlift weak\nstuff!', 'and the grey mittens', 'body-building kid', 'lift glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'the Glove'), 'Power Glove': (True, False, None, 0x1B, 100, 'Now you can\nlift weak\nstuff!', 'and the grey mittens', 'body-building kid', 'lift glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'the Glove'),
'Cape': (True, False, None, 0x19, 50, 'Wear this to\nbecome\ninvisible!', 'the camouflage cape', 'red riding-hood kid', 'red hood for sale', 'hood from a hood', 'dapper boy hides again', 'the cape'), 'Cape': (True, False, None, 0x19, 50, 'Wear this to\nbecome\ninvisible!', 'the camouflage cape', 'red riding-hood kid', 'red hood for sale', 'hood from a hood', 'dapper boy hides again', 'the cape'),

View File

@@ -95,6 +95,7 @@ def main(args, seed=None, fish=None):
world.keyshuffle = args.keyshuffle.copy() world.keyshuffle = args.keyshuffle.copy()
world.bigkeyshuffle = args.bigkeyshuffle.copy() world.bigkeyshuffle = args.bigkeyshuffle.copy()
world.bombbag = args.bombbag.copy() world.bombbag = args.bombbag.copy()
world.flute_mode = args.flute_mode.copy()
world.crystals_needed_for_ganon = {player: random.randint(0, 7) if args.crystals_ganon[player] == 'random' else int(args.crystals_ganon[player]) for player in range(1, world.players + 1)} world.crystals_needed_for_ganon = {player: random.randint(0, 7) if args.crystals_ganon[player] == 'random' else int(args.crystals_ganon[player]) for player in range(1, world.players + 1)}
world.crystals_needed_for_gt = {player: random.randint(0, 7) if args.crystals_gt[player] == 'random' else int(args.crystals_gt[player]) for player in range(1, world.players + 1)} world.crystals_needed_for_gt = {player: random.randint(0, 7) if args.crystals_gt[player] == 'random' else int(args.crystals_gt[player]) for player in range(1, world.players + 1)}
world.crystals_ganon_orig = args.crystals_ganon.copy() world.crystals_ganon_orig = args.crystals_ganon.copy()
@@ -158,7 +159,9 @@ def main(args, seed=None, fish=None):
if args.usestartinventory[player]: if args.usestartinventory[player]:
for tok in filter(None, args.startinventory[player].split(',')): for tok in filter(None, args.startinventory[player].split(',')):
item = ItemFactory(tok.strip(), player) name = tok.strip()
name = name if name != 'Ocarina' or world.flute_mode[player] != 'active' else 'Ocarina (Activated)'
item = ItemFactory(name, player)
if item: if item:
world.push_precollected(item) world.push_precollected(item)
@@ -451,6 +454,7 @@ def copy_world(world):
ret.keyshuffle = world.keyshuffle.copy() ret.keyshuffle = world.keyshuffle.copy()
ret.bigkeyshuffle = world.bigkeyshuffle.copy() ret.bigkeyshuffle = world.bigkeyshuffle.copy()
ret.bombbag = world.bombbag.copy() ret.bombbag = world.bombbag.copy()
ret.flute_mode = world.flute_mode.copy()
ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy() ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy()
ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy() ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy()
ret.crystals_ganon_orig = world.crystals_ganon_orig.copy() ret.crystals_ganon_orig = world.crystals_ganon_orig.copy()

5
Rom.py
View File

@@ -37,7 +37,7 @@ from source.dungeon.RoomList import Room0127
JAP10HASH = '03a63945398191337e896e5771f77173' JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = '7b877dcee4ece38713768b74acb333a6' RANDOMIZERBASEHASH = '0be31dc5cb338e7e85d1ce65e839c99e'
class JsonRom(object): class JsonRom(object):
@@ -2161,8 +2161,8 @@ def write_strings(rom, world, player, team):
while hint_count > 0 and len(items_to_hint) > 0: while hint_count > 0 and len(items_to_hint) > 0:
this_item = items_to_hint.pop(0) this_item = items_to_hint.pop(0)
this_location = world.find_items_not_key_only(this_item, player) this_location = world.find_items_not_key_only(this_item, player)
random.shuffle(this_location)
if this_location: if this_location:
random.shuffle(this_location)
item_name = this_location[0].item.hint_text item_name = this_location[0].item.hint_text
item_name = item_name[0].upper() + item_name[1:] item_name = item_name[0].upper() + item_name[1:]
this_hint = f'{item_name} can be found {hint_text(this_location[0])}.' this_hint = f'{item_name} can be found {hint_text(this_location[0])}.'
@@ -2847,6 +2847,7 @@ RelevantItems = ['Bow',
'Hookshot', 'Hookshot',
'Magic Mirror', 'Magic Mirror',
'Ocarina', 'Ocarina',
'Ocarina (Activated)',
'Pegasus Boots', 'Pegasus Boots',
'Power Glove', 'Power Glove',
'Cape', 'Cape',

View File

@@ -786,9 +786,9 @@ def default_rules(world, player):
set_rule(world.get_entrance('50 Rupee Cave', player), lambda state: state.can_lift_rocks(player)) set_rule(world.get_entrance('50 Rupee Cave', player), lambda state: state.can_lift_rocks(player))
set_rule(world.get_entrance('Death Mountain Entrance Rock', player), lambda state: state.can_lift_rocks(player)) set_rule(world.get_entrance('Death Mountain Entrance Rock', player), lambda state: state.can_lift_rocks(player))
set_rule(world.get_entrance('Bumper Cave Entrance Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Bumper Cave Entrance Mirror Spot', player), lambda state: state.has_Mirror(player))
set_rule(world.get_entrance('Flute Spot 1', player), lambda state: state.has('Ocarina', player)) set_rule(world.get_entrance('Flute Spot 1', player), lambda state: state.can_flute(player))
set_rule(world.get_entrance('Lake Hylia Central Island Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) set_rule(world.get_entrance('Lake Hylia Central Island Teleporter', player), lambda state: state.can_lift_heavy_rocks(player))
set_rule(world.get_entrance('Dark Desert Teleporter', player), lambda state: state.has('Ocarina', player) and state.can_lift_heavy_rocks(player)) set_rule(world.get_entrance('Dark Desert Teleporter', player), lambda state: state.can_flute(player) and state.can_lift_heavy_rocks(player))
set_rule(world.get_entrance('East Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer set_rule(world.get_entrance('East Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer
set_rule(world.get_entrance('South Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer set_rule(world.get_entrance('South Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer
set_rule(world.get_entrance('Kakariko Teleporter', player), lambda state: ((state.has('Hammer', player) and state.can_lift_rocks(player)) or state.can_lift_heavy_rocks(player)) and state.has_Pearl(player)) # bunny cannot lift bushes set_rule(world.get_entrance('Kakariko Teleporter', player), lambda state: ((state.has('Hammer', player) and state.can_lift_rocks(player)) or state.can_lift_heavy_rocks(player)) and state.has_Pearl(player)) # bunny cannot lift bushes
@@ -1514,7 +1514,7 @@ def set_big_bomb_rules(world, player):
#2. Mirror and Flute and basic routes (can make difference if accessed via insanity or w/ mirror from connector, and then via hyrule castle gate, because no gloves are needed in that case) #2. Mirror and Flute and basic routes (can make difference if accessed via insanity or w/ mirror from connector, and then via hyrule castle gate, because no gloves are needed in that case)
#3. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl #3. Go to south DW and then cross peg bridge: Need Mitts and hammer and moon pearl
# -> (Mitts and CPB) or (((G or Flute) and M) and BR)) # -> (Mitts and CPB) or (((G or Flute) and M) and BR))
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and cross_peg_bridge(state)) or (((state.can_lift_rocks(player) or state.has('Ocarina', player)) and state.has_Mirror(player)) and basic_routes(state))) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and cross_peg_bridge(state)) or (((state.can_lift_rocks(player) or state.can_flute(player)) and state.has_Mirror(player)) and basic_routes(state)))
elif bombshop_entrance.name in Southern_DW_entrances: elif bombshop_entrance.name in Southern_DW_entrances:
#1. Mirror and enter via gate: Need mirror and Aga1 #1. Mirror and enter via gate: Need mirror and Aga1
#2. cross peg bridge: Need hammer and moon pearl #2. cross peg bridge: Need hammer and moon pearl
@@ -1523,52 +1523,52 @@ def set_big_bomb_rules(world, player):
elif bombshop_entrance.name in Isolated_DW_entrances: elif bombshop_entrance.name in Isolated_DW_entrances:
# 1. mirror then flute then basic routes # 1. mirror then flute then basic routes
# -> M and Flute and BR # -> M and Flute and BR
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and state.has('Ocarina', player) and basic_routes(state)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and state.can_flute(player) and basic_routes(state))
elif bombshop_entrance.name in Isolated_LW_entrances: elif bombshop_entrance.name in Isolated_LW_entrances:
# 1. flute then basic routes # 1. flute then basic routes
# Prexisting mirror spot is not permitted, because mirror might have been needed to reach these isolated locations. # Prexisting mirror spot is not permitted, because mirror might have been needed to reach these isolated locations.
# -> Flute and BR # -> Flute and BR
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Ocarina', player) and basic_routes(state)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) and basic_routes(state))
elif bombshop_entrance.name in West_LW_DM_entrances: elif bombshop_entrance.name in West_LW_DM_entrances:
# 1. flute then basic routes or mirror # 1. flute then basic routes or mirror
# Prexisting mirror spot is permitted, because flute can be used to reach west DM directly. # Prexisting mirror spot is permitted, because flute can be used to reach west DM directly.
# -> Flute and (M or BR) # -> Flute and (M or BR)
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Ocarina', player) and (state.has_Mirror(player) or basic_routes(state))) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) and (state.has_Mirror(player) or basic_routes(state)))
elif bombshop_entrance.name in East_LW_DM_entrances: elif bombshop_entrance.name in East_LW_DM_entrances:
# 1. flute then basic routes or mirror and hookshot # 1. flute then basic routes or mirror and hookshot
# Prexisting mirror spot is permitted, because flute can be used to reach west DM directly and then east DM via Hookshot # Prexisting mirror spot is permitted, because flute can be used to reach west DM directly and then east DM via Hookshot
# -> Flute and ((M and Hookshot) or BR) # -> Flute and ((M and Hookshot) or BR)
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Ocarina', player) and ((state.has_Mirror(player) and state.has('Hookshot', player)) or basic_routes(state))) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) and ((state.has_Mirror(player) and state.has('Hookshot', player)) or basic_routes(state)))
elif bombshop_entrance.name == 'Fairy Ascension Cave (Bottom)': elif bombshop_entrance.name == 'Fairy Ascension Cave (Bottom)':
# Same as East_LW_DM_entrances except navigation without BR requires Mitts # Same as East_LW_DM_entrances except navigation without BR requires Mitts
# -> Flute and ((M and Hookshot and Mitts) or BR) # -> Flute and ((M and Hookshot and Mitts) or BR)
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Ocarina', player) and ((state.has_Mirror(player) and state.has('Hookshot', player) and state.can_lift_heavy_rocks(player)) or basic_routes(state))) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) and ((state.has_Mirror(player) and state.has('Hookshot', player) and state.can_lift_heavy_rocks(player)) or basic_routes(state)))
elif bombshop_entrance.name in Castle_ledge_entrances: elif bombshop_entrance.name in Castle_ledge_entrances:
# 1. mirror on pyramid to castle ledge, grab bomb, return through mirror spot: Needs mirror # 1. mirror on pyramid to castle ledge, grab bomb, return through mirror spot: Needs mirror
# 2. flute then basic routes # 2. flute then basic routes
# -> M or (Flute and BR) # -> M or (Flute and BR)
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) or (state.has('Ocarina', player) and basic_routes(state))) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) or (state.can_flute(player) and basic_routes(state)))
elif bombshop_entrance.name in Desert_mirrorable_ledge_entrances: elif bombshop_entrance.name in Desert_mirrorable_ledge_entrances:
# Cases when you have mire access: Mirror to reach locations, return via mirror spot, move to center of desert, mirror anagin and: # Cases when you have mire access: Mirror to reach locations, return via mirror spot, move to center of desert, mirror anagin and:
# 1. Have mire access, Mirror to reach locations, return via mirror spot, move to center of desert, mirror again and then basic routes # 1. Have mire access, Mirror to reach locations, return via mirror spot, move to center of desert, mirror again and then basic routes
# 2. flute then basic routes # 2. flute then basic routes
# -> (Mire access and M) or Flute) and BR # -> (Mire access and M) or Flute) and BR
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: ((state.can_reach('Dark Desert', 'Region', player) and state.has_Mirror(player)) or state.has('Ocarina', player)) and basic_routes(state)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: ((state.can_reach('Dark Desert', 'Region', player) and state.has_Mirror(player)) or state.can_flute(player)) and basic_routes(state))
elif bombshop_entrance.name == 'Old Man Cave (West)': elif bombshop_entrance.name == 'Old Man Cave (West)':
# 1. Lift rock then basic_routes # 1. Lift rock then basic_routes
# 2. flute then basic_routes # 2. flute then basic_routes
# -> (Flute or G) and BR # -> (Flute or G) and BR
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Ocarina', player) or state.can_lift_rocks(player)) and basic_routes(state)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or state.can_lift_rocks(player)) and basic_routes(state))
elif bombshop_entrance.name == 'Graveyard Cave': elif bombshop_entrance.name == 'Graveyard Cave':
# 1. flute then basic routes # 1. flute then basic routes
# 2. (has west dark world access) use existing mirror spot (required Pearl), mirror again off ledge # 2. (has west dark world access) use existing mirror spot (required Pearl), mirror again off ledge
# -> (Flute or (M and P and West Dark World access) and BR # -> (Flute or (M and P and West Dark World access) and BR
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Ocarina', player) or (state.can_reach('West Dark World', 'Region', player) and state.has_Pearl(player) and state.has_Mirror(player))) and basic_routes(state)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or (state.can_reach('West Dark World', 'Region', player) and state.has_Pearl(player) and state.has_Mirror(player))) and basic_routes(state))
elif bombshop_entrance.name in Mirror_from_SDW_entrances: elif bombshop_entrance.name in Mirror_from_SDW_entrances:
# 1. flute then basic routes # 1. flute then basic routes
# 2. (has South dark world access) use existing mirror spot, mirror again off ledge # 2. (has South dark world access) use existing mirror spot, mirror again off ledge
# -> (Flute or (M and South Dark World access) and BR # -> (Flute or (M and South Dark World access) and BR
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Ocarina', player) or (state.can_reach('South Dark World', 'Region', player) and state.has_Mirror(player))) and basic_routes(state)) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or (state.can_reach('South Dark World', 'Region', player) and state.has_Mirror(player))) and basic_routes(state))
elif bombshop_entrance.name == 'Dark World Potion Shop': elif bombshop_entrance.name == 'Dark World Potion Shop':
# 1. walk down by lifting rock: needs gloves and pearl` # 1. walk down by lifting rock: needs gloves and pearl`
# 2. walk down by hammering peg: needs hammer and pearl # 2. walk down by hammering peg: needs hammer and pearl
@@ -1580,11 +1580,11 @@ def set_big_bomb_rules(world, player):
# (because otherwise mirror was used to reach the grave, so would cancel a pre-existing mirror spot) # (because otherwise mirror was used to reach the grave, so would cancel a pre-existing mirror spot)
# to account for insanity, must consider a way to escape without a cave for basic_routes # to account for insanity, must consider a way to escape without a cave for basic_routes
# -> (M and Mitts) or ((Mitts or Flute or (M and P and West Dark World access)) and BR) # -> (M and Mitts) or ((Mitts or Flute or (M and P and West Dark World access)) and BR)
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and state.has_Mirror(player)) or ((state.can_lift_heavy_rocks(player) or state.has('Ocarina', player) or (state.can_reach('West Dark World', 'Region', player) and state.has_Pearl(player) and state.has_Mirror(player))) and basic_routes(state))) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_lift_heavy_rocks(player) and state.has_Mirror(player)) or ((state.can_lift_heavy_rocks(player) or state.can_flute(player) or (state.can_reach('West Dark World', 'Region', player) and state.has_Pearl(player) and state.has_Mirror(player))) and basic_routes(state)))
elif bombshop_entrance.name == 'Waterfall of Wishing': elif bombshop_entrance.name == 'Waterfall of Wishing':
# same as the Normal_LW_entrances case except in insanity it's possible you could be here without Flippers which # same as the Normal_LW_entrances case except in insanity it's possible you could be here without Flippers which
# means you need an escape route of either Flippers or Flute # means you need an escape route of either Flippers or Flute
add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Flippers', player) or state.has('Ocarina', player)) and (basic_routes(state) or state.has_Mirror(player))) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.has('Flippers', player) or state.can_flute(player)) and (basic_routes(state) or state.has_Mirror(player)))
def set_inverted_big_bomb_rules(world, player): def set_inverted_big_bomb_rules(world, player):

Binary file not shown.

View File

@@ -45,6 +45,9 @@
bombbag: bombbag:
on: 1 on: 1
off: 4 off: 4
flute_mode:
normal: 3
active: 1
entrance_shuffle: entrance_shuffle:
none: 15 none: 15
dungeonssimple: 3 dungeonssimple: 3

View File

@@ -42,6 +42,12 @@
"vanilla" "vanilla"
] ]
}, },
"flute_mode": {
"choices": [
"normal",
"active"
]
},
"goal": { "goal": {
"choices": [ "choices": [
"ganon", "ganon",

View File

@@ -121,6 +121,11 @@
"Hard: Reduced functionality.", "Hard: Reduced functionality.",
"Expert: Greatly reduced functionality." "Expert: Greatly reduced functionality."
], ],
"flute_mode": [
"Determine if you need to wake up the bird or not on flute pickup (default: %(default)s)",
"Normal: Normal functionality.",
"Active: Flute is activated on pickup."
],
"timer": [ "timer": [
"Select game timer setting. Affects available itempool. (default: %(default)s)", "Select game timer setting. Affects available itempool. (default: %(default)s)",
"None: No timer.", "None: No timer.",

View File

@@ -285,10 +285,9 @@
"randomizer.item.shopsanity": "Shopsanity", "randomizer.item.shopsanity": "Shopsanity",
"randomizer.item.itemfunction": "Item Functionality", "randomizer.item.flute_mode": "Flute Mode",
"randomizer.item.itemfunction.normal": "Normal", "randomizer.item.flute_mode.normal": "Normal",
"randomizer.item.itemfunction.hard": "Hard", "randomizer.item.flute_mode.active": "Pre-Activated",
"randomizer.item.itemfunction.expert": "Expert",
"randomizer.item.timer": "Timer Setting", "randomizer.item.timer": "Timer Setting",
"randomizer.item.timer.none": "No Timer", "randomizer.item.timer.none": "No Timer",
@@ -298,11 +297,6 @@
"randomizer.item.timer.ohko": "OHKO", "randomizer.item.timer.ohko": "OHKO",
"randomizer.item.timer.timed-countdown": "Timed Countdown", "randomizer.item.timer.timed-countdown": "Timed Countdown",
"randomizer.item.progressives": "Progressive Items",
"randomizer.item.progressives.on": "On",
"randomizer.item.progressives.off": "Off",
"randomizer.item.progressives.random": "Random",
"randomizer.item.accessibility": "Accessibility", "randomizer.item.accessibility": "Accessibility",
"randomizer.item.accessibility.items": "100% Inventory", "randomizer.item.accessibility.items": "100% Inventory",
"randomizer.item.accessibility.locations": "100% Locations", "randomizer.item.accessibility.locations": "100% Locations",

View File

@@ -78,12 +78,11 @@
"expert" "expert"
] ]
}, },
"itemfunction": { "flute_mode": {
"type": "selectbox", "type": "selectbox",
"options": [ "options": [
"normal", "normal",
"hard", "active"
"expert"
] ]
}, },
"timer": { "timer": {
@@ -97,14 +96,6 @@
"timed-countdown" "timed-countdown"
] ]
}, },
"progressives": {
"type": "selectbox",
"options": [
"on",
"off",
"random"
]
},
"accessibility": { "accessibility": {
"type": "selectbox", "type": "selectbox",
"options": [ "options": [

View File

@@ -68,6 +68,7 @@ class CustomSettings(object):
args.logic[p] = get_setting(settings['logic'], args.logic[p]) args.logic[p] = get_setting(settings['logic'], args.logic[p])
args.mode[p] = get_setting(settings['mode'], args.mode[p]) args.mode[p] = get_setting(settings['mode'], args.mode[p])
args.swords[p] = get_setting(settings['swords'], args.swords[p]) args.swords[p] = get_setting(settings['swords'], args.swords[p])
args.flute_mode[p] = get_setting(settings['flute_mode'], args.flute_mode[p])
args.item_functionality[p] = get_setting(settings['item_functionality'], args.item_functionality[p]) args.item_functionality[p] = get_setting(settings['item_functionality'], args.item_functionality[p])
args.goal[p] = get_setting(settings['goal'], args.goal[p]) args.goal[p] = get_setting(settings['goal'], args.goal[p])
args.difficulty[p] = get_setting(settings['difficulty'], args.difficulty[p]) args.difficulty[p] = get_setting(settings['difficulty'], args.difficulty[p])
@@ -189,6 +190,7 @@ class CustomSettings(object):
settings_dict[p]['logic'] = world.logic[p] settings_dict[p]['logic'] = world.logic[p]
settings_dict[p]['mode'] = world.mode[p] settings_dict[p]['mode'] = world.mode[p]
settings_dict[p]['swords'] = world.swords[p] settings_dict[p]['swords'] = world.swords[p]
settings_dict[p]['flute_mode'] = world.flute_mode[p]
settings_dict[p]['difficulty'] = world.difficulty[p] settings_dict[p]['difficulty'] = world.difficulty[p]
settings_dict[p]['goal'] = world.goal[p] settings_dict[p]['goal'] = world.goal[p]
settings_dict[p]['accessibility'] = world.accessibility[p] settings_dict[p]['accessibility'] = world.accessibility[p]

View File

@@ -67,9 +67,8 @@ SETTINGSTOPROCESS = {
"crystals_ganon": "crystals_ganon", "crystals_ganon": "crystals_ganon",
"weapons": "swords", "weapons": "swords",
"itempool": "difficulty", "itempool": "difficulty",
"itemfunction": "item_functionality", "flute_mode": "flute_mode",
"timer": "timer", "timer": "timer",
"progressives": "progressive",
"accessibility": "accessibility", "accessibility": "accessibility",
"sortingalgo": "algorithm", "sortingalgo": "algorithm",
"beemizer": "beemizer", "beemizer": "beemizer",

View File

@@ -454,6 +454,7 @@ vanilla_mapping = {
'Hookshot': ['Swamp Palace - Big Chest'], 'Hookshot': ['Swamp Palace - Big Chest'],
'Magic Mirror': ['Old Man'], 'Magic Mirror': ['Old Man'],
'Ocarina': ['Flute Spot'], 'Ocarina': ['Flute Spot'],
'Ocarina (Activated)': ['Flute Spot'],
'Pegasus Boots': ['Sahasrahla'], 'Pegasus Boots': ['Sahasrahla'],
'Power Glove': ['Desert Palace - Big Chest'], 'Power Glove': ['Desert Palace - Big Chest'],
'Cape': ["King's Tomb"], 'Cape': ["King's Tomb"],
@@ -779,7 +780,7 @@ major_items = {'Bombos', 'Book of Mudora', 'Cane of Somaria', 'Ether', 'Fire Rod
'Bug Catching Net', 'Cane of Byrna', 'Blue Boomerang', 'Red Boomerang', 'Progressive Glove', 'Bug Catching Net', 'Cane of Byrna', 'Blue Boomerang', 'Red Boomerang', 'Progressive Glove',
'Power Glove', 'Titans Mitts', 'Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Magic Mirror', 'Power Glove', 'Titans Mitts', 'Bottle', 'Bottle (Red Potion)', 'Bottle (Green Potion)', 'Magic Mirror',
'Bottle (Blue Potion)', 'Bottle (Fairy)', 'Bottle (Bee)', 'Bottle (Good Bee)', 'Magic Upgrade (1/2)', 'Bottle (Blue Potion)', 'Bottle (Fairy)', 'Bottle (Bee)', 'Bottle (Good Bee)', 'Magic Upgrade (1/2)',
'Sanctuary Heart Container', 'Boss Heart Container', 'Progressive Shield', 'Sanctuary Heart Container', 'Boss Heart Container', 'Progressive Shield', 'Ocarina (Activated)',
'Mirror Shield', 'Progressive Armor', 'Blue Mail', 'Red Mail', 'Progressive Sword', 'Fighter Sword', 'Mirror Shield', 'Progressive Armor', 'Blue Mail', 'Red Mail', 'Progressive Sword', 'Fighter Sword',
'Master Sword', 'Tempered Sword', 'Golden Sword', 'Bow', 'Silver Arrows', 'Triforce Piece', 'Moon Pearl', 'Master Sword', 'Tempered Sword', 'Golden Sword', 'Bow', 'Silver Arrows', 'Triforce Piece', 'Moon Pearl',
'Progressive Bow', 'Progressive Bow (Alt)'} 'Progressive Bow', 'Progressive Bow (Alt)'}

View File

@@ -144,6 +144,7 @@ def roll_settings(weights):
}[swords] }[swords]
ret.difficulty = get_choice('item_pool') ret.difficulty = get_choice('item_pool')
ret.flute_mode = get_choice_default('flute_mode', default='normal')
ret.item_functionality = get_choice('item_functionality') ret.item_functionality = get_choice('item_functionality')