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:
+75
-31
@@ -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]
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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)}
|
||||||
|
|||||||
@@ -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
@@ -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]))
|
||||||
|
|||||||
@@ -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
@@ -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':
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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'),
|
||||||
|
|||||||
@@ -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.
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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,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
@@ -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',
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
Reference in New Issue
Block a user