Customizer main work
This commit is contained in:
329
source/classes/CustomSettings.py
Normal file
329
source/classes/CustomSettings.py
Normal file
@@ -0,0 +1,329 @@
|
||||
import os
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import yaml
|
||||
from yaml.representer import Representer
|
||||
from collections import defaultdict
|
||||
|
||||
import RaceRandom as random
|
||||
from BaseClasses import LocationType, DoorType
|
||||
from source.tools.MysteryUtils import roll_settings, get_weights
|
||||
|
||||
|
||||
class CustomSettings(object):
|
||||
|
||||
def __init__(self):
|
||||
self.file_source = None
|
||||
self.relative_dir = None
|
||||
self.world_rep = {}
|
||||
self.player_range = None
|
||||
|
||||
def load_yaml(self, file):
|
||||
self.file_source = load_yaml(file)
|
||||
head, filename = os.path.split(file)
|
||||
self.relative_dir = head
|
||||
|
||||
def determine_seed(self):
|
||||
if 'meta' not in self.file_source:
|
||||
return None
|
||||
meta = defaultdict(lambda: None, self.file_source['meta'])
|
||||
seed = meta['seed']
|
||||
if seed:
|
||||
random.seed(seed)
|
||||
return seed
|
||||
return None
|
||||
|
||||
def determine_players(self):
|
||||
if 'meta' not in self.file_source:
|
||||
return None
|
||||
meta = defaultdict(lambda: None, self.file_source['meta'])
|
||||
return meta['players']
|
||||
|
||||
def adjust_args(self, args):
|
||||
def get_setting(value, default):
|
||||
if value:
|
||||
return value
|
||||
return default
|
||||
if 'meta' in self.file_source:
|
||||
meta = defaultdict(lambda: None, self.file_source['meta'])
|
||||
args.multi = get_setting(meta['players'], args.multi)
|
||||
args.algorithm = get_setting(meta['algorithm'], args.algorithm)
|
||||
args.outputname = get_setting(meta['name'], args.outputname)
|
||||
args.bps = get_setting(meta['bps'], args.bps)
|
||||
args.suppress_rom = get_setting(meta['suppress_rom'], args.suppress_rom)
|
||||
args.names = get_setting(meta['names'], args.names)
|
||||
self.player_range = range(1, args.multi + 1)
|
||||
if 'settings' in self.file_source:
|
||||
for p in self.player_range:
|
||||
player_setting = self.file_source['settings'][p]
|
||||
if isinstance(player_setting, str):
|
||||
weights = get_weights(os.path.join(self.relative_dir, player_setting))
|
||||
settings = defaultdict(lambda: None, vars(roll_settings(weights)))
|
||||
else:
|
||||
settings = defaultdict(lambda: None, player_setting)
|
||||
args.shuffle[p] = get_setting(settings['shuffle'], args.shuffle[p])
|
||||
args.door_shuffle[p] = get_setting(settings['door_shuffle'], args.door_shuffle[p])
|
||||
args.logic[p] = get_setting(settings['logic'], args.logic[p])
|
||||
args.mode[p] = get_setting(settings['mode'], args.mode[p])
|
||||
args.swords[p] = get_setting(settings['swords'], args.swords[p])
|
||||
args.item_functionality[p] = get_setting(settings['item_functionality'], args.item_functionality[p])
|
||||
args.goal[p] = get_setting(settings['goal'], args.goal[p])
|
||||
args.difficulty[p] = get_setting(settings['difficulty'], args.difficulty[p])
|
||||
args.accessibility[p] = get_setting(settings['accessibility'], args.accessibility[p])
|
||||
args.retro[p] = get_setting(settings['retro'], args.retro[p])
|
||||
args.hints[p] = get_setting(settings['hints'], args.hints[p])
|
||||
args.shopsanity[p] = get_setting(settings['shopsanity'], args.shopsanity[p])
|
||||
args.dropshuffle[p] = get_setting(settings['dropshuffle'], args.dropshuffle[p])
|
||||
args.pottery[p] = get_setting(settings['pottery'], args.pottery[p])
|
||||
args.mixed_travel[p] = get_setting(settings['mixed_travel'], args.mixed_travel[p])
|
||||
args.standardize_palettes[p] = get_setting(settings['standardize_palettes'],
|
||||
args.standardize_palettes[p])
|
||||
args.intensity[p] = get_setting(settings['intensity'], args.intensity[p])
|
||||
args.dungeon_counters[p] = get_setting(settings['dungeon_counters'], args.dungeon_counters[p])
|
||||
args.crystals_gt[p] = get_setting(settings['crystals_gt'], args.crystals_gt[p])
|
||||
args.crystals_ganon[p] = get_setting(settings['crystals_ganon'], args.crystals_ganon[p])
|
||||
args.experimental[p] = get_setting(settings['experimental'], args.experimental[p])
|
||||
args.openpyramid[p] = get_setting(settings['openpyramid'], args.openpyramid[p])
|
||||
args.bigkeyshuffle[p] = get_setting(settings['bigkeyshuffle'], args.bigkeyshuffle[p])
|
||||
args.keyshuffle[p] = get_setting(settings['keyshuffle'], args.keyshuffle[p])
|
||||
args.mapshuffle[p] = get_setting(settings['mapshuffle'], args.mapshuffle[p])
|
||||
args.compassshuffle[p] = get_setting(settings['compassshuffle'], args.compassshuffle[p])
|
||||
args.shufflebosses[p] = get_setting(settings['shufflebosses'], args.shufflebosses[p])
|
||||
args.shuffleenemies[p] = get_setting(settings['shuffleenemies'], args.shuffleenemies[p])
|
||||
args.enemy_health[p] = get_setting(settings['enemy_health'], args.enemy_health[p])
|
||||
args.enemy_damage[p] = get_setting(settings['enemy_damage'], args.enemy_damage[p])
|
||||
args.shufflepots[p] = get_setting(settings['shufflepots'], args.shufflepots[p])
|
||||
args.bombbag[p] = get_setting(settings['bombbag'], args.bombbag[p])
|
||||
args.shufflelinks[p] = get_setting(settings['shufflelinks'], args.shufflelinks[p])
|
||||
args.restrict_boss_items[p] = get_setting(settings['restrict_boss_items'], args.restrict_boss_items[p])
|
||||
args.overworld_map[p] = get_setting(settings['overworld_map'], args.overworld_map[p])
|
||||
args.pseudoboots[p] = get_setting(settings['pseudoboots'], args.pseudoboots[p])
|
||||
args.triforce_goal[p] = get_setting(settings['triforce_goal'], args.triforce_goal[p])
|
||||
args.triforce_pool[p] = get_setting(settings['triforce_pool'], args.triforce_pool[p])
|
||||
args.beemizer[p] = get_setting(settings['beemizer'], args.beemizer[p])
|
||||
|
||||
# mystery usage
|
||||
args.usestartinventory[p] = get_setting(settings['usestartinventory'], args.usestartinventory[p])
|
||||
args.startinventory[p] = get_setting(settings['startinventory'], args.startinventory[p])
|
||||
|
||||
# rom adjust stuff
|
||||
args.sprite[p] = get_setting(settings['sprite'], args.sprite[p])
|
||||
args.disablemusic[p] = get_setting(settings['disablemusic'], args.disablemusic[p])
|
||||
args.quickswap[p] = get_setting(settings['quickswap'], args.quickswap[p])
|
||||
args.reduce_flashing[p] = get_setting(settings['reduce_flashing'], args.reduce_flashing[p])
|
||||
args.fastmenu[p] = get_setting(settings['fastmenu'], args.fastmenu[p])
|
||||
args.heartcolor[p] = get_setting(settings['heartcolor'], args.heartcolor[p])
|
||||
args.heartbeep[p] = get_setting(settings['heartbeep'], args.heartbeep[p])
|
||||
args.ow_palettes[p] = get_setting(settings['ow_palettes'], args.ow_palettes[p])
|
||||
args.uw_palettes[p] = get_setting(settings['uw_palettes'], args.uw_palettes[p])
|
||||
args.shuffle_sfx[p] = get_setting(settings['shuffle_sfx'], args.shuffle_sfx[p])
|
||||
|
||||
def get_item_pool(self):
|
||||
if 'item_pool' in self.file_source:
|
||||
return self.file_source['item_pool']
|
||||
return None
|
||||
|
||||
def get_placements(self):
|
||||
if 'placements' in self.file_source:
|
||||
return self.file_source['placements']
|
||||
return None
|
||||
|
||||
def get_entrances(self):
|
||||
if 'entrances' in self.file_source:
|
||||
return self.file_source['entrances']
|
||||
return None
|
||||
|
||||
def get_doors(self):
|
||||
if 'doors' in self.file_source:
|
||||
return self.file_source['doors']
|
||||
return None
|
||||
|
||||
def get_bosses(self):
|
||||
if 'bosses' in self.file_source:
|
||||
return self.file_source['bosses']
|
||||
return None
|
||||
|
||||
def get_start_inventory(self):
|
||||
if 'start_inventory' in self.file_source:
|
||||
return self.file_source['start_inventory']
|
||||
return None
|
||||
|
||||
def get_medallions(self):
|
||||
if 'medallions' in self.file_source:
|
||||
return self.file_source['medallions']
|
||||
return None
|
||||
|
||||
def create_from_world(self, world):
|
||||
self.player_range = range(1, world.players + 1)
|
||||
settings_dict, meta_dict = {}, {}
|
||||
self.world_rep['meta'] = meta_dict
|
||||
meta_dict['players'] = world.players
|
||||
meta_dict['algorithm'] = world.algorithm
|
||||
meta_dict['seed'] = world.seed
|
||||
self.world_rep['settings'] = settings_dict
|
||||
for p in self.player_range:
|
||||
settings_dict[p] = {}
|
||||
settings_dict[p]['shuffle'] = world.shuffle[p]
|
||||
settings_dict[p]['door_shuffle'] = world.doorShuffle[p]
|
||||
settings_dict[p]['intensity'] = world.intensity[p]
|
||||
settings_dict[p]['logic'] = world.logic[p]
|
||||
settings_dict[p]['mode'] = world.mode[p]
|
||||
settings_dict[p]['swords'] = world.swords[p]
|
||||
settings_dict[p]['difficulty'] = world.difficulty[p]
|
||||
settings_dict[p]['goal'] = world.goal[p]
|
||||
settings_dict[p]['accessibility'] = world.accessibility[p]
|
||||
settings_dict[p]['item_functionality'] = world.difficulty_adjustments[p]
|
||||
settings_dict[p]['retro'] = world.retro[p]
|
||||
settings_dict[p]['hints'] = world.hints[p]
|
||||
settings_dict[p]['shopsanity'] = world.shopsanity[p]
|
||||
settings_dict[p]['dropshuffle'] = world.dropshuffle[p]
|
||||
settings_dict[p]['pottery'] = world.pottery[p]
|
||||
settings_dict[p]['mixed_travel'] = world.mixed_travel[p]
|
||||
settings_dict[p]['standardize_palettes'] = world.standardize_palettes[p]
|
||||
settings_dict[p]['dungeon_counters'] = world.dungeon_counters[p]
|
||||
settings_dict[p]['crystals_gt'] = world.crystals_gt_orig[p]
|
||||
settings_dict[p]['crystals_ganon'] = world.crystals_ganon_orig[p]
|
||||
settings_dict[p]['experimental'] = world.experimental[p]
|
||||
settings_dict[p]['openpyramid'] = world.open_pyramid[p]
|
||||
settings_dict[p]['bigkeyshuffle'] = world.bigkeyshuffle[p]
|
||||
settings_dict[p]['keyshuffle'] = world.keyshuffle[p]
|
||||
settings_dict[p]['mapshuffle'] = world.mapshuffle[p]
|
||||
settings_dict[p]['compassshuffle'] = world.compassshuffle[p]
|
||||
settings_dict[p]['shufflebosses'] = world.boss_shuffle[p]
|
||||
settings_dict[p]['shuffleenemies'] = world.enemy_shuffle[p]
|
||||
settings_dict[p]['enemy_health'] = world.enemy_health[p]
|
||||
settings_dict[p]['enemy_damage'] = world.enemy_damage[p]
|
||||
settings_dict[p]['shufflepots'] = world.potshuffle[p]
|
||||
settings_dict[p]['bombbag'] = world.bombbag[p]
|
||||
settings_dict[p]['shufflelinks'] = world.shufflelinks[p]
|
||||
settings_dict[p]['overworld_map'] = world.overworld_map[p]
|
||||
settings_dict[p]['pseudoboots'] = world.pseudoboots[p]
|
||||
settings_dict[p]['triforce_goal'] = world.treasure_hunt_count[p]
|
||||
settings_dict[p]['triforce_pool'] = world.treasure_hunt_total[p]
|
||||
settings_dict[p]['beemizer'] = world.beemizer[p]
|
||||
|
||||
# rom adjust stuff
|
||||
# settings_dict[p]['sprite'] = world.sprite[p]
|
||||
# settings_dict[p]['disablemusic'] = world.disablemusic[p]
|
||||
# settings_dict[p]['quickswap'] = world.quickswap[p]
|
||||
# settings_dict[p]['reduce_flashing'] = world.reduce_flashing[p]
|
||||
# settings_dict[p]['fastmenu'] = world.fastmenu[p]
|
||||
# settings_dict[p]['heartcolor'] = world.heartcolor[p]
|
||||
# settings_dict[p]['heartbeep'] = world.heartbeep[p]
|
||||
# settings_dict[p]['ow_palettes'] = world.ow_palettes[p]
|
||||
# settings_dict[p]['uw_palettes'] = world.uw_palettes[p]
|
||||
# settings_dict[p]['shuffle_sfx'] = world.shuffle_sfx[p]
|
||||
# more settings?
|
||||
|
||||
def record_info(self, world):
|
||||
self.world_rep['bosses'] = bosses = {}
|
||||
self.world_rep['start_inventory'] = start_inv = {}
|
||||
for p in self.player_range:
|
||||
bosses[p] = {}
|
||||
start_inv[p] = []
|
||||
for dungeon in world.dungeons:
|
||||
for level, boss in dungeon.bosses.items():
|
||||
location = dungeon.name if level is None else f'{dungeon.name} ({level})'
|
||||
if boss and 'Agahnim' not in boss.name:
|
||||
bosses[dungeon.player][location] = boss.name
|
||||
for item in world.precollected_items:
|
||||
start_inv[item.player].append(item.name)
|
||||
|
||||
def record_item_pool(self, world):
|
||||
self.world_rep['item_pool'] = item_pool = {}
|
||||
self.world_rep['medallions'] = medallions = {}
|
||||
for p in self.player_range:
|
||||
item_pool[p] = defaultdict(int)
|
||||
medallions[p] = {}
|
||||
for item in world.itempool:
|
||||
item_pool[item.player][item.name] += 1
|
||||
for p, req_medals in world.required_medallions.items():
|
||||
medallions[p]['Misery Mire'] = req_medals[0]
|
||||
medallions[p]['Turtle Rock'] = req_medals[1]
|
||||
|
||||
def record_item_placements(self, world):
|
||||
self.world_rep['placements'] = placements = {}
|
||||
for p in self.player_range:
|
||||
placements[p] = {}
|
||||
for location in world.get_locations():
|
||||
if location.type != LocationType.Logical and not location.skip:
|
||||
if location.player != location.item.player:
|
||||
placements[location.player][location.name] = f'{location.item.name}#{location.item.player}'
|
||||
else:
|
||||
placements[location.player][location.name] = location.item.name
|
||||
|
||||
def record_entrances(self, world):
|
||||
self.world_rep['entrances'] = entrances = {}
|
||||
world.custom_entrances = {}
|
||||
for p in self.player_range:
|
||||
connections = entrances[p] = {}
|
||||
connections['entrances'] = {}
|
||||
connections['exits'] = {}
|
||||
connections['two-way'] = {}
|
||||
for key, data in world.spoiler.entrances.items():
|
||||
player = data['player'] if 'player' in data else 1
|
||||
connections = entrances[player]
|
||||
sub = 'two-way' if data['direction'] == 'both' else 'exits' if data['direction'] == 'exit' else 'entrances'
|
||||
connections[sub][data['entrance']] = data['exit']
|
||||
|
||||
def record_doors(self, world):
|
||||
self.world_rep['doors'] = doors = {}
|
||||
for p in self.player_range:
|
||||
meta_doors = doors[p] = {}
|
||||
lobbies = meta_doors['lobbies'] = {}
|
||||
door_map = meta_doors['doors'] = {}
|
||||
for portal in world.dungeon_portals[p]:
|
||||
lobbies[portal.name] = portal.door.name
|
||||
door_types = {DoorType.Normal, DoorType.SpiralStairs, DoorType.Interior}
|
||||
if world.intensity[p] > 1:
|
||||
door_types.update([DoorType.Open, DoorType.StraightStairs, DoorType.Ladder])
|
||||
door_kinds, skip = {}, set()
|
||||
for key, info in world.spoiler.doorTypes.items():
|
||||
if key[1] == p:
|
||||
if ' <-> ' in info['doorNames']:
|
||||
dns = info['doorNames'].split(' <-> ')
|
||||
for dn in dns:
|
||||
door_kinds[dn] = info['type'] # Key Door, Bomb Door, Dash Door
|
||||
else:
|
||||
door_kinds[info['doorNames']] = info['type']
|
||||
for door in world.doors:
|
||||
if door.player == p and not door.entranceFlag and door.type in door_types and door not in skip:
|
||||
if door.type == DoorType.Interior:
|
||||
if door.name in door_types:
|
||||
door_value = {'type': door_kinds[door.name]}
|
||||
door_map[door.name] = door_value # intra-tile note
|
||||
skip.add(door.dest)
|
||||
elif door.dest:
|
||||
if door.dest.dest == door:
|
||||
door_value = door.dest.name
|
||||
skip.add(door.dest)
|
||||
if door.name in door_kinds:
|
||||
door_value = {'dest': door_value, 'type': door_kinds[door.name]}
|
||||
if door.name not in door_kinds and door.dest.name in door_kinds:
|
||||
# tricky swap thing
|
||||
door_value = {'dest': door.name, 'type': door_kinds[door.dest.name]}
|
||||
door = door.dest # this is weird
|
||||
elif door.name in door_kinds:
|
||||
door_value = {'dest': door.dest.name, 'one-way': True, 'type': door_kinds[door.name]}
|
||||
else:
|
||||
door_value = {'dest': door.dest.name, 'one-way': True}
|
||||
door_map[door.name] = door_value
|
||||
|
||||
def record_medallions(self):
|
||||
pass
|
||||
|
||||
def write_to_file(self, destination):
|
||||
yaml.add_representer(defaultdict, Representer.represent_dict)
|
||||
with open(destination, 'w') as file:
|
||||
yaml.dump(self.world_rep, file)
|
||||
|
||||
|
||||
def load_yaml(path):
|
||||
try:
|
||||
if urllib.parse.urlparse(path).scheme:
|
||||
return yaml.load(urllib.request.urlopen(path), Loader=yaml.FullLoader)
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
return yaml.load(f, Loader=yaml.SafeLoader)
|
||||
except Exception as e:
|
||||
raise Exception(f'Failed to read customizer file: {e}')
|
||||
|
||||
Reference in New Issue
Block a user