def build_world_graphs(settings, window=dummy_window()): logger = logging.getLogger('') worlds = [] for i in range(0, settings.world_count): worlds.append(World(i, settings)) window.update_status('Creating the Worlds') for id, world in enumerate(worlds): logger.info('Generating World %d.' % (id + 1)) window.update_progress(0 + 1 * (id + 1) / settings.world_count) logger.info('Creating Overworld') if settings.logic_rules == 'glitched': overworld_data = os.path.join(data_path('Glitched World'), 'Overworld.json') else: overworld_data = os.path.join(data_path('World'), 'Overworld.json') # Compile the json rules based on settings world.load_regions_from_json(overworld_data) create_dungeons(world) world.create_internal_locations() if settings.shopsanity != 'off': world.random_shop_prices() world.set_scrub_prices() window.update_progress(0 + 4 * (id + 1) / settings.world_count) logger.info('Calculating Access Rules.') set_rules(world) window.update_progress(0 + 5 * (id + 1) / settings.world_count) logger.info('Generating Item Pool.') generate_itempool(world) set_shop_rules(world) set_drop_location_names(world) world.fill_bosses() if settings.triforce_hunt: settings.distribution.configure_triforce_hunt(worlds) logger.info('Setting Entrances.') set_entrances(worlds) return worlds
def main(settings, window=dummy_window()): start = time.process_time() logger = logging.getLogger('') worlds = [] allowed_tricks = {} for trick in logic_tricks.values(): settings.__dict__[ trick['name']] = trick['name'] in settings.allowed_tricks settings.load_distribution() # we load the rom before creating the seed so that error get caught early if settings.compress_rom == 'None' and not settings.create_spoiler: raise Exception( '`No Output` must have spoiler enabled to produce anything.') if settings.compress_rom != 'None': window.update_status('Loading ROM') rom = Rom(settings.rom) if not settings.world_count: settings.world_count = 1 if settings.world_count < 1 or settings.world_count > 255: raise Exception('World Count must be between 1 and 255') if settings.player_num > settings.world_count or settings.player_num < 1: if settings.compress_rom not in ['None', 'Patch']: raise Exception('Player Num must be between 1 and %d' % settings.world_count) else: settings.player_num = 1 logger.info('OoT Randomizer Version %s - Seed: %s\n\n', __version__, settings.seed) settings.remove_disabled() random.seed(settings.numeric_seed) settings.resolve_random_settings() for i in range(0, settings.world_count): worlds.append(World(settings)) window.update_status('Creating the Worlds') for id, world in enumerate(worlds): world.id = id world.distribution = settings.distribution.world_dists[id] logger.info('Generating World %d.' % id) window.update_progress(0 + 1 * (id + 1) / settings.world_count) logger.info('Creating Overworld') # Determine MQ Dungeons dungeon_pool = list(world.dungeon_mq) dist_num_mq = world.distribution.configure_dungeons( world, dungeon_pool) if world.mq_dungeons_random: for dungeon in dungeon_pool: world.dungeon_mq[dungeon] = random.choice([True, False]) world.mq_dungeons = list(world.dungeon_mq.values()).count(True) else: mqd_picks = random.sample(dungeon_pool, world.mq_dungeons - dist_num_mq) for dung in mqd_picks: world.dungeon_mq[dung] = True if settings.logic_rules == 'glitched': overworld_data = os.path.join(data_path('Glitched World'), 'Overworld.json') else: overworld_data = os.path.join(data_path('World'), 'Overworld.json') world.load_regions_from_json(overworld_data) create_dungeons(world) if settings.shopsanity != 'off': world.random_shop_prices() world.set_scrub_prices() window.update_progress(0 + 4 * (id + 1) / settings.world_count) logger.info('Calculating Access Rules.') set_rules(world) window.update_progress(0 + 5 * (id + 1) / settings.world_count) logger.info('Generating Item Pool.') generate_itempool(world) set_shop_rules(world) set_drop_location_names(world) logger.info('Setting Entrances.') set_entrances(worlds) window.update_status('Placing the Items') logger.info('Fill the world.') distribute_items_restrictive(window, worlds) window.update_progress(35) spoiler = Spoiler(worlds) cosmetics_log = None if settings.create_spoiler: window.update_status('Calculating Spoiler Data') logger.info('Calculating playthrough.') create_playthrough(spoiler) window.update_progress(50) if settings.create_spoiler or settings.hints != 'none': window.update_status('Calculating Hint Data') State.update_required_items(spoiler) for world in worlds: world.update_useless_areas(spoiler) buildGossipHints(spoiler, world) window.update_progress(55) spoiler.build_file_hash() logger.info('Patching ROM.') settings_string_hash = hashlib.sha1( settings.settings_string.encode('utf-8')).hexdigest().upper()[:5] if settings.output_file: outfilebase = settings.output_file elif settings.world_count > 1: outfilebase = 'OoT_%s_%s_W%d' % (settings_string_hash, settings.seed, settings.world_count) else: outfilebase = 'OoT_%s_%s' % (settings_string_hash, settings.seed) output_dir = default_output_path(settings.output_dir) if settings.compress_rom == 'Patch': rng_state = random.getstate() file_list = [] window.update_progress(65) for world in worlds: if settings.world_count > 1: window.update_status('Patching ROM: Player %d' % (world.id + 1)) patchfilename = '%sP%d.zpf' % (outfilebase, world.id + 1) else: window.update_status('Patching ROM') patchfilename = '%s.zpf' % outfilebase random.setstate(rng_state) patch_rom(spoiler, world, rom, outfilebase) cosmetics_log = patch_cosmetics(settings, rom) window.update_progress(65 + 20 * (world.id + 1) / settings.world_count) window.update_status('Creating Patch File') output_path = os.path.join(output_dir, patchfilename) file_list.append(patchfilename) create_patch_file(rom, output_path) rom.restore() window.update_progress(65 + 30 * (world.id + 1) / settings.world_count) if settings.create_cosmetics_log and cosmetics_log: window.update_status('Creating Cosmetics Log') if settings.world_count > 1: cosmetics_log_filename = "%sP%d_Cosmetics.txt" % ( outfilebase, world.id + 1) else: cosmetics_log_filename = '%s_Cosmetics.txt' % outfilebase cosmetics_log.to_file( os.path.join(output_dir, cosmetics_log_filename)) file_list.append(cosmetics_log_filename) cosmetics_log = None if settings.world_count > 1: window.update_status('Creating Patch Archive') output_path = os.path.join(output_dir, '%s.zpfz' % outfilebase) with zipfile.ZipFile(output_path, mode="w") as patch_archive: for file in file_list: file_path = os.path.join(output_dir, file) patch_archive.write(file_path, file.replace(outfilebase, ''), compress_type=zipfile.ZIP_DEFLATED) for file in file_list: os.remove(os.path.join(output_dir, file)) logger.info("Created patchfile at: %s" % output_path) window.update_progress(95) elif settings.compress_rom != 'None': window.update_status('Patching ROM') patch_rom(spoiler, worlds[settings.player_num - 1], rom, outfilebase) cosmetics_log = patch_cosmetics(settings, rom) window.update_progress(65) window.update_status('Saving Uncompressed ROM') if settings.world_count > 1: filename = "%sP%d.z64" % (outfilebase, settings.player_num) else: filename = '%s.z64' % outfilebase output_path = os.path.join(output_dir, filename) rom.write_to_file(output_path) if settings.compress_rom == 'True': window.update_status('Compressing ROM') logger.info('Compressing ROM.') if is_bundled(): compressor_path = "." else: compressor_path = "Compress" if platform.system() == 'Windows': if 8 * struct.calcsize("P") == 64: compressor_path += "\\Compress.exe" else: compressor_path += "\\Compress32.exe" elif platform.system() == 'Linux': if platform.uname()[4] == 'aarch64' or platform.uname( )[4] == 'arm64': compressor_path += "/Compress_ARM64" else: compressor_path += "/Compress" elif platform.system() == 'Darwin': compressor_path += "/Compress.out" else: compressor_path = "" logger.info('OS not supported for compression') output_compress_path = output_path[:output_path. rfind('.')] + '-comp.z64' if compressor_path != "": run_process( window, logger, [compressor_path, output_path, output_compress_path]) os.remove(output_path) logger.info("Created compessed rom at: %s" % output_compress_path) else: logger.info("Created uncompessed rom at: %s" % output_path) window.update_progress(95) for world in worlds: for info in setting_infos: world.settings.__dict__[info.name] = world.__dict__[info.name] settings.distribution.update_spoiler(spoiler) if settings.create_spoiler: window.update_status('Creating Spoiler Log') spoiler_path = os.path.join(output_dir, '%s_Spoiler.json' % outfilebase) settings.distribution.to_file(spoiler_path) logger.info("Created spoiler log at: %s" % ('%s_Spoiler.json' % outfilebase)) else: window.update_status('Creating Settings Log') settings_path = os.path.join(output_dir, '%s_Settings.json' % outfilebase) settings.distribution.to_file(settings_path) logger.info("Created settings log at: %s" % ('%s_Settings.json' % outfilebase)) if settings.create_cosmetics_log and cosmetics_log: window.update_status('Creating Cosmetics Log') if settings.world_count > 1 and not settings.output_file: filename = "%sP%d_Cosmetics.txt" % (outfilebase, settings.player_num) else: filename = '%s_Cosmetics.txt' % outfilebase cosmetic_path = os.path.join(output_dir, filename) cosmetics_log.to_file(cosmetic_path) logger.info("Created cosmetic log at: %s" % cosmetic_path) window.update_progress(100) if cosmetics_log and cosmetics_log.error: window.update_status( 'Success: Rom patched successfully. Some cosmetics could not be applied.' ) else: window.update_status('Success: Rom patched successfully') logger.info('Done. Enjoy.') logger.debug('Total Time: %s', time.process_time() - start) return worlds[settings.player_num - 1]
def generate(settings, window): logger = logging.getLogger('') worlds = [] for i in range(0, settings.world_count): worlds.append(World(i, settings)) window.update_status('Creating the Worlds') for id, world in enumerate(worlds): logger.info('Generating World %d.' % (id + 1)) window.update_progress(0 + 1 * (id + 1) / settings.world_count) logger.info('Creating Overworld') if settings.logic_rules == 'glitched': overworld_data = os.path.join(data_path('Glitched World'), 'Overworld.json') else: overworld_data = os.path.join(data_path('World'), 'Overworld.json') # Compile the json rules based on settings world.load_regions_from_json(overworld_data) create_dungeons(world) world.create_internal_locations() if settings.shopsanity != 'off': world.random_shop_prices() world.set_scrub_prices() window.update_progress(0 + 4 * (id + 1) / settings.world_count) logger.info('Calculating Access Rules.') set_rules(world) window.update_progress(0 + 5 * (id + 1) / settings.world_count) logger.info('Generating Item Pool.') generate_itempool(world) set_shop_rules(world) set_drop_location_names(world) world.fill_bosses() if settings.triforce_hunt: settings.distribution.configure_triforce_hunt(worlds) logger.info('Setting Entrances.') set_entrances(worlds) window.update_status('Placing the Items') logger.info('Fill the world.') distribute_items_restrictive(window, worlds) window.update_progress(35) spoiler = Spoiler(worlds) if settings.create_spoiler: window.update_status('Calculating Spoiler Data') logger.info('Calculating playthrough.') create_playthrough(spoiler) window.update_progress(50) if settings.create_spoiler or settings.hints != 'none': window.update_status('Calculating Hint Data') logger.info('Calculating hint data.') State.update_required_items(spoiler) buildGossipHints(spoiler, worlds) window.update_progress(55) spoiler.build_file_hash() return spoiler