Individual settings: map/compass/key/bk shuffle
This commit is contained in:
parent
dc26dfce77
commit
1315eb55cf
|
@ -63,10 +63,10 @@ class World(object):
|
|||
self.quickswap = quickswap
|
||||
self.fastmenu = fastmenu
|
||||
self.disable_music = disable_music
|
||||
self.mapshuffle = False
|
||||
self.compassshuffle = False
|
||||
self.keyshuffle = False
|
||||
self.bigkeyshuffle = False
|
||||
self.mapshuffle = {player: False for player in range(1, players + 1)}
|
||||
self.compassshuffle = {player: False for player in range(1, players + 1)}
|
||||
self.keyshuffle = {player: False for player in range(1, players + 1)}
|
||||
self.bigkeyshuffle = {player: False for player in range(1, players + 1)}
|
||||
self.retro = retro
|
||||
self.custom = custom
|
||||
self.customitemarray = customitemarray
|
||||
|
@ -364,7 +364,7 @@ class CollectionState(object):
|
|||
checked_locations = 0
|
||||
while new_locations:
|
||||
reachable_events = [location for location in locations if location.event and
|
||||
(not key_only or (not self.world.keyshuffle and location.item.smallkey) or (not self.world.bigkeyshuffle and location.item.bigkey))
|
||||
(not key_only or (not self.world.keyshuffle[location.item.player] and location.item.smallkey) or (not self.world.bigkeyshuffle[location.item.player] and location.item.bigkey))
|
||||
and location.can_reach(self)]
|
||||
for event in reachable_events:
|
||||
if (event.name, event.player) not in self.events:
|
||||
|
@ -685,10 +685,10 @@ class Region(object):
|
|||
return False
|
||||
|
||||
def can_fill(self, item):
|
||||
inside_dungeon_item = ((item.smallkey and not self.world.keyshuffle)
|
||||
or (item.bigkey and not self.world.bigkeyshuffle)
|
||||
or (item.map and not self.world.mapshuffle)
|
||||
or (item.compass and not self.world.compassshuffle))
|
||||
inside_dungeon_item = ((item.smallkey and not self.world.keyshuffle[item.player])
|
||||
or (item.bigkey and not self.world.bigkeyshuffle[item.player])
|
||||
or (item.map and not self.world.mapshuffle[item.player])
|
||||
or (item.compass and not self.world.compassshuffle[item.player]))
|
||||
sewer_hack = self.world.mode[item.player] == 'standard' and item.name == 'Small Key (Escape)'
|
||||
if sewer_hack or inside_dungeon_item:
|
||||
return self.dungeon and self.dungeon.is_dungeon_item(item) and item.player == self.player
|
||||
|
@ -1095,10 +1095,10 @@ class Spoiler(object):
|
|||
outfile.write('Accessibility: %s\n' % self.metadata['accessibility'])
|
||||
outfile.write('L\\R Quickswap enabled: %s\n' % ('Yes' if self.world.quickswap else 'No'))
|
||||
outfile.write('Menu speed: %s\n' % self.world.fastmenu)
|
||||
outfile.write('Map shuffle: %s\n' % ('Yes' if self.metadata['mapshuffle'] else 'No'))
|
||||
outfile.write('Compass shuffle: %s\n' % ('Yes' if self.metadata['compassshuffle'] else 'No'))
|
||||
outfile.write('Small Key shuffle: %s\n' % ('Yes' if self.metadata['keyshuffle'] else 'No'))
|
||||
outfile.write('Big Key shuffle: %s\n' % ('Yes' if self.metadata['bigkeyshuffle'] else 'No'))
|
||||
outfile.write('Map shuffle: %s\n' % {k: 'Yes' if v else 'No' for k, v in self.metadata['mapshuffle'].items()})
|
||||
outfile.write('Compass shuffle: %s\n' % {k: 'Yes' if v else 'No' for k, v in self.metadata['compassshuffle'].items()})
|
||||
outfile.write('Small Key shuffle: %s\n' % {k: 'Yes' if v else 'No' for k, v in self.metadata['keyshuffle'].items()})
|
||||
outfile.write('Big Key shuffle: %s\n' % {k: 'Yes' if v else 'No' for k, v in self.metadata['bigkeyshuffle'].items()})
|
||||
outfile.write('Players: %d' % self.world.players)
|
||||
if self.entrances:
|
||||
outfile.write('\n\nEntrances:\n\n')
|
||||
|
|
12
Dungeons.py
12
Dungeons.py
|
@ -136,16 +136,16 @@ def fill_dungeons_restrictive(world, shuffled_locations):
|
|||
|
||||
# with shuffled dungeon items they are distributed as part of the normal item pool
|
||||
for item in world.get_items():
|
||||
if (item.smallkey and world.keyshuffle) or (item.bigkey and world.bigkeyshuffle):
|
||||
if (item.smallkey and world.keyshuffle[item.player]) or (item.bigkey and world.bigkeyshuffle[item.player]):
|
||||
all_state_base.collect(item, True)
|
||||
item.advancement = True
|
||||
elif (item.map and world.mapshuffle) or (item.compass and world.compassshuffle):
|
||||
elif (item.map and world.mapshuffle[item.player]) or (item.compass and world.compassshuffle[item.player]):
|
||||
item.priority = True
|
||||
|
||||
dungeon_items = [item for item in get_dungeon_item_pool(world) if ((item.smallkey and not world.keyshuffle)
|
||||
or (item.bigkey and not world.bigkeyshuffle)
|
||||
or (item.map and not world.mapshuffle)
|
||||
or (item.compass and not world.compassshuffle))]
|
||||
dungeon_items = [item for item in get_dungeon_item_pool(world) if ((item.smallkey and not world.keyshuffle[item.player])
|
||||
or (item.bigkey and not world.bigkeyshuffle[item.player])
|
||||
or (item.map and not world.mapshuffle[item.player])
|
||||
or (item.compass and not world.compassshuffle[item.player]))]
|
||||
|
||||
# sort in the order Big Key, Small Key, Other before placing dungeon items
|
||||
sort_order = {"BigKey": 3, "SmallKey": 2}
|
||||
|
|
|
@ -278,7 +278,9 @@ def parse_arguments(argv, no_defaults=False):
|
|||
for player in range(1, multiargs.multi + 1):
|
||||
playerargs = parse_arguments(shlex.split(getattr(ret,f"p{player}")), True)
|
||||
|
||||
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality', 'shuffle', 'crystals_ganon', 'crystals_gt', 'openpyramid']:
|
||||
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
|
||||
'shuffle', 'crystals_ganon', 'crystals_gt', 'openpyramid',
|
||||
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle']:
|
||||
value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name)
|
||||
if player == 1:
|
||||
setattr(ret, name, {1: value})
|
||||
|
|
7
Fill.py
7
Fill.py
|
@ -243,8 +243,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None
|
|||
fill_locations.reverse()
|
||||
|
||||
# Make sure the escape small key is placed first in standard with key shuffle to prevent running out of spots
|
||||
if world.keyshuffle:
|
||||
progitempool.sort(key=lambda item: 1 if item.name == 'Small Key (Escape)' and world.mode[item.player] == 'standard' else 0)
|
||||
progitempool.sort(key=lambda item: 1 if item.name == 'Small Key (Escape)' and world.mode[item.player] == 'standard' and world.keyshuffle[item.player] else 0)
|
||||
|
||||
fill_restrictive(world, world.state, fill_locations, progitempool)
|
||||
|
||||
|
@ -355,7 +354,7 @@ def balance_multiworld_progression(world):
|
|||
candidate_items = []
|
||||
while True:
|
||||
for location in balancing_sphere:
|
||||
if location.event and (world.keyshuffle or not location.item.smallkey) and (world.bigkeyshuffle or not location.item.bigkey):
|
||||
if location.event and (world.keyshuffle[location.item.player] or not location.item.smallkey) and (world.bigkeyshuffle[location.item.player] or not location.item.bigkey):
|
||||
balancing_state.collect(location.item, True, location)
|
||||
if location.item.player in balancing_players and not location.locked:
|
||||
candidate_items.append(location)
|
||||
|
@ -411,7 +410,7 @@ def balance_multiworld_progression(world):
|
|||
sphere_locations.append(location)
|
||||
|
||||
for location in sphere_locations:
|
||||
if location.event and (world.keyshuffle or not location.item.smallkey) and (world.bigkeyshuffle or not location.item.bigkey):
|
||||
if location.event and (world.keyshuffle[location.item.player] or not location.item.smallkey) and (world.bigkeyshuffle[location.item.player] or not location.item.bigkey):
|
||||
state.collect(location.item, True, location)
|
||||
checked_locations.extend(sphere_locations)
|
||||
|
||||
|
|
|
@ -219,10 +219,10 @@ def generate_itempool(world, player):
|
|||
world.treasure_hunt_icon = treasure_hunt_icon
|
||||
|
||||
world.itempool.extend([item for item in get_dungeon_item_pool(world) if item.player == player
|
||||
and ((item.smallkey and world.keyshuffle)
|
||||
or (item.bigkey and world.bigkeyshuffle)
|
||||
or (item.map and world.mapshuffle)
|
||||
or (item.compass and world.compassshuffle))])
|
||||
and ((item.smallkey and world.keyshuffle[player])
|
||||
or (item.bigkey and world.bigkeyshuffle[player])
|
||||
or (item.map and world.mapshuffle[player])
|
||||
or (item.compass and world.compassshuffle[player]))])
|
||||
|
||||
# logic has some branches where having 4 hearts is one possible requirement (of several alternatives)
|
||||
# rather than making all hearts/heart pieces progression items (which slows down generation considerably)
|
||||
|
|
39
Main.py
39
Main.py
|
@ -34,19 +34,10 @@ def main(args, seed=None):
|
|||
world.seed = int(seed)
|
||||
random.seed(world.seed)
|
||||
|
||||
world.mapshuffle = args.mapshuffle
|
||||
world.compassshuffle = args.compassshuffle
|
||||
world.keyshuffle = args.keyshuffle
|
||||
world.bigkeyshuffle = args.bigkeyshuffle
|
||||
|
||||
mcsb_name = ''
|
||||
if all([world.mapshuffle, world.compassshuffle, world.keyshuffle, world.bigkeyshuffle]):
|
||||
mcsb_name = '-keysanity'
|
||||
elif [world.mapshuffle, world.compassshuffle, world.keyshuffle, world.bigkeyshuffle].count(True) == 1:
|
||||
mcsb_name = '-mapshuffle' if world.mapshuffle else '-compassshuffle' if world.compassshuffle else '-keyshuffle' if world.keyshuffle else '-bigkeyshuffle'
|
||||
elif any([world.mapshuffle, world.compassshuffle, world.keyshuffle, world.bigkeyshuffle]):
|
||||
mcsb_name = '-%s%s%s%sshuffle' % ('M' if world.mapshuffle else '', 'C' if world.compassshuffle else '', 'S' if world.keyshuffle else '', 'B' if world.bigkeyshuffle else '')
|
||||
|
||||
world.mapshuffle = args.mapshuffle.copy()
|
||||
world.compassshuffle = args.compassshuffle.copy()
|
||||
world.keyshuffle = args.keyshuffle.copy()
|
||||
world.bigkeyshuffle = args.bigkeyshuffle.copy()
|
||||
world.crystals_needed_for_ganon = {player: random.randint(0, 7) if args.crystals_ganon[player] == 'random' else int(args.crystals_ganon[player]) for player in range(1, world.players + 1)}
|
||||
world.crystals_needed_for_gt = {player: random.randint(0, 7) if args.crystals_gt[player] == 'random' else int(args.crystals_gt[player]) for player in range(1, world.players + 1)}
|
||||
world.open_pyramid = args.openpyramid.copy()
|
||||
|
@ -94,7 +85,8 @@ def main(args, seed=None):
|
|||
logger.info('Placing Dungeon Items.')
|
||||
|
||||
shuffled_locations = None
|
||||
if args.algorithm in ['balanced', 'vt26'] or args.mapshuffle or args.compassshuffle or args.keyshuffle or args.bigkeyshuffle:
|
||||
if args.algorithm in ['balanced', 'vt26'] or any(list(args.mapshuffle.values()) + list(args.compassshuffle.values()) +
|
||||
list(args.keyshuffle.values()) + list(args.bigkeyshuffle.values())):
|
||||
shuffled_locations = world.get_unfilled_locations()
|
||||
random.shuffle(shuffled_locations)
|
||||
fill_dungeons_restrictive(world, shuffled_locations)
|
||||
|
@ -182,6 +174,17 @@ def main(args, seed=None):
|
|||
rom.write_bytes(int(addr), values)
|
||||
|
||||
apply_rom_settings(rom, args.heartbeep, args.heartcolor, world.quickswap, world.fastmenu, world.disable_music, sprite, player_names)
|
||||
|
||||
mcsb_name = ''
|
||||
if all([world.mapshuffle[player], world.compassshuffle[player], world.keyshuffle[player], world.bigkeyshuffle[player]]):
|
||||
mcsb_name = '-keysanity'
|
||||
elif [world.mapshuffle[player], world.compassshuffle[player], world.keyshuffle[player], world.bigkeyshuffle[player]].count(True) == 1:
|
||||
mcsb_name = '-mapshuffle' if world.mapshuffle[player] else '-compassshuffle' if world.compassshuffle[player] else '-keyshuffle' if world.keyshuffle[player] else '-bigkeyshuffle'
|
||||
elif any([world.mapshuffle[player], world.compassshuffle[player], world.keyshuffle[player], world.bigkeyshuffle[player]]):
|
||||
mcsb_name = '-%s%s%s%sshuffle' % (
|
||||
'M' if world.mapshuffle[player] else '', 'C' if world.compassshuffle[player] else '',
|
||||
'S' if world.keyshuffle[player] else '', 'B' if world.bigkeyshuffle[player] else '')
|
||||
|
||||
outfilesuffix = ('%s%s_%s_%s-%s-%s-%s%s_%s-%s%s%s%s%s' % (f'_P{player}' if world.players > 1 else '',
|
||||
f'_{player_names[player]}' if player in player_names else '',
|
||||
world.logic[player], world.difficulty[player], world.difficulty_adjustments[player],
|
||||
|
@ -235,10 +238,10 @@ def copy_world(world):
|
|||
ret.difficulty_requirements = world.difficulty_requirements.copy()
|
||||
ret.fix_fake_world = world.fix_fake_world
|
||||
ret.lamps_needed_for_dark_rooms = world.lamps_needed_for_dark_rooms
|
||||
ret.mapshuffle = world.mapshuffle
|
||||
ret.compassshuffle = world.compassshuffle
|
||||
ret.keyshuffle = world.keyshuffle
|
||||
ret.bigkeyshuffle = world.bigkeyshuffle
|
||||
ret.mapshuffle = world.mapshuffle.copy()
|
||||
ret.compassshuffle = world.compassshuffle.copy()
|
||||
ret.keyshuffle = world.keyshuffle.copy()
|
||||
ret.bigkeyshuffle = world.bigkeyshuffle.copy()
|
||||
ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy()
|
||||
ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy()
|
||||
ret.open_pyramid = world.open_pyramid.copy()
|
||||
|
|
34
Rom.py
34
Rom.py
|
@ -490,14 +490,14 @@ def patch_rom(world, player, rom, enemized):
|
|||
|
||||
# patch music
|
||||
music_addresses = dungeon_music_addresses[location.name]
|
||||
if world.mapshuffle:
|
||||
if world.mapshuffle[player]:
|
||||
music = random.choice([0x11, 0x16])
|
||||
else:
|
||||
music = 0x11 if 'Pendant' in location.item.name else 0x16
|
||||
for music_address in music_addresses:
|
||||
rom.write_byte(music_address, music)
|
||||
|
||||
if world.mapshuffle:
|
||||
if world.mapshuffle[player]:
|
||||
rom.write_byte(0x155C9, random.choice([0x11, 0x16])) # Randomize GT music too with map shuffle
|
||||
|
||||
# patch entrance/exits/holes
|
||||
|
@ -839,7 +839,7 @@ def patch_rom(world, player, rom, enemized):
|
|||
ERtimeincrease = 10
|
||||
else:
|
||||
ERtimeincrease = 20
|
||||
if world.keyshuffle or world.bigkeyshuffle or world.mapshuffle:
|
||||
if world.keyshuffle[player] or world.bigkeyshuffle[player] or world.mapshuffle[player]:
|
||||
ERtimeincrease = ERtimeincrease + 15
|
||||
if world.clock_mode == 'off':
|
||||
rom.write_bytes(0x180190, [0x00, 0x00, 0x00]) # turn off clock mode
|
||||
|
@ -956,24 +956,24 @@ def patch_rom(world, player, rom, enemized):
|
|||
rom.write_byte(0x18005F, world.crystals_needed_for_ganon[player])
|
||||
rom.write_byte(0x18008A, 0x01 if world.mode[player] == "standard" else 0x00) # block HC upstairs doors in rain state in standard mode
|
||||
|
||||
rom.write_byte(0x18016A, 0x10 | ((0x01 if world.keyshuffle else 0x00)
|
||||
| (0x02 if world.compassshuffle else 0x00)
|
||||
| (0x04 if world.mapshuffle else 0x00)
|
||||
| (0x08 if world.bigkeyshuffle else 0x00))) # free roaming item text boxes
|
||||
rom.write_byte(0x18003B, 0x01 if world.mapshuffle else 0x00) # maps showing crystals on overworld
|
||||
rom.write_byte(0x18016A, 0x10 | ((0x01 if world.keyshuffle[player] else 0x00)
|
||||
| (0x02 if world.compassshuffle[player] else 0x00)
|
||||
| (0x04 if world.mapshuffle[player] else 0x00)
|
||||
| (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
|
||||
|
||||
# compasses showing dungeon count
|
||||
if world.clock_mode != 'off':
|
||||
rom.write_byte(0x18003C, 0x00) # Currently must be off if timer is on, because they use same HUD location
|
||||
elif world.compassshuffle:
|
||||
elif world.compassshuffle[player]:
|
||||
rom.write_byte(0x18003C, 0x01) # show on pickup
|
||||
else:
|
||||
rom.write_byte(0x18003C, 0x00)
|
||||
|
||||
rom.write_byte(0x180045, ((0x01 if world.keyshuffle else 0x00)
|
||||
| (0x02 if world.bigkeyshuffle else 0x00)
|
||||
| (0x04 if world.compassshuffle else 0x00)
|
||||
| (0x08 if world.mapshuffle else 0x00))) # free roaming items in menu
|
||||
rom.write_byte(0x180045, ((0x01 if world.keyshuffle[player] else 0x00)
|
||||
| (0x02 if world.bigkeyshuffle[player] else 0x00)
|
||||
| (0x04 if world.compassshuffle[player] else 0x00)
|
||||
| (0x08 if world.mapshuffle[player] else 0x00))) # free roaming items in menu
|
||||
|
||||
# Map reveals
|
||||
reveal_bytes = {
|
||||
|
@ -998,8 +998,8 @@ def patch_rom(world, player, rom, enemized):
|
|||
return reveal_bytes.get(location.parent_region.dungeon.name, 0x0000)
|
||||
return 0x0000
|
||||
|
||||
write_int16(rom, 0x18017A, get_reveal_bytes('Green Pendant') if world.mapshuffle else 0x0000) # Sahasrahla reveal
|
||||
write_int16(rom, 0x18017C, get_reveal_bytes('Crystal 5')|get_reveal_bytes('Crystal 6') if world.mapshuffle else 0x0000) # Bomb Shop 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
|
||||
|
||||
rom.write_byte(0x180172, 0x01 if world.retro else 0x00) # universal keys
|
||||
rom.write_byte(0x180175, 0x01 if world.retro else 0x00) # rupee bow
|
||||
|
@ -1493,9 +1493,9 @@ def write_strings(rom, world, player):
|
|||
|
||||
# Lastly we write hints to show where certain interesting items are. It is done the way it is to re-use the silver code and also to give one hint per each type of item regardless of how many exist. This supports many settings well.
|
||||
items_to_hint = RelevantItems.copy()
|
||||
if world.keyshuffle:
|
||||
if world.keyshuffle[player]:
|
||||
items_to_hint.extend(SmallKeys)
|
||||
if world.bigkeyshuffle:
|
||||
if world.bigkeyshuffle[player]:
|
||||
items_to_hint.extend(BigKeys)
|
||||
random.shuffle(items_to_hint)
|
||||
hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 8
|
||||
|
|
4
Rules.py
4
Rules.py
|
@ -804,7 +804,7 @@ def set_trock_key_rules(world, player):
|
|||
non_big_key_locations += ['Turtle Rock - Crystaroller Room', 'Turtle Rock - Eye Bridge - Bottom Left',
|
||||
'Turtle Rock - Eye Bridge - Bottom Right', 'Turtle Rock - Eye Bridge - Top Left',
|
||||
'Turtle Rock - Eye Bridge - Top Right']
|
||||
if not world.keyshuffle:
|
||||
if not world.keyshuffle[player]:
|
||||
non_big_key_locations += ['Turtle Rock - Big Key Chest']
|
||||
else:
|
||||
set_rule(world.get_entrance('Turtle Rock (Chain Chomp Room) (South)', player), lambda state: state.has_key('Small Key (Turtle Rock)', player, 2) if item_in_locations(state, 'Big Key (Turtle Rock)', player, [('Turtle Rock - Compass Chest', player), ('Turtle Rock - Roller Room - Left', player), ('Turtle Rock - Roller Room - Right', player)]) else state.has_key('Small Key (Turtle Rock)', player, 4))
|
||||
|
@ -814,7 +814,7 @@ def set_trock_key_rules(world, player):
|
|||
non_big_key_locations += ['Turtle Rock - Crystaroller Room', 'Turtle Rock - Eye Bridge - Bottom Left',
|
||||
'Turtle Rock - Eye Bridge - Bottom Right', 'Turtle Rock - Eye Bridge - Top Left',
|
||||
'Turtle Rock - Eye Bridge - Top Right']
|
||||
if not world.keyshuffle:
|
||||
if not world.keyshuffle[player]:
|
||||
non_big_key_locations += ['Turtle Rock - Big Key Chest', 'Turtle Rock - Chain Chomps']
|
||||
|
||||
# set big key restrictions
|
||||
|
|
Loading…
Reference in New Issue