Merge branch 'DoorDevVolatile' into Customizer
# Conflicts: # DoorShuffle.py
This commit is contained in:
@@ -1948,6 +1948,23 @@ def custom_door_kind(custom_key, kind, bd_candidates, counts, world, player):
|
||||
counts[d_name] += 1
|
||||
|
||||
|
||||
dashable_forbidden = {
|
||||
'Swamp Trench 1 Key Ledge NW', 'Swamp Left Elbow WN', 'Swamp Right Elbow SE', 'Mire Hub WN', 'Mire Hub WS',
|
||||
'Mire Hub Top NW', 'Mire Hub NE', 'Ice Dead End WS'
|
||||
}
|
||||
|
||||
ohko_forbidden = {
|
||||
'GT Invisible Catwalk NE', 'GT Falling Bridge WN', 'GT Falling Bridge WS', 'GT Hidden Star ES', 'GT Hookshot EN',
|
||||
'GT Torch Cross WN', 'TR Torches WN', 'Mire Falling Bridge WS', 'Mire Falling Bridge W', 'Ice Hookshot Balcony SW',
|
||||
'Ice Catwalk WN', 'Ice Catwalk NW', 'Ice Bomb Jump NW', 'GT Cannonball Bridge SE'
|
||||
}
|
||||
|
||||
|
||||
def filter_dashable_candidates(candidates, world):
|
||||
forbidden_set = dashable_forbidden if world.can_take_damage else ohko_forbidden
|
||||
return [x for x in candidates if x not in forbidden_set and x.dest not in forbidden_set]
|
||||
|
||||
|
||||
def shuffle_bombable_dashable(bd_candidates, world, player):
|
||||
dash_counts = defaultdict(int)
|
||||
bomb_counts = defaultdict(int)
|
||||
@@ -1958,7 +1975,8 @@ def shuffle_bombable_dashable(bd_candidates, world, player):
|
||||
for dungeon, candidates in bd_candidates.items():
|
||||
diff = bomb_dash_counts[dungeon.name][1] - dash_counts[dungeon.name]
|
||||
if diff > 0:
|
||||
for chosen in random.sample(candidates, min(diff, len(candidates))):
|
||||
dash_candidates = filter_dashable_candidates(candidates, world)
|
||||
for chosen in random.sample(dash_candidates, min(diff, len(candidates))):
|
||||
change_pair_type(chosen, DoorKind.Dashable, world, player)
|
||||
candidates.remove(chosen)
|
||||
diff = bomb_dash_counts[dungeon.name][0] - bomb_counts[dungeon.name]
|
||||
@@ -1973,7 +1991,8 @@ def shuffle_bombable_dashable(bd_candidates, world, player):
|
||||
desired_dashables = 8 - sum(dash_counts.values(), 0)
|
||||
desired_bombables = 12 - sum(bomb_counts.values(), 0)
|
||||
if desired_dashables > 0:
|
||||
for chosen in random.sample(all_candidates, min(desired_dashables, len(all_candidates))):
|
||||
dash_candidates = filter_dashable_candidates(all_candidates, world)
|
||||
for chosen in random.sample(dash_candidates, min(desired_dashables, len(all_candidates))):
|
||||
change_pair_type(chosen, DoorKind.Dashable, world, player)
|
||||
all_candidates.remove(chosen)
|
||||
if desired_bombables > 0:
|
||||
@@ -2389,6 +2408,8 @@ logical_connections = [
|
||||
('Skull Pot Circle Star Path', 'Skull Map Room'),
|
||||
('Skull Big Chest Hookpath', 'Skull 1 Lobby'),
|
||||
('Skull Back Drop Star Path', 'Skull Small Hall'),
|
||||
('Skull 2 West Lobby Pits', 'Skull 2 West Lobby Ledge'),
|
||||
('Skull 2 West Lobby Ledge Pits', 'Skull 2 West Lobby'),
|
||||
('Thieves Rail Ledge Drop Down', 'Thieves BK Corner'),
|
||||
('Thieves Hellway Orange Barrier', 'Thieves Hellway S Crystal'),
|
||||
('Thieves Hellway Crystal Orange Barrier', 'Thieves Hellway'),
|
||||
|
||||
2
Doors.py
2
Doors.py
@@ -614,6 +614,8 @@ def create_doors(world, player):
|
||||
create_door(player, 'Skull 2 West Lobby S', Nrml).dir(So, 0x56, Left, High).pos(1).portal(Z, 0x00),
|
||||
create_door(player, 'Skull 2 West Lobby ES', Intr).dir(Ea, 0x56, Bot, High).pos(2),
|
||||
create_door(player, 'Skull 2 West Lobby NW', Intr).dir(No, 0x56, Left, High).small_key().pos(0),
|
||||
create_door(player, 'Skull 2 West Lobby Pits', Lgcl),
|
||||
create_door(player, 'Skull 2 West Lobby Ledge Pits', Lgcl),
|
||||
create_door(player, 'Skull X Room SW', Intr).dir(So, 0x56, Left, High).small_key().pos(0),
|
||||
create_door(player, 'Skull Back Drop Star Path', Lgcl),
|
||||
create_door(player, 'Skull 3 Lobby SW', Nrml).dir(So, 0x59, Left, High).pos(1).portal(Z, 0x02),
|
||||
|
||||
@@ -124,10 +124,10 @@ swamp_regions = [
|
||||
skull_regions = [
|
||||
'Skull 1 Lobby', 'Skull Map Room', 'Skull Pot Circle', 'Skull Pull Switch', 'Skull Big Chest', 'Skull Pinball',
|
||||
'Skull Pot Prison', 'Skull Compass Room', 'Skull Left Drop', 'Skull 2 East Lobby', 'Skull Big Key',
|
||||
'Skull Lone Pot', 'Skull Small Hall', 'Skull Back Drop', 'Skull 2 West Lobby', 'Skull X Room', 'Skull 3 Lobby',
|
||||
'Skull East Bridge', 'Skull West Bridge Nook', 'Skull Star Pits', 'Skull Torch Room', 'Skull Vines',
|
||||
'Skull Spike Corner', 'Skull Final Drop', 'Skull Boss', 'Skull 1 Portal', 'Skull 2 East Portal',
|
||||
'Skull 2 West Portal', 'Skull 3 Portal'
|
||||
'Skull Lone Pot', 'Skull Small Hall', 'Skull Back Drop', 'Skull 2 West Lobby', 'Skull 2 West Lobby Ledge',
|
||||
'Skull X Room', 'Skull 3 Lobby', 'Skull East Bridge', 'Skull West Bridge Nook', 'Skull Star Pits',
|
||||
'Skull Torch Room', 'Skull Vines', 'Skull Spike Corner', 'Skull Final Drop', 'Skull Boss',
|
||||
'Skull 1 Portal', 'Skull 2 East Portal', 'Skull 2 West Portal', 'Skull 3 Portal'
|
||||
]
|
||||
|
||||
thieves_regions = [
|
||||
|
||||
42
Fill.py
42
Fill.py
@@ -2,6 +2,7 @@ import RaceRandom as random
|
||||
import collections
|
||||
import itertools
|
||||
import logging
|
||||
import math
|
||||
|
||||
from BaseClasses import CollectionState, FillError, LocationType
|
||||
from Items import ItemFactory
|
||||
@@ -380,12 +381,21 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
|
||||
|
||||
# fill in gtower locations with trash first
|
||||
for player in range(1, world.players + 1):
|
||||
if not gftower_trash or not world.ganonstower_vanilla[player] or world.doorShuffle[player] == 'crossed' or world.logic[player] in ['owglitches', 'nologic']:
|
||||
if (not gftower_trash or not world.ganonstower_vanilla[player]
|
||||
or world.logic[player] in ['owglitches', 'nologic']):
|
||||
continue
|
||||
max_trash = 8 if world.algorithm == 'dungeon_only' else 15
|
||||
gftower_trash_count = (random.randint(15, 50) if world.goal[player] == 'triforcehunt' else random.randint(0, max_trash))
|
||||
gt_count, total_count = calc_trash_locations(world, player)
|
||||
scale_factor = .75 * (world.crystals_needed_for_gt[player] / 7)
|
||||
if world.algorithm == 'dungeon_only':
|
||||
reserved_space = sum(1 for i in progitempool+prioitempool if i.player == player)
|
||||
max_trash = max(0, min(gt_count, total_count - reserved_space))
|
||||
else:
|
||||
max_trash = gt_count
|
||||
scaled_trash = math.floor(max_trash * scale_factor)
|
||||
gftower_trash_count = (random.randint(scaled_trash, max_trash) if world.goal[player] == 'triforcehunt' else random.randint(0, scaled_trash))
|
||||
|
||||
gtower_locations = [location for location in fill_locations if 'Ganons Tower' in location.name and location.player == player]
|
||||
gtower_locations = [location for location in fill_locations if location.parent_region.dungeon
|
||||
and location.parent_region.dungeon.name == 'Ganons Tower' and location.player == player]
|
||||
random.shuffle(gtower_locations)
|
||||
trashcnt = 0
|
||||
while gtower_locations and restitempool and trashcnt < gftower_trash_count:
|
||||
@@ -449,6 +459,19 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
|
||||
ensure_good_pots(world)
|
||||
|
||||
|
||||
def calc_trash_locations(world, player):
|
||||
total_count, gt_count = 0, 0
|
||||
for loc in world.get_locations():
|
||||
if (loc.player == player and loc.item is None
|
||||
and (loc.type not in {LocationType.Pot, LocationType.Drop, LocationType.Normal} or not loc.forced_item)
|
||||
and (loc.type != LocationType.Shop or world.shopsanity[player])
|
||||
and loc.parent_region.dungeon):
|
||||
total_count += 1
|
||||
if loc.parent_region.dungeon.name == 'Ganons Tower':
|
||||
gt_count += 1
|
||||
return gt_count, total_count
|
||||
|
||||
|
||||
def ensure_good_pots(world, write_skips=False):
|
||||
for loc in world.get_locations():
|
||||
if loc.item is None:
|
||||
@@ -457,13 +480,22 @@ def ensure_good_pots(world, write_skips=False):
|
||||
if (loc.item.name in {'Arrows (5)', 'Nothing'}
|
||||
and (loc.type != LocationType.Pot or loc.item.player != loc.player)):
|
||||
loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.item.player)
|
||||
# can be placed here by multiworld balancing or shop balancing
|
||||
# change it to something normal for the player it got swapped to
|
||||
elif (loc.item.name in {'Chicken', 'Big Magic'}
|
||||
and (loc.type != LocationType.Pot or loc.item.player != loc.player)):
|
||||
if loc.type == LocationType.Pot:
|
||||
loc.item.player = loc.player
|
||||
else:
|
||||
loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.player)
|
||||
# don't write out all pots to spoiler
|
||||
if write_skips:
|
||||
if loc.type == LocationType.Pot and loc.item.name in valid_pot_items:
|
||||
loc.skip = True
|
||||
|
||||
|
||||
invalid_location_replacement = {'Arrows (5)': 'Arrows (10)', 'Nothing': 'Rupees (5)'}
|
||||
invalid_location_replacement = {'Arrows (5)': 'Arrows (10)', 'Nothing': 'Rupees (5)',
|
||||
'Chicken': 'Rupees (5)', 'Big Magic': 'Small Magic'}
|
||||
|
||||
|
||||
def fast_fill_helper(world, item_pool, fill_locations):
|
||||
|
||||
@@ -245,6 +245,9 @@ def generate_itempool(world, player):
|
||||
world.push_item(world.get_location('Ice Block Drop', player), ItemFactory('Convenient Block', player), False)
|
||||
world.get_location('Ice Block Drop', player).event = True
|
||||
world.get_location('Ice Block Drop', player).locked = True
|
||||
world.push_item(world.get_location('Skull Star Tile', player), ItemFactory('Hidden Pits', player), False)
|
||||
world.get_location('Skull Star Tile', player).event = True
|
||||
world.get_location('Skull Star Tile', player).locked = True
|
||||
if world.mode[player] == 'standard':
|
||||
world.push_item(world.get_location('Zelda Pickup', player), ItemFactory('Zelda Herself', player), False)
|
||||
world.get_location('Zelda Pickup', player).event = True
|
||||
|
||||
1
Items.py
1
Items.py
@@ -187,6 +187,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche
|
||||
'Maiden Rescued': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
|
||||
'Maiden Unmasked': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
|
||||
'Convenient Block': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
|
||||
'Hidden Pits': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
|
||||
'Zelda Herself': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
|
||||
'Zelda Delivered': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
|
||||
}
|
||||
|
||||
2
Main.py
2
Main.py
@@ -583,7 +583,7 @@ def create_playthrough(world):
|
||||
|
||||
# get locations containing progress items
|
||||
prog_locations = [location for location in world.get_filled_locations() if location.item.advancement]
|
||||
optional_locations = ['Trench 1 Switch', 'Trench 2 Switch', 'Ice Block Drop']
|
||||
optional_locations = ['Trench 1 Switch', 'Trench 2 Switch', 'Ice Block Drop', 'Skull Star Tile']
|
||||
state_cache = [None]
|
||||
collection_spheres = []
|
||||
state = CollectionState(world)
|
||||
|
||||
@@ -6,6 +6,7 @@ import json
|
||||
import logging
|
||||
import re
|
||||
import shlex
|
||||
import ssl
|
||||
import urllib.request
|
||||
import websockets
|
||||
import zlib
|
||||
@@ -159,8 +160,7 @@ def send_new_items(ctx : Context):
|
||||
client.send_index = len(items)
|
||||
|
||||
def forfeit_player(ctx : Context, team, slot):
|
||||
all_locations = {values[0] for values in Regions.location_table.values() if type(values[0]) is int}
|
||||
all_locations.update({values[1] for values in Regions.key_drop_data.values()})
|
||||
all_locations = set(ctx.lookup_id_to_name.keys())
|
||||
notify_all(ctx, "%s (Team #%d) has forfeited" % (ctx.player_names[(team, slot)], team + 1))
|
||||
register_location_checks(ctx, team, slot, all_locations)
|
||||
|
||||
@@ -347,8 +347,8 @@ async def console(ctx : Context):
|
||||
|
||||
|
||||
def init_lookups(ctx):
|
||||
ctx.lookup_id_to_name = {x: y for x, y in Regions.lookup_id_to_name.items()}
|
||||
ctx.lookup_name_to_id = {x: y for x, y in Regions.lookup_name_to_id.items()}
|
||||
ctx.lookup_id_to_name = {x: y for x, y in Regions.lookup_id_to_name.items()}
|
||||
ctx.lookup_name_to_id = {x: y for x, y in Regions.lookup_name_to_id.items()}
|
||||
for location, datum in PotShuffle.key_drop_data.items():
|
||||
type = datum[0]
|
||||
if type == 'Drop':
|
||||
@@ -406,7 +406,7 @@ async def main():
|
||||
logging.error('Failed to read multiworld data (%s)' % e)
|
||||
return
|
||||
|
||||
ip = urllib.request.urlopen('https://v4.ident.me').read().decode('utf8') if not ctx.host else ctx.host
|
||||
ip = urllib.request.urlopen('https://v4.ident.me', context=ssl._create_unverified_context()).read().decode('utf8') if not ctx.host else ctx.host
|
||||
logging.info('Hosting game at %s:%d (%s)' % (ip, ctx.port, 'No password' if not ctx.password else 'Password: %s' % ctx.password))
|
||||
|
||||
ctx.disable_save = args.disable_save
|
||||
|
||||
@@ -157,9 +157,17 @@ Same as above but both small keys and bigs keys of the dungeon are not allowed o
|
||||
#### Volatile
|
||||
|
||||
* 1.0.1.12
|
||||
* Fix for Multiworld forfeits, shops and pot items now included
|
||||
* Reworked GT Trash Fill. Base rate is 0-75% of locations fill with 7 crystals entrance requirements. Triforce hunt is 75%-100% of locations. The 75% number will decrease based on the crystal entrance requirement. Dungeon_only algorithm caps it based on how many items need to be placed in dungeons. Cross dungeon shuffle will now work with the trash fill.
|
||||
* MultiServer fix for ssl certs and python
|
||||
* Inverted bug
|
||||
* Fix for hammerdashing pots, if sprite limit is reached, items won't spawn, error beep won't play either because of other SFX
|
||||
* Killing enemies freeze + hammer results in the droppable item instead of the freeze prize
|
||||
* Fix for hammerdashing pots, if sprite limit is reached, items won't spawn, but error beep won't play either because of other SFX
|
||||
* Arrghus splash no longer used for pottery sprites (used apple instead)
|
||||
* Killing enemies via freeze + hammer properly results in the droppable item instead of the freeze prize
|
||||
* Forbid certain doors from being dashable when you either can't dash them open (but bombs would work) or you'd fall into a pit from the bonk recoil in OHKO
|
||||
* Logic refinements
|
||||
* Skull X Room requires Boots or access to Skull Back Drop
|
||||
* GT Falling Torches requires Boots to get over the falling tile gap (this is a stop-gap measure until more sophisticated crystal switch traversal is possible)
|
||||
* 1.0.1.11
|
||||
* Separated Collection Rate counter from experimental
|
||||
* Added MSU Resume option
|
||||
|
||||
@@ -523,8 +523,9 @@ def create_dungeon_regions(world, player):
|
||||
create_dungeon_region(player, 'Skull Big Key', 'Skull Woods', ['Skull Woods - Big Key Chest'], ['Skull Big Key SW', 'Skull Big Key EN']),
|
||||
create_dungeon_region(player, 'Skull Lone Pot', 'Skull Woods', None, ['Skull Lone Pot WN']),
|
||||
create_dungeon_region(player, 'Skull Small Hall', 'Skull Woods', None, ['Skull Small Hall ES', 'Skull Small Hall WS']),
|
||||
create_dungeon_region(player, 'Skull Back Drop', 'Skull Woods', None, ['Skull Back Drop Star Path']),
|
||||
create_dungeon_region(player, 'Skull 2 West Lobby', 'Skull Woods', ['Skull Woods - West Lobby Pot Key'], ['Skull 2 West Lobby ES', 'Skull 2 West Lobby NW', 'Skull 2 West Lobby S']),
|
||||
create_dungeon_region(player, 'Skull Back Drop', 'Skull Woods', ['Skull Star Tile'], ['Skull Back Drop Star Path']),
|
||||
create_dungeon_region(player, 'Skull 2 West Lobby', 'Skull Woods', ['Skull Woods - West Lobby Pot Key'], ['Skull 2 West Lobby ES', 'Skull 2 West Lobby Pits', 'Skull 2 West Lobby S']),
|
||||
create_dungeon_region(player, 'Skull 2 West Lobby Ledge', 'Skull Woods', None, ['Skull 2 West Lobby NW', 'Skull 2 West Lobby Ledge Pits']),
|
||||
create_dungeon_region(player, 'Skull X Room', 'Skull Woods', None, ['Skull X Room SW']),
|
||||
create_dungeon_region(player, 'Skull 3 Lobby', 'Skull Woods', None, ['Skull 3 Lobby NW', 'Skull 3 Lobby EN', 'Skull 3 Lobby SW']),
|
||||
create_dungeon_region(player, 'Skull East Bridge', 'Skull Woods', None, ['Skull East Bridge WN', 'Skull East Bridge WS']),
|
||||
@@ -1045,7 +1046,7 @@ def adjust_locations(world, player):
|
||||
# unreal events:
|
||||
for l in ['Ganon', 'Agahnim 1', 'Agahnim 2', 'Dark Blacksmith Ruins', 'Frog', 'Missing Smith', 'Floodgate',
|
||||
'Trench 1 Switch', 'Trench 2 Switch', 'Swamp Drain', 'Attic Cracked Floor', 'Suspicious Maiden',
|
||||
'Revealing Light', 'Ice Block Drop', 'Zelda Pickup', 'Zelda Drop Off']:
|
||||
'Revealing Light', 'Ice Block Drop', 'Zelda Pickup', 'Zelda Drop Off', 'Skull Star Tile']:
|
||||
location = world.get_location_unsafe(l, player)
|
||||
if location:
|
||||
location.type = LocationType.Logical
|
||||
@@ -1150,6 +1151,7 @@ dungeon_events = [
|
||||
'Suspicious Maiden',
|
||||
'Revealing Light',
|
||||
'Ice Block Drop',
|
||||
'Skull Star Tile',
|
||||
'Zelda Pickup',
|
||||
'Zelda Drop Off'
|
||||
]
|
||||
@@ -1388,6 +1390,7 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'),
|
||||
'Suspicious Maiden': (None, None, False, None),
|
||||
'Revealing Light': (None, None, False, None),
|
||||
'Ice Block Drop': (None, None, False, None),
|
||||
'Skull Star Tile': (None, None, False, None),
|
||||
'Zelda Pickup': (None, None, False, None),
|
||||
'Zelda Drop Off': (None, None, False, None),
|
||||
'Eastern Palace - Prize': ([0x1209D, 0x53E76, 0x53E77, 0x180052, 0x180070, 0xC6FE, 0x186FE2], None, True, 'Eastern Palace'),
|
||||
|
||||
2
Rom.py
2
Rom.py
@@ -35,7 +35,7 @@ from source.item.FillUtil import valid_pot_items
|
||||
|
||||
|
||||
JAP10HASH = '03a63945398191337e896e5771f77173'
|
||||
RANDOMIZERBASEHASH = '57df02038c77f9a3b915af21d3c59b78'
|
||||
RANDOMIZERBASEHASH = '1e45d174c71b2e079df1c8d5e8d1451b'
|
||||
|
||||
|
||||
class JsonRom(object):
|
||||
|
||||
12
Rules.py
12
Rules.py
@@ -273,6 +273,8 @@ def global_rules(world, player):
|
||||
set_rule(world.get_entrance('Skull Big Chest Hookpath', player), lambda state: state.has('Hookshot', player))
|
||||
set_rule(world.get_entrance('Skull Torch Room WN', player), lambda state: state.has('Fire Rod', player))
|
||||
set_rule(world.get_entrance('Skull Vines NW', player), lambda state: state.has_sword(player))
|
||||
set_rule(world.get_entrance('Skull 2 West Lobby Pits', player), lambda state: state.has_Boots(player) or state.has('Hidden Pits', player))
|
||||
set_rule(world.get_entrance('Skull 2 West Lobby Ledge Pits', player), lambda state: state.has('Hidden Pits', player))
|
||||
set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Boss', player))
|
||||
set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Prize', player))
|
||||
|
||||
@@ -398,6 +400,9 @@ def global_rules(world, player):
|
||||
set_rule(world.get_entrance('GT Lanmolas 2 NW', player), lambda state: world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].can_defeat(state))
|
||||
set_rule(world.get_entrance('GT Torch Cross ES', player), lambda state: state.has_fire_source(player))
|
||||
set_rule(world.get_entrance('GT Falling Torches NE', player), lambda state: state.has_fire_source(player))
|
||||
# todo: the following only applies to crystal state propagation from this supertile
|
||||
# you can also reset the supertile, but I'm not sure how to model that
|
||||
set_rule(world.get_entrance('GT Falling Torches Down Ladder', player), lambda state: state.has_Boots(player))
|
||||
set_rule(world.get_entrance('GT Moldorm Gap', player), lambda state: state.has('Hookshot', player) and world.get_region('GT Moldorm', player).dungeon.bosses['top'].can_defeat(state))
|
||||
set_defeat_dungeon_boss_rule(world.get_location('Agahnim 2', player))
|
||||
|
||||
@@ -736,9 +741,10 @@ def pot_rules(world, player):
|
||||
for l in world.get_region('Palace of Darkness Hint', player).locations:
|
||||
if l.type == LocationType.Pot:
|
||||
add_rule(l, lambda state: state.can_use_bombs(player) or state.has_Boots(player))
|
||||
for l in world.get_region('Dark Lake Hylia Ledge Spike Cave', player).locations:
|
||||
if l.type == LocationType.Pot:
|
||||
add_rule(l, lambda state: state.world.can_take_damage or state.has('Hookshot', player)
|
||||
for number in ['1', '2']:
|
||||
loc = world.get_location_unsafe(f'Dark Lake Hylia Ledge Spike Cave Pot #{number}', player)
|
||||
if loc and loc.type == LocationType.Pot:
|
||||
add_rule(loc, lambda state: state.world.can_take_damage or state.has('Hookshot', player)
|
||||
or state.has('Cape', player)
|
||||
or (state.has('Cane of Byrna', player)
|
||||
and state.world.difficulty_adjustments[player] == 'normal'))
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user