def patch_mesh(self, rom: LocalRom, mesh: CollisionMesh): start = self.file.start final_cams = [] # build final camera data for cam in self.coldelta.cams: data = cam['Data'] pos = cam['PositionIndex'] if pos < 0: final_cams.append((data, 0)) else: addr = start + (mesh.camera_data_addr & 0xFFFFFF) seg_off = rom.read_int32(addr + (pos * 8) + 4) final_cams.append((data, seg_off)) types_move_addr = 0 # if data can't fit within the old mesh space, append camera data if self.coldelta.is_larger: types_move_addr = mesh.camera_data_addr # append to end of file self.write_cam_data(rom, self.file.end, final_cams) mesh.camera_data_addr = get_segment_address( 2, self.file.end - self.file.start) self.file.end += len(final_cams) * 8 else: types_move_addr = mesh.camera_data_addr + (len(final_cams) * 8) # append in place addr = self.file.start + (mesh.camera_data_addr & 0xFFFFFF) self.write_cam_data(rom, addr, final_cams) # if polytypes needs to be moved, do so if (types_move_addr != mesh.polytypes_addr): a_start = self.file.start + (mesh.polytypes_addr & 0xFFFFFF) b_start = self.file.start + (types_move_addr & 0xFFFFFF) size = mesh.polytypes * 8 rom.buffer[b_start:b_start + size] = rom.buffer[a_start:a_start + size] mesh.polytypes_addr = types_move_addr # patch polytypes for item in self.coldelta.polytypes: id = item['Id'] high = item['High'] low = item['Low'] addr = self.file.start + (mesh.polytypes_addr & 0xFFFFFF) + (id * 8) rom.write_int32s(addr, [high, low]) # patch poly data for item in self.coldelta.polys: id = item['Id'] t = item['Type'] flags = item['Flags'] addr = self.file.start + (mesh.poly_addr & 0xFFFFFF) + (id * 0x10) vert_bit = rom.read_byte(addr + 0x02) & 0x1F # VertexA id data rom.write_int16(addr, t) rom.write_byte(addr + 0x02, (flags << 5) + vert_bit) # Write Mesh to Scene mesh.write_to_scene(rom, self.file.start)
def main(args, seed=None): start = time.perf_counter() # 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, not args.nodungeonitems, args.accessibility, args.shuffleganon, args.quickswap, args.fastmenu, args.disablemusic, args.keysanity, args.retro, args.custom, args.customitemarray, args.shufflebosses, 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.crystals_needed_for_ganon = random.randint( 0, 7) if args.crystals_ganon == 'random' else int(args.crystals_ganon) world.crystals_needed_for_gt = random.randint( 0, 7) if args.crystals_gt == 'random' else int(args.crystals_gt) 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\n', __version__, world.seed) world.difficulty_requirements = difficulties[world.difficulty] if world.mode != 'inverted': for player in range(1, world.players + 1): create_regions(world, player) create_dungeons(world, player) else: for player in range(1, world.players + 1): create_inverted_regions(world, player) create_dungeons(world, player) logger.info('Shuffling the World about.') if world.mode != 'inverted': for player in range(1, world.players + 1): link_entrances(world, player) mark_light_world_regions(world) else: for player in range(1, world.players + 1): link_inverted_entrances(world, player) mark_dark_world_regions(world) 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 args.keysanity: 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, 0) elif args.algorithm == 'vt26': distribute_items_restrictive(world, gt_filler(world), shuffled_locations) elif args.algorithm == 'balanced': distribute_items_restrictive(world, gt_filler(world)) if world.players > 1: logger.info('Balancing multiworld progression.') balance_multiworld_progression(world) logger.info('Patching ROM.') if args.sprite is not None: if isinstance(args.sprite, Sprite): sprite = args.sprite else: sprite = Sprite(args.sprite) else: sprite = None outfilebase = 'ER_%s_%s-%s-%s-%s%s_%s-%s%s%s%s%s_%s' % ( world.logic, world.difficulty, world.difficulty_adjustments, world.mode, world.goal, "" if world.timer in ['none', 'display'] else "-" + world.timer, world.shuffle, world.algorithm, "-keysanity" if world.keysanity else "", "-retro" if world.retro else "", "-prog_" + world.progressive if world.progressive in ['off', 'random'] else "", "-nohints" if not world.hints else "", world.seed) use_enemizer = args.enemizercli and ( args.shufflebosses != 'none' or args.shuffleenemies or args.enemy_health != 'default' or args.enemy_health != 'default' or args.enemy_damage or args.shufflepalette or args.shufflepots) jsonout = {} if not args.suppress_rom: if world.players > 1: raise NotImplementedError( "Multiworld rom writes have not been implemented") else: player = 1 local_rom = None if args.jsonout: rom = JsonRom() else: if use_enemizer: local_rom = LocalRom(args.rom) rom = JsonRom() else: rom = LocalRom(args.rom) patch_rom(world, player, rom) enemizer_patch = [] if use_enemizer: enemizer_patch = get_enemizer_patch( world, player, rom, args.rom, args.enemizercli, args.shuffleenemies, args.enemy_health, args.enemy_damage, args.shufflepalette, args.shufflepots) if args.jsonout: jsonout['patch'] = rom.patches if use_enemizer: jsonout['enemizer' % player] = enemizer_patch else: if use_enemizer: local_rom.patch_enemizer( rom.patches, os.path.join(os.path.dirname(args.enemizercli), "enemizerBasePatch.json"), enemizer_patch) rom = local_rom apply_rom_settings(rom, args.heartbeep, args.heartcolor, world.quickswap, world.fastmenu, world.disable_music, sprite) rom.write_to_file(output_path('%s.sfc' % outfilebase)) 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.perf_counter() - start) return world
def main(args, seed=None): start = time.clock() # initialize the world world = World(args.shuffle, args.logic, args.mode, args.difficulty, args.timer, args.progressive, args.goal, args.algorithm, not args.nodungeonitems, args.beatableonly, args.shuffleganon, args.quickswap, args.fastmenu, args.disablemusic, args.keysanity, args.retro, args.custom, args.customitemarray) 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) logger.info('ALttP Entrance Randomizer Version %s - Seed: %s\n\n', __version__, world.seed) world.difficulty_requirements = difficulties[world.difficulty] create_regions(world) create_dungeons(world) logger.info('Shuffling the World about.') link_entrances(world) mark_light_world_regions(world) logger.info('Calculating Access Rules.') set_rules(world) logger.info('Generating Item Pool.') generate_itempool(world) logger.info('Placing Dungeon Items.') shuffled_locations = None if args.algorithm in ['balanced', 'vt26'] or args.keysanity: 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, 0) elif args.algorithm == 'vt26': distribute_items_restrictive(world, gt_filler(world), shuffled_locations) elif args.algorithm == 'balanced': distribute_items_restrictive(world, gt_filler(world)) logger.info('Calculating playthrough.') create_playthrough(world) logger.info('Patching ROM.') if args.sprite is not None: if isinstance(args.sprite, Sprite): sprite = args.sprite else: sprite = Sprite(args.sprite) else: sprite = None outfilebase = 'ER_%s_%s-%s-%s%s_%s-%s%s%s%s_%s' % (world.logic, world.difficulty, world.mode, world.goal, "" if world.timer in ['none', 'display'] else "-" + world.timer, world.shuffle, world.algorithm, "-keysanity" if world.keysanity else "", "-retro" if world.retro else "", "-prog_" + world.progressive if world.progressive in ['off', 'random'] else "", world.seed) if not args.suppress_rom: if args.jsonout: rom = JsonRom() else: rom = LocalRom(args.rom) patch_rom(world, rom, bytearray(logic_hash), args.heartbeep, args.heartcolor, sprite) if args.jsonout: print(json.dumps({'patch': rom.patches, 'spoiler': world.spoiler.to_json()})) else: rom.write_to_file(args.jsonout or output_path('%s.sfc' % outfilebase)) if args.create_spoiler and not args.jsonout: world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) logger.info('Done. Enjoy.') logger.debug('Total Time: %s', time.clock() - start) return world
def write_to_minimap(self, rom: LocalRom, addr): rom.write_sbyte(addr, self.flag) rom.write_byte(addr + 1, self.x) rom.write_byte(addr + 2, self.y)
def main(settings, window=dummy_window()): start = time.clock() logger = logging.getLogger('') # verify that the settings are valid if settings.free_scarecrow: verify_scarecrow_song_str(settings.scarecrow_song, settings.ocarina_songs) # initialize the world worlds = [] if settings.compress_rom == 'None': settings.create_spoiler = True settings.update() if not settings.world_count: settings.world_count = 1 if settings.world_count < 1: raise Exception('World Count must be at least 1') if settings.player_num > settings.world_count or settings.player_num < 1: raise Exception('Player Num must be between 1 and %d' % settings.world_count) for i in range(0, settings.world_count): worlds.append(World(settings)) random.seed(worlds[0].numeric_seed) logger.info('OoT Randomizer Version %s - Seed: %s\n\n', __version__, worlds[0].seed) window.update_status('Creating the Worlds') for id, world in enumerate(worlds): world.id = id logger.info('Generating World %d.' % id) window.update_progress(0 + (((id + 1) / settings.world_count) * 1)) logger.info('Creating Overworld') if world.quest == 'master': for dungeon in world.dungeon_mq: world.dungeon_mq[dungeon] = True elif world.quest == 'mixed': for dungeon in world.dungeon_mq: world.dungeon_mq[dungeon] = random.choice([True, False]) else: for dungeon in world.dungeon_mq: world.dungeon_mq[dungeon] = False create_regions(world) window.update_progress(0 + (((id + 1) / settings.world_count) * 2)) logger.info('Creating Dungeons') create_dungeons(world) window.update_progress(0 + (((id + 1) / settings.world_count) * 3)) logger.info('Linking Entrances') link_entrances(world) if settings.shopsanity != 'off': world.random_shop_prices() window.update_progress(0 + (((id + 1) / settings.world_count) * 4)) logger.info('Calculating Access Rules.') set_rules(world) window.update_progress(0 + (((id + 1) / settings.world_count) * 5)) logger.info('Generating Item Pool.') generate_itempool(world) window.update_status('Placing the Items') logger.info('Fill the world.') distribute_items_restrictive(window, worlds) window.update_progress(35) if settings.create_spoiler: window.update_status('Calculating Spoiler Data') logger.info('Calculating playthrough.') create_playthrough(worlds) window.update_progress(50) if settings.hints != 'none': window.update_status('Calculating Hint Data') CollectionState.update_required_items(worlds) buildGossipHints(worlds[settings.player_num - 1]) window.update_progress(55) logger.info('Patching ROM.') if settings.world_count > 1: outfilebase = 'OoT_%s_%s_W%dP%d' % ( worlds[0].settings_string, worlds[0].seed, worlds[0].world_count, worlds[0].player_num) else: outfilebase = 'OoT_%s_%s' % (worlds[0].settings_string, worlds[0].seed) output_dir = default_output_path(settings.output_dir) if settings.compress_rom != 'None': window.update_status('Patching ROM') rom = LocalRom(settings) patch_rom(worlds[settings.player_num - 1], rom) window.update_progress(65) rom_path = os.path.join(output_dir, '%s.z64' % outfilebase) window.update_status('Saving Uncompressed ROM') rom.write_to_file(rom_path) if settings.compress_rom == 'True': window.update_status('Compressing ROM') logger.info('Compressing ROM.') compressor_path = "" if platform.system() == 'Windows': if 8 * struct.calcsize("P") == 64: compressor_path = "Compress\\Compress.exe" else: compressor_path = "Compress\\Compress32.exe" elif platform.system() == 'Linux': compressor_path = "Compress/Compress" elif platform.system() == 'Darwin': compressor_path = "Compress/Compress.out" else: logger.info('OS not supported for compression') run_process(window, logger, [ compressor_path, rom_path, os.path.join(output_dir, '%s-comp.z64' % outfilebase) ]) os.remove(rom_path) window.update_progress(95) if settings.create_spoiler: window.update_status('Creating Spoiler Log') worlds[settings.player_num - 1].spoiler.to_file( os.path.join(output_dir, '%s_Spoiler.txt' % outfilebase)) window.update_progress(100) window.update_status('Success: Rom patched successfully') logger.info('Done. Enjoy.') logger.debug('Total Time: %s', time.clock() - start) return worlds[settings.player_num - 1]
def main(args): start_time = time.perf_counter() # initialize the world world = World(1, 'vanilla', 'noglitches', 'standard', 'normal', 'none', 'on', 'ganon', 'freshness', False, False, False, args.quickswap, args.fastmenu, args.disablemusic, False, False, False, None, 'none', False) logger = logging.getLogger('') hasher = hashlib.md5() with open(args.plando, 'rb') as plandofile: buf = plandofile.read() hasher.update(buf) world.seed = int(hasher.hexdigest(), 16) % 1000000000 random.seed(world.seed) logger.info('ALttP Plandomizer Version %s - Seed: %s\n\n', __version__, args.plando) world.difficulty_requirements = difficulties[world.difficulty] create_regions(world, 1) create_dungeons(world, 1) link_entrances(world, 1) logger.info('Calculating Access Rules.') set_rules(world, 1) logger.info('Fill the world.') text_patches = [] fill_world(world, args.plando, text_patches) if world.get_entrance( 'Dam', 1).connected_region.name != 'Dam' or world.get_entrance( 'Swamp Palace', 1).connected_region.name != 'Swamp Palace (Entrance)': world.swamp_patch_required[1] = True logger.info('Calculating playthrough.') try: create_playthrough(world) except RuntimeError: if args.ignore_unsolvable: pass else: raise logger.info('Patching ROM.') if args.sprite is not None: sprite = Sprite(args.sprite) else: sprite = None rom = LocalRom(args.rom) patch_rom(world, 1, rom, args.heartbeep, args.heartcolor, sprite) for textname, texttype, text in text_patches: if texttype == 'text': write_string_to_rom(rom, textname, text) #elif texttype == 'credit': # write_credits_string_to_rom(rom, textname, text) outfilebase = 'Plando_%s_%s' % (os.path.splitext( os.path.basename(args.plando))[0], world.seed) rom.write_to_file('%s.sfc' % outfilebase) if args.create_spoiler: world.spoiler.to_file('%s_Spoiler.txt' % outfilebase) logger.info('Done. Enjoy.') logger.debug('Total Time: %s', time.perf_counter() - start_time) return world
def __init__(self, rom: LocalRom, start, offset): self.offset = offset self.poly_addr = rom.read_int32(start + offset + 0x18) self.polytypes_addr = rom.read_int32(start + offset + 0x1C) self.camera_data_addr = rom.read_int32(start + offset + 0x20) self.polytypes = (self.poly_addr - self.polytypes_addr) // 8
def main(args, seed=None): start = time.clock() # initialize the world world = World(args.shuffle, args.logic, args.mode, args.difficulty, args.goal, args.algorithm, not args.nodungeonitems, args.beatableonly, args.shuffleganon, args.quickswap) 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) logger.info('ALttP Entrance Randomizer Version %s - Seed: %s\n\n' % (__version__, world.seed)) create_regions(world) logger.info('Shuffling the World about.') link_entrances(world) logger.info('Calculating Access Rules.') set_rules(world) logger.info('Generating Item Pool.') generate_itempool(world) logger.info('Placing Dungeon Items.') 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, 0) logger.info('Calculating playthrough.') create_playthrough(world) logger.info('Patching ROM.') if args.sprite is not None: sprite = bytearray(open(args.sprite, 'rb').read()) else: sprite = None outfilebase = 'ER_%s_%s-%s-%s_%s-%s%s%s_%s' % ( world.logic, world.difficulty, world.mode, world.goal, world.shuffle, world.algorithm, "-quickswap" if world.quickswap else "", "-shuffleganon" if world.shuffle_ganon else "", world.seed) if not args.suppress_rom: if args.jsonout: rom = JsonRom() else: rom = LocalRom(args.rom) patch_rom(world, rom, bytearray(logic_hash), args.heartbeep, sprite) if args.jsonout: print( json.dumps({ 'patch': rom.patches, 'spoiler': world.spoiler.to_json() })) else: rom.write_to_file(args.jsonout or '%s.sfc' % outfilebase) if args.create_spoiler and not args.jsonout: world.spoiler.to_file('%s_Spoiler.txt' % outfilebase) logger.info('Done. Enjoy.') logger.debug('Total Time: %s' % (time.clock() - start)) return world
def update_scene_table(rom: LocalRom, sceneId, start, end): cur = sceneId * 0x14 + SCENE_TABLE rom.write_int32s(cur, [start, end])
def write_actor_data(rom: LocalRom, cur, actors): for actor in actors: rom.write_int16s(cur, actor) cur += 0x10
def update_dmadata(rom: LocalRom, file: File): key, start, end, from_file = file.dma_key, file.start, file.end, file.from_file rom.update_dmadata_record(key, start, end, from_file) file.dma_key = file.start
def patch_spirit_temple_mq_room_6(rom: LocalRom, room_addr): cur = room_addr actor_list_addr = 0 cmd_actors_offset = 0 # scan for actor list and header end code = rom.read_byte(cur) while code != 0x14: #terminator if code == 0x01: # actors actor_list_addr = rom.read_int32(cur + 4) cmd_actors_offset = cur - room_addr cur += 8 code = rom.read_byte(cur) cur += 8 # original header size header_size = cur - room_addr # set alternate header data location alt_data_off = header_size + 8 # set new alternate header offset alt_header_off = align16(alt_data_off + (4 * 3)) # alt header record size * num records # write alternate header data # the first 3 words are mandatory. the last 3 are just to make the binary # cleaner to read rom.write_int32s(room_addr + alt_data_off, [0, get_segment_address(3, alt_header_off), 0, 0, 0, 0]) # clone header a_start = room_addr a_end = a_start + header_size b_start = room_addr + alt_header_off b_end = b_start + header_size rom.buffer[b_start:b_end] = rom.buffer[a_start:a_end] # make the child header skip the first actor, # which avoids the spawning of the block while in the hole cmd_addr = room_addr + cmd_actors_offset actor_list_addr += 0x10 actors = rom.read_byte(cmd_addr + 1) rom.write_byte(cmd_addr + 1, actors - 1) rom.write_int32(cmd_addr + 4, actor_list_addr) # move header rom.buffer[a_start + 8:a_end + 8] = rom.buffer[a_start:a_end] # write alternate header command seg = get_segment_address(3, alt_data_off) rom.write_int32s(room_addr, [0x18000000, seg])
def write_cam_data(self, rom: LocalRom, addr, cam_data): for item in cam_data: data, pos = item rom.write_int32s(addr, [data, pos]) addr += 8
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 cosmetic_patch(settings, window=dummy_window()): start = time.clock() logger = logging.getLogger('') # we load the rom before creating the seed so that error get caught early if settings.compress_rom == 'None': raise Exception( 'An output type must be specified to produce anything.') window.update_status('Loading ROM') rom = LocalRom(settings) logger.info('OoT Randomizer Version %s', __version__) logger.info('Patching ROM.') outfilebase = 'OoT_cosmetics' output_dir = default_output_path(settings.output_dir) if settings.compress_rom == 'Patch': file_list = [] window.update_progress(65) window.update_status('Patching ROM') patchfilename = '%s.zpf' % outfilebase patch_cosmetics(settings, rom) window.update_progress(65 + 20) window.update_status('Creating Patch File') output_path = os.path.join(output_dir, patchfilename) file_list.append(patchfilename) create_patch_file(rom, output_path) window.update_progress(95) elif settings.compress_rom != 'None': window.update_status('Patching ROM') cosmetics_log = patch_cosmetics(settings, rom) window.update_progress(65) window.update_status('Saving Uncompressed ROM') 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') if compressor_path != "": run_process(window, logger, [ compressor_path, output_path, output_path[:output_path.rfind('.')] + '-comp.z64' ]) os.remove(output_path) window.update_progress(95) if settings.create_cosmetics_log and cosmetics_log: window.update_status('Creating Cosmetics Log') filename = '%s_Cosmetics.txt' % outfilebase cosmetics_log.to_file(os.path.join(output_dir, filename)) window.update_progress(100) window.update_status('Success: Rom patched successfully') logger.info('Done. Enjoy.') logger.debug('Total Time: %s', time.clock() - start)
def _gen_rom(team: int, player: int): use_enemizer = (world.boss_shuffle[player] != 'none' or world.enemy_shuffle[player] or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default' or world.shufflepots[player] or world.bush_shuffle[player] or world.killable_thieves[player]) rom = LocalRom(args.rom) patch_rom(world, rom, player, team, use_enemizer) if use_enemizer: patch_enemizer(world, team, player, rom, args.enemizercli) if args.race: patch_race_rom(rom, world, player) world.spoiler.hashes[(player, team)] = get_hash_string(rom.hash) palettes_options = {} palettes_options['dungeon'] = args.uw_palettes[player] palettes_options['overworld'] = args.ow_palettes[player] palettes_options['hud'] = args.hud_palettes[player] palettes_options['sword'] = args.sword_palettes[player] palettes_options['shield'] = args.shield_palettes[player] palettes_options['link'] = args.link_palettes[player] apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player], args.fastmenu[player], args.disablemusic[player], args.sprite[player], palettes_options, world, player, True, cutscenespeed=args.cutscenespeed[player] if not args.race else "normal", reduceflashing=args.reduceflashing[player]) 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 \ '-universal_keys' if world.keyshuffle[player] == "universal" 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 '', 'U' if world.keyshuffle[player] == "universal" else 'S' if world.keyshuffle[player] else '', 'B' if world.bigkeyshuffle[player] else '') outfilepname = f'_T{team + 1}' if world.teams > 1 else '' outfilepname += f'_P{player}' outfilepname += f"_{world.player_names[player][team].replace(' ', '_')}" \ if world.player_names[player][team] != 'Player%d' % player else '' outfilestuffs = { "logic": world.logic[player], # 0 "difficulty": world.difficulty[player], # 1 "difficulty_adjustments": world.difficulty_adjustments[player], # 2 "mode": world.mode[player], # 3 "goal": world.goal[player], # 4 "timer": str(world.timer[player]), # 5 "shuffle": world.shuffle[player], # 6 "algorithm": world.algorithm, # 7 "mscb": mcsb_name, # 8 "retro": world.retro[player], # 9 "progressive": world.progressive, # A "hints": 'True' if world.hints[player] else 'False' # B } # 0 1 2 3 4 5 6 7 8 9 A B outfilesuffix = ( '_%s_%s-%s-%s-%s%s_%s-%s%s%s%s%s' % ( # 0 1 2 3 4 5 6 7 8 9 A B C # _noglitches_normal-normal-open-ganon-ohko_simple-balanced-keysanity-retro-prog_random-nohints # _noglitches_normal-normal-open-ganon _simple-balanced-keysanity-retro # _noglitches_normal-normal-open-ganon _simple-balanced-keysanity -prog_random # _noglitches_normal-normal-open-ganon _simple-balanced-keysanity -nohints outfilestuffs["logic"], # 0 outfilestuffs["difficulty"], # 1 outfilestuffs["difficulty_adjustments"], # 2 outfilestuffs["mode"], # 3 outfilestuffs["goal"], # 4 "" if outfilestuffs["timer"] in ['False', 'none', 'display'] else "-" + outfilestuffs["timer"], # 5 outfilestuffs["shuffle"], # 6 outfilestuffs["algorithm"], # 7 outfilestuffs["mscb"], # 8 "-retro" if outfilestuffs["retro"] == "True" else "", # 9 "-prog_" + outfilestuffs["progressive"] if outfilestuffs["progressive"] in ['off', 'random'] else "", # A "-nohints" if not outfilestuffs["hints"] == "True" else "" ) # B ) if not args.outputname else '' rompath = output_path( f'{outfilebase}{outfilepname}{outfilesuffix}.sfc') rom.write_to_file(rompath, hide_enemizer=True) if args.create_diff: Patch.create_patch_file(rompath) return player, team, bytes(rom.name).decode()
def main(settings, window=dummy_window()): logger = logging.getLogger('') # initialize the world worlds = [] if not settings.world_count: settings.world_count = 1 if settings.world_count < 1: raise Exception('World Count must be at least 1') if settings.player_num > settings.world_count or settings.player_num < 1: raise Exception('Player Num must be between 1 and %d' % settings.world_count) for i in range(0, settings.world_count): worlds.append(World(settings)) logger.info('Patching ROM.') outfilebase = 'BOoT_%s' % (worlds[0].settings_string) output_dir = default_output_path(settings.output_dir) window.update_status('Patching ROM') rom = LocalRom(settings) patch_rom(worlds[settings.player_num - 1], rom) window.update_progress(50) rom_path = os.path.join(output_dir, '%s.z64' % outfilebase) wad_path = os.path.join(output_dir, '%s.wad' % outfilebase) window.update_status('Saving Uncompressed ROM') rom.write_to_file(rom_path) window.update_status('Compressing ROM') logger.info('Compressing ROM.') compressor_path = "" if platform.system() == 'Windows': if 8 * struct.calcsize("P") == 64: compressor_path = "bin\\Compress\\Compress.exe" else: compressor_path = "bin\\Compress\\Compress32.exe" elif platform.system() == 'Linux': compressor_path = "bin/Compress/Compress" elif platform.system() == 'Darwin': compressor_path = r"bin/Compress/Compress.out" else: logger.info('OS not supported for compression') # uncomment below for decompressed output (for debugging) # rom.write_to_file(default_output_path('%s.z64' % outfilebase)) run_process(window, logger, [ compressor_path, rom_path, os.path.join(output_dir, '%s-comp.z64' % outfilebase) ], None) os.remove(rom_path) window.update_progress(85) # wad generation window.update_status('Generating WAD') logger.info('Generating WAD.') if settings.create_wad == 'True': gzinject_path = "" if platform.system() == 'Windows': gzinject_path = "bin\\gzinject.exe" elif platform.system() == 'Linux': gzinject_path = "bin/gzinject-linux" elif platform.system() == 'Darwin': gzinject_path = "bin/gzinject-mac" else: logger.info('OS not supported for WAD injection') run_process(window, logger, [gzinject_path, "-a", "genkey"], b'45e') #generate common key run_process(window, logger, [ gzinject_path, "-a", "inject", "--rom", os.path.join(output_dir, '%s-comp.z64' % outfilebase), "--wad", settings.wad, "-o", os.path.join(output_dir, '%s.wad' % outfilebase), "-i", "NBOE", "-t", "Better OOT", "--disable-cstick-d-remapping", "--disable-dpad-u-remapping", "--cleanup" ], None) os.remove(os.path.join(output_dir, '%s-comp.z64' % outfilebase)) window.update_progress(95) window.update_progress(100) if settings.create_wad == 'True': window.update_status('WAD patched successfully') else: window.update_status('ROM patched successfully') logger.info('ROM patched successfully') return worlds[settings.player_num - 1]
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] or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default' or world.shufflepots[player] or sprite_random_on_hit or world.bush_shuffle[player] or world.killable_thieves[player] or world.tile_shuffle[player]) rom = LocalRom(args.rom) patch_rom(world, rom, player, team, use_enemizer) if use_enemizer: patch_enemizer(world, player, rom, args.enemizercli, sprite_random_on_hit) 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], world, player) 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 '' outfilepname += f'_P{player}' 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, hide_enemizer=True) if args.create_diff: Patch.create_patch_file(rompath) return player, team, bytes(rom.name).decode()
def from_patch_file(settings, window=dummy_window()): start = time.clock() logger = logging.getLogger('') # we load the rom before creating the seed so that error get caught early if settings.compress_rom == 'None' or settings.compress_rom == 'Patch': raise Exception( 'Output Type must be a ROM when patching from a patch file.') window.update_status('Loading ROM') rom = LocalRom(settings) logger.info('Patching ROM.') filename_split = os.path.basename(settings.patch_file).split('.') if settings.output_file: outfilebase = settings.output_file else: outfilebase = filename_split[0] extension = filename_split[-1] output_dir = default_output_path(settings.output_dir) output_path = os.path.join(output_dir, outfilebase) window.update_status('Patching ROM') if extension == 'zpf': subfile = None else: subfile = 'P%d.zpf' % (settings.player_num) if not settings.output_file: output_path += 'P%d' % (settings.player_num) apply_patch_file(rom, settings.patch_file, subfile) cosmetics_log = None if settings.repatch_cosmetics: cosmetics_log = patch_cosmetics(settings, rom) window.update_progress(65) window.update_status('Saving Uncompressed ROM') uncompressed_output_path = output_path + '.z64' rom.write_to_file(uncompressed_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': 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 + '-comp.z64' if compressor_path != "": run_process(window, logger, [ compressor_path, uncompressed_output_path, output_compress_path ]) os.remove(uncompressed_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) 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.clock() - start) return True
def main(args, seed=None): start = time.clock() # initialize the world world = World(args.shuffle, args.bridge, args.open_forest, not args.nodungeonitems, args.beatableonly) 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) logger.info('ALttP Entrance Randomizer Version %s - Seed: %s\n\n', __version__, world.seed) create_regions(world) create_dungeons(world) logger.info('Shuffling the World about.') link_entrances(world) logger.info('Calculating Access Rules.') set_rules(world) logger.info('Generating Item Pool.') generate_itempool(world) logger.info('Placing Dungeon Items.') shuffled_locations = None shuffled_locations = world.get_unfilled_locations() random.shuffle(shuffled_locations) fill_dungeons_restrictive(world, shuffled_locations) logger.info('Fill the world.') distribute_items_restrictive(world) logger.info('Calculating playthrough.') create_playthrough(world) logger.info('Patching ROM.') outfilebase = 'OoT_%s_%s_%s' % (world.shuffle, world.bridge, world.seed) if not args.suppress_rom: rom = LocalRom(args.rom) patch_rom(world, rom) rom.write_to_file(output_path('%s.z64' % outfilebase)) if args.create_spoiler: world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) logger.info('Done. Enjoy.') logger.debug('Total Time: %s', time.clock() - start) return world
def cosmetic_patch(settings, window=dummy_window()): start = time.clock() logger = logging.getLogger('') if settings.patch_file == '': raise Exception('Cosmetic Only must have a patch file supplied.') window.update_status('Loading ROM') rom = LocalRom(settings) logger.info('Patching ROM.') filename_split = os.path.basename(settings.patch_file).split('.') if settings.output_file: outfilebase = settings.output_file else: outfilebase = filename_split[0] extension = filename_split[-1] output_dir = default_output_path(settings.output_dir) output_path = os.path.join(output_dir, outfilebase) window.update_status('Patching ROM') if extension == 'zpf': subfile = None else: subfile = 'P%d.zpf' % (settings.player_num) apply_patch_file(rom, settings.patch_file, subfile) window.update_progress(65) # clear changes from the base patch file patched_base_rom = copy.copy(rom.buffer) rom.changed_address = {} rom.changed_dma = {} rom.force_patch = [] window.update_status('Patching ROM') patchfilename = '%s_Cosmetic.zpf' % output_path cosmetics_log = patch_cosmetics(settings, rom) window.update_progress(80) window.update_status('Creating Patch File') # base the new patch file on the base patch file rom.original = patched_base_rom rom.update_crc() create_patch_file(rom, patchfilename) logger.info("Created patchfile at: %s" % patchfilename) window.update_progress(95) 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.clock() - start) return True
def main(args, seed=None): start = time.clock() # initialize the world world = World(args.shuffle, args.logic, args.mode, args.difficulty, args.goal, args.algorithm, not args.nodungeonitems, args.beatableonly) 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.spoiler += 'ALttP Entrance Randomizer Version %s - Seed: %s\n\n' % ( __version__, world.seed) world.spoiler += 'Logic: %s Mode: %s Goal: %s Entrance Shuffle: %s Filling Algorithm: %s\n\n' % ( args.logic, args.mode, args.goal, args.shuffle, args.algorithm) # todo logger.info(world.spoiler) create_regions(world) logger.info('Shuffling the World about.') world.spoiler += link_entrances(world) logger.info('Generating Item Pool.') world.spoiler += generate_itempool(world) logger.info('Calculating Access Rules.') world.spoiler += set_rules(world) logger.info('Placing Dungeon Items.') world.spoiler += 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 == 'restrictive': distribute_items_restrictive(world, 10 if world.goal is not 'starhunt' else 0) world.spoiler += print_location_spoiler(world) logger.info('Calculating playthrough.') world.spoiler += create_playthrough(world) logger.info('Patching ROM.') if args.sprite is not None: sprite = bytearray(open(args.sprite, 'rb').read()) else: sprite = None outfilebase = 'ER_%s_%s_%s_%s_%s_%s' % (world.mode, world.goal, world.shuffle, world.difficulty, world.algorithm, world.seed) if not args.suppress_rom: if args.jsonout: rom = JsonRom() else: rom = LocalRom(args.rom) patch_rom(world, rom, bytearray(logic_hash), args.quickswap, args.heartbeep, sprite) rom.write_to_file(args.jsonout or '%s.sfc' % outfilebase) if args.create_spoiler: with open('%s_Spoiler.txt' % outfilebase, 'w') as outfile: outfile.write(world.spoiler) logger.info('Done. Enjoy.') logger.debug('Total Time: %s' % (time.clock() - start)) return world
def main(settings, window=dummy_window()): start = time.clock() 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 = LocalRom(settings) 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: world.mq_dungeons = dist_num_mq + random.randint( 0, len(dungeon_pool)) mqd_count = world.mq_dungeons mqd_picks = random.sample(dungeon_pool, mqd_count - dist_num_mq) for dung in mqd_picks: world.dungeon_mq[dung] = True overworld_data = os.path.join(data_path('World'), 'Overworld.json') world.load_regions_from_json(overworld_data) create_dungeons(world) world.initialize_entrances() 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) 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) 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) 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.clock() - start) return worlds[settings.player_num - 1]
def main(settings): start = time.clock() # initialize the world worlds = [] if not settings.world_count: settings.world_count = 1 if settings.world_count < 1: raise Exception('World Count must be at least 1') if settings.player_num > settings.world_count or settings.player_num < 1: raise Exception('Player Num must be between 1 and %d' % settings.world_count) for i in range(0, settings.world_count): worlds.append(World(settings)) logger = logging.getLogger('') random.seed(worlds[0].numeric_seed) logger.info('OoT Randomizer Version %s - Seed: %s\n\n', __version__, worlds[0].seed) for id, world in enumerate(worlds): world.id = id logger.info('Generating World %d.' % id) logger.info('Creating Overworld') create_regions(world) logger.info('Creating Dungeons') create_dungeons(world) logger.info('Linking Entrances') link_entrances(world) logger.info('Calculating Access Rules.') set_rules(world) logger.info('Generating Item Pool.') generate_itempool(world) logger.info('Fill the world.') distribute_items_restrictive(worlds) if settings.create_spoiler: logger.info('Calculating playthrough.') create_playthrough(worlds) CollectionState.update_required_items(worlds) logger.info('Patching ROM.') if settings.world_count > 1: outfilebase = 'OoT_%s_%s_W%dP%d' % ( worlds[0].settings_string, worlds[0].seed, worlds[0].world_count, worlds[0].player_num) else: outfilebase = 'OoT_%s_%s' % (worlds[0].settings_string, worlds[0].seed) output_dir = default_output_path(settings.output_dir) if not settings.suppress_rom: rom = LocalRom(settings) patch_rom(worlds[settings.player_num - 1], rom) rom_path = os.path.join(output_dir, '%s.z64' % outfilebase) rom.write_to_file(rom_path) if settings.compress_rom: logger.info('Compressing ROM.') if platform.system() == 'Windows': subprocess.call([ "Compress\\Compress.exe", rom_path, os.path.join(output_dir, '%s-comp.z64' % outfilebase) ]) elif platform.system() == 'Linux': subprocess.call([ "Compress/Compress", rom_path, os.path.join(output_dir, '%s-comp.z64' % outfilebase) ]) elif platform.system() == 'Darwin': subprocess.call( ["Compress/Compress.out", ('%s.z64' % outfilebase)]) else: logger.info('OS not supported for compression') if settings.create_spoiler: worlds[settings.player_num - 1].spoiler.to_file( os.path.join(output_dir, '%s_Spoiler.txt' % outfilebase)) os.remove('hints.txt') logger.info('Done. Enjoy.') logger.debug('Total Time: %s', time.clock() - start) return worlds[settings.player_num - 1]
def main(args, seed=None): start = time.clock() # initialize the world world = World(args.bridge, args.open_forest, args.open_door_of_time, not args.nodungeonitems, args.beatableonly, 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) logger.info('ALttP Entrance Randomizer Version %s - Seed: %s\n\n', __version__, world.seed) create_regions(world) create_dungeons(world) logger.info('Shuffling the World about.') link_entrances(world) logger.info('Calculating Access Rules.') set_rules(world) logger.info('Generating Item Pool.') generate_itempool(world) logger.info('Placing Dungeon Items.') shuffled_locations = None shuffled_locations = world.get_unfilled_locations() random.shuffle(shuffled_locations) fill_dungeons_restrictive(world, shuffled_locations) logger.info('Fill the world.') distribute_items_restrictive(world) logger.info('Calculating playthrough.') create_playthrough(world) logger.info('Patching ROM.') outfilebase = 'OoT_%s%s%s%s_%s' % (world.bridge, "-openforest" if world.open_forest else "", "-opendoor" if world.open_door_of_time else "", "-beatableonly" if world.check_beatable_only else "", world.seed) if not args.suppress_rom: rom = LocalRom(args.rom) patch_rom(world, rom) rom.write_to_file(output_path('%s.z64' % outfilebase)) if args.compress_rom: logger.info('Compressing ROM.') if platform.system() == 'Windows': subprocess.call(["Compress\Compress.exe", ('%s.z64' % outfilebase)]) elif platform.system() == 'Linux': subprocess.call(["Compress/Compress", ('%s.z64' % outfilebase)]) elif platform.system() == 'Darwin': subprocess.call(["Compress/Compress.out", ('%s.z64' % outfilebase)]) else: logger.info('OS not supported for compression') if args.create_spoiler: world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) logger.info('Done. Enjoy.') logger.debug('Total Time: %s', time.clock() - start) return world
def write_to_floormap(self, rom: LocalRom, addr): rom.write_int16(addr, self.flag) rom.write_f32(addr + 4, float(self.x)) rom.write_f32(addr + 8, float(self.y))
def write_to_scene(self, rom: LocalRom, start): addr = start + self.offset + 0x18 rom.write_int32s( addr, [self.poly_addr, self.polytypes_addr, self.camera_data_addr])