Merge branch 'DoorDevVolatile' into Customizer

# Conflicts:
#	DoorShuffle.py
This commit is contained in:
aerinon
2022-03-29 13:30:49 -06:00
13 changed files with 102 additions and 26 deletions

View File

@@ -1948,6 +1948,23 @@ def custom_door_kind(custom_key, kind, bd_candidates, counts, world, player):
counts[d_name] += 1 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): def shuffle_bombable_dashable(bd_candidates, world, player):
dash_counts = defaultdict(int) dash_counts = defaultdict(int)
bomb_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(): for dungeon, candidates in bd_candidates.items():
diff = bomb_dash_counts[dungeon.name][1] - dash_counts[dungeon.name] diff = bomb_dash_counts[dungeon.name][1] - dash_counts[dungeon.name]
if diff > 0: 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) change_pair_type(chosen, DoorKind.Dashable, world, player)
candidates.remove(chosen) candidates.remove(chosen)
diff = bomb_dash_counts[dungeon.name][0] - bomb_counts[dungeon.name] 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_dashables = 8 - sum(dash_counts.values(), 0)
desired_bombables = 12 - sum(bomb_counts.values(), 0) desired_bombables = 12 - sum(bomb_counts.values(), 0)
if desired_dashables > 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) change_pair_type(chosen, DoorKind.Dashable, world, player)
all_candidates.remove(chosen) all_candidates.remove(chosen)
if desired_bombables > 0: if desired_bombables > 0:
@@ -2389,6 +2408,8 @@ logical_connections = [
('Skull Pot Circle Star Path', 'Skull Map Room'), ('Skull Pot Circle Star Path', 'Skull Map Room'),
('Skull Big Chest Hookpath', 'Skull 1 Lobby'), ('Skull Big Chest Hookpath', 'Skull 1 Lobby'),
('Skull Back Drop Star Path', 'Skull Small Hall'), ('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 Rail Ledge Drop Down', 'Thieves BK Corner'),
('Thieves Hellway Orange Barrier', 'Thieves Hellway S Crystal'), ('Thieves Hellway Orange Barrier', 'Thieves Hellway S Crystal'),
('Thieves Hellway Crystal Orange Barrier', 'Thieves Hellway'), ('Thieves Hellway Crystal Orange Barrier', 'Thieves Hellway'),

View File

@@ -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 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 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 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 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 Back Drop Star Path', Lgcl),
create_door(player, 'Skull 3 Lobby SW', Nrml).dir(So, 0x59, Left, High).pos(1).portal(Z, 0x02), create_door(player, 'Skull 3 Lobby SW', Nrml).dir(So, 0x59, Left, High).pos(1).portal(Z, 0x02),

View File

@@ -124,10 +124,10 @@ swamp_regions = [
skull_regions = [ skull_regions = [
'Skull 1 Lobby', 'Skull Map Room', 'Skull Pot Circle', 'Skull Pull Switch', 'Skull Big Chest', 'Skull Pinball', '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 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 Lone Pot', 'Skull Small Hall', 'Skull Back Drop', 'Skull 2 West Lobby', 'Skull 2 West Lobby Ledge',
'Skull East Bridge', 'Skull West Bridge Nook', 'Skull Star Pits', 'Skull Torch Room', 'Skull Vines', 'Skull X Room', 'Skull 3 Lobby', 'Skull East Bridge', 'Skull West Bridge Nook', 'Skull Star Pits',
'Skull Spike Corner', 'Skull Final Drop', 'Skull Boss', 'Skull 1 Portal', 'Skull 2 East Portal', 'Skull Torch Room', 'Skull Vines', 'Skull Spike Corner', 'Skull Final Drop', 'Skull Boss',
'Skull 2 West Portal', 'Skull 3 Portal' 'Skull 1 Portal', 'Skull 2 East Portal', 'Skull 2 West Portal', 'Skull 3 Portal'
] ]
thieves_regions = [ thieves_regions = [

42
Fill.py
View File

@@ -2,6 +2,7 @@ import RaceRandom as random
import collections import collections
import itertools import itertools
import logging import logging
import math
from BaseClasses import CollectionState, FillError, LocationType from BaseClasses import CollectionState, FillError, LocationType
from Items import ItemFactory 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 # fill in gtower locations with trash first
for player in range(1, world.players + 1): 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 continue
max_trash = 8 if world.algorithm == 'dungeon_only' else 15 gt_count, total_count = calc_trash_locations(world, player)
gftower_trash_count = (random.randint(15, 50) if world.goal[player] == 'triforcehunt' else random.randint(0, max_trash)) 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) random.shuffle(gtower_locations)
trashcnt = 0 trashcnt = 0
while gtower_locations and restitempool and trashcnt < gftower_trash_count: 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) 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): def ensure_good_pots(world, write_skips=False):
for loc in world.get_locations(): for loc in world.get_locations():
if loc.item is None: 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'} if (loc.item.name in {'Arrows (5)', 'Nothing'}
and (loc.type != LocationType.Pot or loc.item.player != loc.player)): and (loc.type != LocationType.Pot or loc.item.player != loc.player)):
loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.item.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 # don't write out all pots to spoiler
if write_skips: if write_skips:
if loc.type == LocationType.Pot and loc.item.name in valid_pot_items: if loc.type == LocationType.Pot and loc.item.name in valid_pot_items:
loc.skip = True 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): def fast_fill_helper(world, item_pool, fill_locations):

