Initial Prize Shuffle Implementation

commit c89c5d3798e2a777011e90d565d74af792330d9f
commit 4159f2e7097fca648828a60d8f6878211d0ded9e
commit a80e3a4301d69146ccfffe0f2f375adac381e165
commit d8ac588cb904152831f514d8276be4e39a43dcd0
commit 68eb75e3391631355b4f56f1dcb7e9dadadf1fdf
commit ba241b47964eadfb40ad323f87b1117598dd91a6
commit aed2821c7165822f5fd5cc1ff3f58f2af095d915
commit bd1c5d8d35ae3cae5f27f236346fff057b7b8cd7
commit f034e31cc585a1648657fc2c4850ebc0c1d8bf78

Author: codemann8 <codemann8@gmail.com>
This commit is contained in:
codemann8
2024-05-23 18:40:50 -05:00
parent 8b295a74ad
commit 103e098a2e
25 changed files with 573 additions and 331 deletions
+75 -31
View File
@@ -142,6 +142,7 @@ class World(object):
set_player_attr('compassshuffle', False) set_player_attr('compassshuffle', False)
set_player_attr('keyshuffle', 'none') set_player_attr('keyshuffle', 'none')
set_player_attr('bigkeyshuffle', False) set_player_attr('bigkeyshuffle', False)
set_player_attr('prizeshuffle', 'none')
set_player_attr('restrict_boss_items', 'none') set_player_attr('restrict_boss_items', 'none')
set_player_attr('bombbag', False) set_player_attr('bombbag', False)
set_player_attr('flute_mode', False) set_player_attr('flute_mode', False)
@@ -473,7 +474,8 @@ class World(object):
def push_precollected(self, item): def push_precollected(self, item):
item.world = self item.world = self
if ((item.smallkey and self.keyshuffle[item.player] != 'none') if ((item.prize and self.prizeshuffle[item.player] != 'none')
or (item.smallkey and self.keyshuffle[item.player] != 'none')
or (item.bigkey and self.bigkeyshuffle[item.player])): or (item.bigkey and self.bigkeyshuffle[item.player])):
item.advancement = True item.advancement = True
self.precollected_items.append(item) self.precollected_items.append(item)
@@ -1607,7 +1609,8 @@ class Region(object):
inside_dungeon_item = ((item.smallkey and self.world.keyshuffle[item.player] == 'none') inside_dungeon_item = ((item.smallkey and self.world.keyshuffle[item.player] == 'none')
or (item.bigkey and not self.world.bigkeyshuffle[item.player]) or (item.bigkey and not self.world.bigkeyshuffle[item.player])
or (item.map and not self.world.mapshuffle[item.player]) or (item.map and not self.world.mapshuffle[item.player])
or (item.compass and not self.world.compassshuffle[item.player])) or (item.compass and not self.world.compassshuffle[item.player])
or (item.prize and self.world.prizeshuffle[item.player] == 'dungeon'))
# not all small keys to escape must be in escape # not all small keys to escape must be in escape
# sewer_hack = self.world.mode[item.player] == 'standard' and item.name == 'Small Key (Escape)' # sewer_hack = self.world.mode[item.player] == 'standard' and item.name == 'Small Key (Escape)'
if inside_dungeon_item: if inside_dungeon_item:
@@ -1840,6 +1843,7 @@ class Dungeon(object):
def __init__(self, name, regions, big_key, small_keys, dungeon_items, player, dungeon_id): def __init__(self, name, regions, big_key, small_keys, dungeon_items, player, dungeon_id):
self.name = name self.name = name
self.regions = regions self.regions = regions
self.prize = None
self.big_key = big_key self.big_key = big_key
self.small_keys = small_keys self.small_keys = small_keys
self.dungeon_items = dungeon_items self.dungeon_items = dungeon_items
@@ -1864,10 +1868,13 @@ class Dungeon(object):
@property @property
def all_items(self): def all_items(self):
return self.dungeon_items + self.keys return self.dungeon_items + self.keys + ([self.prize] if self.prize else [])
def is_dungeon_item(self, item): def is_dungeon_item(self, item):
return item.player == self.player and item.name in [dungeon_item.name for dungeon_item in self.all_items] if item.prize:
return item.player == self.player and self.prize is None and self.name not in ['Hyrule Castle', 'Agahnims Tower', 'Ganons Tower']
else:
return item.player == self.player and item.name in [dungeon_item.name for dungeon_item in self.all_items]
def count_dungeon_item(self): def count_dungeon_item(self):
return len(self.dungeon_items) + 1 if self.big_key_required else 0 + self.key_number return len(self.dungeon_items) + 1 if self.big_key_required else 0 + self.key_number
@@ -2621,7 +2628,7 @@ class Boss(object):
class Location(object): class Location(object):
def __init__(self, player, name='', address=None, crystal=False, hint_text=None, parent=None, forced_item=None, def __init__(self, player, name='', address=None, prize=False, hint_text=None, parent=None, forced_item=None,
player_address=None, note=None): player_address=None, note=None):
self.name = name self.name = name
self.parent_region = parent self.parent_region = parent
@@ -2635,7 +2642,7 @@ class Location(object):
self.forced_item = None self.forced_item = None
self.item = None self.item = None
self.event = False self.event = False
self.crystal = crystal self.prize = prize
self.address = address self.address = address
self.player_address = player_address self.player_address = player_address
self.spot_type = 'Location' self.spot_type = 'Location'
@@ -2643,14 +2650,14 @@ class Location(object):
self.recursion_count = 0 self.recursion_count = 0
self.staleness_count = 0 self.staleness_count = 0
self.locked = False self.locked = False
self.real = not crystal self.real = not prize
self.always_allow = lambda item, state: False self.always_allow = lambda item, state: False
self.access_rule = lambda state: True self.access_rule = lambda state: True
self.verbose_rule = None self.verbose_rule = None
self.item_rule = lambda item: True self.item_rule = lambda item: True
self.player = player self.player = player
self.skip = False self.skip = False
self.type = LocationType.Normal if not crystal else LocationType.Prize self.type = LocationType.Normal if not prize else LocationType.Prize
self.pot = None self.pot = None
self.drop = None self.drop = None
self.note = note self.note = note
@@ -2738,8 +2745,8 @@ class Item(object):
self.player = player self.player = player
@property @property
def crystal(self): def prize(self):
return self.type == 'Crystal' return self.type == 'Prize'
@property @property
def smallkey(self): def smallkey(self):
@@ -2767,11 +2774,33 @@ class Item(object):
return item_dungeon return item_dungeon
def is_inside_dungeon_item(self, world): def is_inside_dungeon_item(self, world):
return ((self.smallkey and world.keyshuffle[self.player] == 'none') return ((self.prize and world.prizeshuffle[self.player] in ['none', 'dungeon'])
or (self.smallkey and world.keyshuffle[self.player] == 'none')
or (self.bigkey and not world.bigkeyshuffle[self.player]) or (self.bigkey and not world.bigkeyshuffle[self.player])
or (self.compass and not world.compassshuffle[self.player]) or (self.compass and not world.compassshuffle[self.player])
or (self.map and not world.mapshuffle[self.player])) or (self.map and not world.mapshuffle[self.player]))
def get_map_location(self):
if self.location:
if self.location.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]:
return self.location
else:
def explore_region(region):
explored_regions.append(region.name)
for ent in region.entrances:
if ent.parent_region is not None:
if ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]:
return ent
elif ent.parent_region.name not in explored_regions:
ret = explore_region(ent.parent_region)
if ret:
return ret
return None
explored_regions = list()
return explore_region(self.location.parent_region)
return None
def __str__(self): def __str__(self):
return str(self.__unicode__()) return str(self.__unicode__())
@@ -2983,6 +3012,7 @@ class Spoiler(object):
'compassshuffle': self.world.compassshuffle, 'compassshuffle': self.world.compassshuffle,
'keyshuffle': self.world.keyshuffle, 'keyshuffle': self.world.keyshuffle,
'bigkeyshuffle': self.world.bigkeyshuffle, 'bigkeyshuffle': self.world.bigkeyshuffle,
'prizeshuffle': self.world.prizeshuffle,
'boss_shuffle': self.world.boss_shuffle, 'boss_shuffle': self.world.boss_shuffle,
'enemy_shuffle': self.world.enemy_shuffle, 'enemy_shuffle': self.world.enemy_shuffle,
'enemy_health': self.world.enemy_health, 'enemy_health': self.world.enemy_health,
@@ -3023,6 +3053,13 @@ class Spoiler(object):
self.medallions[f'Misery Mire ({self.world.get_player_names(player)})'] = self.world.required_medallions[player][0] self.medallions[f'Misery Mire ({self.world.get_player_names(player)})'] = self.world.required_medallions[player][0]
self.medallions[f'Turtle Rock ({self.world.get_player_names(player)})'] = self.world.required_medallions[player][1] self.medallions[f'Turtle Rock ({self.world.get_player_names(player)})'] = self.world.required_medallions[player][1]
self.prizes = OrderedDict()
for player in range(1, self.world.players + 1):
player_name = '' if self.world.players == 1 else str(' (Player ' + str(player) + ')')
for dungeon in self.world.dungeons:
if dungeon.player == player and dungeon.prize:
self.prizes[dungeon.name + player_name] = dungeon.prize.name
self.bottles = OrderedDict() self.bottles = OrderedDict()
if self.world.players == 1: if self.world.players == 1:
self.bottles['Waterfall Bottle'] = self.world.bottle_refills[1][0] self.bottles['Waterfall Bottle'] = self.world.bottle_refills[1][0]
@@ -3033,7 +3070,7 @@ class Spoiler(object):
self.bottles[f'Pyramid Bottle ({self.world.get_player_names(player)})'] = self.world.bottle_refills[player][1] self.bottles[f'Pyramid Bottle ({self.world.get_player_names(player)})'] = self.world.bottle_refills[player][1]
def include_item(item): def include_item(item):
return 'all' in self.settings or ('items' in self.settings and not item.crystal) or ('prizes' in self.settings and item.crystal) return 'all' in self.settings or ('items' in self.settings and not item.prize) or ('prizes' in self.settings and item.prize)
self.locations = OrderedDict() self.locations = OrderedDict()
listed_locations = set() listed_locations = set()
@@ -3214,6 +3251,7 @@ class Spoiler(object):
outfile.write('Compass Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['compassshuffle'][player])) outfile.write('Compass Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['compassshuffle'][player]))
outfile.write('Small Key Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['keyshuffle'][player]) outfile.write('Small Key Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['keyshuffle'][player])
outfile.write('Big Key Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['bigkeyshuffle'][player])) outfile.write('Big Key Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['bigkeyshuffle'][player]))
outfile.write('Prize Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['prizeshuffle'][player])
outfile.write('Key Logic Algorithm:'.ljust(line_width) + '%s\n' % self.metadata['key_logic'][player]) outfile.write('Key Logic Algorithm:'.ljust(line_width) + '%s\n' % self.metadata['key_logic'][player])
outfile.write('\n') outfile.write('\n')
outfile.write('Door Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['door_shuffle'][player]) outfile.write('Door Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['door_shuffle'][player])
@@ -3282,6 +3320,9 @@ class Spoiler(object):
outfile.write(str('Crystals Required for GT' + player_name + ':').ljust(line_width) + '%s\n' % (str(self.metadata['gt_crystals'][player]))) outfile.write(str('Crystals Required for GT' + player_name + ':').ljust(line_width) + '%s\n' % (str(self.metadata['gt_crystals'][player])))
if self.world.crystals_ganon_orig[player] == 'random': if self.world.crystals_ganon_orig[player] == 'random':
outfile.write(str('Crystals Required for Ganon' + player_name + ':').ljust(line_width) + '%s\n' % (str(self.metadata['ganon_crystals'][player]))) outfile.write(str('Crystals Required for Ganon' + player_name + ':').ljust(line_width) + '%s\n' % (str(self.metadata['ganon_crystals'][player])))
outfile.write('\n\nPrizes:\n\n')
for dungeon, prize in self.prizes.items():
outfile.write(str(dungeon + ':').ljust(line_width) + '%s\n' % prize)
if 'all' in self.settings or 'misc' in self.settings: if 'all' in self.settings or 'misc' in self.settings:
outfile.write('\n\nBottle Refills:\n\n') outfile.write('\n\nBottle Refills:\n\n')
@@ -3539,7 +3580,7 @@ dr_mode = {"basic": 1, "crossed": 2, "vanilla": 0, "partitioned": 3, 'paired': 4
er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4, "insanity": 5, 'lite': 8, er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4, "insanity": 5, 'lite': 8,
'lean': 9, "dungeonsfull": 7, "dungeonssimple": 6, "swapped": 10, "district": 11} 'lean': 9, "dungeonsfull": 7, "dungeonssimple": 6, "swapped": 10, "district": 11}
# byte 1: LLLW WSS? (logic, mode, sword) # byte 1: LLLW WSSB (logic, mode, sword, bombbag)
logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4, "hybridglitches": 5} logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4, "hybridglitches": 5}
world_mode = {"open": 0, "standard": 1, "inverted": 2} world_mode = {"open": 0, "standard": 1, "inverted": 2}
sword_mode = {"random": 0, "assured": 1, "swordless": 2, "vanilla": 3} sword_mode = {"random": 0, "assured": 1, "swordless": 2, "vanilla": 3}
@@ -3550,12 +3591,12 @@ goal_mode = {'ganon': 0, 'pedestal': 1, 'dungeons': 2, 'triforcehunt': 3, 'cryst
diff_mode = {"normal": 0, "hard": 1, "expert": 2} diff_mode = {"normal": 0, "hard": 1, "expert": 2}
func_mode = {"normal": 0, "hard": 1, "expert": 2} func_mode = {"normal": 0, "hard": 1, "expert": 2}
# byte 3: SDMM PIII (shop, decouple doors, mixed, palettes, intensity) # byte 3: SDMM PIII (shop, decouple doors, mixed travel, palettes, intensity)
# keydrop now has it's own byte # keydrop now has it's own byte
mixed_travel_mode = {"prevent": 0, "allow": 1, "force": 2} mixed_travel_mode = {"prevent": 0, "allow": 1, "force": 2}
# intensity is 3 bits (reserves 4-7 levels) # intensity is 3 bits (reserves 4-7 levels)
# new byte 4: TDDD PPPP (tavern shuffle, drop, pottery) # byte 4: TDDD PPPP (tavern shuffle, drop, pottery)
# dropshuffle reserves 2 bits, pottery needs 4) # dropshuffle reserves 2 bits, pottery needs 4)
drop_shuffle_mode = {'none': 0, 'keys': 1, 'underworld': 2} drop_shuffle_mode = {'none': 0, 'keys': 1, 'underworld': 2}
pottery_mode = {'none': 0, 'keys': 2, 'lottery': 3, 'dungeon': 4, 'cave': 5, 'cavekeys': 6, 'reduced': 7, pottery_mode = {'none': 0, 'keys': 2, 'lottery': 3, 'dungeon': 4, 'cave': 5, 'cavekeys': 6, 'reduced': 7,
@@ -3564,17 +3605,17 @@ pottery_mode = {'none': 0, 'keys': 2, 'lottery': 3, 'dungeon': 4, 'cave': 5, 'ca
# byte 5: SCCC CTTX (self-loop doors, crystals gt, ctr2, experimental) # byte 5: SCCC CTTX (self-loop doors, crystals gt, ctr2, experimental)
counter_mode = {"default": 0, "off": 1, "on": 2, "pickup": 3} counter_mode = {"default": 0, "off": 1, "on": 2, "pickup": 3}
# byte 6: ?CCC CPAA (crystals ganon, pyramid, access # byte 6: LCCC CPAA (shuffle links, crystals ganon, pyramid, access
access_mode = {"items": 0, "locations": 1, "none": 2} access_mode = {"items": 0, "locations": 1, "none": 2}
# byte 7: B?MC DDEE (big, ?, maps, compass, door_type, enemies) # byte 7: B?MC DDPP (big, ?, maps, compass, door_type, prize shuffle)
door_type_mode = {'original': 0, 'big': 1, 'all': 2, 'chaos': 3} door_type_mode = {'original': 0, 'big': 1, 'all': 2, 'chaos': 3}
enemy_mode = {"none": 0, "shuffled": 1, "chaos": 2, "random": 2, "legacy": 3} prizeshuffle_mode = {'none': 0, 'dungeon': 1, 'wild': 3}
# byte 8: HHHD DPBS (enemy_health, enemy_dmg, potshuffle, bomb logic, shuffle links) # byte 8: HHHD DPEE (enemy_health, enemy_dmg, potshuffle, enemies)
# potshuffle decprecated, now unused
e_health = {"default": 0, "easy": 1, "normal": 2, "hard": 3, "expert": 4} e_health = {"default": 0, "easy": 1, "normal": 2, "hard": 3, "expert": 4}
e_dmg = {"default": 0, "shuffled": 1, "random": 2} e_dmg = {"default": 0, "shuffled": 1, "random": 2}
enemy_mode = {"none": 0, "shuffled": 1, "chaos": 2, "random": 2, "legacy": 3}
# byte 9: RRAA ABBB (restrict boss mode, algorithm, boss shuffle) # byte 9: RRAA ABBB (restrict boss mode, algorithm, boss shuffle)
rb_mode = {"none": 0, "mapcompass": 1, "dungeon": 2} rb_mode = {"none": 0, "mapcompass": 1, "dungeon": 2}
@@ -3593,9 +3634,9 @@ flutespot_mode = {"vanilla": 0, "balanced": 1, "random": 2}
# byte 13: FBBB TTSS (flute_mode, bow_mode, take_any, small_key_mode) # byte 13: FBBB TTSS (flute_mode, bow_mode, take_any, small_key_mode)
flute_mode = {'normal': 0, 'active': 1} flute_mode = {'normal': 0, 'active': 1}
keyshuffle_mode = {'none': 0, 'off': 0, 'wild': 1, 'on': 1, 'universal': 2} # reserved 8 modes? bow_mode = {'progressive': 0, 'silvers': 1, 'retro': 2, 'retro_silvers': 3} # reserved 8 modes?
take_any_mode = {'none': 0, 'random': 1, 'fixed': 2} take_any_mode = {'none': 0, 'random': 1, 'fixed': 2}
bow_mode = {'progressive': 0, 'silvers': 1, 'retro': 2, 'retro_silvers': 3} keyshuffle_mode = {'none': 0, 'off': 0, 'wild': 1, 'on': 1, 'universal': 2}
# additions # additions
# byte 14: POOT TKKK (pseudoboots, overworld_map, trap_door_mode, key_logic_algo) # byte 14: POOT TKKK (pseudoboots, overworld_map, trap_door_mode, key_logic_algo)
@@ -3617,7 +3658,7 @@ class Settings(object):
(dr_mode[w.doorShuffle[p]] << 5) | er_mode[w.shuffle[p]], (dr_mode[w.doorShuffle[p]] << 5) | er_mode[w.shuffle[p]],
(logic_mode[w.logic[p]] << 5) | (world_mode[w.mode[p]] << 3) (logic_mode[w.logic[p]] << 5) | (world_mode[w.mode[p]] << 3)
| (sword_mode[w.swords[p]] << 1), | (sword_mode[w.swords[p]] << 1) | (0x1 if w.bombbag[p] else 0),
(goal_mode[w.goal[p]] << 5) | (diff_mode[w.difficulty[p]] << 3) (goal_mode[w.goal[p]] << 5) | (diff_mode[w.difficulty[p]] << 3)
| (func_mode[w.difficulty_adjustments[p]] << 1) | (1 if w.hints[p] else 0), | (func_mode[w.difficulty_adjustments[p]] << 1) | (1 if w.hints[p] else 0),
@@ -3633,15 +3674,15 @@ class Settings(object):
| ((8 if w.crystals_gt_orig[p] == "random" else int(w.crystals_gt_orig[p])) << 3) | ((8 if w.crystals_gt_orig[p] == "random" else int(w.crystals_gt_orig[p])) << 3)
| (counter_mode[w.dungeon_counters[p]] << 1) | (1 if w.experimental[p] else 0), | (counter_mode[w.dungeon_counters[p]] << 1) | (1 if w.experimental[p] else 0),
((8 if w.crystals_ganon_orig[p] == "random" else int(w.crystals_ganon_orig[p])) << 3) (0x80 if w.shufflelinks[p] else 0) | ((8 if w.crystals_ganon_orig[p] == "random" else int(w.crystals_ganon_orig[p])) << 3)
| (0x4 if w.is_pyramid_open(p) else 0) | access_mode[w.accessibility[p]], | (0x4 if w.is_pyramid_open(p) else 0) | access_mode[w.accessibility[p]],
(0x80 if w.bigkeyshuffle[p] else 0) (0x80 if w.bigkeyshuffle[p] else 0)
| (0x20 if w.mapshuffle[p] else 0) | (0x10 if w.compassshuffle[p] else 0) | (0x20 if w.mapshuffle[p] else 0) | (0x10 if w.compassshuffle[p] else 0)
| (door_type_mode[w.door_type_mode[p]] << 2) | (enemy_mode[w.enemy_shuffle[p]]), | (door_type_mode[w.door_type_mode[p]] << 2) | prizeshuffle_mode[w.prizeshuffle[p]],
(e_health[w.enemy_health[p]] << 5) | (e_dmg[w.enemy_damage[p]] << 3) | (0x4 if w.potshuffle[p] else 0) (e_health[w.enemy_health[p]] << 5) | (e_dmg[w.enemy_damage[p]] << 3)
| (0x2 if w.bombbag[p] else 0) | (1 if w.shufflelinks[p] else 0), | (0x4 if w.potshuffle[p] else 0) | (enemy_mode[w.enemy_shuffle[p]]),
(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]]),
@@ -3675,15 +3716,19 @@ class Settings(object):
args.shuffle[p] = r(er_mode)[settings[0] & 0x1F] args.shuffle[p] = r(er_mode)[settings[0] & 0x1F]
args.door_shuffle[p] = r(dr_mode)[(settings[0] & 0xE0) >> 5] args.door_shuffle[p] = r(dr_mode)[(settings[0] & 0xE0) >> 5]
args.logic[p] = r(logic_mode)[(settings[1] & 0xE0) >> 5] args.logic[p] = r(logic_mode)[(settings[1] & 0xE0) >> 5]
args.mode[p] = r(world_mode)[(settings[1] & 0x18) >> 3] args.mode[p] = r(world_mode)[(settings[1] & 0x18) >> 3]
args.swords[p] = r(sword_mode)[(settings[1] & 0x6) >> 1] args.swords[p] = r(sword_mode)[(settings[1] & 0x6) >> 1]
args.bombbag[p] = True if settings[1] & 0x1 else False
args.difficulty[p] = r(diff_mode)[(settings[2] & 0x18) >> 3] args.difficulty[p] = r(diff_mode)[(settings[2] & 0x18) >> 3]
args.item_functionality[p] = r(func_mode)[(settings[2] & 0x6) >> 1] args.item_functionality[p] = r(func_mode)[(settings[2] & 0x6) >> 1]
args.goal[p] = r(goal_mode)[(settings[2] & 0xE0) >> 5] args.goal[p] = r(goal_mode)[(settings[2] & 0xE0) >> 5]
args.accessibility[p] = r(access_mode)[settings[6] & 0x3] args.accessibility[p] = r(access_mode)[settings[6] & 0x3]
# args.retro[p] = True if settings[1] & 0x01 else False # args.retro[p] = True if settings[1] & 0x01 else False
args.hints[p] = True if settings[2] & 0x01 else False args.hints[p] = True if settings[2] & 0x01 else False
args.shopsanity[p] = True if settings[3] & 0x80 else False args.shopsanity[p] = True if settings[3] & 0x80 else False
args.decoupledoors[p] = True if settings[3] & 0x40 else False args.decoupledoors[p] = True if settings[3] & 0x40 else False
args.mixed_travel[p] = r(mixed_travel_mode)[(settings[3] & 0x30) >> 4] args.mixed_travel[p] = r(mixed_travel_mode)[(settings[3] & 0x30) >> 4]
@@ -3701,22 +3746,21 @@ class Settings(object):
args.crystals_gt[p] = "random" if cgt == 8 else cgt args.crystals_gt[p] = "random" if cgt == 8 else cgt
args.experimental[p] = True if settings[5] & 0x1 else False args.experimental[p] = True if settings[5] & 0x1 else False
args.shufflelinks[p] = True if settings[6] & 0x80 else False
cgan = (settings[6] & 0x78) >> 3 cgan = (settings[6] & 0x78) >> 3
args.crystals_ganon[p] = "random" if cgan == 8 else cgan args.crystals_ganon[p] = "random" if cgan == 8 else cgan
args.openpyramid[p] = True if settings[6] & 0x4 else False args.openpyramid[p] = True if settings[6] & 0x4 else False
args.bigkeyshuffle[p] = True if settings[7] & 0x80 else False args.bigkeyshuffle[p] = True if settings[7] & 0x80 else False
# args.keyshuffle[p] = True if settings[7] & 0x40 else False
args.mapshuffle[p] = True if settings[7] & 0x20 else False args.mapshuffle[p] = True if settings[7] & 0x20 else False
args.compassshuffle[p] = True if settings[7] & 0x10 else False args.compassshuffle[p] = True if settings[7] & 0x10 else False
args.door_type_mode[p] = r(door_type_mode)[(settings[7] & 0xc) >> 2] args.door_type_mode[p] = r(door_type_mode)[(settings[7] & 0xc) >> 2]
args.shuffleenemies[p] = r(enemy_mode)[settings[7] & 0x3] args.prizeshuffle[p] = r(prizeshuffle_mode)[settings[7] & 0x3]
args.enemy_health[p] = r(e_health)[(settings[8] & 0xE0) >> 5] args.enemy_health[p] = r(e_health)[(settings[8] & 0xE0) >> 5]
args.enemy_damage[p] = r(e_dmg)[(settings[8] & 0x18) >> 3] args.enemy_damage[p] = r(e_dmg)[(settings[8] & 0x18) >> 3]
args.shufflepots[p] = True if settings[8] & 0x4 else False args.shufflepots[p] = True if settings[8] & 0x4 else False
args.bombbag[p] = True if settings[8] & 0x2 else False args.shuffleenemies[p] = r(enemy_mode)[settings[8] & 0x3]
args.shufflelinks[p] = True if settings[8] & 0x1 else False
if len(settings) > 9: if len(settings) > 9:
args.restrict_boss_items[p] = r(rb_mode)[(settings[9] & 0xC0) >> 6] args.restrict_boss_items[p] = r(rb_mode)[(settings[9] & 0xC0) >> 6]
+2 -1
View File
@@ -135,7 +135,7 @@ def parse_cli(argv, no_defaults=False):
'ow_terrain', 'ow_crossed', 'ow_keepsimilar', 'ow_mixed', 'ow_whirlpool', 'ow_fluteshuffle', 'ow_terrain', 'ow_crossed', 'ow_keepsimilar', 'ow_mixed', 'ow_whirlpool', 'ow_fluteshuffle',
'flute_mode', 'bow_mode', 'take_any', 'boots_hint', 'flute_mode', 'bow_mode', 'take_any', 'boots_hint',
'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', 'prizeshuffle', 'startinventory',
'usestartinventory', 'bombbag', 'shuffleganon', 'overworld_map', 'restrict_boss_items', 'usestartinventory', 'bombbag', 'shuffleganon', 'overworld_map', 'restrict_boss_items',
'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max', 'triforce_max_difference', 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max', 'triforce_max_difference',
'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'shuffletavern', 'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'shuffletavern',
@@ -223,6 +223,7 @@ def parse_settings():
"compassshuffle": False, "compassshuffle": False,
"keyshuffle": "none", "keyshuffle": "none",
"bigkeyshuffle": False, "bigkeyshuffle": False,
"prizeshuffle": "none",
"keysanity": False, "keysanity": False,
"door_shuffle": "vanilla", "door_shuffle": "vanilla",
"intensity": 3, "intensity": 3,
+1 -1
View File
@@ -1629,7 +1629,7 @@ def refine_hints(dungeon_builders):
for name, builder in dungeon_builders.items(): for name, builder in dungeon_builders.items():
for region in builder.master_sector.regions: for region in builder.master_sector.regions:
for location in region.locations: for location in region.locations:
if not location.event and '- Boss' not in location.name and '- Prize' not in location.name and location.name != 'Sanctuary': if not location.event and '- Boss' not in location.name and not location.prize and location.name != 'Sanctuary':
if location.type == LocationType.Pot and location.pot: if location.type == LocationType.Pot and location.pot:
hint_text = ('under a block' if location.pot.flags & PotFlags.Block else 'in a pot') hint_text = ('under a block' if location.pot.flags & PotFlags.Block else 'in a pot')
location.hint_text = f'{hint_text} {dungeon_hints[name]}' location.hint_text = f'{hint_text} {dungeon_hints[name]}'
+10 -8
View File
@@ -11,7 +11,7 @@ from typing import List
from BaseClasses import DoorType, Direction, CrystalBarrier, RegionType, Polarity, PolSlot, flooded_keys, Sector from BaseClasses import DoorType, Direction, CrystalBarrier, RegionType, Polarity, PolSlot, flooded_keys, Sector
from BaseClasses import Hook, hook_from_door, Door from BaseClasses import Hook, hook_from_door, Door
from Regions import dungeon_events, flooded_keys_reverse from Regions import location_events, flooded_keys_reverse
from Dungeons import split_region_starts from Dungeons import split_region_starts
from RoomData import DoorKind from RoomData import DoorKind
@@ -884,19 +884,19 @@ class ExplorationState(object):
if key_checks and location not in self.found_locations: if key_checks and location not in self.found_locations:
if location.forced_item and 'Small Key' in location.item.name: if location.forced_item and 'Small Key' in location.item.name:
self.key_locations += 1 self.key_locations += 1
if location.name not in dungeon_events and '- Prize' not in location.name and location.name not in ['Agahnim 1', 'Agahnim 2']: if location.name not in location_events and not location.prize:
self.ttl_locations += 1 self.ttl_locations += 1
if location not in self.found_locations: if location not in self.found_locations:
self.found_locations.append(location) self.found_locations.append(location)
if not bk_flag and (not location.forced_item or 'Big Key' in location.item.name): if not bk_flag and (not location.forced_item or 'Big Key' in location.item.name):
self.bk_found.add(location) self.bk_found.add(location)
if location.name in dungeon_events and location.name not in self.events: if location.name in location_events and location.name not in self.events:
if self.flooded_key_check(location): if self.flooded_key_check(location):
self.perform_event(location.name, key_region) self.perform_event(location.name, key_region)
if location.name in flooded_keys_reverse.keys() and self.location_found( if location.name in flooded_keys_reverse.keys() and self.location_found(
flooded_keys_reverse[location.name]): flooded_keys_reverse[location.name]):
self.perform_event(flooded_keys_reverse[location.name], key_region) self.perform_event(flooded_keys_reverse[location.name], key_region)
if '- Prize' in location.name: if location.prize:
self.prize_received = True self.prize_received = True
def flooded_key_check(self, location): def flooded_key_check(self, location):
@@ -1096,7 +1096,7 @@ def count_locations_exclude_big_chest(locations, world, player):
def prize_or_event(loc): def prize_or_event(loc):
return loc.name in dungeon_events or '- Prize' in loc.name or loc.name in ['Agahnim 1', 'Agahnim 2'] return loc.name in location_events or loc.prize
def reserved_location(loc, world, player): def reserved_location(loc, world, player):
@@ -1557,13 +1557,13 @@ def define_sector_features(sectors):
for sector in sectors: for sector in sectors:
for region in sector.regions: for region in sector.regions:
for loc in region.locations: for loc in region.locations:
if '- Prize' in loc.name or loc.name in ['Agahnim 1', 'Agahnim 2']: if loc.prize or loc.name in ['Agahnim 1', 'Agahnim 2']:
pass pass
elif loc.forced_item and 'Small Key' in loc.item.name: elif loc.forced_item and 'Small Key' in loc.item.name:
sector.key_only_locations += 1 sector.key_only_locations += 1
elif loc.forced_item and loc.forced_item.bigkey: elif loc.forced_item and loc.forced_item.bigkey:
sector.bk_provided = True sector.bk_provided = True
elif loc.name not in dungeon_events and not loc.forced_item: elif loc.name not in location_events and not loc.forced_item:
sector.chest_locations += 1 sector.chest_locations += 1
sector.chest_location_set.add(loc.name) sector.chest_location_set.add(loc.name)
if '- Big Chest' in loc.name or loc.name in ["Hyrule Castle - Zelda's Chest", if '- Big Chest' in loc.name or loc.name in ["Hyrule Castle - Zelda's Chest",
@@ -1773,6 +1773,8 @@ def build_orig_location_set(dungeon_map):
def requested_dungeon_items(world, player): def requested_dungeon_items(world, player):
num = 0 num = 0
if world.prizeshuffle[player] == 'dungeon':
num += 1
if not world.bigkeyshuffle[player]: if not world.bigkeyshuffle[player]:
num += 1 num += 1
if not world.compassshuffle[player]: if not world.compassshuffle[player]:
@@ -4055,7 +4057,7 @@ def calc_door_equation(door, sector, look_for_entrance, sewers_flag=None):
crystal_barrier = CrystalBarrier.Either crystal_barrier = CrystalBarrier.Either
# todo: backtracking from double switch with orange on-- # todo: backtracking from double switch with orange on--
for loc in region.locations: for loc in region.locations:
if loc.name in dungeon_events: if loc.name in location_events:
found_events.add(loc.name) found_events.add(loc.name)
for d in event_doors: for d in event_doors:
if loc.name == d.req_event: if loc.name == d.req_event:
+26 -25
View File
@@ -34,16 +34,16 @@ def create_dungeons(world, player):
world.dungeons += [ES, EP, DP, ToH, AT, PoD, TT, SW, SP, IP, MM, TR, GT] world.dungeons += [ES, EP, DP, ToH, AT, PoD, TT, SW, SP, IP, MM, TR, GT]
dungeon_music_addresses = {'Eastern Palace - Prize': [0x1559A], dungeon_music_addresses = {'Eastern Palace': [0x1559A],
'Desert Palace - Prize': [0x1559B, 0x1559C, 0x1559D, 0x1559E], 'Desert Palace': [0x1559B, 0x1559C, 0x1559D, 0x1559E],
'Tower of Hera - Prize': [0x155C5, 0x1107A, 0x10B8C], 'Tower of Hera': [0x155C5, 0x1107A, 0x10B8C],
'Palace of Darkness - Prize': [0x155B8], 'Palace of Darkness': [0x155B8],
'Swamp Palace - Prize': [0x155B7], 'Swamp Palace': [0x155B7],
'Thieves\' Town - Prize': [0x155C6], 'Thieves Town': [0x155C6],
'Skull Woods - Prize': [0x155BA, 0x155BB, 0x155BC, 0x155BD, 0x15608, 0x15609, 0x1560A, 0x1560B], 'Skull Woods': [0x155BA, 0x155BB, 0x155BC, 0x155BD, 0x15608, 0x15609, 0x1560A, 0x1560B],
'Ice Palace - Prize': [0x155BF], 'Ice Palace': [0x155BF],
'Misery Mire - Prize': [0x155B9], 'Misery Mire': [0x155B9],
'Turtle Rock - Prize': [0x155C7, 0x155A7, 0x155AA, 0x155AB]} 'Turtle Rock': [0x155C7, 0x155A7, 0x155AA, 0x155AB]}
hyrule_castle_regions = [ hyrule_castle_regions = [
'Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby', 'Hyrule Castle East Hall', 'Hyrule Castle Lobby', 'Hyrule Castle West Lobby', 'Hyrule Castle East Lobby', 'Hyrule Castle East Hall',
@@ -270,7 +270,7 @@ flexible_starts = {
class DungeonInfo: class DungeonInfo:
def __init__(self, free, keys, bk, map, compass, bk_drop, drops, prize, midx): def __init__(self, free, keys, bk, map, compass, bk_drop, drops, prize, dungeon_idx, extra_map_idx):
# todo reduce static maps ideas: prize, bk_name, sm_name, cmp_name, map_name): # todo reduce static maps ideas: prize, bk_name, sm_name, cmp_name, map_name):
self.free_items = free self.free_items = free
self.key_num = keys self.key_num = keys
@@ -281,23 +281,24 @@ class DungeonInfo:
self.key_drops = drops self.key_drops = drops
self.prize = prize self.prize = prize
self.map_index = midx self.dungeon_index = dungeon_idx
self.extra_map_index = extra_map_idx
dungeon_table = { dungeon_table = {
'Hyrule Castle': DungeonInfo(6, 1, False, True, False, True, 3, None, 0xc), 'Hyrule Castle': DungeonInfo(6, 1, False, True, False, True, 3, None, 0x00, 0x00),
'Eastern Palace': DungeonInfo(3, 0, True, True, True, False, 2, 'Eastern Palace - Prize', 0x0), 'Eastern Palace': DungeonInfo(3, 0, True, True, True, False, 2, [0x1209D, 0x53F3A, 0x53F3B, 0x180052, 0x180070, 0x186FE2], 0x04, None),
'Desert Palace': DungeonInfo(2, 1, True, True, True, False, 3, 'Desert Palace - Prize', 0x2), 'Desert Palace': DungeonInfo(2, 1, True, True, True, False, 3, [0x1209E, 0x53F3C, 0x53F3D, 0x180053, 0x180072, 0x186FE3], 0x06, 0x12),
'Tower of Hera': DungeonInfo(2, 1, True, True, True, False, 0, 'Tower of Hera - Prize', 0x1), 'Tower of Hera': DungeonInfo(2, 1, True, True, True, False, 0, [0x120A5, 0x53F4A, 0x53F4B, 0x18005A, 0x180071, 0x186FEA], 0x14, None),
'Agahnims Tower': DungeonInfo(0, 2, False, False, False, False, 2, None, 0xb), 'Agahnims Tower': DungeonInfo(0, 2, False, False, False, False, 2, None, 0x08, None),
'Palace of Darkness': DungeonInfo(5, 6, True, True, True, False, 0, 'Palace of Darkness - Prize', 0x3), 'Palace of Darkness': DungeonInfo(5, 6, True, True, True, False, 0, [0x120A1, 0x53F22, 0x53F43, 0x180056, 0x180073, 0x186FE6], 0x0C, None),
'Swamp Palace': DungeonInfo(6, 1, True, True, True, False, 5, 'Swamp Palace - Prize', 0x9), 'Swamp Palace': DungeonInfo(6, 1, True, True, True, False, 5, [0x120A0, 0x53F40, 0x53F41, 0x180055, 0x180079, 0x186FE5], 0x0A, None),
'Skull Woods': DungeonInfo(2, 3, True, True, True, False, 2, 'Skull Woods - Prize', 0x4), 'Skull Woods': DungeonInfo(2, 3, True, True, True, False, 2, [0x120A3, 0x53F46, 0x53F47, 0x180058, 0x180074, 0x186FE8], 0x10, 0x20),
'Thieves Town': DungeonInfo(4, 1, True, True, True, False, 2, "Thieves' Town - Prize", 0x6), 'Thieves Town': DungeonInfo(4, 1, True, True, True, False, 2, [0x120A6, 0x53F4C, 0x53F4D, 0x18005B, 0x180076, 0x186FEB], 0x16, None),
'Ice Palace': DungeonInfo(3, 2, True, True, True, False, 4, 'Ice Palace - Prize', 0x8), 'Ice Palace': DungeonInfo(3, 2, True, True, True, False, 4, [0x120A4, 0x53F48, 0x53F49, 0x180059, 0x180078, 0x186FE9], 0x12, None),
'Misery Mire': DungeonInfo(2, 3, True, True, True, False, 3, 'Misery Mire - Prize', 0x7), 'Misery Mire': DungeonInfo(2, 3, True, True, True, False, 3, [0x120A2, 0x53F44, 0x53F45, 0x180057, 0x180077, 0x186FE7], 0x0E, None),
'Turtle Rock': DungeonInfo(5, 4, True, True, True, False, 2, 'Turtle Rock - Prize', 0x5), 'Turtle Rock': DungeonInfo(5, 4, True, True, True, False, 2, [0x120A7, 0x53F4E, 0x53F4F, 0x18005C, 0x180075, 0x186FEC], 0x18, 0x3E),
'Ganons Tower': DungeonInfo(20, 4, True, True, True, False, 4, None, 0xa), 'Ganons Tower': DungeonInfo(20, 4, True, True, True, False, 4, None, 0x1A, None),
} }
+1 -1
View File
@@ -316,7 +316,7 @@ Library: near books
Potion Shop: near potions Potion Shop: near potions
Spike Cave: beyond spikes Spike Cave: beyond spikes
Mimic Cave: in a cave of mimicry Mimic Cave: in a cave of mimicry
Chest Game: as a prize Chest Game: as a game reward
Chicken House: near poultry Chicken House: near poultry
Aginah's Cave: with Aginah Aginah's Cave: with Aginah
Ice Rod Cave: in a frozen cave Ice Rod Cave: in a frozen cave
+22 -20
View File
@@ -2675,11 +2675,16 @@ ow_prize_table = {'Links House': (0x8b1, 0xb2d),
'Desert Palace Entrance (North)': (0x0e1, 0xba0), 'Desert Palace Entrance (East)': (0x191, 0xca0), 'Desert Palace Entrance (North)': (0x0e1, 0xba0), 'Desert Palace Entrance (East)': (0x191, 0xca0),
'Eastern Palace': (0xf31, 0x620), 'Tower of Hera': (0x8D0, 0x080), 'Eastern Palace': (0xf31, 0x620), 'Tower of Hera': (0x8D0, 0x080),
'Hyrule Castle Entrance (South)': (0x820, 0x730), 'Hyrule Castle Entrance (West)': (0x740, 0x5D0), 'Hyrule Castle Entrance (South)': (0x820, 0x730), 'Hyrule Castle Entrance (West)': (0x740, 0x5D0),
'Hyrule Castle Entrance (East)': (0x8f0, 0x5D0), 'Inverted Pyramid Entrance': (0x6C0, 0x5D0), 'Hyrule Castle Entrance (East)': (0x8f0, 0x5D0),
'Agahnims Tower': (0x820, 0x5D0), 'Agahnims Tower': (0x820, 0x5D0),
'Thieves Town': (0x1d0, 0x780), 'Skull Woods First Section Door': (0x240, 0x280), 'Thieves Town': (0x1d0, 0x780), 'Skull Woods First Section Door': (0x2e0, 0x280),
'Skull Woods Second Section Door (East)': (0x1a0, 0x240), 'Skull Woods Second Section Door (East)': (0x200, 0x240),
'Skull Woods Second Section Door (West)': (0x0c0, 0x1c0), 'Skull Woods Final Section': (0x082, 0x0b0), 'Skull Woods Second Section Door (West)': (0x0c0, 0x1c0),
'Skull Woods Final Section': (0x082, 0x0b0),
'Skull Woods First Section Hole (West)': (0x200, 0x2b0),
'Skull Woods First Section Hole (East)': (0x340, 0x2e0),
'Skull Woods First Section Hole (North)': (0x320, 0x1e0),
'Skull Woods Second Section Hole': (0x0f0, 0x0b0),
'Ice Palace': (0xca0, 0xda0), 'Ice Palace': (0xca0, 0xda0),
'Misery Mire': (0x100, 0xca0), 'Misery Mire': (0x100, 0xca0),
'Palace of Darkness': (0xf40, 0x620), 'Swamp Palace': (0x759, 0xED0), 'Palace of Darkness': (0xf40, 0x620), 'Swamp Palace': (0x759, 0xED0),
@@ -2687,16 +2692,23 @@ ow_prize_table = {'Links House': (0x8b1, 0xb2d),
'Dark Death Mountain Ledge (West)': (0xb80, 0x180), 'Dark Death Mountain Ledge (West)': (0xb80, 0x180),
'Dark Death Mountain Ledge (East)': (0xc80, 0x180), 'Dark Death Mountain Ledge (East)': (0xc80, 0x180),
'Turtle Rock Isolated Ledge Entrance': (0xc00, 0x240), 'Turtle Rock Isolated Ledge Entrance': (0xc00, 0x240),
'Hyrule Castle Secret Entrance Drop': (0x9D0, 0x680),
'Hyrule Castle Secret Entrance Stairs': (0x8D0, 0x700), 'Hyrule Castle Secret Entrance Stairs': (0x8D0, 0x700),
'Kakariko Well Drop': (0x030, 0x680),
'Kakariko Well Cave': (0x060, 0x680), 'Kakariko Well Cave': (0x060, 0x680),
'Bat Cave Cave': (0x540, 0x8f0), 'Bat Cave Drop': (0x520, 0x8f0),
'Bat Cave Cave': (0x560, 0x940),
'Elder House (East)': (0x2b0, 0x6a0), 'Elder House (East)': (0x2b0, 0x6a0),
'Elder House (West)': (0x230, 0x6a0), 'Elder House (West)': (0x230, 0x6a0),
'North Fairy Cave Drop': (0xa40, 0x500),
'North Fairy Cave': (0xa80, 0x440), 'North Fairy Cave': (0xa80, 0x440),
'Lost Woods Hideout Drop': (0x290, 0x200),
'Lost Woods Hideout Stump': (0x240, 0x280), 'Lost Woods Hideout Stump': (0x240, 0x280),
'Lumberjack Tree Cave': (0x4e0, 0x004), 'Lumberjack Tree Tree': (0x4e0, 0x140),
'Lumberjack Tree Cave': (0x560, 0x004),
'Two Brothers House (East)': (0x200, 0x0b60), 'Two Brothers House (East)': (0x200, 0x0b60),
'Two Brothers House (West)': (0x180, 0x0b60), 'Two Brothers House (West)': (0x180, 0x0b60),
'Sanctuary Grave': (0x820, 0x4c0),
'Sanctuary': (0x720, 0x4a0), 'Sanctuary': (0x720, 0x4a0),
'Old Man Cave (West)': (0x580, 0x2c0), 'Old Man Cave (West)': (0x580, 0x2c0),
'Old Man Cave (East)': (0x620, 0x2c0), 'Old Man Cave (East)': (0x620, 0x2c0),
@@ -2721,17 +2733,13 @@ ow_prize_table = {'Links House': (0x8b1, 0xb2d),
'Hookshot Cave': (0xc80, 0x0c0), 'Hookshot Cave': (0xc80, 0x0c0),
'Hookshot Cave Back Entrance': (0xcf0, 0x004), 'Hookshot Cave Back Entrance': (0xcf0, 0x004),
'Ganons Tower': (0x8D0, 0x080), 'Ganons Tower': (0x8D0, 0x080),
'Pyramid Hole': (0x820, 0x680),
'Inverted Pyramid Hole': (0x820, 0x680),
'Pyramid Entrance': (0x640, 0x7c0), 'Pyramid Entrance': (0x640, 0x7c0),
'Skull Woods First Section Hole (West)': None, 'Inverted Pyramid Entrance': (0x6C0, 0x5D0),
'Skull Woods First Section Hole (East)': None,
'Skull Woods First Section Hole (North)': None,
'Skull Woods Second Section Hole': None,
'Pyramid Hole': None,
'Inverted Pyramid Hole': None,
'Waterfall of Wishing': (0xe80, 0x280), 'Waterfall of Wishing': (0xe80, 0x280),
'Dam': (0x759, 0xED0), 'Dam': (0x759, 0xED0),
'Blinds Hideout': (0x190, 0x6c0), 'Blinds Hideout': (0x190, 0x6c0),
'Hyrule Castle Secret Entrance Drop': None,
'Bonk Fairy (Light)': (0x740, 0xa80), 'Bonk Fairy (Light)': (0x740, 0xa80),
'Lake Hylia Fairy': (0xd40, 0x9f0), 'Lake Hylia Fairy': (0xd40, 0x9f0),
'Light Hype Fairy': (0x940, 0xc80), 'Light Hype Fairy': (0x940, 0xc80),
@@ -2743,11 +2751,8 @@ ow_prize_table = {'Links House': (0x8b1, 0xb2d),
'Sahasrahlas Hut': (0xcf0, 0x6c0), 'Sahasrahlas Hut': (0xcf0, 0x6c0),
'Lake Hylia Shop': (0xbc0, 0xc00), 'Lake Hylia Shop': (0xbc0, 0xc00),
'Capacity Upgrade': (0xca0, 0xda0), 'Capacity Upgrade': (0xca0, 0xda0),
'Kakariko Well Drop': None,
'Blacksmiths Hut': (0x4a0, 0x880), 'Blacksmiths Hut': (0x4a0, 0x880),
'Bat Cave Drop': None,
'Sick Kids House': (0x220, 0x880), 'Sick Kids House': (0x220, 0x880),
'North Fairy Cave Drop': None,
'Lost Woods Gamble': (0x240, 0x080), 'Lost Woods Gamble': (0x240, 0x080),
'Fortune Teller (Light)': (0x2c0, 0x4c0), 'Fortune Teller (Light)': (0x2c0, 0x4c0),
'Snitch Lady (East)': (0x310, 0x7a0), 'Snitch Lady (East)': (0x310, 0x7a0),
@@ -2756,8 +2761,6 @@ ow_prize_table = {'Links House': (0x8b1, 0xb2d),
'Tavern (Front)': (0x270, 0x980), 'Tavern (Front)': (0x270, 0x980),
'Light World Bomb Hut': (0x070, 0x980), 'Light World Bomb Hut': (0x070, 0x980),
'Kakariko Shop': (0x170, 0x980), 'Kakariko Shop': (0x170, 0x980),
'Lost Woods Hideout Drop': None,
'Lumberjack Tree Tree': None,
'Cave 45': (0x440, 0xca0), 'Graveyard Cave': (0x8f0, 0x430), 'Cave 45': (0x440, 0xca0), 'Graveyard Cave': (0x8f0, 0x430),
'Checkerboard Cave': (0x260, 0xc00), 'Checkerboard Cave': (0x260, 0xc00),
'Mini Moldorm Cave': (0xa40, 0xe80), 'Mini Moldorm Cave': (0xa40, 0xe80),
@@ -2769,7 +2772,6 @@ ow_prize_table = {'Links House': (0x8b1, 0xb2d),
'Bonk Rock Cave': (0x5f0, 0x460), 'Bonk Rock Cave': (0x5f0, 0x460),
'Library': (0x270, 0xaa0), 'Library': (0x270, 0xaa0),
'Potion Shop': (0xc80, 0x4c0), 'Potion Shop': (0xc80, 0x4c0),
'Sanctuary Grave': None,
'Hookshot Fairy': (0xd00, 0x180), 'Hookshot Fairy': (0xd00, 0x180),
'Pyramid Fairy': (0x740, 0x740), 'Pyramid Fairy': (0x740, 0x740),
'East Dark World Hint': (0xf60, 0xb00), 'East Dark World Hint': (0xf60, 0xb00),
@@ -2798,6 +2800,6 @@ ow_prize_table = {'Links House': (0x8b1, 0xb2d),
'Mimic Cave': (0xc80, 0x180), 'Mimic Cave': (0xc80, 0x180),
'Big Bomb Shop': (0x8b1, 0xb2d), 'Big Bomb Shop': (0x8b1, 0xb2d),
'Dark Lake Hylia Shop': (0xa40, 0xc40), 'Dark Lake Hylia Shop': (0xa40, 0xc40),
'Lumberjack House': (0x4e0, 0x0d0), 'Lumberjack House': (0x580, 0x100),
'Lake Hylia Fortune Teller': (0xa40, 0xc40), 'Lake Hylia Fortune Teller': (0xa40, 0xc40),
'Kakariko Gamble Game': (0x2f0, 0xaf0)} 'Kakariko Gamble Game': (0x2f0, 0xaf0)}
+63 -25
View File
@@ -14,14 +14,19 @@ from source.item.FillUtil import filter_special_locations, valid_pot_items
def get_dungeon_item_pool(world): def get_dungeon_item_pool(world):
return [item for dungeon in world.dungeons for item in dungeon.all_items if item.location is None] dungeon_items = [item for dungeon in world.dungeons for item in dungeon.all_items if item.location is None]
for player in range(1, world.players+1):
if world.prizeshuffle[player] != 'none':
dungeon_items.extend(ItemFactory(['Red Pendant', 'Blue Pendant', 'Green Pendant', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 7', 'Crystal 5', 'Crystal 6'], player))
return dungeon_items
def promote_dungeon_items(world): def promote_dungeon_items(world):
world.itempool += get_dungeon_item_pool(world) world.itempool += get_dungeon_item_pool(world)
for item in world.get_items(): for item in world.get_items():
if item.smallkey or item.bigkey: if item.smallkey or item.bigkey or item.prize:
item.advancement = True item.advancement = True
elif item.map or item.compass: elif item.map or item.compass:
item.priority = True item.priority = True
@@ -38,36 +43,62 @@ def fill_dungeons_restrictive(world, shuffled_locations):
# with shuffled dungeon items they are distributed as part of the normal item pool # with shuffled dungeon items they are distributed as part of the normal item pool
for item in world.get_items(): for item in world.get_items():
if ((item.smallkey and world.keyshuffle[item.player] != 'none') if ((item.prize and world.prizeshuffle[item.player] != 'none')
or (item.smallkey and world.keyshuffle[item.player] != 'none')
or (item.bigkey and world.bigkeyshuffle[item.player])): or (item.bigkey and world.bigkeyshuffle[item.player])):
item.advancement = True item.advancement = True
elif (item.map and world.mapshuffle[item.player]) or (item.compass and world.compassshuffle[item.player]): elif (item.map and world.mapshuffle[item.player]) or (item.compass and world.compassshuffle[item.player]):
item.priority = True item.priority = True
dungeon_items = [item for item in get_dungeon_item_pool(world) if item.is_inside_dungeon_item(world)] dungeon_items = [item for item in get_dungeon_item_pool(world) if item.is_inside_dungeon_item(world)]
bigs, smalls, others = [], [], [] bigs, smalls, prizes, others = [], [], [], []
for i in dungeon_items: for i in dungeon_items:
(bigs if i.bigkey else smalls if i.smallkey else others).append(i) (bigs if i.bigkey else smalls if i.smallkey else prizes if i.prize else others).append(i)
unplaced_smalls = list(smalls) unplaced_smalls = list(smalls)
for i in world.itempool: for i in world.itempool:
if i.smallkey and world.keyshuffle[i.player] != 'none': if i.smallkey and world.keyshuffle[i.player] != 'none':
unplaced_smalls.append(i) unplaced_smalls.append(i)
def fill(base_state, items, key_pool): def fill(base_state, items, locations, key_pool=None):
fill_restrictive(world, base_state, shuffled_locations, items, key_pool, True) fill_restrictive(world, base_state, locations, items, key_pool, True)
all_state_base = world.get_all_state() all_state_base = world.get_all_state()
big_state_base = all_state_base.copy() big_state_base = all_state_base.copy()
for x in smalls + others: for x in smalls + prizes + others:
big_state_base.collect(x, True) big_state_base.collect(x, True)
fill(big_state_base, bigs, unplaced_smalls) fill(big_state_base, bigs, shuffled_locations, unplaced_smalls)
random.shuffle(shuffled_locations) random.shuffle(shuffled_locations)
small_state_base = all_state_base.copy() small_state_base = all_state_base.copy()
for x in others: for x in prizes + others:
small_state_base.collect(x, True) small_state_base.collect(x, True)
fill(small_state_base, smalls, unplaced_smalls) fill(small_state_base, smalls, shuffled_locations, unplaced_smalls)
prizes_copy = prizes.copy()
for attempt in range(15):
try:
random.shuffle(prizes)
random.shuffle(shuffled_locations)
prize_state_base = all_state_base.copy()
for x in others:
prize_state_base.collect(x, True)
fill(prize_state_base, prizes, shuffled_locations)
except FillError as e:
logging.getLogger('').info("Failed to place dungeon prizes (%s). Will retry %s more times", e, 14 - attempt)
prizes = prizes_copy.copy()
for dungeon in world.dungeons:
if world.prizeshuffle[dungeon.player] == 'dungeon':
dungeon.prize = None
for prize in prizes:
if prize.location:
prize.location.item = None
prize.location = None
continue
break
else:
raise FillError(f'Unable to place dungeon prizes {", ".join(list(map(lambda d: d.hint_text, prize_locs)))}')
random.shuffle(shuffled_locations) random.shuffle(shuffled_locations)
fill(all_state_base, others, None) fill(all_state_base, others, shuffled_locations)
def fill_restrictive(world, base_state, locations, itempool, key_pool=None, single_player_placement=False, def fill_restrictive(world, base_state, locations, itempool, key_pool=None, single_player_placement=False,
@@ -141,7 +172,7 @@ def fill_restrictive(world, base_state, locations, itempool, key_pool=None, sing
def verify_spot_to_fill(location, item_to_place, max_exp_state, single_player_placement, perform_access_check, def verify_spot_to_fill(location, item_to_place, max_exp_state, single_player_placement, perform_access_check,
key_pool, world): key_pool, world):
if item_to_place.smallkey or item_to_place.bigkey: # a better test to see if a key can go there if item_to_place.smallkey or item_to_place.bigkey or item_to_place.prize: # a better test to see if a key can go there
location.item = item_to_place location.item = item_to_place
location.event = True location.event = True
if item_to_place.smallkey: if item_to_place.smallkey:
@@ -155,9 +186,9 @@ def verify_spot_to_fill(location, item_to_place, max_exp_state, single_player_pl
test_state.sweep_for_events() test_state.sweep_for_events()
if location.can_fill(test_state, item_to_place, perform_access_check): if location.can_fill(test_state, item_to_place, perform_access_check):
if valid_key_placement(item_to_place, location, key_pool, test_state, world): if valid_key_placement(item_to_place, location, key_pool, test_state, world):
if item_to_place.crystal or valid_dungeon_placement(item_to_place, location, world): if item_to_place.prize or valid_dungeon_placement(item_to_place, location, world):
return location return location
if item_to_place.smallkey or item_to_place.bigkey: if item_to_place.smallkey or item_to_place.bigkey or item_to_place.prize:
location.item = None location.item = None
location.event = False location.event = False
if item_to_place.smallkey: if item_to_place.smallkey:
@@ -181,8 +212,8 @@ def valid_key_placement(item, location, key_pool, collection_state, world):
key_logic = world.key_logic[item.player][dungeon.name] key_logic = world.key_logic[item.player][dungeon.name]
unplaced_keys = len([x for x in key_pool if x.name == key_logic.small_key_name and x.player == item.player]) unplaced_keys = len([x for x in key_pool if x.name == key_logic.small_key_name and x.player == item.player])
prize_loc = None prize_loc = None
if key_logic.prize_location: if key_logic.prize_location and dungeon.prize and dungeon.prize.location and dungeon.prize.location.player == item.player:
prize_loc = world.get_location(key_logic.prize_location, location.player) prize_loc = dungeon.prize.location
cr_count = world.crystals_needed_for_gt[location.player] cr_count = world.crystals_needed_for_gt[location.player]
wild_keys = world.keyshuffle[item.player] != 'none' wild_keys = world.keyshuffle[item.player] != 'none'
if wild_keys: if wild_keys:
@@ -193,7 +224,7 @@ def valid_key_placement(item, location, key_pool, collection_state, world):
self_locking_keys = sum(1 for d, rule in key_logic.door_rules.items() if rule.allow_small self_locking_keys = sum(1 for d, rule in key_logic.door_rules.items() if rule.allow_small
and rule.small_location.item and rule.small_location.item.name == key_logic.small_key_name) and rule.small_location.item and rule.small_location.item.name == key_logic.small_key_name)
return key_logic.check_placement(unplaced_keys, wild_keys, reached_keys, self_locking_keys, return key_logic.check_placement(unplaced_keys, wild_keys, reached_keys, self_locking_keys,
location if item.bigkey else None, prize_loc, cr_count) location if item.bigkey else None, prize_loc, cr_count)
else: else:
return not item.is_inside_dungeon_item(world) return not item.is_inside_dungeon_item(world)
@@ -228,16 +259,19 @@ def track_outside_keys(item, location, world):
def track_dungeon_items(item, location, world): def track_dungeon_items(item, location, world):
if location.parent_region.dungeon and not item.crystal: if location.parent_region.dungeon and (not item.prize or world.prizeshuffle[item.player] == 'dungeon'):
layout = world.dungeon_layouts[location.player][location.parent_region.dungeon.name] layout = world.dungeon_layouts[location.player][location.parent_region.dungeon.name]
if is_dungeon_item(item, world) and item.player == location.player: if is_dungeon_item(item, world) and item.player == location.player:
layout.dungeon_items -= 1 layout.dungeon_items -= 1
else: else:
layout.free_items -= 1 layout.free_items -= 1
if item.prize:
location.parent_region.dungeon.prize = item
def is_dungeon_item(item, world): def is_dungeon_item(item, world):
return ((item.smallkey and world.keyshuffle[item.player] == 'none') return ((item.prize and world.prizeshuffle[item.player] == 'none')
or (item.smallkey and world.keyshuffle[item.player] == 'none')
or (item.bigkey and not world.bigkeyshuffle[item.player]) or (item.bigkey and not world.bigkeyshuffle[item.player])
or (item.compass and not world.compassshuffle[item.player]) or (item.compass and not world.compassshuffle[item.player])
or (item.map and not world.mapshuffle[item.player])) or (item.map and not world.mapshuffle[item.player]))
@@ -250,8 +284,8 @@ def recovery_placement(item_to_place, locations, world, state, base_state, itemp
return last_ditch_placement(item_to_place, locations, world, state, base_state, itempool, key_pool, return last_ditch_placement(item_to_place, locations, world, state, base_state, itempool, key_pool,
single_player_placement) single_player_placement)
elif world.algorithm == 'vanilla_fill': elif world.algorithm == 'vanilla_fill':
if item_to_place.type == 'Crystal': if item_to_place.prize:
possible_swaps = [x for x in state.locations_checked if x.item.type == 'Crystal'] possible_swaps = [x for x in state.locations_checked if x.item.prize]
return try_possible_swaps(possible_swaps, item_to_place, locations, world, base_state, itempool, return try_possible_swaps(possible_swaps, item_to_place, locations, world, base_state, itempool,
key_pool, single_player_placement) key_pool, single_player_placement)
else: else:
@@ -310,11 +344,15 @@ def last_ditch_placement(item_to_place, locations, world, state, base_state, ite
return 3 return 3
return 4 return 4
if item_to_place.type == 'Crystal': # TODO: Verify correctness in using item player in multiworld situations
possible_swaps = [x for x in state.locations_checked if x.item.type == 'Crystal'] if item_to_place.prize and world.prizeshuffle[item_to_place.player] == 'none':
possible_swaps = [x for x in state.locations_checked if x.item.prize]
else: else:
ignored_types = ['Event']
if world.prizeshuffle[item_to_place.player] == 'none':
ignored_types.append('Prize')
possible_swaps = [x for x in state.locations_checked possible_swaps = [x for x in state.locations_checked
if x.item.type not in ['Event', 'Crystal'] and not x.forced_item and not x.locked] if x.item.type not in ignored_types and not x.forced_item and not x.locked]
swap_locations = sorted(possible_swaps, key=location_preference) swap_locations = sorted(possible_swaps, key=location_preference)
return try_possible_swaps(swap_locations, item_to_place, locations, world, base_state, itempool, return try_possible_swaps(swap_locations, item_to_place, locations, world, base_state, itempool,
key_pool, single_player_placement) key_pool, single_player_placement)
+21 -44
View File
@@ -5,7 +5,7 @@ import RaceRandom as random
from BaseClasses import LocationType, Region, RegionType, Shop, ShopType, Location, CollectionState, PotItem from BaseClasses import LocationType, Region, RegionType, Shop, ShopType, Location, CollectionState, PotItem
from EntranceShuffle import connect_entrance from EntranceShuffle import connect_entrance
from Regions import shop_to_location_table, retro_shops, shop_table_by_location, valid_pot_location from Regions import location_events, shop_to_location_table, retro_shops, shop_table_by_location, valid_pot_location
from Fill import FillError, fill_restrictive, get_dungeon_item_pool, track_dungeon_items, track_outside_keys from Fill import FillError, fill_restrictive, get_dungeon_item_pool, track_dungeon_items, track_outside_keys
from PotShuffle import vanilla_pots from PotShuffle import vanilla_pots
from Tables import bonk_prize_lookup from Tables import bonk_prize_lookup
@@ -240,41 +240,9 @@ def generate_itempool(world, player):
old_man.forced_item = old_man.item old_man.forced_item = old_man.item
old_man.skip = True old_man.skip = True
event_items = { for loc, item in location_events.items():
'Agahnim 1': 'Beat Agahnim 1', if item:
'Agahnim 2': 'Beat Agahnim 2', set_event_item(loc, item)
'Eastern Palace - Boss Kill': 'Beat Boss',
'Desert Palace - Boss Kill': 'Beat Boss',
'Tower of Hera - Boss Kill': 'Beat Boss',
'Palace of Darkness - Boss Kill': 'Beat Boss',
'Swamp Palace - Boss Kill': 'Beat Boss',
'Skull Woods - Boss Kill': 'Beat Boss',
'Thieves\' Town - Boss Kill': 'Beat Boss',
'Ice Palace - Boss Kill': 'Beat Boss',
'Misery Mire - Boss Kill': 'Beat Boss',
'Turtle Rock - Boss Kill': 'Beat Boss',
'Lost Old Man': 'Escort Old Man',
'Old Man Drop Off': 'Return Old Man',
'Floodgate': 'Open Floodgate',
'Big Bomb': 'Pick Up Big Bomb',
'Pyramid Crack': 'Detonate Big Bomb',
'Frog': 'Get Frog',
'Missing Smith': 'Return Smith',
'Dark Blacksmith Ruins': 'Pick Up Purple Chest',
'Middle Aged Man': 'Deliver Purple Chest',
'Trench 1 Switch': 'Trench 1 Filled',
'Trench 2 Switch': 'Trench 2 Filled',
'Swamp Drain': 'Drained Swamp',
'Turtle Medallion Pad': 'Turtle Opened',
'Attic Cracked Floor': 'Shining Light',
'Suspicious Maiden': 'Maiden Rescued',
'Revealing Light': 'Maiden Unmasked',
'Ice Block Drop': 'Convenient Block',
'Skull Star Tile': 'Hidden Pits'
}
for loc, item in event_items.items():
set_event_item(loc, item)
if world.mode[player] == 'standard': if world.mode[player] == 'standard':
set_event_item('Zelda Pickup', 'Zelda Herself') set_event_item('Zelda Pickup', 'Zelda Herself')
@@ -375,7 +343,8 @@ def generate_itempool(world, player):
world.treasure_hunt_icon[player] = 'Triforce Piece' world.treasure_hunt_icon[player] = 'Triforce Piece'
world.itempool.extend([item for item in get_dungeon_item_pool(world) if item.player == player world.itempool.extend([item for item in get_dungeon_item_pool(world) if item.player == player
and ((item.smallkey and world.keyshuffle[player] != 'none') and ((item.prize and world.prizeshuffle[player] == 'wild')
or (item.smallkey and world.keyshuffle[player] != 'none')
or (item.bigkey and world.bigkeyshuffle[player]) or (item.bigkey and world.bigkeyshuffle[player])
or (item.map and world.mapshuffle[player]) or (item.map and world.mapshuffle[player])
or (item.compass and world.compassshuffle[player]))]) or (item.compass and world.compassshuffle[player]))])
@@ -486,7 +455,7 @@ def generate_itempool(world, player):
world.itempool = [beemizer(item) for item in world.itempool] world.itempool = [beemizer(item) for item in world.itempool]
# increase pool if not enough items # increase pool if not enough items
ttl_locations = sum(1 for x in world.get_unfilled_locations(player) if '- Prize' not in x.name) ttl_locations = sum(1 for x in world.get_unfilled_locations(player) if world.prizeshuffle[player] != 'none' or not x.prize)
pool_size = count_player_dungeon_item_pool(world, player) pool_size = count_player_dungeon_item_pool(world, player)
pool_size += sum(1 for x in world.itempool if x.player == player) pool_size += sum(1 for x in world.itempool if x.player == player)
@@ -725,9 +694,12 @@ def create_dynamic_bonkdrop_locations(world, player):
def fill_prizes(world, attempts=15): def fill_prizes(world, attempts=15):
from Items import prize_item_table
all_state = world.get_all_state(keys=True) all_state = world.get_all_state(keys=True)
for player in range(1, world.players + 1): for player in range(1, world.players + 1):
crystals = ItemFactory(['Red Pendant', 'Blue Pendant', 'Green Pendant', 'Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 7', 'Crystal 5', 'Crystal 6'], player) if world.prizeshuffle[player] != 'none':
continue
crystals = ItemFactory(list(prize_item_table.keys()), player)
crystal_locations = [world.get_location('Turtle Rock - Prize', player), world.get_location('Eastern Palace - Prize', player), world.get_location('Desert Palace - Prize', player), world.get_location('Tower of Hera - Prize', player), world.get_location('Palace of Darkness - Prize', player), crystal_locations = [world.get_location('Turtle Rock - Prize', player), world.get_location('Eastern Palace - Prize', player), world.get_location('Desert Palace - Prize', player), world.get_location('Tower of Hera - Prize', player), world.get_location('Palace of Darkness - Prize', player),
world.get_location('Thieves\' Town - Prize', player), world.get_location('Skull Woods - Prize', player), world.get_location('Swamp Palace - Prize', player), world.get_location('Ice Palace - Prize', player), world.get_location('Thieves\' Town - Prize', player), world.get_location('Skull Woods - Prize', player), world.get_location('Swamp Palace - Prize', player), world.get_location('Ice Palace - Prize', player),
world.get_location('Misery Mire - Prize', player)] world.get_location('Misery Mire - Prize', player)]
@@ -742,6 +714,8 @@ def fill_prizes(world, attempts=15):
random.shuffle(prizepool) random.shuffle(prizepool)
random.shuffle(prize_locs) random.shuffle(prize_locs)
fill_restrictive(world, all_state, prize_locs, prizepool, single_player_placement=True) fill_restrictive(world, all_state, prize_locs, prizepool, single_player_placement=True)
for prize_loc in crystal_locations:
prize_loc.parent_region.dungeon.prize = prize_loc.item
except FillError as e: except FillError as e:
logging.getLogger('').info("Failed to place dungeon prizes (%s). Will retry %s more times", e, attempts - attempt - 1) logging.getLogger('').info("Failed to place dungeon prizes (%s). Will retry %s more times", e, attempts - attempt - 1)
for location in empty_crystal_locations: for location in empty_crystal_locations:
@@ -1419,9 +1393,11 @@ def make_customizer_pool(world, player):
target_amount = max(amount, len(dungeon.small_keys)) target_amount = max(amount, len(dungeon.small_keys))
additional_amount = target_amount - len(dungeon.small_keys) additional_amount = target_amount - len(dungeon.small_keys)
dungeon.small_keys.extend([d_item] * additional_amount) dungeon.small_keys.extend([d_item] * additional_amount)
elif item_name.startswith('Big Key') or item_name.startswith('Map') or item_name.startswith('Compass'): elif (item_name.startswith('Big Key') or item_name.startswith('Map') or item_name.startswith('Compass')
or item_name.startswith('Crystal') or item_name.endswith('Pendant')):
d_item = ItemFactory(item_name, player) d_item = ItemFactory(item_name, player)
if ((d_item.bigkey and not world.bigkeyshuffle[player]) if ((d_item.prize and world.prizeshuffle[player] in ['none', 'dungeon'])
or (d_item.bigkey and not world.bigkeyshuffle[player])
or (d_item.compass and not world.compassshuffle[player]) or (d_item.compass and not world.compassshuffle[player])
or (d_item.map and not world.mapshuffle[player])): or (d_item.map and not world.mapshuffle[player])):
d_name = d_item.dungeon d_name = d_item.dungeon
@@ -1566,11 +1542,11 @@ def set_default_triforce(goal, custom_goal, custom_total):
def fill_specific_items(world): def fill_specific_items(world):
if world.customizer: if world.customizer:
from Items import prize_item_table
placements = world.customizer.get_placements() placements = world.customizer.get_placements()
dungeon_pool = get_dungeon_item_pool(world) dungeon_pool = get_dungeon_item_pool(world)
prize_pool = [] prize_pool = []
prize_set = {'Red Pendant', 'Blue Pendant', 'Green Pendant', 'Crystal 1', 'Crystal 2', prize_set = set(prize_item_table.keys())
'Crystal 3', 'Crystal 4', 'Crystal 7', 'Crystal 5', 'Crystal 6'}
for p in range(1, world.players + 1): for p in range(1, world.players + 1):
prize_pool.extend(prize_set) prize_pool.extend(prize_set)
if placements: if placements:
@@ -1672,7 +1648,8 @@ def get_item_and_event_flag(item, world, player, dungeon_pool, prize_set, prize_
def is_dungeon_item(item, world, player): def is_dungeon_item(item, world, player):
return ((item.startswith('Small Key') and world.keyshuffle[player] == 'none') return (((item.startswith('Crystal') or item.endswith('Pendant')) and world.prizeshuffle[player] in ['none', 'dungeon'])
or (item.startswith('Small Key') and world.keyshuffle[player] == 'none')
or (item.startswith('Big Key') and not world.bigkeyshuffle[player]) or (item.startswith('Big Key') and not world.bigkeyshuffle[player])
or (item.startswith('Compass') and not world.compassshuffle[player]) or (item.startswith('Compass') and not world.compassshuffle[player])
or (item.startswith('Map') and not world.mapshuffle[player])) or (item.startswith('Map') and not world.mapshuffle[player]))
+23 -10
View File
@@ -60,19 +60,19 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche
'Progressive Sword': (True, False, 'Sword', 0x5E, 150, 'A better copy\nof your sword\nfor your time', 'the unknown sword', 'sword-wielding kid', 'sword for sale', 'fungus for some slasher', 'sword boy fights again', 'a Sword'), 'Progressive Sword': (True, False, 'Sword', 0x5E, 150, 'A better copy\nof your sword\nfor your time', 'the unknown sword', 'sword-wielding kid', 'sword for sale', 'fungus for some slasher', 'sword boy fights again', 'a Sword'),
'Progressive Glove': (True, False, None, 0x61, 150, 'A way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a Glove'), 'Progressive Glove': (True, False, None, 0x61, 150, 'A way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a Glove'),
'Silver Arrows': (True, False, None, 0x58, 100, 'Do you fancy\nsilver tipped\narrows?', 'and the ganonsbane', 'ganon-killing kid', 'ganon doom for sale', 'fungus for pork', 'archer boy shines again', 'the Silver Arrows'), 'Silver Arrows': (True, False, None, 0x58, 100, 'Do you fancy\nsilver tipped\narrows?', 'and the ganonsbane', 'ganon-killing kid', 'ganon doom for sale', 'fungus for pork', 'archer boy shines again', 'the Silver Arrows'),
'Green Pendant': (True, False, 'Crystal', [0x04, 0x38, 0x62, 0x00, 0x69, 0x37, 0x08], 999, None, None, None, None, None, None, None), 'Green Pendant': (True, False, 'Prize', 0x37, 250, 'A pendant that\nsome old guy\nwill never see', 'and the green pendant', 'pendant kid', 'pendant for sale', 'fungus for pendant', 'pendant boy dabs again', 'a Prize'),
'Blue Pendant': (True, False, 'Crystal', [0x02, 0x34, 0x60, 0x00, 0x69, 0x39, 0x09], 999, None, None, None, None, None, None, None), 'Blue Pendant': (True, False, 'Prize', 0x39, 150, 'A pendant that you\'ll never get', 'and the pendant', 'pendant kid', 'pendant for sale', 'fungus for pendant', 'pendant boy dabs again', 'a Prize'),
'Red Pendant': (True, False, 'Crystal', [0x01, 0x32, 0x60, 0x00, 0x69, 0x38, 0x0a], 999, None, None, None, None, None, None, None), 'Red Pendant': (True, False, 'Prize', 0x38, 150, 'A pendant that you\'ll never get', 'and the pendant', 'pendant kid', 'pendant for sale', 'fungus for pendant', 'pendant boy dabs again', 'a Prize'),
'Triforce': (True, False, None, 0x6A, 777, '\n YOU WIN!', 'and the triforce', 'victorious kid', 'victory for sale', 'fungus for the win', 'greedy boy wins game again', 'the Triforce'), 'Triforce': (True, False, None, 0x6A, 777, '\n YOU WIN!', 'and the triforce', 'victorious kid', 'victory for sale', 'fungus for the win', 'greedy boy wins game again', 'the Triforce'),
'Power Star': (True, False, None, 0x6B, 100, 'A small victory', 'and the power star', 'star-struck kid', 'star for sale', 'see stars with shroom', 'mario powers up again', 'a Power Star'), 'Power Star': (True, False, None, 0x6B, 100, 'A small victory', 'and the power star', 'star-struck kid', 'star for sale', 'see stars with shroom', 'mario powers up again', 'a Power Star'),
'Triforce Piece': (True, False, None, 0x6C, 100, 'A small victory', 'and the thirdforce', 'triangular kid', 'triangle for sale', 'fungus for triangle', 'wise boy has triangle again', 'a Triforce piece'), 'Triforce Piece': (True, False, None, 0x6C, 100, 'A small victory', 'and the thirdforce', 'triangular kid', 'triangle for sale', 'fungus for triangle', 'wise boy has triangle again', 'a Triforce piece'),
'Crystal 1': (True, False, 'Crystal', [0x02, 0x34, 0x64, 0x40, 0x7F, 0x20, 0x01], 999, None, None, None, None, None, None, None), 'Crystal 1': (True, False, 'Prize', 0xB1, 200, 'A pretty nice,\nshiny crystal', 'and the crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'),
'Crystal 2': (True, False, 'Crystal', [0x10, 0x34, 0x64, 0x40, 0x79, 0x20, 0x02], 999, None, None, None, None, None, None, None), 'Crystal 2': (True, False, 'Prize', 0xB4, 200, 'A pretty nice,\nshiny crystal', 'and the crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'),
'Crystal 3': (True, False, 'Crystal', [0x40, 0x34, 0x64, 0x40, 0x6C, 0x20, 0x03], 999, None, None, None, None, None, None, None), 'Crystal 3': (True, False, 'Prize', 0xB6, 200, 'A pretty nice,\nshiny crystal', 'and the crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'),
'Crystal 4': (True, False, 'Crystal', [0x20, 0x34, 0x64, 0x40, 0x6D, 0x20, 0x04], 999, None, None, None, None, None, None, None), 'Crystal 4': (True, False, 'Prize', 0xB5, 200, 'A pretty nice,\nshiny crystal', 'and the crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'),
'Crystal 5': (True, False, 'Crystal', [0x04, 0x32, 0x64, 0x40, 0x6E, 0x20, 0x05], 999, None, None, None, None, None, None, None), 'Crystal 5': (True, False, 'Prize', 0xB2, 250, 'A crystal that\nunlocks a bomb', 'and the red crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'),
'Crystal 6': (True, False, 'Crystal', [0x01, 0x32, 0x64, 0x40, 0x6F, 0x20, 0x06], 999, None, None, None, None, None, None, None), 'Crystal 6': (True, False, 'Prize', 0xB0, 250, 'A crystal that\nunlocks a bomb', 'and the red crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'),
'Crystal 7': (True, False, 'Crystal', [0x08, 0x34, 0x64, 0x40, 0x7C, 0x20, 0x07], 999, None, None, None, None, None, None, None), 'Crystal 7': (True, False, 'Prize', 0xB3, 200, 'A pretty nice,\nshiny crystal', 'and the crystal', 'crystal kid', 'crystal for sale', 'fungus for crystal', 'crystal boy sparkles again', 'a Prize'),
'Single Arrow': (False, False, None, 0x43, 3, 'A lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again', 'an arrow'), 'Single Arrow': (False, False, None, 0x43, 3, 'A lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again', 'an arrow'),
'Arrows (10)': (False, False, None, 0x44, 30, 'This will give\nyou ten shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'ten arrows'), 'Arrows (10)': (False, False, None, 0x44, 30, 'This will give\nyou ten shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'ten arrows'),
'Arrow Upgrade (+10)': (False, False, None, 0x54, 100, 'Increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), 'Arrow Upgrade (+10)': (False, False, None, 0x54, 100, 'Increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
@@ -203,3 +203,16 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche
'Farmable Bombs': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Farmable Bombs': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
'Farmable Rupees': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Farmable Rupees': (True, False, 'Event', 999, None, None, None, None, None, None, None, None),
} }
prize_item_table = {
'Green Pendant': [0x04, 0x38, 0x62, 0x00, 0x69, 0x08],
'Blue Pendant': [0x02, 0x34, 0x60, 0x00, 0x69, 0x09],
'Red Pendant': [0x01, 0x32, 0x60, 0x00, 0x69, 0x0a],
'Crystal 1': [0x02, 0x34, 0x64, 0x40, 0x7F, 0x01],
'Crystal 2': [0x10, 0x34, 0x64, 0x40, 0x79, 0x02],
'Crystal 3': [0x40, 0x34, 0x64, 0x40, 0x6C, 0x03],
'Crystal 4': [0x20, 0x34, 0x64, 0x40, 0x6D, 0x04],
'Crystal 5': [0x04, 0x32, 0x64, 0x40, 0x6E, 0x05],
'Crystal 6': [0x01, 0x32, 0x64, 0x40, 0x6F, 0x06],
'Crystal 7': [0x08, 0x34, 0x64, 0x40, 0x7C, 0x07]
}
+9 -9
View File
@@ -3,7 +3,7 @@ import logging
from collections import defaultdict, deque from collections import defaultdict, deque
from BaseClasses import DoorType, dungeon_keys, KeyRuleType, RegionType from BaseClasses import DoorType, dungeon_keys, KeyRuleType, RegionType
from Regions import dungeon_events from Regions import location_events
from Dungeons import dungeon_keys, dungeon_bigs, dungeon_table from Dungeons import dungeon_keys, dungeon_bigs, dungeon_table
from DungeonGenerator import ExplorationState, get_special_big_key_doors, count_locations_exclude_big_chest, prize_or_event from DungeonGenerator import ExplorationState, get_special_big_key_doors, count_locations_exclude_big_chest, prize_or_event
from DungeonGenerator import reserved_location, blind_boss_unavail from DungeonGenerator import reserved_location, blind_boss_unavail
@@ -1116,7 +1116,7 @@ def location_is_bk_locked(loc, key_logic):
# todo: verfiy this code is defunct # todo: verfiy this code is defunct
# def prize_or_event(loc): # def prize_or_event(loc):
# return loc.name in dungeon_events or '- Prize' in loc.name or loc.name in ['Agahnim 1', 'Agahnim 2'] # return loc.name in location_events or '- Prize' in loc.name or loc.name in ['Agahnim 1', 'Agahnim 2']
# #
# #
# def reserved_location(loc, world, player): # def reserved_location(loc, world, player):
@@ -1504,7 +1504,7 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa
bk_done = state.big_key_opened or num_bigs == 0 or (available_big_locations == 0 and not found_forced_bk) bk_done = state.big_key_opened or num_bigs == 0 or (available_big_locations == 0 and not found_forced_bk)
# prize door should not be opened if the boss is reachable - but not reached yet # prize door should not be opened if the boss is reachable - but not reached yet
allow_for_prize_lock = (key_layout.prize_can_lock and allow_for_prize_lock = (key_layout.prize_can_lock and
not any(x for x in state.found_locations if '- Prize' in x.name)) not any(x for x in state.found_locations if x.prize))
prize_done = not key_layout.prize_relevant or state.prize_doors_opened or allow_for_prize_lock prize_done = not key_layout.prize_relevant or state.prize_doors_opened or allow_for_prize_lock
if smalls_done and bk_done and prize_done: if smalls_done and bk_done and prize_done:
return False return False
@@ -1623,7 +1623,7 @@ def determine_prize_lock(key_layout, world, player):
elif len(state.small_doors) > 0: elif len(state.small_doors) > 0:
open_a_door(state.small_doors[0].door, state, flat_proposal, world, player) open_a_door(state.small_doors[0].door, state, flat_proposal, world, player)
expand_key_state(state, flat_proposal, world, player) expand_key_state(state, flat_proposal, world, player)
if any(x for x in state.found_locations if '- Prize' in x.name): if any(x for x in state.found_locations if x.prize):
key_layout.prize_can_lock = True key_layout.prize_can_lock = True
@@ -1776,7 +1776,7 @@ def create_key_counter(state, key_layout, world, player):
key_counter.key_only_locations[loc] = None key_counter.key_only_locations[loc] = None
elif loc.forced_item and loc.item.name == key_layout.key_logic.bk_name: elif loc.forced_item and loc.item.name == key_layout.key_logic.bk_name:
key_counter.other_locations[loc] = None key_counter.other_locations[loc] = None
elif loc.name not in dungeon_events: elif loc.name not in location_events:
key_counter.free_locations[loc] = None key_counter.free_locations[loc] = None
else: else:
key_counter.other_locations[loc] = None key_counter.other_locations[loc] = None
@@ -1785,7 +1785,7 @@ def create_key_counter(state, key_layout, world, player):
key_counter.big_key_opened = state.big_key_opened key_counter.big_key_opened = state.big_key_opened
if len(state.prize_door_set) > 0 and state.prize_doors_opened: if len(state.prize_door_set) > 0 and state.prize_doors_opened:
key_counter.prize_doors_opened = True key_counter.prize_doors_opened = True
if any(x for x in key_counter.important_locations if '- Prize' in x.name): if any(x for x in key_counter.important_locations if x.prize):
key_counter.prize_received = True key_counter.prize_received = True
return key_counter return key_counter
@@ -1805,7 +1805,7 @@ def imp_locations_factory(world, player):
def important_location(loc, world, player): def important_location(loc, world, player):
return '- Prize' in loc.name or loc.name in imp_locations_factory(world, player) or (loc.forced_big_key()) return loc.prize or loc.name in imp_locations_factory(world, player) or (loc.forced_big_key())
def create_odd_key_counter(door, parent_counter, key_layout, world, player): def create_odd_key_counter(door, parent_counter, key_layout, world, player):
@@ -2135,9 +2135,9 @@ def validate_key_placement(key_layout, world, player):
found_keys = sum(1 for i in found_locations if i.item is not None and i.item.name == smallkey_name and i.item.player == player) + \ found_keys = sum(1 for i in found_locations if i.item is not None and i.item.name == smallkey_name and i.item.player == player) + \
len(counter.key_only_locations) + keys_outside len(counter.key_only_locations) + keys_outside
if key_layout.prize_relevant: if key_layout.prize_relevant:
found_prize = any(x for x in counter.important_locations if '- Prize' in x.name) found_prize = any(x for x in counter.important_locations if x.prize)
if not found_prize and dungeon_table[key_layout.sector.name].prize: if not found_prize and dungeon_table[key_layout.sector.name].prize:
prize_loc = world.get_location(dungeon_table[key_layout.sector.name].prize, player) prize_loc = dungeon_table[key_layout.sector.name].prize.location
if key_layout.prize_relevant == 'BigBomb': if key_layout.prize_relevant == 'BigBomb':
found_prize = prize_loc.item.name not in ['Crystal 5', 'Crystal 6'] found_prize = prize_loc.item.name not in ['Crystal 5', 'Crystal 6']
elif key_layout.prize_relevant == 'GT': elif key_layout.prize_relevant == 'GT':
+4 -1
View File
@@ -460,6 +460,7 @@ def init_world(args, fish):
world.compassshuffle = args.compassshuffle.copy() world.compassshuffle = args.compassshuffle.copy()
world.keyshuffle = args.keyshuffle.copy() world.keyshuffle = args.keyshuffle.copy()
world.bigkeyshuffle = args.bigkeyshuffle.copy() world.bigkeyshuffle = args.bigkeyshuffle.copy()
world.prizeshuffle = args.prizeshuffle.copy()
world.bombbag = args.bombbag.copy() world.bombbag = args.bombbag.copy()
world.flute_mode = args.flute_mode.copy() world.flute_mode = args.flute_mode.copy()
world.bow_mode = args.bow_mode.copy() world.bow_mode = args.bow_mode.copy()
@@ -555,6 +556,7 @@ def copy_world(world):
ret.compassshuffle = world.compassshuffle.copy() ret.compassshuffle = world.compassshuffle.copy()
ret.keyshuffle = world.keyshuffle.copy() ret.keyshuffle = world.keyshuffle.copy()
ret.bigkeyshuffle = world.bigkeyshuffle.copy() ret.bigkeyshuffle = world.bigkeyshuffle.copy()
ret.prizeshuffle = world.prizeshuffle.copy()
ret.bombbag = world.bombbag.copy() ret.bombbag = world.bombbag.copy()
ret.flute_mode = world.flute_mode.copy() ret.flute_mode = world.flute_mode.copy()
ret.bow_mode = world.bow_mode.copy() ret.bow_mode = world.bow_mode.copy()
@@ -751,6 +753,7 @@ def copy_world_premature(world, player):
ret.compassshuffle = world.compassshuffle.copy() ret.compassshuffle = world.compassshuffle.copy()
ret.keyshuffle = world.keyshuffle.copy() ret.keyshuffle = world.keyshuffle.copy()
ret.bigkeyshuffle = world.bigkeyshuffle.copy() ret.bigkeyshuffle = world.bigkeyshuffle.copy()
ret.prizeshuffle = world.prizeshuffle.copy()
ret.bombbag = world.bombbag.copy() ret.bombbag = world.bombbag.copy()
ret.flute_mode = world.flute_mode.copy() ret.flute_mode = world.flute_mode.copy()
ret.bow_mode = world.bow_mode.copy() ret.bow_mode = world.bow_mode.copy()
@@ -889,7 +892,7 @@ def copy_dynamic_regions_and_locations(world, ret):
for location in world.dynamic_locations: for location in world.dynamic_locations:
new_reg = ret.get_region(location.parent_region.name, location.parent_region.player) new_reg = ret.get_region(location.parent_region.name, location.parent_region.player)
new_loc = Location(location.player, location.name, location.address, location.crystal, location.hint_text, new_reg) new_loc = Location(location.player, location.name, location.address, location.prize, location.hint_text, new_reg)
new_loc.type = location.type new_loc.type = location.type
new_reg.locations.append(new_loc) new_reg.locations.append(new_loc)
+18
View File
@@ -340,6 +340,16 @@ location_table_uw = {"Blind's Hideout - Top": (0x11d, 0x10),
'Ganons Tower - Mini Helmasaur Key Drop': (0x3d, 0x400), 'Ganons Tower - Mini Helmasaur Key Drop': (0x3d, 0x400),
'Ganons Tower - Pre-Moldorm Chest': (0x3d, 0x40), 'Ganons Tower - Pre-Moldorm Chest': (0x3d, 0x40),
'Ganons Tower - Validation Chest': (0x4d, 0x10)} 'Ganons Tower - Validation Chest': (0x4d, 0x10)}
location_table_boss = {'Eastern Palace - Prize': 0x2000,
'Desert Palace - Prize': 0x1000,
'Tower of Hera - Prize': 0x0020,
'Palace of Darkness - Prize': 0x0200,
'Thieves Town - Prize': 0x0010,
'Skull Woods - Prize': 0x0080,
'Swamp Palace - Prize': 0x0400,
'Ice Palace - Prize': 0x0040,
'Misery Mire - Prize': 0x0100,
'Turtle Rock - Prize': 0x0008}
location_table_npc = {'Mushroom': 0x1000, location_table_npc = {'Mushroom': 0x1000,
'King Zora': 0x2, 'King Zora': 0x2,
'Sahasrahla': 0x10, 'Sahasrahla': 0x10,
@@ -949,6 +959,14 @@ async def track_locations(ctx : Context, roomid, roomdata):
if roomdata & mask != 0: if roomdata & mask != 0:
new_check(location) new_check(location)
if not all([location in ctx.locations_checked for location in location_table_boss.keys()]):
boss_data = await snes_read(ctx, SAVEDATA_START + 0x472, 2)
if boss_data is not None:
boss_value = boss_data[0] | (boss_data[1] << 8)
for location, mask in location_table_boss.items():
if boss_value & mask != 0 and location not in ctx.locations_checked:
new_check(location)
ow_begin = 0x82 ow_begin = 0x82
ow_end = 0 ow_end = 0
ow_unchecked = {} ow_unchecked = {}
+56 -32
View File
@@ -1124,8 +1124,8 @@ def _create_region(player, name, type, hint='Hyrule', locations=None, exits=None
ko_hint = key_drop_data[location][2] ko_hint = key_drop_data[location][2]
ret.locations.append(Location(player, location, None, False, ko_hint, ret, key_drop_data[location][3])) ret.locations.append(Location(player, location, None, False, ko_hint, ret, key_drop_data[location][3]))
else: else:
address, player_address, crystal, hint_text = location_table[location] address, player_address, prize, hint_text = location_table[location]
ret.locations.append(Location(player, location, address, crystal, hint_text, ret, None, player_address)) ret.locations.append(Location(player, location, address, prize, hint_text, ret, None, player_address))
return ret return ret
def mark_light_dark_world_regions(world, player): def mark_light_dark_world_regions(world, player):
@@ -1240,14 +1240,14 @@ def adjust_locations(world, player):
# player address? it is in the shop table # player address? it is in the shop table
index += 1 index += 1
setup_enemy_locations(world, player) setup_enemy_locations(world, player)
# disable forced prize locations
if world.prizeshuffle[player] != 'none':
for l in [name for name, data in location_table.items() if data[2]]:
location = world.get_location_unsafe(l, player)
if location:
location.prize = False
# unreal events: # unreal events:
for l in ['Ganon', 'Agahnim 1', 'Agahnim 2', 'Frog', 'Missing Smith', 'Dark Blacksmith Ruins', 'Middle Aged Man', for l in ['Ganon', 'Zelda Pickup', 'Zelda Drop Off'] + list(location_events):
'Floodgate', 'Trench 1 Switch', 'Trench 2 Switch', 'Swamp Drain', 'Turtle Medallion Pad',
'Attic Cracked Floor', 'Suspicious Maiden', 'Revealing Light', 'Big Bomb', 'Pyramid Crack',
'Ice Block Drop', 'Lost Old Man', 'Old Man Drop Off', 'Zelda Pickup', 'Zelda Drop Off', 'Skull Star Tile',
'Eastern Palace - Boss Kill', 'Desert Palace - Boss Kill', 'Tower of Hera - Boss Kill',
'Palace of Darkness - Boss Kill', 'Swamp Palace - Boss Kill', 'Skull Woods - Boss Kill',
'Thieves\' Town - Boss Kill', 'Ice Palace - Boss Kill', 'Misery Mire - Boss Kill', 'Turtle Rock - Boss Kill']:
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
@@ -1397,18 +1397,42 @@ shop_table_by_location_id = {0x400000+cnt: x for cnt, x in enumerate(flat_normal
shop_table_by_location_id = {**shop_table_by_location_id, **{0x400020+cnt: x for cnt, x in enumerate(flat_retro_shops)}} shop_table_by_location_id = {**shop_table_by_location_id, **{0x400020+cnt: x for cnt, x in enumerate(flat_retro_shops)}}
shop_table_by_location = {y: x for x, y in shop_table_by_location_id.items()} shop_table_by_location = {y: x for x, y in shop_table_by_location_id.items()}
dungeon_events = [
'Trench 1 Switch', location_events = {
'Trench 2 Switch', 'Agahnim 1': 'Beat Agahnim 1',
'Swamp Drain', 'Agahnim 2': 'Beat Agahnim 2',
'Attic Cracked Floor', 'Eastern Palace - Boss Kill': 'Beat Boss',
'Suspicious Maiden', 'Desert Palace - Boss Kill': 'Beat Boss',
'Revealing Light', 'Tower of Hera - Boss Kill': 'Beat Boss',
'Ice Block Drop', 'Palace of Darkness - Boss Kill': 'Beat Boss',
'Skull Star Tile', 'Swamp Palace - Boss Kill': 'Beat Boss',
'Zelda Pickup', 'Skull Woods - Boss Kill': 'Beat Boss',
'Zelda Drop Off' 'Thieves\' Town - Boss Kill': 'Beat Boss',
] 'Ice Palace - Boss Kill': 'Beat Boss',
'Misery Mire - Boss Kill': 'Beat Boss',
'Turtle Rock - Boss Kill': 'Beat Boss',
'Lost Old Man': 'Escort Old Man',
'Old Man Drop Off': 'Return Old Man',
'Floodgate': 'Open Floodgate',
'Big Bomb': 'Pick Up Big Bomb',
'Pyramid Crack': 'Detonate Big Bomb',
'Frog': 'Get Frog',
'Missing Smith': 'Return Smith',
'Dark Blacksmith Ruins': 'Pick Up Purple Chest',
'Middle Aged Man': 'Deliver Purple Chest',
'Trench 1 Switch': 'Trench 1 Filled',
'Trench 2 Switch': 'Trench 2 Filled',
'Swamp Drain': 'Drained Swamp',
'Turtle Medallion Pad': 'Turtle Opened',
'Attic Cracked Floor': 'Shining Light',
'Suspicious Maiden': 'Maiden Rescued',
'Revealing Light': 'Maiden Unmasked',
'Ice Block Drop': 'Convenient Block',
'Skull Star Tile': 'Hidden Pits',
'Zelda Pickup': None,
'Zelda Drop Off': None
}
flooded_keys_reverse = { flooded_keys_reverse = {
'Swamp Palace - Trench 1 Pot Key': 'Trench 1 Switch', 'Swamp Palace - Trench 1 Pot Key': 'Trench 1 Switch',
@@ -1524,7 +1548,7 @@ location_table = {'Mushroom': (0x180013, 0x186df8, False, 'in the woods'),
'Pyramid Fairy - Right': (0xe983, 0x186c17, False, 'near a fairy'), 'Pyramid Fairy - Right': (0xe983, 0x186c17, False, 'near a fairy'),
'Brewery': (0xe9ec, 0x186c80, False, 'alone in a home'), 'Brewery': (0xe9ec, 0x186c80, False, 'alone in a home'),
'C-Shaped House': (0xe9ef, 0x186c83, False, 'alone in a home'), 'C-Shaped House': (0xe9ef, 0x186c83, False, 'alone in a home'),
'Chest Game': (0xeda8, 0x186e2b, False, 'as a prize'), 'Chest Game': (0xeda8, 0x186e2b, False, 'as a game reward'),
'Bumper Cave Ledge': (0x180146, 0x186e15, False, 'on a ledge'), 'Bumper Cave Ledge': (0x180146, 0x186e15, False, 'on a ledge'),
'Mire Shed - Left': (0xea73, 0x186d07, False, 'near sparks'), 'Mire Shed - Left': (0xea73, 0x186d07, False, 'near sparks'),
'Mire Shed - Right': (0xea76, 0x186d0a, False, 'near sparks'), 'Mire Shed - Right': (0xea76, 0x186d0a, False, 'near sparks'),
@@ -1665,16 +1689,16 @@ location_table = {'Mushroom': (0x180013, 0x186df8, False, 'in the woods'),
'Skull Star Tile': (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': (0xC6FE, 0x186E2C, True, 'with the Armos'),
'Desert Palace - Prize': ([0x1209E, 0x53E7A, 0x53E7B, 0x180053, 0x180072, 0xC6FF, 0x186FE3], None, True, 'Desert Palace'), 'Desert Palace - Prize': (0xC6FF, 0x186E2D, True, 'with Lanmolas'),
'Tower of Hera - Prize': ([0x120A5, 0x53E78, 0x53E79, 0x18005A, 0x180071, 0xC706, 0x186FEA], None, True, 'Tower of Hera'), 'Tower of Hera - Prize': (0xC706, 0x186E2E, True, 'with Moldorm'),
'Palace of Darkness - Prize': ([0x120A1, 0x53E7C, 0x53E7D, 0x180056, 0x180073, 0xC702, 0x186FE6], None, True, 'Palace of Darkness'), 'Palace of Darkness - Prize': (0xC702, 0x186E2F, True, 'with Helmasaur King'),
'Swamp Palace - Prize': ([0x120A0, 0x53E88, 0x53E89, 0x180055, 0x180079, 0xC701, 0x186FE5], None, True, 'Swamp Palace'), 'Swamp Palace - Prize': (0xC701, 0x186E30, True, 'with Arrghus'),
'Thieves\' Town - Prize': ([0x120A6, 0x53E82, 0x53E83, 0x18005B, 0x180076, 0xC707, 0x186FEB], None, True, 'Thieves Town'), 'Skull Woods - Prize': (0xC704, 0x186E31, True, 'with Mothula'),
'Skull Woods - Prize': ([0x120A3, 0x53E7E, 0x53E7F, 0x180058, 0x180074, 0xC704, 0x186FE8], None, True, 'Skull Woods'), 'Thieves\' Town - Prize': (0xC707, 0x186E32, True, 'with Blind'),
'Ice Palace - Prize': ([0x120A4, 0x53E86, 0x53E87, 0x180059, 0x180078, 0xC705, 0x186FE9], None, True, 'Ice Palace'), 'Ice Palace - Prize': (0xC705, 0x186E33, True, 'with Kholdstare'),
'Misery Mire - Prize': ([0x120A2, 0x53E84, 0x53E85, 0x180057, 0x180077, 0xC703, 0x186FE7], None, True, 'Misery Mire'), 'Misery Mire - Prize': (0xC703, 0x186E34, True, 'with Vitreous'),
'Turtle Rock - Prize': ([0x120A7, 0x53E80, 0x53E81, 0x18005C, 0x180075, 0xC708, 0x186FEC], None, True, 'Turtle Rock'), 'Turtle Rock - Prize': (0xC708, 0x186E35, True, 'with Trinexx'),
'Kakariko Shop - Left': (None, None, False, 'for sale in Kakariko'), 'Kakariko Shop - Left': (None, None, False, 'for sale in Kakariko'),
'Kakariko Shop - Middle': (None, None, False, 'for sale in Kakariko'), 'Kakariko Shop - Middle': (None, None, False, 'for sale in Kakariko'),
'Kakariko Shop - Right': (None, None, False, 'for sale in Kakariko'), 'Kakariko Shop - Right': (None, None, False, 'for sale in Kakariko'),
+178 -113
View File
@@ -29,7 +29,7 @@ from Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts, Blacksmiths
from Text import LostWoods_texts, WishingWell_texts, DesertPalace_texts, MountainTower_texts, LinksHouse_texts from Text import LostWoods_texts, WishingWell_texts, DesertPalace_texts, MountainTower_texts, LinksHouse_texts
from Text import Lumberjacks_texts, SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts, Sahasrahla_names from Text import Lumberjacks_texts, SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts, Sahasrahla_names
from Utils import output_path, local_path, int16_as_bytes, int32_as_bytes, snes_to_pc from Utils import output_path, local_path, int16_as_bytes, int32_as_bytes, snes_to_pc
from Items import ItemFactory from Items import ItemFactory, prize_item_table
from EntranceShuffle import door_addresses, exit_ids, ow_prize_table from EntranceShuffle import door_addresses, exit_ids, ow_prize_table
from OverworldShuffle import default_flute_connections, flute_data from OverworldShuffle import default_flute_connections, flute_data
from InitialSram import InitialSram from InitialSram import InitialSram
@@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings
JAP10HASH = '03a63945398191337e896e5771f77173' JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = 'ddc8fd3a8c7265b8748c0df4382f8f0e' RANDOMIZERBASEHASH = 'd0d67e62722fc7c2192c1f3b1cd8284b'
class JsonRom(object): class JsonRom(object):
@@ -462,32 +462,31 @@ def patch_rom(world, rom, player, team, is_mystery=False):
if location.address is None or (type(location.address) is int and location.address >= 0x400000): if location.address is None or (type(location.address) is int and location.address >= 0x400000):
continue continue
if not location.crystal: if location.item is not None:
if location.item is not None: # Keys in their native dungeon should use the original item code for keys
# Keys in their native dungeon should use the original item code for keys itemid = handle_native_dungeon(location, itemid)
itemid = handle_native_dungeon(location, itemid) if world.remote_items[player]:
if world.remote_items[player]: itemid = list(location_table.keys()).index(location.name) + 1
itemid = list(location_table.keys()).index(location.name) + 1 assert itemid < 0x100
assert itemid < 0x100 rom.write_byte(location.player_address, 0xFF)
rom.write_byte(location.player_address, 0xFF) elif location.item.player != player:
elif location.item.player != player: if location.player_address is not None:
if location.player_address is not None: rom.write_byte(location.player_address, location.item.player)
rom.write_byte(location.player_address, location.item.player) else:
else: itemid = 0x5A
itemid = 0x5A rom.write_byte(location.address, itemid)
rom.write_byte(location.address, itemid) for dungeon in [d for d in world.dungeons if d.player == player]:
else: if dungeon.prize:
# crystals # crystals
for address, value in zip(location.address, itemid): for address, value in zip(dungeon_table[dungeon.name].prize, prize_item_table[dungeon.prize.name]):
rom.write_byte(address, value) rom.write_byte(address, value)
# patch music # patch music
music_addresses = dungeon_music_addresses[location.name]
if world.mapshuffle[player]: if world.mapshuffle[player]:
music = random.choice([0x11, 0x16]) music = random.choice([0x11, 0x16])
else: else:
music = 0x11 if 'Pendant' in location.item.name else 0x16 music = 0x11 if 'Pendant' in dungeon.prize.name else 0x16
for music_address in music_addresses: for music_address in dungeon_music_addresses[dungeon.name]:
rom.write_byte(music_address, music) rom.write_byte(music_address, music)
if world.mapshuffle[player]: if world.mapshuffle[player]:
@@ -724,7 +723,8 @@ def patch_rom(world, rom, player, team, is_mystery=False):
or (l.type == LocationType.Drop and not l.forced_item) or (l.type == LocationType.Drop and not l.forced_item)
or (l.type == LocationType.Normal and not l.forced_item) or (l.type == LocationType.Normal and not l.forced_item)
or (l.type == LocationType.Bonk and not l.forced_item) or (l.type == LocationType.Bonk and not l.forced_item)
or (l.type == LocationType.Shop and world.shopsanity[player]))] or (l.type == LocationType.Shop and world.shopsanity[player])
or (l.type == LocationType.Prize and not l.prize))]
valid_loc_by_dungeon = valid_dungeon_locations(valid_locations) valid_loc_by_dungeon = valid_dungeon_locations(valid_locations)
# fix hc big key problems (map and compass too) # fix hc big key problems (map and compass too)
@@ -1077,7 +1077,11 @@ def patch_rom(world, rom, player, team, is_mystery=False):
0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel 0xFF, 0xFF, 0xFF, 0xFF, # end of table sentinel
]) ])
# item GFX changes # item property changes
if world.prizeshuffle[player] != 'none':
# allows prizes to contribute to collection rate
write_int16s(rom, snes_to_pc(0x22C06E), [0x01]*3) # pendants
write_int16s(rom, snes_to_pc(0x22C160), [0x81]*7) # crystals
if world.bombbag[player]: if world.bombbag[player]:
rom.write_byte(snes_to_pc(0x22C8A4), 0xE0) # use new bomb bag gfx rom.write_byte(snes_to_pc(0x22C8A4), 0xE0) # use new bomb bag gfx
rom.write_byte(snes_to_pc(0x22BD52), 0x02) rom.write_byte(snes_to_pc(0x22BD52), 0x02)
@@ -1207,6 +1211,8 @@ def patch_rom(world, rom, player, team, is_mystery=False):
rom.write_byte(0x1800A1, 0x01) # enable overworld screen transition draining for water level inside swamp rom.write_byte(0x1800A1, 0x01) # enable overworld screen transition draining for water level inside swamp
rom.write_byte(0x180174, 0x01 if world.fix_fake_world[player] else 0x00) rom.write_byte(0x180174, 0x01 if world.fix_fake_world[player] else 0x00)
rom.write_byte(0x18017E, 0x01) # Fairy fountains only trade in bottles rom.write_byte(0x18017E, 0x01) # Fairy fountains only trade in bottles
# fix for allowing prize itemgets being able to update the HUD (buying prizes in shops and updating rupees)
rom.write_bytes(snes_to_pc(0x0799FB), [0x80, 0x11, 0xEA])
# Starting equipment # Starting equipment
if world.pseudoboots[player]: if world.pseudoboots[player]:
@@ -1262,94 +1268,152 @@ def patch_rom(world, rom, player, team, is_mystery=False):
# Bitfield - enable text box to show with free roaming items # Bitfield - enable text box to show with free roaming items
# #
# ---o bmcs # --po bmcs
# p - enabled for non-prize crystals
# o - enabled for outside dungeon items # o - enabled for outside dungeon items
# b - enabled for inside big keys # b - enabled for inside big keys
# m - enabled for inside maps # m - enabled for inside maps
# c - enabled for inside compasses # c - enabled for inside compasses
# s - enabled for inside small keys # s - enabled for inside small keys
rom.write_byte(0x18016A, 0x10 | ((0x01 if world.keyshuffle[player] == 'wild' else 0x00) rom.write_byte(0x18016A, 0x10 | ((0x20 if world.prizeshuffle[player] == 'wild' else 0x00)
| (0x01 if world.keyshuffle[player] == 'wild' else 0x00)
| (0x02 if world.compassshuffle[player] else 0x00) | (0x02 if world.compassshuffle[player] else 0x00)
| (0x04 if world.mapshuffle[player] else 0x00) | (0x04 if world.mapshuffle[player] else 0x00)
| (0x08 if world.bigkeyshuffle[player] else 0x00))) # free roaming item text boxes | (0x08 if world.bigkeyshuffle[player] else 0x00))) # free roaming item text boxes
rom.write_byte(0x18003B, 0x01 if world.mapshuffle[player] else 0x00) # maps showing crystals on overworld rom.write_byte(0x18003B, 0x01 if world.mapshuffle[player] else 0x00) # maps showing crystals on overworld
# compasses showing dungeon count # compasses showing dungeon count
compass_mode = 0x00 compass_mode = 0x80 if world.compassshuffle[player] else 0x00
if world.clock_mode != 'none' or world.dungeon_counters[player] == 'off': if world.clock_mode != 'none' or world.dungeon_counters[player] == 'off':
compass_mode = 0x00 # Currently must be off if timer is on, because they use same HUD location pass
rom.write_byte(0x18003C, 0x00)
elif world.dungeon_counters[player] == 'on': elif world.dungeon_counters[player] == 'on':
compass_mode = 0x02 # always on compass_mode |= 0x02 # always on
elif (world.compassshuffle[player] or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none' elif (world.compassshuffle[player] or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none'
or world.dungeon_counters[player] == 'pickup' or world.pottery[player] not in ['none', 'cave']): or world.dungeon_counters[player] == 'pickup' or world.pottery[player] not in ['none', 'cave']):
compass_mode = 0x01 # show on pickup compass_mode |= 0x01 # show on pickup
if (world.shuffle[player] != 'vanilla' and world.overworld_map[player] != 'default') or world.owMixed[player]: if world.overworld_map[player] == 'map':
compass_mode |= 0x80 # turn on locating dungeons compass_mode |= 0x10 # show icon if map is collected
if world.overworld_map[player] == 'compass': elif world.overworld_map[player] == 'compass':
compass_mode |= 0x20 # show icon if compass is collected, 0x00 for maps compass_mode |= 0x20 # show icon if compass is collected
if world.compassshuffle[player]: if world.prizeshuffle[player] == 'wild':
compass_mode |= 0x40 # dungeon item that enables icon is wild compass_mode |= 0x40 # show icon if boss is defeated, hide if collected
elif world.overworld_map[player] == 'map' or world.owMixed[player]: rom.write_byte(0x18003C, compass_mode)
if world.mapshuffle[player]:
compass_mode |= 0x40 # dungeon item that enables icon is wild
if world.shuffle[player] != 'vanilla' and world.overworld_map[player] != 'default': def get_entrance_coords(ent):
x_map_position_generic = [0x3c0, 0xbc0, 0x7c0, 0x1c0, 0x5c0, 0xdc0, 0x7c0, 0xbc0, 0x9c0, 0x3c0] if type(ent) is Location:
for idx, x_map in enumerate(x_map_position_generic): from OverworldShuffle import OWTileRegions
rom.write_bytes(0x53df6+idx*2, int16_as_bytes(x_map)) if ent.name == 'Hobo':
rom.write_bytes(0x53e16+idx*2, int16_as_bytes(0xFC0)) coords = (0xb80, 0xb80)
elif world.overworld_map[player] == 'default': elif ent.name == 'Master Sword Pedestal':
# disable HC/AT/GT icons coords = (0x06d, 0x070)
if not world.owMixed[player]:
rom.write_bytes(0x53E8A, int16_as_bytes(0xFF00)) # GT
rom.write_bytes(0x53E8C, int16_as_bytes(0xFF00)) # AT
rom.write_bytes(0x53E8E, int16_as_bytes(0xFF00)) # HC
for dungeon, portal_list in dungeon_portals.items():
ow_map_index = dungeon_table[dungeon].map_index
if world.shuffle[player] != 'vanilla' and world.overworld_map[player] == 'default':
vanilla_entrances = { 'Hyrule Castle': 'Hyrule Castle Entrance (South)',
'Desert Palace': 'Desert Palace Entrance (North)',
'Skull Woods': 'Skull Woods Final Section'
}
entrance_name = vanilla_entrances[dungeon] if dungeon in vanilla_entrances else dungeon
entrance = world.get_entrance(entrance_name, player)
else: else:
if world.shuffle[player] != 'vanilla': owid = OWTileRegions[ent.parent_region.name]
if len(portal_list) == 1: if owid == 0x81:
portal_idx = 0 coords = (0x220, 0xf40)
else:
if world.doorShuffle[player] not in ['vanilla', 'basic']:
# the random choice excludes sanctuary
portal_idx = next((i for i, elem in enumerate(portal_list)
if world.get_portal(elem, player).chosen), random.choice([1, 2, 3]))
else:
portal_idx = {'Hyrule Castle': 0, 'Desert Palace': 0, 'Skull Woods': 3, 'Turtle Rock': 3}[dungeon]
else: else:
if dungeon in ['Hyrule Castle', 'Agahnims Tower', 'Ganons Tower']: owid = owid % 0x40
portal_idx = -1 coords = (0x200 * (owid % 0x08) + 0x100, 0x200 * int(owid / 0x08) + 0x100)
elif len(portal_list) == 1: if owid in [0x00, 0x03, 0x05, 0x18, 0x1b, 0x1e, 0x30, 0x35]:
portal_idx = 0 coords = (coords[0] + 0x100, coords[1] + 0x100)
else: else:
portal_idx = {'Desert Palace': 1, 'Skull Woods': 3, 'Turtle Rock': 0}[dungeon] coords = ow_prize_table[ent.name]
portal = world.get_portal(portal_list[0 if portal_idx == -1 else portal_idx], player) coords = ((0x8000 if ent.parent_region.type == RegionType.DarkWorld else 0x0000) | coords[0], coords[1])
entrance = portal.find_portal_entrance() return coords
world_indicator = 0x01 if entrance.parent_region.type == RegionType.DarkWorld else 0x00 if world.overworld_map[player] == 'default':
coords = ow_prize_table[entrance.name] # disable HC/AT/GT icons
# figure out compass entrances and what world (light/dark) if not world.owMixed[player]:
if world.overworld_map[player] != 'default' or world.owMixed[player]: write_int16(rom, snes_to_pc(0x0ABF52)+0x1A, 0x0000) # GT
rom.write_bytes(0x53E36+ow_map_index*2, int16_as_bytes(coords[0])) write_int16(rom, snes_to_pc(0x0ABF52)+0x08, 0x0000) # AT
rom.write_bytes(0x53E56+ow_map_index*2, int16_as_bytes(coords[1])) write_int16(rom, snes_to_pc(0x0ABF52)+0x00, 0x0000) # HC
rom.write_byte(0x53EA6+ow_map_index, world_indicator) for dungeon, portal_list in dungeon_portals.items():
dungeon_index = dungeon_table[dungeon].dungeon_index
extra_map_index = dungeon_table[dungeon].extra_map_index
map_index = max(0, dungeon_index - 2)
# write out dislocated coords
if map_index >= 0x02 and map_index < 0x18 and (world.overworld_map[player] != 'default' or world.prizeshuffle[player] == 'wild'):
owid_map = [0x1E, 0x30, 0xFF, 0x7B, 0x5E, 0x70, 0x40, 0x75, 0x03, 0x58, 0x47]
x_map_position_generic = [0x03c0, 0x0740, 0xff00, 0x03c0, 0x01c0, 0x0bc0, 0x05c0, 0x09c0, 0x0ac0, 0x07c0, 0x0dc0]
y_map_position_generic = [0xff00, 0xff00, 0xff00, 0x0fc0, 0x0fc0, 0x0fc0, 0x0fc0, 0x0fc0, 0xff00, 0x0fc0, 0x0fc0]
world_indicator = 0x0000
idx = int((map_index-2)/2)
owid = owid_map[idx]
if owid != 0xFF:
if (owid < 0x40) == (world.is_tile_swapped(owid, player)):
world_indicator = 0x8000
write_int16(rom, snes_to_pc(0x0ABE2E)+(map_index*6)+4, world_indicator | x_map_position_generic[idx])
write_int16(rom, snes_to_pc(0x0ABE2E)+(map_index*6)+6, y_map_position_generic[idx])
# write out icon coord data
if world.prizeshuffle[player] == 'wild' and dungeon_table[dungeon].prize:
dungeon_obj = world.get_dungeon(dungeon, player)
entrance = dungeon_obj.prize.get_map_location()
coords = get_entrance_coords(entrance)
# prize location
write_int16s(rom, snes_to_pc(0x0ABE2E)+(map_index*6)+8, coords)
if world.shuffle[player] == 'vanilla' or world.overworld_map[player] == 'default':
# TODO: I think this is logically the same as some of the vanilla stuff below
vanilla_entrances = { 'Hyrule Castle': 'Hyrule Castle Entrance (South)',
'Desert Palace': 'Desert Palace Entrance (North)',
'Skull Woods': 'Skull Woods Final Section' }
entrance_name = vanilla_entrances[dungeon] if dungeon in vanilla_entrances else dungeon
if world.is_atgt_swapped(player):
swap_entrances = { 'Agahnims Tower': 'Ganons Tower',
'Ganons Tower': 'Agahnims Tower' }
entrance_name = swap_entrances[dungeon] if dungeon in swap_entrances else entrance_name
entrance = world.get_entrance(entrance_name, player)
else:
if len(portal_list) == 1:
portal_idx = 0
else:
vanilla_portal_idx = {'Hyrule Castle': 0, 'Desert Palace': 0, 'Skull Woods': 3, 'Turtle Rock': 3}
extra_map_offsets = {'Hyrule Castle': 0, 'Desert Palace': 0x12, 'Skull Woods': 0x20, 'Turtle Rock': 0x3E}
portal_idx = vanilla_portal_idx[dungeon]
offset = 0
if (world.overworld_map[player] != 'default' and world.shuffle[player] not in ['vanilla', 'dungeonssimple']
and (dungeon != 'Skull Woods' or world.shuffle[player] in ['district', 'insanity'])):
for i, elem in enumerate(portal_list):
if i != portal_idx and (elem != 'Sanctuary' or world.shuffle[player] in ['district', 'insanity']):
portal = world.get_portal(elem, player)
entrance = portal.find_portal_entrance()
coords = get_entrance_coords(entrance)
write_int16s(rom, snes_to_pc(0x8ABECA+extra_map_offsets[dungeon]+offset), coords)
offset += 4
portal = world.get_portal(portal_list[portal_idx], player)
entrance = portal.find_portal_entrance()
coords = get_entrance_coords(entrance)
# figure out compass entrances and what world (light/dark)
write_int16s(rom, snes_to_pc(0x0ABE2E)+(map_index*6), coords)
if world.prizeshuffle[player] != 'wild' and dungeon_table[dungeon].prize:
# prize location
write_int16s(rom, snes_to_pc(0x0ABE2E)+(map_index*6)+8, coords)
# Map reveals
reveal_bytes = {
"Hyrule Castle": 0xC000,
"Eastern Palace": 0x2000,
"Desert Palace": 0x1000,
"Tower of Hera": 0x0020,
"Agahnims Tower": 0x800,
"Palace of Darkness": 0x0200,
"Thieves Town": 0x0010,
"Skull Woods": 0x0080,
"Swamp Palace": 0x0400,
"Ice Palace": 0x0040,
"Misery Mire": 0x0100,
"Turtle Rock": 0x0008,
"Ganons Tower": 0x0004
}
# in crossed doors - flip the compass exists flags # in crossed doors - flip the compass exists flags
if world.doorShuffle[player] not in ['vanilla', 'basic']: if world.doorShuffle[player] not in ['vanilla', 'basic']:
compass_exists = 0x0000
for dungeon, portal_list in dungeon_portals.items(): for dungeon, portal_list in dungeon_portals.items():
ow_map_index = dungeon_table[dungeon].map_index dungeon_index = dungeon_table[dungeon].dungeon_index
exists_flag = any(x for x in world.get_dungeon(dungeon, player).dungeon_items if x.type == 'Compass') if any(x for x in world.get_dungeon(dungeon, player).dungeon_items if x.type == 'Compass'):
rom.write_byte(0x53E96+ow_map_index, 0x1 if exists_flag else 0x0) compass_exists |= reveal_bytes.get(dungeon, 0x0000)
write_int16(rom, snes_to_pc(0x0ABF6E), compass_exists)
rom.write_byte(0x18003C, compass_mode)
# Bitfield - enable free items to show up in menu # Bitfield - enable free items to show up in menu
# #
@@ -1360,34 +1424,17 @@ def patch_rom(world, rom, player, team, is_mystery=False):
# b - Big Key # b - Big Key
# a - Small Key # a - Small Key
# #
enable_menu_map_check = world.overworld_map[player] != 'default' and world.shuffle[player] != 'vanilla' enable_menu_map_check = (world.overworld_map[player] != 'default' and world.shuffle[player] != 'vanilla') or world.prizeshuffle[player] == 'wild'
rom.write_byte(0x180045, ((0x01 if world.keyshuffle[player] == 'wild' else 0x00) rom.write_byte(0x180045, ((0x01 if world.keyshuffle[player] == 'wild' else 0x00)
| (0x02 if world.bigkeyshuffle[player] else 0x00) | (0x02 if world.bigkeyshuffle[player] else 0x00)
| (0x04 if world.mapshuffle[player] or enable_menu_map_check else 0x00) | (0x04 if world.mapshuffle[player] or enable_menu_map_check else 0x00)
| (0x08 if world.compassshuffle[player] else 0x00) # free roaming items in menu | (0x08 if world.compassshuffle[player] else 0x00) # free roaming items in menu
| (0x10 if world.logic[player] == 'nologic' else 0))) # boss icon | (0x10 if world.logic[player] == 'nologic' else 0))) # boss icon
# Map reveals
reveal_bytes = {
"Eastern Palace": 0x2000,
"Desert Palace": 0x1000,
"Tower of Hera": 0x0020,
"Palace of Darkness": 0x0200,
"Thieves Town": 0x0010,
"Skull Woods": 0x0080,
"Swamp Palace": 0x0400,
"Ice Palace": 0x0040,
"Misery Mire'": 0x0100,
"Turtle Rock": 0x0008,
}
def get_reveal_bytes(itemName): def get_reveal_bytes(itemName):
locations = world.find_items(itemName, player) for dungeon in world.dungeons:
if len(locations) < 1: if dungeon.player == player and dungeon.prize and dungeon.prize.name == itemName:
return 0x0000 return reveal_bytes.get(dungeon.name, 0x0000)
location = locations[0]
if location.parent_region and location.parent_region.dungeon:
return reveal_bytes.get(location.parent_region.dungeon.name, 0x0000)
return 0x0000 return 0x0000
write_int16(rom, 0x18017A, get_reveal_bytes('Green Pendant') if world.mapshuffle[player] else 0x0000) # Sahasrahla reveal write_int16(rom, 0x18017A, get_reveal_bytes('Green Pendant') if world.mapshuffle[player] else 0x0000) # Sahasrahla reveal
@@ -2160,6 +2207,8 @@ def write_strings(rom, world, player, team):
items_to_hint.extend(SmallKeys) items_to_hint.extend(SmallKeys)
if world.bigkeyshuffle[player]: if world.bigkeyshuffle[player]:
items_to_hint.extend(BigKeys) items_to_hint.extend(BigKeys)
if world.prizeshuffle[player] == 'wild':
items_to_hint.extend(Prizes)
random.shuffle(items_to_hint) random.shuffle(items_to_hint)
hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'district', 'swapped'] else 8 hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'district', 'swapped'] else 8
hint_count += 2 if world.doorShuffle[player] not in ['vanilla', 'basic'] else 0 hint_count += 2 if world.doorShuffle[player] not in ['vanilla', 'basic'] else 0
@@ -2273,10 +2322,14 @@ def write_strings(rom, world, player, team):
crystal5 = world.find_items('Crystal 5', player)[0] crystal5 = world.find_items('Crystal 5', player)[0]
crystal6 = world.find_items('Crystal 6', player)[0] crystal6 = world.find_items('Crystal 6', player)[0]
tt['bomb_shop'] = 'Big Bomb?\nMy supply is blocked until you clear %s and %s.' % (crystal5.hint_text, crystal6.hint_text)
greenpendant = world.find_items('Green Pendant', player)[0] greenpendant = world.find_items('Green Pendant', player)[0]
tt['sahasrahla_bring_courage'] = 'I lost my family heirloom in %s' % greenpendant.hint_text if world.prizeshuffle[player] == 'none':
(crystal5, crystal6, greenpendant) = tuple([x.parent_region.dungeon.name for x in [crystal5, crystal6, greenpendant]])
tt['bomb_shop'] = 'Big Bomb?\nMy supply is blocked until you clear %s and %s.' % (crystal5, crystal6)
tt['sahasrahla_bring_courage'] = 'I lost my family heirloom in %s' % greenpendant
else:
tt['bomb_shop'] = 'Big Bomb?\nThe crystals can be found %s and %s.' % (crystal5.hint_text, crystal6.hint_text)
tt['sahasrahla_bring_courage'] = 'My family heirloom can be found %s' % greenpendant.hint_text
tt['sign_ganons_tower'] = ('You need %d crystal to enter.' if world.crystals_needed_for_gt[player] == 1 else 'You need %d crystals to enter.') % world.crystals_needed_for_gt[player] tt['sign_ganons_tower'] = ('You need %d crystal to enter.' if world.crystals_needed_for_gt[player] == 1 else 'You need %d crystals to enter.') % world.crystals_needed_for_gt[player]
@@ -2966,6 +3019,18 @@ BigKeys = ['Big Key (Eastern Palace)',
'Big Key (Ganons Tower)' 'Big Key (Ganons Tower)'
] ]
Prizes = ['Green Pendant',
'Blue Pendant',
'Red Pendant',
'Crystal 1',
'Crystal 2',
'Crystal 3',
'Crystal 4',
'Crystal 5',
'Crystal 6',
'Crystal 7'
]
hash_alphabet = [ hash_alphabet = [
"Bow", "Boomerang", "Hookshot", "Bomb", "Mushroom", "Powder", "Rod", "Pendant", "Bombos", "Ether", "Quake", "Bow", "Boomerang", "Hookshot", "Bomb", "Mushroom", "Powder", "Rod", "Pendant", "Bombos", "Ether", "Quake",
"Lamp", "Hammer", "Shovel", "Ocarina", "Bug Net", "Book", "Bottle", "Potion", "Cane", "Cape", "Mirror", "Boots", "Lamp", "Hammer", "Shovel", "Ocarina", "Bug Net", "Book", "Bottle", "Potion", "Cane", "Cape", "Mirror", "Boots",
Binary file not shown.
+7
View File
@@ -387,6 +387,13 @@
"action": "store_true", "action": "store_true",
"type": "bool" "type": "bool"
}, },
"prizeshuffle": {
"choices": [
"none",
"dungeon",
"wild"
]
},
"keysanity": { "keysanity": {
"action": "store_true", "action": "store_true",
"type": "bool", "type": "bool",
+1
View File
@@ -350,6 +350,7 @@
"compassshuffle": [ "Compasses are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ], "compassshuffle": [ "Compasses are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ],
"keyshuffle": [ "Small Keys are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ], "keyshuffle": [ "Small Keys are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ],
"bigkeyshuffle": [ "Big Keys are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ], "bigkeyshuffle": [ "Big Keys are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ],
"prizeshuffle": [ "Prizes are no longer restricted to the bosses, but can be anywhere. (default: %(default)s)" ],
"shopsanity": ["Shop contents are shuffle in the main item pool and other items can take their place. (default: %(default)s)"], "shopsanity": ["Shop contents are shuffle in the main item pool and other items can take their place. (default: %(default)s)"],
"dropshuffle": [ "Controls how enemies drop items (default: %(default)s)", "dropshuffle": [ "Controls how enemies drop items (default: %(default)s)",
"None: Enemies drops prize packs or keys as normal", "None: Enemies drops prize packs or keys as normal",
+4
View File
@@ -59,6 +59,10 @@
"randomizer.dungeon.smallkeyshuffle.wild": "Randomized", "randomizer.dungeon.smallkeyshuffle.wild": "Randomized",
"randomizer.dungeon.smallkeyshuffle.universal": "Universal", "randomizer.dungeon.smallkeyshuffle.universal": "Universal",
"randomizer.dungeon.bigkeyshuffle": "Big Keys", "randomizer.dungeon.bigkeyshuffle": "Big Keys",
"randomizer.dungeon.prizeshuffle": "Prizes",
"randomizer.dungeon.prizeshuffle.none": "On Boss",
"randomizer.dungeon.prizeshuffle.dungeon": "In Dungeon",
"randomizer.dungeon.prizeshuffle.wild": "Randomized",
"randomizer.dungeon.decoupledoors": "Decouple Doors", "randomizer.dungeon.decoupledoors": "Decouple Doors",
"randomizer.dungeon.door_self_loops": "Allow Self-Looping Spiral Stairs", "randomizer.dungeon.door_self_loops": "Allow Self-Looping Spiral Stairs",
@@ -1,5 +1,17 @@
{ {
"widgets": { "widgets": {
"prizeshuffle": {
"type": "selectbox",
"default": "default",
"options": [
"none",
"dungeon",
"wild"
],
"config": {
"padx": [20,0]
}
},
"key_logic_algorithm": { "key_logic_algorithm": {
"type": "selectbox", "type": "selectbox",
"default": "default", "default": "default",
+2
View File
@@ -138,6 +138,7 @@ class CustomSettings(object):
args.experimental[p] = get_setting(settings['experimental'], args.experimental[p]) args.experimental[p] = get_setting(settings['experimental'], args.experimental[p])
args.collection_rate[p] = get_setting(settings['collection_rate'], args.collection_rate[p]) args.collection_rate[p] = get_setting(settings['collection_rate'], args.collection_rate[p])
args.openpyramid[p] = get_setting(settings['openpyramid'], args.openpyramid[p]) args.openpyramid[p] = get_setting(settings['openpyramid'], args.openpyramid[p])
args.prizeshuffle[p] = get_setting(settings['prizeshuffle'], args.prizeshuffle[p])
args.bigkeyshuffle[p] = get_setting(settings['bigkeyshuffle'], args.bigkeyshuffle[p]) args.bigkeyshuffle[p] = get_setting(settings['bigkeyshuffle'], args.bigkeyshuffle[p])
args.keyshuffle[p] = get_setting(settings['keyshuffle'], args.keyshuffle[p]) args.keyshuffle[p] = get_setting(settings['keyshuffle'], args.keyshuffle[p])
args.mapshuffle[p] = get_setting(settings['mapshuffle'], args.mapshuffle[p]) args.mapshuffle[p] = get_setting(settings['mapshuffle'], args.mapshuffle[p])
@@ -320,6 +321,7 @@ class CustomSettings(object):
settings_dict[p]['experimental'] = world.experimental[p] settings_dict[p]['experimental'] = world.experimental[p]
settings_dict[p]['collection_rate'] = world.collection_rate[p] settings_dict[p]['collection_rate'] = world.collection_rate[p]
settings_dict[p]['openpyramid'] = world.open_pyramid[p] settings_dict[p]['openpyramid'] = world.open_pyramid[p]
settings_dict[p]['prizeshuffle'] = world.prizeshuffle[p]
settings_dict[p]['bigkeyshuffle'] = world.bigkeyshuffle[p] settings_dict[p]['bigkeyshuffle'] = world.bigkeyshuffle[p]
settings_dict[p]['keyshuffle'] = world.keyshuffle[p] settings_dict[p]['keyshuffle'] = world.keyshuffle[p]
settings_dict[p]['mapshuffle'] = world.mapshuffle[p] settings_dict[p]['mapshuffle'] = world.mapshuffle[p]
+1
View File
@@ -111,6 +111,7 @@ SETTINGSTOPROCESS = {
"mapshuffle": "mapshuffle", "mapshuffle": "mapshuffle",
"compassshuffle": "compassshuffle", "compassshuffle": "compassshuffle",
"bigkeyshuffle": "bigkeyshuffle", "bigkeyshuffle": "bigkeyshuffle",
"prizeshuffle": "prizeshuffle",
"key_logic_algorithm": "key_logic_algorithm", "key_logic_algorithm": "key_logic_algorithm",
"dungeondoorshuffle": "door_shuffle", "dungeondoorshuffle": "door_shuffle",
"dungeonintensity": "intensity", "dungeonintensity": "intensity",
+5 -5
View File
@@ -5,7 +5,7 @@ import time
from BaseClasses import CrystalBarrier, DoorType, Hook, RegionType, Sector from BaseClasses import CrystalBarrier, DoorType, Hook, RegionType, Sector
from BaseClasses import hook_from_door, flooded_keys from BaseClasses import hook_from_door, flooded_keys
from Regions import dungeon_events, flooded_keys_reverse from Regions import location_events, flooded_keys_reverse
def pre_validate(builder, entrance_region_names, split_dungeon, world, player): def pre_validate(builder, entrance_region_names, split_dungeon, world, player):
@@ -556,7 +556,7 @@ class ExplorationState(object):
if key_checks and location not in self.found_locations: if key_checks and location not in self.found_locations:
if location.forced_item and 'Small Key' in location.item.name: if location.forced_item and 'Small Key' in location.item.name:
self.key_locations += 1 self.key_locations += 1
if location.name not in dungeon_events and '- Prize' not in location.name and location.name not in ['Agahnim 1', 'Agahnim 2']: if location.name not in location_events and not ('- Prize' in location.name and location.prize) and location.name not in ['Agahnim 1', 'Agahnim 2']:
self.ttl_locations += 1 self.ttl_locations += 1
if location not in self.found_locations: if location not in self.found_locations:
self.found_locations.append(location) self.found_locations.append(location)
@@ -568,13 +568,13 @@ class ExplorationState(object):
else: else:
self.bk_found.add(location) self.bk_found.add(location)
self.re_add_big_key_doors() self.re_add_big_key_doors()
if location.name in dungeon_events and location.name not in self.events: if location.name in location_events and location.name not in self.events:
if self.flooded_key_check(location): if self.flooded_key_check(location):
self.perform_event(location.name, key_region) self.perform_event(location.name, key_region)
if location.name in flooded_keys_reverse.keys() and self.location_found( if location.name in flooded_keys_reverse.keys() and self.location_found(
flooded_keys_reverse[location.name]): flooded_keys_reverse[location.name]):
self.perform_event(flooded_keys_reverse[location.name], key_region) self.perform_event(flooded_keys_reverse[location.name], key_region)
if '- Prize' in location.name: if location.prize:
self.prize_received = True self.prize_received = True
def flooded_key_check(self, location): def flooded_key_check(self, location):
@@ -837,7 +837,7 @@ def count_locations_exclude_big_chest(locations, world, player):
def prize_or_event(loc): def prize_or_event(loc):
return loc.name in dungeon_events or '- Prize' in loc.name or loc.name in ['Agahnim 1', 'Agahnim 2'] return loc.name in location_events or loc.prize
def reserved_location(loc, world, player): def reserved_location(loc, world, player):
+29 -3
View File
@@ -59,6 +59,8 @@ def create_item_pool_config(world):
if info.prize: if info.prize:
d_name = "Thieves' Town" if dungeon.startswith('Thieves') else dungeon d_name = "Thieves' Town" if dungeon.startswith('Thieves') else dungeon
config.reserved_locations[player].add(f'{d_name} - Boss') config.reserved_locations[player].add(f'{d_name} - Boss')
if world.prizeshuffle[player] != 'none':
config.reserved_locations[player].add(f'{d_name} - Prize')
for dungeon in world.dungeons: for dungeon in world.dungeons:
if world.restrict_boss_items[dungeon.player] != 'none': if world.restrict_boss_items[dungeon.player] != 'none':
for item in dungeon.all_items: for item in dungeon.all_items:
@@ -118,6 +120,9 @@ def create_item_pool_config(world):
LocationGroup('bkgt').locs(mode_grouping['GT Trash'])] LocationGroup('bkgt').locs(mode_grouping['GT Trash'])]
for loc_name in mode_grouping['Big Chests'] + mode_grouping['Heart Containers']: for loc_name in mode_grouping['Big Chests'] + mode_grouping['Heart Containers']:
config.reserved_locations[player].add(loc_name) config.reserved_locations[player].add(loc_name)
if world.prizeshuffle[player] != 'none':
for loc_name in mode_grouping['Prizes']:
config.reserved_locations[player].add(loc_name)
elif world.algorithm == 'major_only': elif world.algorithm == 'major_only':
config.location_groups = [ config.location_groups = [
LocationGroup('MajorItems'), LocationGroup('MajorItems'),
@@ -127,6 +132,8 @@ def create_item_pool_config(world):
init_set = mode_grouping['Overworld Major'] + mode_grouping['Big Chests'] + mode_grouping['Heart Containers'] init_set = mode_grouping['Overworld Major'] + mode_grouping['Big Chests'] + mode_grouping['Heart Containers']
for player in range(1, world.players + 1): for player in range(1, world.players + 1):
groups = LocationGroup('Major').locs(init_set) groups = LocationGroup('Major').locs(init_set)
if world.prizeshuffle[player] != 'none':
groups.locations.extend(mode_grouping['Prizes'])
if world.bigkeyshuffle[player]: if world.bigkeyshuffle[player]:
groups.locations.extend(mode_grouping['Big Keys']) groups.locations.extend(mode_grouping['Big Keys'])
if world.dropshuffle[player] != 'none': if world.dropshuffle[player] != 'none':
@@ -251,21 +258,33 @@ def location_prefilled(location, world, player):
def previously_reserved(location, world, player): def previously_reserved(location, world, player):
if '- Boss' in location.name: if '- Boss' in location.name or '- Prize' in location.name:
if world.restrict_boss_items[player] == 'mapcompass' and (not world.compassshuffle[player] if world.restrict_boss_items[player] == 'mapcompass' and (not world.compassshuffle[player]
or not world.mapshuffle[player]): or not world.mapshuffle[player]):
return True return True
if world.restrict_boss_items[player] == 'dungeon' and (not world.compassshuffle[player] if world.restrict_boss_items[player] == 'dungeon' and (not world.compassshuffle[player]
or not world.mapshuffle[player] or not world.mapshuffle[player]
or not world.bigkeyshuffle[player] or not world.bigkeyshuffle[player]
or world.keyshuffle[player] == 'none'): or world.keyshuffle[player] == 'none'
or world.prizeshuffle[player] in ['none', 'dungeon']):
return True return True
return False return False
def massage_item_pool(world): def massage_item_pool(world):
player_pool = defaultdict(list) player_pool = defaultdict(list)
dungeon_pool = defaultdict(list)
for dungeon in world.dungeons:
if dungeon_table[dungeon.name].prize:
dungeon_pool[dungeon.player].append(dungeon)
for player in dungeon_pool:
dungeons = list(dungeon_pool[player])
random.shuffle(dungeons)
dungeon_pool[player] = dungeons
for item in world.itempool: for item in world.itempool:
if item.prize:
dungeon = dungeon_pool[item.player].pop()
dungeon.prize = item
player_pool[item.player].append(item) player_pool[item.player].append(item)
for dungeon in world.dungeons: for dungeon in world.dungeons:
for item in dungeon.all_items: for item in dungeon.all_items:
@@ -273,7 +292,7 @@ def massage_item_pool(world):
player_pool[item.player].append(item) player_pool[item.player].append(item)
player_locations = defaultdict(list) player_locations = defaultdict(list)
for player in player_pool: for player in player_pool:
player_locations[player] = [x for x in world.get_unfilled_locations(player) if '- Prize' not in x.name] player_locations[player] = [x for x in world.get_unfilled_locations(player) if not x.prize]
discrepancy = len(player_pool[player]) - len(player_locations[player]) discrepancy = len(player_pool[player]) - len(player_locations[player])
if discrepancy: if discrepancy:
trash_options = [x for x in player_pool[player] if x.name in trash_items] trash_options = [x for x in player_pool[player] if x.name in trash_items]
@@ -342,6 +361,8 @@ def determine_major_items(world, player):
major_item_set = set(major_items) major_item_set = set(major_items)
if world.progressive == 'off': if world.progressive == 'off':
pass # now what? pass # now what?
if world.prizeshuffle[player] not in ['none', 'dungeon']:
major_item_set.update({x for x, y in item_table.items() if y[2] == 'Prize'})
if world.bigkeyshuffle[player]: if world.bigkeyshuffle[player]:
major_item_set.update({x for x, y in item_table.items() if y[2] == 'BigKey'}) major_item_set.update({x for x, y in item_table.items() if y[2] == 'BigKey'})
if world.keyshuffle[player] != 'none': if world.keyshuffle[player] != 'none':
@@ -687,6 +708,11 @@ mode_grouping = {
'Graveyard Cave', 'Kakariko Well - Top', "Blind's Hideout - Top", 'Bonk Rock Cave', "Aginah's Cave", 'Graveyard Cave', 'Kakariko Well - Top', "Blind's Hideout - Top", 'Bonk Rock Cave', "Aginah's Cave",
'Chest Game', 'Digging Game', 'Mire Shed - Left', 'Mimic Cave' 'Chest Game', 'Digging Game', 'Mire Shed - Left', 'Mimic Cave'
], ],
'Prizes': [
'Eastern Palace - Prize', 'Desert Palace - Prize', 'Tower of Hera - Prize',
'Palace of Darkness - Prize', 'Swamp Palace - Prize', 'Skull Woods - Prize',
"Thieves' Town - Prize", 'Ice Palace - Prize', 'Misery Mire - Prize', 'Turtle Rock - Prize',
],
'Big Keys': [ 'Big Keys': [
'Eastern Palace - Big Key Chest', 'Ganons Tower - Big Key Chest', 'Eastern Palace - Big Key Chest', 'Ganons Tower - Big Key Chest',
'Desert Palace - Big Key Chest', 'Tower of Hera - Big Key Chest', 'Palace of Darkness - Big Key Chest', 'Desert Palace - Big Key Chest', 'Tower of Hera - Big Key Chest', 'Palace of Darkness - Big Key Chest',
+1
View File
@@ -106,6 +106,7 @@ def roll_settings(weights):
if 'u' in dungeon_items: if 'u' in dungeon_items:
ret.keyshuffle = 'universal' ret.keyshuffle = 'universal'
ret.bigkeyshuffle = get_choice_bool('bigkey_shuffle') if 'bigkey_shuffle' in weights else 'b' in dungeon_items ret.bigkeyshuffle = get_choice_bool('bigkey_shuffle') if 'bigkey_shuffle' in weights else 'b' in dungeon_items
ret.prizeshuffle = get_choice('prize_shuffle')
ret.accessibility = get_choice('accessibility') ret.accessibility = get_choice('accessibility')
ret.restrict_boss_items = get_choice('restrict_boss_items') ret.restrict_boss_items = get_choice('restrict_boss_items')