def main(args, seed=None): if args.outputpath: os.makedirs(args.outputpath, exist_ok=True) output_path.cached_path = args.outputpath start = time.process_time() # initialize the world world = World(args.multi, args.shuffle, args.logic, args.mode, args.swords, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, args.accessibility, args.shuffleganon, args.retro, args.custom, args.customitemarray, args.hints) logger = logging.getLogger('') if seed is None: random.seed(None) world.seed = random.randint(0, 999999999) else: world.seed = int(seed) random.seed(world.seed) world.remote_items = args.remote_items.copy() 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() world.boss_shuffle = args.shufflebosses.copy() world.enemy_shuffle = args.shuffleenemies.copy() world.enemy_health = args.enemy_health.copy() world.enemy_damage = args.enemy_damage.copy() world.beemizer = args.beemizer.copy() world.rom_seeds = { player: random.randint(0, 999999999) for player in range(1, world.players + 1) } logger.info('ALttP Entrance Randomizer Version %s - Seed: %s\n', __version__, world.seed) parsed_names = parse_player_names(args.names, world.players, args.teams) world.teams = len(parsed_names) for i, team in enumerate(parsed_names, 1): if world.players > 1: logger.info('%s%s', 'Team%d: ' % i if world.teams > 1 else 'Players: ', ', '.join(team)) for player, name in enumerate(team, 1): world.player_names[player].append(name) logger.info('') for player in range(1, world.players + 1): world.difficulty_requirements[player] = difficulties[ world.difficulty[player]] if world.mode[player] == 'standard' and world.enemy_shuffle[ player] != 'none': world.escape_assist[player].append( 'bombs' ) # enemized escape assumes infinite bombs available and will likely be unbeatable without it for tok in filter(None, args.startinventory[player].split(',')): item = ItemFactory(tok.strip(), player) if item: world.push_precollected(item) if world.mode[player] != 'inverted': create_regions(world, player) else: create_inverted_regions(world, player) create_shops(world, player) create_dungeons(world, player) logger.info('Shuffling the World about.') for player in range(1, world.players + 1): if world.mode[player] != 'inverted': link_entrances(world, player) mark_light_world_regions(world, player) else: link_inverted_entrances(world, player) mark_dark_world_regions(world, player) logger.info('Generating Item Pool.') for player in range(1, world.players + 1): generate_itempool(world, player) logger.info('Calculating Access Rules.') for player in range(1, world.players + 1): set_rules(world, player) logger.info('Placing Dungeon Prizes.') fill_prizes(world) logger.info('Placing Dungeon Items.') shuffled_locations = None 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) else: fill_dungeons(world) logger.info('Fill the world.') if args.algorithm == 'flood': flood_items( world) # different algo, biased towards early game progress items elif args.algorithm == 'vt21': distribute_items_cutoff(world, 1) elif args.algorithm == 'vt22': distribute_items_cutoff(world, 0.66) elif args.algorithm == 'freshness': distribute_items_staleness(world) elif args.algorithm == 'vt25': distribute_items_restrictive(world, False) elif args.algorithm == 'vt26': distribute_items_restrictive(world, True, shuffled_locations) elif args.algorithm == 'balanced': distribute_items_restrictive(world, True) if world.players > 1: logger.info('Balancing multiworld progression.') balance_multiworld_progression(world) logger.info('Patching ROM.') outfilebase = 'ER_%s' % (args.outputname if args.outputname else world.seed) rom_names = [] jsonout = {} if not args.suppress_rom: for team in range(world.teams): for player in range(1, world.players + 1): sprite_random_on_hit = type( args.sprite[player]) is str and args.sprite[player].lower( ) == 'randomonhit' use_enemizer = (world.boss_shuffle[player] != 'none' or world.enemy_shuffle[player] != 'none' or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default' or args.shufflepots[player] or sprite_random_on_hit) rom = JsonRom() if args.jsonout or use_enemizer else LocalRom( args.rom) patch_rom(world, rom, player, team, use_enemizer) if use_enemizer and (args.enemizercli or not args.jsonout): patch_enemizer(world, player, rom, args.rom, args.enemizercli, args.shufflepots[player], sprite_random_on_hit) if not args.jsonout: rom = LocalRom.fromJsonRom(rom, args.rom, 0x400000) if args.race: patch_race_rom(rom) rom_names.append((player, team, list(rom.name))) world.spoiler.hashes[(player, team)] = get_hash_string(rom.hash) apply_rom_settings( rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player], args.fastmenu[player], args.disablemusic[player], args.sprite[player], args.ow_palettes[player], args.uw_palettes[player]) if args.jsonout: jsonout[f'patch_t{team}_p{player}'] = rom.patches else: 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 '') outfilepname = f'_T{team+1}' if world.teams > 1 else '' if world.players > 1: outfilepname += f'_P{player}' if world.players > 1 or world.teams > 1: outfilepname += f"_{world.player_names[player][team].replace(' ', '_')}" if world.player_names[ player][team] != 'Player %d' % player else '' outfilesuffix = ( '_%s_%s-%s-%s-%s%s_%s-%s%s%s%s%s' % (world.logic[player], world.difficulty[player], world.difficulty_adjustments[player], world.mode[player], world.goal[player], "" if world.timer in ['none', 'display'] else "-" + world.timer, world.shuffle[player], world.algorithm, mcsb_name, "-retro" if world.retro[player] else "", "-prog_" + world.progressive if world.progressive in ['off', 'random'] else "", "-nohints" if not world.hints[player] else "") ) if not args.outputname else '' rom.write_to_file( output_path( f'{outfilebase}{outfilepname}{outfilesuffix}.sfc')) multidata = zlib.compress( json.dumps({ "names": parsed_names, "roms": rom_names, "remote_items": [ player for player in range(1, world.players + 1) if world.remote_items[player] ], "locations": [((location.address, location.player), (location.item.code, location.item.player)) for location in world.get_filled_locations() if type(location.address) is int] }).encode("utf-8")) if args.jsonout: jsonout["multidata"] = list(multidata) else: with open(output_path('%s_multidata' % outfilebase), 'wb') as f: f.write(multidata) if args.create_spoiler and not args.jsonout: world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) if not args.skip_playthrough: logger.info('Calculating playthrough.') create_playthrough(world) if args.jsonout: print(json.dumps({**jsonout, 'spoiler': world.spoiler.to_json()})) elif args.create_spoiler and not args.skip_playthrough: world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) logger.info('Done. Enjoy.') logger.debug('Total Time: %s', time.process_time() - start) return world
def _gen_rom(team: int, player: int): sprite_random_on_hit = type( args.sprite[player]) is str and args.sprite[player].lower( ) == 'randomonhit' use_enemizer = (world.boss_shuffle[player] != 'none' or world.enemy_shuffle[player] != 'none' or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default' or args.shufflepots[player] or sprite_random_on_hit) rom = JsonRom() if args.jsonout or use_enemizer else LocalRom( args.rom, extendedmsu=args.extendedmsu[player]) patch_rom(world, rom, player, team, use_enemizer) if use_enemizer and (args.enemizercli or not args.jsonout): patch_enemizer(world, player, rom, args.rom, args.enemizercli, args.shufflepots[player], sprite_random_on_hit, extendedmsu=args.extendedmsu[player]) if not args.jsonout: rom = LocalRom.fromJsonRom(rom, args.rom, 0x400000, args.extendedmsu[player]) if args.race: patch_race_rom(rom) world.spoiler.hashes[(player, team)] = get_hash_string(rom.hash) apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player], args.fastmenu[player], args.disablemusic[player], args.sprite[player], args.ow_palettes[player], args.uw_palettes[player]) if args.jsonout: jsonout[f'patch_t{team}_p{player}'] = rom.patches else: 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 '') outfilepname = f'_T{team + 1}' if world.teams > 1 else '' if world.players > 1: outfilepname += f'_P{player}' if world.players > 1 or world.teams > 1: outfilepname += f"_{world.player_names[player][team].replace(' ', '_')}" if world.player_names[ player][team] != 'Player%d' % player else '' outfilesuffix = ( '_%s_%s-%s-%s-%s%s_%s-%s%s%s%s%s' % (world.logic[player], world.difficulty[player], world.difficulty_adjustments[player], world.mode[player], world.goal[player], "" if world.timer[player] in [False, 'display'] else "-" + world.timer[player], world.shuffle[player], world.algorithm, mcsb_name, "-retro" if world.retro[player] else "", "-prog_" + world.progressive[player] if world.progressive[player] in ['off', 'random'] else "", "-nohints" if not world.hints[player] else "") ) if not args.outputname else '' rompath = output_path( f'{outfilebase}{outfilepname}{outfilesuffix}.sfc') rom.write_to_file(rompath) if args.create_diff: import Patch Patch.create_patch_file(rompath) return (player, team, list(rom.name))