View File

@@ -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.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).event = True
world.get_location('Ice Block Drop', player).locked = 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': if world.mode[player] == 'standard':
world.push_item(world.get_location('Zelda Pickup', player), ItemFactory('Zelda Herself', player), False) world.push_item(world.get_location('Zelda Pickup', player), ItemFactory('Zelda Herself', player), False)
world.get_location('Zelda Pickup', player).event = True world.get_location('Zelda Pickup', player).event = True

View File

@@ -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 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), '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), '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 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), 'Zelda Delivered': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
} }

View File

@@ -583,7 +583,7 @@ def create_playthrough(world):
# get locations containing progress items # get locations containing progress items
prog_locations = [location for location in world.get_filled_locations() if location.item.advancement] 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] state_cache = [None]
collection_spheres = [] collection_spheres = []
state = CollectionState(world) state = CollectionState(world)

View File

@@ -6,6 +6,7 @@ import json
import logging import logging
import re import re
import shlex import shlex
import ssl
import urllib.request import urllib.request
import websockets import websockets
import zlib import zlib
@@ -159,8 +160,7 @@ def send_new_items(ctx : Context):
client.send_index = len(items) client.send_index = len(items)
def forfeit_player(ctx : Context, team, slot): 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 = set(ctx.lookup_id_to_name.keys())
all_locations.update({values[1] for values in Regions.key_drop_data.values()})
notify_all(ctx, "%s (Team #%d) has forfeited" % (ctx.player_names[(team, slot)], team + 1)) notify_all(ctx, "%s (Team #%d) has forfeited" % (ctx.player_names[(team, slot)], team + 1))
register_location_checks(ctx, team, slot, all_locations) register_location_checks(ctx, team, slot, all_locations)
@@ -406,7 +406,7 @@ async def main():
logging.error('Failed to read multiworld data (%s)' % e) logging.error('Failed to read multiworld data (%s)' % e)
return 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)) 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 ctx.disable_save = args.disable_save

View File

@@ -157,9 +157,17 @@ Same as above but both small keys and bigs keys of the dungeon are not allowed o
#### Volatile #### Volatile
* 1.0.1.12 * 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 * 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 * Fix for hammerdashing pots, if sprite limit is reached, items won't spawn, but error beep won't play either because of other SFX
* Killing enemies freeze + hammer results in the droppable item instead of the freeze prize * 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 * 1.0.1.11
* Separated Collection Rate counter from experimental * Separated Collection Rate counter from experimental
* Added MSU Resume option * Added MSU Resume option

View File

@@ -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 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 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 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 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 NW', 'Skull 2 West Lobby S']), 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 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 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']), 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: # unreal events:
for l in ['Ganon', 'Agahnim 1', 'Agahnim 2', 'Dark Blacksmith Ruins', 'Frog', 'Missing Smith', 'Floodgate', 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', '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) location = world.get_location_unsafe(l, player)
if location: if location:
location.type = LocationType.Logical location.type = LocationType.Logical
@@ -1150,6 +1151,7 @@ dungeon_events = [
'Suspicious Maiden', 'Suspicious Maiden',
'Revealing Light', 'Revealing Light',
'Ice Block Drop', 'Ice Block Drop',
'Skull Star Tile',
'Zelda Pickup', 'Zelda Pickup',
'Zelda Drop Off' 'Zelda Drop Off'
] ]
@@ -1388,6 +1390,7 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'),
'Suspicious Maiden': (None, None, False, None), 'Suspicious Maiden': (None, None, False, None),
'Revealing Light': (None, None, False, None), 'Revealing Light': (None, None, False, None),
'Ice Block Drop': (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 Pickup': (None, None, False, None),
'Zelda Drop Off': (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'), 'Eastern Palace - Prize': ([0x1209D, 0x53E76, 0x53E77, 0x180052, 0x180070, 0xC6FE, 0x186FE2], None, True, 'Eastern Palace'),

2
Rom.py
View File

@@ -35,7 +35,7 @@ from source.item.FillUtil import valid_pot_items
JAP10HASH = '03a63945398191337e896e5771f77173' JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = '57df02038c77f9a3b915af21d3c59b78' RANDOMIZERBASEHASH = '1e45d174c71b2e079df1c8d5e8d1451b'
class JsonRom(object): class JsonRom(object):

View File

@@ -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 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 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 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 - Boss', player))
set_defeat_dungeon_boss_rule(world.get_location('Skull Woods - Prize', 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 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 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)) 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_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)) 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: for l in world.get_region('Palace of Darkness Hint', player).locations:
if l.type == LocationType.Pot: if l.type == LocationType.Pot:
add_rule(l, lambda state: state.can_use_bombs(player) or state.has_Boots(player)) 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: for number in ['1', '2']:
if l.type == LocationType.Pot: loc = world.get_location_unsafe(f'Dark Lake Hylia Ledge Spike Cave Pot #{number}', player)
add_rule(l, lambda state: state.world.can_take_damage or state.has('Hookshot', 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('Cape', player)
or (state.has('Cane of Byrna', player) or (state.has('Cane of Byrna', player)
and state.world.difficulty_adjustments[player] == 'normal')) and state.world.difficulty_adjustments[player] == 'normal'))

Binary file not shown.