feat(spoiler): introducing custom spoiler levels
This commit is contained in:
191
BaseClasses.py
191
BaseClasses.py
@@ -21,7 +21,7 @@ from source.dungeon.RoomObject import RoomObject
|
|||||||
class World(object):
|
class World(object):
|
||||||
|
|
||||||
def __init__(self, players, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments,
|
def __init__(self, players, shuffle, doorShuffle, logic, mode, swords, difficulty, difficulty_adjustments,
|
||||||
timer, progressive, goal, algorithm, accessibility, shuffle_ganon, custom, customitemarray, hints):
|
timer, progressive, goal, algorithm, accessibility, shuffle_ganon, custom, customitemarray, hints, spoiler_mode):
|
||||||
self.players = players
|
self.players = players
|
||||||
self.teams = 1
|
self.teams = 1
|
||||||
self.shuffle = shuffle.copy()
|
self.shuffle = shuffle.copy()
|
||||||
@@ -68,6 +68,7 @@ class World(object):
|
|||||||
self.hints = hints.copy()
|
self.hints = hints.copy()
|
||||||
self.dynamic_regions = []
|
self.dynamic_regions = []
|
||||||
self.dynamic_locations = []
|
self.dynamic_locations = []
|
||||||
|
self.spoiler_mode = spoiler_mode
|
||||||
self.spoiler = Spoiler(self)
|
self.spoiler = Spoiler(self)
|
||||||
self.lamps_needed_for_dark_rooms = 1
|
self.lamps_needed_for_dark_rooms = 1
|
||||||
self.doors = []
|
self.doors = []
|
||||||
@@ -89,6 +90,7 @@ class World(object):
|
|||||||
self.data_tables = {}
|
self.data_tables = {}
|
||||||
self.damage_table = {}
|
self.damage_table = {}
|
||||||
|
|
||||||
|
|
||||||
for player in range(1, players + 1):
|
for player in range(1, players + 1):
|
||||||
def set_player_attr(attr, val):
|
def set_player_attr(attr, val):
|
||||||
self.__dict__.setdefault(attr, {})[player] = val
|
self.__dict__.setdefault(attr, {})[player] = val
|
||||||
@@ -2497,6 +2499,17 @@ class Spoiler(object):
|
|||||||
self.metadata = {}
|
self.metadata = {}
|
||||||
self.shops = []
|
self.shops = []
|
||||||
self.bosses = OrderedDict()
|
self.bosses = OrderedDict()
|
||||||
|
if world.spoiler_mode == 'settings':
|
||||||
|
self.settings = {'settings'}
|
||||||
|
elif world.spoiler_mode == 'semi':
|
||||||
|
self.settings = {'settings', 'entrances', 'requirements', 'prizes'}
|
||||||
|
elif world.spoiler_mode == 'full':
|
||||||
|
self.settings = {'settings', 'entrances', 'requirements', 'prizes', 'shops', 'doors', 'items', 'bosses', 'misc'}
|
||||||
|
elif world.spoiler_mode == 'debug':
|
||||||
|
self.settings = {'settings', 'entrances', 'requirements', 'prizes', 'shops', 'doors', 'items', 'misc', 'bosses', 'debug'}
|
||||||
|
else:
|
||||||
|
self.settings = {}
|
||||||
|
|
||||||
|
|
||||||
def set_entrance(self, entrance, exit, direction, player):
|
def set_entrance(self, entrance, exit, direction, player):
|
||||||
if self.world.players == 1:
|
if self.world.players == 1:
|
||||||
@@ -2609,27 +2622,29 @@ class Spoiler(object):
|
|||||||
self.bottles[f'Waterfall Bottle ({self.world.get_player_names(player)})'] = self.world.bottle_refills[player][0]
|
self.bottles[f'Waterfall Bottle ({self.world.get_player_names(player)})'] = self.world.bottle_refills[player][0]
|
||||||
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):
|
||||||
|
return ('items' in self.settings and not item.crystal) or ('prizes' in self.settings and item.crystal)
|
||||||
self.locations = OrderedDict()
|
self.locations = OrderedDict()
|
||||||
listed_locations = set()
|
listed_locations = set()
|
||||||
|
|
||||||
lw_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.LightWorld]
|
lw_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.LightWorld and include_item(loc.item)]
|
||||||
self.locations['Light World'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in lw_locations])
|
self.locations['Light World'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in lw_locations])
|
||||||
listed_locations.update(lw_locations)
|
listed_locations.update(lw_locations)
|
||||||
|
|
||||||
dw_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.DarkWorld]
|
dw_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.DarkWorld and include_item(loc.item)]
|
||||||
self.locations['Dark World'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in dw_locations])
|
self.locations['Dark World'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in dw_locations])
|
||||||
listed_locations.update(dw_locations)
|
listed_locations.update(dw_locations)
|
||||||
|
|
||||||
cave_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.Cave and not loc.skip]
|
cave_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.Cave and not loc.skip and include_item(loc.item)]
|
||||||
self.locations['Caves'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in cave_locations])
|
self.locations['Caves'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in cave_locations])
|
||||||
listed_locations.update(cave_locations)
|
listed_locations.update(cave_locations)
|
||||||
|
|
||||||
for dungeon in self.world.dungeons:
|
for dungeon in self.world.dungeons:
|
||||||
dungeon_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.dungeon == dungeon and not loc.forced_item and not loc.skip]
|
dungeon_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.dungeon == dungeon and not loc.forced_item and not loc.skip and include_item(loc.item)]
|
||||||
self.locations[str(dungeon)] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in dungeon_locations])
|
self.locations[str(dungeon)] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in dungeon_locations])
|
||||||
listed_locations.update(dungeon_locations)
|
listed_locations.update(dungeon_locations)
|
||||||
|
|
||||||
other_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and not loc.skip]
|
other_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and not loc.skip and include_item(loc.item)]
|
||||||
if other_locations:
|
if other_locations:
|
||||||
self.locations['Other Locations'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in other_locations])
|
self.locations['Other Locations'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in other_locations])
|
||||||
listed_locations.update(other_locations)
|
listed_locations.update(other_locations)
|
||||||
@@ -2721,65 +2736,68 @@ class Spoiler(object):
|
|||||||
outfile.write('ALttP Dungeon Randomizer Version %s - Seed: %s\n\n' % (self.metadata['version'], self.world.seed))
|
outfile.write('ALttP Dungeon Randomizer Version %s - Seed: %s\n\n' % (self.metadata['version'], self.world.seed))
|
||||||
if self.metadata['user_notes']:
|
if self.metadata['user_notes']:
|
||||||
outfile.write('User Notes: %s\n' % self.metadata['user_notes'])
|
outfile.write('User Notes: %s\n' % self.metadata['user_notes'])
|
||||||
outfile.write('Filling Algorithm: %s\n' % self.world.algorithm)
|
if 'settings' in self.settings:
|
||||||
|
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)
|
||||||
for player in range(1, self.world.players + 1):
|
for player in range(1, self.world.players + 1):
|
||||||
if self.world.players > 1:
|
if self.world.players > 1:
|
||||||
outfile.write('\nPlayer %d: %s\n' % (player, self.world.get_player_names(player)))
|
outfile.write('\nPlayer %d: %s\n' % (player, self.world.get_player_names(player)))
|
||||||
outfile.write(f'Settings Code: {self.metadata["code"][player]}\n')
|
if 'settings' in self.settings:
|
||||||
|
outfile.write(f'Settings Code: {self.metadata["code"][player]}\n')
|
||||||
outfile.write('Logic: %s\n' % self.metadata['logic'][player])
|
outfile.write('Logic: %s\n' % self.metadata['logic'][player])
|
||||||
outfile.write('Mode: %s\n' % self.metadata['mode'][player])
|
if 'settings' in self.settings:
|
||||||
outfile.write('Swords: %s\n' % self.metadata['weapons'][player])
|
outfile.write('Mode: %s\n' % self.metadata['mode'][player])
|
||||||
outfile.write('Goal: %s\n' % self.metadata['goal'][player])
|
outfile.write('Swords: %s\n' % self.metadata['weapons'][player])
|
||||||
if self.metadata['goal'][player] in ['triforcehunt', 'trinity', 'ganonhunt']:
|
outfile.write('Goal: %s\n' % self.metadata['goal'][player])
|
||||||
outfile.write('Triforce Pieces Required: %s\n' % self.metadata['triforcegoal'][player])
|
if self.metadata['goal'][player] in ['triforcehunt', 'trinity', 'ganonhunt']:
|
||||||
outfile.write('Triforce Pieces Total: %s\n' % self.metadata['triforcepool'][player])
|
outfile.write('Triforce Pieces Required: %s\n' % self.metadata['triforcegoal'][player])
|
||||||
outfile.write('Crystals required for GT: %s\n' % (str(self.world.crystals_gt_orig[player])))
|
outfile.write('Triforce Pieces Total: %s\n' % self.metadata['triforcepool'][player])
|
||||||
outfile.write('Crystals required for Ganon: %s\n' % (str(self.world.crystals_ganon_orig[player])))
|
outfile.write('Crystals required for GT: %s\n' % (str(self.world.crystals_gt_orig[player])))
|
||||||
outfile.write('Accessibility: %s\n' % self.metadata['accessibility'][player])
|
outfile.write('Crystals required for Ganon: %s\n' % (str(self.world.crystals_ganon_orig[player])))
|
||||||
outfile.write(f"Restricted Boss Items: {self.metadata['restricted_boss_items'][player]}\n")
|
outfile.write('Accessibility: %s\n' % self.metadata['accessibility'][player])
|
||||||
outfile.write('Difficulty: %s\n' % self.metadata['item_pool'][player])
|
outfile.write(f"Restricted Boss Items: {self.metadata['restricted_boss_items'][player]}\n")
|
||||||
outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality'][player])
|
outfile.write('Difficulty: %s\n' % self.metadata['item_pool'][player])
|
||||||
outfile.write(f"Flute Mode: {self.metadata['flute_mode'][player]}\n")
|
outfile.write('Item Functionality: %s\n' % self.metadata['item_functionality'][player])
|
||||||
outfile.write(f"Bow Mode: {self.metadata['bow_mode'][player]}\n")
|
outfile.write(f"Flute Mode: {self.metadata['flute_mode'][player]}\n")
|
||||||
outfile.write(f"Shopsanity: {yn(self.metadata['shopsanity'][player])}\n")
|
outfile.write(f"Bow Mode: {self.metadata['bow_mode'][player]}\n")
|
||||||
outfile.write(f"Bombbag: {yn(self.metadata['bombbag'][player])}\n")
|
outfile.write(f"Shopsanity: {yn(self.metadata['shopsanity'][player])}\n")
|
||||||
outfile.write(f"Pseudoboots: {yn(self.metadata['pseudoboots'][player])}\n")
|
outfile.write(f"Bombbag: {yn(self.metadata['bombbag'][player])}\n")
|
||||||
outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle'][player])
|
outfile.write(f"Pseudoboots: {yn(self.metadata['pseudoboots'][player])}\n")
|
||||||
if self.metadata['shuffle'][player] != 'vanilla':
|
outfile.write('Entrance Shuffle: %s\n' % self.metadata['shuffle'][player])
|
||||||
outfile.write(f"Link's House Shuffled: {yn(self.metadata['shufflelinks'][player])}\n")
|
if self.metadata['shuffle'][player] != 'vanilla':
|
||||||
outfile.write(f"Back of Tavern Shuffled: {yn(self.metadata['shuffletavern'][player])}\n")
|
outfile.write(f"Link's House Shuffled: {yn(self.metadata['shufflelinks'][player])}\n")
|
||||||
outfile.write(f"GT/Ganon Shuffled: {yn(self.metadata['shuffleganon'])}\n")
|
outfile.write(f"Back of Tavern Shuffled: {yn(self.metadata['shuffletavern'][player])}\n")
|
||||||
outfile.write(f"Overworld Map: {self.metadata['overworld_map'][player]}\n")
|
outfile.write(f"GT/Ganon Shuffled: {yn(self.metadata['shuffleganon'])}\n")
|
||||||
outfile.write(f"Take Any Caves: {self.metadata['take_any'][player]}\n")
|
outfile.write(f"Overworld Map: {self.metadata['overworld_map'][player]}\n")
|
||||||
if self.metadata['goal'][player] != 'trinity':
|
outfile.write(f"Take Any Caves: {self.metadata['take_any'][player]}\n")
|
||||||
outfile.write('Pyramid hole pre-opened: %s\n' % (self.metadata['open_pyramid'][player]))
|
if self.metadata['goal'][player] != 'trinity':
|
||||||
outfile.write('Door Shuffle: %s\n' % self.metadata['door_shuffle'][player])
|
outfile.write('Pyramid hole pre-opened: %s\n' % (self.metadata['open_pyramid'][player]))
|
||||||
if self.metadata['door_shuffle'][player] != 'vanilla':
|
outfile.write('Door Shuffle: %s\n' % self.metadata['door_shuffle'][player])
|
||||||
outfile.write(f"Intensity: {self.metadata['intensity'][player]}\n")
|
if self.metadata['door_shuffle'][player] != 'vanilla':
|
||||||
outfile.write(f"Door Type Mode: {self.metadata['door_type_mode'][player]}\n")
|
outfile.write(f"Intensity: {self.metadata['intensity'][player]}\n")
|
||||||
outfile.write(f"Trap Door Mode: {self.metadata['trap_door_mode'][player]}\n")
|
outfile.write(f"Door Type Mode: {self.metadata['door_type_mode'][player]}\n")
|
||||||
outfile.write(f"Key Logic Algorithm: {self.metadata['key_logic'][player]}\n")
|
outfile.write(f"Trap Door Mode: {self.metadata['trap_door_mode'][player]}\n")
|
||||||
outfile.write(f"Decouple Doors: {yn(self.metadata['decoupledoors'][player])}\n")
|
outfile.write(f"Key Logic Algorithm: {self.metadata['key_logic'][player]}\n")
|
||||||
outfile.write(f"Spiral Stairs can self-loop: {yn(self.metadata['door_self_loops'][player])}\n")
|
outfile.write(f"Decouple Doors: {yn(self.metadata['decoupledoors'][player])}\n")
|
||||||
outfile.write(f"Experimental: {yn(self.metadata['experimental'][player])}\n")
|
outfile.write(f"Spiral Stairs can self-loop: {yn(self.metadata['door_self_loops'][player])}\n")
|
||||||
outfile.write(f"Dungeon Counters: {self.metadata['dungeon_counters'][player]}\n")
|
outfile.write(f"Experimental: {yn(self.metadata['experimental'][player])}\n")
|
||||||
outfile.write(f"Drop Shuffle: {self.metadata['dropshuffle'][player]}\n")
|
outfile.write(f"Dungeon Counters: {self.metadata['dungeon_counters'][player]}\n")
|
||||||
outfile.write(f"Pottery Mode: {self.metadata['pottery'][player]}\n")
|
outfile.write(f"Drop Shuffle: {self.metadata['dropshuffle'][player]}\n")
|
||||||
outfile.write(f"Pot Shuffle (Legacy): {yn(self.metadata['potshuffle'][player])}\n")
|
outfile.write(f"Pottery Mode: {self.metadata['pottery'][player]}\n")
|
||||||
outfile.write('Map shuffle: %s\n' % ('Yes' if self.metadata['mapshuffle'][player] else 'No'))
|
outfile.write(f"Pot Shuffle (Legacy): {yn(self.metadata['potshuffle'][player])}\n")
|
||||||
outfile.write('Compass shuffle: %s\n' % ('Yes' if self.metadata['compassshuffle'][player] else 'No'))
|
outfile.write('Map shuffle: %s\n' % ('Yes' if self.metadata['mapshuffle'][player] else 'No'))
|
||||||
outfile.write(f"Small Key shuffle: {self.metadata['keyshuffle'][player]}\n")
|
outfile.write('Compass shuffle: %s\n' % ('Yes' if self.metadata['compassshuffle'][player] else 'No'))
|
||||||
outfile.write('Big Key shuffle: %s\n' % ('Yes' if self.metadata['bigkeyshuffle'][player] else 'No'))
|
outfile.write(f"Small Key shuffle: {self.metadata['keyshuffle'][player]}\n")
|
||||||
outfile.write('Boss shuffle: %s\n' % self.metadata['boss_shuffle'][player])
|
outfile.write('Big Key shuffle: %s\n' % ('Yes' if self.metadata['bigkeyshuffle'][player] else 'No'))
|
||||||
outfile.write('Enemy shuffle: %s\n' % self.metadata['enemy_shuffle'][player])
|
outfile.write('Boss shuffle: %s\n' % self.metadata['boss_shuffle'][player])
|
||||||
outfile.write('Enemy health: %s\n' % self.metadata['enemy_health'][player])
|
outfile.write('Enemy shuffle: %s\n' % self.metadata['enemy_shuffle'][player])
|
||||||
outfile.write('Enemy damage: %s\n' % self.metadata['enemy_damage'][player])
|
outfile.write('Enemy health: %s\n' % self.metadata['enemy_health'][player])
|
||||||
if self.metadata['enemy_shuffle'][player] != 'none':
|
outfile.write('Enemy damage: %s\n' % self.metadata['enemy_damage'][player])
|
||||||
outfile.write(f"Enemy logic: {self.metadata['any_enemy_logic'][player]}\n")
|
if self.metadata['enemy_shuffle'][player] != 'none':
|
||||||
outfile.write(f"Hints: {yn(self.metadata['hints'][player])}\n")
|
outfile.write(f"Enemy logic: {self.metadata['any_enemy_logic'][player]}\n")
|
||||||
outfile.write('Race: %s\n' % ('Yes' if self.world.settings.world_rep['meta']['race'] else 'No'))
|
outfile.write(f"Hints: {yn(self.metadata['hints'][player])}\n")
|
||||||
|
outfile.write('Race: %s\n' % ('Yes' if self.world.settings.world_rep['meta']['race'] else 'No'))
|
||||||
|
|
||||||
if self.startinventory:
|
if self.startinventory:
|
||||||
outfile.write('Starting Inventory:'.ljust(line_width))
|
outfile.write('Starting Inventory:'.ljust(line_width))
|
||||||
@@ -2817,26 +2835,29 @@ class Spoiler(object):
|
|||||||
with open(filename, 'a') as outfile:
|
with open(filename, 'a') as outfile:
|
||||||
line_width = 35
|
line_width = 35
|
||||||
|
|
||||||
outfile.write('\nRequirements:\n\n')
|
|
||||||
for dungeon, medallion in self.medallions.items():
|
|
||||||
outfile.write(f'{dungeon}: {medallion} Medallion\n')
|
|
||||||
for player in range(1, self.world.players + 1):
|
|
||||||
player_name = '' if self.world.players == 1 else str(' (Player ' + str(player) + ')')
|
|
||||||
if self.world.crystals_gt_orig[player] == 'random':
|
|
||||||
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':
|
|
||||||
outfile.write(str('Crystals Required for Ganon' + player_name + ':').ljust(line_width) + '%s\n' % (str(self.metadata['ganon_crystals'][player])))
|
|
||||||
|
|
||||||
outfile.write('\n\nBottle Refills:\n\n')
|
if 'requirements' in self.settings:
|
||||||
for fairy, bottle in self.bottles.items():
|
outfile.write('\nRequirements:\n\n')
|
||||||
outfile.write(f'{fairy}: {bottle}\n')
|
for dungeon, medallion in self.medallions.items():
|
||||||
|
outfile.write(f'{dungeon}: {medallion} Medallion\n')
|
||||||
|
for player in range(1, self.world.players + 1):
|
||||||
|
player_name = '' if self.world.players == 1 else str(' (Player ' + str(player) + ')')
|
||||||
|
if self.world.crystals_gt_orig[player] == 'random':
|
||||||
|
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':
|
||||||
|
outfile.write(str('Crystals Required for Ganon' + player_name + ':').ljust(line_width) + '%s\n' % (str(self.metadata['ganon_crystals'][player])))
|
||||||
|
|
||||||
if self.entrances:
|
if 'misc' in self.settings:
|
||||||
|
outfile.write('\n\nBottle Refills:\n\n')
|
||||||
|
for fairy, bottle in self.bottles.items():
|
||||||
|
outfile.write(f'{fairy}: {bottle}\n')
|
||||||
|
|
||||||
|
if self.entrances and 'entrances' in self.settings:
|
||||||
# entrances: To/From overworld; Checking w/ & w/out "Exit" and translating accordingly
|
# entrances: To/From overworld; Checking w/ & w/out "Exit" and translating accordingly
|
||||||
outfile.write('\nEntrances:\n\n')
|
outfile.write('\nEntrances:\n\n')
|
||||||
outfile.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_names(entry["player"])}: ' if self.world.players > 1 else '', self.world.fish.translate("meta", "entrances", entry['entrance']), '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', self.world.fish.translate("meta", "entrances", entry['exit'])) for entry in self.entrances.values()]))
|
outfile.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_names(entry["player"])}: ' if self.world.players > 1 else '', self.world.fish.translate("meta", "entrances", entry['entrance']), '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', self.world.fish.translate("meta", "entrances", entry['exit'])) for entry in self.entrances.values()]))
|
||||||
|
|
||||||
if self.doors:
|
if self.doors and 'doors' in self.settings:
|
||||||
outfile.write('\n\nDoors:\n\n')
|
outfile.write('\n\nDoors:\n\n')
|
||||||
outfile.write('\n'.join(
|
outfile.write('\n'.join(
|
||||||
['%s%s %s %s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '',
|
['%s%s %s %s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '',
|
||||||
@@ -2845,33 +2866,37 @@ class Spoiler(object):
|
|||||||
self.world.fish.translate("meta", "doors", entry['exit']),
|
self.world.fish.translate("meta", "doors", entry['exit']),
|
||||||
'({0})'.format(entry['dname']) if self.world.doorShuffle[entry['player']] != 'basic' else '') for
|
'({0})'.format(entry['dname']) if self.world.doorShuffle[entry['player']] != 'basic' else '') for
|
||||||
entry in self.doors.values()]))
|
entry in self.doors.values()]))
|
||||||
if self.lobbies:
|
if self.lobbies and 'doors' in self.settings:
|
||||||
outfile.write('\n\nDungeon Lobbies:\n\n')
|
outfile.write('\n\nDungeon Lobbies:\n\n')
|
||||||
outfile.write('\n'.join(
|
outfile.write('\n'.join(
|
||||||
[f"{'Player {0}: '.format(entry['player']) if self.world.players > 1 else ''}{entry['lobby_name']}: {entry['door_name']}"
|
[f"{'Player {0}: '.format(entry['player']) if self.world.players > 1 else ''}{entry['lobby_name']}: {entry['door_name']}"
|
||||||
for
|
for
|
||||||
entry in self.lobbies.values()]))
|
entry in self.lobbies.values()]))
|
||||||
if self.doorTypes:
|
if self.doorTypes and 'doors' in self.settings:
|
||||||
# doorNames: For some reason these come in combined, somehow need to split on the thing to translate
|
# doorNames: For some reason these come in combined, somehow need to split on the thing to translate
|
||||||
# doorTypes: Small Key, Bombable, Bonkable
|
# doorTypes: Small Key, Bombable, Bonkable
|
||||||
outfile.write('\n\nDoor Types:\n\n')
|
outfile.write('\n\nDoor Types:\n\n')
|
||||||
outfile.write('\n'.join(['%s%s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '', self.world.fish.translate("meta", "doors", entry['doorNames']), self.world.fish.translate("meta", "doorTypes", entry['type'])) for entry in self.doorTypes.values()]))
|
outfile.write('\n'.join(['%s%s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '', self.world.fish.translate("meta", "doors", entry['doorNames']), self.world.fish.translate("meta", "doorTypes", entry['type'])) for entry in self.doorTypes.values()]))
|
||||||
|
|
||||||
|
|
||||||
# locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name
|
# locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name
|
||||||
# items: Item names
|
# items: Item names
|
||||||
outfile.write('\n\nLocations:\n\n')
|
outfile.write('\n\nLocations:\n\n')
|
||||||
outfile.write('\n'.join(['%s: %s' % (self.world.fish.translate("meta", "locations", location), self.world.fish.translate("meta", "items", item)) for grouping in self.locations.values() for (location, item) in grouping.items()]))
|
outfile.write('\n'.join(['%s: %s' % (self.world.fish.translate("meta", "locations", location), self.world.fish.translate("meta", "items", item))
|
||||||
|
for grouping in self.locations.values() for (location, item) in grouping.items()]))
|
||||||
|
|
||||||
# locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name
|
# locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name
|
||||||
# items: Item names
|
# items: Item names
|
||||||
outfile.write('\n\nShops:\n\n')
|
if 'shops' in self.settings:
|
||||||
outfile.write('\n'.join("{} [{}]\n {}".format(self.world.fish.translate("meta", "locations", shop['location']), shop['type'], "\n ".join(self.world.fish.translate("meta", "items", item) for item in [shop.get('item_0', None), shop.get('item_1', None), shop.get('item_2', None)] if item)) for shop in self.shops))
|
outfile.write('\n\nShops:\n\n')
|
||||||
|
outfile.write('\n'.join("{} [{}]\n {}".format(self.world.fish.translate("meta", "locations", shop['location']), shop['type'], "\n ".join(self.world.fish.translate("meta", "items", item) for item in [shop.get('item_0', None), shop.get('item_1', None), shop.get('item_2', None)] if item)) for shop in self.shops))
|
||||||
|
|
||||||
for player in range(1, self.world.players + 1):
|
if 'bosses' in self.settings:
|
||||||
if self.world.boss_shuffle[player] != 'none':
|
for player in range(1, self.world.players + 1):
|
||||||
bossmap = self.bosses[str(player)] if self.world.players > 1 else self.bosses
|
if self.world.boss_shuffle[player] != 'none':
|
||||||
outfile.write(f'\n\nBosses ({self.world.get_player_names(player)}):\n\n')
|
bossmap = self.bosses[str(player)] if self.world.players > 1 else self.bosses
|
||||||
outfile.write('\n'.join([f'{x}: {y}' for x, y in bossmap.items() if y not in ['Agahnim', 'Agahnim 2', 'Ganon']]))
|
outfile.write(f'\n\nBosses ({self.world.get_player_names(player)}):\n\n')
|
||||||
|
outfile.write('\n'.join([f'{x}: {y}' for x, y in bossmap.items() if y not in ['Agahnim', 'Agahnim 2', 'Ganon']]))
|
||||||
|
|
||||||
def extras(self, filename):
|
def extras(self, filename):
|
||||||
# todo: conditional on enemy shuffle mode
|
# todo: conditional on enemy shuffle mode
|
||||||
|
|||||||
5
CLI.py
5
CLI.py
@@ -224,7 +224,7 @@ def parse_settings():
|
|||||||
'mixed_travel': 'prevent',
|
'mixed_travel': 'prevent',
|
||||||
'standardize_palettes': 'standardize',
|
'standardize_palettes': 'standardize',
|
||||||
'aga_randomness': True,
|
'aga_randomness': True,
|
||||||
|
|
||||||
"triforce_pool": 0,
|
"triforce_pool": 0,
|
||||||
"triforce_goal": 0,
|
"triforce_goal": 0,
|
||||||
"triforce_pool_min": 0,
|
"triforce_pool_min": 0,
|
||||||
@@ -253,10 +253,9 @@ def parse_settings():
|
|||||||
'msu_resume': False,
|
'msu_resume': False,
|
||||||
'collection_rate': False,
|
'collection_rate': False,
|
||||||
|
|
||||||
# Spoiler defaults to TRUE
|
'spoiler': 'full',
|
||||||
# Playthrough defaults to TRUE
|
# Playthrough defaults to TRUE
|
||||||
# ROM defaults to TRUE
|
# ROM defaults to TRUE
|
||||||
"create_spoiler": True,
|
|
||||||
"calc_playthrough": True,
|
"calc_playthrough": True,
|
||||||
"create_rom": True,
|
"create_rom": True,
|
||||||
"bps": False,
|
"bps": False,
|
||||||
|
|||||||
16
Main.py
16
Main.py
@@ -85,7 +85,7 @@ def main(args, seed=None, fish=None):
|
|||||||
raise RuntimeError(BabelFish().translate("cli","cli","hybridglitches.door.shuffle"))
|
raise RuntimeError(BabelFish().translate("cli","cli","hybridglitches.door.shuffle"))
|
||||||
world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords,
|
world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords,
|
||||||
args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm,
|
args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm,
|
||||||
args.accessibility, args.shuffleganon, args.custom, args.customitemarray, args.hints)
|
args.accessibility, args.shuffleganon, args.custom, args.customitemarray, args.hints, args.spoiler)
|
||||||
world.customizer = customized if customized else None
|
world.customizer = customized if customized else None
|
||||||
logger = logging.getLogger('')
|
logger = logging.getLogger('')
|
||||||
if seed is None:
|
if seed is None:
|
||||||
@@ -202,7 +202,7 @@ def main(args, seed=None, fish=None):
|
|||||||
if item:
|
if item:
|
||||||
world.push_precollected(item)
|
world.push_precollected(item)
|
||||||
|
|
||||||
if args.create_spoiler and not args.jsonout:
|
if world.spoiler_mode != 'none' and not args.jsonout:
|
||||||
logger.info(world.fish.translate("cli", "cli", "create.meta"))
|
logger.info(world.fish.translate("cli", "cli", "create.meta"))
|
||||||
world.spoiler.meta_to_file(output_path(f'{outfilebase}_Spoiler.txt'))
|
world.spoiler.meta_to_file(output_path(f'{outfilebase}_Spoiler.txt'))
|
||||||
if args.mystery and not args.suppress_meta:
|
if args.mystery and not args.suppress_meta:
|
||||||
@@ -406,12 +406,12 @@ def main(args, seed=None, fish=None):
|
|||||||
|
|
||||||
if args.mystery and not args.suppress_meta:
|
if args.mystery and not args.suppress_meta:
|
||||||
world.spoiler.hashes_to_file(output_path(f'{outfilebase}_meta.txt'))
|
world.spoiler.hashes_to_file(output_path(f'{outfilebase}_meta.txt'))
|
||||||
elif args.create_spoiler and not args.jsonout:
|
elif world.spoiler_mode != 'none' and not args.jsonout:
|
||||||
world.spoiler.hashes_to_file(output_path(f'{outfilebase}_Spoiler.txt'))
|
world.spoiler.hashes_to_file(output_path(f'{outfilebase}_Spoiler.txt'))
|
||||||
if args.create_spoiler and not args.jsonout:
|
if world.spoiler_mode != 'none' and not args.jsonout:
|
||||||
logger.info(world.fish.translate("cli", "cli", "patching.spoiler"))
|
logger.info(world.fish.translate("cli", "cli", "patching.spoiler"))
|
||||||
world.spoiler.to_file(output_path(f'{outfilebase}_Spoiler.txt'))
|
world.spoiler.to_file(output_path(f'{outfilebase}_Spoiler.txt'))
|
||||||
if args.loglevel == 'debug':
|
if 'debug' in world.spoiler.settings:
|
||||||
world.spoiler.extras(output_path(f'{outfilebase}_Spoiler.txt'))
|
world.spoiler.extras(output_path(f'{outfilebase}_Spoiler.txt'))
|
||||||
|
|
||||||
if not args.skip_playthrough:
|
if not args.skip_playthrough:
|
||||||
@@ -420,7 +420,7 @@ def main(args, seed=None, fish=None):
|
|||||||
|
|
||||||
if args.jsonout:
|
if args.jsonout:
|
||||||
print(json.dumps({**jsonout, 'spoiler': world.spoiler.to_json()}))
|
print(json.dumps({**jsonout, 'spoiler': world.spoiler.to_json()}))
|
||||||
elif args.create_spoiler:
|
elif world.spoiler_mode != 'none':
|
||||||
logger.info(world.fish.translate("cli","cli","patching.spoiler"))
|
logger.info(world.fish.translate("cli","cli","patching.spoiler"))
|
||||||
if args.jsonout:
|
if args.jsonout:
|
||||||
with open(output_path('%s_Spoiler.json' % outfilebase), 'w') as outfile:
|
with open(output_path('%s_Spoiler.json' % outfilebase), 'w') as outfile:
|
||||||
@@ -435,7 +435,7 @@ def main(args, seed=None, fish=None):
|
|||||||
logger.info("")
|
logger.info("")
|
||||||
logger.info(world.fish.translate("cli","cli","made.rom") % (YES if (args.create_rom) else NO))
|
logger.info(world.fish.translate("cli","cli","made.rom") % (YES if (args.create_rom) else NO))
|
||||||
logger.info(world.fish.translate("cli","cli","made.playthrough") % (YES if (args.calc_playthrough) else NO))
|
logger.info(world.fish.translate("cli","cli","made.playthrough") % (YES if (args.calc_playthrough) else NO))
|
||||||
logger.info(world.fish.translate("cli","cli","made.spoiler") % (YES if (not args.jsonout and args.create_spoiler) else NO))
|
logger.info(world.fish.translate("cli","cli","made.spoiler") % (YES if (not args.jsonout and world.spoiler_mode != 'none') else NO))
|
||||||
logger.info(world.fish.translate("cli","cli","seed") + ": %s", world.seed)
|
logger.info(world.fish.translate("cli","cli","seed") + ": %s", world.seed)
|
||||||
logger.info(world.fish.translate("cli","cli","total.time"), time.perf_counter() - start)
|
logger.info(world.fish.translate("cli","cli","total.time"), time.perf_counter() - start)
|
||||||
|
|
||||||
@@ -449,7 +449,7 @@ def copy_world(world):
|
|||||||
# ToDo: Not good yet
|
# ToDo: Not good yet
|
||||||
ret = World(world.players, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords,
|
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.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm,
|
||||||
world.accessibility, world.shuffle_ganon, world.custom, world.customitemarray, world.hints)
|
world.accessibility, world.shuffle_ganon, world.custom, world.customitemarray, world.hints, world.spoiler_mode)
|
||||||
ret.teams = world.teams
|
ret.teams = world.teams
|
||||||
ret.player_names = copy.deepcopy(world.player_names)
|
ret.player_names = copy.deepcopy(world.player_names)
|
||||||
ret.remote_items = world.remote_items.copy()
|
ret.remote_items = world.remote_items.copy()
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ def main():
|
|||||||
parser.add_argument('--multi', default=1, type=lambda value: min(max(int(value), 1), 255))
|
parser.add_argument('--multi', default=1, type=lambda value: min(max(int(value), 1), 255))
|
||||||
parser.add_argument('--names', default='')
|
parser.add_argument('--names', default='')
|
||||||
parser.add_argument('--teams', default=1, type=lambda value: max(int(value), 1))
|
parser.add_argument('--teams', default=1, type=lambda value: max(int(value), 1))
|
||||||
parser.add_argument('--create_spoiler', action='store_true')
|
parser.add_argument('--spoiler', default='none', choices=['none', 'settings', 'semi', 'full', 'debug'])
|
||||||
parser.add_argument('--suppress_rom', action='store_true')
|
parser.add_argument('--suppress_rom', action='store_true')
|
||||||
parser.add_argument('--suppress_meta', action='store_true')
|
parser.add_argument('--suppress_meta', action='store_true')
|
||||||
parser.add_argument('--bps', action='store_true')
|
parser.add_argument('--bps', action='store_true')
|
||||||
@@ -63,7 +63,7 @@ def main():
|
|||||||
erargs = parse_cli(['--multi', str(args.multi)])
|
erargs = parse_cli(['--multi', str(args.multi)])
|
||||||
erargs.seed = seed
|
erargs.seed = seed
|
||||||
erargs.names = args.names
|
erargs.names = args.names
|
||||||
erargs.create_spoiler = args.create_spoiler
|
erargs.spoiler = args.spoiler
|
||||||
erargs.suppress_rom = args.suppress_rom
|
erargs.suppress_rom = args.suppress_rom
|
||||||
erargs.suppress_meta = args.suppress_meta
|
erargs.suppress_meta = args.suppress_meta
|
||||||
erargs.bps = args.bps
|
erargs.bps = args.bps
|
||||||
|
|||||||
4
Utils.py
4
Utils.py
@@ -342,11 +342,11 @@ def update_deprecated_args(args):
|
|||||||
# Don't do: Yes
|
# Don't do: Yes
|
||||||
# Do: No
|
# Do: No
|
||||||
if "suppress_spoiler" in argVars:
|
if "suppress_spoiler" in argVars:
|
||||||
args.create_spoiler = not args.suppress_spoiler in truthy
|
args.spoiler = 'none'
|
||||||
# Don't do: No
|
# Don't do: No
|
||||||
# Do: Yes
|
# Do: Yes
|
||||||
if "create_spoiler" in argVars:
|
if "create_spoiler" in argVars:
|
||||||
args.suppress_spoiler = not args.create_spoiler in truthy
|
args.spoiler = 'full'
|
||||||
|
|
||||||
# ROM defaults to TRUE
|
# ROM defaults to TRUE
|
||||||
# Don't do: Yes
|
# Don't do: Yes
|
||||||
|
|||||||
@@ -4,14 +4,14 @@
|
|||||||
"action": "store_true",
|
"action": "store_true",
|
||||||
"type": "bool"
|
"type": "bool"
|
||||||
},
|
},
|
||||||
"create_spoiler": {
|
"spoiler": {
|
||||||
"action": "store_false",
|
"choices": [
|
||||||
"dest": "suppress_spoiler",
|
"none",
|
||||||
"type": "bool",
|
"settings",
|
||||||
"help": "suppress"
|
"semi",
|
||||||
},
|
"full",
|
||||||
"suppress_spoiler": {
|
"debug"
|
||||||
"action": "store_true"
|
]
|
||||||
},
|
},
|
||||||
"mystery": {
|
"mystery": {
|
||||||
"action": "store_true",
|
"action": "store_true",
|
||||||
|
|||||||
@@ -60,7 +60,15 @@
|
|||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"lang": [ "App Language, if available, defaults to English" ],
|
"lang": [ "App Language, if available, defaults to English" ],
|
||||||
"create_spoiler": [ "Output a Spoiler File" ],
|
"spoiler": [
|
||||||
|
"Spoiler File Options. (default: %(default)s)",
|
||||||
|
"None: No Spoiler",
|
||||||
|
"Meta: Meta information only about game. Intended for mystery settings",
|
||||||
|
"Settings: Only settings information",
|
||||||
|
"Semi: ",
|
||||||
|
"Full: Full spoiler generated",
|
||||||
|
"Debug: Includes debug information"
|
||||||
|
],
|
||||||
"bps": [ "Output BPS patches instead of ROMs"],
|
"bps": [ "Output BPS patches instead of ROMs"],
|
||||||
"logic": [
|
"logic": [
|
||||||
"Select Enforcement of Item Requirements. (default: %(default)s)",
|
"Select Enforcement of Item Requirements. (default: %(default)s)",
|
||||||
|
|||||||
@@ -206,7 +206,12 @@
|
|||||||
|
|
||||||
|
|
||||||
"randomizer.generation.bps": "Create BPS Patches",
|
"randomizer.generation.bps": "Create BPS Patches",
|
||||||
"randomizer.generation.createspoiler": "Create Spoiler Log",
|
"randomizer.generation.spoiler": "Create Spoiler Log",
|
||||||
|
"randomizer.generation.spoiler.none": "None",
|
||||||
|
"randomizer.generation.spoiler.settings": "Settings Only",
|
||||||
|
"randomizer.generation.spoiler.semi": "Semi (Entrances and Prizes)",
|
||||||
|
"randomizer.generation.spoiler.full": "Full",
|
||||||
|
"randomizer.generation.spoiler.debug": "Debug",
|
||||||
"randomizer.generation.createrom": "Create Patched ROM",
|
"randomizer.generation.createrom": "Create Patched ROM",
|
||||||
"randomizer.generation.calcplaythrough": "Calculate Playthrough",
|
"randomizer.generation.calcplaythrough": "Calculate Playthrough",
|
||||||
"randomizer.generation.print_custom_yaml": "Print Customizer File",
|
"randomizer.generation.print_custom_yaml": "Print Customizer File",
|
||||||
|
|||||||
@@ -2,7 +2,16 @@
|
|||||||
"checkboxes": {
|
"checkboxes": {
|
||||||
"createrom": { "type": "checkbox" },
|
"createrom": { "type": "checkbox" },
|
||||||
"bps": { "type": "checkbox" },
|
"bps": { "type": "checkbox" },
|
||||||
"createspoiler": { "type": "checkbox" },
|
"spoiler": {
|
||||||
|
"type": "selectbox",
|
||||||
|
"options": [
|
||||||
|
"none",
|
||||||
|
"settings",
|
||||||
|
"semi",
|
||||||
|
"full",
|
||||||
|
"debug"
|
||||||
|
]
|
||||||
|
},
|
||||||
"calcplaythrough": { "type": "checkbox" },
|
"calcplaythrough": { "type": "checkbox" },
|
||||||
"print_custom_yaml": { "type": "checkbox" }
|
"print_custom_yaml": { "type": "checkbox" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ SETTINGSTOPROCESS = {
|
|||||||
},
|
},
|
||||||
"generation": {
|
"generation": {
|
||||||
"bps": "bps",
|
"bps": "bps",
|
||||||
"createspoiler": "create_spoiler",
|
"spoiler": "spoiler",
|
||||||
"createrom": "create_rom",
|
"createrom": "create_rom",
|
||||||
"calcplaythrough": "calc_playthrough",
|
"calcplaythrough": "calc_playthrough",
|
||||||
"print_custom_yaml": "print_custom_yaml",
|
"print_custom_yaml": "print_custom_yaml",
|
||||||
|
|||||||
@@ -126,7 +126,6 @@ def bottom_frame(self, parent, args=None):
|
|||||||
made[k] = m.group(1) + m.group(2) + ' ' + m.group(4)
|
made[k] = m.group(1) + m.group(2) + ' ' + m.group(4)
|
||||||
successMsg += (made["rom"] % (YES if (guiargs.create_rom) else NO)) + "\n"
|
successMsg += (made["rom"] % (YES if (guiargs.create_rom) else NO)) + "\n"
|
||||||
successMsg += (made["playthrough"] % (YES if (guiargs.calc_playthrough) else NO)) + "\n"
|
successMsg += (made["playthrough"] % (YES if (guiargs.calc_playthrough) else NO)) + "\n"
|
||||||
successMsg += (made["spoiler"] % (YES if (not guiargs.jsonout and guiargs.create_spoiler) else NO)) + "\n"
|
|
||||||
successMsg += (made["enemizer"] % (YES if needEnemizer else NO)) + "\n"
|
successMsg += (made["enemizer"] % (YES if needEnemizer else NO)) + "\n"
|
||||||
# FIXME: English
|
# FIXME: English
|
||||||
successMsg += ("Seed%s: %s" % ('s' if len(seeds) > 1 else "", ','.join(str(x) for x in seeds)))
|
successMsg += ("Seed%s: %s" % ('s' if len(seeds) > 1 else "", ','.join(str(x) for x in seeds)))
|
||||||
|
|||||||
Reference in New Issue
Block a user