Separate doorShuffle for each player
Add doorShuffle to spoiler metadata
This commit is contained in:
@@ -16,7 +16,7 @@ class World(object):
|
|||||||
def __init__(self, players, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments, timer, progressive, goal, algorithm, accessibility, shuffle_ganon, retro, custom, customitemarray, hints):
|
def __init__(self, players, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments, timer, progressive, goal, algorithm, accessibility, shuffle_ganon, retro, custom, customitemarray, hints):
|
||||||
self.players = players
|
self.players = players
|
||||||
self.shuffle = shuffle.copy()
|
self.shuffle = shuffle.copy()
|
||||||
self.doorShuffle = doorShuffle
|
self.doorShuffle = doorShuffle.copy()
|
||||||
self.logic = logic.copy()
|
self.logic = logic.copy()
|
||||||
self.mode = mode.copy()
|
self.mode = mode.copy()
|
||||||
self.swords = swords.copy()
|
self.swords = swords.copy()
|
||||||
@@ -1510,6 +1510,7 @@ class Spoiler(object):
|
|||||||
'weapons': self.world.swords,
|
'weapons': self.world.swords,
|
||||||
'goal': self.world.goal,
|
'goal': self.world.goal,
|
||||||
'shuffle': self.world.shuffle,
|
'shuffle': self.world.shuffle,
|
||||||
|
'door_shuffle': self.world.doorShuffle,
|
||||||
'item_pool': self.world.difficulty,
|
'item_pool': self.world.difficulty,
|
||||||
'item_functionality': self.world.difficulty_adjustments,
|
'item_functionality': self.world.difficulty_adjustments,
|
||||||
'gt_crystals': self.world.crystals_needed_for_gt,
|
'gt_crystals': self.world.crystals_needed_for_gt,
|
||||||
@@ -1560,6 +1561,7 @@ class Spoiler(object):
|
|||||||
outfile.write('Difficulty: %s\n' % self.metadata['item_pool'])
|
outfile.write('Difficulty: %s\n' % self.metadata['item_pool'])
|
||||||
outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality'])
|
outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality'])
|
||||||
outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle'])
|
outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle'])
|
||||||
|
outfile.write('Door Shuffle: %s\n' % self.metadata['door_shuffle'])
|
||||||
outfile.write('Crystals required for GT: %s\n' % self.metadata['gt_crystals'])
|
outfile.write('Crystals required for GT: %s\n' % self.metadata['gt_crystals'])
|
||||||
outfile.write('Crystals required for Ganon: %s\n' % self.metadata['ganon_crystals'])
|
outfile.write('Crystals required for Ganon: %s\n' % self.metadata['ganon_crystals'])
|
||||||
outfile.write('Pyramid hole pre-opened: %s\n' % {k: 'Yes' if v else 'No' for k, v in self.metadata['open_pyramid'].items()})
|
outfile.write('Pyramid hole pre-opened: %s\n' % {k: 'Yes' if v else 'No' for k, v in self.metadata['open_pyramid'].items()})
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ def link_doors(world, player):
|
|||||||
for ent, ext in ladders:
|
for ent, ext in ladders:
|
||||||
connect_two_way(world, ent, ext, player)
|
connect_two_way(world, ent, ext, player)
|
||||||
|
|
||||||
if world.doorShuffle == 'vanilla':
|
if world.doorShuffle[player] == 'vanilla':
|
||||||
for exitName, regionName in vanilla_logical_connections:
|
for exitName, regionName in vanilla_logical_connections:
|
||||||
connect_simple_door(world, exitName, regionName, player)
|
connect_simple_door(world, exitName, regionName, player)
|
||||||
for entrance, ext in spiral_staircases:
|
for entrance, ext in spiral_staircases:
|
||||||
@@ -49,14 +49,14 @@ def link_doors(world, player):
|
|||||||
for ent, ext in default_one_way_connections:
|
for ent, ext in default_one_way_connections:
|
||||||
connect_one_way(world, ent, ext, player)
|
connect_one_way(world, ent, ext, player)
|
||||||
vanilla_key_logic(world, player)
|
vanilla_key_logic(world, player)
|
||||||
elif world.doorShuffle == 'basic':
|
elif world.doorShuffle[player] == 'basic':
|
||||||
within_dungeon(world, player)
|
within_dungeon(world, player)
|
||||||
elif world.doorShuffle == 'crossed':
|
elif world.doorShuffle[player] == 'crossed':
|
||||||
cross_dungeon(world, player)
|
cross_dungeon(world, player)
|
||||||
elif world.doorShuffle == 'experimental':
|
elif world.doorShuffle[player] == 'experimental':
|
||||||
experiment(world, player)
|
experiment(world, player)
|
||||||
|
|
||||||
if world.doorShuffle != 'vanilla':
|
if world.doorShuffle[player] != 'vanilla':
|
||||||
create_door_spoiler(world, player)
|
create_door_spoiler(world, player)
|
||||||
|
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ def mark_regions(world, player):
|
|||||||
|
|
||||||
def create_door_spoiler(world, player):
|
def create_door_spoiler(world, player):
|
||||||
logger = logging.getLogger('')
|
logger = logging.getLogger('')
|
||||||
queue = collections.deque(world.doors)
|
queue = collections.deque((door for door in world.doors if door.player == player))
|
||||||
while len(queue) > 0:
|
while len(queue) > 0:
|
||||||
door_a = queue.popleft()
|
door_a = queue.popleft()
|
||||||
if door_a.type in [DoorType.Normal, DoorType.SpiralStairs]:
|
if door_a.type in [DoorType.Normal, DoorType.SpiralStairs]:
|
||||||
@@ -283,7 +283,7 @@ def within_dungeon(world, player):
|
|||||||
handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map)
|
handle_split_dungeons(dungeon_builders, recombinant_builders, entrances_map)
|
||||||
main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player)
|
main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player)
|
||||||
|
|
||||||
paths = determine_required_paths(world)
|
paths = determine_required_paths(world, player)
|
||||||
check_required_paths(paths, world, player)
|
check_required_paths(paths, world, player)
|
||||||
|
|
||||||
# shuffle_key_doors for dungeons
|
# shuffle_key_doors for dungeons
|
||||||
@@ -637,7 +637,7 @@ def cross_dungeon(world, player):
|
|||||||
|
|
||||||
main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player)
|
main_dungeon_generation(dungeon_builders, recombinant_builders, connections_tuple, world, player)
|
||||||
|
|
||||||
paths = determine_required_paths(world)
|
paths = determine_required_paths(world, player)
|
||||||
check_required_paths(paths, world, player)
|
check_required_paths(paths, world, player)
|
||||||
|
|
||||||
start = time.process_time()
|
start = time.process_time()
|
||||||
@@ -1097,7 +1097,7 @@ def change_door_to_small_key(d, world, player):
|
|||||||
room.change(d.doorListPos, DoorKind.SmallKey)
|
room.change(d.doorListPos, DoorKind.SmallKey)
|
||||||
|
|
||||||
|
|
||||||
def determine_required_paths(world):
|
def determine_required_paths(world, player):
|
||||||
paths = {
|
paths = {
|
||||||
'Hyrule Castle': [],
|
'Hyrule Castle': [],
|
||||||
'Eastern Palace': ['Eastern Boss'],
|
'Eastern Palace': ['Eastern Boss'],
|
||||||
@@ -1122,7 +1122,7 @@ def determine_required_paths(world):
|
|||||||
paths['Hyrule Castle'].append('Hyrule Dungeon Cellblock')
|
paths['Hyrule Castle'].append('Hyrule Dungeon Cellblock')
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
paths['Hyrule Castle'].append(('Hyrule Dungeon Cellblock', 'Sanctuary'))
|
paths['Hyrule Castle'].append(('Hyrule Dungeon Cellblock', 'Sanctuary'))
|
||||||
if world.doorShuffle in ['basic', 'experimental']: # todo: crossed?
|
if world.doorShuffle[player] in ['basic', 'experimental']: # todo: crossed?
|
||||||
paths['Thieves Town'].append('Thieves Attic Window')
|
paths['Thieves Town'].append('Thieves Attic Window')
|
||||||
return paths
|
return paths
|
||||||
|
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ def parse_arguments(argv, no_defaults=False):
|
|||||||
playerargs = parse_arguments(shlex.split(getattr(ret,f"p{player}")), True)
|
playerargs = parse_arguments(shlex.split(getattr(ret,f"p{player}")), True)
|
||||||
|
|
||||||
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
|
for name in ['logic', 'mode', 'swords', 'goal', 'difficulty', 'item_functionality',
|
||||||
'shuffle', 'crystals_ganon', 'crystals_gt', 'openpyramid',
|
'shuffle', 'door_shuffle', 'crystals_ganon', 'crystals_gt', 'openpyramid',
|
||||||
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
|
'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory',
|
||||||
'retro', 'accessibility', 'hints', 'beemizer',
|
'retro', 'accessibility', 'hints', 'beemizer',
|
||||||
'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots',
|
'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots',
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ def build_key_layout(builder, start_regions, proposal, world, player):
|
|||||||
|
|
||||||
|
|
||||||
def calc_max_chests(builder, key_layout, world, player):
|
def calc_max_chests(builder, key_layout, world, player):
|
||||||
if world.doorShuffle != 'crossed':
|
if world.doorShuffle[player] != 'crossed':
|
||||||
return len(world.get_dungeon(key_layout.sector.name, player).small_keys)
|
return len(world.get_dungeon(key_layout.sector.name, player).small_keys)
|
||||||
return builder.key_doors_num - key_layout.max_drops
|
return builder.key_doors_num - key_layout.max_drops
|
||||||
|
|
||||||
@@ -129,7 +129,7 @@ def analyze_dungeon(key_layout, world, player):
|
|||||||
child_queue = collections.deque()
|
child_queue = collections.deque()
|
||||||
for child in key_counter.child_doors.keys():
|
for child in key_counter.child_doors.keys():
|
||||||
if not child.bigKey or not key_layout.big_key_special or key_counter.big_key_opened:
|
if not child.bigKey or not key_layout.big_key_special or key_counter.big_key_opened:
|
||||||
odd_counter = create_odd_key_counter(child, key_counter, key_layout, world)
|
odd_counter = create_odd_key_counter(child, key_counter, key_layout, world, player)
|
||||||
if not empty_counter(odd_counter) and child not in doors_completed:
|
if not empty_counter(odd_counter) and child not in doors_completed:
|
||||||
child_queue.append((child, odd_counter))
|
child_queue.append((child, odd_counter))
|
||||||
while len(child_queue) > 0:
|
while len(child_queue) > 0:
|
||||||
@@ -137,7 +137,7 @@ def analyze_dungeon(key_layout, world, player):
|
|||||||
if not child.bigKey:
|
if not child.bigKey:
|
||||||
best_counter = find_best_counter(child, odd_counter, key_counter, key_layout, world, player, False)
|
best_counter = find_best_counter(child, odd_counter, key_counter, key_layout, world, player, False)
|
||||||
rule = create_rule(best_counter, key_counter, key_layout, world, player)
|
rule = create_rule(best_counter, key_counter, key_layout, world, player)
|
||||||
check_for_self_lock_key(rule, child, best_counter, key_layout, world)
|
check_for_self_lock_key(rule, child, best_counter, key_layout, world, player)
|
||||||
bk_restricted_rules(rule, child, odd_counter, key_counter, key_layout, world, player)
|
bk_restricted_rules(rule, child, odd_counter, key_counter, key_layout, world, player)
|
||||||
key_logic.door_rules[child.name] = rule
|
key_logic.door_rules[child.name] = rule
|
||||||
doors_completed.add(child)
|
doors_completed.add(child)
|
||||||
@@ -330,9 +330,9 @@ def create_rule(key_counter, prev_counter, key_layout, world, player):
|
|||||||
return DoorRules(rule_num)
|
return DoorRules(rule_num)
|
||||||
|
|
||||||
|
|
||||||
def check_for_self_lock_key(rule, door, parent_counter, key_layout, world):
|
def check_for_self_lock_key(rule, door, parent_counter, key_layout, world, player):
|
||||||
if world.accessibility != 'locations':
|
if world.accessibility != 'locations':
|
||||||
counter = find_inverted_counter(door, parent_counter, key_layout, world)
|
counter = find_inverted_counter(door, parent_counter, key_layout, world, player)
|
||||||
if not self_lock_possible(counter):
|
if not self_lock_possible(counter):
|
||||||
return
|
return
|
||||||
if len(counter.free_locations) == 1 and len(counter.key_only_locations) == 0 and not counter.important_location:
|
if len(counter.free_locations) == 1 and len(counter.key_only_locations) == 0 and not counter.important_location:
|
||||||
@@ -340,7 +340,7 @@ def check_for_self_lock_key(rule, door, parent_counter, key_layout, world):
|
|||||||
rule.small_location = next(iter(counter.free_locations))
|
rule.small_location = next(iter(counter.free_locations))
|
||||||
|
|
||||||
|
|
||||||
def find_inverted_counter(door, parent_counter, key_layout, world):
|
def find_inverted_counter(door, parent_counter, key_layout, world, player):
|
||||||
# open all doors in counter
|
# open all doors in counter
|
||||||
counter = open_all_counter(parent_counter, key_layout, door=door)
|
counter = open_all_counter(parent_counter, key_layout, door=door)
|
||||||
max_counter = find_max_counter(key_layout)
|
max_counter = find_max_counter(key_layout)
|
||||||
@@ -352,7 +352,7 @@ def find_inverted_counter(door, parent_counter, key_layout, world):
|
|||||||
inverted_counter.open_doors = dict_difference(max_counter.open_doors, counter.open_doors)
|
inverted_counter.open_doors = dict_difference(max_counter.open_doors, counter.open_doors)
|
||||||
inverted_counter.other_locations = dict_difference(max_counter.other_locations, counter.other_locations)
|
inverted_counter.other_locations = dict_difference(max_counter.other_locations, counter.other_locations)
|
||||||
for loc in inverted_counter.other_locations:
|
for loc in inverted_counter.other_locations:
|
||||||
if important_location(loc, world):
|
if important_location(loc, world, player):
|
||||||
inverted_counter.important_location = True
|
inverted_counter.important_location = True
|
||||||
return inverted_counter
|
return inverted_counter
|
||||||
|
|
||||||
@@ -732,7 +732,7 @@ def create_key_counter(state, key_layout, world, player):
|
|||||||
key_counter = KeyCounter(key_layout.max_chests)
|
key_counter = KeyCounter(key_layout.max_chests)
|
||||||
key_counter.child_doors.update(dict.fromkeys(unique_doors(state.small_doors+state.big_doors)))
|
key_counter.child_doors.update(dict.fromkeys(unique_doors(state.small_doors+state.big_doors)))
|
||||||
for loc in state.found_locations:
|
for loc in state.found_locations:
|
||||||
if important_location(loc, world):
|
if important_location(loc, world, player):
|
||||||
key_counter.important_location = True
|
key_counter.important_location = True
|
||||||
key_counter.other_locations[loc] = None
|
key_counter.other_locations[loc] = None
|
||||||
elif loc.event and 'Small Key' in loc.item.name:
|
elif loc.event and 'Small Key' in loc.item.name:
|
||||||
@@ -755,14 +755,14 @@ def create_key_counter(state, key_layout, world, player):
|
|||||||
return key_counter
|
return key_counter
|
||||||
|
|
||||||
|
|
||||||
def important_location(loc, world):
|
def important_location(loc, world, player):
|
||||||
important_locations = ['Agahnim 1', 'Agahnim 2', 'Attic Cracked Floor', 'Suspicious Maiden']
|
important_locations = ['Agahnim 1', 'Agahnim 2', 'Attic Cracked Floor', 'Suspicious Maiden']
|
||||||
if world.mode == 'standard' or world.doorShuffle == 'crossed':
|
if world.mode[player] == 'standard' or world.doorShuffle[player] == 'crossed':
|
||||||
important_locations.append('Hyrule Dungeon Cellblock')
|
important_locations.append('Hyrule Dungeon Cellblock')
|
||||||
return '- Prize' in loc.name or loc.name in important_locations
|
return '- Prize' in loc.name or loc.name in important_locations
|
||||||
|
|
||||||
|
|
||||||
def create_odd_key_counter(door, parent_counter, key_layout, world):
|
def create_odd_key_counter(door, parent_counter, key_layout, world, player):
|
||||||
odd_counter = KeyCounter(key_layout.max_chests)
|
odd_counter = KeyCounter(key_layout.max_chests)
|
||||||
next_counter = find_next_counter(door, parent_counter, key_layout)
|
next_counter = find_next_counter(door, parent_counter, key_layout)
|
||||||
odd_counter.free_locations = dict_difference(next_counter.free_locations, parent_counter.free_locations)
|
odd_counter.free_locations = dict_difference(next_counter.free_locations, parent_counter.free_locations)
|
||||||
@@ -770,7 +770,7 @@ def create_odd_key_counter(door, parent_counter, key_layout, world):
|
|||||||
odd_counter.child_doors = dict_difference(next_counter.child_doors, parent_counter.child_doors)
|
odd_counter.child_doors = dict_difference(next_counter.child_doors, parent_counter.child_doors)
|
||||||
odd_counter.other_locations = dict_difference(next_counter.other_locations, parent_counter.other_locations)
|
odd_counter.other_locations = dict_difference(next_counter.other_locations, parent_counter.other_locations)
|
||||||
for loc in odd_counter.other_locations:
|
for loc in odd_counter.other_locations:
|
||||||
if important_location(loc, world):
|
if important_location(loc, world, player):
|
||||||
odd_counter.important_location = True
|
odd_counter.important_location = True
|
||||||
return odd_counter
|
return odd_counter
|
||||||
|
|
||||||
|
|||||||
6
Main.py
6
Main.py
@@ -145,7 +145,7 @@ def main(args, seed=None):
|
|||||||
logger.info('Patching ROM.')
|
logger.info('Patching ROM.')
|
||||||
|
|
||||||
player_names = parse_names_string(args.names)
|
player_names = parse_names_string(args.names)
|
||||||
outfilebase = 'ER_%s' % (args.outputname if args.outputname else world.seed)
|
outfilebase = 'DR_%s' % (args.outputname if args.outputname else world.seed)
|
||||||
|
|
||||||
rom_names = []
|
rom_names = []
|
||||||
jsonout = {}
|
jsonout = {}
|
||||||
@@ -225,7 +225,7 @@ def main(args, seed=None):
|
|||||||
|
|
||||||
def copy_world(world):
|
def copy_world(world):
|
||||||
# ToDo: Not good yet
|
# ToDo: Not good yet
|
||||||
ret = World(world.players, world.shuffle, world.door_shuffle, world.logic, world.mode, world.swords, world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm, world.accessibility, world.shuffle_ganon, world.retro, world.custom, world.customitemarray, world.hints)
|
ret = World(world.players, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords, world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm, world.accessibility, world.shuffle_ganon, world.retro, world.custom, world.customitemarray, world.hints)
|
||||||
ret.required_medallions = world.required_medallions.copy()
|
ret.required_medallions = world.required_medallions.copy()
|
||||||
ret.swamp_patch_required = world.swamp_patch_required.copy()
|
ret.swamp_patch_required = world.swamp_patch_required.copy()
|
||||||
ret.ganon_at_pyramid = world.ganon_at_pyramid.copy()
|
ret.ganon_at_pyramid = world.ganon_at_pyramid.copy()
|
||||||
@@ -264,6 +264,8 @@ def copy_world(world):
|
|||||||
else:
|
else:
|
||||||
create_inverted_regions(ret, player)
|
create_inverted_regions(ret, player)
|
||||||
create_shops(ret, player)
|
create_shops(ret, player)
|
||||||
|
create_doors(ret, player)
|
||||||
|
create_rooms(ret, player)
|
||||||
create_dungeons(ret, player)
|
create_dungeons(ret, player)
|
||||||
|
|
||||||
copy_dynamic_regions_and_locations(world, ret)
|
copy_dynamic_regions_and_locations(world, ret)
|
||||||
|
|||||||
4
Rom.py
4
Rom.py
@@ -579,7 +579,7 @@ def patch_rom(world, player, rom, enemized):
|
|||||||
patch_shuffled_dark_sanc(world, rom, player)
|
patch_shuffled_dark_sanc(world, rom, player)
|
||||||
|
|
||||||
# patch doors
|
# patch doors
|
||||||
if world.doorShuffle == 'crossed':
|
if world.doorShuffle[player] == 'crossed':
|
||||||
rom.write_byte(0x151f1, 2)
|
rom.write_byte(0x151f1, 2)
|
||||||
rom.write_byte(0x15270, 2)
|
rom.write_byte(0x15270, 2)
|
||||||
rom.write_byte(0x1597b, 2)
|
rom.write_byte(0x1597b, 2)
|
||||||
@@ -1137,7 +1137,7 @@ def patch_rom(world, player, rom, enemized):
|
|||||||
# compasses showing dungeon count
|
# compasses showing dungeon count
|
||||||
if world.clock_mode != 'off':
|
if world.clock_mode != 'off':
|
||||||
rom.write_byte(0x18003C, 0x00) # Currently must be off if timer is on, because they use same HUD location
|
rom.write_byte(0x18003C, 0x00) # Currently must be off if timer is on, because they use same HUD location
|
||||||
elif world.compassshuffle[player] or world.doorShuffle != 'vanilla':
|
elif world.compassshuffle[player] or world.doorShuffle[player] != 'vanilla':
|
||||||
rom.write_byte(0x18003C, 0x01) # show on pickup
|
rom.write_byte(0x18003C, 0x01) # show on pickup
|
||||||
else:
|
else:
|
||||||
rom.write_byte(0x18003C, 0x00)
|
rom.write_byte(0x18003C, 0x00)
|
||||||
|
|||||||
Reference in New Issue
Block a user