make "universal" small key shuffle a thing and split it out of retro
also make retro usable independently from the other world modes in mystery
This commit is contained in:
parent
bea54d91de
commit
685ff49711
|
@ -516,17 +516,13 @@ class CollectionState(object):
|
||||||
def has_key(self, item, player, count: int = 1):
|
def has_key(self, item, player, count: int = 1):
|
||||||
if self.world.logic[player] == 'nologic':
|
if self.world.logic[player] == 'nologic':
|
||||||
return True
|
return True
|
||||||
if self.world.retro[player]:
|
if self.world.keyshuffle[player] == "universal":
|
||||||
return self.can_buy_unlimited('Small Key (Universal)', player)
|
return self.can_buy_unlimited('Small Key (Universal)', player)
|
||||||
if count == 1:
|
|
||||||
return (item, player) in self.prog_items
|
|
||||||
return self.prog_items[item, player] >= count
|
return self.prog_items[item, player] >= count
|
||||||
|
|
||||||
def can_buy_unlimited(self, item: str, player: int) -> bool:
|
def can_buy_unlimited(self, item: str, player: int) -> bool:
|
||||||
for shop in self.world.shops:
|
return any(shop.region.player == player and shop.has_unlimited(item) and shop.region.can_reach(self) for
|
||||||
if shop.region.player == player and shop.has_unlimited(item) and shop.region.can_reach(self):
|
shop in self.world.shops)
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def item_count(self, item, player: int) -> int:
|
def item_count(self, item, player: int) -> int:
|
||||||
return self.prog_items[item, player]
|
return self.prog_items[item, player]
|
||||||
|
@ -619,9 +615,10 @@ class CollectionState(object):
|
||||||
)
|
)
|
||||||
|
|
||||||
def has_sword(self, player: int) -> bool:
|
def has_sword(self, player: int) -> bool:
|
||||||
return self.has('Fighter Sword', player) or self.has('Master Sword', player) or self.has('Tempered Sword',
|
return self.has('Fighter Sword', player) \
|
||||||
player) or self.has(
|
or self.has('Master Sword', player) \
|
||||||
'Golden Sword', player)
|
or self.has('Tempered Sword', player) \
|
||||||
|
or self.has('Golden Sword', player)
|
||||||
|
|
||||||
def has_beam_sword(self, player: int) -> bool:
|
def has_beam_sword(self, player: int) -> bool:
|
||||||
return self.has('Master Sword', player) or self.has('Tempered Sword', player) or self.has('Golden Sword', player)
|
return self.has('Master Sword', player) or self.has('Tempered Sword', player) or self.has('Golden Sword', player)
|
||||||
|
@ -1267,13 +1264,15 @@ class Spoiler(object):
|
||||||
def to_file(self, filename):
|
def to_file(self, filename):
|
||||||
self.parse_data()
|
self.parse_data()
|
||||||
|
|
||||||
def bool_to_text(variable: bool) -> str:
|
def bool_to_text(variable: Union[bool, str]) -> str:
|
||||||
|
if type(variable) == str:
|
||||||
|
return variable
|
||||||
return 'Yes' if variable else 'No'
|
return 'Yes' if variable else 'No'
|
||||||
|
|
||||||
with open(filename, 'w', encoding="utf-8-sig") as outfile:
|
with open(filename, 'w', encoding="utf-8-sig") as outfile:
|
||||||
outfile.write(
|
outfile.write(
|
||||||
'ALttP Berserker\'s Multiworld Version %s - Seed: %s\n\n' % (
|
'ALttP Berserker\'s Multiworld Version %s - Seed: %s\n\n' % (
|
||||||
self.metadata['version'], self.world.seed))
|
self.metadata['version'], self.world.seed))
|
||||||
outfile.write('Filling Algorithm: %s\n' % self.world.algorithm)
|
outfile.write('Filling Algorithm: %s\n' % self.world.algorithm)
|
||||||
outfile.write('Players: %d\n' % self.world.players)
|
outfile.write('Players: %d\n' % self.world.players)
|
||||||
outfile.write('Teams: %d\n' % self.world.teams)
|
outfile.write('Teams: %d\n' % self.world.teams)
|
||||||
|
@ -1312,7 +1311,7 @@ class Spoiler(object):
|
||||||
outfile.write('Compass shuffle: %s\n' % (
|
outfile.write('Compass shuffle: %s\n' % (
|
||||||
'Yes' if self.metadata['compassshuffle'][player] else 'No'))
|
'Yes' if self.metadata['compassshuffle'][player] else 'No'))
|
||||||
outfile.write(
|
outfile.write(
|
||||||
'Small Key shuffle: %s\n' % ('Yes' if self.metadata['keyshuffle'][player] else 'No'))
|
'Small Key shuffle: %s\n' % (bool_to_text(self.metadata['keyshuffle'][player])))
|
||||||
outfile.write('Big Key shuffle: %s\n' % (
|
outfile.write('Big Key shuffle: %s\n' % (
|
||||||
'Yes' if self.metadata['bigkeyshuffle'][player] else 'No'))
|
'Yes' if self.metadata['bigkeyshuffle'][player] else 'No'))
|
||||||
outfile.write('Boss shuffle: %s\n' % self.metadata['boss_shuffle'][player])
|
outfile.write('Boss shuffle: %s\n' % self.metadata['boss_shuffle'][player])
|
||||||
|
|
|
@ -6,7 +6,8 @@ from Items import ItemFactory
|
||||||
|
|
||||||
def create_dungeons(world, player):
|
def create_dungeons(world, player):
|
||||||
def make_dungeon(name, default_boss, dungeon_regions, big_key, small_keys, dungeon_items):
|
def make_dungeon(name, default_boss, dungeon_regions, big_key, small_keys, dungeon_items):
|
||||||
dungeon = Dungeon(name, dungeon_regions, big_key, [] if world.retro[player] else small_keys, dungeon_items, player)
|
dungeon = Dungeon(name, dungeon_regions, big_key, [] if world.keyshuffle[player] == "universal" else small_keys,
|
||||||
|
dungeon_items, player)
|
||||||
dungeon.boss = BossFactory(default_boss, player)
|
dungeon.boss = BossFactory(default_boss, player)
|
||||||
for region in dungeon.regions:
|
for region in dungeon.regions:
|
||||||
world.get_region(region, player).dungeon = dungeon
|
world.get_region(region, player).dungeon = dungeon
|
||||||
|
|
|
@ -231,17 +231,27 @@ def parse_arguments(argv, no_defaults=False):
|
||||||
--seed given will produce the same 10 (different) roms each
|
--seed given will produce the same 10 (different) roms each
|
||||||
time).
|
time).
|
||||||
''', type=int)
|
''', type=int)
|
||||||
parser.add_argument('--fastmenu', default=defval('normal'), const='normal', nargs='?', choices=['normal', 'instant', 'double', 'triple', 'quadruple', 'half'],
|
parser.add_argument('--fastmenu', default=defval('normal'), const='normal', nargs='?',
|
||||||
|
choices=['normal', 'instant', 'double', 'triple', 'quadruple', 'half'],
|
||||||
help='''\
|
help='''\
|
||||||
Select the rate at which the menu opens and closes.
|
Select the rate at which the menu opens and closes.
|
||||||
(default: %(default)s)
|
(default: %(default)s)
|
||||||
''')
|
''')
|
||||||
parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true')
|
parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true')
|
||||||
parser.add_argument('--disablemusic', help='Disables game music.', action='store_true')
|
parser.add_argument('--disablemusic', help='Disables game music.', action='store_true')
|
||||||
parser.add_argument('--mapshuffle', default=defval(False), help='Maps are no longer restricted to their dungeons, but can be anywhere', action='store_true')
|
parser.add_argument('--mapshuffle', default=defval(False),
|
||||||
parser.add_argument('--compassshuffle', default=defval(False), help='Compasses are no longer restricted to their dungeons, but can be anywhere', action='store_true')
|
help='Maps are no longer restricted to their dungeons, but can be anywhere',
|
||||||
parser.add_argument('--keyshuffle', default=defval(False), help='Small Keys are no longer restricted to their dungeons, but can be anywhere', action='store_true')
|
action='store_true')
|
||||||
parser.add_argument('--bigkeyshuffle', default=defval(False), help='Big Keys are no longer restricted to their dungeons, but can be anywhere', action='store_true')
|
parser.add_argument('--compassshuffle', default=defval(False),
|
||||||
|
help='Compasses are no longer restricted to their dungeons, but can be anywhere',
|
||||||
|
action='store_true')
|
||||||
|
parser.add_argument('--keyshuffle', default=defval("off"), help='\
|
||||||
|
on: Small Keys are no longer restricted to their dungeons, but can be anywhere.\
|
||||||
|
universal: Makes all Small Keys usable in any dungeon and places shops to buy more keys.',
|
||||||
|
choices=["on", "universal", "off"])
|
||||||
|
parser.add_argument('--bigkeyshuffle', default=defval(False),
|
||||||
|
help='Big Keys are no longer restricted to their dungeons, but can be anywhere',
|
||||||
|
action='store_true')
|
||||||
parser.add_argument('--keysanity', default=defval(False), help=argparse.SUPPRESS, action='store_true')
|
parser.add_argument('--keysanity', default=defval(False), help=argparse.SUPPRESS, action='store_true')
|
||||||
parser.add_argument('--retro', default=defval(False), help='''\
|
parser.add_argument('--retro', default=defval(False), help='''\
|
||||||
Keys are universal, shooting arrows costs rupees,
|
Keys are universal, shooting arrows costs rupees,
|
||||||
|
@ -326,9 +336,13 @@ def parse_arguments(argv, no_defaults=False):
|
||||||
ret.dungeon_counters = True
|
ret.dungeon_counters = True
|
||||||
elif ret.dungeon_counters == 'off':
|
elif ret.dungeon_counters == 'off':
|
||||||
ret.dungeon_counters = False
|
ret.dungeon_counters = False
|
||||||
if ret.keysanity:
|
|
||||||
ret.mapshuffle, ret.compassshuffle, ret.keyshuffle, ret.bigkeyshuffle = [True] * 4
|
|
||||||
|
|
||||||
|
if ret.keysanity:
|
||||||
|
ret.mapshuffle = ret.compassshuffle = ret.keyshuffle = ret.bigkeyshuffle = True
|
||||||
|
elif ret.keyshuffle == "on":
|
||||||
|
ret.keyshuffle = True
|
||||||
|
elif ret.keyshuffle == "off":
|
||||||
|
ret.keyshuffle = False
|
||||||
if multiargs.multi:
|
if multiargs.multi:
|
||||||
defaults = copy.deepcopy(ret)
|
defaults = copy.deepcopy(ret)
|
||||||
for player in range(1, multiargs.multi + 1):
|
for player in range(1, multiargs.multi + 1):
|
||||||
|
|
8
Fill.py
8
Fill.py
|
@ -203,7 +203,13 @@ def fill_restrictive(world, base_state: CollectionState, locations, itempool, si
|
||||||
logging.warning(
|
logging.warning(
|
||||||
f'Not all items placed. Game beatable anyway. (Could not place {item_to_place})')
|
f'Not all items placed. Game beatable anyway. (Could not place {item_to_place})')
|
||||||
continue
|
continue
|
||||||
raise FillError(f'No more spots to place {item_to_place}, locations {locations} are invalid')
|
placements = []
|
||||||
|
for region in world.regions:
|
||||||
|
for location in region.locations:
|
||||||
|
if location.item and not location.event:
|
||||||
|
placements.append(location)
|
||||||
|
raise FillError(f'No more spots to place {item_to_place}, locations {locations} are invalid. '
|
||||||
|
f'\nAlready placed {len(placements)}: {", ".join(placements)}')
|
||||||
|
|
||||||
world.push_item(spot_to_fill, item_to_place, False)
|
world.push_item(spot_to_fill, item_to_place, False)
|
||||||
locations.remove(spot_to_fill)
|
locations.remove(spot_to_fill)
|
||||||
|
|
21
Gui.py
21
Gui.py
|
@ -67,16 +67,27 @@ def guiMain(args=None):
|
||||||
openpyramidCheckbutton = Checkbutton(checkBoxFrame, text="Pre-open Pyramid Hole", variable=openpyramidVar)
|
openpyramidCheckbutton = Checkbutton(checkBoxFrame, text="Pre-open Pyramid Hole", variable=openpyramidVar)
|
||||||
mcsbshuffleFrame = Frame(checkBoxFrame)
|
mcsbshuffleFrame = Frame(checkBoxFrame)
|
||||||
mcsbLabel = Label(mcsbshuffleFrame, text="Shuffle: ")
|
mcsbLabel = Label(mcsbshuffleFrame, text="Shuffle: ")
|
||||||
|
|
||||||
mapshuffleVar = IntVar()
|
mapshuffleVar = IntVar()
|
||||||
mapshuffleCheckbutton = Checkbutton(mcsbshuffleFrame, text="Maps", variable=mapshuffleVar)
|
mapshuffleCheckbutton = Checkbutton(mcsbshuffleFrame, text="Maps", variable=mapshuffleVar)
|
||||||
|
|
||||||
compassshuffleVar = IntVar()
|
compassshuffleVar = IntVar()
|
||||||
compassshuffleCheckbutton = Checkbutton(mcsbshuffleFrame, text="Compasses", variable=compassshuffleVar)
|
compassshuffleCheckbutton = Checkbutton(mcsbshuffleFrame, text="Compasses", variable=compassshuffleVar)
|
||||||
keyshuffleVar = IntVar()
|
|
||||||
keyshuffleCheckbutton = Checkbutton(mcsbshuffleFrame, text="Keys", variable=keyshuffleVar)
|
|
||||||
bigkeyshuffleVar = IntVar()
|
bigkeyshuffleVar = IntVar()
|
||||||
bigkeyshuffleCheckbutton = Checkbutton(mcsbshuffleFrame, text="BigKeys", variable=bigkeyshuffleVar)
|
bigkeyshuffleCheckbutton = Checkbutton(mcsbshuffleFrame, text="BigKeys", variable=bigkeyshuffleVar)
|
||||||
|
|
||||||
|
keyshuffleFrame = Frame(checkBoxFrame)
|
||||||
|
keyshuffleVar = StringVar()
|
||||||
|
keyshuffleVar.set('off')
|
||||||
|
modeOptionMenu = OptionMenu(keyshuffleFrame, keyshuffleVar, 'off', 'universal', 'on')
|
||||||
|
modeOptionMenu.pack(side=LEFT)
|
||||||
|
modeLabel = Label(keyshuffleFrame, text='Key Shuffle')
|
||||||
|
modeLabel.pack(side=LEFT)
|
||||||
|
|
||||||
retroVar = IntVar()
|
retroVar = IntVar()
|
||||||
retroCheckbutton = Checkbutton(checkBoxFrame, text="Retro mode (universal keys)", variable=retroVar)
|
retroCheckbutton = Checkbutton(checkBoxFrame, text="Retro mode", variable=retroVar)
|
||||||
|
|
||||||
shuffleGanonVar = IntVar()
|
shuffleGanonVar = IntVar()
|
||||||
shuffleGanonVar.set(1) # set default
|
shuffleGanonVar.set(1) # set default
|
||||||
shuffleGanonCheckbutton = Checkbutton(checkBoxFrame, text="Include Ganon's Tower and Pyramid Hole in shuffle pool",
|
shuffleGanonCheckbutton = Checkbutton(checkBoxFrame, text="Include Ganon's Tower and Pyramid Hole in shuffle pool",
|
||||||
|
@ -99,8 +110,8 @@ def guiMain(args=None):
|
||||||
mcsbLabel.grid(row=0, column=0)
|
mcsbLabel.grid(row=0, column=0)
|
||||||
mapshuffleCheckbutton.grid(row=0, column=1)
|
mapshuffleCheckbutton.grid(row=0, column=1)
|
||||||
compassshuffleCheckbutton.grid(row=0, column=2)
|
compassshuffleCheckbutton.grid(row=0, column=2)
|
||||||
keyshuffleCheckbutton.grid(row=0, column=3)
|
|
||||||
bigkeyshuffleCheckbutton.grid(row=0, column=4)
|
bigkeyshuffleCheckbutton.grid(row=0, column=4)
|
||||||
|
keyshuffleFrame.pack(expand=True, anchor=W)
|
||||||
retroCheckbutton.pack(expand=True, anchor=W)
|
retroCheckbutton.pack(expand=True, anchor=W)
|
||||||
shuffleGanonCheckbutton.pack(expand=True, anchor=W)
|
shuffleGanonCheckbutton.pack(expand=True, anchor=W)
|
||||||
hintsCheckbutton.pack(expand=True, anchor=W)
|
hintsCheckbutton.pack(expand=True, anchor=W)
|
||||||
|
@ -476,7 +487,7 @@ def guiMain(args=None):
|
||||||
guiargs.openpyramid = bool(openpyramidVar.get())
|
guiargs.openpyramid = bool(openpyramidVar.get())
|
||||||
guiargs.mapshuffle = bool(mapshuffleVar.get())
|
guiargs.mapshuffle = bool(mapshuffleVar.get())
|
||||||
guiargs.compassshuffle = bool(compassshuffleVar.get())
|
guiargs.compassshuffle = bool(compassshuffleVar.get())
|
||||||
guiargs.keyshuffle = bool(keyshuffleVar.get())
|
guiargs.keyshuffle = {"on": True, "universal": "universal", "off": False}[keyshuffleVar.get()]
|
||||||
guiargs.bigkeyshuffle = bool(bigkeyshuffleVar.get())
|
guiargs.bigkeyshuffle = bool(bigkeyshuffleVar.get())
|
||||||
guiargs.retro = bool(retroVar.get())
|
guiargs.retro = bool(retroVar.get())
|
||||||
guiargs.quickswap = bool(quickSwapVar.get())
|
guiargs.quickswap = bool(quickSwapVar.get())
|
||||||
|
|
40
ItemPool.py
40
ItemPool.py
|
@ -43,7 +43,7 @@ Difficulty = namedtuple('Difficulty',
|
||||||
['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield',
|
['baseitems', 'bottles', 'bottle_count', 'same_bottle', 'progressiveshield',
|
||||||
'basicshield', 'progressivearmor', 'basicarmor', 'swordless', 'progressivemagic', 'basicmagic',
|
'basicshield', 'progressivearmor', 'basicarmor', 'swordless', 'progressivemagic', 'basicmagic',
|
||||||
'progressivesword', 'basicsword', 'progressivebow', 'basicbow', 'timedohko', 'timedother',
|
'progressivesword', 'basicsword', 'progressivebow', 'basicbow', 'timedohko', 'timedother',
|
||||||
'triforcehunt', 'retro',
|
'triforcehunt', 'universal_keys',
|
||||||
'extras', 'progressive_sword_limit', 'progressive_shield_limit',
|
'extras', 'progressive_sword_limit', 'progressive_shield_limit',
|
||||||
'progressive_armor_limit', 'progressive_bottle_limit',
|
'progressive_armor_limit', 'progressive_bottle_limit',
|
||||||
'progressive_bow_limit', 'heart_piece_limit', 'boss_heart_container_limit'])
|
'progressive_bow_limit', 'heart_piece_limit', 'boss_heart_container_limit'])
|
||||||
|
@ -70,7 +70,7 @@ difficulties = {
|
||||||
timedohko=['Green Clock'] * 25,
|
timedohko=['Green Clock'] * 25,
|
||||||
timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
|
timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
|
||||||
triforcehunt=['Triforce Piece'] * 30,
|
triforcehunt=['Triforce Piece'] * 30,
|
||||||
retro=['Small Key (Universal)'] * 28,
|
universal_keys=['Small Key (Universal)'] * 28,
|
||||||
extras=[easyfirst15extra, easysecond15extra, easythird10extra, easyfourth5extra, easyfinal25extra],
|
extras=[easyfirst15extra, easysecond15extra, easythird10extra, easyfourth5extra, easyfinal25extra],
|
||||||
progressive_sword_limit=8,
|
progressive_sword_limit=8,
|
||||||
progressive_shield_limit=6,
|
progressive_shield_limit=6,
|
||||||
|
@ -99,15 +99,15 @@ difficulties = {
|
||||||
timedohko=['Green Clock'] * 25,
|
timedohko=['Green Clock'] * 25,
|
||||||
timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
|
timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
|
||||||
triforcehunt=['Triforce Piece'] * 30,
|
triforcehunt=['Triforce Piece'] * 30,
|
||||||
retro=['Small Key (Universal)'] * 18 + ['Rupees (20)'] * 10,
|
universal_keys=['Small Key (Universal)'] * 18 + ['Rupees (20)'] * 10,
|
||||||
extras=[normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
|
extras=[normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
|
||||||
progressive_sword_limit=4,
|
progressive_sword_limit=4,
|
||||||
progressive_shield_limit=3,
|
progressive_shield_limit=3,
|
||||||
progressive_armor_limit=2,
|
progressive_armor_limit=2,
|
||||||
progressive_bow_limit=2,
|
progressive_bow_limit=2,
|
||||||
progressive_bottle_limit = 4,
|
progressive_bottle_limit=4,
|
||||||
boss_heart_container_limit = 10,
|
boss_heart_container_limit=10,
|
||||||
heart_piece_limit = 24,
|
heart_piece_limit=24,
|
||||||
),
|
),
|
||||||
'hard': Difficulty(
|
'hard': Difficulty(
|
||||||
baseitems = normalbaseitems,
|
baseitems = normalbaseitems,
|
||||||
|
@ -128,7 +128,7 @@ difficulties = {
|
||||||
timedohko=['Green Clock'] * 25,
|
timedohko=['Green Clock'] * 25,
|
||||||
timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
|
timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
|
||||||
triforcehunt=['Triforce Piece'] * 30,
|
triforcehunt=['Triforce Piece'] * 30,
|
||||||
retro=['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 16,
|
universal_keys=['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 16,
|
||||||
extras=[normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
|
extras=[normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
|
||||||
progressive_sword_limit=3,
|
progressive_sword_limit=3,
|
||||||
progressive_shield_limit=2,
|
progressive_shield_limit=2,
|
||||||
|
@ -158,7 +158,7 @@ difficulties = {
|
||||||
timedohko=['Green Clock'] * 20 + ['Red Clock'] * 5,
|
timedohko=['Green Clock'] * 20 + ['Red Clock'] * 5,
|
||||||
timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
|
timedother=['Green Clock'] * 20 + ['Blue Clock'] * 10 + ['Red Clock'] * 10,
|
||||||
triforcehunt=['Triforce Piece'] * 30,
|
triforcehunt=['Triforce Piece'] * 30,
|
||||||
retro=['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 16,
|
universal_keys=['Small Key (Universal)'] * 12 + ['Rupees (5)'] * 16,
|
||||||
extras=[normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
|
extras=[normalfirst15extra, normalsecond15extra, normalthird10extra, normalfourth5extra, normalfinal25extra],
|
||||||
progressive_sword_limit=2,
|
progressive_sword_limit=2,
|
||||||
progressive_shield_limit=1,
|
progressive_shield_limit=1,
|
||||||
|
@ -425,21 +425,26 @@ def fill_prizes(world, attempts=15):
|
||||||
raise FillError('Unable to place dungeon prizes')
|
raise FillError('Unable to place dungeon prizes')
|
||||||
|
|
||||||
|
|
||||||
def set_up_shops(world, player):
|
def set_up_shops(world, player: int):
|
||||||
# TODO: move hard+ mode changes for sheilds here, utilizing the new shops
|
# TODO: move hard+ mode changes for shields here, utilizing the new shops
|
||||||
|
|
||||||
if world.retro[player]:
|
if world.retro[player]:
|
||||||
rss = world.get_region('Red Shield Shop', player).shop
|
rss = world.get_region('Red Shield Shop', player).shop
|
||||||
if not rss.locked:
|
if not rss.locked:
|
||||||
rss.add_inventory(2, 'Single Arrow', 80)
|
rss.add_inventory(2, 'Single Arrow', 80)
|
||||||
|
rss.locked = True
|
||||||
|
|
||||||
|
if world.keyshuffle[player] == "universal":
|
||||||
for shop in world.random.sample([s for s in world.shops if
|
for shop in world.random.sample([s for s in world.shops if
|
||||||
s.custom and not s.locked and s.type == ShopType.Shop and s.region.player == player],
|
s.custom and not s.locked and s.type == ShopType.Shop and s.region.player == player],
|
||||||
5):
|
5):
|
||||||
shop.locked = True
|
shop.locked = True
|
||||||
shop.add_inventory(0, 'Single Arrow', 80)
|
if world.retro[player]:
|
||||||
|
shop.add_inventory(0, 'Single Arrow', 80)
|
||||||
|
else:
|
||||||
|
shop.add_inventory(0, "Red Potion", 150)
|
||||||
shop.add_inventory(1, 'Small Key (Universal)', 100)
|
shop.add_inventory(1, 'Small Key (Universal)', 100)
|
||||||
shop.add_inventory(2, 'Bombs (10)', 50)
|
shop.add_inventory(2, 'Bombs (10)', 50)
|
||||||
rss.locked = True
|
|
||||||
|
|
||||||
|
|
||||||
def get_pool_core(world, player: int):
|
def get_pool_core(world, player: int):
|
||||||
|
@ -592,7 +597,8 @@ def get_pool_core(world, player: int):
|
||||||
pool = [item.replace('Arrows (10)', 'Rupees (5)') for item in pool]
|
pool = [item.replace('Arrows (10)', 'Rupees (5)') for item in pool]
|
||||||
pool = [item.replace('Arrow Upgrade (+5)', 'Rupees (5)') for item in pool]
|
pool = [item.replace('Arrow Upgrade (+5)', 'Rupees (5)') for item in pool]
|
||||||
pool = [item.replace('Arrow Upgrade (+10)', 'Rupees (5)') for item in pool]
|
pool = [item.replace('Arrow Upgrade (+10)', 'Rupees (5)') for item in pool]
|
||||||
pool.extend(diff.retro)
|
if world.keyshuffle[player] == "universal":
|
||||||
|
pool.extend(diff.universal_keys)
|
||||||
if mode == 'standard':
|
if mode == 'standard':
|
||||||
key_location = world.random.choice(
|
key_location = world.random.choice(
|
||||||
['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest',
|
['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest',
|
||||||
|
@ -609,7 +615,6 @@ def make_custom_item_pool(world, player):
|
||||||
timer = world.timer[player]
|
timer = world.timer[player]
|
||||||
goal = world.goal[player]
|
goal = world.goal[player]
|
||||||
mode = world.mode[player]
|
mode = world.mode[player]
|
||||||
retro = world.retro[player]
|
|
||||||
customitemarray = world.customitemarray[player]
|
customitemarray = world.customitemarray[player]
|
||||||
|
|
||||||
pool = []
|
pool = []
|
||||||
|
@ -726,7 +731,7 @@ def make_custom_item_pool(world, player):
|
||||||
itemtotal = itemtotal + 1
|
itemtotal = itemtotal + 1
|
||||||
|
|
||||||
if mode == 'standard':
|
if mode == 'standard':
|
||||||
if retro:
|
if world.keyshuffle == "universal":
|
||||||
key_location = world.random.choice(
|
key_location = world.random.choice(
|
||||||
['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest',
|
['Secret Passage', 'Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest',
|
||||||
'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross'])
|
'Hyrule Castle - Zelda\'s Chest', 'Sewers - Dark Cross'])
|
||||||
|
@ -749,9 +754,10 @@ def make_custom_item_pool(world, player):
|
||||||
pool.extend(['Magic Mirror'] * customitemarray[22])
|
pool.extend(['Magic Mirror'] * customitemarray[22])
|
||||||
pool.extend(['Moon Pearl'] * customitemarray[28])
|
pool.extend(['Moon Pearl'] * customitemarray[28])
|
||||||
|
|
||||||
if retro:
|
if world.keyshuffle == "universal":
|
||||||
itemtotal = itemtotal - 28 # Corrects for small keys not being in item pool in Retro Mode
|
itemtotal = itemtotal - 28 # Corrects for small keys not being in item pool in Retro Mode
|
||||||
if itemtotal < total_items_to_place:
|
if itemtotal < total_items_to_place:
|
||||||
pool.extend(['Nothing'] * (total_items_to_place - itemtotal))
|
pool.extend(['Nothing'] * (total_items_to_place - itemtotal))
|
||||||
|
logging.warning(f"Pool was filled up with {total_items_to_place - itemtotal} Nothing's for player {player}")
|
||||||
|
|
||||||
return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon)
|
return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon)
|
||||||
|
|
|
@ -278,7 +278,8 @@ def roll_settings(weights):
|
||||||
|
|
||||||
ret.mapshuffle = get_choice('map_shuffle', weights, 'm' in dungeon_items)
|
ret.mapshuffle = get_choice('map_shuffle', weights, 'm' in dungeon_items)
|
||||||
ret.compassshuffle = get_choice('compass_shuffle', weights, 'c' in dungeon_items)
|
ret.compassshuffle = get_choice('compass_shuffle', weights, 'c' in dungeon_items)
|
||||||
ret.keyshuffle = get_choice('smallkey_shuffle', weights, 's' in dungeon_items)
|
ret.keyshuffle = get_choice('smallkey_shuffle', weights,
|
||||||
|
'universal' if 'u' in dungeon_items else 's' in dungeon_items)
|
||||||
ret.bigkeyshuffle = get_choice('bigkey_shuffle', weights, 'b' in dungeon_items)
|
ret.bigkeyshuffle = get_choice('bigkey_shuffle', weights, 'b' in dungeon_items)
|
||||||
|
|
||||||
ret.accessibility = get_choice('accessibility', weights)
|
ret.accessibility = get_choice('accessibility', weights)
|
||||||
|
@ -309,10 +310,13 @@ def roll_settings(weights):
|
||||||
ret.triforce_pieces_required = get_choice('triforce_pieces_required', weights, 20)
|
ret.triforce_pieces_required = get_choice('triforce_pieces_required', weights, 20)
|
||||||
ret.triforce_pieces_required = min(max(1, int(ret.triforce_pieces_required)), 90)
|
ret.triforce_pieces_required = min(max(1, int(ret.triforce_pieces_required)), 90)
|
||||||
|
|
||||||
ret.mode = get_choice('world_state', weights)
|
ret.mode = get_choice('world_state', weights, None) # legacy support
|
||||||
if ret.mode == 'retro':
|
if ret.mode == 'retro':
|
||||||
ret.mode = 'open'
|
ret.mode = 'open'
|
||||||
ret.retro = True
|
ret.retro = True
|
||||||
|
elif ret.mode is None:
|
||||||
|
ret.mode = get_choice("mode", weights)
|
||||||
|
ret.retro = get_choice("retro", weights)
|
||||||
|
|
||||||
ret.hints = get_choice('hints', weights)
|
ret.hints = get_choice('hints', weights)
|
||||||
|
|
||||||
|
|
2
Rom.py
2
Rom.py
|
@ -1237,7 +1237,7 @@ def patch_rom(world, rom, player, team, enemized):
|
||||||
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
|
||||||
write_int16(rom, 0x18017C, get_reveal_bytes('Crystal 5')|get_reveal_bytes('Crystal 6') if world.mapshuffle[player] else 0x0000) # Bomb Shop Reveal
|
write_int16(rom, 0x18017C, get_reveal_bytes('Crystal 5')|get_reveal_bytes('Crystal 6') if world.mapshuffle[player] else 0x0000) # Bomb Shop Reveal
|
||||||
|
|
||||||
rom.write_byte(0x180172, 0x01 if world.retro[player] else 0x00) # universal keys
|
rom.write_byte(0x180172, int(world.keyshuffle == "universal")) # universal keys
|
||||||
rom.write_byte(0x180175, 0x01 if world.retro[player] else 0x00) # rupee bow
|
rom.write_byte(0x180175, 0x01 if world.retro[player] else 0x00) # rupee bow
|
||||||
rom.write_byte(0x180176, 0x0A if world.retro[player] else 0x00) # wood arrow cost
|
rom.write_byte(0x180176, 0x0A if world.retro[player] else 0x00) # wood arrow cost
|
||||||
rom.write_byte(0x180178, 0x32 if world.retro[player] else 0x00) # silver arrow cost
|
rom.write_byte(0x180178, 0x32 if world.retro[player] else 0x00) # silver arrow cost
|
||||||
|
|
10
Rules.py
10
Rules.py
|
@ -206,8 +206,9 @@ def global_rules(world, player):
|
||||||
set_rule(world.get_location('Hookshot Cave - Bottom Left', player), lambda state: state.has('Hookshot', player))
|
set_rule(world.get_location('Hookshot Cave - Bottom Left', player), lambda state: state.has('Hookshot', player))
|
||||||
|
|
||||||
set_rule(world.get_entrance('Sewers Door', player),
|
set_rule(world.get_entrance('Sewers Door', player),
|
||||||
lambda state: state.has_key('Small Key (Hyrule Castle)', player) or (world.retro[player] and world.mode[
|
lambda state: state.has_key('Small Key (Hyrule Castle)', player) or (
|
||||||
player] == 'standard')) # standard retro cannot access the shop
|
world.keyshuffle[player] == "universal" and world.mode[
|
||||||
|
player] == 'standard')) # standard universal small keys cannot access the shop
|
||||||
set_rule(world.get_entrance('Sewers Back Door', player),
|
set_rule(world.get_entrance('Sewers Back Door', player),
|
||||||
lambda state: state.has_key('Small Key (Hyrule Castle)', player))
|
lambda state: state.has_key('Small Key (Hyrule Castle)', player))
|
||||||
set_rule(world.get_entrance('Agahnim 1', player),
|
set_rule(world.get_entrance('Agahnim 1', player),
|
||||||
|
@ -896,7 +897,7 @@ def set_trock_key_rules(world, player):
|
||||||
return 4
|
return 4
|
||||||
|
|
||||||
# If TR is only accessible from the middle, the big key must be further restricted to prevent softlock potential
|
# If TR is only accessible from the middle, the big key must be further restricted to prevent softlock potential
|
||||||
if not can_reach_front and not world.keyshuffle[player] and not world.retro[player]:
|
if not can_reach_front and not world.keyshuffle[player]:
|
||||||
# Must not go in the Big Key Chest - only 1 other chest available and 2+ keys required for all other chests
|
# Must not go in the Big Key Chest - only 1 other chest available and 2+ keys required for all other chests
|
||||||
forbid_item(world.get_location('Turtle Rock - Big Key Chest', player), 'Big Key (Turtle Rock)', player)
|
forbid_item(world.get_location('Turtle Rock - Big Key Chest', player), 'Big Key (Turtle Rock)', player)
|
||||||
if not can_reach_big_chest:
|
if not can_reach_big_chest:
|
||||||
|
@ -905,7 +906,8 @@ def set_trock_key_rules(world, player):
|
||||||
if world.accessibility[player] == 'locations':
|
if world.accessibility[player] == 'locations':
|
||||||
if world.bigkeyshuffle[player] and can_reach_big_chest:
|
if world.bigkeyshuffle[player] and can_reach_big_chest:
|
||||||
# Must not go in the dungeon - all 3 available chests (Chomps, Big Chest, Crystaroller) must be keys to access laser bridge, and the big key is required first
|
# Must not go in the dungeon - all 3 available chests (Chomps, Big Chest, Crystaroller) must be keys to access laser bridge, and the big key is required first
|
||||||
for location in ['Turtle Rock - Chain Chomps', 'Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left', 'Turtle Rock - Roller Room - Right']:
|
for location in ['Turtle Rock - Chain Chomps', 'Turtle Rock - Compass Chest',
|
||||||
|
'Turtle Rock - Roller Room - Left', 'Turtle Rock - Roller Room - Right']:
|
||||||
forbid_item(world.get_location(location, player), 'Big Key (Turtle Rock)', player)
|
forbid_item(world.get_location(location, player), 'Big Key (Turtle Rock)', player)
|
||||||
else:
|
else:
|
||||||
# A key is required in the Big Key Chest to prevent a possible softlock. Place an extra key to ensure 100% locations still works
|
# A key is required in the Big Key Chest to prevent a possible softlock. Place an extra key to ensure 100% locations still works
|
||||||
|
|
|
@ -38,6 +38,7 @@ compass_shuffle: # Shuffle compasses into the world and other dungeons, includin
|
||||||
off: 1
|
off: 1
|
||||||
smallkey_shuffle: # Shuffle small keys into the world and other dungeons, including other players' worlds
|
smallkey_shuffle: # Shuffle small keys into the world and other dungeons, including other players' worlds
|
||||||
on: 0
|
on: 0
|
||||||
|
universal: 0 # allows small keys to be used in any dungeon and adds shops to buy more
|
||||||
off: 1
|
off: 1
|
||||||
bigkey_shuffle: # Shuffle big keys into the world and other dungeons, including other players' worlds
|
bigkey_shuffle: # Shuffle big keys into the world and other dungeons, including other players' worlds
|
||||||
on: 0
|
on: 0
|
||||||
|
@ -50,6 +51,8 @@ dungeon_items: # Alternative to the 4 shuffles and local_keys above this, does n
|
||||||
none: 1 # Shuffle none of the 4
|
none: 1 # Shuffle none of the 4
|
||||||
mcsb: 0 # Shuffle all of the 4, any combination of m, c, s and b will shuffle the respective item, or not if it's missing, so you can add more options here
|
mcsb: 0 # Shuffle all of the 4, any combination of m, c, s and b will shuffle the respective item, or not if it's missing, so you can add more options here
|
||||||
lmcsb: 0 # Like mcsb above, but with keys kept local to your world. l is what makes your keys local, or not if it's missing
|
lmcsb: 0 # Like mcsb above, but with keys kept local to your world. l is what makes your keys local, or not if it's missing
|
||||||
|
ub: 0 # universal small keys and shuffled big keys
|
||||||
|
# you can add more combos of these letters here
|
||||||
dungeon_counters:
|
dungeon_counters:
|
||||||
on: 0 # Always display amount of items checked in a dungeon
|
on: 0 # Always display amount of items checked in a dungeon
|
||||||
pickup: 1 # Show when compass is picked up
|
pickup: 1 # Show when compass is picked up
|
||||||
|
@ -119,11 +122,13 @@ ganon_open: # Crystals required to hurt Ganon
|
||||||
'6': 2
|
'6': 2
|
||||||
'7': 1
|
'7': 1
|
||||||
random: 0
|
random: 0
|
||||||
world_state:
|
mode:
|
||||||
standard: 1 # Begin the game by rescuing Zelda from her cell and escorting her to the Sanctuary
|
standard: 1 # Begin the game by rescuing Zelda from her cell and escorting her to the Sanctuary
|
||||||
open: 1 # Begin the game from your choice of Link's House or the Sanctuary
|
open: 1 # Begin the game from your choice of Link's House or the Sanctuary
|
||||||
inverted: 0 # Begin in the Dark World. The Moon Pearl is required to avoid bunny-state in Light World, and the Light World game map is altered
|
inverted: 0 # Begin in the Dark World. The Moon Pearl is required to avoid bunny-state in Light World, and the Light World game map is altered
|
||||||
retro: 0 # Small keys are universal, you must buy a quiver, take-any caves and an old-man cave are added to the world. You may need to find your sword from the old man's cave
|
retro:
|
||||||
|
on: 0 # you must buy a quiver to use the bow, take-any caves and an old-man cave are added to the world. You may need to find your sword from the old man's cave
|
||||||
|
off: 1
|
||||||
hints:
|
hints:
|
||||||
'on': 1 # Hint tiles sometimes give item location hints
|
'on': 1 # Hint tiles sometimes give item location hints
|
||||||
'off': 0 # Hint tiles provide gameplay tips
|
'off': 0 # Hint tiles provide gameplay tips
|
||||||
|
|
Loading…
Reference in New Issue