diff --git a/.gitignore b/.gitignore index 829090ca..1d7b3795 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *_Spoiler.txt *.bmbp *.apbp +*.apm3 *.apmc *.apz5 *.pyc diff --git a/CommonClient.py b/CommonClient.py index f78a3d64..bde3adb5 100644 --- a/CommonClient.py +++ b/CommonClient.py @@ -3,13 +3,16 @@ import logging import asyncio import urllib.parse import sys -import os import typing import time import websockets import Utils + +if __name__ == "__main__": + Utils.init_logging("TextClient") + from MultiServer import CommandProcessor from NetUtils import Endpoint, decode, NetworkItem, encode, JSONtoTextParser, ClientStatus, Permission from Utils import Version @@ -17,10 +20,8 @@ from worlds import network_data_package, AutoWorldRegister logger = logging.getLogger("Client") -gui_enabled = Utils.is_frozen() or "--nogui" not in sys.argv - -log_folder = Utils.local_path("logs") -os.makedirs(log_folder, exist_ok=True) +# without terminal we have to use gui mode +gui_enabled = not sys.stdout or "--nogui" not in sys.argv class ClientCommandProcessor(CommandProcessor): @@ -58,7 +59,7 @@ class ClientCommandProcessor(CommandProcessor): """List all missing location checks, from your local game state""" if not self.ctx.game: self.output("No game set, cannot determine missing checks.") - return + return False count = 0 checked_count = 0 for location, location_id in AutoWorldRegister.world_types[self.ctx.game].location_name_to_id.items(): @@ -270,6 +271,7 @@ class CommonContext(): logger.info(f"DeathLink: Received from {data['source']}") async def send_death(self, death_text: str = ""): + logger.info("Sending death to your friends...") self.last_death_link = time.time() await self.send_msgs([{ "cmd": "Bounce", "tags": ["DeathLink"], @@ -517,19 +519,18 @@ async def console_loop(ctx: CommonContext): logger.exception(e) -def init_logging(name: str): - if gui_enabled: - logging.basicConfig(format='[%(name)s]: %(message)s', level=logging.INFO, - filename=os.path.join(log_folder, f"{name}.txt"), filemode="w", force=True) - else: - logging.basicConfig(format='[%(name)s]: %(message)s', level=logging.INFO, force=True) - logging.getLogger().addHandler(logging.FileHandler(os.path.join(log_folder, f"{name}.txt"), "w")) +def get_base_parser(description=None): + import argparse + parser = argparse.ArgumentParser(description=description) + parser.add_argument('--connect', default=None, help='Address of the multiworld host.') + parser.add_argument('--password', default=None, help='Password of the multiworld host.') + if sys.stdout: # If terminal output exists, offer gui-less mode + parser.add_argument('--nogui', default=False, action='store_true', help="Turns off Client GUI.") + return parser if __name__ == '__main__': # Text Mode to use !hint and such with games that have no text entry - init_logging("TextClient") - class TextContext(CommonContext): tags = {"AP", "IgnoreGame"} @@ -581,15 +582,9 @@ if __name__ == '__main__': if input_task: input_task.cancel() - - import argparse import colorama - parser = argparse.ArgumentParser(description="Gameless Archipelago Client, for text interfaction.") - parser.add_argument('--connect', default=None, help='Address of the multiworld host.') - parser.add_argument('--password', default=None, help='Password of the multiworld host.') - if not Utils.is_frozen(): # Frozen state has no cmd window in the first place - parser.add_argument('--nogui', default=False, action='store_true', help="Turns off Client GUI.") + parser = get_base_parser(description="Gameless Archipelago Client, for text interfaction.") args, rest = parser.parse_known_args() colorama.init() diff --git a/FactorioClient.py b/FactorioClient.py index fefb4cb2..5eb39035 100644 --- a/FactorioClient.py +++ b/FactorioClient.py @@ -6,24 +6,24 @@ import string import copy import subprocess import time +import random import factorio_rcon - import colorama import asyncio from queue import Queue -from CommonClient import CommonContext, server_loop, console_loop, ClientCommandProcessor, logger, gui_enabled, \ - init_logging -from MultiServer import mark_raw - import Utils -import random + +if __name__ == "__main__": + Utils.init_logging("FactorioClient") + +from CommonClient import CommonContext, server_loop, console_loop, ClientCommandProcessor, logger, gui_enabled, \ + get_base_parser +from MultiServer import mark_raw from NetUtils import NetworkItem, ClientStatus, JSONtoTextParser, JSONMessagePart from worlds.factorio import Factorio -init_logging("FactorioClient") - class FactorioCommandProcessor(ClientCommandProcessor): ctx: FactorioContext @@ -353,17 +353,11 @@ class FactorioJSONtoTextParser(JSONtoTextParser): if __name__ == '__main__': - import argparse - - parser = argparse.ArgumentParser(description="Optional arguments to FactorioClient follow. " - "Remaining arguments get passed into bound Factorio instance." - "Refer to Factorio --help for those.") + parser = get_base_parser(description="Optional arguments to FactorioClient follow. " + "Remaining arguments get passed into bound Factorio instance." + "Refer to Factorio --help for those.") parser.add_argument('--rcon-port', default='24242', type=int, help='Port to use to communicate with Factorio') parser.add_argument('--rcon-password', help='Password to authenticate with RCON.') - parser.add_argument('--connect', default=None, help='Address of the multiworld host.') - parser.add_argument('--password', default=None, help='Password of the multiworld host.') - if not Utils.is_frozen(): # Frozen state has no cmd window in the first place - parser.add_argument('--nogui', default=False, action='store_true', help="Turns off Client GUI.") args, rest = parser.parse_known_args() colorama.init() diff --git a/Fill.py b/Fill.py index f2f00027..f434010b 100644 --- a/Fill.py +++ b/Fill.py @@ -56,7 +56,6 @@ def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations, logging.warning( f'Not all items placed. Game beatable anyway. (Could not place {item_to_place})') continue - raise FillError(f'No more spots to place {item_to_place}, locations {locations} are invalid. ' f'Already placed {len(placements)}: {", ".join(str(place) for place in placements)}') diff --git a/Generate.py b/Generate.py index 70aa6b23..99631165 100644 --- a/Generate.py +++ b/Generate.py @@ -12,6 +12,7 @@ import ModuleUpdate ModuleUpdate.update() +import Utils from worlds.alttp import Options as LttPOptions from worlds.generic import PlandoItem, PlandoConnection from Utils import parse_yaml, version_tuple, __version__, tuplize_version, get_options @@ -39,12 +40,12 @@ def mystery_argparse(): parser.add_argument('--seed', help='Define seed number to generate.', type=int) parser.add_argument('--multi', default=defaults["players"], type=lambda value: max(int(value), 1)) parser.add_argument('--spoiler', type=int, default=defaults["spoiler"]) - parser.add_argument('--rom', default=options["lttp_options"]["rom_file"], help="Path to the 1.0 JP LttP Baserom.") + parser.add_argument('--lttp_rom', default=options["lttp_options"]["rom_file"], help="Path to the 1.0 JP LttP Baserom.") + parser.add_argument('--sm_rom', default=options["sm_options"]["rom_file"], help="Path to the 1.0 JP SM Baserom.") parser.add_argument('--enemizercli', default=defaults["enemizer_path"]) parser.add_argument('--outputpath', default=options["general_options"]["output_path"]) parser.add_argument('--race', action='store_true', default=defaults["race"]) parser.add_argument('--meta_file_path', default=defaults["meta_file_path"]) - parser.add_argument('--log_output_path', help='Path to store output log') parser.add_argument('--log_level', default='info', help='Sets log level') parser.add_argument('--yaml_output', default=0, type=lambda value: max(int(value), 0), help='Output rolled mystery results to yaml up to specified number (made for async multiworld)') @@ -125,20 +126,10 @@ def main(args=None, callback=ERmain): erargs.outputname = seed_name erargs.outputpath = args.outputpath - # set up logger - if args.log_level: - erargs.loglevel = args.log_level - loglevel = {'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG}[ - erargs.loglevel] + Utils.init_logging(f"Generate_{seed}.txt", loglevel=args.log_level) - if args.log_output_path: - os.makedirs(args.log_output_path, exist_ok=True) - logging.basicConfig(format='%(message)s', level=loglevel, force=True, - filename=os.path.join(args.log_output_path, f"{seed}.log")) - else: - logging.basicConfig(format='%(message)s', level=loglevel, force=True) - - erargs.rom = args.rom + erargs.lttp_rom = args.lttp_rom + erargs.sm_rom = args.sm_rom erargs.enemizercli = args.enemizercli settings_cache = {k: (roll_settings(v, args.plando) if args.samesettings else None) diff --git a/LttPAdjuster.py b/LttPAdjuster.py index 0cf1496b..77732059 100644 --- a/LttPAdjuster.py +++ b/LttPAdjuster.py @@ -15,7 +15,7 @@ from argparse import Namespace from concurrent.futures import as_completed, ThreadPoolExecutor from glob import glob from tkinter import Tk, Frame, Label, StringVar, Entry, filedialog, messagebox, Button, LEFT, X, TOP, LabelFrame, \ - IntVar, Checkbutton, E, OptionMenu, Toplevel, BOTTOM, RIGHT, font as font, PhotoImage + IntVar, Checkbutton, E, W, OptionMenu, Toplevel, BOTTOM, RIGHT, font as font, PhotoImage from urllib.parse import urlparse from urllib.request import urlopen @@ -51,6 +51,7 @@ def main(): (default: %(default)s) ''') parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true') + parser.add_argument('--deathlink', help='Enable DeathLink system.', action='store_true') parser.add_argument('--disablemusic', help='Disables game music.', action='store_true') parser.add_argument('--triforcehud', default='hide_goal', const='hide_goal', nargs='?', choices=['normal', 'hide_goal', 'hide_required', 'hide_both'], @@ -152,7 +153,8 @@ def adjust(args): world = getattr(args, "world") apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.menuspeed, args.music, - args.sprite, palettes_options, reduceflashing=args.reduceflashing or racerom, world=world) + args.sprite, palettes_options, reduceflashing=args.reduceflashing or racerom, world=world, + deathlink=args.deathlink) path = output_path(f'{os.path.basename(args.rom)[:-4]}_adjusted.sfc') rom.write_to_file(path) @@ -205,6 +207,7 @@ def adjustGUI(): guiargs.quickswap = bool(rom_vars.quickSwapVar.get()) guiargs.music = bool(rom_vars.MusicVar.get()) guiargs.reduceflashing = bool(rom_vars.disableFlashingVar.get()) + guiargs.deathlink = bool(rom_vars.DeathLinkVar.get()) guiargs.rom = romVar2.get() guiargs.baserom = romVar.get() guiargs.sprite = rom_vars.sprite @@ -272,17 +275,18 @@ def update_sprites(task, on_finish=None): current_sprites = [os.path.basename(file) for file in glob(sprite_dir + '/*')] alttpr_sprites = [(sprite['file'], os.path.basename(urlparse(sprite['file']).path)) for sprite in sprites_arr if sprite["author"] != "Nintendo"] - needed_sprites = [(sprite_url, filename) for (sprite_url, filename) in alttpr_sprites if filename not in current_sprites] + needed_sprites = [(sprite_url, filename) for (sprite_url, filename) in alttpr_sprites if + filename not in current_sprites] alttpr_filenames = [filename for (_, filename) in alttpr_sprites] obsolete_sprites = [sprite for sprite in current_sprites if sprite not in alttpr_filenames] except Exception as e: - resultmessage = "Error Determining which sprites to update. Sprites not updated.\n\n%s: %s" % (type(e).__name__, e) + resultmessage = "Error Determining which sprites to update. Sprites not updated.\n\n%s: %s" % ( + type(e).__name__, e) successful = False task.queue_event(finished) return - def dl(sprite_url, filename): target = os.path.join(sprite_dir, filename) with urlopen(sprite_url) as response, open(target, 'wb') as out: @@ -291,7 +295,6 @@ def update_sprites(task, on_finish=None): def rem(sprite): os.remove(os.path.join(sprite_dir, sprite)) - with ThreadPoolExecutor() as pool: dl_tasks = [] rem_tasks = [] @@ -313,7 +316,7 @@ def update_sprites(task, on_finish=None): except Exception as e: logging.exception(e) resultmessage = "Error downloading sprite. Not all sprites updated.\n\n%s: %s" % ( - type(e).__name__, e) + type(e).__name__, e) successful = False for rem_task in as_completed(rem_tasks): @@ -324,7 +327,7 @@ def update_sprites(task, on_finish=None): except Exception as e: logging.exception(e) resultmessage = "Error removing obsolete sprite. Not all sprites updated.\n\n%s: %s" % ( - type(e).__name__, e) + type(e).__name__, e) successful = False if successful: @@ -362,7 +365,7 @@ class BackgroundTask(object): event = self.queue.get_nowait() event() if self.running: - #if self is no longer running self.window may no longer be valid + # if self is no longer running self.window may no longer be valid self.window.update_idletasks() except queue.Empty: pass @@ -420,6 +423,7 @@ def get_rom_frame(parent=None): romVar.set(rom) romSelectButton['state'] = "disabled" romSelectButton["text"] = "ROM verified" + romSelectButton = Button(romFrame, text='Select Rom', command=RomSelect) baseRomLabel.pack(side=LEFT) @@ -444,17 +448,21 @@ def get_rom_options_frame(parent=None): MusicCheckbutton.grid(row=0, column=0, sticky=E) vars.disableFlashingVar = IntVar(value=1) - disableFlashingCheckbutton = Checkbutton(romOptionsFrame, text="Disable flashing (anti-epilepsy)", variable=vars.disableFlashingVar) - disableFlashingCheckbutton.grid(row=6, column=0, sticky=E) + disableFlashingCheckbutton = Checkbutton(romOptionsFrame, text="Disable flashing (anti-epilepsy)", + variable=vars.disableFlashingVar) + disableFlashingCheckbutton.grid(row=6, column=0, sticky=W) + + vars.DeathLinkVar = IntVar(value=0) + DeathLinkCheckbutton = Checkbutton(romOptionsFrame, text="DeathLink (Team Deaths)", variable=vars.DeathLinkVar) + DeathLinkCheckbutton.grid(row=7, column=0, sticky=W) spriteDialogFrame = Frame(romOptionsFrame) spriteDialogFrame.grid(row=0, column=1) baseSpriteLabel = Label(spriteDialogFrame, text='Sprite:') - - vars.spriteNameVar = StringVar() vars.sprite = None + def set_sprite(sprite_param): nonlocal vars if isinstance(sprite_param, str): @@ -491,7 +499,8 @@ def get_rom_options_frame(parent=None): menuspeedLabel.pack(side=LEFT) vars.menuspeedVar = StringVar() vars.menuspeedVar.set('normal') - menuspeedOptionMenu = OptionMenu(menuspeedFrame, vars.menuspeedVar, 'normal', 'instant', 'double', 'triple', 'quadruple', 'half') + menuspeedOptionMenu = OptionMenu(menuspeedFrame, vars.menuspeedVar, 'normal', 'instant', 'double', 'triple', + 'quadruple', 'half') menuspeedOptionMenu.pack(side=LEFT) heartcolorFrame = Frame(romOptionsFrame) @@ -518,7 +527,8 @@ def get_rom_options_frame(parent=None): owPalettesLabel.pack(side=LEFT) vars.owPalettesVar = StringVar() vars.owPalettesVar.set('default') - owPalettesOptionMenu = OptionMenu(owPalettesFrame, vars.owPalettesVar, 'default', 'good', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') + owPalettesOptionMenu = OptionMenu(owPalettesFrame, vars.owPalettesVar, 'default', 'good', 'blackout', 'grayscale', + 'negative', 'classic', 'dizzy', 'sick', 'puke') owPalettesOptionMenu.pack(side=LEFT) uwPalettesFrame = Frame(romOptionsFrame) @@ -527,7 +537,8 @@ def get_rom_options_frame(parent=None): uwPalettesLabel.pack(side=LEFT) vars.uwPalettesVar = StringVar() vars.uwPalettesVar.set('default') - uwPalettesOptionMenu = OptionMenu(uwPalettesFrame, vars.uwPalettesVar, 'default', 'good', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') + uwPalettesOptionMenu = OptionMenu(uwPalettesFrame, vars.uwPalettesVar, 'default', 'good', 'blackout', 'grayscale', + 'negative', 'classic', 'dizzy', 'sick', 'puke') uwPalettesOptionMenu.pack(side=LEFT) hudPalettesFrame = Frame(romOptionsFrame) @@ -536,7 +547,8 @@ def get_rom_options_frame(parent=None): hudPalettesLabel.pack(side=LEFT) vars.hudPalettesVar = StringVar() vars.hudPalettesVar.set('default') - hudPalettesOptionMenu = OptionMenu(hudPalettesFrame, vars.hudPalettesVar, 'default', 'good', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') + hudPalettesOptionMenu = OptionMenu(hudPalettesFrame, vars.hudPalettesVar, 'default', 'good', 'blackout', + 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') hudPalettesOptionMenu.pack(side=LEFT) swordPalettesFrame = Frame(romOptionsFrame) @@ -545,7 +557,8 @@ def get_rom_options_frame(parent=None): swordPalettesLabel.pack(side=LEFT) vars.swordPalettesVar = StringVar() vars.swordPalettesVar.set('default') - swordPalettesOptionMenu = OptionMenu(swordPalettesFrame, vars.swordPalettesVar, 'default', 'good', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') + swordPalettesOptionMenu = OptionMenu(swordPalettesFrame, vars.swordPalettesVar, 'default', 'good', 'blackout', + 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') swordPalettesOptionMenu.pack(side=LEFT) shieldPalettesFrame = Frame(romOptionsFrame) @@ -554,7 +567,8 @@ def get_rom_options_frame(parent=None): shieldPalettesLabel.pack(side=LEFT) vars.shieldPalettesVar = StringVar() vars.shieldPalettesVar.set('default') - shieldPalettesOptionMenu = OptionMenu(shieldPalettesFrame, vars.shieldPalettesVar, 'default', 'good', 'blackout', 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') + shieldPalettesOptionMenu = OptionMenu(shieldPalettesFrame, vars.shieldPalettesVar, 'default', 'good', 'blackout', + 'grayscale', 'negative', 'classic', 'dizzy', 'sick', 'puke') shieldPalettesOptionMenu.pack(side=LEFT) spritePoolFrame = Frame(romOptionsFrame) @@ -563,6 +577,7 @@ def get_rom_options_frame(parent=None): vars.spritePoolCountVar = StringVar() vars.sprite_pool = [] + def set_sprite_pool(sprite_param): nonlocal vars operation = "add" @@ -632,8 +647,10 @@ class SpriteSelector(): title_link.pack(side=LEFT) title_link.bind("", open_custom_sprite_dir) - self.icon_section(alttpr_frametitle, self.alttpr_sprite_dir, 'ALTTPR sprites not found. Click "Update alttpr sprites" to download them.') - self.icon_section(custom_frametitle, self.custom_sprite_dir, 'Put sprites in the custom sprites folder (see open link above) to have them appear here.') + self.icon_section(alttpr_frametitle, self.alttpr_sprite_dir, + 'ALTTPR sprites not found. Click "Update alttpr sprites" to download them.') + self.icon_section(custom_frametitle, self.custom_sprite_dir, + 'Put sprites in the custom sprites folder (see open link above) to have them appear here.') if not randomOnEvent: self.sprite_pool_section(spritePool) @@ -683,7 +700,8 @@ class SpriteSelector(): button = Checkbutton(frame, text="Bonk", command=self.update_random_button, variable=self.randomOnBonkVar) button.pack(side=LEFT, padx=(0, 5)) - button = Checkbutton(frame, text="Random", command=self.update_random_button, variable=self.randomOnRandomVar) + button = Checkbutton(frame, text="Random", command=self.update_random_button, + variable=self.randomOnRandomVar) button.pack(side=LEFT, padx=(0, 5)) if adjuster: @@ -805,7 +823,6 @@ class SpriteSelector(): BackgroundTaskProgress(self.parent, update_sprites, "Updating Sprites", on_finish) - def browse_for_sprite(self): sprite = filedialog.askopenfilename( filetypes=[("All Sprite Sources", (".zspr", ".spr", ".sfc", ".smc")), @@ -819,7 +836,6 @@ class SpriteSelector(): self.callback(None) self.window.destroy() - def use_default_sprite(self): self.callback(None) self.window.destroy() @@ -923,7 +939,8 @@ def get_image_for_sprite(sprite, gif_only: bool = False): gif_lsd = bytearray(7) gif_lsd[0] = width gif_lsd[2] = height - gif_lsd[4] = 0xF4 # 32 color palette follows. transparant + 15 for sprite + 1 for shadow=17 which rounds up to 32 as nearest power of 2 + gif_lsd[ + 4] = 0xF4 # 32 color palette follows. transparant + 15 for sprite + 1 for shadow=17 which rounds up to 32 as nearest power of 2 gif_lsd[5] = 0 # background color is zero gif_lsd[6] = 0 # aspect raio not specified gif_gct = bytearray(3 * 32) @@ -943,7 +960,8 @@ def get_image_for_sprite(sprite, gif_only: bool = False): gif_id[7] = height gif_id[9] = 0 # no local color table - gif_img_minimum_code_size = bytes([7]) # we choose 7 bits, so that each pixel is represented by a byte, for conviennce. + gif_img_minimum_code_size = bytes( + [7]) # we choose 7 bits, so that each pixel is represented by a byte, for conviennce. clear = 0x80 stop = 0x81 @@ -1100,5 +1118,6 @@ class ToolTips(object): widget.after_cancel(cls.after_id) cls.after_id = None + if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/Main.py b/Main.py index 6491f581..671e56de 100644 --- a/Main.py +++ b/Main.py @@ -34,7 +34,6 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No output_path.cached_path = args.outputpath start = time.perf_counter() - # initialize the world world = MultiWorld(args.multi) diff --git a/MinecraftClient.py b/MinecraftClient.py index bf90271b..509c70b0 100644 --- a/MinecraftClient.py +++ b/MinecraftClient.py @@ -4,8 +4,8 @@ import re import atexit from subprocess import Popen from shutil import copyfile -from base64 import b64decode from time import strftime +import logging import requests @@ -34,7 +34,7 @@ def prompt_yes_no(prompt): def find_forge_jar(forge_dir): for entry in os.scandir(forge_dir): if ".jar" in entry.name and "forge" in entry.name: - print(f"Found forge .jar: {entry.name}") + logging.info(f"Found forge .jar: {entry.name}") return entry.name raise FileNotFoundError(f"Could not find forge .jar in {forge_dir}.") @@ -47,12 +47,12 @@ def find_ap_randomizer_jar(forge_dir): for entry in os.scandir(mods_dir): match = ap_mod_re.match(entry.name) if match: - print(f"Found AP randomizer mod: {match.group()}") + logging.info(f"Found AP randomizer mod: {match.group()}") return match.group() return None else: os.mkdir(mods_dir) - print(f"Created mods folder in {forge_dir}") + logging.info(f"Created mods folder in {forge_dir}") return None @@ -64,17 +64,17 @@ def replace_apmc_files(forge_dir, apmc_file): copy_apmc = True if not os.path.isdir(apdata_dir): os.mkdir(apdata_dir) - print(f"Created APData folder in {forge_dir}") + logging.info(f"Created APData folder in {forge_dir}") for entry in os.scandir(apdata_dir): if entry.name.endswith(".apmc") and entry.is_file(): if not os.path.samefile(apmc_file, entry.path): os.remove(entry.path) - print(f"Removed {entry.name} in {apdata_dir}") + logging.info(f"Removed {entry.name} in {apdata_dir}") else: # apmc already in apdata copy_apmc = False if copy_apmc: copyfile(apmc_file, os.path.join(apdata_dir, os.path.basename(apmc_file))) - print(f"Copied {os.path.basename(apmc_file)} to {apdata_dir}") + logging.info(f"Copied {os.path.basename(apmc_file)} to {apdata_dir}") # Check mod version, download new mod from GitHub releases page if needed. @@ -86,30 +86,31 @@ def update_mod(forge_dir): if resp.status_code == 200: # OK latest_release = resp.json()[0] if ap_randomizer != latest_release['assets'][0]['name']: - print(f"A new release of the Minecraft AP randomizer mod was found: {latest_release['assets'][0]['name']}") + logging.info(f"A new release of the Minecraft AP randomizer mod was found: " + f"{latest_release['assets'][0]['name']}") if ap_randomizer is not None: - print(f"Your current mod is {ap_randomizer}.") + logging.info(f"Your current mod is {ap_randomizer}.") else: - print(f"You do not have the AP randomizer mod installed.") + logging.info(f"You do not have the AP randomizer mod installed.") if prompt_yes_no("Would you like to update?"): old_ap_mod = os.path.join(forge_dir, 'mods', ap_randomizer) if ap_randomizer is not None else None new_ap_mod = os.path.join(forge_dir, 'mods', latest_release['assets'][0]['name']) - print("Downloading AP randomizer mod. This may take a moment...") + logging.info("Downloading AP randomizer mod. This may take a moment...") apmod_resp = requests.get(latest_release['assets'][0]['browser_download_url']) if apmod_resp.status_code == 200: with open(new_ap_mod, 'wb') as f: f.write(apmod_resp.content) - print(f"Wrote new mod file to {new_ap_mod}") + logging.info(f"Wrote new mod file to {new_ap_mod}") if old_ap_mod is not None: os.remove(old_ap_mod) - print(f"Removed old mod file from {old_ap_mod}") + logging.info(f"Removed old mod file from {old_ap_mod}") else: - print(f"Error retrieving the randomizer mod (status code {apmod_resp.status_code}).") - print(f"Please report this issue on the Archipelago Discord server.") + logging.error(f"Error retrieving the randomizer mod (status code {apmod_resp.status_code}).") + logging.error(f"Please report this issue on the Archipelago Discord server.") sys.exit(1) else: - print(f"Error checking for randomizer mod updates (status code {resp.status_code}).") - print(f"If this was not expected, please report this issue on the Archipelago Discord server.") + logging.error(f"Error checking for randomizer mod updates (status code {resp.status_code}).") + logging.error(f"If this was not expected, please report this issue on the Archipelago Discord server.") if not prompt_yes_no("Continue anyways?"): sys.exit(0) @@ -127,13 +128,13 @@ def check_eula(forge_dir): text = f.read() if 'false' in text: # Prompt user to agree to the EULA - print("You need to agree to the Minecraft EULA in order to run the server.") - print("The EULA can be found at https://account.mojang.com/documents/minecraft_eula") + logging.info("You need to agree to the Minecraft EULA in order to run the server.") + logging.info("The EULA can be found at https://account.mojang.com/documents/minecraft_eula") if prompt_yes_no("Do you agree to the EULA?"): f.seek(0) f.write(text.replace('false', 'true')) f.truncate() - print(f"Set {eula_path} to true") + logging.info(f"Set {eula_path} to true") else: sys.exit(0) @@ -152,12 +153,13 @@ def run_forge_server(forge_dir, heap_arg): heap_arg = "-Xmx" + heap_arg argstring = ' '.join([java_exe, heap_arg, "-jar", forge_server, "-nogui"]) - print(f"Running Forge server: {argstring}") + logging.info(f"Running Forge server: {argstring}") os.chdir(forge_dir) return Popen(argstring) if __name__ == '__main__': + Utils.init_logging("MinecraftClient") parser = argparse.ArgumentParser() parser.add_argument("apmc_file", default=None, nargs='?', help="Path to an Archipelago Minecraft data file (.apmc)") diff --git a/MultiServer.py b/MultiServer.py index 4bbf3486..c1be4d9e 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -431,6 +431,17 @@ class Context: else: return self.player_names[team, slot] + def on_goal_achieved(self, client: Client): + finished_msg = f'{self.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1})' \ + f' has completed their goal.' + self.notify_all(finished_msg) + if "auto" in self.forfeit_mode: + forfeit_player(self, client.team, client.slot) + elif proxy_worlds[self.games[client.slot]].forced_auto_forfeit: + forfeit_player(self, client.team, client.slot) + if "auto" in self.collect_mode: + collect_player(self, client.team, client.slot) + def notify_hints(ctx: Context, team: int, hints: typing.List[NetUtils.Hint]): concerns = collections.defaultdict(list) @@ -1203,19 +1214,20 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict): cmd: str = args["cmd"] except: logging.exception(f"Could not get command from {args}") - await ctx.send_msgs(client, [{'cmd': 'InvalidPacket', "type": "cmd", + await ctx.send_msgs(client, [{'cmd': 'InvalidPacket', "type": "cmd", "original_cmd": None, "text": f"Could not get command from {args} at `cmd`"}]) raise if type(cmd) is not str: - await ctx.send_msgs(client, [{'cmd': 'InvalidPacket', "type": "cmd", + await ctx.send_msgs(client, [{'cmd': 'InvalidPacket', "type": "cmd", "original_cmd": None, "text": f"Command should be str, got {type(cmd)}"}]) return if cmd == 'Connect': if not args or 'password' not in args or type(args['password']) not in [str, type(None)] or \ 'game' not in args: - await ctx.send_msgs(client, [{'cmd': 'InvalidPacket', "type": "arguments", 'text': 'Connect'}]) + await ctx.send_msgs(client, [{'cmd': 'InvalidPacket', "type": "arguments", 'text': 'Connect', + "original_cmd": cmd}]) return errors = set() @@ -1282,14 +1294,21 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict): else: await ctx.send_msgs(client, [{"cmd": "DataPackage", "data": network_data_package}]) + elif client.auth: if cmd == "ConnectUpdate": if not args: - await ctx.send_msgs(client, [{'cmd': 'InvalidPacket', "type": "arguments", 'text': cmd}]) + await ctx.send_msgs(client, [{'cmd': 'InvalidPacket', "type": "arguments", 'text': cmd, + "original_cmd": cmd}]) return if "tags" in args: + old_tags = client.tags client.tags = args["tags"] + if set(old_tags) != set(client.tags): + ctx.notify_all( + f"{ctx.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1}) has changed tags " + f"from {old_tags} to {client.tags}.") elif cmd == 'Sync': items = get_received_items(ctx, client.team, client.slot) @@ -1301,7 +1320,8 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict): elif cmd == 'LocationChecks': if "Tracker" in client.tags: await ctx.send_msgs(client, [{'cmd': 'InvalidPacket', "type": "cmd", - "text": "Trackers can't register new Location Checks"}]) + "text": "Trackers can't register new Location Checks", + "original_cmd": cmd}]) else: register_location_checks(ctx, client.team, client.slot, args["locations"]) @@ -1310,7 +1330,8 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict): for location in args["locations"]: if type(location) is not int or location not in lookup_any_location_id_to_name: await ctx.send_msgs(client, - [{'cmd': 'InvalidPacket', "type": "arguments", "text": 'LocationScouts'}]) + [{'cmd': 'InvalidPacket', "type": "arguments", "text": 'LocationScouts', + "original_cmd": cmd}]) return target_item, target_player = ctx.locations[client.slot][location] locs.append(NetworkItem(target_item, location, target_player)) @@ -1322,7 +1343,8 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict): elif cmd == 'Say': if "text" not in args or type(args["text"]) is not str or not args["text"].isprintable(): - await ctx.send_msgs(client, [{'cmd': 'InvalidPacket', "type": "arguments", "text": 'Say'}]) + await ctx.send_msgs(client, [{'cmd': 'InvalidPacket', "type": "arguments", "text": 'Say', + "original_cmd": cmd}]) return client.messageprocessor(args["text"]) @@ -1345,14 +1367,7 @@ def update_client_status(ctx: Context, client: Client, new_status: ClientStatus) current = ctx.client_game_state[client.team, client.slot] if current != ClientStatus.CLIENT_GOAL: # can't undo goal completion if new_status == ClientStatus.CLIENT_GOAL: - finished_msg = f'{ctx.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1}) has completed their goal.' - ctx.notify_all(finished_msg) - if "auto" in ctx.forfeit_mode: - forfeit_player(ctx, client.team, client.slot) - elif proxy_worlds[ctx.games[client.slot]].forced_auto_forfeit: - forfeit_player(ctx, client.team, client.slot) - if "auto" in ctx.collect_mode: - collect_player(ctx, client.team, client.slot) + ctx.on_goal_achieved(client) ctx.client_game_state[client.team, client.slot] = new_status @@ -1641,8 +1656,7 @@ async def auto_shutdown(ctx, to_cancel=None): async def main(args: argparse.Namespace): - logging.basicConfig(force=True, - format='[%(asctime)s] %(message)s', level=getattr(logging, args.loglevel.upper(), logging.INFO)) + Utils.init_logging("Server", loglevel=args.loglevel.lower()) ctx = Context(args.host, args.port, args.server_password, args.password, args.location_check_points, args.hint_cost, not args.disable_item_cheat, args.forfeit_mode, args.collect_mode, diff --git a/NetUtils.py b/NetUtils.py index 20c78528..d4f90b2a 100644 --- a/NetUtils.py +++ b/NetUtils.py @@ -13,8 +13,6 @@ class JSONMessagePart(typing.TypedDict, total=False): # optional type: str color: str - # mainly for items, optional - found: bool # owning player for location/item player: int @@ -274,7 +272,7 @@ class Hint(typing.NamedTuple): add_json_text(parts, "[Hint]: ") add_json_text(parts, self.receiving_player, type="player_id") add_json_text(parts, "'s ") - add_json_item(parts, self.item, self.receiving_player, found=self.found) + add_json_item(parts, self.item, self.receiving_player) add_json_text(parts, " is at ") add_json_location(parts, self.location, self.finding_player) add_json_text(parts, " in ") @@ -291,7 +289,8 @@ class Hint(typing.NamedTuple): return {"cmd": "PrintJSON", "data": parts, "type": "Hint", "receiving": self.receiving_player, - "item": NetworkItem(self.item, self.location, self.finding_player)} + "item": NetworkItem(self.item, self.location, self.finding_player), + "found": self.found} @property def local(self): diff --git a/Patch.py b/Patch.py index 8bb3f302..b136e932 100644 --- a/Patch.py +++ b/Patch.py @@ -10,45 +10,74 @@ from typing import Tuple, Optional import Utils +current_patch_version = 3 -current_patch_version = 2 +GAME_ALTTP = "A Link to the Past" +GAME_SM = "Super Metroid" +supported_games = {"A Link to the Past", "Super Metroid"} -def generate_yaml(patch: bytes, metadata: Optional[dict] = None) -> bytes: - from worlds.alttp.Rom import JAP10HASH +def generate_yaml(patch: bytes, metadata: Optional[dict] = None, game: str = GAME_ALTTP) -> bytes: + if game == GAME_ALTTP: + from worlds.alttp.Rom import JAP10HASH + elif game == GAME_SM: + from worlds.sm.Rom import JAP10HASH + else: + raise RuntimeError("Selected game for base rom not found.") + patch = yaml.dump({"meta": metadata, "patch": patch, - "game": "A Link to the Past", + "game": game, # minimum version of patch system expected for patching to be successful - "compatible_version": 1, + "compatible_version": 3, "version": current_patch_version, "base_checksum": JAP10HASH}) return patch.encode(encoding="utf-8-sig") -def generate_patch(rom: bytes, metadata: Optional[dict] = None) -> bytes: - from worlds.alttp.Rom import get_base_rom_bytes +def generate_patch(rom: bytes, metadata: Optional[dict] = None, game: str = GAME_ALTTP) -> bytes: + if game == GAME_ALTTP: + from worlds.alttp.Rom import get_base_rom_bytes + elif game == GAME_SM: + from worlds.sm.Rom import get_base_rom_bytes + else: + raise RuntimeError("Selected game for base rom not found.") + if metadata is None: metadata = {} patch = bsdiff4.diff(get_base_rom_bytes(), rom) - return generate_yaml(patch, metadata) + return generate_yaml(patch, metadata, game) def create_patch_file(rom_file_to_patch: str, server: str = "", destination: str = None, - player: int = 0, player_name: str = "") -> str: - meta = {"server": server, # allow immediate connection to server in multiworld. Empty string otherwise + player: int = 0, player_name: str = "", game: str = GAME_ALTTP) -> str: + meta = {"server": server, # allow immediate connection to server in multiworld. Empty string otherwise "player_id": player, "player_name": player_name} bytes = generate_patch(load_bytes(rom_file_to_patch), - meta) - target = destination if destination else os.path.splitext(rom_file_to_patch)[0] + ".apbp" + meta, + game) + target = destination if destination else os.path.splitext(rom_file_to_patch)[0] + ( + ".apbp" if game == GAME_ALTTP else ".apm3") write_lzma(bytes, target) return target def create_rom_bytes(patch_file: str, ignore_version: bool = False) -> Tuple[dict, str, bytearray]: - from worlds.alttp.Rom import get_base_rom_bytes data = Utils.parse_yaml(lzma.decompress(load_bytes(patch_file)).decode("utf-8-sig")) + game_name = data["game"] + if game_name in supported_games: + if game_name == GAME_ALTTP: + from worlds.alttp.Rom import get_base_rom_bytes + elif game_name == GAME_SM: + from worlds.sm.Rom import get_base_rom_bytes + else: + raise Exception(f"No Patch handler for game {game_name}") + elif game_name == "alttp": # old version for A Link to the Past + from worlds.alttp.Rom import get_base_rom_bytes + else: + raise Exception(f"Cannot handle game {game_name}") + if not ignore_version and data["compatible_version"] > current_patch_version: raise RuntimeError("Patch file is incompatible with this patcher, likely an update is required.") patched_data = bsdiff4.patch(get_base_rom_bytes(), data["patch"]) @@ -68,7 +97,7 @@ def create_rom_file(patch_file: str) -> Tuple[dict, str]: def update_patch_data(patch_data: bytes, server: str = "") -> bytes: data = Utils.parse_yaml(lzma.decompress(patch_data).decode("utf-8-sig")) data["meta"]["server"] = server - bytes = generate_yaml(data["patch"], data["meta"]) + bytes = generate_yaml(data["patch"], data["meta"], data["game"]) return lzma.compress(bytes) @@ -82,6 +111,14 @@ def write_lzma(data: bytes, path: str): f.write(data) +def read_rom(stream, strip_header=True) -> bytearray: + """Reads rom into bytearray and optionally strips off any smc header""" + buffer = bytearray(stream.read()) + if strip_header and len(buffer) % 0x400 == 0x200: + return buffer[0x200:] + return buffer + + if __name__ == "__main__": host = Utils.get_public_ipv4() options = Utils.get_options()['server_options'] @@ -113,7 +150,13 @@ if __name__ == "__main__": if 'server' in data: Utils.persistent_store("servers", data['hash'], data['server']) print(f"Host is {data['server']}") - + elif rom.endswith(".apm3"): + print(f"Applying patch {rom}") + data, target = create_rom_file(rom) + print(f"Created rom {target}.") + if 'server' in data: + Utils.persistent_store("servers", data['hash'], data['server']) + print(f"Host is {data['server']}") elif rom.endswith(".archipelago"): import json import zlib @@ -139,7 +182,7 @@ if __name__ == "__main__": def _handle_zip_file_entry(zfinfo: zipfile.ZipInfo, server: str): data = zfr.read(zfinfo) - if zfinfo.filename.endswith(".apbp"): + if zfinfo.filename.endswith(".apbp") or zfinfo.filename.endswith(".apm3"): data = update_patch_data(data, server) with ziplock: zfw.writestr(zfinfo, data) @@ -160,12 +203,4 @@ if __name__ == "__main__": import traceback traceback.print_exc() - input("Press enter to close.") - - -def read_rom(stream, strip_header=True) -> bytearray: - """Reads rom into bytearray and optionally strips off any smc header""" - buffer = bytearray(stream.read()) - if strip_header and len(buffer) % 0x400 == 0x200: - return buffer[0x200:] - return buffer + input("Press enter to close.") \ No newline at end of file diff --git a/LttPClient.py b/SNIClient.py similarity index 79% rename from LttPClient.py rename to SNIClient.py index a81071cb..e7f4b8ae 100644 --- a/LttPClient.py +++ b/SNIClient.py @@ -1,8 +1,5 @@ from __future__ import annotations -import argparse -import atexit -exit_func = atexit.register(input, "Press enter to close.") import threading import time import multiprocessing @@ -14,28 +11,33 @@ import logging import asyncio from json import loads, dumps -import ModuleUpdate +from Utils import get_item_name_from_id, init_logging -ModuleUpdate.update() +if __name__ == "__main__": + init_logging("SNIClient") -from Utils import get_item_name_from_id import colorama from NetUtils import * - from worlds.alttp import Regions, Shops from worlds.alttp import Items from worlds.alttp.Rom import ROM_PLAYER_LIMIT import Utils -from CommonClient import CommonContext, server_loop, console_loop, ClientCommandProcessor, gui_enabled, init_logging +from CommonClient import CommonContext, server_loop, console_loop, ClientCommandProcessor, gui_enabled, get_base_parser +from Patch import GAME_ALTTP, GAME_SM -init_logging("LttPClient") snes_logger = logging.getLogger("SNES") from MultiServer import mark_raw +class DeathState(enum.IntEnum): + killing_player = 1 + alive = 2 + dead = 3 + + class LttPCommandProcessor(ClientCommandProcessor): ctx: Context @@ -92,6 +94,10 @@ class LttPCommandProcessor(ClientCommandProcessor): self.output("Data Sent") return True + def _cmd_test_death(self): + self.ctx.on_deathlink({"source": "Console", + "time": time.time()}) + class Context(CommonContext): command_processor = LttPCommandProcessor @@ -110,7 +116,8 @@ class Context(CommonContext): self.snes_request_lock = asyncio.Lock() self.snes_write_buffer = [] self.snes_connector_lock = threading.Lock() - self.death_state = False # for death link flop behaviour + self.death_state = DeathState.alive # for death link flop behaviour + self.killing_player_task = None self.awaiting_rom = False self.rom = None @@ -140,16 +147,51 @@ class Context(CommonContext): await self.send_msgs([{"cmd": 'Connect', 'password': self.password, 'name': auth, 'version': Utils.version_tuple, 'tags': self.tags, - 'uuid': Utils.get_unique_identifier(), 'game': "A Link to the Past" + 'uuid': Utils.get_unique_identifier(), + 'game': self.game }]) def on_deathlink(self, data: dict): - snes_buffered_write(self, WRAM_START + 0xF36D, bytes([0])) - snes_buffered_write(self, WRAM_START + 0x0373, bytes([8])) - asyncio.create_task(snes_flush_writes(self)) - self.death_state = True + if not self.killing_player_task or self.killing_player_task.done(): + self.killing_player_task = asyncio.create_task(deathlink_kill_player(self)) super(Context, self).on_deathlink(data) + async def handle_deathlink_state(self, currently_dead: bool): + # in this state we only care about triggering a death send + if self.death_state == DeathState.alive: + if currently_dead: + self.death_state = DeathState.dead + await self.send_death() + # in this state we care about confirming a kill, to move state to dead + elif self.death_state == DeathState.killing_player: + # this is being handled in deathlink_kill_player(ctx) already + pass + # in this state we wait until the player is alive again + elif self.death_state == DeathState.dead: + if not currently_dead: + self.death_state = DeathState.alive + + +async def deathlink_kill_player(ctx: Context): + ctx.death_state = DeathState.killing_player + while ctx.death_state == DeathState.killing_player and \ + ctx.snes_state == SNESState.SNES_ATTACHED: + if ctx.game == GAME_ALTTP: + snes_buffered_write(ctx, WRAM_START + 0xF36D, bytes([0])) # set current health to 0 + snes_buffered_write(ctx, WRAM_START + 0x0373, bytes([8])) # deal 1 full heart of damage at next opportunity + elif ctx.game == GAME_SM: + snes_buffered_write(ctx, WRAM_START + 0x09C2, bytes([0, 0])) # set current health to 0 + await snes_flush_writes(ctx) + await asyncio.sleep(1) + gamemode = None + if ctx.game == GAME_ALTTP: + gamemode = await snes_read(ctx, WRAM_START + 0x10, 1) + elif ctx.game == GAME_SM: + gamemode = await snes_read(ctx, WRAM_START + 0x0998, 1) + if not gamemode or gamemode[0] in (DEATH_MODES if ctx.game == GAME_ALTTP else SM_DEATH_MODES): + ctx.death_state = DeathState.dead + ctx.last_death_link = time.time() + def color_item(item_id: int, green: bool = False) -> str: item_name = get_item_name_from_id(item_id) @@ -161,6 +203,7 @@ def color_item(item_id: int, green: bool = False) -> str: SNES_RECONNECT_DELAY = 5 +# LttP ROM_START = 0x000000 WRAM_START = 0xF50000 WRAM_SIZE = 0x20000 @@ -187,7 +230,20 @@ SCOUTREPLY_ITEM_ADDR = SAVEDATA_START + 0x4D9 # 1 byte SCOUTREPLY_PLAYER_ADDR = SAVEDATA_START + 0x4DA # 1 byte SHOP_ADDR = SAVEDATA_START + 0x302 # 2 bytes -DEATH_LINK_ACTIVE_ADDR = ROM_START + 0x18008D # 1 byte +DEATH_LINK_ACTIVE_ADDR = ROMNAME_START + 0x15 # 1 byte + +# SM +SM_ROMNAME_START = 0x1C4F00 + +SM_INGAME_MODES = {0x07, 0x09, 0x0b} +SM_ENDGAME_MODES = {0x26, 0x27} +SM_DEATH_MODES = {0x15, 0x17, 0x18, 0x19, 0x1A} + +SM_RECV_PROGRESS_ADDR = SRAM_START + 0x2000 # 2 bytes +SM_RECV_ITEM_ADDR = SAVEDATA_START + 0x4D2 # 1 byte +SM_RECV_ITEM_PLAYER_ADDR = SAVEDATA_START + 0x4D3 # 1 byte + +SM_DEATH_LINK_ACTIVE_ADDR = ROM_START + 0x277f04 # 1 byte location_shop_ids = set([info[0] for name, info in Shops.shop_table.items()]) @@ -473,8 +529,8 @@ def launch_sni(ctx: Context): if os.path.isfile(sni_path): snes_logger.info(f"Attempting to start {sni_path}") - import subprocess - if Utils.is_frozen(): # if it spawns a visible console, may as well populate it + import sys + if not sys.stdout: # if it spawns a visible console, may as well populate it subprocess.Popen(sni_path, cwd=os.path.dirname(sni_path)) else: subprocess.Popen(sni_path, cwd=os.path.dirname(sni_path), stdout=subprocess.DEVNULL, @@ -540,7 +596,7 @@ async def verify_snes_app(socket): await socket.send(dumps(AppVersion_Request)) app: str = loads(await socket.recv())["Results"][0] - if not "SNI" in app: + if "SNI" not in app: snes_logger.warning(f"Warning: Did not find SNI as the endpoint, instead {app} was found.") @@ -839,14 +895,23 @@ async def game_watcher(ctx: Context): if not ctx.rom: ctx.finished_game = False - rom = await snes_read(ctx, ROMNAME_START, ROMNAME_SIZE) + gameName = await snes_read(ctx, SM_ROMNAME_START, 2) + if gameName is None: + continue + elif gameName == b"SM": + ctx.game = GAME_SM + else: + ctx.game = GAME_ALTTP + + rom = await snes_read(ctx, SM_ROMNAME_START if ctx.game == GAME_SM else ROMNAME_START, ROMNAME_SIZE) if rom is None or rom == bytes([0] * ROMNAME_SIZE): continue ctx.rom = rom - death_link = await snes_read(ctx, DEATH_LINK_ACTIVE_ADDR, 1) + death_link = await snes_read(ctx, DEATH_LINK_ACTIVE_ADDR if ctx.game == GAME_ALTTP else + SM_DEATH_LINK_ACTIVE_ADDR, 1) if death_link: - death_link = bool(death_link[0]) + death_link = bool(death_link[0] & 0b1) old_tags = ctx.tags.copy() if death_link: ctx.tags.add("DeathLink") @@ -866,77 +931,131 @@ async def game_watcher(ctx: Context): snes_logger.warning("ROM change detected, please reconnect to the multiworld server") await ctx.disconnect() - gamemode = await snes_read(ctx, WRAM_START + 0x10, 1) - if "DeathLink" in ctx.tags and gamemode and ctx.last_death_link + 1 < time.time(): - if gamemode[0] in DEATH_MODES: - if not ctx.death_state: # new death - await ctx.send_death() - ctx.death_state = True - else: - ctx.death_state = False # reset death state, so next death can trigger - gameend = await snes_read(ctx, SAVEDATA_START + 0x443, 1) - game_timer = await snes_read(ctx, SAVEDATA_START + 0x42E, 4) - if gamemode is None or gameend is None or game_timer is None or \ - (gamemode[0] not in INGAME_MODES and gamemode[0] not in ENDGAME_MODES): - continue + if ctx.game == GAME_ALTTP: + gamemode = await snes_read(ctx, WRAM_START + 0x10, 1) + if "DeathLink" in ctx.tags and gamemode and ctx.last_death_link + 1 < time.time(): + currently_dead = gamemode[0] in DEATH_MODES + await ctx.handle_deathlink_state(currently_dead) - delay = 7 if ctx.slow_mode else 2 - if gameend[0]: - if not ctx.finished_game: - await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}]) - ctx.finished_game = True - - if time.perf_counter() - perf_counter < delay: + gameend = await snes_read(ctx, SAVEDATA_START + 0x443, 1) + game_timer = await snes_read(ctx, SAVEDATA_START + 0x42E, 4) + if gamemode is None or gameend is None or game_timer is None or \ + (gamemode[0] not in INGAME_MODES and gamemode[0] not in ENDGAME_MODES): continue + + delay = 7 if ctx.slow_mode else 2 + if gameend[0]: + if not ctx.finished_game: + await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}]) + ctx.finished_game = True + + if time.perf_counter() - perf_counter < delay: + continue + else: + perf_counter = time.perf_counter() else: - perf_counter = time.perf_counter() - else: - game_timer = game_timer[0] | (game_timer[1] << 8) | (game_timer[2] << 16) | (game_timer[3] << 24) - if abs(game_timer - prev_game_timer) < (delay * 60): + game_timer = game_timer[0] | (game_timer[1] << 8) | (game_timer[2] << 16) | (game_timer[3] << 24) + if abs(game_timer - prev_game_timer) < (delay * 60): + continue + else: + prev_game_timer = game_timer + + if gamemode in ENDGAME_MODES: # triforce room and credits continue - else: - prev_game_timer = game_timer - if gamemode in ENDGAME_MODES: # triforce room and credits - continue + data = await snes_read(ctx, RECV_PROGRESS_ADDR, 8) + if data is None: + continue - data = await snes_read(ctx, RECV_PROGRESS_ADDR, 8) - if data is None: - continue + recv_index = data[0] | (data[1] << 8) + recv_item = data[2] + roomid = data[4] | (data[5] << 8) + roomdata = data[6] + scout_location = data[7] - recv_index = data[0] | (data[1] << 8) - recv_item = data[2] - roomid = data[4] | (data[5] << 8) - roomdata = data[6] - scout_location = data[7] + if recv_index < len(ctx.items_received) and recv_item == 0: + item = ctx.items_received[recv_index] + recv_index += 1 + logging.info('Received %s from %s (%s) (%d/%d in list)' % ( + color(ctx.item_name_getter(item.item), 'red', 'bold'), color(ctx.player_names[item.player], 'yellow'), + ctx.location_name_getter(item.location), recv_index, len(ctx.items_received))) - if recv_index < len(ctx.items_received) and recv_item == 0: - item = ctx.items_received[recv_index] - recv_index += 1 - logging.info('Received %s from %s (%s) (%d/%d in list)' % ( - color(ctx.item_name_getter(item.item), 'red', 'bold'), color(ctx.player_names[item.player], 'yellow'), - ctx.location_name_getter(item.location), recv_index, len(ctx.items_received))) + snes_buffered_write(ctx, RECV_PROGRESS_ADDR, + bytes([recv_index & 0xFF, (recv_index >> 8) & 0xFF])) + snes_buffered_write(ctx, RECV_ITEM_ADDR, + bytes([item.item])) + snes_buffered_write(ctx, RECV_ITEM_PLAYER_ADDR, + bytes([min(ROM_PLAYER_LIMIT, item.player) if item.player != ctx.slot else 0])) + if scout_location > 0 and scout_location in ctx.locations_info: + snes_buffered_write(ctx, SCOUTREPLY_LOCATION_ADDR, + bytes([scout_location])) + snes_buffered_write(ctx, SCOUTREPLY_ITEM_ADDR, + bytes([ctx.locations_info[scout_location][0]])) + snes_buffered_write(ctx, SCOUTREPLY_PLAYER_ADDR, + bytes([min(ROM_PLAYER_LIMIT, ctx.locations_info[scout_location][1])])) - snes_buffered_write(ctx, RECV_PROGRESS_ADDR, - bytes([recv_index & 0xFF, (recv_index >> 8) & 0xFF])) - snes_buffered_write(ctx, RECV_ITEM_ADDR, - bytes([item.item])) - snes_buffered_write(ctx, RECV_ITEM_PLAYER_ADDR, - bytes([min(ROM_PLAYER_LIMIT, item.player) if item.player != ctx.slot else 0])) - if scout_location > 0 and scout_location in ctx.locations_info: - snes_buffered_write(ctx, SCOUTREPLY_LOCATION_ADDR, - bytes([scout_location])) - snes_buffered_write(ctx, SCOUTREPLY_ITEM_ADDR, - bytes([ctx.locations_info[scout_location][0]])) - snes_buffered_write(ctx, SCOUTREPLY_PLAYER_ADDR, - bytes([min(ROM_PLAYER_LIMIT, ctx.locations_info[scout_location][1])])) + await snes_flush_writes(ctx) - await snes_flush_writes(ctx) + if scout_location > 0 and scout_location not in ctx.locations_scouted: + ctx.locations_scouted.add(scout_location) + await ctx.send_msgs([{"cmd": "LocationScouts", "locations": [scout_location]}]) + await track_locations(ctx, roomid, roomdata) + elif ctx.game == GAME_SM: + gamemode = await snes_read(ctx, WRAM_START + 0x0998, 1) + if "DeathLink" in ctx.tags and gamemode and ctx.last_death_link + 1 < time.time(): + currently_dead = gamemode[0] in SM_DEATH_MODES + await ctx.handle_deathlink_state(currently_dead) + if gamemode is not None and gamemode[0] in SM_ENDGAME_MODES: + if not ctx.finished_game: + await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}]) + ctx.finished_game = True + continue - if scout_location > 0 and scout_location not in ctx.locations_scouted: - ctx.locations_scouted.add(scout_location) - await ctx.send_msgs([{"cmd": "LocationScouts", "locations": [scout_location]}]) - await track_locations(ctx, roomid, roomdata) + data = await snes_read(ctx, SM_RECV_PROGRESS_ADDR + 0x680, 4) + if data is None: + continue + + recv_index = data[0] | (data[1] << 8) + recv_item = data[2] | (data[3] << 8) + + while (recv_index < recv_item): + itemAdress = recv_index * 8 + message = await snes_read(ctx, SM_RECV_PROGRESS_ADDR + 0x700 + itemAdress, 8) + # worldId = message[0] | (message[1] << 8) # unused + # itemId = message[2] | (message[3] << 8) # unused + itemIndex = (message[4] | (message[5] << 8)) >> 3 + + recv_index += 1 + snes_buffered_write(ctx, SM_RECV_PROGRESS_ADDR + 0x680, bytes([recv_index & 0xFF, (recv_index >> 8) & 0xFF])) + + from worlds.sm.Locations import locations_start_id + location_id = locations_start_id + itemIndex + + ctx.locations_checked.add(location_id) + location = ctx.location_name_getter(location_id) + snes_logger.info(f'New Check: {location} ({len(ctx.locations_checked)}/{len(ctx.missing_locations) + len(ctx.checked_locations)})') + await ctx.send_msgs([{"cmd": 'LocationChecks', "locations": [location_id]}]) + + data = await snes_read(ctx, SM_RECV_PROGRESS_ADDR + 0x600, 4) + if data is None: + continue + + # recv_itemOutPtr = data[0] | (data[1] << 8) # unused + itemOutPtr = data[2] | (data[3] << 8) + + from worlds.sm.Items import items_start_id + if itemOutPtr < len(ctx.items_received): + item = ctx.items_received[itemOutPtr] + itemId = item.item - items_start_id + + playerID = (item.player-1) if item.player != 0 else (len(ctx.player_names)-1) + snes_buffered_write(ctx, SM_RECV_PROGRESS_ADDR + itemOutPtr * 4, bytes([playerID & 0xFF, (playerID >> 8) & 0xFF, itemId & 0xFF, (itemId >> 8) & 0xFF])) + itemOutPtr += 1 + snes_buffered_write(ctx, SM_RECV_PROGRESS_ADDR + 0x602, bytes([itemOutPtr & 0xFF, (itemOutPtr >> 8) & 0xFF])) + logging.info('Received %s from %s (%s) (%d/%d in list)' % ( + color(ctx.item_name_getter(item.item), 'red', 'bold'), color(ctx.player_names[item.player], 'yellow'), + ctx.location_name_getter(item.location), itemOutPtr, len(ctx.items_received))) + await snes_flush_writes(ctx) async def run_game(romfile): @@ -951,17 +1070,13 @@ async def run_game(romfile): async def main(): multiprocessing.freeze_support() - parser = argparse.ArgumentParser() + parser = get_base_parser() parser.add_argument('diff_file', default="", type=str, nargs="?", help='Path to a Archipelago Binary Patch file') parser.add_argument('--snes', default='localhost:8080', help='Address of the SNI server.') - parser.add_argument('--connect', default=None, help='Address of the multiworld host.') - parser.add_argument('--password', default=None, help='Password of the multiworld host.') parser.add_argument('--loglevel', default='info', choices=['debug', 'info', 'warning', 'error', 'critical']) - if not Utils.is_frozen(): # Frozen state has no cmd window in the first place - parser.add_argument('--nogui', default=False, action='store_true', help="Turns off Client GUI.") args = parser.parse_args() - logging.basicConfig(format='%(message)s', level=getattr(logging, args.loglevel.upper(), logging.INFO)) + if args.diff_file: import Patch logging.info("Patch file was supplied. Creating sfc rom..") @@ -983,8 +1098,8 @@ async def main(): if gui_enabled: input_task = None - from kvui import LttPManager - ctx.ui = LttPManager(ctx) + from kvui import SNIManager + ctx.ui = SNIManager(ctx) ui_task = asyncio.create_task(ctx.ui.async_run(), name="UI") else: input_task = asyncio.create_task(console_loop(ctx), name="Input") @@ -1026,4 +1141,3 @@ if __name__ == '__main__': loop.run_until_complete(main()) loop.close() colorama.deinit() - atexit.unregister(exit_func) diff --git a/Utils.py b/Utils.py index 27d5d68c..c5a0a48c 100644 --- a/Utils.py +++ b/Utils.py @@ -13,7 +13,7 @@ class Version(typing.NamedTuple): build: int -__version__ = "0.1.9" +__version__ = "0.2.0" version_tuple = tuplize_version(__version__) import builtins @@ -25,6 +25,8 @@ import functools import io import collections import importlib +import logging + from yaml import load, dump, safe_load try: @@ -124,7 +126,6 @@ unsafe_parse_yaml = functools.partial(load, Loader=Loader) def get_public_ipv4() -> str: import socket import urllib.request - import logging ip = socket.gethostbyname(socket.gethostname()) try: ip = urllib.request.urlopen('https://checkip.amazonaws.com/').read().decode('utf8').strip() @@ -141,7 +142,6 @@ def get_public_ipv4() -> str: def get_public_ipv6() -> str: import socket import urllib.request - import logging ip = socket.gethostbyname(socket.gethostname()) try: ip = urllib.request.urlopen('https://v6.ident.me').read().decode('utf8').strip() @@ -161,6 +161,11 @@ def get_default_options() -> dict: "factorio_options": { "executable": "factorio\\bin\\x64\\factorio", }, + "sm_options": { + "rom_file": "Super Metroid (JU).sfc", + "sni": "SNI", + "rom_start": True, + }, "lttp_options": { "rom_file": "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc", "sni": "SNI", @@ -211,7 +216,6 @@ def get_default_options() -> dict: def update_options(src: dict, dest: dict, filename: str, keys: list) -> dict: - import logging for key, value in src.items(): new_keys = keys.copy() new_keys.append(key) @@ -278,7 +282,6 @@ def persistent_load() -> typing.Dict[dict]: with open(path, "r") as f: storage = unsafe_parse_yaml(f.read()) except Exception as e: - import logging logging.debug(f"Could not read store: {e}") if storage is None: storage = {} @@ -337,7 +340,6 @@ def get_adjuster_settings(romfile: str, skip_questions: bool = False) -> typing. return romfile, False else: adjusted = False - import logging if not hasattr(get_adjuster_settings, "adjust_wanted"): logging.info(f"Skipping post-patch adjustment") get_adjuster_settings.adjuster_settings = adjuster_settings @@ -406,3 +408,28 @@ class KeyedDefaultDict(collections.defaultdict): def get_text_between(text: str, start: str, end: str) -> str: return text[text.index(start) + len(start): text.rindex(end)] + + +loglevel_mapping = {'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG} + + +def init_logging(name: str, loglevel: typing.Union[str, int] = logging.INFO, write_mode: str = "w", + log_format: str = "[%(name)s]: %(message)s"): + loglevel: int = loglevel_mapping.get(loglevel, loglevel) + log_folder = local_path("logs") + os.makedirs(log_folder, exist_ok=True) + root_logger = logging.getLogger() + for handler in root_logger.handlers[:]: + root_logger.removeHandler(handler) + handler.close() + root_logger.setLevel(loglevel) + file_handler = logging.FileHandler( + os.path.join(log_folder, f"{name}.txt"), + write_mode, + encoding="utf-8-sig") + file_handler.setFormatter(logging.Formatter(log_format)) + root_logger.addHandler(file_handler) + if sys.stdout: + root_logger.addHandler( + logging.StreamHandler(sys.stdout) + ) diff --git a/WebHostLib/__init__.py b/WebHostLib/__init__.py index a471d4eb..cfce8b05 100644 --- a/WebHostLib/__init__.py +++ b/WebHostLib/__init__.py @@ -155,7 +155,6 @@ def _read_log(path: str): @app.route('/log/') def display_log(room: UUID): - # noinspection PyTypeChecker return Response(_read_log(os.path.join("logs", str(room) + ".txt")), mimetype="text/plain;charset=UTF-8") diff --git a/WebHostLib/customserver.py b/WebHostLib/customserver.py index c3b0215e..20d1d784 100644 --- a/WebHostLib/customserver.py +++ b/WebHostLib/customserver.py @@ -11,7 +11,7 @@ import time import random import pickle - +import Utils from .models import * from MultiServer import Context, server, auto_shutdown, ServerCommandProcessor, ClientMessageProcessor @@ -111,11 +111,7 @@ def run_server_process(room_id, ponyconfig: dict): db.generate_mapping(check_tables=False) async def main(): - - logging.basicConfig(format='[%(asctime)s] %(message)s', - level=logging.INFO, - handlers=[ - logging.FileHandler(os.path.join(LOGS_FOLDER, f"{room_id}.txt"), 'a', 'utf-8-sig')]) + Utils.init_logging(str(room_id), write_mode="a") ctx = WebHostContext() ctx.load(room_id) ctx.init_save() diff --git a/data/basepatch.apbp b/data/basepatch.apbp index a77bf1fb..2a30d9f8 100644 Binary files a/data/basepatch.apbp and b/data/basepatch.apbp differ diff --git a/docs/network protocol.md b/docs/network protocol.md index 8758df25..42669af4 100644 --- a/docs/network protocol.md +++ b/docs/network protocol.md @@ -44,6 +44,7 @@ These packets are are sent from the multiworld server to the client. They are no * [PrintJSON](#PrintJSON) * [DataPackage](#DataPackage) * [Bounced](#Bounced) +* [InvalidPacket](#InvalidPacket) ### RoomInfo Sent to clients when they connect to an Archipelago server. @@ -157,9 +158,10 @@ Sent to clients purely to display a message to the player. This packet differs f | Name | Type | Notes | | ---- | ---- | ----- | | data | list\[JSONMessagePart\] | See [JSONMessagePart](#JSONMessagePart) for more details on this type. | -| type | str | May be present to indicate the nature of this message. Known types are Hint and ItemSend. -| receiving | int | Is present if type is Hint or ItemSend and marks the destination player's ID. -| item | NetworkItem | Is present if type is Hint or ItemSend and marks the source player id, location id and item id. +| type | str | May be present to indicate the nature of this message. Known types are Hint and ItemSend. | +| receiving | int | Is present if type is Hint or ItemSend and marks the destination player's ID. | +| item | NetworkItem | Is present if type is Hint or ItemSend and marks the source player id, location id and item id. | +| found | bool | Is present if type is Hint, denotes whether the location hinted for was checked. | ### DataPackage Sent to clients to provide what is known as a 'data package' which contains information to enable a client to most easily communicate with the Archipelago server. Contents include things like location id to name mappings, among others; see [Data Package Contents](#Data-Package-Contents) for more info. @@ -184,6 +186,7 @@ Sent to clients if the server caught a problem with a packet. This only occurs f | ---- | ---- | ----- | | type | string | "cmd" if the Packet isn't available/allowed, "arguments" if the problem is with the package data. | | text | string | Error text explaining the caught error. | +| original_cmd | string | Echoes the cmd it failed on. May be null if the cmd was not found. ## (Client -> Server) These packets are sent purely from client to server. They are not accepted by clients. @@ -212,6 +215,14 @@ Sent by the client to initiate a connection to an Archipelago game session. #### Authentication Many, if not all, other packets require a successfully authenticated client. This is described in more detail in [Archipelago Connection Handshake](#Archipelago-Connection-Handshake). +### ConnectUpdate +Update arguments from the Connect package, currently only updating tags is supported. + +#### Arguments +| Name | Type | Notes | +| ---- | ---- | ----- | +| tags | list\[str\] | Denotes special features or capabilities that the sender is capable of. [Tags](#Tags) | + ### Sync Sent to server to request a [ReceivedItems](#ReceivedItems) packet to synchronize items. #### Arguments @@ -409,6 +420,8 @@ We encourage clients to cache the data package they receive on disk, or otherwis Note: * Any ID is unique to its type across AP: Item 56 only exists once and Location 56 only exists once. * Any Name is unique to its type across its own Game only: Single Arrow can exist in two games. + * The IDs from the game "Archipelago" may be used in any other game. + Especially Location ID -1: Cheat Console and -2: Server (typically Remote Start Inventory) #### Contents | Name | Type | Notes | diff --git a/host.yaml b/host.yaml index dd88d269..bfc7ec1d 100644 --- a/host.yaml +++ b/host.yaml @@ -91,6 +91,15 @@ lttp_options: # True for operating system default program # Alternatively, a path to a program to open the .sfc file with rom_start: true +sm_options: + # File name of the v1.0 J rom + rom_file: "Super Metroid (JU).sfc" + # Set this to your SNI folder location if you want the MultiClient to attempt an auto start, does nothing if not found + sni: "SNI" + # Set this to false to never autostart a rom (such as after patching) + # True for operating system default program + # Alternatively, a path to a program to open the .sfc file with + rom_start: true factorio_options: executable: "factorio\\bin\\x64\\factorio" minecraft_options: diff --git a/inno_setup_310.iss b/inno_setup_310.iss index bd73b58a..c5be9691 100644 --- a/inno_setup_310.iss +++ b/inno_setup_310.iss @@ -34,7 +34,6 @@ SignTool= signtool LicenseFile= LICENSE WizardStyle= modern SetupLogging=yes -MinVersion=6.3.9200 [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" @@ -51,11 +50,14 @@ Name: "custom"; Description: "Custom installation"; Flags: iscustom [Components] Name: "core"; Description: "Core Files"; Types: full hosting playing custom; Flags: fixed Name: "generator"; Description: "Generator"; Types: full hosting +Name: "generator/sm"; Description: "Super Metroid ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 3145728 Name: "generator/lttp"; Description: "A Link to the Past ROM Setup and Enemizer"; Types: full hosting; ExtraDiskSpaceRequired: 5191680 Name: "generator/oot"; Description: "Ocarina of Time ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 100663296 Name: "server"; Description: "Server"; Types: full hosting Name: "client"; Description: "Clients"; Types: full playing -Name: "client/lttp"; Description: "A Link to the Past"; Types: full playing +Name: "client/sni"; Description: "SNI Client"; Types: full playing +Name: "client/sni/lttp"; Description: "SNI Client - A Link to the Past Patch Setup"; Types: full playing +Name: "client/sni/sm"; Description: "SNI Client - Super Metroid Patch Setup"; Types: full playing Name: "client/factorio"; Description: "Factorio"; Types: full playing Name: "client/minecraft"; Description: "Minecraft"; Types: full playing; ExtraDiskSpaceRequired: 226894278 Name: "client/text"; Description: "Text, to !command and chat"; Types: full playing @@ -64,18 +66,19 @@ Name: "client/text"; Description: "Text, to !command and chat"; Types: full play NAME: "{app}"; Flags: setntfscompression; Permissions: everyone-modify users-modify authusers-modify; [Files] -Source: "{code:GetROMPath}"; DestDir: "{app}"; DestName: "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"; Flags: external; Components: client/lttp or generator/lttp +Source: "{code:GetROMPath}"; DestDir: "{app}"; DestName: "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"; Flags: external; Components: client/sni/lttp or generator/lttp +Source: "{code:GetSMROMPath}"; DestDir: "{app}"; DestName: "Super Metroid (JU).sfc"; Flags: external; Components: client/sni/sm or generator/sm Source: "{code:GetOoTROMPath}"; DestDir: "{app}"; DestName: "The Legend of Zelda - Ocarina of Time.z64"; Flags: external; Components: generator/oot Source: "{#sourcepath}\*"; Excludes: "*.sfc, *.log, data\sprites\alttpr, SNI, EnemizerCLI, Archipelago*.exe"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs -Source: "{#sourcepath}\SNI\*"; Excludes: "*.sfc, *.log"; DestDir: "{app}\SNI"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: client/lttp +Source: "{#sourcepath}\SNI\*"; Excludes: "*.sfc, *.log"; DestDir: "{app}\SNI"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: client/sni Source: "{#sourcepath}\EnemizerCLI\*"; Excludes: "*.sfc, *.log"; DestDir: "{app}\EnemizerCLI"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: generator/lttp Source: "{#sourcepath}\ArchipelagoGenerate.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: generator Source: "{#sourcepath}\ArchipelagoServer.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: server Source: "{#sourcepath}\ArchipelagoFactorioClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/factorio Source: "{#sourcepath}\ArchipelagoTextClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/text -Source: "{#sourcepath}\ArchipelagoLttPClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/lttp -Source: "{#sourcepath}\ArchipelagoLttPAdjuster.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/lttp or generator/lttp +Source: "{#sourcepath}\ArchipelagoSNIClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/sni +Source: "{#sourcepath}\ArchipelagoLttPAdjuster.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/sni/lttp or generator/lttp Source: "{#sourcepath}\ArchipelagoMinecraftClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/minecraft Source: "vc_redist.x64.exe"; DestDir: {tmp}; Flags: deleteafterinstall @@ -85,30 +88,38 @@ Source: "{tmp}\forge-installer.jar"; DestDir: "{app}"; Flags: skipifsourcedoesnt [Icons] Name: "{group}\{#MyAppName} Folder"; Filename: "{app}"; Name: "{group}\{#MyAppName} Server"; Filename: "{app}\{#MyAppExeName}"; Components: server -Name: "{group}\{#MyAppName} Text Client"; Filename: "{app}\ArchipelagoTextClient.exe"; Components: client/lttp -Name: "{group}\{#MyAppName} LttP Client"; Filename: "{app}\ArchipelagoLttPClient.exe"; Components: client/lttp +Name: "{group}\{#MyAppName} Text Client"; Filename: "{app}\ArchipelagoTextClient.exe"; Components: client/text +Name: "{group}\{#MyAppName} SNI Client"; Filename: "{app}\ArchipelagoSNIClient.exe"; Components: client/sni Name: "{group}\{#MyAppName} Factorio Client"; Filename: "{app}\ArchipelagoFactorioClient.exe"; Components: client/factorio Name: "{group}\{#MyAppName} Minecraft Client"; Filename: "{app}\ArchipelagoMinecraftClient.exe"; Components: client/minecraft Name: "{commondesktop}\{#MyAppName} Folder"; Filename: "{app}"; Tasks: desktopicon Name: "{commondesktop}\{#MyAppName} Server"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon; Components: server -Name: "{commondesktop}\{#MyAppName} LttP Client"; Filename: "{app}\ArchipelagoLttPClient.exe"; Tasks: desktopicon; Components: client/lttp +Name: "{commondesktop}\{#MyAppName} SNI Client"; Filename: "{app}\ArchipelagoSNIClient.exe"; Tasks: desktopicon; Components: client/sni Name: "{commondesktop}\{#MyAppName} Factorio Client"; Filename: "{app}\ArchipelagoFactorioClient.exe"; Tasks: desktopicon; Components: client/factorio [Run] Filename: "{tmp}\vc_redist.x64.exe"; Parameters: "/passive /norestart"; Check: IsVCRedist64BitNeeded; StatusMsg: "Installing VC++ redistributable..." -Filename: "{app}\ArchipelagoLttPAdjuster"; Parameters: "--update_sprites"; StatusMsg: "Updating Sprite Library..."; Components: client/lttp or generator/lttp +Filename: "{app}\ArchipelagoLttPAdjuster"; Parameters: "--update_sprites"; StatusMsg: "Updating Sprite Library..."; Components: client/sni/lttp or generator/lttp Filename: "{app}\jre8\bin\java.exe"; Parameters: "-jar ""{app}\forge-installer.jar"" --installServer ""{app}\Minecraft Forge server"""; Flags: runhidden; Check: IsForgeNeeded(); StatusMsg: "Installing Forge Server..."; Components: client/minecraft [UninstallDelete] Type: dirifempty; Name: "{app}" +[InstallDelete] +Type: files; Name: "{app}\ArchipelagoLttPClient.exe" + [Registry] -Root: HKCR; Subkey: ".apbp"; ValueData: "{#MyAppName}patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/lttp -Root: HKCR; Subkey: "{#MyAppName}patch"; ValueData: "Archipelago Binary Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/lttp -Root: HKCR; Subkey: "{#MyAppName}patch\DefaultIcon"; ValueData: "{app}\ArchipelagoLttPClient.exe,0"; ValueType: string; ValueName: ""; Components: client/lttp -Root: HKCR; Subkey: "{#MyAppName}patch\shell\open\command"; ValueData: """{app}\ArchipelagoLttPClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/lttp +Root: HKCR; Subkey: ".apbp"; ValueData: "{#MyAppName}patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: "{#MyAppName}patch"; ValueData: "Archipelago Binary Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: "{#MyAppName}patch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: "{#MyAppName}patch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/sni + +Root: HKCR; Subkey: ".apm3"; ValueData: "{#MyAppName}smpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: "{#MyAppName}patch"; ValueData: "Archipelago Super Metroid Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: "{#MyAppName}patch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: "{#MyAppName}patch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/sni Root: HKCR; Subkey: ".apmc"; ValueData: "{#MyAppName}mcdata"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/minecraft Root: HKCR; Subkey: "{#MyAppName}mcdata"; ValueData: "Archipelago Minecraft Data"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/minecraft @@ -190,11 +201,17 @@ begin ZipFile.Items, SHCONTCH_NOPROGRESSBOX or SHCONTCH_RESPONDYESTOALL); end; -var ROMFilePage: TInputFileWizardPage; var R : longint; + var rom: string; +var ROMFilePage: TInputFileWizardPage; + +var smrom: string; +var SMRomFilePage: TInputFileWizardPage; + var ootrom: string; var OoTROMFilePage: TInputFileWizardPage; + var MinecraftDownloadPage: TDownloadWizardPage; procedure AddRomPage(); @@ -225,6 +242,34 @@ begin '.sfc'); end; +procedure AddSMRomPage(); +begin + smrom := FileSearch('Super Metroid (JU).sfc', WizardDirValue()); + if Length(smrom) > 0 then + begin + log('existing SM ROM found'); + log(IntToStr(CompareStr(GetMD5OfFile(smrom), '21f3e98df4780ee1c667b84e57d88675'))); + if CompareStr(GetMD5OfFile(smrom), '21f3e98df4780ee1c667b84e57d88675') = 0 then + begin + log('existing SM ROM verified'); + exit; + end; + log('existing SM ROM failed verification'); + end; + smrom := '' + SMROMFilePage := + CreateInputFilePage( + wpSelectComponents, + 'Select ROM File', + 'Where is your Super Metroid located?', + 'Select the file, then click Next.'); + + SMROMFilePage.Add( + 'Location of Super Metroid ROM file:', + 'SNES ROM files|*.sfc|All files|*.*', + '.sfc'); +end; + procedure AddMinecraftDownloads(); begin MinecraftDownloadPage := CreateDownloadPage(SetupMessage(msgWizardPreparing), SetupMessage(msgPreparingDesc), @OnDownloadMinecraftProgress); @@ -295,6 +340,7 @@ procedure InitializeWizard(); begin AddOoTRomPage(); AddRomPage(); + AddSMRomPage(); AddMinecraftDownloads(); end; @@ -303,7 +349,9 @@ function ShouldSkipPage(PageID: Integer): Boolean; begin Result := False; if (assigned(ROMFilePage)) and (PageID = ROMFilePage.ID) then - Result := not (WizardIsComponentSelected('client/lttp') or WizardIsComponentSelected('generator/lttp')); + Result := not (WizardIsComponentSelected('client/sni/lttp') or WizardIsComponentSelected('generator/lttp')); + if (assigned(SMROMFilePage)) and (PageID = SMROMFilePage.ID) then + Result := not (WizardIsComponentSelected('client/sni/sm') or WizardIsComponentSelected('generator/sm')); if (assigned(OoTROMFilePage)) and (PageID = OoTROMFilePage.ID) then Result := not (WizardIsComponentSelected('generator/oot')); end; @@ -324,6 +372,22 @@ begin Result := ''; end; +function GetSMROMPath(Param: string): string; +begin + if Length(smrom) > 0 then + Result := smrom + else if Assigned(SMRomFilePage) then + begin + R := CompareStr(GetMD5OfFile(SMROMFilePage.Values[0]), '21f3e98df4780ee1c667b84e57d88675') + if R <> 0 then + MsgBox('Super Metroid ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); + + Result := SMROMFilePage.Values[0] + end + else + Result := ''; + end; + function GetOoTROMPath(Param: string): string; begin if Length(ootrom) > 0 then diff --git a/inno_setup_38.iss b/inno_setup_38.iss index 49caf36e..f5fd9c53 100644 --- a/inno_setup_38.iss +++ b/inno_setup_38.iss @@ -50,11 +50,14 @@ Name: "custom"; Description: "Custom installation"; Flags: iscustom [Components] Name: "core"; Description: "Core Files"; Types: full hosting playing custom; Flags: fixed Name: "generator"; Description: "Generator"; Types: full hosting +Name: "generator/sm"; Description: "Super Metroid ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 3145728 Name: "generator/lttp"; Description: "A Link to the Past ROM Setup and Enemizer"; Types: full hosting; ExtraDiskSpaceRequired: 5191680 Name: "generator/oot"; Description: "Ocarina of Time ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 100663296 Name: "server"; Description: "Server"; Types: full hosting Name: "client"; Description: "Clients"; Types: full playing -Name: "client/lttp"; Description: "A Link to the Past"; Types: full playing +Name: "client/sni"; Description: "SNI Client"; Types: full playing +Name: "client/sni/lttp"; Description: "SNI Client - A Link to the Past Patch Setup"; Types: full playing +Name: "client/sni/sm"; Description: "SNI Client - Super Metroid Patch Setup"; Types: full playing Name: "client/factorio"; Description: "Factorio"; Types: full playing Name: "client/minecraft"; Description: "Minecraft"; Types: full playing; ExtraDiskSpaceRequired: 226894278 Name: "client/text"; Description: "Text, to !command and chat"; Types: full playing @@ -63,18 +66,19 @@ Name: "client/text"; Description: "Text, to !command and chat"; Types: full play NAME: "{app}"; Flags: setntfscompression; Permissions: everyone-modify users-modify authusers-modify; [Files] -Source: "{code:GetROMPath}"; DestDir: "{app}"; DestName: "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"; Flags: external; Components: client/lttp or generator/lttp +Source: "{code:GetROMPath}"; DestDir: "{app}"; DestName: "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"; Flags: external; Components: client/sni/lttp or generator/lttp +Source: "{code:GetSMROMPath}"; DestDir: "{app}"; DestName: "Super Metroid (JU).sfc"; Flags: external; Components: client/sni/sm or generator/sm Source: "{code:GetOoTROMPath}"; DestDir: "{app}"; DestName: "The Legend of Zelda - Ocarina of Time.z64"; Flags: external; Components: generator/oot Source: "{#sourcepath}\*"; Excludes: "*.sfc, *.log, data\sprites\alttpr, SNI, EnemizerCLI, Archipelago*.exe"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs -Source: "{#sourcepath}\SNI\*"; Excludes: "*.sfc, *.log"; DestDir: "{app}\SNI"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: client/lttp +Source: "{#sourcepath}\SNI\*"; Excludes: "*.sfc, *.log"; DestDir: "{app}\SNI"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: client/sni Source: "{#sourcepath}\EnemizerCLI\*"; Excludes: "*.sfc, *.log"; DestDir: "{app}\EnemizerCLI"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: generator/lttp Source: "{#sourcepath}\ArchipelagoGenerate.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: generator Source: "{#sourcepath}\ArchipelagoServer.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: server Source: "{#sourcepath}\ArchipelagoFactorioClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/factorio Source: "{#sourcepath}\ArchipelagoTextClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/text -Source: "{#sourcepath}\ArchipelagoLttPClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/lttp -Source: "{#sourcepath}\ArchipelagoLttPAdjuster.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/lttp or generator/lttp +Source: "{#sourcepath}\ArchipelagoSNIClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/sni +Source: "{#sourcepath}\ArchipelagoLttPAdjuster.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/sni/lttp or generator/lttp Source: "{#sourcepath}\ArchipelagoMinecraftClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/minecraft Source: "vc_redist.x64.exe"; DestDir: {tmp}; Flags: deleteafterinstall @@ -84,30 +88,38 @@ Source: "{tmp}\forge-installer.jar"; DestDir: "{app}"; Flags: skipifsourcedoesnt [Icons] Name: "{group}\{#MyAppName} Folder"; Filename: "{app}"; Name: "{group}\{#MyAppName} Server"; Filename: "{app}\{#MyAppExeName}"; Components: server -Name: "{group}\{#MyAppName} Text Client"; Filename: "{app}\ArchipelagoTextClient.exe"; Components: client/lttp -Name: "{group}\{#MyAppName} LttP Client"; Filename: "{app}\ArchipelagoLttPClient.exe"; Components: client/lttp +Name: "{group}\{#MyAppName} Text Client"; Filename: "{app}\ArchipelagoTextClient.exe"; Components: client/text +Name: "{group}\{#MyAppName} SNI Client"; Filename: "{app}\ArchipelagoSNIClient.exe"; Components: client/sni Name: "{group}\{#MyAppName} Factorio Client"; Filename: "{app}\ArchipelagoFactorioClient.exe"; Components: client/factorio Name: "{group}\{#MyAppName} Minecraft Client"; Filename: "{app}\ArchipelagoMinecraftClient.exe"; Components: client/minecraft Name: "{commondesktop}\{#MyAppName} Folder"; Filename: "{app}"; Tasks: desktopicon Name: "{commondesktop}\{#MyAppName} Server"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon; Components: server -Name: "{commondesktop}\{#MyAppName} LttP Client"; Filename: "{app}\ArchipelagoLttPClient.exe"; Tasks: desktopicon; Components: client/lttp +Name: "{commondesktop}\{#MyAppName} SNI Client"; Filename: "{app}\ArchipelagoSNIClient.exe"; Tasks: desktopicon; Components: client/sni Name: "{commondesktop}\{#MyAppName} Factorio Client"; Filename: "{app}\ArchipelagoFactorioClient.exe"; Tasks: desktopicon; Components: client/factorio [Run] Filename: "{tmp}\vc_redist.x64.exe"; Parameters: "/passive /norestart"; Check: IsVCRedist64BitNeeded; StatusMsg: "Installing VC++ redistributable..." -Filename: "{app}\ArchipelagoLttPAdjuster"; Parameters: "--update_sprites"; StatusMsg: "Updating Sprite Library..."; Components: client/lttp or generator/lttp +Filename: "{app}\ArchipelagoLttPAdjuster"; Parameters: "--update_sprites"; StatusMsg: "Updating Sprite Library..."; Components: client/sni/lttp or generator/lttp Filename: "{app}\jre8\bin\java.exe"; Parameters: "-jar ""{app}\forge-installer.jar"" --installServer ""{app}\Minecraft Forge server"""; Flags: runhidden; Check: IsForgeNeeded(); StatusMsg: "Installing Forge Server..."; Components: client/minecraft [UninstallDelete] Type: dirifempty; Name: "{app}" +[InstallDelete] +Type: files; Name: "{app}\ArchipelagoLttPClient.exe" + [Registry] -Root: HKCR; Subkey: ".apbp"; ValueData: "{#MyAppName}patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/lttp -Root: HKCR; Subkey: "{#MyAppName}patch"; ValueData: "Archipelago Binary Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/lttp -Root: HKCR; Subkey: "{#MyAppName}patch\DefaultIcon"; ValueData: "{app}\ArchipelagoLttPClient.exe,0"; ValueType: string; ValueName: ""; Components: client/lttp -Root: HKCR; Subkey: "{#MyAppName}patch\shell\open\command"; ValueData: """{app}\ArchipelagoLttPClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/lttp +Root: HKCR; Subkey: ".apbp"; ValueData: "{#MyAppName}patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: "{#MyAppName}patch"; ValueData: "Archipelago Binary Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: "{#MyAppName}patch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: "{#MyAppName}patch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/sni + +Root: HKCR; Subkey: ".apm3"; ValueData: "{#MyAppName}smpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: "{#MyAppName}smpatch"; ValueData: "Archipelago Super Metroid Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: "{#MyAppName}smpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: "{#MyAppName}smpatch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/sni Root: HKCR; Subkey: ".apmc"; ValueData: "{#MyAppName}mcdata"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/minecraft Root: HKCR; Subkey: "{#MyAppName}mcdata"; ValueData: "Archipelago Minecraft Data"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/minecraft @@ -189,11 +201,17 @@ begin ZipFile.Items, SHCONTCH_NOPROGRESSBOX or SHCONTCH_RESPONDYESTOALL); end; -var ROMFilePage: TInputFileWizardPage; var R : longint; + var rom: string; +var ROMFilePage: TInputFileWizardPage; + +var smrom: string; +var SMRomFilePage: TInputFileWizardPage; + var ootrom: string; var OoTROMFilePage: TInputFileWizardPage; + var MinecraftDownloadPage: TDownloadWizardPage; procedure AddRomPage(); @@ -224,6 +242,34 @@ begin '.sfc'); end; +procedure AddSMRomPage(); +begin + smrom := FileSearch('Super Metroid (JU).sfc', WizardDirValue()); + if Length(smrom) > 0 then + begin + log('existing SM ROM found'); + log(IntToStr(CompareStr(GetMD5OfFile(smrom), '21f3e98df4780ee1c667b84e57d88675'))); + if CompareStr(GetMD5OfFile(smrom), '21f3e98df4780ee1c667b84e57d88675') = 0 then + begin + log('existing SM ROM verified'); + exit; + end; + log('existing SM ROM failed verification'); + end; + smrom := '' + SMROMFilePage := + CreateInputFilePage( + wpSelectComponents, + 'Select ROM File', + 'Where is your Super Metroid located?', + 'Select the file, then click Next.'); + + SMROMFilePage.Add( + 'Location of Super Metroid ROM file:', + 'SNES ROM files|*.sfc|All files|*.*', + '.sfc'); +end; + procedure AddMinecraftDownloads(); begin MinecraftDownloadPage := CreateDownloadPage(SetupMessage(msgWizardPreparing), SetupMessage(msgPreparingDesc), @OnDownloadMinecraftProgress); @@ -294,6 +340,7 @@ procedure InitializeWizard(); begin AddOoTRomPage(); AddRomPage(); + AddSMRomPage(); AddMinecraftDownloads(); end; @@ -302,7 +349,9 @@ function ShouldSkipPage(PageID: Integer): Boolean; begin Result := False; if (assigned(ROMFilePage)) and (PageID = ROMFilePage.ID) then - Result := not (WizardIsComponentSelected('client/lttp') or WizardIsComponentSelected('generator/lttp')); + Result := not (WizardIsComponentSelected('client/sni/lttp') or WizardIsComponentSelected('generator/lttp')); + if (assigned(SMROMFilePage)) and (PageID = SMROMFilePage.ID) then + Result := not (WizardIsComponentSelected('client/sni/sm') or WizardIsComponentSelected('generator/sm')); if (assigned(OoTROMFilePage)) and (PageID = OoTROMFilePage.ID) then Result := not (WizardIsComponentSelected('generator/oot')); end; @@ -323,6 +372,22 @@ begin Result := ''; end; +function GetSMROMPath(Param: string): string; +begin + if Length(smrom) > 0 then + Result := smrom + else if Assigned(SMRomFilePage) then + begin + R := CompareStr(GetMD5OfFile(SMROMFilePage.Values[0]), '21f3e98df4780ee1c667b84e57d88675') + if R <> 0 then + MsgBox('Super Metroid ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); + + Result := SMROMFilePage.Values[0] + end + else + Result := ''; + end; + function GetOoTROMPath(Param: string): string; begin if Length(ootrom) > 0 then diff --git a/kvui.py b/kvui.py index 2c8c5d08..1a0071c9 100644 --- a/kvui.py +++ b/kvui.py @@ -7,6 +7,7 @@ import sys os.environ["KIVY_NO_CONSOLELOG"] = "1" os.environ["KIVY_NO_FILELOG"] = "1" os.environ["KIVY_NO_ARGS"] = "1" +os.environ["KIVY_LOG_ENABLE"] = "0" from kivy.app import App from kivy.core.window import Window @@ -286,12 +287,12 @@ class FactorioManager(GameManager): base_title = "Archipelago Factorio Client" -class LttPManager(GameManager): +class SNIManager(GameManager): logging_pairs = [ ("Client", "Archipelago"), ("SNES", "SNES"), ] - base_title = "Archipelago LttP Client" + base_title = "Archipelago SNI Client" class TextManager(GameManager): diff --git a/playerSettings.yaml b/playerSettings.yaml index 838817c1..d1e7e430 100644 --- a/playerSettings.yaml +++ b/playerSettings.yaml @@ -30,6 +30,8 @@ game: # Pick a game to play Subnautica: 0 Slay the Spire: 0 Ocarina of Time: 0 + Super Metroid: 0 + requires: version: 0.1.7 # Version of Archipelago required for this yaml to work as expected. # Shared Options supported by all games: @@ -57,6 +59,270 @@ progression_balancing: # exclude_locations: # Force certain locations to never contain progression items, and always be filled with junk. # - "Master Sword Pedestal" +Super Metroid: # see https://randommetroidsolver.pythonanywhere.com/randomizer advanced tab for detailed info on each option +# start_inventory: # Begin the file with the listed items/upgrades +# Screw Attack: 1 +# Bomb: 1 +# Speed Booster: 1 +# Grappling Beam: 1 +# Space Jump: 1 +# Hi-Jump Boots: 1 +# Spring Ball: 1 +# Charge Beam: 1 +# Ice Beam: 1 +# Spazer: 1 +# Reserve Tank: 4 +# Missile: 46 +# Super Missile: 20 +# Power Bomb: 20 +# Energy Tank: 14 +# Morph Ball: 1 +# X-Ray Scope: 1 +# Wave Beam: 1 +# Plasma Beam: 1 +# Varia Suit: 1 +# Gravity Suit: 1 + start_inventory_removes_from_pool: + on: 0 + off: 1 + death_link: + on: 0 + off: 1 + preset: # choose one of the preset or specify "custom" to use customPreset option + newbie: 0 + casual: 0 + regular: 1 + veteran: 0 + expert: 0 + master: 0 + samus: 0 + Season_Races: 0 + SMRAT2021: 0 + solution: 0 + custom: 0 # see https://randommetroidsolver.pythonanywhere.com/presets for detailed info on each preset settings + varia_custom: 0 # use an entry from the preset list on https://randommetroidsolver.pythonanywhere.com/presets + varia_custom_preset: # use an entry from the preset list on https://randommetroidsolver.pythonanywhere.com/presets + regular + start_location: + Ceres: 0 + Landing_Site: 1 + Gauntlet_Top: 0 + Green_Brinstar_Elevator: 0 + Big_Pink: 0 + Etecoons_Supers: 0 + Wrecked_Ship_Main: 0 + Firefleas_Top: 0 + Business_Center: 0 + Bubble_Mountain: 0 + Mama_Turtle: 0 + Watering_Hole: 0 + Aqueduct: 0 + Red_Brinstar_Elevator: 0 + Golden_Four: 0 + max_difficulty: + easy: 0 + medium: 0 + hard: 0 + harder: 0 + hardcore: 1 + mania: 0 + infinity: 0 + morph_placement: + early: 1 + normal: 0 + suits_restriction: + on: 1 + off: 0 + strict_minors: + on: 0 + off: 1 + missile_qty: 30 # a range between 10 and 90 that is divided by 10 as a float + super_qty: 20 # a range between 10 and 90 that is divided by 10 as a float + power_bomb_qty: 10 # a range between 10 and 90 that is divided by 10 as a float + minor_qty: 100 # a range between 7 (minimum to beat the game) and 100 + energy_qty: + ultra_sparse: 0 + sparse: 0 + medium: 0 + vanilla: 1 + area_randomization: + on: 0 + light: 0 + off: 1 + area_layout: + on: 0 + off: 1 + doors_colors_rando: + on: 0 + off: 1 + allow_grey_doors: + on: 0 + off: 1 + boss_randomization: + on: 0 + off: 1 + fun_combat: + on: 0 + off: 1 + fun_movement: + on: 0 + off: 1 + fun_suits: + on: 0 + off: 1 + layout_patches: + on: 1 + off: 0 + varia_tweaks: + on: 0 + off: 1 + nerfed_charge: + on: 0 + off: 1 + gravity_behaviour: + Vanilla: 0 + Balanced: 1 + Progressive: 0 + elevators_doors_speed: + on: 1 + off: 0 + spin_jump_restart: + on: 0 + off: 1 + infinite_space_jump: + on: 0 + off: 1 + refill_before_save: + on: 0 + off: 1 + hud: + on: 0 + off: 1 + animals: + on: 0 + off: 1 + no_music: + on: 0 + off: 1 + random_music: + on: 0 + off: 1 + #item_sounds: always forced on due to a conflict in patching + #majors_split: not supported always "Full" + #scav_num_locs: not supported always off + #scav_randomized: not supported always off + #scav_escape: not supported always off + #progression_speed: not supported always random + #progression_difficulty: not supported always random + #hide_items: not supported always off + #minimizer: not supported always off + #minimizer_qty: not supported always off + #minimizer_tourian: not supported always off + #escape_rando: not supported always off + #remove_escape_enemies: not supported always off + #rando_speed: not supported always off + custom_preset: # see https://randommetroidsolver.pythonanywhere.com/presets for detailed info on each preset settings + Knows: # each skill (know) has a pair [can use, perceived difficulty using one of the following values] + # easy = 1 + # medium = 5 + # hard = 10 + # harder = 25 + # hardcore = 50 + # mania = 100 + Mockball: [True, 1] + SimpleShortCharge: [True, 1] + InfiniteBombJump: [True, 5] + GreenGateGlitch: [True, 5] + ShortCharge: [False, 0] + GravityJump: [True, 10] + SpringBallJump: [True, 10] + SpringBallJumpFromWall: [False, 0] + GetAroundWallJump: [True, 10] + DraygonGrappleKill: [True, 5] + DraygonSparkKill: [False, 0] + MicrowaveDraygon: [True, 1] + MicrowavePhantoon: [True, 5] + IceZebSkip: [False, 0] + SpeedZebSkip: [False, 0] + HiJumpMamaTurtle: [False, 0] + GravLessLevel1: [True, 50] + GravLessLevel2: [False, 0] + GravLessLevel3: [False, 0] + CeilingDBoost: [True, 1] + BillyMays: [True, 1] + AlcatrazEscape: [True, 25] + ReverseGateGlitch: [True, 5] + ReverseGateGlitchHiJumpLess: [False, 0] + EarlyKraid: [True, 1] + XrayDboost: [False, 0] + XrayIce: [True, 10] + RedTowerClimb: [True, 25] + RonPopeilScrew: [False, 0] + OldMBWithSpeed: [False, 0] + Moondance: [False, 0] + HiJumpLessGauntletAccess: [True, 50] + HiJumpGauntletAccess: [True, 25] + LowGauntlet: [False, 0] + IceEscape: [False, 0] + WallJumpCathedralExit: [True, 5] + BubbleMountainWallJump: [True, 5] + NovaBoost: [False, 0] + NorfairReserveDBoost: [False, 0] + CrocPBsDBoost: [False, 0] + CrocPBsIce: [False, 0] + IceMissileFromCroc: [False, 0] + FrogSpeedwayWithoutSpeed: [False, 0] + LavaDive: [True, 50] + LavaDiveNoHiJump: [False, 0] + WorstRoomIceCharge: [False, 0] + ScrewAttackExit: [False, 0] + ScrewAttackExitWithoutScrew: [False, 0] + FirefleasWalljump: [True, 25] + ContinuousWallJump: [False, 0] + DiagonalBombJump: [False, 0] + MockballWs: [False, 0] + SpongeBathBombJump: [False, 0] + SpongeBathHiJump: [True, 1] + SpongeBathSpeed: [True, 5] + TediousMountEverest: [False, 0] + DoubleSpringBallJump: [False, 0] + BotwoonToDraygonWithIce: [False, 0] + DraygonRoomGrappleExit: [False, 0] + DraygonRoomCrystalFlash: [False, 0] + PreciousRoomXRayExit: [False, 0] + MochtroidClip: [True, 5] + PuyoClip: [False, 0] + PuyoClipXRay: [False, 0] + SnailClip: [False, 0] + SuitlessPuyoClip: [False, 0] + KillPlasmaPiratesWithSpark: [False, 0] + KillPlasmaPiratesWithCharge: [True, 5] + AccessSpringBallWithHiJump: [True, 1] + AccessSpringBallWithSpringBallBombJumps: [True, 10] + AccessSpringBallWithBombJumps: [False, 0] + AccessSpringBallWithSpringBallJump: [False, 0] + AccessSpringBallWithXRayClimb: [False, 0] + AccessSpringBallWithGravJump: [False, 0] + Controller: + A: Jump + B: Dash + X: Shoot + Y: Item Cancel + L: Angle Down + R: Angle Up + Select: Item Select + Moonwalk: False + Settings: + Ice: "Gimme energy" + MainUpperNorfair: "Gimme energy" + LowerNorfair: "Default" + Kraid: "Default" + Phantoon: "Default" + Draygon: "Default" + Ridley: "Default" + MotherBrain: "Default" + X-Ray: "I don't like spikes" + Gauntlet: "I don't like acid" Subnautica: {} Slay the Spire: character: # Pick What Character you wish to play with. @@ -1415,4 +1681,4 @@ triggers: percentage: 0 # AND has a 0 percent chance (meaning this is default disabled, just to show how it works) options: # then inserts these options A Link to the Past: - swordless: off \ No newline at end of file + swordless: off diff --git a/requirements.txt b/requirements.txt index 77c6107d..bf31f568 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ colorama>=0.4.4 websockets>=10.0 PyYAML>=6.0 fuzzywuzzy>=0.18.0 -prompt_toolkit>=3.0.20 +prompt_toolkit>=3.0.22 appdirs>=1.4.4 -jinja2>=3.0.2 +jinja2>=3.0.3 schema>=0.7.4 diff --git a/setup.py b/setup.py index d12c5d4d..908645fc 100644 --- a/setup.py +++ b/setup.py @@ -73,8 +73,8 @@ scripts = { "MultiServer.py": ("ArchipelagoServer", False, icon), "Generate.py": ("ArchipelagoGenerate", False, icon), "CommonClient.py": ("ArchipelagoTextClient", True, icon), - # LttP - "LttPClient.py": ("ArchipelagoLttPClient", True, icon), + # SNI + "SNIClient.py": ("ArchipelagoSNIClient", True, icon), "LttPAdjuster.py": ("ArchipelagoLttPAdjuster", True, icon), # Factorio "FactorioClient.py": ("ArchipelagoFactorioClient", True, icon), diff --git a/test/general/TestItems.py b/test/general/TestItems.py new file mode 100644 index 00000000..6cf3e2d1 --- /dev/null +++ b/test/general/TestItems.py @@ -0,0 +1,12 @@ +import unittest +from worlds.AutoWorld import AutoWorldRegister + + +class TestBase(unittest.TestCase): + def testCreateItem(self): + for game_name, world_type in AutoWorldRegister.world_types.items(): + proxy_world = world_type(None, 0) # this is identical to MultiServer.py creating worlds + for item_name in world_type.item_name_to_id: + with self.subTest("Create Item", item_name=item_name, game_name=game_name): + item = proxy_world.create_item(item_name) + self.assertEqual(item.name, item_name) diff --git a/test/general/TestReachability.py b/test/general/TestReachability.py index b730d7be..8786d13c 100644 --- a/test/general/TestReachability.py +++ b/test/general/TestReachability.py @@ -6,7 +6,6 @@ from worlds.AutoWorld import AutoWorldRegister from . import setup_default_world class TestBase(unittest.TestCase): - _state_cache = {} gen_steps = ["generate_early", "create_regions", "create_items", "set_rules", "generate_basic", "pre_fill"] def testAllStateCanReachEverything(self): diff --git a/test/general/TestUniqueness.py b/test/general/TestUniqueness.py index baf3792c..f8ea8b26 100644 --- a/test/general/TestUniqueness.py +++ b/test/general/TestUniqueness.py @@ -1,12 +1,8 @@ import unittest -from BaseClasses import MultiWorld from worlds.AutoWorld import AutoWorldRegister class TestBase(unittest.TestCase): - world: MultiWorld - _state_cache = {} - def testUniqueItems(self): known_item_ids = set() for gamename, world_type in AutoWorldRegister.world_types.items(): diff --git a/worlds/alttp/Rom.py b/worlds/alttp/Rom.py index 2226012a..7235844a 100644 --- a/worlds/alttp/Rom.py +++ b/worlds/alttp/Rom.py @@ -4,7 +4,7 @@ import Utils from Patch import read_rom JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'e397fef0e947d1bd760c68c4fe99a600' +RANDOMIZERBASEHASH = '9952c2a3ec1b421e408df0d20c8f0c7f' ROM_PLAYER_LIMIT = 255 import io @@ -1645,7 +1645,6 @@ def patch_rom(world, rom, player, enemized): # remote items flag, does not currently work rom.write_byte(0x18637C, int(world.worlds[player].remote_items)) - rom.write_byte(0x18008D, int(world.death_link[player])) # set rom name # 21 bytes from Main import __version__ @@ -1768,7 +1767,7 @@ def hud_format_text(text): def apply_rom_settings(rom, beep, color, quickswap, menuspeed, music: bool, sprite: str, palettes_options, world=None, player=1, allow_random_on_event=False, reduceflashing=False, - triforcehud: str = None): + triforcehud: str = None, deathlink: bool = False): local_random = random if not world else world.slot_seeds[player] disable_music: bool = not music # enable instant item menu @@ -1902,6 +1901,8 @@ def apply_rom_settings(rom, beep, color, quickswap, menuspeed, music: bool, spri elif palettes_options['dungeon'] == 'random': randomize_uw_palettes(rom, local_random) + rom.write_byte(0x18008D, int(deathlink)) + apply_random_sprite_on_event(rom, sprite, local_random, allow_random_on_event, world.sprite_pool[player] if world else []) if isinstance(rom, LocalRom): diff --git a/worlds/alttp/__init__.py b/worlds/alttp/__init__.py index 47aad525..62cf4dec 100644 --- a/worlds/alttp/__init__.py +++ b/worlds/alttp/__init__.py @@ -294,7 +294,8 @@ class ALTTPWorld(World): world.sprite[player], palettes_options, world, player, True, reduceflashing=world.reduceflashing[player] or world.is_race, - triforcehud=world.triforcehud[player].current_key) + triforcehud=world.triforcehud[player].current_key, + deathlink=world.death_link[player]) outfilepname = f'_P{player}' outfilepname += f"_{world.player_name[player].replace(' ', '_')}" \ @@ -323,7 +324,7 @@ class ALTTPWorld(World): del (multidata["connect_names"][self.world.player_name[self.player]]) def get_required_client_version(self) -> tuple: - return max((0, 1, 4), super(ALTTPWorld, self).get_required_client_version()) + return max((0, 2, 0), super(ALTTPWorld, self).get_required_client_version()) def create_item(self, name: str) -> Item: return ALttPItem(name, self.player, **as_dict_item_table[name]) diff --git a/worlds/factorio/Mod.py b/worlds/factorio/Mod.py index 84c9da25..484ed626 100644 --- a/worlds/factorio/Mod.py +++ b/worlds/factorio/Mod.py @@ -29,7 +29,11 @@ base_info = { "author": "Berserker", "homepage": "https://archipelago.gg", "description": "Integration client for the Archipelago Randomizer", - "factorio_version": "1.1" + "factorio_version": "1.1", + "dependencies": [ + "base >= 1.1.0", + "? science-not-invited" + ] } recipe_time_scales = { @@ -95,7 +99,8 @@ def generate_mod(world, output_directory: str): "free_sample_blacklist": {item : 1 for item in free_sample_blacklist}, "progressive_technology_table": {tech.name : tech.progressive for tech in progressive_technology_table.values()}, - "custom_recipes": world.custom_recipes} + "custom_recipes": world.custom_recipes, + "max_science_pack": multiworld.max_science_pack[player].value} for factorio_option in Options.factorio_options: template_data[factorio_option] = getattr(multiworld, factorio_option)[player].value diff --git a/worlds/factorio/Technologies.py b/worlds/factorio/Technologies.py index 04736a2e..fbf283d3 100644 --- a/worlds/factorio/Technologies.py +++ b/worlds/factorio/Technologies.py @@ -85,7 +85,8 @@ class CustomTechnology(Technology): def __init__(self, origin: Technology, world, allowed_packs: Set[str], player: int): ingredients = origin.ingredients & allowed_packs military_allowed = "military-science-pack" in allowed_packs \ - and (ingredients & {"chemical-science-pack", "production-science-pack", "utility-science-pack"}) + and ((ingredients & {"chemical-science-pack", "production-science-pack", "utility-science-pack"}) + or origin.name == "rocket-silo") self.player = player if origin.name not in world.worlds[player].static_nodes: if military_allowed: diff --git a/worlds/factorio/data/mod/info.json b/worlds/factorio/data/mod/info.json index 2b6c428c..b93686d0 100644 --- a/worlds/factorio/data/mod/info.json +++ b/worlds/factorio/data/mod/info.json @@ -5,5 +5,9 @@ "author": "Berserker and Dewiniaid", "homepage": "https://archipelago.gg", "description": "Integration client for the Archipelago Randomizer", - "factorio_version": "1.1" + "factorio_version": "1.1", + "dependencies": [ + "base >= 1.1.0", + "? science-not-invited" + ] } diff --git a/worlds/factorio/data/mod_template/data-final-fixes.lua b/worlds/factorio/data/mod_template/data-final-fixes.lua index df911a18..4b2d67a2 100644 --- a/worlds/factorio/data/mod_template/data-final-fixes.lua +++ b/worlds/factorio/data/mod_template/data-final-fixes.lua @@ -26,11 +26,31 @@ template_tech.prerequisites = {} function prep_copy(new_copy, old_tech) old_tech.hidden = true - new_copy.unit = table.deepcopy(old_tech.unit) local ingredient_filter = allowed_ingredients[old_tech.name] if ingredient_filter ~= nil then - new_copy.unit.ingredients = filter_ingredients(new_copy.unit.ingredients, ingredient_filter) - new_copy.unit.ingredients = add_ingredients(new_copy.unit.ingredients, ingredient_filter) + if mods["science-not-invited"] then + local weights = { + ["automation-science-pack"] = 0, -- Red science + ["logistic-science-pack"] = 0, -- Green science + ["military-science-pack"] = 0, -- Black science + ["chemical-science-pack"] = 0, -- Blue science + ["production-science-pack"] = 0, -- Purple science + ["utility-science-pack"] = 0, -- Yellow science + ["space-science-pack"] = 0 -- Space science + } + for key, value in pairs(ingredient_filter) do + weights[key] = value + end + SNI.setWeights(weights) + SNI.sendInvite(old_tech) + -- SCIENCE-not-invited could potentially make tech cost 9.223e+18. + old_tech.unit.count = math.min(10000, old_tech.unit.count) + end + new_copy.unit = table.deepcopy(old_tech.unit) + new_copy.unit.ingredients = filter_ingredients(new_copy.unit.ingredients, ingredient_filter) + new_copy.unit.ingredients = add_ingredients(new_copy.unit.ingredients, ingredient_filter) + else + new_copy.unit = table.deepcopy(old_tech.unit) end end diff --git a/worlds/factorio/data/mod_template/data.lua b/worlds/factorio/data/mod_template/data.lua index bf10ee98..ef62f72a 100644 --- a/worlds/factorio/data/mod_template/data.lua +++ b/worlds/factorio/data/mod_template/data.lua @@ -1,2 +1,20 @@ {% from "macros.lua" import dict_to_lua %} data.raw["map-gen-presets"].default["archipelago"] = {{ dict_to_lua({"default": False, "order": "a", "basic_settings": world_gen["basic"], "advanced_settings": world_gen["advanced"]}) }} +if mods["science-not-invited"] then + local weights = { + ["automation-science-pack"] = 0, -- Red science + ["logistic-science-pack"] = 0, -- Green science + ["military-science-pack"] = 0, -- Black science + ["chemical-science-pack"] = 0, -- Blue science + ["production-science-pack"] = 0, -- Purple science + ["utility-science-pack"] = 0, -- Yellow science + ["space-science-pack"] = 0 -- Space science + } +{% if max_science_pack == 6 -%} + weights["space-science-pack"] = 1 +{%- endif %} +{% for key in allowed_science_packs -%} + weights["{{key}}"] = 1 +{% endfor %} + SNI.setWeights(weights) +end diff --git a/worlds/generic/__init__.py b/worlds/generic/__init__.py index 3693da47..50fb1038 100644 --- a/worlds/generic/__init__.py +++ b/worlds/generic/__init__.py @@ -1,6 +1,8 @@ from typing import NamedTuple, Union import logging +from BaseClasses import Item + from ..AutoWorld import World @@ -11,11 +13,17 @@ class GenericWorld(World): "Nothing": -1 } location_name_to_id = { - "Cheat Console" : -1, + "Cheat Console": -1, "Server": -2 } hidden = True + def create_item(self, name: str) -> Item: + if name == "Nothing": + return Item(name, False, -1, self.player) + raise KeyError(name) + + class PlandoItem(NamedTuple): item: str location: str diff --git a/worlds/oot/Entrance.py b/worlds/oot/Entrance.py index 310fcee4..4e11083e 100644 --- a/worlds/oot/Entrance.py +++ b/worlds/oot/Entrance.py @@ -5,8 +5,9 @@ from .Regions import TimeOfDay class OOTEntrance(Entrance): game: str = 'Ocarina of Time' - def __init__(self, player, name='', parent=None): + def __init__(self, player, world, name='', parent=None): super(OOTEntrance, self).__init__(player, name, parent) + self.world = world self.access_rules = [] self.reverse = None self.replaces = None @@ -17,3 +18,27 @@ class OOTEntrance(Entrance): self.primary = False self.always = False self.never = False + + def bind_two_way(self, other_entrance): + self.reverse = other_entrance + other_entrance.reverse = self + + def disconnect(self): + self.connected_region.entrances.remove(self) + previously_connected = self.connected_region + self.connected_region = None + return previously_connected + + def get_new_target(self): + root = self.world.get_region('Root Exits', self.player) + target_entrance = OOTEntrance(self.player, self.world, 'Root -> ' + self.connected_region.name, root) + target_entrance.connect(self.connected_region) + target_entrance.replaces = self + root.exits.append(target_entrance) + return target_entrance + + def assume_reachable(self): + if self.assumed == None: + self.assumed = self.get_new_target() + self.disconnect() + return self.assumed diff --git a/worlds/oot/EntranceShuffle.py b/worlds/oot/EntranceShuffle.py index f1e91773..1a8b4f2d 100644 --- a/worlds/oot/EntranceShuffle.py +++ b/worlds/oot/EntranceShuffle.py @@ -1,25 +1,771 @@ +from itertools import chain +import logging + +from worlds.generic.Rules import set_rule + +from .Hints import get_hint_area, HintAreaNotFound +from .Regions import TimeOfDay + + +def set_all_entrances_data(world, player): + for type, forward_entry, *return_entry in entrance_shuffle_table: + forward_entrance = world.get_entrance(forward_entry[0], player) + forward_entrance.data = forward_entry[1] + forward_entrance.type = type + forward_entrance.primary = True + if type == 'Grotto': + forward_entrance.data['index'] = 0x1000 + forward_entrance.data['grotto_id'] + if return_entry: + return_entry = return_entry[0] + return_entrance = world.get_entrance(return_entry[0], player) + return_entrance.data = return_entry[1] + return_entrance.type = type + forward_entrance.bind_two_way(return_entrance) + if type == 'Grotto': + return_entrance.data['index'] = 0x7FFF + + +def assume_entrance_pool(entrance_pool, ootworld): + assumed_pool = [] + for entrance in entrance_pool: + assumed_forward = entrance.assume_reachable() + if entrance.reverse != None: + assumed_return = entrance.reverse.assume_reachable() + if (entrance.type in ('Dungeon', 'Grotto', 'Grave') and entrance.reverse.name != 'Spirit Temple Lobby -> Desert Colossus From Spirit Lobby') or \ + (entrance.type == 'Interior' and ootworld.shuffle_special_interior_entrances): + # In most cases, Dungeon, Grotto/Grave and Simple Interior exits shouldn't be assumed able to give access to their parent region + set_rule(assumed_return, lambda state, **kwargs: False) + assumed_forward.bind_two_way(assumed_return) + assumed_pool.append(assumed_forward) + return assumed_pool + + +def build_one_way_targets(world, types_to_include, exclude=(), target_region_names=()): + one_way_entrances = [] + for pool_type in types_to_include: + one_way_entrances += world.get_shufflable_entrances(type=pool_type) + valid_one_way_entrances = list(filter(lambda entrance: entrance.name not in exclude, one_way_entrances)) + if target_region_names: + return [entrance.get_new_target() for entrance in valid_one_way_entrances + if entrance.connected_region.name in target_region_names] + return [entrance.get_new_target() for entrance in valid_one_way_entrances] + + +# Abbreviations +# DMC Death Mountain Crater +# DMT Death Mountain Trail +# GC Goron City +# GF Gerudo Fortress +# GS Gold Skulltula +# GV Gerudo Valley +# HC Hyrule Castle +# HF Hyrule Field +# KF Kokiri Forest +# LH Lake Hylia +# LLR Lon Lon Ranch +# LW Lost Woods +# OGC Outside Ganon's Castle +# SFM Sacred Forest Meadow +# ToT Temple of Time +# ZD Zora's Domain +# ZF Zora's Fountain +# ZR Zora's River + +entrance_shuffle_table = [ + ('Dungeon', ('KF Outside Deku Tree -> Deku Tree Lobby', { 'index': 0x0000 }), + ('Deku Tree Lobby -> KF Outside Deku Tree', { 'index': 0x0209, 'blue_warp': 0x0457 })), + ('Dungeon', ('Death Mountain -> Dodongos Cavern Beginning', { 'index': 0x0004 }), + ('Dodongos Cavern Beginning -> Death Mountain', { 'index': 0x0242, 'blue_warp': 0x047A })), + ('Dungeon', ('Zoras Fountain -> Jabu Jabus Belly Beginning', { 'index': 0x0028 }), + ('Jabu Jabus Belly Beginning -> Zoras Fountain', { 'index': 0x0221, 'blue_warp': 0x010E })), + ('Dungeon', ('SFM Forest Temple Entrance Ledge -> Forest Temple Lobby', { 'index': 0x0169 }), + ('Forest Temple Lobby -> SFM Forest Temple Entrance Ledge', { 'index': 0x0215, 'blue_warp': 0x0608 })), + ('Dungeon', ('DMC Fire Temple Entrance -> Fire Temple Lower', { 'index': 0x0165 }), + ('Fire Temple Lower -> DMC Fire Temple Entrance', { 'index': 0x024A, 'blue_warp': 0x0564 })), + ('Dungeon', ('Lake Hylia -> Water Temple Lobby', { 'index': 0x0010 }), + ('Water Temple Lobby -> Lake Hylia', { 'index': 0x021D, 'blue_warp': 0x060C })), + ('Dungeon', ('Desert Colossus -> Spirit Temple Lobby', { 'index': 0x0082 }), + ('Spirit Temple Lobby -> Desert Colossus From Spirit Lobby', { 'index': 0x01E1, 'blue_warp': 0x0610 })), + ('Dungeon', ('Graveyard Warp Pad Region -> Shadow Temple Entryway', { 'index': 0x0037 }), + ('Shadow Temple Entryway -> Graveyard Warp Pad Region', { 'index': 0x0205, 'blue_warp': 0x0580 })), + ('Dungeon', ('Kakariko Village -> Bottom of the Well', { 'index': 0x0098 }), + ('Bottom of the Well -> Kakariko Village', { 'index': 0x02A6 })), + ('Dungeon', ('ZF Ice Ledge -> Ice Cavern Beginning', { 'index': 0x0088 }), + ('Ice Cavern Beginning -> ZF Ice Ledge', { 'index': 0x03D4 })), + ('Dungeon', ('Gerudo Fortress -> Gerudo Training Grounds Lobby', { 'index': 0x0008 }), + ('Gerudo Training Grounds Lobby -> Gerudo Fortress', { 'index': 0x03A8 })), + + ('Interior', ('Kokiri Forest -> KF Midos House', { 'index': 0x0433 }), + ('KF Midos House -> Kokiri Forest', { 'index': 0x0443 })), + ('Interior', ('Kokiri Forest -> KF Sarias House', { 'index': 0x0437 }), + ('KF Sarias House -> Kokiri Forest', { 'index': 0x0447 })), + ('Interior', ('Kokiri Forest -> KF House of Twins', { 'index': 0x009C }), + ('KF House of Twins -> Kokiri Forest', { 'index': 0x033C })), + ('Interior', ('Kokiri Forest -> KF Know It All House', { 'index': 0x00C9 }), + ('KF Know It All House -> Kokiri Forest', { 'index': 0x026A })), + ('Interior', ('Kokiri Forest -> KF Kokiri Shop', { 'index': 0x00C1 }), + ('KF Kokiri Shop -> Kokiri Forest', { 'index': 0x0266 })), + ('Interior', ('Lake Hylia -> LH Lab', { 'index': 0x0043 }), + ('LH Lab -> Lake Hylia', { 'index': 0x03CC })), + ('Interior', ('LH Fishing Island -> LH Fishing Hole', { 'index': 0x045F }), + ('LH Fishing Hole -> LH Fishing Island', { 'index': 0x0309 })), + ('Interior', ('GV Fortress Side -> GV Carpenter Tent', { 'index': 0x03A0 }), + ('GV Carpenter Tent -> GV Fortress Side', { 'index': 0x03D0 })), + ('Interior', ('Market Entrance -> Market Guard House', { 'index': 0x007E }), + ('Market Guard House -> Market Entrance', { 'index': 0x026E })), + ('Interior', ('Market -> Market Mask Shop', { 'index': 0x0530 }), + ('Market Mask Shop -> Market', { 'index': 0x01D1, 'addresses': [0xC6DA5E] })), + ('Interior', ('Market -> Market Bombchu Bowling', { 'index': 0x0507 }), + ('Market Bombchu Bowling -> Market', { 'index': 0x03BC })), + ('Interior', ('Market -> Market Potion Shop', { 'index': 0x0388 }), + ('Market Potion Shop -> Market', { 'index': 0x02A2 })), + ('Interior', ('Market -> Market Treasure Chest Game', { 'index': 0x0063 }), + ('Market Treasure Chest Game -> Market', { 'index': 0x01D5 })), + ('Interior', ('Market Back Alley -> Market Bombchu Shop', { 'index': 0x0528 }), + ('Market Bombchu Shop -> Market Back Alley', { 'index': 0x03C0 })), + ('Interior', ('Market Back Alley -> Market Man in Green House', { 'index': 0x043B }), + ('Market Man in Green House -> Market Back Alley', { 'index': 0x0067 })), + ('Interior', ('Kakariko Village -> Kak Carpenter Boss House', { 'index': 0x02FD }), + ('Kak Carpenter Boss House -> Kakariko Village', { 'index': 0x0349 })), + ('Interior', ('Kakariko Village -> Kak House of Skulltula', { 'index': 0x0550 }), + ('Kak House of Skulltula -> Kakariko Village', { 'index': 0x04EE })), + ('Interior', ('Kakariko Village -> Kak Impas House', { 'index': 0x039C }), + ('Kak Impas House -> Kakariko Village', { 'index': 0x0345 })), + ('Interior', ('Kak Impas Ledge -> Kak Impas House Back', { 'index': 0x05C8 }), + ('Kak Impas House Back -> Kak Impas Ledge', { 'index': 0x05DC })), + ('Interior', ('Kak Backyard -> Kak Odd Medicine Building', { 'index': 0x0072 }), + ('Kak Odd Medicine Building -> Kak Backyard', { 'index': 0x034D })), + ('Interior', ('Graveyard -> Graveyard Dampes House', { 'index': 0x030D }), + ('Graveyard Dampes House -> Graveyard', { 'index': 0x0355 })), + ('Interior', ('Goron City -> GC Shop', { 'index': 0x037C }), + ('GC Shop -> Goron City', { 'index': 0x03FC })), + ('Interior', ('Zoras Domain -> ZD Shop', { 'index': 0x0380 }), + ('ZD Shop -> Zoras Domain', { 'index': 0x03C4 })), + ('Interior', ('Lon Lon Ranch -> LLR Talons House', { 'index': 0x004F }), + ('LLR Talons House -> Lon Lon Ranch', { 'index': 0x0378 })), + ('Interior', ('Lon Lon Ranch -> LLR Stables', { 'index': 0x02F9 }), + ('LLR Stables -> Lon Lon Ranch', { 'index': 0x042F })), + ('Interior', ('Lon Lon Ranch -> LLR Tower', { 'index': 0x05D0 }), + ('LLR Tower -> Lon Lon Ranch', { 'index': 0x05D4 })), + ('Interior', ('Market -> Market Bazaar', { 'index': 0x052C }), + ('Market Bazaar -> Market', { 'index': 0x03B8, 'addresses': [0xBEFD74] })), + ('Interior', ('Market -> Market Shooting Gallery', { 'index': 0x016D }), + ('Market Shooting Gallery -> Market', { 'index': 0x01CD, 'addresses': [0xBEFD7C] })), + ('Interior', ('Kakariko Village -> Kak Bazaar', { 'index': 0x00B7 }), + ('Kak Bazaar -> Kakariko Village', { 'index': 0x0201, 'addresses': [0xBEFD72] })), + ('Interior', ('Kakariko Village -> Kak Shooting Gallery', { 'index': 0x003B }), + ('Kak Shooting Gallery -> Kakariko Village', { 'index': 0x0463, 'addresses': [0xBEFD7A] })), + ('Interior', ('Desert Colossus -> Colossus Great Fairy Fountain', { 'index': 0x0588 }), + ('Colossus Great Fairy Fountain -> Desert Colossus', { 'index': 0x057C, 'addresses': [0xBEFD82] })), + ('Interior', ('Hyrule Castle Grounds -> HC Great Fairy Fountain', { 'index': 0x0578 }), + ('HC Great Fairy Fountain -> Castle Grounds', { 'index': 0x0340, 'addresses': [0xBEFD80] })), + ('Interior', ('Ganons Castle Grounds -> OGC Great Fairy Fountain', { 'index': 0x04C2 }), + ('OGC Great Fairy Fountain -> Castle Grounds', { 'index': 0x0340, 'addresses': [0xBEFD6C] })), + ('Interior', ('DMC Lower Nearby -> DMC Great Fairy Fountain', { 'index': 0x04BE }), + ('DMC Great Fairy Fountain -> DMC Lower Local', { 'index': 0x0482, 'addresses': [0xBEFD6A] })), + ('Interior', ('Death Mountain Summit -> DMT Great Fairy Fountain', { 'index': 0x0315 }), + ('DMT Great Fairy Fountain -> Death Mountain Summit', { 'index': 0x045B, 'addresses': [0xBEFD68] })), + ('Interior', ('Zoras Fountain -> ZF Great Fairy Fountain', { 'index': 0x0371 }), + ('ZF Great Fairy Fountain -> Zoras Fountain', { 'index': 0x0394, 'addresses': [0xBEFD7E] })), + + ('SpecialInterior', ('Kokiri Forest -> KF Links House', { 'index': 0x0272 }), + ('KF Links House -> Kokiri Forest', { 'index': 0x0211 })), + ('SpecialInterior', ('ToT Entrance -> Temple of Time', { 'index': 0x0053 }), + ('Temple of Time -> ToT Entrance', { 'index': 0x0472 })), + ('SpecialInterior', ('Kakariko Village -> Kak Windmill', { 'index': 0x0453 }), + ('Kak Windmill -> Kakariko Village', { 'index': 0x0351 })), + ('SpecialInterior', ('Kakariko Village -> Kak Potion Shop Front', { 'index': 0x0384 }), + ('Kak Potion Shop Front -> Kakariko Village', { 'index': 0x044B })), + ('SpecialInterior', ('Kak Backyard -> Kak Potion Shop Back', { 'index': 0x03EC }), + ('Kak Potion Shop Back -> Kak Backyard', { 'index': 0x04FF })), + + ('Grotto', ('Desert Colossus -> Colossus Grotto', { 'grotto_id': 0x00, 'entrance': 0x05BC, 'content': 0xFD, 'scene': 0x5C }), + ('Colossus Grotto -> Desert Colossus', { 'grotto_id': 0x00 })), + ('Grotto', ('Lake Hylia -> LH Grotto', { 'grotto_id': 0x01, 'entrance': 0x05A4, 'content': 0xEF, 'scene': 0x57 }), + ('LH Grotto -> Lake Hylia', { 'grotto_id': 0x01 })), + ('Grotto', ('Zora River -> ZR Storms Grotto', { 'grotto_id': 0x02, 'entrance': 0x05BC, 'content': 0xEB, 'scene': 0x54 }), + ('ZR Storms Grotto -> Zora River', { 'grotto_id': 0x02 })), + ('Grotto', ('Zora River -> ZR Fairy Grotto', { 'grotto_id': 0x03, 'entrance': 0x036D, 'content': 0xE6, 'scene': 0x54 }), + ('ZR Fairy Grotto -> Zora River', { 'grotto_id': 0x03 })), + ('Grotto', ('Zora River -> ZR Open Grotto', { 'grotto_id': 0x04, 'entrance': 0x003F, 'content': 0x29, 'scene': 0x54 }), + ('ZR Open Grotto -> Zora River', { 'grotto_id': 0x04 })), + ('Grotto', ('DMC Lower Nearby -> DMC Hammer Grotto', { 'grotto_id': 0x05, 'entrance': 0x05A4, 'content': 0xF9, 'scene': 0x61 }), + ('DMC Hammer Grotto -> DMC Lower Local', { 'grotto_id': 0x05 })), + ('Grotto', ('DMC Upper Nearby -> DMC Upper Grotto', { 'grotto_id': 0x06, 'entrance': 0x003F, 'content': 0x7A, 'scene': 0x61 }), + ('DMC Upper Grotto -> DMC Upper Local', { 'grotto_id': 0x06 })), + ('Grotto', ('GC Grotto Platform -> GC Grotto', { 'grotto_id': 0x07, 'entrance': 0x05A4, 'content': 0xFB, 'scene': 0x62 }), + ('GC Grotto -> GC Grotto Platform', { 'grotto_id': 0x07 })), + ('Grotto', ('Death Mountain -> DMT Storms Grotto', { 'grotto_id': 0x08, 'entrance': 0x003F, 'content': 0x57, 'scene': 0x60 }), + ('DMT Storms Grotto -> Death Mountain', { 'grotto_id': 0x08 })), + ('Grotto', ('Death Mountain Summit -> DMT Cow Grotto', { 'grotto_id': 0x09, 'entrance': 0x05FC, 'content': 0xF8, 'scene': 0x60 }), + ('DMT Cow Grotto -> Death Mountain Summit', { 'grotto_id': 0x09 })), + ('Grotto', ('Kak Backyard -> Kak Open Grotto', { 'grotto_id': 0x0A, 'entrance': 0x003F, 'content': 0x28, 'scene': 0x52 }), + ('Kak Open Grotto -> Kak Backyard', { 'grotto_id': 0x0A })), + ('Grotto', ('Kakariko Village -> Kak Redead Grotto', { 'grotto_id': 0x0B, 'entrance': 0x05A0, 'content': 0xE7, 'scene': 0x52 }), + ('Kak Redead Grotto -> Kakariko Village', { 'grotto_id': 0x0B })), + ('Grotto', ('Hyrule Castle Grounds -> HC Storms Grotto', { 'grotto_id': 0x0C, 'entrance': 0x05B8, 'content': 0xF6, 'scene': 0x5F }), + ('HC Storms Grotto -> Castle Grounds', { 'grotto_id': 0x0C })), + ('Grotto', ('Hyrule Field -> HF Tektite Grotto', { 'grotto_id': 0x0D, 'entrance': 0x05C0, 'content': 0xE1, 'scene': 0x51 }), + ('HF Tektite Grotto -> Hyrule Field', { 'grotto_id': 0x0D })), + ('Grotto', ('Hyrule Field -> HF Near Kak Grotto', { 'grotto_id': 0x0E, 'entrance': 0x0598, 'content': 0xE5, 'scene': 0x51 }), + ('HF Near Kak Grotto -> Hyrule Field', { 'grotto_id': 0x0E })), + ('Grotto', ('Hyrule Field -> HF Fairy Grotto', { 'grotto_id': 0x0F, 'entrance': 0x036D, 'content': 0xFF, 'scene': 0x51 }), + ('HF Fairy Grotto -> Hyrule Field', { 'grotto_id': 0x0F })), + ('Grotto', ('Hyrule Field -> HF Near Market Grotto', { 'grotto_id': 0x10, 'entrance': 0x003F, 'content': 0x00, 'scene': 0x51 }), + ('HF Near Market Grotto -> Hyrule Field', { 'grotto_id': 0x10 })), + ('Grotto', ('Hyrule Field -> HF Cow Grotto', { 'grotto_id': 0x11, 'entrance': 0x05A8, 'content': 0xE4, 'scene': 0x51 }), + ('HF Cow Grotto -> Hyrule Field', { 'grotto_id': 0x11 })), + ('Grotto', ('Hyrule Field -> HF Inside Fence Grotto', { 'grotto_id': 0x12, 'entrance': 0x059C, 'content': 0xE6, 'scene': 0x51 }), + ('HF Inside Fence Grotto -> Hyrule Field', { 'grotto_id': 0x12 })), + ('Grotto', ('Hyrule Field -> HF Open Grotto', { 'grotto_id': 0x13, 'entrance': 0x003F, 'content': 0x03, 'scene': 0x51 }), + ('HF Open Grotto -> Hyrule Field', { 'grotto_id': 0x13 })), + ('Grotto', ('Hyrule Field -> HF Southeast Grotto', { 'grotto_id': 0x14, 'entrance': 0x003F, 'content': 0x22, 'scene': 0x51 }), + ('HF Southeast Grotto -> Hyrule Field', { 'grotto_id': 0x14 })), + ('Grotto', ('Lon Lon Ranch -> LLR Grotto', { 'grotto_id': 0x15, 'entrance': 0x05A4, 'content': 0xFC, 'scene': 0x63 }), + ('LLR Grotto -> Lon Lon Ranch', { 'grotto_id': 0x15 })), + ('Grotto', ('SFM Entryway -> SFM Wolfos Grotto', { 'grotto_id': 0x16, 'entrance': 0x05B4, 'content': 0xED, 'scene': 0x56 }), + ('SFM Wolfos Grotto -> SFM Entryway', { 'grotto_id': 0x16 })), + ('Grotto', ('Sacred Forest Meadow -> SFM Storms Grotto', { 'grotto_id': 0x17, 'entrance': 0x05BC, 'content': 0xEE, 'scene': 0x56 }), + ('SFM Storms Grotto -> Sacred Forest Meadow', { 'grotto_id': 0x17 })), + ('Grotto', ('Sacred Forest Meadow -> SFM Fairy Grotto', { 'grotto_id': 0x18, 'entrance': 0x036D, 'content': 0xFF, 'scene': 0x56 }), + ('SFM Fairy Grotto -> Sacred Forest Meadow', { 'grotto_id': 0x18 })), + ('Grotto', ('LW Beyond Mido -> LW Scrubs Grotto', { 'grotto_id': 0x19, 'entrance': 0x05B0, 'content': 0xF5, 'scene': 0x5B }), + ('LW Scrubs Grotto -> LW Beyond Mido', { 'grotto_id': 0x19 })), + ('Grotto', ('Lost Woods -> LW Near Shortcuts Grotto', { 'grotto_id': 0x1A, 'entrance': 0x003F, 'content': 0x14, 'scene': 0x5B }), + ('LW Near Shortcuts Grotto -> Lost Woods', { 'grotto_id': 0x1A })), + ('Grotto', ('Kokiri Forest -> KF Storms Grotto', { 'grotto_id': 0x1B, 'entrance': 0x003F, 'content': 0x2C, 'scene': 0x55 }), + ('KF Storms Grotto -> Kokiri Forest', { 'grotto_id': 0x1B })), + ('Grotto', ('Zoras Domain -> ZD Storms Grotto', { 'grotto_id': 0x1C, 'entrance': 0x036D, 'content': 0xFF, 'scene': 0x58 }), + ('ZD Storms Grotto -> Zoras Domain', { 'grotto_id': 0x1C })), + ('Grotto', ('Gerudo Fortress -> GF Storms Grotto', { 'grotto_id': 0x1D, 'entrance': 0x036D, 'content': 0xFF, 'scene': 0x5D }), + ('GF Storms Grotto -> Gerudo Fortress', { 'grotto_id': 0x1D })), + ('Grotto', ('GV Fortress Side -> GV Storms Grotto', { 'grotto_id': 0x1E, 'entrance': 0x05BC, 'content': 0xF0, 'scene': 0x5A }), + ('GV Storms Grotto -> GV Fortress Side', { 'grotto_id': 0x1E })), + ('Grotto', ('GV Grotto Ledge -> GV Octorok Grotto', { 'grotto_id': 0x1F, 'entrance': 0x05AC, 'content': 0xF2, 'scene': 0x5A }), + ('GV Octorok Grotto -> GV Grotto Ledge', { 'grotto_id': 0x1F })), + ('Grotto', ('LW Beyond Mido -> Deku Theater', { 'grotto_id': 0x20, 'entrance': 0x05C4, 'content': 0xF3, 'scene': 0x5B }), + ('Deku Theater -> LW Beyond Mido', { 'grotto_id': 0x20 })), + + ('Grave', ('Graveyard -> Graveyard Shield Grave', { 'index': 0x004B }), + ('Graveyard Shield Grave -> Graveyard', { 'index': 0x035D })), + ('Grave', ('Graveyard -> Graveyard Heart Piece Grave', { 'index': 0x031C }), + ('Graveyard Heart Piece Grave -> Graveyard', { 'index': 0x0361 })), + ('Grave', ('Graveyard -> Graveyard Composers Grave', { 'index': 0x002D }), + ('Graveyard Composers Grave -> Graveyard', { 'index': 0x050B })), + ('Grave', ('Graveyard -> Graveyard Dampes Grave', { 'index': 0x044F }), + ('Graveyard Dampes Grave -> Graveyard', { 'index': 0x0359 })), + + ('Overworld', ('Kokiri Forest -> LW Bridge From Forest', { 'index': 0x05E0 }), + ('LW Bridge -> Kokiri Forest', { 'index': 0x020D })), + ('Overworld', ('Kokiri Forest -> Lost Woods', { 'index': 0x011E }), + ('LW Forest Exit -> Kokiri Forest', { 'index': 0x0286 })), + ('Overworld', ('Lost Woods -> GC Woods Warp', { 'index': 0x04E2 }), + ('GC Woods Warp -> Lost Woods', { 'index': 0x04D6 })), + ('Overworld', ('Lost Woods -> Zora River', { 'index': 0x01DD }), + ('Zora River -> Lost Woods', { 'index': 0x04DA })), + ('Overworld', ('LW Beyond Mido -> SFM Entryway', { 'index': 0x00FC }), + ('SFM Entryway -> LW Beyond Mido', { 'index': 0x01A9 })), + ('Overworld', ('LW Bridge -> Hyrule Field', { 'index': 0x0185 }), + ('Hyrule Field -> LW Bridge', { 'index': 0x04DE })), + ('Overworld', ('Hyrule Field -> Lake Hylia', { 'index': 0x0102 }), + ('Lake Hylia -> Hyrule Field', { 'index': 0x0189 })), + ('Overworld', ('Hyrule Field -> Gerudo Valley', { 'index': 0x0117 }), + ('Gerudo Valley -> Hyrule Field', { 'index': 0x018D })), + ('Overworld', ('Hyrule Field -> Market Entrance', { 'index': 0x0276 }), + ('Market Entrance -> Hyrule Field', { 'index': 0x01FD })), + ('Overworld', ('Hyrule Field -> Kakariko Village', { 'index': 0x00DB }), + ('Kakariko Village -> Hyrule Field', { 'index': 0x017D })), + ('Overworld', ('Hyrule Field -> ZR Front', { 'index': 0x00EA }), + ('ZR Front -> Hyrule Field', { 'index': 0x0181 })), + ('Overworld', ('Hyrule Field -> Lon Lon Ranch', { 'index': 0x0157 }), + ('Lon Lon Ranch -> Hyrule Field', { 'index': 0x01F9 })), + ('Overworld', ('Lake Hylia -> Zoras Domain', { 'index': 0x0328 }), + ('Zoras Domain -> Lake Hylia', { 'index': 0x0560 })), + ('Overworld', ('GV Fortress Side -> Gerudo Fortress', { 'index': 0x0129 }), + ('Gerudo Fortress -> GV Fortress Side', { 'index': 0x022D })), + ('Overworld', ('GF Outside Gate -> Wasteland Near Fortress', { 'index': 0x0130 }), + ('Wasteland Near Fortress -> GF Outside Gate', { 'index': 0x03AC })), + ('Overworld', ('Wasteland Near Colossus -> Desert Colossus', { 'index': 0x0123 }), + ('Desert Colossus -> Wasteland Near Colossus', { 'index': 0x0365 })), + ('Overworld', ('Market Entrance -> Market', { 'index': 0x00B1 }), + ('Market -> Market Entrance', { 'index': 0x0033 })), + ('Overworld', ('Market -> Castle Grounds', { 'index': 0x0138 }), + ('Castle Grounds -> Market', { 'index': 0x025A })), + ('Overworld', ('Market -> ToT Entrance', { 'index': 0x0171 }), + ('ToT Entrance -> Market', { 'index': 0x025E })), + ('Overworld', ('Kakariko Village -> Graveyard', { 'index': 0x00E4 }), + ('Graveyard -> Kakariko Village', { 'index': 0x0195 })), + ('Overworld', ('Kak Behind Gate -> Death Mountain', { 'index': 0x013D }), + ('Death Mountain -> Kak Behind Gate', { 'index': 0x0191 })), + ('Overworld', ('Death Mountain -> Goron City', { 'index': 0x014D }), + ('Goron City -> Death Mountain', { 'index': 0x01B9 })), + ('Overworld', ('GC Darunias Chamber -> DMC Lower Local', { 'index': 0x0246 }), + ('DMC Lower Nearby -> GC Darunias Chamber', { 'index': 0x01C1 })), + ('Overworld', ('Death Mountain Summit -> DMC Upper Local', { 'index': 0x0147 }), + ('DMC Upper Nearby -> Death Mountain Summit', { 'index': 0x01BD })), + ('Overworld', ('ZR Behind Waterfall -> Zoras Domain', { 'index': 0x0108 }), + ('Zoras Domain -> ZR Behind Waterfall', { 'index': 0x019D })), + ('Overworld', ('ZD Behind King Zora -> Zoras Fountain', { 'index': 0x0225 }), + ('Zoras Fountain -> ZD Behind King Zora', { 'index': 0x01A1 })), + + ('OwlDrop', ('LH Owl Flight -> Hyrule Field', { 'index': 0x027E, 'addresses': [0xAC9F26] })), + ('OwlDrop', ('DMT Owl Flight -> Kak Impas Rooftop', { 'index': 0x0554, 'addresses': [0xAC9EF2] })), + + ('Spawn', ('Child Spawn -> KF Links House', { 'index': 0x00BB, 'addresses': [0xB06342] })), + ('Spawn', ('Adult Spawn -> Temple of Time', { 'index': 0x05F4, 'addresses': [0xB06332] })), + + ('WarpSong', ('Minuet of Forest Warp -> Sacred Forest Meadow', { 'index': 0x0600, 'addresses': [0xBF023C] })), + ('WarpSong', ('Bolero of Fire Warp -> DMC Central Local', { 'index': 0x04F6, 'addresses': [0xBF023E] })), + ('WarpSong', ('Serenade of Water Warp -> Lake Hylia', { 'index': 0x0604, 'addresses': [0xBF0240] })), + ('WarpSong', ('Requiem of Spirit Warp -> Desert Colossus', { 'index': 0x01F1, 'addresses': [0xBF0242] })), + ('WarpSong', ('Nocturne of Shadow Warp -> Graveyard Warp Pad Region', { 'index': 0x0568, 'addresses': [0xBF0244] })), + ('WarpSong', ('Prelude of Light Warp -> Temple of Time', { 'index': 0x05F4, 'addresses': [0xBF0246] })), + + ('Extra', ('ZD Eyeball Frog Timeout -> Zoras Domain', { 'index': 0x0153 })), + ('Extra', ('ZR Top of Waterfall -> Zora River', { 'index': 0x0199 })), +] + + +# Basically, the entrances in the list above that go to: +# - DMC Central Local (child access for the bean and skull) +# - Desert Colossus (child access to colossus and spirit) +# - Graveyard Warp Pad Region (access to shadow, plus the gossip stone) +# We will always need to pick one from each list to receive a one-way entrance +# if shuffling warp songs (depending on other settings). +# Table maps: short key -> ([target regions], [allowed types]) +priority_entrance_table = { + 'Bolero': (['DMC Central Local'], ['OwlDrop', 'WarpSong']), + 'Nocturne': (['Graveyard Warp Pad Region'], ['OwlDrop', 'Spawn', 'WarpSong']), + 'Requiem': (['Desert Colossus', 'Desert Colossus From Spirit Lobby'], ['OwlDrop', 'Spawn', 'WarpSong']), +} + + +class EntranceShuffleError(Exception): + pass + def shuffle_random_entrances(ootworld): world = ootworld.world player = ootworld.player # Gather locations to keep reachable for validation + all_state = world.get_all_state(use_cache=True) + locations_to_ensure_reachable = {loc for loc in world.get_reachable_locations(all_state, player) if not (loc.type == 'Drop' or (loc.type == 'Event' and 'Subrule' in loc.name))} # Set entrance data for all entrances + set_all_entrances_data(world, player) # Determine entrance pools based on settings + one_way_entrance_pools = {} + entrance_pools = {} + one_way_priorities = {} + + if ootworld.owl_drops: + one_way_entrance_pools['OwlDrop'] = ootworld.get_shufflable_entrances(type='OwlDrop') + if ootworld.spawn_positions: + one_way_entrance_pools['Spawn'] = ootworld.get_shufflable_entrances(type='Spawn') + if ootworld.warp_songs: + one_way_entrance_pools['WarpSong'] = ootworld.get_shufflable_entrances(type='WarpSong') + if world.accessibility[player].current_key != 'minimal' and ootworld.logic_rules == 'glitchless': + one_way_priorities['Bolero'] = priority_entrance_table['Bolero'] + one_way_priorities['Nocturne'] = priority_entrance_table['Nocturne'] + if not ootworld.shuffle_dungeon_entrances and not ootworld.shuffle_overworld_entrances: + one_way_priorities['Requiem'] = priority_entrance_table['Requiem'] + + if ootworld.shuffle_dungeon_entrances: + entrance_pools['Dungeon'] = ootworld.get_shufflable_entrances(type='Dungeon', only_primary=True) + if ootworld.open_forest == 'closed': + entrance_pools['Dungeon'].remove(world.get_entrance('KF Outside Deku Tree -> Deku Tree Lobby', player)) + if ootworld.shuffle_interior_entrances != 'off': + entrance_pools['Interior'] = ootworld.get_shufflable_entrances(type='Interior', only_primary=True) + if ootworld.shuffle_special_interior_entrances: + entrance_pools['Interior'] += ootworld.get_shufflable_entrances(type='SpecialInterior', only_primary=True) + if ootworld.shuffle_grotto_entrances: + entrance_pools['GrottoGrave'] = ootworld.get_shufflable_entrances(type='Grotto', only_primary=True) + entrance_pools['GrottoGrave'] += ootworld.get_shufflable_entrances(type='Grave', only_primary=True) + if ootworld.shuffle_overworld_entrances: + entrance_pools['Overworld'] = ootworld.get_shufflable_entrances(type='Overworld') # Mark shuffled entrances + for entrance in chain(chain.from_iterable(one_way_entrance_pools.values()), chain.from_iterable(entrance_pools.values())): + entrance.shuffled = True + if entrance.reverse: + entrance.reverse.shuffled = True # Build target entrance pools + one_way_target_entrance_pools = {} + for pool_type, entrance_pool in one_way_entrance_pools.items(): + if pool_type == 'OwlDrop': + valid_target_types = ('WarpSong', 'OwlDrop', 'Overworld', 'Extra') + one_way_target_entrance_pools[pool_type] = build_one_way_targets(ootworld, valid_target_types, exclude=['Prelude of Light Warp -> Temple of Time']) + for target in one_way_target_entrance_pools[pool_type]: + set_rule(target, lambda state: state._oot_reach_as_age(target.parent_region, 'child', player)) + elif pool_type in {'Spawn', 'WarpSong'}: + valid_target_types = ('Spawn', 'WarpSong', 'OwlDrop', 'Overworld', 'Interior', 'SpecialInterior', 'Extra') + one_way_target_entrance_pools[pool_type] = build_one_way_targets(ootworld, valid_target_types) + # Ensure that the last entrance doesn't assume the rest of the targets are reachable? + # Disconnect one-way entrances for priority placement + for entrance in chain.from_iterable(one_way_entrance_pools.values()): + entrance.disconnect() + + target_entrance_pools = {} + for pool_type, entrance_pool in entrance_pools.items(): + target_entrance_pools[pool_type] = assume_entrance_pool(entrance_pool, ootworld) + + # Build all_state and none_state + all_state = ootworld.get_state_with_complete_itempool() + none_state = all_state.copy() + for item_tuple in none_state.prog_items: + if item_tuple[1] == player: + none_state.prog_items[item_tuple] = 0 + + # Plando entrances? # Place priority entrances + shuffle_one_way_priority_entrances(ootworld, one_way_priorities, one_way_entrance_pools, one_way_target_entrance_pools, locations_to_ensure_reachable, all_state, none_state, retry_count=2) # Delete priority targets from one-way pools + replaced_entrances = [entrance.replaces for entrance in chain.from_iterable(one_way_entrance_pools.values())] + for remaining_target in chain.from_iterable(one_way_target_entrance_pools.values()): + if remaining_target.replaces in replaced_entrances: + delete_target_entrance(remaining_target) + + for pool_type, entrance_pool in one_way_entrance_pools.items(): + shuffle_entrance_pool(ootworld, entrance_pool, one_way_target_entrance_pools[pool_type], locations_to_ensure_reachable, all_state, none_state, check_all=True, retry_count=5) + replaced_entrances = [entrance.replaces for entrance in entrance_pool] + for remaining_target in chain.from_iterable(one_way_target_entrance_pools.values()): + if remaining_target.replaces in replaced_entrances: + delete_target_entrance(remaining_target) + for unused_target in one_way_target_entrance_pools[pool_type]: + delete_target_entrance(unused_target) # Shuffle all entrance pools, in order + for pool_type, entrance_pool in entrance_pools.items(): + shuffle_entrance_pool(ootworld, entrance_pool, target_entrance_pools[pool_type], locations_to_ensure_reachable, all_state, none_state) - # Verification steps: - # All entrances are properly connected to a region + # Multiple checks after shuffling to ensure everything is OK + # Check that all entrances hook up correctly + for entrance in ootworld.get_shuffled_entrances(): + if entrance.connected_region == None: + logging.getLogger('').error(f'{entrance} was shuffled but is not connected to any region') + if entrance.replaces == None: + logging.getLogger('').error(f'{entrance} was shuffled but does not replace any entrance') + if len(ootworld.get_region('Root Exits').exits) > 8: + for exit in ootworld.get_region('Root Exits').exits: + logging.getLogger('').error(f'Root Exit: {exit} -> {exit.connected_region}') + logging.getLogger('').error(f'Root has too many entrances left after shuffling entrances') # Game is beatable + new_all_state = world.get_all_state(use_cache=False) + if not world.has_beaten_game(new_all_state, player): + raise EntranceShuffleError('Cannot beat game') # Validate world + validate_world(ootworld, None, locations_to_ensure_reachable, all_state, none_state) + + +def replace_entrance(ootworld, entrance, target, rollbacks, locations_to_ensure_reachable, all_state, none_state): + try: + check_entrances_compatibility(entrance, target, rollbacks) + change_connections(entrance, target) + validate_world(ootworld, entrance, locations_to_ensure_reachable, all_state, none_state) + rollbacks.append((entrance, target)) + return True + except EntranceShuffleError as e: + logging.getLogger('').debug(f'Failed to connect {entrance} to {target}, reason: {e}') + if entrance.connected_region: + restore_connections(entrance, target) + return False + + +def shuffle_one_way_priority_entrances(ootworld, one_way_priorities, one_way_entrance_pools, one_way_target_entrance_pools, + locations_to_ensure_reachable, all_state, none_state, retry_count=2): + + ootworld.priority_entrances = [] + + while retry_count: + retry_count -= 1 + rollbacks = [] + + try: + for key, (regions, types) in one_way_priorities.items(): + place_one_way_priority_entrance(ootworld, key, regions, types, rollbacks, locations_to_ensure_reachable, + all_state, none_state, one_way_entrance_pools, one_way_target_entrance_pools) + for entrance, target in rollbacks: + confirm_replacement(entrance, target) + return + except EntranceShuffleError as error: + for entrance, target in rollbacks: + restore_connections(entrance, target) + logging.getLogger('').debug(f'Failed to place all priority one-way entrances, retrying {retry_count} more times') + + raise EntranceShuffleError(f'Priority one-way entrance placement attempt count exceeded for world {ootworld.player}') + +def place_one_way_priority_entrance(ootworld, priority_name, allowed_regions, allowed_types, rollbacks, locations_to_ensure_reachable, + all_state, none_state, one_way_entrance_pools, one_way_target_entrance_pools): + + avail_pool = list(chain.from_iterable(one_way_entrance_pools[t] for t in allowed_types if t in one_way_entrance_pools)) + ootworld.world.random.shuffle(avail_pool) + + for entrance in avail_pool: + if entrance.replaces: + continue + if entrance.parent_region.name == 'Adult Spawn' and (priority_name != 'Nocturne' or ootworld.hints == 'mask'): + continue + if not ootworld.shuffle_dungeon_entrances and priority_name == 'Nocturne': + if entrance.type != 'WarpSong' and entrance.parent_region.name != 'Adult Spawn': + continue + for target in one_way_target_entrance_pools[entrance.type]: + if target.connected_region and target.connected_region.name in allowed_regions: + if replace_entrance(ootworld, entrance, target, rollbacks, locations_to_ensure_reachable, all_state, none_state): + logging.getLogger('').debug(f'Priority placing {entrance} as {target} for {priority_name}') + ootworld.priority_entrances.append(entrance) + return + raise EntranceShuffleError(f'Unable to place priority one-way entrance for {priority_name} in world {ootworld.player}') + + +def shuffle_entrance_pool(ootworld, entrance_pool, target_entrances, locations_to_ensure_reachable, all_state, none_state, check_all=False, retry_count=20): + + restrictive_entrances, soft_entrances = split_entrances_by_requirements(ootworld, entrance_pool, target_entrances) + + while retry_count: + retry_count -= 1 + rollbacks = [] + try: + shuffle_entrances(ootworld, restrictive_entrances, target_entrances, rollbacks, locations_to_ensure_reachable, all_state, none_state) + if check_all: + shuffle_entrances(ootworld, soft_entrances, target_entrances, rollbacks, locations_to_ensure_reachable, all_state, none_state) + else: + shuffle_entrances(ootworld, soft_entrances, target_entrances, rollbacks, set(), all_state, none_state) + + validate_world(ootworld, None, locations_to_ensure_reachable, all_state, none_state) + for entrance, target in rollbacks: + confirm_replacement(entrance, target) + return + except EntranceShuffleError as e: + for entrance, target in rollbacks: + restore_connections(entrance, target) + logging.getLogger('').debug(f'Failed to place all entrances in pool, retrying {retry_count} more times') + + raise EntranceShuffleError(f'Entrance placement attempt count exceeded for world {ootworld.player}') + +def shuffle_entrances(ootworld, entrances, target_entrances, rollbacks, locations_to_ensure_reachable, all_state, none_state): + ootworld.world.random.shuffle(entrances) + for entrance in entrances: + if entrance.connected_region != None: + continue + ootworld.world.random.shuffle(target_entrances) + for target in target_entrances: + if target.connected_region == None: + continue + if replace_entrance(ootworld, entrance, target, rollbacks, locations_to_ensure_reachable, all_state, none_state): + break + if entrance.connected_region == None: + raise EntranceShuffleError('No more valid entrances') + + +def split_entrances_by_requirements(ootworld, entrances_to_split, assumed_entrances): + world = ootworld.world + player = ootworld.player + + # Disconnect all root assumed entrances and save original connections + original_connected_regions = {} + entrances_to_disconnect = set(assumed_entrances).union(entrance.reverse for entrance in assumed_entrances if entrance.reverse) + for entrance in entrances_to_disconnect: + if entrance.connected_region: + original_connected_regions[entrance] = entrance.disconnect() + + all_state = world.get_all_state(use_cache=False) + + restrictive_entrances = [] + soft_entrances = [] + + for entrance in entrances_to_split: + all_state.age[player] = 'child' + if not all_state.can_reach(entrance, 'Entrance', player): + restrictive_entrances.append(entrance) + continue + all_state.age[player] = 'adult' + if not all_state.can_reach(entrance, 'Entrance', player): + restrictive_entrances.append(entrance) + continue + all_state.age[player] = None + if not all_state._oot_reach_at_time(entrance.parent_region.name, TimeOfDay.ALL, [], player): + restrictive_entrances.append(entrance) + continue + soft_entrances.append(entrance) + + # Reconnect assumed entrances + for entrance in entrances_to_disconnect: + if entrance in original_connected_regions: + entrance.connect(original_connected_regions[entrance]) + + return restrictive_entrances, soft_entrances + + +# Check to ensure the world is valid. +# TODO: improve this function +def validate_world(ootworld, entrance_placed, locations_to_ensure_reachable, all_state_orig, none_state_orig): + + world = ootworld.world + player = ootworld.player + + all_state = all_state_orig.copy() + none_state = none_state_orig.copy() + + all_state.sweep_for_events() + none_state.sweep_for_events() + + if ootworld.shuffle_interior_entrances or ootworld.shuffle_overworld_entrances or ootworld.spawn_positions: + time_travel_state = none_state.copy() + time_travel_state.collect(ootworld.create_item('Time Travel'), event=True) + time_travel_state._oot_update_age_reachable_regions(player) + + # For various reasons, we don't want the player to end up through certain entrances as the wrong age + # This means we need to hard check that none of the relevant entrances are ever reachable as that age + # This is mostly relevant when shuffling special interiors (such as windmill or kak potion shop) + # Warp Songs and Overworld Spawns can also end up inside certain indoors so those need to be handled as well + CHILD_FORBIDDEN = ['OGC Great Fairy Fountain -> Castle Grounds', 'GV Carpenter Tent -> GV Fortress Side'] + ADULT_FORBIDDEN = ['HC Great Fairy Fountain -> Castle Grounds', 'HC Storms Grotto -> Castle Grounds'] + + for entrance in ootworld.get_shufflable_entrances(): + if entrance.shuffled and entrance.replaces: + if entrance.replaces.name in CHILD_FORBIDDEN and not entrance_unreachable_as(entrance, 'child', already_checked=[entrance.replaces.reverse]): + raise EntranceShuffleError(f'{entrance.replaces.name} replaced by an entrance with potential child access') + if entrance.replaces.name in ADULT_FORBIDDEN and not entrance_unreachable_as(entrance, 'adult', already_checked=[entrance.replaces.reverse]): + raise EntranceShuffleError(f'{entrance.replaces.name} replaced by an entrance with potential adult access') + else: + if entrance.name in CHILD_FORBIDDEN and not entrance_unreachable_as(entrance, 'child', already_checked=[entrance.reverse]): + raise EntranceShuffleError(f'{entrance.name} potentially accessible as child') + if entrance.name in ADULT_FORBIDDEN and not entrance_unreachable_as(entrance, 'adult', already_checked=[entrance.reverse]): + raise EntranceShuffleError(f'{entrance.name} potentially accessible as adult') + + # Check if all locations are reachable if not beatable-only or game is not yet complete + if locations_to_ensure_reachable: + if world.accessibility[player].current_key != 'minimal' or not world.can_beat_game(all_state): + for loc in locations_to_ensure_reachable: + if not all_state.can_reach(loc, 'Location', player): + raise EntranceShuffleError(f'{loc} is unreachable') + + if ootworld.shuffle_interior_entrances and (entrance_placed == None or entrance_placed.type in ['Interior', 'SpecialInterior']): + # Ensure Kak Potion Shop entrances are in the same hint area so there is no ambiguity as to which entrance is used for hints + potion_front_entrance = get_entrance_replacing(world.get_region('Kak Potion Shop Front', player), 'Kakariko Village -> Kak Potion Shop Front', player) + potion_back_entrance = get_entrance_replacing(world.get_region('Kak Potion Shop Back', player), 'Kak Backyard -> Kak Potion Shop Back', player) + if potion_front_entrance is not None and potion_back_entrance is not None and not same_hint_area(potion_front_entrance, potion_back_entrance): + raise EntranceShuffleError('Kak Potion Shop entrances are not in the same hint area') + + # When cows are shuffled, ensure the same thing for Impa's House, since the cow is reachable from both sides + if ootworld.shuffle_cows: + impas_front_entrance = get_entrance_replacing(world.get_region('Kak Impas House', player), 'Kakariko Village -> Kak Impas House', player) + impas_back_entrance = get_entrance_replacing(world.get_region('Kak Impas House Back', player), 'Kak Impas Ledge -> Kak Impas House Back', player) + if impas_front_entrance is not None and impas_back_entrance is not None and not same_hint_area(impas_front_entrance, impas_back_entrance): + raise EntranceShuffleError('Kak Impas House entrances are not in the same hint area') + + # Check basic refills, time passing, return to ToT + if (ootworld.shuffle_special_interior_entrances or ootworld.shuffle_overworld_entrances or ootworld.spawn_positions) and \ + (entrance_placed == None or entrance_placed.type in ['SpecialInterior', 'Overworld', 'Spawn', 'WarpSong', 'OwlDrop']): + + valid_starting_regions = {'Kokiri Forest', 'Kakariko Village'} + if not any(region for region in valid_starting_regions if none_state.can_reach(region, 'Region', player)): + raise EntranceShuffleError('Invalid starting area') + + if not (any(region for region in time_travel_state.child_reachable_regions[player] if region.time_passes) and + any(region for region in time_travel_state.adult_reachable_regions[player] if region.time_passes)): + raise EntranceShuffleError('Time passing is not guaranteed as both ages') + + if ootworld.starting_age == 'child' and (world.get_region('Temple of Time', player) not in time_travel_state.adult_reachable_regions[player]): + raise EntranceShuffleError('Path to ToT as adult not guaranteed') + if ootworld.starting_age == 'adult' and (world.get_region('Temple of Time', player) not in time_travel_state.child_reachable_regions[player]): + raise EntranceShuffleError('Path to ToT as child not guaranteed') + + if (ootworld.shuffle_interior_entrances or ootworld.shuffle_overworld_entrances) and \ + (entrance_placed == None or entrance_placed.type in ['Interior', 'SpecialInterior', 'Overworld', 'Spawn', 'WarpSong', 'OwlDrop']): + # Ensure big poe shop is always reachable as adult + if world.get_region('Market Guard House', player) not in time_travel_state.adult_reachable_regions[player]: + raise EntranceShuffleError('Big Poe Shop access not guaranteed as adult') + if ootworld.shopsanity == 'off': + # Ensure that Goron and Zora shops are accessible as adult + if world.get_region('GC Shop', player) not in all_state.adult_reachable_regions[player]: + raise EntranceShuffleError('Goron City Shop not accessible as adult') + if world.get_region('ZD Shop', player) not in all_state.adult_reachable_regions[player]: + raise EntranceShuffleError('Zora\'s Domain Shop not accessible as adult') + + + +# Recursively check if a given entrance is unreachable as a given age +def entrance_unreachable_as(entrance, age, already_checked=[]): + already_checked.append(entrance) + + if entrance.type in {'WarpSong', 'Overworld'}: + return False + elif entrance.type == 'OwlDrop': + return age == 'adult' + elif entrance.name == 'Child Spawn -> KF Links House': + return age == 'adult' + elif entrance.name == 'Adult Spawn -> Temple of Time': + return age == 'child' + + for parent_entrance in entrance.parent_region.entrances: + if parent_entrance in already_checked: + continue + unreachable = entrance_unreachable_as(parent_entrance, age, already_checked) + if not unreachable: + return False + return True + +def same_hint_area(first, second): + try: + return get_hint_area(first) == get_hint_area(second) + except HintAreaNotFound: + return False + +def get_entrance_replacing(region, entrance_name, player): + original_entrance = region.world.get_entrance(entrance_name, player) + if not original_entrance.shuffled: + return original_entrance + + try: + return next(filter(lambda entrance: entrance.replaces and entrance.replaces.name == entrance_name and \ + entrance.parent_region and entrance.parent_region.name != 'Root Exits' and \ + entrance.type not in ('OwlDrop', 'Spawn', 'WarpSong') and entrance.player == player, + region.entrances)) + except StopIteration: + return None + +def change_connections(entrance, target): + entrance.connect(target.disconnect()) + entrance.replaces = target.replaces + if entrance.reverse: + target.replaces.reverse.connect(entrance.reverse.assumed.disconnect()) + target.replaces.reverse.replaces = entrance.reverse + +def restore_connections(entrance, target): + target.connect(entrance.disconnect()) + entrance.replaces = None + if entrance.reverse: + entrance.reverse.assumed.connect(target.replaces.reverse.disconnect()) + target.replaces.reverse.replaces = None + +def check_entrances_compatibility(entrance, target, rollbacks): + # An entrance shouldn't be connected to its own scene + if entrance.parent_region.get_scene() and entrance.parent_region.get_scene() == target.connected_region.get_scene(): + raise EntranceShuffleError('Self-scene connections are forbidden') + + # One-way entrances shouldn't lead to the same scene as other one-ways + if entrance.type in {'OwlDrop', 'Spawn', 'WarpSong'} and \ + any([rollback[0].connected_region.get_scene() == target.connected_region.get_scene() for rollback in rollbacks]): + raise EntranceShuffleError('Another one-way entrance leads to the same scene') + +def confirm_replacement(entrance, target): + delete_target_entrance(target) + logging.getLogger('').debug(f'Connected {entrance} to {entrance.connected_region}') + if entrance.reverse: + replaced_reverse = target.replaces.reverse + delete_target_entrance(entrance.reverse.assumed) + logging.getLogger('').debug(f'Connected {replaced_reverse} to {replaced_reverse.connected_region}') + + +def delete_target_entrance(target): + if target.connected_region != None: + target.disconnect() + if target.parent_region != None: + target.parent_region.exits.remove(target) + target.parent_region = None diff --git a/worlds/oot/Hints.py b/worlds/oot/Hints.py index c05f8986..014b852f 100644 --- a/worlds/oot/Hints.py +++ b/worlds/oot/Hints.py @@ -397,6 +397,8 @@ def get_barren_hint(world, checked): return None area_weights = [world.empty_areas[area]['weight'] for area in areas] + if not any(area_weights): + return None area = world.hint_rng.choices(areas, weights=area_weights)[0] if world.empty_areas[area]['dungeon']: @@ -637,8 +639,6 @@ hint_dist_keys = { # builds out general hints based on location and whether an item is required or not def buildWorldGossipHints(world, checkedLocations=None): - # Seed the RNG - world.hint_rng = world.world.slot_seeds[world.player] # rebuild hint exclusion list hintExclusions(world, clear_cache=True) diff --git a/worlds/oot/LogicTricks.py b/worlds/oot/LogicTricks.py index 90abc13a..db8b792a 100644 --- a/worlds/oot/LogicTricks.py +++ b/worlds/oot/LogicTricks.py @@ -727,6 +727,14 @@ known_logic_tricks = { To kill it, the logic normally guarantees one of Hookshot, Bow, or Magic. '''}, + 'Skip King Zora as Adult with Nothing': { + 'name' : 'logic_king_zora_skip', + 'tags' : ("Zora's Domain",), + 'tooltip' : '''\ + With a precise jump as adult, it is possible to + get on the fence next to King Zora from the front + to access Zora's Fountain. + '''}, 'Shadow Temple River Statue with Bombchu': { 'name' : 'logic_shadow_statue', 'tags' : ("Shadow Temple",), diff --git a/worlds/oot/Options.py b/worlds/oot/Options.py index 88b6a68c..ee441410 100644 --- a/worlds/oot/Options.py +++ b/worlds/oot/Options.py @@ -1,5 +1,5 @@ import typing -from Options import Option, DefaultOnToggle, Toggle, Choice, Range, OptionList +from Options import Option, DefaultOnToggle, Toggle, Choice, Range, OptionList, DeathLink from .ColorSFXOptions import * @@ -94,12 +94,37 @@ class StartingAge(Choice): option_adult = 1 -# TODO: document and name ER options class InteriorEntrances(Choice): + """Shuffles interior entrances. "Simple" shuffles houses and Great Fairies; "All" includes Windmill, Link's House, Temple of Time, and Kak potion shop.""" option_off = 0 option_simple = 1 option_all = 2 alias_false = 0 + alias_true = 2 + + +class GrottoEntrances(Toggle): + """Shuffles grotto and grave entrances.""" + + +class DungeonEntrances(Toggle): + """Shuffles dungeon entrances, excluding Ganon's Castle. Opens Deku, Fire and BotW to both ages.""" + + +class OverworldEntrances(Toggle): + """Shuffles overworld loading zones.""" + + +class OwlDrops(Toggle): + """Randomizes owl drops from Lake Hylia or Death Mountain Trail as child.""" + + +class WarpSongs(Toggle): + """Randomizes warp song destinations.""" + + +class SpawnPositions(Toggle): + """Randomizes the starting position on loading a save. Consistent between savewarps.""" class TriforceHunt(Toggle): @@ -138,13 +163,13 @@ class MQDungeons(Range): world_options: typing.Dict[str, type(Option)] = { "starting_age": StartingAge, - # "shuffle_interior_entrances": InteriorEntrances, - # "shuffle_grotto_entrances": Toggle, - # "shuffle_dungeon_entrances": Toggle, - # "shuffle_overworld_entrances": Toggle, - # "owl_drops": Toggle, - # "warp_songs": Toggle, - # "spawn_positions": Toggle, + "shuffle_interior_entrances": InteriorEntrances, + "shuffle_grotto_entrances": GrottoEntrances, + "shuffle_dungeon_entrances": DungeonEntrances, + "shuffle_overworld_entrances": OverworldEntrances, + "owl_drops": OwlDrops, + "warp_songs": WarpSongs, + "spawn_positions": SpawnPositions, "triforce_hunt": TriforceHunt, "triforce_goal": TriforceGoal, "extra_triforce_percentage": ExtraTriforces, @@ -765,6 +790,13 @@ sfx_options: typing.Dict[str, type(Option)] = { } +class LogicTricks(OptionList): + """Set various tricks for logic in Ocarina of Time. +Format as a comma-separated list of "nice" names: ["Fewer Tunic Requirements", "Hidden Grottos without Stone of Agony"]. +A full list of supported tricks can be found at https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/oot/LogicTricks.py""" + displayname = "Logic Tricks" + + # All options assembled into a single dict oot_options: typing.Dict[str, type(Option)] = { "logic_rules": Logic, @@ -780,5 +812,6 @@ oot_options: typing.Dict[str, type(Option)] = { **itempool_options, **cosmetic_options, **sfx_options, - "logic_tricks": OptionList, + "logic_tricks": LogicTricks, + "death_link": DeathLink, } diff --git a/worlds/oot/Patches.py b/worlds/oot/Patches.py index 444a1af7..f42ddf12 100644 --- a/worlds/oot/Patches.py +++ b/worlds/oot/Patches.py @@ -3,6 +3,7 @@ import itertools import re import zlib from collections import defaultdict +from functools import partial from .LocationList import business_scrubs from .Hints import writeGossipStoneHints, buildAltarHints, \ @@ -1321,9 +1322,12 @@ def patch_rom(world, rom): # Write item overrides override_table = get_override_table(world) rom.write_bytes(rom.sym('cfg_item_overrides'), get_override_table_bytes(override_table)) - rom.write_byte(rom.sym('PLAYER_ID'), world.player) # Write player ID + rom.write_byte(rom.sym('PLAYER_ID'), min(world.player, 255)) # Write player ID rom.write_bytes(rom.sym('AP_PLAYER_NAME'), bytearray(world.world.get_player_name(world.player), 'ascii')) + if world.death_link: + rom.write_byte(rom.sym('DEATH_LINK'), 0x01) + # Revert Song Get Override Injection if not songs_as_items: # general get song @@ -1804,7 +1808,7 @@ def write_rom_item(rom, item_id, item): def get_override_table(world): - return list(filter(lambda val: val != None, map(get_override_entry, world.world.get_filled_locations(world.player)))) + return list(filter(lambda val: val != None, map(partial(get_override_entry, world.player), world.world.get_filled_locations(world.player)))) override_struct = struct.Struct('>xBBBHBB') # match override_t in get_items.c @@ -1812,10 +1816,10 @@ def get_override_table_bytes(override_table): return b''.join(sorted(itertools.starmap(override_struct.pack, override_table))) -def get_override_entry(location): +def get_override_entry(player_id, location): scene = location.scene default = location.default - player_id = location.item.player + player_id = 0 if player_id == location.item.player else min(location.item.player, 255) if location.item.game != 'Ocarina of Time': # This is an AP sendable. It's guaranteed to not be None. looks_like_item_id = 0 diff --git a/worlds/oot/RuleParser.py b/worlds/oot/RuleParser.py index 5223d3c2..6f972447 100644 --- a/worlds/oot/RuleParser.py +++ b/worlds/oot/RuleParser.py @@ -451,14 +451,16 @@ class Rule_AST_Transformer(ast.NodeTransformer): if self.world.ensure_tod_access: # tod has DAY or (tod == NONE and (ss or find a path from a provider)) # parsing is better than constructing this expression by hand - return ast.parse("(tod & TimeOfDay.DAY) if tod else (state.has_all(('Ocarina', 'Suns Song')) or state.search.can_reach(spot.parent_region, age=age, tod=TimeOfDay.DAY))", mode='eval').body + r = self.current_spot if type(self.current_spot) == OOTRegion else self.current_spot.parent_region + return ast.parse(f"(state.has('Ocarina', player) and state.has('Suns Song', player)) or state._oot_reach_at_time('{r.name}', TimeOfDay.DAY, [], player)", mode='eval').body return ast.NameConstant(True) def at_dampe_time(self, node): if self.world.ensure_tod_access: # tod has DAMPE or (tod == NONE and (find a path from a provider)) # parsing is better than constructing this expression by hand - return ast.parse("(tod & TimeOfDay.DAMPE) if tod else state.search.can_reach(spot.parent_region, age=age, tod=TimeOfDay.DAMPE)", mode='eval').body + r = self.current_spot if type(self.current_spot) == OOTRegion else self.current_spot.parent_region + return ast.parse(f"state._oot_reach_at_time('{r.name}', TimeOfDay.DAMPE, [], player)", mode='eval').body return ast.NameConstant(True) def at_night(self, node): @@ -468,7 +470,8 @@ class Rule_AST_Transformer(ast.NodeTransformer): if self.world.ensure_tod_access: # tod has DAMPE or (tod == NONE and (ss or find a path from a provider)) # parsing is better than constructing this expression by hand - return ast.parse("(tod & TimeOfDay.DAMPE) if tod else (state.has_all(('Ocarina', 'Suns Song')) or state.search.can_reach(spot.parent_region, age=age, tod=TimeOfDay.DAMPE))", mode='eval').body + r = self.current_spot if type(self.current_spot) == OOTRegion else self.current_spot.parent_region + return ast.parse(f"(state.has('Ocarina', player) and state.has('Suns Song', player)) or state._oot_reach_at_time('{r.name}', TimeOfDay.DAMPE, [], player)", mode='eval').body return ast.NameConstant(True) diff --git a/worlds/oot/Rules.py b/worlds/oot/Rules.py index 05b3f015..ec44459b 100644 --- a/worlds/oot/Rules.py +++ b/worlds/oot/Rules.py @@ -2,6 +2,7 @@ from collections import deque import logging from .SaveContext import SaveContext +from .Regions import TimeOfDay from BaseClasses import CollectionState from worlds.generic.Rules import set_rule, add_rule, add_item_rule, forbid_item @@ -42,6 +43,36 @@ class OOTLogic(LogicMixin): return can_reach return self.age[player] == age + def _oot_reach_at_time(self, regionname, tod, already_checked, player): + name_map = { + TimeOfDay.DAY: self.day_reachable_regions[player], + TimeOfDay.DAMPE: self.dampe_reachable_regions[player], + TimeOfDay.ALL: self.day_reachable_regions[player].intersection(self.dampe_reachable_regions[player]) + } + if regionname in name_map[tod]: + return True + region = self.world.get_region(regionname, player) + if region.provides_time == TimeOfDay.ALL or regionname == 'Root': + self.day_reachable_regions[player].add(regionname) + self.dampe_reachable_regions[player].add(regionname) + return True + if region.provides_time == TimeOfDay.DAMPE: + self.dampe_reachable_regions[player].add(regionname) + return tod == TimeOfDay.DAMPE + for entrance in region.entrances: + if entrance.parent_region.name in already_checked: + continue + if self._oot_reach_at_time(entrance.parent_region.name, tod, already_checked + [regionname], player): + if tod == TimeOfDay.DAY: + self.day_reachable_regions[player].add(regionname) + elif tod == TimeOfDay.DAMPE: + self.dampe_reachable_regions[player].add(regionname) + elif tod == TimeOfDay.ALL: + self.day_reachable_regions[player].add(regionname) + self.dampe_reachable_regions[player].add(regionname) + return True + return False + # Store the age before calling this! def _oot_update_age_reachable_regions(self, player): self.stale[player] = False @@ -62,6 +93,8 @@ class OOTLogic(LogicMixin): while queue: connection = queue.popleft() new_region = connection.connected_region + if new_region is None: + continue if new_region in rrp: bc.remove(connection) elif connection.can_reach(self): diff --git a/worlds/oot/__init__.py b/worlds/oot/__init__.py index e4dc8d84..a97a6a0b 100644 --- a/worlds/oot/__init__.py +++ b/worlds/oot/__init__.py @@ -7,7 +7,7 @@ logger = logging.getLogger("Ocarina of Time") from .Location import OOTLocation, LocationFactory, location_name_to_id from .Entrance import OOTEntrance -from .EntranceShuffle import shuffle_random_entrances +from .EntranceShuffle import shuffle_random_entrances, entrance_shuffle_table, EntranceShuffleError from .Items import OOTItem, item_table, oot_data_to_ap_id from .ItemPool import generate_itempool, add_dungeon_items, get_junk_item, get_junk_pool from .Regions import OOTRegion, TimeOfDay @@ -39,6 +39,11 @@ i_o_limiter = threading.Semaphore(2) class OOTWorld(World): + """ + The Legend of Zelda: Ocarina of Time is a 3D action/adventure game. Travel through Hyrule in two time periods, + learn magical ocarina songs, and explore twelve dungeons on your quest. Use Link's many items and abilities + to rescue the Seven Sages, and then confront Ganondorf to save Hyrule! + """ game: str = "Ocarina of Time" options: dict = oot_options topology_present: bool = True @@ -61,6 +66,8 @@ class OOTWorld(World): self.adult_reachable_regions = {player: set() for player in range(1, parent.players + 1)} self.child_blocked_connections = {player: set() for player in range(1, parent.players + 1)} self.adult_blocked_connections = {player: set() for player in range(1, parent.players + 1)} + self.day_reachable_regions = {player: set() for player in range(1, parent.players + 1)} + self.dampe_reachable_regions = {player: set() for player in range(1, parent.players + 1)} self.age = {player: None for player in range(1, parent.players + 1)} def oot_copy(self): @@ -73,6 +80,10 @@ class OOTWorld(World): range(1, self.world.players + 1)} ret.adult_blocked_connections = {player: copy.copy(self.adult_blocked_connections[player]) for player in range(1, self.world.players + 1)} + ret.day_reachable_regions = {player: copy.copy(self.adult_reachable_regions[player]) for player in + range(1, self.world.players + 1)} + ret.dampe_reachable_regions = {player: copy.copy(self.adult_reachable_regions[player]) for player in + range(1, self.world.players + 1)} return ret CollectionState.__init__ = oot_init @@ -83,6 +94,8 @@ class OOTWorld(World): world.state.adult_reachable_regions = {player: set() for player in range(1, world.players + 1)} world.state.child_blocked_connections = {player: set() for player in range(1, world.players + 1)} world.state.adult_blocked_connections = {player: set() for player in range(1, world.players + 1)} + world.state.day_reachable_regions = {player: set() for player in range(1, world.players + 1)} + world.state.dampe_reachable_regions = {player: set() for player in range(1, world.players + 1)} world.state.age = {player: None for player in range(1, world.players + 1)} return super().__new__(cls) @@ -173,14 +186,6 @@ class OOTWorld(World): self.mq_dungeons_random = False # this will be a deprecated option later self.ocarina_songs = False # just need to pull in the OcarinaSongs module self.big_poe_count = 1 # disabled due to client-side issues for now - # ER options - self.shuffle_interior_entrances = 'off' - self.shuffle_grotto_entrances = False - self.shuffle_dungeon_entrances = False - self.shuffle_overworld_entrances = False - self.owl_drops = False - self.warp_songs = False - self.spawn_positions = False # Set internal names used by the OoT generator self.keysanity = self.shuffle_smallkeys in ['keysanity', 'remove', 'any_dungeon', 'overworld'] @@ -313,7 +318,7 @@ class OOTWorld(World): new_location.show_in_spoiler = False if 'exits' in region: for exit, rule in region['exits'].items(): - new_exit = OOTEntrance(self.player, '%s => %s' % (new_region.name, exit), new_region) + new_exit = OOTEntrance(self.player, self.world, '%s -> %s' % (new_region.name, exit), new_region) new_exit.vanilla_connected_region = exit new_exit.rule_string = rule if self.world.logic_rules != 'none': @@ -411,7 +416,8 @@ class OOTWorld(World): def create_item(self, name: str): if name in item_table: - return OOTItem(name, self.player, item_table[name], False, (name in self.nonadvancement_items)) + return OOTItem(name, self.player, item_table[name], False, + (name in self.nonadvancement_items if getattr(self, 'nonadvancement_items', None) else False)) return OOTItem(name, self.player, ('Event', True, None, None), True, False) def make_event_item(self, name, location, item=None): @@ -431,7 +437,7 @@ class OOTWorld(World): world_type = 'Glitched World' overworld_data_path = data_path(world_type, 'Overworld.json') menu = OOTRegion('Menu', None, None, self.player) - start = OOTEntrance(self.player, 'New Game', menu) + start = OOTEntrance(self.player, self.world, 'New Game', menu) menu.exits.append(start) self.world.regions.append(menu) self.load_regions_from_json(overworld_data_path) @@ -443,14 +449,10 @@ class OOTWorld(World): self.random_shop_prices() self.set_scrub_prices() - # logger.info('Setting Entrances.') - # set_entrances(self) - # Enforce vanilla for now + # Bind entrances to vanilla for region in self.regions: for exit in region.exits: exit.connect(self.world.get_region(exit.vanilla_connected_region, self.player)) - if self.entrance_shuffle: - shuffle_random_entrances(self) def create_items(self): # Generate itempool @@ -481,6 +483,50 @@ class OOTWorld(World): self.remove_from_start_inventory.extend(removed_items) def set_rules(self): + # This has to run AFTER creating items but BEFORE set_entrances_based_rules + if self.entrance_shuffle: + # 10 attempts at shuffling entrances + tries = 10 + while tries: + try: + shuffle_random_entrances(self) + except EntranceShuffleError as e: + tries -= 1 + logging.getLogger('').debug(f"Failed shuffling entrances for world {self.player}, retrying {tries} more times") + if tries == 0: + raise e + # Restore original state and delete assumed entrances + for entrance in self.get_shuffled_entrances(): + entrance.connect(self.world.get_region(entrance.vanilla_connected_region, self.player)) + if entrance.assumed: + assumed_entrance = entrance.assumed + if assumed_entrance.connected_region is not None: + assumed_entrance.disconnect() + del assumed_entrance + entrance.reverse = None + entrance.replaces = None + entrance.assumed = None + entrance.shuffled = False + # Clean up root entrances + root = self.get_region("Root Exits") + root.exits = root.exits[:8] + else: + break + + # Write entrances to spoiler log + all_entrances = self.get_shuffled_entrances() + all_entrances.sort(key=lambda x: x.name) + all_entrances.sort(key=lambda x: x.type) + for loadzone in all_entrances: + if loadzone.primary: + entrance = loadzone + else: + entrance = loadzone.reverse + if entrance.reverse is not None: + self.world.spoiler.set_entrance(entrance, entrance.replaces, 'both', self.player) + else: + self.world.spoiler.set_entrance(entrance, entrance.replaces, 'entrance', self.player) + set_rules(self) set_entrances_based_rules(self) @@ -506,7 +552,7 @@ class OOTWorld(World): all_locations = self.get_locations() reachable = self.world.get_reachable_locations(all_state, self.player) unreachable = [loc for loc in all_locations if - loc.internal and loc.event and loc.locked and loc not in reachable] + (loc.internal or loc.type == 'Drop') and loc.event and loc.locked and loc not in reachable] for loc in unreachable: loc.parent_region.locations.remove(loc) # Exception: Sell Big Poe is an event which is only reachable if Bottle with Big Poe is in the item pool. @@ -618,18 +664,35 @@ class OOTWorld(World): songs = list(filter(lambda item: item.player == self.player and item.type == 'Song', self.world.itempool)) for song in songs: self.world.itempool.remove(song) + + important_warps = (self.shuffle_special_interior_entrances or self.shuffle_overworld_entrances or + self.warp_songs or self.spawn_positions) + song_order = { + 'Zeldas Lullaby': 1, + 'Eponas Song': 1, + 'Sarias Song': 3 if important_warps else 0, + 'Suns Song': 0, + 'Song of Time': 0, + 'Song of Storms': 3, + 'Minuet of Forest': 2 if important_warps else 0, + 'Bolero of Fire': 2 if important_warps else 0, + 'Serenade of Water': 2 if important_warps else 0, + 'Requiem of Spirit': 2, + 'Nocturne of Shadow': 2, + 'Prelude of Light': 2 if important_warps else 0, + } + songs.sort(key=lambda song: song_order.get(song.name, 0)) + while tries: try: - self.world.random.shuffle(songs) # shuffling songs makes it less likely to fail by placing ZL last self.world.random.shuffle(song_locations) fill_restrictive(self.world, self.world.get_all_state(False), song_locations[:], songs[:], True, True) logger.debug(f"Successfully placed songs for player {self.player} after {6 - tries} attempt(s)") - tries = 0 except FillError as e: tries -= 1 if tries == 0: - raise e + raise Exception(f"Failed placing songs for player {self.player}. Error cause: {e}") logger.debug(f"Failed placing songs for player {self.player}. Retries left: {tries}") # undo what was done for song in songs: @@ -639,6 +702,8 @@ class OOTWorld(World): location.item = None location.locked = False location.event = False + else: + break # Place shop items # fast fill will fail because there is some logic on the shop items. we'll gather them up and place the shop items @@ -712,6 +777,9 @@ class OOTWorld(World): for trap in ice_traps: trap.looks_like_item = self.create_item(self.world.slot_seeds[self.player].choice(self.fake_items).name) + # Seed hint RNG, used for ganon text lines also + self.hint_rng = self.world.slot_seeds[self.player] + outfile_name = f"AP_{self.world.seed_name}_P{self.player}_{self.world.get_player_name(self.player)}" rom = Rom(file=get_options()['oot_options']['rom_file']) if self.hints != 'none': @@ -787,6 +855,23 @@ class OOTWorld(World): autoworld.hint_data_available.set() def modify_multidata(self, multidata: dict): + + hint_entrances = set() + for entrance in entrance_shuffle_table: + hint_entrances.add(entrance[1][0]) + if len(entrance) > 2: + hint_entrances.add(entrance[2][0]) + + def get_entrance_to_region(region): + if region.name == 'Root': + return None + for entrance in region.entrances: + if entrance.name in hint_entrances: + return entrance + for entrance in region.entrances: + return get_entrance_to_region(entrance.parent_region) + + # Remove undesired items from start_inventory for item_name in self.remove_from_start_inventory: item_id = self.item_name_to_id.get(item_name, None) try: @@ -794,10 +879,26 @@ class OOTWorld(World): except ValueError as e: logger.warning(f"Attempted to remove nonexistent item id {item_id} from OoT precollected items ({item_name})") + # Add ER hint data + if self.shuffle_interior_entrances != 'off' or self.shuffle_dungeon_entrances or self.shuffle_grotto_entrances: + er_hint_data = {} + for region in self.regions: + main_entrance = get_entrance_to_region(region) + if main_entrance is not None and main_entrance.shuffled: + for location in region.locations: + if type(location.address) == int: + er_hint_data[location.address] = main_entrance.name + multidata['er_hint_data'][self.player] = er_hint_data + # Helper functions - def get_shuffled_entrances(self): - return [] # later this will return all entrances modified by ER. patching process needs it now though + def get_shufflable_entrances(self, type=None, only_primary=False): + return [entrance for entrance in self.world.get_entrances() if (entrance.player == self.player and + (type == None or entrance.type == type) and + (not only_primary or entrance.primary))] + + def get_shuffled_entrances(self, type=None, only_primary=False): + return [entrance for entrance in self.get_shufflable_entrances(type=type, only_primary=only_primary) if entrance.shuffled] def get_locations(self): for region in self.regions: @@ -810,6 +911,9 @@ class OOTWorld(World): def get_region(self, region): return self.world.get_region(region, self.player) + def get_entrance(self, entrance): + return self.world.get_entrance(entrance, self.player) + def is_major_item(self, item: OOTItem): if item.type == 'Token': return self.bridge == 'tokens' or self.lacs_condition == 'tokens' @@ -835,3 +939,29 @@ class OOTWorld(World): return False return True + + # Specifically ensures that only real items are gotten, not any events. + # In particular, ensures that Time Travel needs to be found. + def get_state_with_complete_itempool(self): + all_state = self.world.get_all_state(use_cache=False) + # Remove event progression items + for item, player in all_state.prog_items: + if (item not in item_table or item_table[item][2] is None) and player == self.player: + all_state.prog_items[(item, player)] = 0 + # Remove all events and checked locations + all_state.locations_checked = {loc for loc in all_state.locations_checked if loc.player != self.player} + all_state.events = {loc for loc in all_state.events if loc.player != self.player} + # If free_scarecrow give Scarecrow Song + if self.free_scarecrow: + all_state.collect(self.create_item("Scarecrow Song"), event=True) + + # Invalidate caches + all_state.child_reachable_regions[self.player] = set() + all_state.adult_reachable_regions[self.player] = set() + all_state.child_blocked_connections[self.player] = set() + all_state.adult_blocked_connections[self.player] = set() + all_state.day_reachable_regions[self.player] = set() + all_state.dampe_reachable_regions[self.player] = set() + all_state.stale[self.player] = True + + return all_state diff --git a/worlds/oot/data/World/Overworld.json b/worlds/oot/data/World/Overworld.json index 398f3fed..b483a18d 100644 --- a/worlds/oot/data/World/Overworld.json +++ b/worlds/oot/data/World/Overworld.json @@ -1720,7 +1720,8 @@ "Lake Hylia": "is_child and can_dive", "ZD Behind King Zora": " Deliver_Letter or zora_fountain == 'open' or - (zora_fountain == 'adult' and is_adult)", + (zora_fountain == 'adult' and is_adult) or + (logic_king_zora_skip and is_adult)", "ZD Shop": "is_child or Blue_Fire", "ZD Storms Grotto": "can_open_storm_grotto" } diff --git a/worlds/oot/data/generated/rom_patch.txt b/worlds/oot/data/generated/rom_patch.txt index ed1fe3cf..8a6283bf 100644 --- a/worlds/oot/data/generated/rom_patch.txt +++ b/worlds/oot/data/generated/rom_patch.txt @@ -186,8 +186,8 @@ d288,0 89ebe8,0 89ebec,0 89ebf0,0 -a88f78,c102359 -a89048,c102372 +a88f78,c10235a +a89048,c102373 a98c30,c1003c6 a9e838,810077c ac7ad4,c100346 @@ -201,7 +201,7 @@ ac9abc,c100407 ac9ac0,0 accd34,c1004c1 accd38,8e190000 -accde0,c101b66 +accde0,c101b67 acce18,c1008d6 acce1c,8e0200a4 acce20,1060001e @@ -249,7 +249,7 @@ aeb67c,c1009b3 aeb680,0 aeb764,26380008 aeb768,ae9802b0 -aeb76c,c101da2 +aeb76c,c101da3 aeb778,400821 af1814,c100e6e af1818,0 @@ -323,8 +323,8 @@ b10cc0,c1004a9 b10cc4,3c010001 b12a34,c100655 b12a38,0 -b12a60,8101f86 -b12e44,8101f95 +b12a60,8101f87 +b12e44,8101f96 b17bb4,afbf001c b17bb8,afa40140 b17bbc,3c048040 @@ -581,7 +581,7 @@ c898a4,92191d29 c898c8,920a1d2a c8b24c,c100504 c8b250,2002025 -ca6dc0,8102394 +ca6dc0,8102395 ca6dc4,0 cb6874,c10069c cc0038,c100465 @@ -730,7 +730,7 @@ d7ec60,8100d7e d7ec70,8100da4 db13d0,24090076 db532c,c100435 -db53e8,81023c6 +db53e8,81023c7 db53ec,0 dbec80,34020000 dbf428,c1007f9 @@ -758,7 +758,7 @@ dd3754,3c064000 dd375c,3c074000 dd3760,0 dd3764,0 -de1018,c1023df +de1018,c1023e0 de101c,0 de1020,0 de1024,0 @@ -768,7 +768,7 @@ de1030,0 de1034,0 de1038,0 de103c,0 -de1050,81023df +de1050,81023e0 de1054,0 df2644,76 df7a90,340e0018 @@ -870,9 +870,9 @@ e565d4,0 e565d8,2002025 e59cd4,0 e59cd8,0 -e59e68,8102418 +e59e68,8102419 e59e6c,0 -e59ecc,8102441 +e59ecc,8102442 e59ed0,0 e5b2f4,c100e83 e5b2f8,afa5001c @@ -964,7 +964,7 @@ ec1124,0 ec68bc,8fad002c ec68c0,340c000a ec68c4,a5ac0110 -ec68c8,c101b2f +ec68c8,c101b25 ec68cc,2002021 ec68d0,0 ec68d4,0 @@ -974,14 +974,14 @@ ec68e0,0 ec69ac,8fad002c ec69b0,340c000a ec69b4,a5ac0110 -ec69b8,c101b2f +ec69b8,c101b25 ec69bc,2002021 ec69c0,0 ec69c4,0 ec69c8,0 ec69cc,0 ec69d0,0 -ec6b04,81023ad +ec6b04,81023ae ec6b08,0 ec9ce4,2419007a ed2858,20180008 @@ -1011,7 +1011,7 @@ ef4f9c,0 26c10e0,38ff 3480000,80400020 3480004,80400844 -3480008,80409f9c +3480008,80409fa4 3480020,3 3480034,dfdfdfdf 3480038,dfdfdfdf @@ -1546,7 +1546,7 @@ ef4f9c,0 3480cd4,100 3480ce4,27bdffe8 3480ce8,afbf0010 -3480cec,c101f66 +3480cec,c101f67 3480cf4,3c028012 3480cf8,2442d2a0 3480cfc,240e0140 @@ -1595,19 +1595,19 @@ ef4f9c,0 3480dac,810036d 3480db0,87250004 3480db4,3c0a8041 -3480db8,254ab1b0 +3480db8,254ab1b8 3480dbc,8d4a0000 3480dc0,11400004 3480dc8,3c058041 -3480dcc,24a5b1a4 +3480dcc,24a5b1ac 3480dd0,8ca50000 3480dd4,3e00008 3480ddc,3c088041 -3480de0,2508b1b0 +3480de0,2508b1b8 3480de4,8d080000 3480de8,11000004 3480df0,3c038041 -3480df4,2463b1a0 +3480df4,2463b1a8 3480df8,8c630000 3480dfc,30fc3 3480e00,614026 @@ -1620,12 +1620,12 @@ ef4f9c,0 3480e20,1500000c 3480e24,240bffff 3480e28,3c088041 -3480e2c,2508b1b0 +3480e2c,2508b1b8 3480e30,8d080000 3480e34,11000007 3480e38,1405821 3480e3c,3c088041 -3480e40,2508b19c +3480e40,2508b1a4 3480e44,8d080000 3480e48,15000002 3480e4c,240bffff @@ -1641,32 +1641,32 @@ ef4f9c,0 3480e7c,3e00008 3480e84,90450003 3480e88,3c088041 -3480e8c,2508b1b0 +3480e8c,2508b1b8 3480e90,8d080000 3480e94,11000004 3480e9c,3c058041 -3480ea0,24a5b1a8 +3480ea0,24a5b1b0 3480ea4,8ca50000 3480ea8,3e00008 3480eb0,27bdffe8 3480eb4,afb00010 3480eb8,afbf0014 3480ebc,3c088041 -3480ec0,2508b1b4 +3480ec0,2508b1bc 3480ec4,8d080000 3480ec8,31080001 3480ecc,1500000b 3480ed0,34100041 3480ed4,3c048041 -3480ed8,2484b1b0 +3480ed8,2484b1b8 3480edc,8c840000 3480ee0,10800006 3480ee4,90500000 3480ee8,3c088041 -3480eec,2508b1ac +3480eec,2508b1b4 3480ef0,8d100000 -3480ef4,c101ebd -3480efc,c101a7b +3480ef4,c101ebe +3480efc,c101a78 3480f04,2002821 3480f08,8fb00010 3480f0c,8fbf0014 @@ -1677,7 +1677,7 @@ ef4f9c,0 3480f20,afa20014 3480f24,afa30018 3480f28,afbf001c -3480f2c,c101aec +3480f2c,c101ae9 3480f30,e02821 3480f34,8fa70010 3480f38,8fa20014 @@ -1718,7 +1718,7 @@ ef4f9c,0 3480fd8,ac881d2c 3480fdc,c101a2b 3480fe0,1402021 -3480fe4,c1024e5 +3480fe4,c1024e6 3480fec,8fbf0010 3480ff0,34020000 3480ff4,3e00008 @@ -2272,7 +2272,7 @@ ef4f9c,0 348194c,3e00008 3481954,27bdffe8 3481958,afbf0010 -348195c,c101f75 +348195c,c101f76 3481964,8fbf0010 3481968,27bd0018 348196c,8fae0018 @@ -2319,7 +2319,7 @@ ef4f9c,0 3481a24,afbf0000 3481a28,afa30004 3481a2c,afa40008 -3481a30,c101fa2 +3481a30,c101fa3 3481a38,8fbf0000 3481a3c,8fa30004 3481a40,8fa40008 @@ -2564,7 +2564,7 @@ ef4f9c,0 3481eb0,24a50008 3481eb4,c015c0c 3481eb8,24c6fff8 -3481ebc,c1024ef +3481ebc,c1024f0 3481ec4,8fbf0010 3481ec8,8fa70008 3481ecc,8fa60004 @@ -4040,7 +4040,7 @@ ef4f9c,0 34839c0,8f180004 34839c4,17000003 34839cc,3c0a8041 -34839d0,254ab100 +34839d0,254ab108 34839d4,24780008 34839d8,3e00008 34839dc,adf802c0 @@ -4049,7 +4049,7 @@ ef4f9c,0 34839e8,8def0004 34839ec,15e00003 34839f4,3c0e8041 -34839f8,25ceb100 +34839f8,25ceb108 34839fc,ac4e0004 3483a00,3e00008 3483a04,820f013f @@ -4105,7 +4105,7 @@ ef4f9c,0 3483ad8,27bdffe8 3483adc,afa20010 3483ae0,afbf0014 -3483ae4,c102111 +3483ae4,c102112 3483ae8,46000306 3483aec,406821 3483af0,8fa20010 @@ -4114,28 +4114,28 @@ ef4f9c,0 3483afc,27bd0018 3483b00,3e00008 3483b08,3c028041 -3483b0c,8c439ed8 +3483b0c,8c439ee0 3483b10,3c028041 -3483b14,24429c4e +3483b14,24429c52 3483b18,14620004 3483b1c,3c038041 3483b20,3c028041 -3483b24,24429c50 -3483b28,ac629ed8 +3483b24,24429c54 +3483b28,ac629ee0 3483b2c,3e00008 3483b34,3c028041 -3483b38,8c439ed8 +3483b38,8c439ee0 3483b3c,3c028041 -3483b40,24429c34 +3483b40,24429c38 3483b44,10620003 3483b48,3c028041 3483b4c,10000003 -3483b50,24429c54 +3483b50,24429c58 3483b54,3c028041 -3483b58,24429c36 +3483b58,24429c3a 3483b5c,3c038041 3483b60,3e00008 -3483b64,ac629ed8 +3483b64,ac629ee0 3483b68,27bdffc8 3483b6c,afbf0034 3483b70,afb40030 @@ -4162,7 +4162,7 @@ ef4f9c,0 3483bc4,3c04de00 3483bc8,ac640000 3483bcc,3c048041 -3483bd0,2484a0b0 +3483bd0,2484a0b8 3483bd4,ac640004 3483bd8,8e0302b0 3483bdc,24640008 @@ -4195,8 +4195,8 @@ ef4f9c,0 3483c48,24070001 3483c4c,24060009 3483c50,3c148041 -3483c54,2685a070 -3483c58,c101b99 +3483c54,2685a078 +3483c58,c101b9a 3483c5c,2602025 3483c60,24020010 3483c64,afa20018 @@ -4205,8 +4205,8 @@ ef4f9c,0 3483c70,afb10010 3483c74,2647001b 3483c78,3025 -3483c7c,2685a070 -3483c80,c101c01 +3483c7c,2685a078 +3483c80,c101c02 3483c84,2602025 3483c88,8e0202b0 3483c8c,24430008 @@ -4223,9 +4223,9 @@ ef4f9c,0 3483cb8,3e00008 3483cbc,27bd0038 3483cc0,3c028041 -3483cc4,8c439ed8 +3483cc4,8c439ee0 3483cc8,3c028041 -3483ccc,24429c34 +3483ccc,24429c38 3483cd0,10620021 3483cd8,27bdffe8 3483cdc,afbf0014 @@ -4237,10 +4237,10 @@ ef4f9c,0 3483cf4,3042003f 3483cf8,21040 3483cfc,3c038041 -3483d00,24639c34 +3483d00,24639c38 3483d04,621021 3483d08,3c038041 -3483d0c,ac629ed8 +3483d0c,ac629ee0 3483d10,3c02801c 3483d14,344284a0 3483d18,944300a4 @@ -4282,11 +4282,11 @@ ef4f9c,0 3483db0,16200003 3483db8,1000000b 3483dbc,920201e9 -3483dc0,c101e96 +3483dc0,c101e97 3483dc4,93a40017 3483dc8,54400004 3483dcc,90420007 -3483dd0,c101e96 +3483dd0,c101e97 3483dd4,2202025 3483dd8,90420007 3483ddc,30420001 @@ -4320,11 +4320,11 @@ ef4f9c,0 3483e4c,16200003 3483e54,10000009 3483e58,920201e9 -3483e5c,c101e96 +3483e5c,c101e97 3483e60,93a40017 3483e64,54400004 3483e68,90420007 -3483e6c,c101e96 +3483e6c,c101e97 3483e70,2202025 3483e74,90420007 3483e78,30420002 @@ -4559,7 +4559,7 @@ ef4f9c,0 3484214,3c04de00 3484218,ac640000 348421c,3c048041 -3484220,2484a0b0 +3484220,2484a0b8 3484224,ac640004 3484228,8e0302b0 348422c,24640008 @@ -4593,8 +4593,8 @@ ef4f9c,0 348429c,ac430004 34842a0,3025 34842a4,3c138041 -34842a8,2665a050 -34842ac,c101b99 +34842a8,2665a058 +34842ac,c101b9a 34842b0,2202025 34842b4,24020010 34842b8,afa20018 @@ -4603,8 +4603,8 @@ ef4f9c,0 34842c4,afa20010 34842c8,2407010f 34842cc,3025 -34842d0,2665a050 -34842d4,c101c01 +34842d0,2665a058 +34842d4,c101c02 34842d8,2202025 34842dc,240200ff 34842e0,16420023 @@ -4655,8 +4655,8 @@ ef4f9c,0 3484394,24070001 3484398,24060045 348439c,3c058041 -34843a0,24a5a080 -34843a4,c101b99 +34843a0,24a5a088 +34843a4,c101b9a 34843a8,2202025 34843ac,3c028011 34843b0,3442a5d0 @@ -4673,8 +4673,8 @@ ef4f9c,0 34843dc,24070102 34843e0,3025 34843e4,3c058041 -34843e8,24a5a080 -34843ec,c101c01 +34843e8,24a5a088 +34843ec,c101c02 34843f0,2202025 34843f4,1000000c 34843f8,3c028011 @@ -4685,8 +4685,8 @@ ef4f9c,0 348440c,24070104 3484410,3025 3484414,3c058041 -3484418,24a5a080 -348441c,c101c01 +3484418,24a5a088 +348441c,c101c02 3484420,2202025 3484424,3c028011 3484428,3442a5d0 @@ -4701,8 +4701,8 @@ ef4f9c,0 348444c,24070001 3484450,24060046 3484454,3c058041 -3484458,24a5a080 -348445c,c101b99 +3484458,24a5a088 +348445c,c101b9a 3484460,2202025 3484464,3c028011 3484468,3442a5d0 @@ -4719,8 +4719,8 @@ ef4f9c,0 3484494,2407011b 3484498,3025 348449c,3c058041 -34844a0,24a5a080 -34844a4,c101c01 +34844a0,24a5a088 +34844a4,c101c02 34844a8,2202025 34844ac,1000000c 34844b0,3c028011 @@ -4731,8 +4731,8 @@ ef4f9c,0 34844c4,2407011d 34844c8,3025 34844cc,3c058041 -34844d0,24a5a080 -34844d4,c101c01 +34844d0,24a5a088 +34844d4,c101c02 34844d8,2202025 34844dc,3c028011 34844e0,3442a5d0 @@ -4779,8 +4779,8 @@ ef4f9c,0 3484584,3442a5d0 3484588,8046007b 348458c,3c128041 -3484590,2645a080 -3484594,c101b99 +3484590,2645a088 +3484594,c101b9a 3484598,2202025 348459c,2402000c 34845a0,afa20018 @@ -4789,8 +4789,8 @@ ef4f9c,0 34845ac,afa20010 34845b0,24070111 34845b4,3025 -34845b8,2645a080 -34845bc,c101c01 +34845b8,2645a088 +34845bc,c101c02 34845c0,2202025 34845c4,8e0202b0 34845c8,24430008 @@ -4815,8 +4815,8 @@ ef4f9c,0 3484614,ac430004 3484618,3025 348461c,3c128041 -3484620,2645a050 -3484624,c101b99 +3484620,2645a058 +3484624,c101b9a 3484628,2202025 348462c,24020010 3484630,afa20018 @@ -4825,14 +4825,14 @@ ef4f9c,0 348463c,afa20010 3484640,2407010f 3484644,3025 -3484648,2645a050 -348464c,c101c01 +3484648,2645a058 +348464c,c101c02 3484650,2202025 3484654,1000ff24 3484658,241200ff 348465c,3e00008 3484664,3c028041 -3484668,8c42b0e4 +3484668,8c42b0ec 348466c,10400274 3484670,3c02801c 3484674,27bdff90 @@ -4875,24 +4875,24 @@ ef4f9c,0 3484708,3c03de00 348470c,ac430000 3484710,3c038041 -3484714,2463a0b0 +3484714,2463a0b8 3484718,ac430004 348471c,3c028011 3484720,3442a5d0 3484724,94430f2e 3484728,3c028041 -348472c,8c42b0e0 +348472c,8c42b0e8 3484730,10400012 3484734,3025 3484738,3c028041 -348473c,8c42b17c +348473c,8c42b184 3484740,10400010 3484744,24060001 3484748,30620001 348474c,54400006 3484750,30630002 3484754,3c028041 -3484758,8c42b17c +3484758,8c42b184 348475c,1040000c 3484760,3025 3484764,30630002 @@ -4909,17 +4909,17 @@ ef4f9c,0 3484790,24020001 3484794,afa2003c 3484798,3c028041 -348479c,8c55b188 +348479c,8c55b190 34847a0,12a00007 34847a4,2a01825 34847a8,3c028041 -34847ac,9442a064 +34847ac,9442a06c 34847b0,21840 34847b4,621821 34847b8,31840 34847bc,24630001 34847c0,3c028041 -34847c4,9442a064 +34847c4,9442a06c 34847c8,210c0 34847cc,24420057 34847d0,431021 @@ -4995,29 +4995,29 @@ ef4f9c,0 34848e8,10c0004c 34848ec,ac430004 34848f0,3c058041 -34848f4,24a5a090 +34848f4,24a5a098 34848f8,94a70008 34848fc,3025 -3484900,c101b99 +3484900,c101b9a 3484904,2002025 3484908,3c028041 -348490c,8c42b0e8 +348490c,8c42b0f0 3484910,18400042 3484914,3c028041 3484918,3c128041 -348491c,26529f00 +348491c,26529f08 3484920,2414000a 3484924,9825 3484928,3c1e8041 348492c,3c168041 -3484930,26d69edc -3484934,24429eec +3484930,26d69ee4 +3484934,24429ef4 3484938,afa20040 348493c,3c028041 -3484940,2442a090 +3484940,2442a098 3484944,afa20044 3484948,3c178041 -348494c,8fc2b180 +348494c,8fc2b188 3484950,5040000b 3484954,92420000 3484958,92430000 @@ -5061,11 +5061,11 @@ ef4f9c,0 34849f0,afb40010 34849f4,8fa70038 34849f8,8fa50044 -34849fc,c101c01 +34849fc,c101c02 3484a00,2002025 3484a04,26730001 3484a08,2652000c -3484a0c,8ee2b0e8 +3484a0c,8ee2b0f0 3484a10,262102a 3484a14,1440ffcd 3484a18,26940011 @@ -5080,29 +5080,29 @@ ef4f9c,0 3484a3c,10400037 3484a40,3c028041 3484a44,3c058041 -3484a48,24a5a0a0 +3484a48,24a5a0a8 3484a4c,94a70008 3484a50,3025 -3484a54,c101b99 +3484a54,c101b9a 3484a58,2002025 3484a5c,3c028041 -3484a60,8c42b0e8 +3484a60,8c42b0f0 3484a64,18400168 3484a68,3c1e8041 3484a6c,3c128041 -3484a70,26529f00 +3484a70,26529f08 3484a74,2414000a 3484a78,9825 3484a7c,3c168041 -3484a80,26d69edc +3484a80,26d69ee4 3484a84,3c028041 -3484a88,2442a0a0 +3484a88,2442a0a8 3484a8c,afa20040 3484a90,3c028011 3484a94,3442a5d0 3484a98,afa2003c 3484a9c,3c178041 -3484aa0,8fc2b180 +3484aa0,8fc2b188 3484aa4,10400009 3484aa8,92420000 3484aac,8fa3003c @@ -5124,50 +5124,50 @@ ef4f9c,0 3484aec,afb40010 3484af0,8fa70038 3484af4,8fa50040 -3484af8,c101c01 +3484af8,c101c02 3484afc,2002025 3484b00,26730001 3484b04,2652000c -3484b08,8ee2b0e8 +3484b08,8ee2b0f0 3484b0c,262102a 3484b10,1440ffe3 3484b14,26940011 3484b18,3c028041 -3484b1c,8c42b0e8 +3484b1c,8c42b0f0 3484b20,18400010 3484b24,26310012 3484b28,3c128041 -3484b2c,26529f02 +3484b2c,26529f0a 3484b30,2414000b 3484b34,9825 3484b38,3c168041 3484b3c,2803025 3484b40,2202825 -3484b44,c10250c +3484b44,c10250d 3484b48,2402025 3484b4c,26730001 3484b50,2652000c -3484b54,8ec2b0e8 +3484b54,8ec2b0f0 3484b58,262102a 3484b5c,1440fff7 3484b60,26940011 3484b64,3c028041 -3484b68,9456a064 +3484b68,9456a06c 3484b6c,16b0c0 3484b70,26d60001 3484b74,2d1b021 3484b78,24070001 3484b7c,24060011 3484b80,3c058041 -3484b84,24a5a070 -3484b88,c101b99 +3484b84,24a5a078 +3484b88,c101b9a 3484b8c,2002025 3484b90,3c028041 -3484b94,8c42b0e8 +3484b94,8c42b0f0 3484b98,18400024 3484b9c,241e3000 3484ba0,3c118041 -3484ba4,26319f00 +3484ba4,26319f08 3484ba8,2413000b 3484bac,9025 3484bb0,3c178011 @@ -5193,11 +5193,11 @@ ef4f9c,0 3484c00,a3a20020 3484c04,2603025 3484c08,2c02825 -3484c0c,c10250c +3484c0c,c10250d 3484c10,27a40020 3484c14,26520001 3484c18,2631000c -3484c1c,8e82b0e8 +3484c1c,8e82b0f0 3484c20,242102a 3484c24,1440ffe5 3484c28,26730011 @@ -5205,20 +5205,20 @@ ef4f9c,0 3484c30,24070001 3484c34,2406000e 3484c38,3c058041 -3484c3c,24a5a070 -3484c40,c101b99 +3484c3c,24a5a078 +3484c40,c101b9a 3484c44,2002025 3484c48,3c028041 -3484c4c,8c42b0e8 +3484c4c,8c42b0f0 3484c50,18400027 3484c54,3c028041 3484c58,3c118041 -3484c5c,26319f00 +3484c5c,26319f08 3484c60,2413000a 3484c64,9025 3484c68,3c178011 3484c6c,36f7a5d0 -3484c70,2442a070 +3484c70,2442a078 3484c74,afa20038 3484c78,3c148041 3484c7c,92230000 @@ -5242,31 +5242,31 @@ ef4f9c,0 3484cc4,3c03825 3484cc8,3025 3484ccc,8fa50038 -3484cd0,c101c01 +3484cd0,c101c02 3484cd4,2002025 3484cd8,26520001 3484cdc,2631000c -3484ce0,8e82b0e8 +3484ce0,8e82b0f0 3484ce4,242102a 3484ce8,1440ffe4 3484cec,26730011 3484cf0,24070001 3484cf4,2406000a 3484cf8,3c058041 -3484cfc,24a5a070 -3484d00,c101b99 +3484cfc,24a5a078 +3484d00,c101b9a 3484d04,2002025 3484d08,3c028041 -3484d0c,8c42b0e8 +3484d0c,8c42b0f0 3484d10,18400022 3484d14,3c028041 3484d18,3c118041 -3484d1c,26319f01 +3484d1c,26319f09 3484d20,2413000a 3484d24,9025 3484d28,3c178011 3484d2c,36f7a5d0 -3484d30,2442a070 +3484d30,2442a078 3484d34,afa20038 3484d38,3c148041 3484d3c,92220000 @@ -5285,11 +5285,11 @@ ef4f9c,0 3484d70,3c03825 3484d74,3025 3484d78,8fa50038 -3484d7c,c101c01 +3484d7c,c101c02 3484d80,2002025 3484d84,26520001 3484d88,2631000c -3484d8c,8e82b0e8 +3484d8c,8e82b0f0 3484d90,242102a 3484d94,1440ffe9 3484d98,26730011 @@ -5297,20 +5297,20 @@ ef4f9c,0 3484da0,24070001 3484da4,24060010 3484da8,3c058041 -3484dac,24a5a070 -3484db0,c101b99 +3484dac,24a5a078 +3484db0,c101b9a 3484db4,2002025 3484db8,3c028041 -3484dbc,8c42b0e8 +3484dbc,8c42b0f0 3484dc0,18400024 3484dc4,3c118041 -3484dc8,26319f00 +3484dc8,26319f08 3484dcc,2413000a 3484dd0,9025 3484dd4,3c178011 3484dd8,36f7a5d0 3484ddc,3c028041 -3484de0,2442a070 +3484de0,2442a078 3484de4,afa20038 3484de8,3c148041 3484dec,92220001 @@ -5331,11 +5331,11 @@ ef4f9c,0 3484e28,3c03825 3484e2c,3025 3484e30,8fa50038 -3484e34,c101c01 +3484e34,c101c02 3484e38,2002025 3484e3c,26520001 3484e40,2631000c -3484e44,8e82b0e8 +3484e44,8e82b0f0 3484e48,242102a 3484e4c,1440ffe7 3484e50,26730011 @@ -5344,21 +5344,21 @@ ef4f9c,0 3484e5c,24070001 3484e60,2406000f 3484e64,3c058041 -3484e68,24a5a070 -3484e6c,c101b99 +3484e68,24a5a078 +3484e6c,c101b9a 3484e70,2002025 3484e74,3c028041 -3484e78,8c42b0e8 +3484e78,8c42b0f0 3484e7c,18400053 3484e80,3c148041 -3484e84,26949f00 +3484e84,26949f08 3484e88,2808825 3484e8c,2413000a 3484e90,9025 3484e94,3c1e8011 3484e98,37dea5d0 3484e9c,3c028041 -3484ea0,2442a070 +3484ea0,2442a078 3484ea4,afa2003c 3484ea8,3c178041 3484eac,92220001 @@ -5379,10 +5379,10 @@ ef4f9c,0 3484ee8,8fa70038 3484eec,3025 3484ef0,8fa5003c -3484ef4,c101c01 +3484ef4,c101c02 3484ef8,2002025 3484efc,26520001 -3484f00,8ee2b0e8 +3484f00,8ee2b0f0 3484f04,2631000c 3484f08,242182a 3484f0c,1460ffe7 @@ -5394,14 +5394,14 @@ ef4f9c,0 3484f28,8825 3484f2c,3c1e8041 3484f30,3c158041 -3484f34,26b5b1e8 +3484f34,26b5b1f0 3484f38,3c138041 -3484f3c,26739e64 +3484f3c,26739e68 3484f40,3c028041 -3484f44,24429e68 +3484f44,24429e6c 3484f48,afa20038 3484f4c,3c178041 -3484f50,8fc2b184 +3484f50,8fc2b18c 3484f54,5040000f 3484f58,92820000 3484f5c,92820001 @@ -5424,15 +5424,15 @@ ef4f9c,0 3484fa0,2602025 3484fa4,8fa40038 3484fa8,2403025 -3484fac,c10250c +3484fac,c10250d 3484fb0,2c02825 3484fb4,26310001 3484fb8,2694000c -3484fbc,8ee2b0e8 +3484fbc,8ee2b0f0 3484fc0,222102a 3484fc4,1440ffe2 3484fc8,26520011 -3484fcc,c102536 +3484fcc,c102537 3484fd0,2002025 3484fd4,8e020008 3484fd8,24430008 @@ -5484,11 +5484,11 @@ ef4f9c,0 34850b4,c44000d4 34850b8,3c028041 34850bc,3e00008 -34850c0,e440b0f0 +34850c0,e440b0f8 34850c4,27bdffe8 34850c8,afbf0014 34850cc,3c028041 -34850d0,90429fa8 +34850d0,90429fb0 34850d4,5040001b 34850d8,3c028041 34850dc,3c038011 @@ -5510,14 +5510,14 @@ ef4f9c,0 348511c,40f809 3485120,248484a0 3485124,3c028041 -3485128,90439fa8 +3485128,90439fb0 348512c,24020001 3485130,14620004 3485134,3c028041 3485138,3c028041 -348513c,a0409fa8 +348513c,a0409fb0 3485140,3c028041 -3485144,c44e9fa0 +3485144,c44e9fa8 3485148,44800000 3485150,46007032 3485158,45010010 @@ -5528,10 +5528,10 @@ ef4f9c,0 3485170,45010019 3485174,3c02801c 3485178,3c028041 -348517c,8c469e70 +348517c,8c469e74 3485180,3c028041 3485184,c101412 -3485188,c44cb0ec +3485188,c44cb0f4 348518c,3c02801c 3485190,344284a0 3485194,1000000f @@ -5539,13 +5539,13 @@ ef4f9c,0 348519c,344284a0 34851a0,c44c00d4 34851a4,3c028041 -34851a8,c44eb0f0 +34851a8,c44eb0f8 34851ac,460e6032 34851b4,45010008 34851b8,3c02801c 34851bc,3c028041 34851c0,c101412 -34851c4,8c469e74 +34851c4,8c469e78 34851c8,3c02801c 34851cc,344284a0 34851d0,e44000d4 @@ -5553,9 +5553,9 @@ ef4f9c,0 34851d8,344284a0 34851dc,c44000d4 34851e0,3c028041 -34851e4,e440b0ec +34851e4,e440b0f4 34851e8,3c028041 -34851ec,90429fa9 +34851ec,90429fb1 34851f0,24030001 34851f4,1443000f 34851f8,24030002 @@ -5563,7 +5563,7 @@ ef4f9c,0 3485200,344284a0 3485204,94420322 3485208,3c038041 -348520c,24639c5c +348520c,24639c60 3485210,431021 3485214,90420000 3485218,10400018 @@ -5579,7 +5579,7 @@ ef4f9c,0 3485240,344284a0 3485244,94420322 3485248,3c038041 -348524c,24639c5c +348524c,24639c60 3485250,431021 3485254,90420000 3485258,10400006 @@ -5589,9 +5589,9 @@ ef4f9c,0 3485268,2403001f 348526c,a4430322 3485270,3c028041 -3485274,a0409fa9 +3485274,a0409fb1 3485278,3c028041 -348527c,24429f9c +348527c,24429fa4 3485280,c4400008 3485284,3c038040 3485288,e4602760 @@ -5603,10 +5603,10 @@ ef4f9c,0 34852a0,3c028041 34852a4,2442ffff 34852a8,3c038041 -34852ac,c101db9 -34852b0,a0629fab +34852ac,c101dba +34852b0,a0629fb3 34852b4,3c028041 -34852b8,90429fac +34852b8,90429fb4 34852bc,1040000b 34852c0,3c028041 34852c4,3c02801c @@ -5619,7 +5619,7 @@ ef4f9c,0 34852e0,641824 34852e4,a4430020 34852e8,3c028041 -34852ec,90429fad +34852ec,90429fb5 34852f0,10400016 34852f4,8fbf0014 34852f8,3c02801c @@ -5717,7 +5717,7 @@ ef4f9c,0 348546c,a08825 3485470,afa7003c 3485474,8fb20040 -3485478,c101b99 +3485478,c101b9a 348547c,24070001 3485480,93a7003c 3485484,afb20018 @@ -5728,7 +5728,7 @@ ef4f9c,0 3485498,24e70037 348549c,3025 34854a0,2202825 -34854a4,c101c01 +34854a4,c101c02 34854a8,2002025 34854ac,8fbf002c 34854b0,8fb20028 @@ -5746,10 +5746,10 @@ ef4f9c,0 34854e0,24070001 34854e4,4825 34854e8,3c028041 -34854ec,24429d20 +34854ec,24429d24 34854f0,2408ffe0 34854f4,3c048041 -34854f8,24849d68 +34854f8,24849d6c 34854fc,90430000 3485500,1031824 3485504,14600005 @@ -5763,9 +5763,9 @@ ef4f9c,0 3485524,1444fff5 3485528,24a50001 348552c,3c028041 -3485530,ac49b1f8 +3485530,ac49b200 3485534,8e1100a4 -3485538,2442b1f8 +3485538,2442b200 348553c,3223003f 3485540,a0430004 3485544,9602009c @@ -5774,7 +5774,7 @@ ef4f9c,0 3485550,3042fffb 3485554,34420004 3485558,3c038041 -348555c,a462b1fe +348555c,a462b206 3485560,112c02 3485564,30a5007c 3485568,26030086 @@ -5792,7 +5792,7 @@ ef4f9c,0 3485598,5466fff7 348559c,90640000 34855a0,3c028041 -34855a4,a045b1fd +34855a4,a045b205 34855a8,9203007b 34855ac,2462fff9 34855b0,304200ff @@ -5803,7 +5803,7 @@ ef4f9c,0 34855c4,24030007 34855c8,24040001 34855cc,3c028041 -34855d0,2442b1f8 +34855d0,2442b200 34855d4,a0440008 34855d8,a0430009 34855dc,9203007d @@ -5816,7 +5816,7 @@ ef4f9c,0 34855f8,2403000a 34855fc,24040001 3485600,3c028041 -3485604,2442b1f8 +3485604,2442b200 3485608,a044000a 348560c,a043000b 3485610,86020ef6 @@ -5831,7 +5831,7 @@ ef4f9c,0 3485634,50400007 3485638,9203008b 348563c,3c038041 -3485640,24639ca0 +3485640,24639ca4 3485644,431021 3485648,90430000 348564c,1000000d @@ -5849,7 +5849,7 @@ ef4f9c,0 348567c,24040001 3485680,2025 3485684,3c028041 -3485688,2442b1f8 +3485688,2442b200 348568c,a043000d 3485690,a044000c 3485694,9203008a @@ -5861,21 +5861,21 @@ ef4f9c,0 34856ac,24030037 34856b0,2025 34856b4,3c028041 -34856b8,2442b1f8 +34856b8,2442b200 34856bc,a043000f 34856c0,a044000e 34856c4,9202003c 34856c8,10400005 34856cc,3c028041 34856d0,24030013 -34856d4,a043b209 +34856d4,a043b211 34856d8,10000004 34856dc,24030001 34856e0,24030012 -34856e4,a043b209 +34856e4,a043b211 34856e8,9203003a 34856ec,3c028041 -34856f0,a043b208 +34856f0,a043b210 34856f4,8e0200a0 34856f8,21182 34856fc,30420007 @@ -5891,7 +5891,7 @@ ef4f9c,0 3485724,24040001 3485728,24030050 348572c,3c028041 -3485730,2442b1f8 +3485730,2442b200 3485734,a0440012 3485738,a0430013 348573c,8e0200a0 @@ -5909,7 +5909,7 @@ ef4f9c,0 348576c,24040001 3485770,24030053 3485774,3c028041 -3485778,2442b1f8 +3485778,2442b200 348577c,a0440014 3485780,a0430015 3485784,8e0300a0 @@ -5919,7 +5919,7 @@ ef4f9c,0 3485794,86050034 3485798,3c048041 348579c,c1014de -34857a0,2484b213 +34857a0,2484b21b 34857a4,3c020080 34857a8,2221024 34857ac,10400002 @@ -5927,15 +5927,15 @@ ef4f9c,0 34857b4,860500d0 34857b8,3c048041 34857bc,c1014de -34857c0,2484b216 +34857c0,2484b21e 34857c4,860508c6 34857c8,58a00001 34857cc,2405ffff 34857d0,3c048041 34857d4,c1014de -34857d8,2484b219 +34857d8,2484b221 34857dc,3c128041 -34857e0,2652b1f8 +34857e0,2652b200 34857e4,9202003d 34857e8,a2420017 34857ec,8602002e @@ -5945,11 +5945,11 @@ ef4f9c,0 34857fc,52903 3485800,3c048041 3485804,c1014de -3485808,2484b210 +3485808,2484b218 348580c,86050022 3485810,3c048041 3485814,c1014de -3485818,2484b21c +3485818,2484b224 348581c,118982 3485820,32310fff 3485824,a6510028 @@ -6126,12 +6126,12 @@ ef4f9c,0 3485ad0,3c028041 3485ad4,afa20030 3485ad8,3c028041 -3485adc,2442b1f8 +3485adc,2442b200 3485ae0,afa2004c 3485ae4,3c178041 -3485ae8,26f79ea0 +3485ae8,26f79ea4 3485aec,3c158041 -3485af0,26b59e20 +3485af0,26b59e24 3485af4,8e240008 3485af8,24820008 3485afc,ae220008 @@ -6146,7 +6146,7 @@ ef4f9c,0 3485b20,431025 3485b24,ac820004 3485b28,8fa20030 -3485b2c,24539da0 +3485b2c,24539da4 3485b30,8fbe004c 3485b34,2670ff80 3485b38,8fd20000 @@ -6190,11 +6190,11 @@ ef4f9c,0 3485bd0,3c178041 3485bd4,3c1e8041 3485bd8,3c158041 -3485bdc,26b59e20 +3485bdc,26b59e24 3485be0,2416000c 3485be4,3c148041 3485be8,10000002 -3485bec,2694b20e +3485bec,2694b216 3485bf0,8fa50044 3485bf4,8e240008 3485bf8,24820008 @@ -6208,8 +6208,8 @@ ef4f9c,0 3485c18,31a00 3485c1c,431025 3485c20,ac820004 -3485c24,26f29d08 -3485c28,27d0b200 +3485c24,26f29d0c +3485c28,27d0b208 3485c2c,92020000 3485c30,5453000f 3485c34,26100002 @@ -6235,27 +6235,27 @@ ef4f9c,0 3485c84,1662ffda 3485c88,240300ff 3485c8c,3c028041 -3485c90,9456b220 +3485c90,9456b228 3485c94,24070001 3485c98,3025 3485c9c,3c058041 -3485ca0,24a5a030 -3485ca4,c101b99 +3485ca0,24a5a038 +3485ca4,c101b9a 3485ca8,2202025 3485cac,afa00038 3485cb0,afa00034 3485cb4,afa00030 3485cb8,b825 3485cbc,3c108041 -3485cc0,26109ccc +3485cc0,26109cd0 3485cc4,8fa20044 3485cc8,afa2003c 3485ccc,3c028041 -3485cd0,2442a030 +3485cd0,2442a038 3485cd4,afa20040 3485cd8,3c1e8041 3485cdc,10000005 -3485ce0,27de9d08 +3485ce0,27de9d0c 3485ce4,afb50038 3485ce8,afb40034 3485cec,afb30030 @@ -6310,20 +6310,20 @@ ef4f9c,0 3485db0,24e70037 3485db4,3025 3485db8,8fa50040 -3485dbc,c101c01 +3485dbc,c101c02 3485dc0,2202025 3485dc4,26100005 3485dc8,161effc6 3485dcc,16b042 3485dd0,3c108041 -3485dd4,2610b1f8 +3485dd4,2610b200 3485dd8,92020016 3485ddc,8fb50044 3485de0,2a09025 3485de4,21840 3485de8,621821 3485dec,3c028041 -3485df0,24429e2c +3485df0,24429e30 3485df4,621821 3485df8,90620000 3485dfc,21600 @@ -6341,7 +6341,7 @@ ef4f9c,0 3485e2c,ac730000 3485e30,ac620004 3485e34,3c028041 -3485e38,24429cb0 +3485e38,24429cb4 3485e3c,24030010 3485e40,afa30010 3485e44,90430005 @@ -6351,7 +6351,7 @@ ef4f9c,0 3485e54,73c00 3485e58,24060001 3485e5c,3c058041 -3485e60,24a5a020 +3485e60,24a5a028 3485e64,c101515 3485e68,2202025 3485e6c,2414ff00 @@ -6364,8 +6364,8 @@ ef4f9c,0 3485e88,24070001 3485e8c,2406000c 3485e90,3c058041 -3485e94,24a5a070 -3485e98,c101b99 +3485e94,24a5a078 +3485e98,c101b9a 3485e9c,2202025 3485ea0,92020017 3485ea4,1440000e @@ -6378,8 +6378,8 @@ ef4f9c,0 3485ec0,2407003c 3485ec4,3025 3485ec8,3c058041 -3485ecc,24a5a070 -3485ed0,c101c01 +3485ecc,24a5a078 +3485ed0,c101c02 3485ed4,2202025 3485ed8,10000014 3485edc,3c028041 @@ -6390,36 +6390,36 @@ ef4f9c,0 3485ef0,2407003a 3485ef4,3025 3485ef8,3c138041 -3485efc,2665a070 -3485f00,c101c01 +3485efc,2665a078 +3485f00,c101c02 3485f04,2202025 3485f08,afb00018 3485f0c,afb00014 3485f10,afb50010 3485f14,2407003e 3485f18,3025 -3485f1c,2665a070 -3485f20,c101c01 +3485f1c,2665a078 +3485f20,c101c02 3485f24,2202025 3485f28,3c028041 -3485f2c,9042b21e +3485f2c,9042b226 3485f30,2c42000a 3485f34,1040000b 3485f38,24070001 3485f3c,2402000a 3485f40,afa20010 3485f44,3c028041 -3485f48,8c479cc4 +3485f48,8c479cc8 3485f4c,24060001 3485f50,3c058041 -3485f54,24a5a000 +3485f54,24a5a008 3485f58,c101515 3485f5c,2202025 3485f60,24070001 3485f64,2406000b 3485f68,3c108041 -3485f6c,2605a070 -3485f70,c101b99 +3485f6c,2605a078 +3485f70,c101b9a 3485f74,2202025 3485f78,24020010 3485f7c,afa20018 @@ -6428,11 +6428,11 @@ ef4f9c,0 3485f88,afa20010 3485f8c,2407003c 3485f90,3025 -3485f94,2605a070 -3485f98,c101c01 +3485f94,2605a078 +3485f98,c101c02 3485f9c,2202025 3485fa0,3c028041 -3485fa4,9042b21b +3485fa4,9042b223 3485fa8,2c42000a 3485fac,1040001d 3485fb0,8e220008 @@ -6445,11 +6445,11 @@ ef4f9c,0 3485fcc,2439025 3485fd0,ac520004 3485fd4,3c038041 -3485fd8,9062b18c +3485fd8,9062b194 3485fdc,24440001 -3485fe0,a064b18c +3485fe0,a064b194 3485fe4,3c038041 -3485fe8,24639cb0 +3485fe8,24639cb4 3485fec,21082 3485ff0,24040010 3485ff4,afa40010 @@ -6460,7 +6460,7 @@ ef4f9c,0 3486008,73c00 348600c,3046000f 3486010,3c058041 -3486014,24a5a040 +3486014,24a5a048 3486018,c101515 348601c,2202025 3486020,8e220008 @@ -6472,21 +6472,21 @@ ef4f9c,0 3486038,2407000a 348603c,3025 3486040,3c058041 -3486044,24a5a010 -3486048,c101b99 +3486044,24a5a018 +3486048,c101b9a 348604c,2202025 3486050,8fa2004c 3486054,2453001b 3486058,3c168041 -348605c,26d6b210 +348605c,26d6b218 3486060,3c148041 -3486064,26949cb0 +3486064,26949cb4 3486068,26820019 348606c,afa20034 3486070,24170001 3486074,241e0008 3486078,3c028041 -348607c,2442a010 +348607c,2442a018 3486080,afa20038 3486084,afa00020 3486088,afa00024 @@ -6540,7 +6540,7 @@ ef4f9c,0 3486148,afa20010 348614c,2a73821 3486150,8fa50038 -3486154,c101c01 +3486154,c101c02 3486158,2202025 348615c,26100001 3486160,1613fff1 @@ -6585,7 +6585,7 @@ ef4f9c,0 34861fc,3c03de00 3486200,ac430000 3486204,3c038041 -3486208,2463a0b0 +3486208,2463a0b8 348620c,ac430004 3486210,8e0202c0 3486214,24430008 @@ -6610,12 +6610,12 @@ ef4f9c,0 3486260,2403ffff 3486264,ac430004 3486268,3c028041 -348626c,8c52b190 +348626c,8c52b198 3486270,24110054 3486274,3c178041 -3486278,26f79fb0 +3486278,26f79fb8 348627c,3c168041 -3486280,26d6b0f4 +3486280,26d6b0fc 3486284,24150018 3486288,241e000c 348628c,3242001f @@ -6629,7 +6629,7 @@ ef4f9c,0 34862ac,24070001 34862b0,90460001 34862b4,2602825 -34862b8,c101b99 +34862b8,c101b9a 34862bc,2802025 34862c0,afb50018 34862c4,afb50014 @@ -6637,7 +6637,7 @@ ef4f9c,0 34862cc,2203825 34862d0,3025 34862d4,2602825 -34862d8,c101c01 +34862d8,c101c02 34862dc,2802025 34862e0,26310020 34862e4,240200f4 @@ -6710,7 +6710,7 @@ ef4f9c,0 34863f0,a0401640 34863f4,3e00008 34863fc,3c028041 -3486400,9042b194 +3486400,9042b19c 3486404,1040000d 3486408,3c028011 348640c,3442a5d0 @@ -6719,7 +6719,7 @@ ef4f9c,0 3486418,14620008 3486420,27bdffe8 3486424,afbf0014 -3486428,c1024e5 +3486428,c1024e6 3486430,8fbf0014 3486434,3e00008 3486438,27bd0018 @@ -6727,17 +6727,17 @@ ef4f9c,0 3486444,27bdffe8 3486448,afbf0014 348644c,3c028041 -3486450,8c42b1c4 +3486450,8c42b1cc 3486454,218c0 3486458,3c048041 -348645c,2484b23c +348645c,2484b244 3486460,641821 3486464,8c630000 3486468,1060000c 348646c,24420001 3486470,220c0 3486474,3c038041 -3486478,2463b23c +3486478,2463b244 348647c,641821 3486480,402825 3486484,8c640000 @@ -6745,11 +6745,11 @@ ef4f9c,0 348648c,1480fffc 3486490,24630008 3486494,3c028041 -3486498,ac45b1c4 -348649c,c1026ed +3486498,ac45b1cc +348649c,c1026ee 34864a0,2404013c 34864a4,3c038041 -34864a8,ac62b1c0 +34864a8,ac62b1c8 34864ac,24030001 34864b0,ac430130 34864b4,8fbf0014 @@ -6824,14 +6824,14 @@ ef4f9c,0 34865c8,3e00008 34865cc,a0470003 34865d0,3c038041 -34865d4,8c67b1c4 +34865d4,8c67b1cc 34865d8,24e7ffff 34865dc,4e00021 34865e0,801025 34865e4,27bdfff8 34865e8,4825 34865ec,3c0a8041 -34865f0,254ab23c +34865f0,254ab244 34865f4,1273021 34865f8,61fc2 34865fc,661821 @@ -6888,40 +6888,40 @@ ef4f9c,0 34866cc,58202 34866d0,afa5001c 34866d4,321000ff -34866d8,c101ea5 +34866d8,c101ea6 34866dc,52402 -34866e0,c101e96 +34866e0,c101e97 34866e4,402025 34866e8,3c038041 34866ec,8fa40018 -34866f0,ac64b1b8 -34866f4,2463b1b8 +34866f0,ac64b1c0 +34866f4,2463b1c0 34866f8,8fa4001c 34866fc,ac640004 -3486700,3c038040 -3486704,90630024 -3486708,701826 -348670c,3182b -3486710,3c048041 -3486714,ac83b1b4 +3486700,10202b +3486704,3c038041 +3486708,ac64b1bc +348670c,3c038041 +3486710,ac62b1b8 +3486714,90440001 3486718,3c038041 -348671c,ac62b1b0 -3486720,90440001 +348671c,ac64b1b4 +3486720,94440002 3486724,3c038041 -3486728,ac64b1ac -348672c,94440002 +3486728,ac64b1b0 +348672c,94440004 3486730,3c038041 -3486734,ac64b1a8 -3486738,94440004 +3486734,ac64b1ac +3486738,80440006 348673c,3c038041 -3486740,ac64b1a4 -3486744,80440006 -3486748,3c038041 -348674c,ac64b1a0 -3486750,90420007 -3486754,30420001 -3486758,3c038041 -348675c,ac62b19c +3486740,ac64b1a8 +3486744,90420007 +3486748,30420001 +348674c,3c038041 +3486750,16000003 +3486754,ac62b1a4 +3486758,3c028040 +348675c,90500024 3486760,3c028040 3486764,a0500025 3486768,8fbf0014 @@ -6929,24 +6929,24 @@ ef4f9c,0 3486770,3e00008 3486774,27bd0018 3486778,3c028041 -348677c,ac40b1b8 -3486780,2442b1b8 +348677c,ac40b1c0 +3486780,2442b1c0 3486784,ac400004 3486788,3c028041 -348678c,ac40b1b4 +348678c,ac40b1bc 3486790,3c028041 -3486794,ac40b1b0 +3486794,ac40b1b8 3486798,3c028041 -348679c,ac40b1ac +348679c,ac40b1b4 34867a0,3c028041 -34867a4,ac40b1a8 +34867a4,ac40b1b0 34867a8,3c028041 -34867ac,ac40b1a4 +34867ac,ac40b1ac 34867b0,3c028041 -34867b4,ac40b1a0 +34867b4,ac40b1a8 34867b8,3c028041 34867bc,3e00008 -34867c0,ac40b19c +34867c0,ac40b1a4 34867c4,8c830000 34867c8,3c028040 34867cc,ac43002c @@ -6960,14 +6960,14 @@ ef4f9c,0 34867ec,afa40000 34867f0,afa50004 34867f4,3c028041 -34867f8,2442b224 +34867f8,2442b22c 34867fc,1825 3486800,24060003 3486804,8c450000 3486808,14a0000a 3486810,318c0 3486814,3c028041 -3486818,2442b224 +3486818,2442b22c 348681c,621821 3486820,8fa20000 3486824,ac620000 @@ -7019,10 +7019,10 @@ ef4f9c,0 34868e4,3e00008 34868e8,27bd0020 34868ec,3c038041 -34868f0,2462b224 +34868f0,2462b22c 34868f4,8c450008 34868f8,8c44000c -34868fc,ac65b224 +34868fc,ac65b22c 3486900,ac440004 3486904,8c440010 3486908,8c430014 @@ -7056,3228 +7056,3229 @@ ef4f9c,0 3486978,ac431d38 348697c,3e00008 3486980,27bd0008 -3486984,27bdffe8 -3486988,afbf0014 -348698c,afb00010 +3486984,27bdffe0 +3486988,afbf001c +348698c,afb00018 3486990,3c028041 -3486994,8c50b224 -3486998,2442b224 -348699c,94430004 -34869a0,2402007c -34869a4,1462000e -34869a8,8fbf0014 -34869ac,3c028040 -34869b0,90430024 -34869b4,3c028041 -34869b8,9042b22a -34869bc,54620009 -34869c0,8fb00010 -34869c4,c101db9 -34869cc,c101a3b -34869d4,c101a48 -34869d8,2002025 -34869dc,8fbf0014 -34869e0,8fb00010 -34869e4,3e00008 -34869e8,27bd0018 -34869ec,27bdffe8 -34869f0,afbf0014 -34869f4,afb00010 -34869f8,3c028041 -34869fc,8c50b1b8 -3486a00,1200000e -3486a04,8fbf0014 -3486a08,c1019f1 -3486a0c,2444b1b8 -3486a10,3c028041 -3486a14,8c42b224 -3486a18,14500003 -3486a20,c101a3b -3486a28,c101a48 -3486a2c,2002025 -3486a30,c1019de -3486a38,8fbf0014 -3486a3c,8fb00010 -3486a40,3e00008 -3486a44,27bd0018 -3486a48,27bdffe0 -3486a4c,afbf001c -3486a50,3c028041 -3486a54,8c43b224 -3486a58,2442b224 -3486a5c,8c420004 -3486a60,afa30010 -3486a64,1060000d -3486a68,afa20014 -3486a6c,602025 -3486a70,c1019af -3486a74,402825 -3486a78,3c02801d -3486a7c,3442aa30 +3486994,8c50b22c +3486998,2442b22c +348699c,8c420004 +34869a0,afa20010 +34869a4,2403ff00 +34869a8,431024 +34869ac,3c03007c +34869b0,14430008 +34869b4,8fbf001c +34869b8,c101dba +34869c0,c101a3b +34869c8,c101a48 +34869cc,2002025 +34869d0,8fbf001c +34869d4,8fb00018 +34869d8,3e00008 +34869dc,27bd0020 +34869e0,27bdffe8 +34869e4,afbf0014 +34869e8,afb00010 +34869ec,3c028041 +34869f0,8c50b1c0 +34869f4,1200000e +34869f8,8fbf0014 +34869fc,c1019f1 +3486a00,2444b1c0 +3486a04,3c028041 +3486a08,8c42b22c +3486a0c,14500003 +3486a14,c101a3b +3486a1c,c101a48 +3486a20,2002025 +3486a24,c1019de +3486a2c,8fbf0014 +3486a30,8fb00010 +3486a34,3e00008 +3486a38,27bd0018 +3486a3c,27bdffe0 +3486a40,afbf001c +3486a44,3c028041 +3486a48,8c43b22c +3486a4c,2442b22c +3486a50,8c420004 +3486a54,afa30010 +3486a58,1060000d +3486a5c,afa20014 +3486a60,602025 +3486a64,c1019af +3486a68,402825 +3486a6c,3c02801d +3486a70,3442aa30 +3486a74,3c038041 +3486a78,8c63b1c8 +3486a7c,ac430428 3486a80,3c038041 -3486a84,8c63b1c0 -3486a88,ac430428 -3486a8c,3c038041 -3486a90,8c63b1b0 -3486a94,80630000 -3486a98,a0430424 -3486a9c,8fbf001c -3486aa0,3e00008 -3486aa4,27bd0020 -3486aa8,27bdffe8 -3486aac,afbf0014 -3486ab0,c101a13 -3486ab8,3c02801d -3486abc,3442aa30 -3486ac0,8c42066c -3486ac4,3c03fcac -3486ac8,24632485 -3486acc,431024 -3486ad0,14400033 -3486ad4,3c028041 -3486ad8,3c02801d -3486adc,3442aa30 -3486ae0,94420088 -3486ae4,30420001 -3486ae8,1040002a -3486aec,1025 -3486af0,3c02801d -3486af4,3442aa30 -3486af8,8c420670 -3486afc,3c03000c -3486b00,431024 -3486b04,14400023 -3486b08,1025 -3486b0c,3c02800e -3486b10,3442f1b0 -3486b14,8c420000 -3486b18,30420020 -3486b1c,1440001d -3486b20,1025 -3486b24,3c02801c -3486b28,344284a0 -3486b2c,8c420794 -3486b30,14400018 -3486b34,1025 -3486b38,3c028041 -3486b3c,9042b198 -3486b40,24420001 -3486b44,304200ff -3486b48,2c430002 -3486b4c,14600012 -3486b50,3c038041 -3486b54,3c028041 -3486b58,c101a61 -3486b5c,a040b198 -3486b60,c101db5 -3486b68,10400005 -3486b70,c101dbe -3486b78,1000000b -3486b7c,8fbf0014 -3486b80,c101a92 -3486b88,10000007 -3486b8c,8fbf0014 -3486b90,1025 -3486b94,3c038041 -3486b98,10000002 -3486b9c,a062b198 -3486ba0,a040b198 -3486ba4,8fbf0014 -3486ba8,3e00008 -3486bac,27bd0018 -3486bb0,27bdffd0 -3486bb4,afbf002c -3486bb8,afb40028 -3486bbc,afb30024 -3486bc0,afb20020 -3486bc4,afb1001c -3486bc8,afb00018 -3486bcc,a09025 -3486bd0,10800013 -3486bd4,c08825 -3486bd8,10c00011 -3486bdc,808025 -3486be0,4c10004 -3486be4,c03825 -3486be8,63823 -3486bec,73e00 -3486bf0,73e03 -3486bf4,30e700ff -3486bf8,3c02801c -3486bfc,344284a0 -3486c00,904600a5 -3486c04,2002825 -3486c08,c10199c -3486c0c,27a40010 -3486c10,97b30014 -3486c14,8fa20010 -3486c18,14400005 -3486c1c,93b40016 -3486c20,c1019de -3486c28,1000001b -3486c2c,2201025 -3486c30,8fa40010 -3486c34,c1019af -3486c38,8fa50014 -3486c3c,3c028041 -3486c40,8c42b1b0 -3486c44,86040000 -3486c48,2403000a -3486c4c,1483000d -3486c50,80420000 -3486c54,2403007c -3486c58,56630006 -3486c5c,9603001c -3486c60,3c038040 -3486c64,90630024 -3486c68,50740001 -3486c6c,2402007c -3486c70,9603001c -3486c74,3063f01f -3486c78,22140 -3486c7c,641825 -3486c80,a603001c -3486c84,6230005 -3486c88,a2420424 -3486c8c,21023 -3486c90,21600 -3486c94,21603 -3486c98,a2420424 -3486c9c,8fbf002c -3486ca0,8fb40028 -3486ca4,8fb30024 -3486ca8,8fb20020 -3486cac,8fb1001c -3486cb0,8fb00018 -3486cb4,3e00008 -3486cb8,27bd0030 -3486cbc,27bdffd8 -3486cc0,afbf0024 -3486cc4,afb20020 -3486cc8,afb1001c -3486ccc,afb00018 -3486cd0,808825 -3486cd4,3825 -3486cd8,3025 -3486cdc,802825 -3486ce0,c10199c -3486ce4,27a40010 -3486ce8,8fa20010 -3486cec,54400005 -3486cf0,97a40014 -3486cf4,3c028040 -3486cf8,90520024 -3486cfc,10000002 -3486d00,2404005b -3486d04,93b20016 -3486d08,c101ea5 -3486d10,c101e96 -3486d14,402025 -3486d18,408025 -3486d1c,ae200134 -3486d20,3c028040 -3486d24,a0520025 -3486d28,3025 -3486d2c,96050002 -3486d30,3c04801c -3486d34,3c02800d -3486d38,3442ce14 -3486d3c,40f809 -3486d40,348484a0 -3486d44,3c028040 -3486d48,90420024 -3486d4c,50520005 -3486d50,92050001 -3486d54,c1019f1 -3486d58,27a40010 -3486d5c,10000009 -3486d60,8fbf0024 -3486d64,3c04801c -3486d68,3c028006 -3486d6c,3442fdcc -3486d70,40f809 -3486d74,348484a0 -3486d78,c101ebd -3486d7c,2002025 -3486d80,8fbf0024 -3486d84,8fb20020 -3486d88,8fb1001c -3486d8c,8fb00018 -3486d90,3e00008 -3486d94,27bd0028 -3486d98,27bdffe8 -3486d9c,afbf0014 -3486da0,afb00010 -3486da4,3c028011 -3486da8,3442a5d0 -3486dac,94500eec -3486db0,32100002 -3486db4,1600000c -3486db8,3c028040 -3486dbc,90420cd5 -3486dc0,50400004 -3486dc4,3c028011 -3486dc8,c101a2b -3486dcc,24040002 -3486dd0,3c028011 -3486dd4,3442a5d0 -3486dd8,94430eec -3486ddc,34630002 -3486de0,a4430eec -3486de4,3c028040 -3486de8,90430cd5 -3486dec,14600002 -3486df0,24020001 -3486df4,10102b -3486df8,8fbf0014 -3486dfc,8fb00010 -3486e00,3e00008 -3486e04,27bd0018 -3486e08,94830004 -3486e0c,94820006 -3486e10,620018 -3486e14,9082000c -3486e18,1812 -3486e24,620018 -3486e28,1012 -3486e2c,3e00008 -3486e34,27bdffe8 -3486e38,afbf0014 -3486e3c,afb00010 -3486e40,c101b82 -3486e44,808025 -3486e48,96030008 -3486e4c,620018 -3486e50,1012 -3486e54,8fbf0014 -3486e58,8fb00010 -3486e5c,3e00008 -3486e60,27bd0018 -3486e64,27bdff98 -3486e68,afbf0064 -3486e6c,afb60060 -3486e70,afb5005c -3486e74,afb40058 -3486e78,afb30054 -3486e7c,afb20050 -3486e80,afb1004c -3486e84,afb00048 -3486e88,808025 -3486e8c,a0a025 -3486e90,c0a825 -3486e94,94b10004 -3486e98,94a20006 -3486e9c,470018 -3486ea0,9012 -3486ea4,90b6000b -3486ea8,90b3000a -3486eac,139d40 -3486eb0,3c0200e0 -3486eb4,2629824 -3486eb8,1614c0 -3486ebc,3c030018 -3486ec0,431024 -3486ec4,2629825 -3486ec8,2622ffff -3486ecc,30420fff -3486ed0,531025 -3486ed4,3c03fd00 -3486ed8,431025 -3486edc,afa20010 -3486ee0,c101b82 -3486ee4,a02025 -3486ee8,550018 -3486eec,8e820000 -3486ef0,1812 -3486ef4,431021 -3486ef8,afa20014 -3486efc,2ec30002 -3486f00,10600003 -3486f04,24020010 -3486f08,24020004 -3486f0c,2c21004 -3486f10,510018 -3486f14,1812 -3486f18,2463003f -3486f1c,317c3 -3486f20,3042003f -3486f24,431021 -3486f28,210c0 -3486f2c,3c030003 -3486f30,3463fe00 -3486f34,431024 -3486f38,531025 -3486f3c,3c03f500 -3486f40,431025 -3486f44,afa20018 -3486f48,3c040700 -3486f4c,afa4001c -3486f50,3c03e600 -3486f54,afa30020 -3486f58,afa00024 -3486f5c,3c03f400 -3486f60,afa30028 -3486f64,2623ffff -3486f68,31b80 -3486f6c,3c0500ff -3486f70,34a5f000 -3486f74,651824 -3486f78,2652ffff -3486f7c,129080 -3486f80,32520ffc -3486f84,721825 -3486f88,642025 -3486f8c,afa4002c -3486f90,3c04e700 -3486f94,afa40030 -3486f98,afa00034 -3486f9c,afa20038 -3486fa0,afa0003c -3486fa4,3c02f200 -3486fa8,afa20040 -3486fac,afa30044 -3486fb0,27a20010 -3486fb4,27a60048 -3486fb8,8e030008 -3486fbc,24640008 -3486fc0,ae040008 -3486fc4,8c450004 -3486fc8,8c440000 -3486fcc,ac650004 -3486fd0,24420008 -3486fd4,1446fff8 -3486fd8,ac640000 -3486fdc,8fbf0064 -3486fe0,8fb60060 -3486fe4,8fb5005c -3486fe8,8fb40058 -3486fec,8fb30054 -3486ff0,8fb20050 -3486ff4,8fb1004c -3486ff8,8fb00048 -3486ffc,3e00008 -3487000,27bd0068 -3487004,27bdffe0 -3487008,8fa80030 -348700c,8fa20034 -3487010,8faa0038 -3487014,94a30004 -3487018,31a80 -348701c,14400002 -3487020,62001a -3487024,7000d -3487028,4812 -348702c,94a30006 -3487030,471021 -3487034,21380 -3487038,3c0b00ff -348703c,356bf000 -3487040,4b1024 -3487044,1482821 -3487048,52880 -348704c,30a50fff -3487050,451025 -3487054,3c05e400 -3487058,451025 -348705c,afa20000 -3487060,73b80 -3487064,eb3824 -3487068,84080 -348706c,31080fff -3487070,e83825 -3487074,afa70004 -3487078,3c02e100 -348707c,afa20008 -3487080,660018 -3487084,1012 -3487088,21140 -348708c,3042ffff -3487090,afa2000c -3487094,3c02f100 -3487098,afa20010 -348709c,31a80 -34870a0,15400002 -34870a4,6a001a -34870a8,7000d -34870ac,1012 -34870b0,3042ffff -34870b4,94c00 -34870b8,491025 -34870bc,afa20014 -34870c0,afbd0018 -34870c4,27a50018 -34870c8,8c820008 -34870cc,24430008 -34870d0,ac830008 -34870d4,8fa30018 -34870d8,8c670004 -34870dc,8c660000 -34870e0,ac470004 -34870e4,ac460000 -34870e8,24620008 -34870ec,1445fff6 -34870f0,afa20018 -34870f4,3e00008 -34870f8,27bd0020 -34870fc,27bdffa0 -3487100,afbf005c -3487104,afb10058 -3487108,afb00054 -348710c,afa00010 -3487110,3c0201a0 -3487114,24422000 -3487118,afa20014 -348711c,3c110003 -3487120,362295c0 -3487124,afa20018 -3487128,c1026fc -348712c,27a40010 -3487130,afa0001c -3487134,3c020084 -3487138,24426000 -348713c,afa20020 -3487140,3402b400 -3487144,afa20024 -3487148,c1026fc -348714c,27a4001c -3487150,afa00028 -3487154,3c02007b -3487158,3442d000 -348715c,afa2002c -3487160,3c100008 -3487164,361088a0 -3487168,afb00030 -348716c,c1026fc -3487170,27a40028 -3487174,afa00034 -3487178,3c0201a3 -348717c,3442c000 -3487180,afa20038 -3487184,24023b00 -3487188,afa2003c -348718c,c1026fc -3487190,27a40034 -3487194,afa00040 -3487198,3c020085 -348719c,3442e000 -34871a0,afa20044 -34871a4,24021d80 -34871a8,afa20048 -34871ac,c1026fc -34871b0,27a40040 -34871b4,8fa20010 -34871b8,2631a300 -34871bc,518821 -34871c0,3c038041 -34871c4,ac71a0a0 -34871c8,24422980 -34871cc,3c038041 -34871d0,ac62a090 -34871d4,8fa20028 -34871d8,3c038041 -34871dc,ac62a080 -34871e0,3c038041 -34871e4,8fa4001c -34871e8,ac64a070 -34871ec,3c048041 -34871f0,3c038041 -34871f4,2463d748 -34871f8,ac83a050 -34871fc,3c048041 -3487200,3c038041 -3487204,2463df48 -3487208,ac83a040 -348720c,2610f7a0 -3487210,501021 -3487214,3c038041 -3487218,ac62a030 -348721c,8fa20034 -3487220,24441e00 -3487224,3c038041 -3487228,ac64a020 -348722c,244435c0 -3487230,3c038041 -3487234,ac64a010 -3487238,8fa30040 -348723c,24631980 -3487240,3c048041 -3487244,ac83a000 -3487248,3c038041 -348724c,ac629ff0 -3487250,3c118041 -3487254,c101b8d -3487258,2624a060 -348725c,408025 -3487260,c1026ed -3487264,402025 -3487268,104fc2 -348726c,1304821 -3487270,2a100002 -3487274,16000018 -3487278,ae22a060 -348727c,94843 -3487280,3c038041 -3487284,2463c280 -3487288,2025 -348728c,3025 -3487290,2204025 -3487294,2407fff0 -3487298,8d05a060 -348729c,a42821 -34872a0,90620000 -34872a4,21102 -34872a8,471025 -34872ac,a0a20000 -34872b0,8d02a060 -34872b4,441021 -34872b8,90650000 -34872bc,a72825 -34872c0,a0450001 -34872c4,24c60001 -34872c8,24630001 -34872cc,c9102a -34872d0,1440fff1 -34872d4,24840002 -34872d8,8fbf005c -34872dc,8fb10058 -34872e0,8fb00054 -34872e4,3e00008 -34872e8,27bd0060 -34872ec,3c038040 -34872f0,9462084e -34872f4,2463084e -34872f8,94640002 -34872fc,94630004 -3487300,3c058041 -3487304,8ca5b168 -3487308,a4a20000 -348730c,a4a40002 -3487310,a4a30004 -3487314,3c058041 -3487318,8ca6b164 -348731c,a4c20000 -3487320,8ca5b164 -3487324,a4a40004 -3487328,a4a30008 -348732c,240500ff -3487330,1445000a -3487334,3c058041 -3487338,24050046 -348733c,14850007 -3487340,3c058041 -3487344,24050032 -3487348,14650004 -348734c,3c058041 -3487350,1825 -3487354,2025 -3487358,240200c8 -348735c,8ca5b160 -3487360,a4a20000 -3487364,a4a40002 -3487368,a4a30004 -348736c,3c058041 -3487370,8ca5b15c -3487374,a4a20000 -3487378,a4a40002 -348737c,a4a30004 -3487380,3c028041 -3487384,8c43b158 -3487388,3c028040 -348738c,94450854 -3487390,24420854 -3487394,94440002 -3487398,94420004 -348739c,a4650000 -34873a0,a4640002 -34873a4,a4620004 -34873a8,3c028041 -34873ac,8c43b154 -34873b0,3c028040 -34873b4,9445085a -34873b8,2442085a -34873bc,94440002 -34873c0,94420004 -34873c4,a4650000 -34873c8,a4640002 -34873cc,a4620004 -34873d0,3c028041 -34873d4,8c43b150 -34873d8,3c028040 -34873dc,94450860 -34873e0,24420860 -34873e4,94440002 -34873e8,94420004 -34873ec,a4650000 -34873f0,a4640002 -34873f4,a4620004 -34873f8,3c028041 -34873fc,8c42b14c -3487400,3c068040 -3487404,94c30872 -3487408,a4430000 -348740c,3c028041 -3487410,8c43b148 -3487414,24c20872 -3487418,94440002 -348741c,a4640000 -3487420,3c038041 -3487424,8c63b144 -3487428,94440004 -348742c,a4640000 -3487430,3c038041 -3487434,8c63b140 -3487438,3c058040 -348743c,94a40878 -3487440,a4640000 -3487444,3c038041 -3487448,8c64b13c -348744c,24a30878 -3487450,94670002 -3487454,a4870000 -3487458,3c048041 -348745c,8c84b138 -3487460,94670004 -3487464,a4870000 -3487468,3c048041 -348746c,8c84b134 -3487470,94c80872 -3487474,94470002 -3487478,94460004 -348747c,a4880000 -3487480,a4870002 -3487484,a4860004 -3487488,3c048041 -348748c,8c84b124 -3487490,94a60878 -3487494,94650002 -3487498,94630004 -348749c,a4860000 -34874a0,a4850002 -34874a4,a4830004 -34874a8,94420002 -34874ac,3043ffff -34874b0,2c6300ce -34874b4,50600001 -34874b8,240200cd -34874bc,24420032 -34874c0,3047ffff -34874c4,3c028040 -34874c8,94420876 -34874cc,3043ffff -34874d0,2c6300ce -34874d4,50600001 -34874d8,240200cd -34874dc,24420032 -34874e0,3046ffff -34874e4,3c028040 -34874e8,94420878 -34874ec,3043ffff -34874f0,2c6300ce -34874f4,50600001 -34874f8,240200cd -34874fc,24420032 -3487500,3044ffff -3487504,3c028040 -3487508,9442087a -348750c,3043ffff -3487510,2c6300ce -3487514,50600001 -3487518,240200cd -348751c,24420032 -3487520,3043ffff -3487524,3c028040 -3487528,9442087c -348752c,3045ffff -3487530,2ca500ce -3487534,50a00001 -3487538,240200cd -348753c,24420032 -3487540,3c058041 -3487544,8ca8b130 -3487548,3c058040 -348754c,94a50872 -3487550,30a9ffff -3487554,2d2900ce -3487558,15200002 -348755c,3042ffff -3487560,240500cd -3487564,24a50032 -3487568,a5050000 -348756c,a5070002 -3487570,a5060004 -3487574,3c058041 -3487578,8ca5b120 -348757c,a4a40000 -3487580,a4a30002 -3487584,a4a20004 -3487588,3c028041 -348758c,8c43b128 -3487590,3c028040 -3487594,94450872 -3487598,24420872 -348759c,94440002 -34875a0,94420004 -34875a4,a4650000 -34875a8,a4640002 -34875ac,a4620004 -34875b0,3c028041 -34875b4,8c43b118 -34875b8,3c028040 -34875bc,94450878 -34875c0,24420878 -34875c4,94440002 -34875c8,94420004 -34875cc,a4650000 -34875d0,a4640002 -34875d4,a4620004 -34875d8,3c028041 -34875dc,8c43b114 -34875e0,3c028040 -34875e4,94460866 -34875e8,24440866 -34875ec,94850002 -34875f0,94840004 -34875f4,a4660000 -34875f8,a4650002 -34875fc,a4640004 -3487600,94420866 -3487604,3043ffff -3487608,2c6300ce -348760c,50600001 -3487610,240200cd -3487614,24420032 -3487618,3044ffff -348761c,3c028040 -3487620,94420868 -3487624,3043ffff -3487628,2c6300ce -348762c,50600001 -3487630,240200cd -3487634,24420032 -3487638,3043ffff -348763c,3c028040 -3487640,9442086a -3487644,3045ffff -3487648,2ca500ce -348764c,50a00001 -3487650,240200cd -3487654,24420032 -3487658,3042ffff -348765c,3c058041 -3487660,8ca5b110 -3487664,a4a40000 -3487668,a4a30002 -348766c,a4a20004 -3487670,3c058041 -3487674,8ca5b108 -3487678,a4a40000 -348767c,a4a30002 -3487680,3e00008 -3487684,a4a20004 -3487688,3c028011 -348768c,3442a5d0 -3487690,8c4200a0 -3487694,21302 -3487698,30420003 -348769c,21840 -34876a0,621821 -34876a4,3c028041 -34876a8,24429e2c -34876ac,621821 -34876b0,90640000 -34876b4,42600 -34876b8,90620001 -34876bc,21400 -34876c0,822021 -34876c4,90620002 -34876c8,21200 -34876cc,3e00008 -34876d0,821021 -34876d4,3c028041 -34876d8,9042b1c8 -34876dc,3e00008 -34876e0,2102b -34876e4,3c038041 -34876e8,9062b1c8 -34876ec,24420001 -34876f0,3e00008 -34876f4,a062b1c8 -34876f8,3c028041 -34876fc,9042b1c8 -3487700,1040001b -3487704,2442ffff -3487708,27bdffd8 -348770c,afbf0024 -3487710,afb10020 -3487714,afb0001c -3487718,3c038041 -348771c,a062b1c8 -3487720,3c108038 -3487724,3610e578 -3487728,24050014 -348772c,3c11801d -3487730,200f809 -3487734,3624aa30 -3487738,24020014 -348773c,afa20014 -3487740,afa00010 -3487744,26100130 -3487748,3825 -348774c,24060003 -3487750,3625aa30 -3487754,200f809 -3487758,262484a0 -348775c,8fbf0024 -3487760,8fb10020 -3487764,8fb0001c -3487768,3e00008 -348776c,27bd0028 -3487770,3e00008 -3487778,3e00008 -3487780,24020140 -3487784,3e00008 -3487788,a4821424 -348778c,27bdffe0 -3487790,afbf001c -3487794,afb10018 -3487798,afb00014 -348779c,808025 -34877a0,8c8208c4 -34877a4,24420001 -34877a8,c10258b -34877ac,ac8208c4 -34877b0,3c028041 -34877b4,9442b17a -34877b8,8e0308c4 -34877bc,1462001e -34877c0,8fbf001c -34877c4,920200b2 -34877c8,34420001 -34877cc,a20200b2 -34877d0,3c04801c -34877d4,348484a0 -34877d8,3c110001 -34877dc,918821 -34877e0,86221e1a -34877e4,ae020000 -34877e8,948200a4 -34877ec,a6020066 -34877f0,3c108009 -34877f4,3602d894 -34877f8,40f809 -34877fc,261005d4 -3487800,3c04a34b -3487804,200f809 -3487808,3484e820 -348780c,3c028011 -3487810,3442a5d0 -3487814,2403fff8 -3487818,a4431412 -348781c,240200a0 -3487820,a6221e1a -3487824,24020014 -3487828,a2221e15 -348782c,24020001 -3487830,a2221e5e -3487834,8fbf001c -3487838,8fb10018 -348783c,8fb00014 -3487840,3e00008 -3487844,27bd0020 -3487848,8c8200a0 -348784c,34423000 -3487850,ac8200a0 -3487854,3c028041 -3487858,9042b1cb -348785c,304200ff -3487860,10400005 -3487864,52840 -3487868,3c028010 -348786c,451021 -3487870,94428cec -3487874,a4820034 -3487878,3e00008 -3487880,24020001 -3487884,3e00008 -3487888,a082003e -348788c,24020012 -3487890,2406ffff -3487894,24070016 -3487898,821821 -348789c,80630074 -34878a0,54660004 -34878a4,24420001 -34878a8,822021 -34878ac,3e00008 -34878b0,a0850074 -34878b4,1447fff9 -34878b8,821821 -34878bc,3e00008 -34878c4,862021 -34878c8,908200a8 -34878cc,a22825 -34878d0,3e00008 -34878d4,a08500a8 -34878d8,852021 -34878dc,908200bc -34878e0,21e00 -34878e4,31e03 -34878e8,4620001 -34878ec,1025 -34878f0,24420001 -34878f4,3e00008 -34878f8,a08200bc -34878fc,24020001 -3487900,a082003d -3487904,24020014 -3487908,a08200cf -348790c,24020140 -3487910,3e00008 -3487914,a4821424 -3487918,24020001 -348791c,a0820032 -3487920,a082003a -3487924,24020030 -3487928,a48213f4 -348792c,3e00008 -3487930,a0820033 -3487934,24020002 -3487938,a0820032 -348793c,24020001 -3487940,a082003a -3487944,a082003c -3487948,24020060 -348794c,a48213f4 -3487950,3e00008 -3487954,a0820033 -3487958,24020007 -348795c,3e00008 -3487960,a082007b -3487964,24020001 -3487968,a21004 -348796c,8c8500a4 -3487970,a22825 -3487974,3e00008 -3487978,ac8500a4 -348797c,27bdffe8 -3487980,afbf0014 -3487984,c101db9 -348798c,8fbf0014 -3487990,3e00008 -3487994,27bd0018 -3487998,24020010 -348799c,a0820082 -34879a0,9082009a -34879a4,2442000a -34879a8,3e00008 -34879ac,a082009a -34879b0,3c028041 -34879b4,9042b1cb -34879b8,304200ff -34879bc,10400005 -34879c0,52840 -34879c4,3c028010 -34879c8,451021 -34879cc,94428cec -34879d0,a4820034 -34879d4,3e00008 -34879dc,3c028041 -34879e0,9042b1ca -34879e4,1040000c -34879e8,3c028041 -34879ec,94820f06 -34879f0,34420040 -34879f4,a4820f06 -34879f8,3c028041 -34879fc,9042b1c9 -3487a00,54400009 -3487a04,94820f06 -3487a08,94820ef4 -3487a0c,3042fb87 -3487a10,3e00008 -3487a14,a4820ef4 -3487a18,9042b1c9 -3487a1c,1040000c -3487a24,94820f06 -3487a28,34420080 -3487a2c,a4820f06 -3487a30,94820ef6 -3487a34,24038f00 -3487a38,431025 -3487a3c,a4820ef6 -3487a40,94820ee4 -3487a44,2403f000 -3487a48,431025 -3487a4c,a4820ee4 -3487a50,3e00008 -3487a58,2c8200cd -3487a5c,1040000b -3487a60,41880 -3487a64,641021 -3487a68,21080 -3487a6c,3c058041 -3487a70,24a5a0d8 -3487a74,a21021 -3487a78,80430000 -3487a7c,3182b -3487a80,31823 -3487a84,3e00008 -3487a88,431024 -3487a8c,3e00008 -3487a90,1025 -3487a94,27bdffe0 -3487a98,afbf001c -3487a9c,afb20018 -3487aa0,afb10014 -3487aa4,afb00010 -3487aa8,808025 -3487aac,3c128011 -3487ab0,3652a5d0 -3487ab4,c101e96 -3487ab8,2002025 -3487abc,2008825 -3487ac0,8c420008 -3487ac4,2002825 -3487ac8,40f809 -3487acc,2402025 -3487ad0,1622fff8 -3487ad4,408025 -3487ad8,2201025 -3487adc,8fbf001c -3487ae0,8fb20018 -3487ae4,8fb10014 -3487ae8,8fb00010 -3487aec,3e00008 -3487af0,27bd0020 -3487af4,27bdffe8 -3487af8,afbf0014 -3487afc,8c82000c -3487b00,84860012 -3487b04,84850010 -3487b08,3c048011 -3487b0c,40f809 -3487b10,3484a5d0 -3487b14,8fbf0014 -3487b18,3e00008 -3487b1c,27bd0018 -3487b20,3e00008 -3487b24,a01025 -3487b28,8082007d -3487b2c,21027 -3487b30,2102b -3487b34,3e00008 -3487b38,24420008 -3487b3c,8c8200a0 -3487b40,21182 -3487b44,30420007 -3487b48,10400005 -3487b50,38420001 -3487b54,2102b -3487b58,3e00008 -3487b5c,24420035 -3487b60,3e00008 -3487b64,24020054 -3487b68,8c8200a0 -3487b6c,210c2 -3487b70,30420007 -3487b74,10400005 -3487b7c,38420001 -3487b80,2102b -3487b84,3e00008 -3487b88,24420033 -3487b8c,3e00008 -3487b90,24020032 -3487b94,8c8200a0 -3487b98,30420007 -3487b9c,10400005 -3487ba4,38420001 -3487ba8,2102b -3487bac,3e00008 -3487bb0,24420030 -3487bb4,3e00008 -3487bb8,24020004 -3487bbc,8c8300a0 -3487bc0,31b82 -3487bc4,30630007 -3487bc8,10600005 -3487bcc,24040001 -3487bd0,14640004 -3487bd4,2402007b -3487bd8,3e00008 -3487bdc,24020060 -3487be0,24020005 -3487be4,3e00008 -3487bec,8c8300a0 -3487bf0,31b02 -3487bf4,30630003 -3487bf8,10600005 -3487bfc,24040001 -3487c00,14640004 -3487c04,240200c7 -3487c08,3e00008 -3487c0c,24020046 -3487c10,24020045 -3487c14,3e00008 -3487c1c,8c8200a0 -3487c20,21242 -3487c24,30420007 -3487c28,2102b -3487c2c,3e00008 -3487c30,24420037 -3487c34,8c8200a0 -3487c38,21502 -3487c3c,30420007 -3487c40,2c420002 -3487c44,2c420001 -3487c48,3e00008 -3487c4c,24420079 -3487c50,8c8200a0 -3487c54,21442 -3487c58,30420007 -3487c5c,2c420002 -3487c60,2c420001 -3487c64,3e00008 -3487c68,24420077 -3487c6c,9082003a -3487c70,2102b -3487c74,3e00008 -3487c78,244200b9 -3487c7c,8083007c -3487c80,2402ffff -3487c84,50620007 -3487c88,2402006b -3487c8c,80830094 -3487c90,28630006 -3487c94,10600003 -3487c98,2402006a -3487c9c,3e00008 -3487ca0,24020003 -3487ca4,3e00008 -3487cac,8083007b -3487cb0,2402ffff -3487cb4,10620003 -3487cbc,3e00008 -3487cc0,2402000c -3487cc4,3e00008 -3487cc8,2402003b -3487ccc,8c8300a0 -3487cd0,30630007 -3487cd4,14600002 -3487cd8,a01025 -3487cdc,2402004d -3487ce0,3e00008 -3487ce8,8c8300a0 -3487cec,30630038 -3487cf0,14600002 -3487cf4,a01025 -3487cf8,2402004d -3487cfc,3e00008 -3487d04,8c8300a0 -3487d08,3c040001 -3487d0c,3484c000 -3487d10,641824 -3487d14,14600002 -3487d18,a01025 -3487d1c,2402004d -3487d20,3e00008 -3487d28,94820eda -3487d2c,30420008 -3487d30,14400010 -3487d38,80830086 -3487d3c,2402001b -3487d40,1062000e -3487d48,80830087 -3487d4c,1062000d -3487d54,80830088 -3487d58,1062000c -3487d5c,2403001b -3487d60,80840089 -3487d64,1483000a -3487d68,a01025 -3487d6c,3e00008 -3487d70,240200c8 -3487d74,3e00008 -3487d78,240200c8 -3487d7c,3e00008 -3487d80,240200c8 -3487d84,3e00008 -3487d88,240200c8 +3486a84,8c63b1b8 +3486a88,80630000 +3486a8c,a0430424 +3486a90,8fbf001c +3486a94,3e00008 +3486a98,27bd0020 +3486a9c,27bdffe8 +3486aa0,afbf0014 +3486aa4,c101a13 +3486aac,3c02801d +3486ab0,3442aa30 +3486ab4,8c42066c +3486ab8,3c03fcac +3486abc,24632485 +3486ac0,431024 +3486ac4,14400033 +3486ac8,3c028041 +3486acc,3c02801d +3486ad0,3442aa30 +3486ad4,94420088 +3486ad8,30420001 +3486adc,1040002a +3486ae0,1025 +3486ae4,3c02801d +3486ae8,3442aa30 +3486aec,8c420670 +3486af0,3c03000c +3486af4,431024 +3486af8,14400023 +3486afc,1025 +3486b00,3c02800e +3486b04,3442f1b0 +3486b08,8c420000 +3486b0c,30420020 +3486b10,1440001d +3486b14,1025 +3486b18,3c02801c +3486b1c,344284a0 +3486b20,8c420794 +3486b24,14400018 +3486b28,1025 +3486b2c,3c028041 +3486b30,9042b1a0 +3486b34,24420001 +3486b38,304200ff +3486b3c,2c430002 +3486b40,14600012 +3486b44,3c038041 +3486b48,3c028041 +3486b4c,c101a61 +3486b50,a040b1a0 +3486b54,c101db6 +3486b5c,10400005 +3486b64,c101dbf +3486b6c,1000000b +3486b70,8fbf0014 +3486b74,c101a8f +3486b7c,10000007 +3486b80,8fbf0014 +3486b84,1025 +3486b88,3c038041 +3486b8c,10000002 +3486b90,a062b1a0 +3486b94,a040b1a0 +3486b98,8fbf0014 +3486b9c,3e00008 +3486ba0,27bd0018 +3486ba4,27bdffd8 +3486ba8,afbf0024 +3486bac,afb20020 +3486bb0,afb1001c +3486bb4,afb00018 +3486bb8,a09025 +3486bbc,10800012 +3486bc0,c08825 +3486bc4,10c00010 +3486bc8,808025 +3486bcc,4c10004 +3486bd0,c03825 +3486bd4,63823 +3486bd8,73e00 +3486bdc,73e03 +3486be0,30e700ff +3486be4,3c02801c +3486be8,344284a0 +3486bec,904600a5 +3486bf0,2002825 +3486bf4,c10199c +3486bf8,27a40010 +3486bfc,8fa20010 +3486c00,14400005 +3486c04,8fa40010 +3486c08,c1019de +3486c10,10000019 +3486c14,2201025 +3486c18,c1019af +3486c1c,8fa50014 +3486c20,3c028041 +3486c24,8c42b1b8 +3486c28,86040000 +3486c2c,2403000a +3486c30,1483000c +3486c34,80420000 +3486c38,8fa30014 +3486c3c,2404ff00 +3486c40,641824 +3486c44,3c04007c +3486c48,50640001 +3486c4c,2402007c +3486c50,9603001c +3486c54,3063f01f +3486c58,22140 +3486c5c,641825 +3486c60,a603001c +3486c64,6230005 +3486c68,a2420424 +3486c6c,21023 +3486c70,21600 +3486c74,21603 +3486c78,a2420424 +3486c7c,8fbf0024 +3486c80,8fb20020 +3486c84,8fb1001c +3486c88,8fb00018 +3486c8c,3e00008 +3486c90,27bd0028 +3486c94,27bdffd8 +3486c98,afbf0024 +3486c9c,afb20020 +3486ca0,afb1001c +3486ca4,afb00018 +3486ca8,808825 +3486cac,3825 +3486cb0,3025 +3486cb4,802825 +3486cb8,c10199c +3486cbc,27a40010 +3486cc0,8fa20010 +3486cc4,10400029 +3486cc8,93b20016 +3486ccc,c101ea6 +3486cd0,97a40014 +3486cd4,c101e97 +3486cd8,402025 +3486cdc,408025 +3486ce0,ae200134 +3486ce4,3c028040 +3486ce8,16400015 +3486cec,a0520025 +3486cf0,3c028040 +3486cf4,90430024 +3486cf8,3c028040 +3486cfc,a0430025 +3486d00,3025 +3486d04,96050002 +3486d08,3c11801c +3486d0c,3c02800d +3486d10,3442ce14 +3486d14,40f809 +3486d18,362484a0 +3486d1c,92050001 +3486d20,3c028006 +3486d24,3442fdcc +3486d28,40f809 +3486d2c,362484a0 +3486d30,c101ebe +3486d34,2002025 +3486d38,10000013 +3486d3c,8fbf0024 +3486d40,3025 +3486d44,96050002 +3486d48,3c04801c +3486d4c,3c02800d +3486d50,3442ce14 +3486d54,40f809 +3486d58,348484a0 +3486d5c,c1019f1 +3486d60,27a40010 +3486d64,10000008 +3486d68,8fbf0024 +3486d6c,c101ea6 +3486d70,2404005b +3486d74,c101e97 +3486d78,402025 +3486d7c,408025 +3486d80,1000ffdb +3486d84,ae200134 +3486d88,8fb20020 +3486d8c,8fb1001c +3486d90,8fb00018 +3486d94,3e00008 +3486d98,27bd0028 +3486d9c,27bdffe8 +3486da0,afbf0014 +3486da4,afb00010 +3486da8,3c028011 +3486dac,3442a5d0 +3486db0,94500eec +3486db4,32100002 +3486db8,1600000c +3486dbc,3c028040 +3486dc0,90420cd5 +3486dc4,50400004 +3486dc8,3c028011 +3486dcc,c101a2b +3486dd0,24040002 +3486dd4,3c028011 +3486dd8,3442a5d0 +3486ddc,94430eec +3486de0,34630002 +3486de4,a4430eec +3486de8,3c028040 +3486dec,90430cd5 +3486df0,14600002 +3486df4,24020001 +3486df8,10102b +3486dfc,8fbf0014 +3486e00,8fb00010 +3486e04,3e00008 +3486e08,27bd0018 +3486e0c,94830004 +3486e10,94820006 +3486e14,620018 +3486e18,9082000c +3486e1c,1812 +3486e28,620018 +3486e2c,1012 +3486e30,3e00008 +3486e38,27bdffe8 +3486e3c,afbf0014 +3486e40,afb00010 +3486e44,c101b83 +3486e48,808025 +3486e4c,96030008 +3486e50,620018 +3486e54,1012 +3486e58,8fbf0014 +3486e5c,8fb00010 +3486e60,3e00008 +3486e64,27bd0018 +3486e68,27bdff98 +3486e6c,afbf0064 +3486e70,afb60060 +3486e74,afb5005c +3486e78,afb40058 +3486e7c,afb30054 +3486e80,afb20050 +3486e84,afb1004c +3486e88,afb00048 +3486e8c,808025 +3486e90,a0a025 +3486e94,c0a825 +3486e98,94b10004 +3486e9c,94a20006 +3486ea0,470018 +3486ea4,9012 +3486ea8,90b6000b +3486eac,90b3000a +3486eb0,139d40 +3486eb4,3c0200e0 +3486eb8,2629824 +3486ebc,1614c0 +3486ec0,3c030018 +3486ec4,431024 +3486ec8,2629825 +3486ecc,2622ffff +3486ed0,30420fff +3486ed4,531025 +3486ed8,3c03fd00 +3486edc,431025 +3486ee0,afa20010 +3486ee4,c101b83 +3486ee8,a02025 +3486eec,550018 +3486ef0,8e820000 +3486ef4,1812 +3486ef8,431021 +3486efc,afa20014 +3486f00,2ec30002 +3486f04,10600003 +3486f08,24020010 +3486f0c,24020004 +3486f10,2c21004 +3486f14,510018 +3486f18,1812 +3486f1c,2463003f +3486f20,317c3 +3486f24,3042003f +3486f28,431021 +3486f2c,210c0 +3486f30,3c030003 +3486f34,3463fe00 +3486f38,431024 +3486f3c,531025 +3486f40,3c03f500 +3486f44,431025 +3486f48,afa20018 +3486f4c,3c040700 +3486f50,afa4001c +3486f54,3c03e600 +3486f58,afa30020 +3486f5c,afa00024 +3486f60,3c03f400 +3486f64,afa30028 +3486f68,2623ffff +3486f6c,31b80 +3486f70,3c0500ff +3486f74,34a5f000 +3486f78,651824 +3486f7c,2652ffff +3486f80,129080 +3486f84,32520ffc +3486f88,721825 +3486f8c,642025 +3486f90,afa4002c +3486f94,3c04e700 +3486f98,afa40030 +3486f9c,afa00034 +3486fa0,afa20038 +3486fa4,afa0003c +3486fa8,3c02f200 +3486fac,afa20040 +3486fb0,afa30044 +3486fb4,27a20010 +3486fb8,27a60048 +3486fbc,8e030008 +3486fc0,24640008 +3486fc4,ae040008 +3486fc8,8c450004 +3486fcc,8c440000 +3486fd0,ac650004 +3486fd4,24420008 +3486fd8,1446fff8 +3486fdc,ac640000 +3486fe0,8fbf0064 +3486fe4,8fb60060 +3486fe8,8fb5005c +3486fec,8fb40058 +3486ff0,8fb30054 +3486ff4,8fb20050 +3486ff8,8fb1004c +3486ffc,8fb00048 +3487000,3e00008 +3487004,27bd0068 +3487008,27bdffe0 +348700c,8fa80030 +3487010,8fa20034 +3487014,8faa0038 +3487018,94a30004 +348701c,31a80 +3487020,14400002 +3487024,62001a +3487028,7000d +348702c,4812 +3487030,94a30006 +3487034,471021 +3487038,21380 +348703c,3c0b00ff +3487040,356bf000 +3487044,4b1024 +3487048,1482821 +348704c,52880 +3487050,30a50fff +3487054,451025 +3487058,3c05e400 +348705c,451025 +3487060,afa20000 +3487064,73b80 +3487068,eb3824 +348706c,84080 +3487070,31080fff +3487074,e83825 +3487078,afa70004 +348707c,3c02e100 +3487080,afa20008 +3487084,660018 +3487088,1012 +348708c,21140 +3487090,3042ffff +3487094,afa2000c +3487098,3c02f100 +348709c,afa20010 +34870a0,31a80 +34870a4,15400002 +34870a8,6a001a +34870ac,7000d +34870b0,1012 +34870b4,3042ffff +34870b8,94c00 +34870bc,491025 +34870c0,afa20014 +34870c4,afbd0018 +34870c8,27a50018 +34870cc,8c820008 +34870d0,24430008 +34870d4,ac830008 +34870d8,8fa30018 +34870dc,8c670004 +34870e0,8c660000 +34870e4,ac470004 +34870e8,ac460000 +34870ec,24620008 +34870f0,1445fff6 +34870f4,afa20018 +34870f8,3e00008 +34870fc,27bd0020 +3487100,27bdffa0 +3487104,afbf005c +3487108,afb10058 +348710c,afb00054 +3487110,afa00010 +3487114,3c0201a0 +3487118,24422000 +348711c,afa20014 +3487120,3c110003 +3487124,362295c0 +3487128,afa20018 +348712c,c1026fd +3487130,27a40010 +3487134,afa0001c +3487138,3c020084 +348713c,24426000 +3487140,afa20020 +3487144,3402b400 +3487148,afa20024 +348714c,c1026fd +3487150,27a4001c +3487154,afa00028 +3487158,3c02007b +348715c,3442d000 +3487160,afa2002c +3487164,3c100008 +3487168,361088a0 +348716c,afb00030 +3487170,c1026fd +3487174,27a40028 +3487178,afa00034 +348717c,3c0201a3 +3487180,3442c000 +3487184,afa20038 +3487188,24023b00 +348718c,afa2003c +3487190,c1026fd +3487194,27a40034 +3487198,afa00040 +348719c,3c020085 +34871a0,3442e000 +34871a4,afa20044 +34871a8,24021d80 +34871ac,afa20048 +34871b0,c1026fd +34871b4,27a40040 +34871b8,8fa20010 +34871bc,2631a300 +34871c0,518821 +34871c4,3c038041 +34871c8,ac71a0a8 +34871cc,24422980 +34871d0,3c038041 +34871d4,ac62a098 +34871d8,8fa20028 +34871dc,3c038041 +34871e0,ac62a088 +34871e4,3c038041 +34871e8,8fa4001c +34871ec,ac64a078 +34871f0,3c048041 +34871f4,3c038041 +34871f8,2463d750 +34871fc,ac83a058 +3487200,3c048041 +3487204,3c038041 +3487208,2463df50 +348720c,ac83a048 +3487210,2610f7a0 +3487214,501021 +3487218,3c038041 +348721c,ac62a038 +3487220,8fa20034 +3487224,24441e00 +3487228,3c038041 +348722c,ac64a028 +3487230,244435c0 +3487234,3c038041 +3487238,ac64a018 +348723c,8fa30040 +3487240,24631980 +3487244,3c048041 +3487248,ac83a008 +348724c,3c038041 +3487250,ac629ff8 +3487254,3c118041 +3487258,c101b8e +348725c,2624a068 +3487260,408025 +3487264,c1026ee +3487268,402025 +348726c,104fc2 +3487270,1304821 +3487274,2a100002 +3487278,16000018 +348727c,ae22a068 +3487280,94843 +3487284,3c038041 +3487288,2463c288 +348728c,2025 +3487290,3025 +3487294,2204025 +3487298,2407fff0 +348729c,8d05a068 +34872a0,a42821 +34872a4,90620000 +34872a8,21102 +34872ac,471025 +34872b0,a0a20000 +34872b4,8d02a068 +34872b8,441021 +34872bc,90650000 +34872c0,a72825 +34872c4,a0450001 +34872c8,24c60001 +34872cc,24630001 +34872d0,c9102a +34872d4,1440fff1 +34872d8,24840002 +34872dc,8fbf005c +34872e0,8fb10058 +34872e4,8fb00054 +34872e8,3e00008 +34872ec,27bd0060 +34872f0,3c038040 +34872f4,9462084e +34872f8,2463084e +34872fc,94640002 +3487300,94630004 +3487304,3c058041 +3487308,8ca5b170 +348730c,a4a20000 +3487310,a4a40002 +3487314,a4a30004 +3487318,3c058041 +348731c,8ca6b16c +3487320,a4c20000 +3487324,8ca5b16c +3487328,a4a40004 +348732c,a4a30008 +3487330,240500ff +3487334,1445000a +3487338,3c058041 +348733c,24050046 +3487340,14850007 +3487344,3c058041 +3487348,24050032 +348734c,14650004 +3487350,3c058041 +3487354,1825 +3487358,2025 +348735c,240200c8 +3487360,8ca5b168 +3487364,a4a20000 +3487368,a4a40002 +348736c,a4a30004 +3487370,3c058041 +3487374,8ca5b164 +3487378,a4a20000 +348737c,a4a40002 +3487380,a4a30004 +3487384,3c028041 +3487388,8c43b160 +348738c,3c028040 +3487390,94450854 +3487394,24420854 +3487398,94440002 +348739c,94420004 +34873a0,a4650000 +34873a4,a4640002 +34873a8,a4620004 +34873ac,3c028041 +34873b0,8c43b15c +34873b4,3c028040 +34873b8,9445085a +34873bc,2442085a +34873c0,94440002 +34873c4,94420004 +34873c8,a4650000 +34873cc,a4640002 +34873d0,a4620004 +34873d4,3c028041 +34873d8,8c43b158 +34873dc,3c028040 +34873e0,94450860 +34873e4,24420860 +34873e8,94440002 +34873ec,94420004 +34873f0,a4650000 +34873f4,a4640002 +34873f8,a4620004 +34873fc,3c028041 +3487400,8c42b154 +3487404,3c068040 +3487408,94c30872 +348740c,a4430000 +3487410,3c028041 +3487414,8c43b150 +3487418,24c20872 +348741c,94440002 +3487420,a4640000 +3487424,3c038041 +3487428,8c63b14c +348742c,94440004 +3487430,a4640000 +3487434,3c038041 +3487438,8c63b148 +348743c,3c058040 +3487440,94a40878 +3487444,a4640000 +3487448,3c038041 +348744c,8c64b144 +3487450,24a30878 +3487454,94670002 +3487458,a4870000 +348745c,3c048041 +3487460,8c84b140 +3487464,94670004 +3487468,a4870000 +348746c,3c048041 +3487470,8c84b13c +3487474,94c80872 +3487478,94470002 +348747c,94460004 +3487480,a4880000 +3487484,a4870002 +3487488,a4860004 +348748c,3c048041 +3487490,8c84b12c +3487494,94a60878 +3487498,94650002 +348749c,94630004 +34874a0,a4860000 +34874a4,a4850002 +34874a8,a4830004 +34874ac,94420002 +34874b0,3043ffff +34874b4,2c6300ce +34874b8,50600001 +34874bc,240200cd +34874c0,24420032 +34874c4,3047ffff +34874c8,3c028040 +34874cc,94420876 +34874d0,3043ffff +34874d4,2c6300ce +34874d8,50600001 +34874dc,240200cd +34874e0,24420032 +34874e4,3046ffff +34874e8,3c028040 +34874ec,94420878 +34874f0,3043ffff +34874f4,2c6300ce +34874f8,50600001 +34874fc,240200cd +3487500,24420032 +3487504,3044ffff +3487508,3c028040 +348750c,9442087a +3487510,3043ffff +3487514,2c6300ce +3487518,50600001 +348751c,240200cd +3487520,24420032 +3487524,3043ffff +3487528,3c028040 +348752c,9442087c +3487530,3045ffff +3487534,2ca500ce +3487538,50a00001 +348753c,240200cd +3487540,24420032 +3487544,3c058041 +3487548,8ca8b138 +348754c,3c058040 +3487550,94a50872 +3487554,30a9ffff +3487558,2d2900ce +348755c,15200002 +3487560,3042ffff +3487564,240500cd +3487568,24a50032 +348756c,a5050000 +3487570,a5070002 +3487574,a5060004 +3487578,3c058041 +348757c,8ca5b128 +3487580,a4a40000 +3487584,a4a30002 +3487588,a4a20004 +348758c,3c028041 +3487590,8c43b130 +3487594,3c028040 +3487598,94450872 +348759c,24420872 +34875a0,94440002 +34875a4,94420004 +34875a8,a4650000 +34875ac,a4640002 +34875b0,a4620004 +34875b4,3c028041 +34875b8,8c43b120 +34875bc,3c028040 +34875c0,94450878 +34875c4,24420878 +34875c8,94440002 +34875cc,94420004 +34875d0,a4650000 +34875d4,a4640002 +34875d8,a4620004 +34875dc,3c028041 +34875e0,8c43b11c +34875e4,3c028040 +34875e8,94460866 +34875ec,24440866 +34875f0,94850002 +34875f4,94840004 +34875f8,a4660000 +34875fc,a4650002 +3487600,a4640004 +3487604,94420866 +3487608,3043ffff +348760c,2c6300ce +3487610,50600001 +3487614,240200cd +3487618,24420032 +348761c,3044ffff +3487620,3c028040 +3487624,94420868 +3487628,3043ffff +348762c,2c6300ce +3487630,50600001 +3487634,240200cd +3487638,24420032 +348763c,3043ffff +3487640,3c028040 +3487644,9442086a +3487648,3045ffff +348764c,2ca500ce +3487650,50a00001 +3487654,240200cd +3487658,24420032 +348765c,3042ffff +3487660,3c058041 +3487664,8ca5b118 +3487668,a4a40000 +348766c,a4a30002 +3487670,a4a20004 +3487674,3c058041 +3487678,8ca5b110 +348767c,a4a40000 +3487680,a4a30002 +3487684,3e00008 +3487688,a4a20004 +348768c,3c028011 +3487690,3442a5d0 +3487694,8c4200a0 +3487698,21302 +348769c,30420003 +34876a0,21840 +34876a4,621821 +34876a8,3c028041 +34876ac,24429e30 +34876b0,621821 +34876b4,90640000 +34876b8,42600 +34876bc,90620001 +34876c0,21400 +34876c4,822021 +34876c8,90620002 +34876cc,21200 +34876d0,3e00008 +34876d4,821021 +34876d8,3c028041 +34876dc,9042b1d0 +34876e0,3e00008 +34876e4,2102b +34876e8,3c038041 +34876ec,9062b1d0 +34876f0,24420001 +34876f4,3e00008 +34876f8,a062b1d0 +34876fc,3c028041 +3487700,9042b1d0 +3487704,1040001b +3487708,2442ffff +348770c,27bdffd8 +3487710,afbf0024 +3487714,afb10020 +3487718,afb0001c +348771c,3c038041 +3487720,a062b1d0 +3487724,3c108038 +3487728,3610e578 +348772c,24050014 +3487730,3c11801d +3487734,200f809 +3487738,3624aa30 +348773c,24020014 +3487740,afa20014 +3487744,afa00010 +3487748,26100130 +348774c,3825 +3487750,24060003 +3487754,3625aa30 +3487758,200f809 +348775c,262484a0 +3487760,8fbf0024 +3487764,8fb10020 +3487768,8fb0001c +348776c,3e00008 +3487770,27bd0028 +3487774,3e00008 +348777c,3e00008 +3487784,24020140 +3487788,3e00008 +348778c,a4821424 +3487790,27bdffe0 +3487794,afbf001c +3487798,afb10018 +348779c,afb00014 +34877a0,808025 +34877a4,8c8208c4 +34877a8,24420001 +34877ac,c10258c +34877b0,ac8208c4 +34877b4,3c028041 +34877b8,9442b182 +34877bc,8e0308c4 +34877c0,1462001e +34877c4,8fbf001c +34877c8,920200b2 +34877cc,34420001 +34877d0,a20200b2 +34877d4,3c04801c +34877d8,348484a0 +34877dc,3c110001 +34877e0,918821 +34877e4,86221e1a +34877e8,ae020000 +34877ec,948200a4 +34877f0,a6020066 +34877f4,3c108009 +34877f8,3602d894 +34877fc,40f809 +3487800,261005d4 +3487804,3c04a34b +3487808,200f809 +348780c,3484e820 +3487810,3c028011 +3487814,3442a5d0 +3487818,2403fff8 +348781c,a4431412 +3487820,240200a0 +3487824,a6221e1a +3487828,24020014 +348782c,a2221e15 +3487830,24020001 +3487834,a2221e5e +3487838,8fbf001c +348783c,8fb10018 +3487840,8fb00014 +3487844,3e00008 +3487848,27bd0020 +348784c,8c8200a0 +3487850,34423000 +3487854,ac8200a0 +3487858,3c028041 +348785c,9042b1d3 +3487860,304200ff +3487864,10400005 +3487868,52840 +348786c,3c028010 +3487870,451021 +3487874,94428cec +3487878,a4820034 +348787c,3e00008 +3487884,24020001 +3487888,3e00008 +348788c,a082003e +3487890,24020012 +3487894,2406ffff +3487898,24070016 +348789c,821821 +34878a0,80630074 +34878a4,54660004 +34878a8,24420001 +34878ac,822021 +34878b0,3e00008 +34878b4,a0850074 +34878b8,1447fff9 +34878bc,821821 +34878c0,3e00008 +34878c8,862021 +34878cc,908200a8 +34878d0,a22825 +34878d4,3e00008 +34878d8,a08500a8 +34878dc,852021 +34878e0,908200bc +34878e4,21e00 +34878e8,31e03 +34878ec,4620001 +34878f0,1025 +34878f4,24420001 +34878f8,3e00008 +34878fc,a08200bc +3487900,24020001 +3487904,a082003d +3487908,24020014 +348790c,a08200cf +3487910,24020140 +3487914,3e00008 +3487918,a4821424 +348791c,24020001 +3487920,a0820032 +3487924,a082003a +3487928,24020030 +348792c,a48213f4 +3487930,3e00008 +3487934,a0820033 +3487938,24020002 +348793c,a0820032 +3487940,24020001 +3487944,a082003a +3487948,a082003c +348794c,24020060 +3487950,a48213f4 +3487954,3e00008 +3487958,a0820033 +348795c,24020007 +3487960,3e00008 +3487964,a082007b +3487968,24020001 +348796c,a21004 +3487970,8c8500a4 +3487974,a22825 +3487978,3e00008 +348797c,ac8500a4 +3487980,27bdffe8 +3487984,afbf0014 +3487988,c101dba +3487990,8fbf0014 +3487994,3e00008 +3487998,27bd0018 +348799c,24020010 +34879a0,a0820082 +34879a4,9082009a +34879a8,2442000a +34879ac,3e00008 +34879b0,a082009a +34879b4,3c028041 +34879b8,9042b1d3 +34879bc,304200ff +34879c0,10400005 +34879c4,52840 +34879c8,3c028010 +34879cc,451021 +34879d0,94428cec +34879d4,a4820034 +34879d8,3e00008 +34879e0,3c028041 +34879e4,9042b1d2 +34879e8,1040000c +34879ec,3c028041 +34879f0,94820f06 +34879f4,34420040 +34879f8,a4820f06 +34879fc,3c028041 +3487a00,9042b1d1 +3487a04,54400009 +3487a08,94820f06 +3487a0c,94820ef4 +3487a10,3042fb87 +3487a14,3e00008 +3487a18,a4820ef4 +3487a1c,9042b1d1 +3487a20,1040000c +3487a28,94820f06 +3487a2c,34420080 +3487a30,a4820f06 +3487a34,94820ef6 +3487a38,24038f00 +3487a3c,431025 +3487a40,a4820ef6 +3487a44,94820ee4 +3487a48,2403f000 +3487a4c,431025 +3487a50,a4820ee4 +3487a54,3e00008 +3487a5c,2c8200cd +3487a60,1040000b +3487a64,41880 +3487a68,641021 +3487a6c,21080 +3487a70,3c058041 +3487a74,24a5a0e0 +3487a78,a21021 +3487a7c,80430000 +3487a80,3182b +3487a84,31823 +3487a88,3e00008 +3487a8c,431024 +3487a90,3e00008 +3487a94,1025 +3487a98,27bdffe0 +3487a9c,afbf001c +3487aa0,afb20018 +3487aa4,afb10014 +3487aa8,afb00010 +3487aac,808025 +3487ab0,3c128011 +3487ab4,3652a5d0 +3487ab8,c101e97 +3487abc,2002025 +3487ac0,2008825 +3487ac4,8c420008 +3487ac8,2002825 +3487acc,40f809 +3487ad0,2402025 +3487ad4,1622fff8 +3487ad8,408025 +3487adc,2201025 +3487ae0,8fbf001c +3487ae4,8fb20018 +3487ae8,8fb10014 +3487aec,8fb00010 +3487af0,3e00008 +3487af4,27bd0020 +3487af8,27bdffe8 +3487afc,afbf0014 +3487b00,8c82000c +3487b04,84860012 +3487b08,84850010 +3487b0c,3c048011 +3487b10,40f809 +3487b14,3484a5d0 +3487b18,8fbf0014 +3487b1c,3e00008 +3487b20,27bd0018 +3487b24,3e00008 +3487b28,a01025 +3487b2c,8082007d +3487b30,21027 +3487b34,2102b +3487b38,3e00008 +3487b3c,24420008 +3487b40,8c8200a0 +3487b44,21182 +3487b48,30420007 +3487b4c,10400005 +3487b54,38420001 +3487b58,2102b +3487b5c,3e00008 +3487b60,24420035 +3487b64,3e00008 +3487b68,24020054 +3487b6c,8c8200a0 +3487b70,210c2 +3487b74,30420007 +3487b78,10400005 +3487b80,38420001 +3487b84,2102b +3487b88,3e00008 +3487b8c,24420033 +3487b90,3e00008 +3487b94,24020032 +3487b98,8c8200a0 +3487b9c,30420007 +3487ba0,10400005 +3487ba8,38420001 +3487bac,2102b +3487bb0,3e00008 +3487bb4,24420030 +3487bb8,3e00008 +3487bbc,24020004 +3487bc0,8c8300a0 +3487bc4,31b82 +3487bc8,30630007 +3487bcc,10600005 +3487bd0,24040001 +3487bd4,14640004 +3487bd8,2402007b +3487bdc,3e00008 +3487be0,24020060 +3487be4,24020005 +3487be8,3e00008 +3487bf0,8c8300a0 +3487bf4,31b02 +3487bf8,30630003 +3487bfc,10600005 +3487c00,24040001 +3487c04,14640004 +3487c08,240200c7 +3487c0c,3e00008 +3487c10,24020046 +3487c14,24020045 +3487c18,3e00008 +3487c20,8c8200a0 +3487c24,21242 +3487c28,30420007 +3487c2c,2102b +3487c30,3e00008 +3487c34,24420037 +3487c38,8c8200a0 +3487c3c,21502 +3487c40,30420007 +3487c44,2c420002 +3487c48,2c420001 +3487c4c,3e00008 +3487c50,24420079 +3487c54,8c8200a0 +3487c58,21442 +3487c5c,30420007 +3487c60,2c420002 +3487c64,2c420001 +3487c68,3e00008 +3487c6c,24420077 +3487c70,9082003a +3487c74,2102b +3487c78,3e00008 +3487c7c,244200b9 +3487c80,8083007c +3487c84,2402ffff +3487c88,50620007 +3487c8c,2402006b +3487c90,80830094 +3487c94,28630006 +3487c98,10600003 +3487c9c,2402006a +3487ca0,3e00008 +3487ca4,24020003 +3487ca8,3e00008 +3487cb0,8083007b +3487cb4,2402ffff +3487cb8,10620003 +3487cc0,3e00008 +3487cc4,2402000c +3487cc8,3e00008 +3487ccc,2402003b +3487cd0,8c8300a0 +3487cd4,30630007 +3487cd8,14600002 +3487cdc,a01025 +3487ce0,2402004d +3487ce4,3e00008 +3487cec,8c8300a0 +3487cf0,30630038 +3487cf4,14600002 +3487cf8,a01025 +3487cfc,2402004d +3487d00,3e00008 +3487d08,8c8300a0 +3487d0c,3c040001 +3487d10,3484c000 +3487d14,641824 +3487d18,14600002 +3487d1c,a01025 +3487d20,2402004d +3487d24,3e00008 +3487d2c,94820eda +3487d30,30420008 +3487d34,14400010 +3487d3c,80830086 +3487d40,2402001b +3487d44,1062000e +3487d4c,80830087 +3487d50,1062000d +3487d58,80830088 +3487d5c,1062000c +3487d60,2403001b +3487d64,80840089 +3487d68,1483000a +3487d6c,a01025 +3487d70,3e00008 +3487d74,240200c8 +3487d78,3e00008 +3487d7c,240200c8 +3487d80,3e00008 +3487d84,240200c8 +3487d88,3e00008 3487d8c,240200c8 -3487d90,3e00008 -3487d98,27bdffe8 -3487d9c,afbf0014 -3487da0,c1026e8 -3487da8,c101c3f -3487db0,c102501 -3487db8,c101911 -3487dc0,c102314 -3487dc8,8fbf0014 -3487dcc,3e00008 -3487dd0,27bd0018 -3487dd4,27bdffe8 -3487dd8,afbf0014 -3487ddc,c101aaa -3487de4,c100fa4 -3487dec,c102237 -3487df4,c101cbb -3487dfc,c101431 -3487e04,c1018f8 -3487e0c,8fbf0014 -3487e10,3e00008 -3487e14,27bd0018 -3487e18,27bdffe8 -3487e1c,afbf0014 -3487e20,afb00010 -3487e24,3c10801c -3487e28,361084a0 -3487e2c,8e040000 -3487e30,c101199 -3487e34,248402a8 -3487e38,8e040000 -3487e3c,c102595 -3487e40,248402a8 -3487e44,8fbf0014 -3487e48,8fb00010 -3487e4c,3e00008 -3487e50,27bd0018 -3487e54,27bdffe8 -3487e58,afbf0014 -3487e5c,c1018ff -3487e64,c1026e3 -3487e6c,c102327 -3487e74,c10142b -3487e7c,8fbf0014 -3487e80,3e00008 -3487e84,27bd0018 -3487e88,3c02801c -3487e8c,344284a0 -3487e90,3c030001 -3487e94,431021 -3487e98,84430988 -3487e9c,14600022 -3487ea0,3c02801c -3487ea4,344284a0 -3487ea8,3c030001 -3487eac,431021 -3487eb0,84420992 -3487eb4,14400014 -3487eb8,21840 -3487ebc,3c028011 -3487ec0,3442a5d0 -3487ec4,8c420004 -3487ec8,14400009 -3487ecc,3c028011 -3487ed0,3442a5d0 -3487ed4,8c4300a0 -3487ed8,3c020001 -3487edc,3442c007 -3487ee0,621824 -3487ee4,14600026 -3487ee8,24020001 -3487eec,3c028011 -3487ef0,3442a5d0 -3487ef4,8c4200a0 -3487ef8,21382 -3487efc,30420007 -3487f00,3e00008 -3487f04,2102b -3487f08,621821 -3487f0c,3c028011 -3487f10,3442a5d0 -3487f14,8c4200a0 -3487f18,621006 -3487f1c,30420007 -3487f20,3e00008 -3487f24,2102b -3487f28,344284a0 -3487f2c,3c040001 -3487f30,441021 -3487f34,84440992 -3487f38,1480000a -3487f3c,3c028011 -3487f40,24020003 -3487f44,14620007 -3487f48,3c028011 -3487f4c,3442a5d0 -3487f50,8c42009c -3487f54,3c03000c -3487f58,431024 -3487f5c,3e00008 -3487f60,2102b -3487f64,3442a5d0 -3487f68,9442009c -3487f6c,42080 -3487f70,2463ffff -3487f74,832021 -3487f78,821007 -3487f7c,30420001 -3487f80,3e00008 -3487f88,27bdffe0 -3487f8c,afbf001c -3487f90,3c028040 -3487f94,9042088b -3487f98,10400010 -3487f9c,3c028040 -3487fa0,2406000c -3487fa4,3c028041 -3487fa8,8c45b1cc -3487fac,c10245a -3487fb0,27a40010 -3487fb4,3c028011 -3487fb8,97a30010 -3487fbc,a4435dd2 -3487fc0,93a30012 -3487fc4,a0435dd4 -3487fc8,97a30010 -3487fcc,a4435dda -3487fd0,93a30012 -3487fd4,a0435ddc -3487fd8,3c028040 -3487fdc,9042088c -3487fe0,10400010 -3487fe4,8fbf001c -3487fe8,2406000a -3487fec,3c028041 -3487ff0,8c45b1cc -3487ff4,c10245a -3487ff8,27a40010 -3487ffc,3c028011 -3488000,97a30010 -3488004,a4435dce -3488008,93a30012 -348800c,a0435dd0 -3488010,97a30010 -3488014,a4435dd6 -3488018,93a30012 -348801c,a0435dd8 -3488020,8fbf001c -3488024,3e00008 -3488028,27bd0020 -348802c,3c02801d -3488030,3442aa30 -3488034,8c420678 -3488038,10400063 -3488040,8c430130 -3488044,10600060 -348804c,8c4201c8 -3488050,2c43001f -3488054,1060005c -348805c,27bdffd8 -3488060,afbf0024 -3488064,afb10020 -3488068,afb0001c -348806c,280c0 -3488070,2028023 -3488074,108080 -3488078,2028023 -348807c,108100 -3488080,3c028011 -3488084,2028021 -3488088,3c028040 -348808c,9042088d -3488090,10400018 -3488094,2610572c -3488098,3c118041 -348809c,24060006 -34880a0,8e25b1cc -34880a4,c10245a -34880a8,27a40010 -34880ac,93a20010 -34880b0,a2020192 -34880b4,93a20011 -34880b8,a2020193 -34880bc,93a20012 -34880c0,a2020194 -34880c4,8e25b1cc -34880c8,24060006 -34880cc,24a5000c -34880d0,c10245a -34880d4,27a40010 -34880d8,93a20010 -34880dc,a202019a -34880e0,93a20011 -34880e4,a202019b -34880e8,93a20012 -34880ec,1000000c -34880f0,a202019c -34880f4,3c028040 -34880f8,9044087e -34880fc,a2040192 -3488100,2442087e -3488104,90430001 -3488108,a2030193 -348810c,90420002 -3488110,a2020194 -3488114,a204019a -3488118,a203019b -348811c,a202019c -3488120,3c028040 -3488124,9042088e -3488128,10400018 -348812c,3c028040 -3488130,3c118041 -3488134,24060005 -3488138,8e25b1cc -348813c,c10245a -3488140,27a40010 -3488144,93a20010 -3488148,a2020196 -348814c,93a20011 -3488150,a2020197 -3488154,93a20012 -3488158,a2020198 -348815c,8e25b1cc -3488160,24060005 -3488164,24a5000a -3488168,c10245a -348816c,27a40010 -3488170,93a20010 -3488174,a202019e -3488178,93a20011 -348817c,a202019f -3488180,93a20012 -3488184,1000000b -3488188,a20201a0 -348818c,90440881 -3488190,a2040196 -3488194,24420881 -3488198,90430001 -348819c,a2030197 -34881a0,90420002 -34881a4,a2020198 -34881a8,a204019e -34881ac,a203019f -34881b0,a20201a0 -34881b4,8fbf0024 -34881b8,8fb10020 -34881bc,8fb0001c -34881c0,3e00008 -34881c4,27bd0028 -34881c8,3e00008 -34881d0,27bdffd0 -34881d4,afbf002c -34881d8,afb20028 -34881dc,afb10024 -34881e0,afb00020 -34881e4,3c028040 -34881e8,90430884 -34881ec,240200fa -34881f0,14620008 -34881f4,24100001 -34881f8,3c028040 -34881fc,24420884 -3488200,90500001 -3488204,90420002 -3488208,2028025 -348820c,321000ff -3488210,10802b -3488214,3c028040 -3488218,90430887 -348821c,240200fa -3488220,14620008 -3488224,24110001 -3488228,3c028040 -348822c,24420887 -3488230,90510001 -3488234,90420002 -3488238,2228825 -348823c,323100ff -3488240,11882b -3488244,3c128041 -3488248,24060009 -348824c,8e45b1cc -3488250,c10245a -3488254,27a40010 -3488258,8e45b1cc -348825c,24060009 -3488260,24a50012 -3488264,c10245a -3488268,27a40014 -348826c,24060007 -3488270,8e45b1cc -3488274,c10245a -3488278,27a40018 -348827c,8e45b1cc -3488280,24060007 -3488284,24a5000e -3488288,c10245a -348828c,27a4001c -3488290,3c02801c -3488294,344284a0 -3488298,8c421c4c -348829c,10400064 -34882a0,8fbf002c -34882a4,240500da -34882a8,3c068011 -34882ac,24c65c3c -34882b0,3c088040 -34882b4,3c078040 -34882b8,3c0a8040 -34882bc,254c0887 -34882c0,3c098040 -34882c4,252b0884 -34882c8,8c430130 -34882cc,50600055 -34882d0,8c420124 -34882d4,84430000 -34882d8,54650052 -34882dc,8c420124 -34882e0,8c43016c -34882e4,320c0 -34882e8,832023 -34882ec,42080 -34882f0,832023 -34882f4,42100 -34882f8,2484faf0 -34882fc,862021 -3488300,8c4d0170 -3488304,d18c0 -3488308,6d1823 -348830c,31880 -3488310,6d1823 -3488314,31900 -3488318,2463faf0 -348831c,910d088f -3488320,11a0000e -3488324,661821 -3488328,97ae0010 -348832c,a48e0192 -3488330,93ad0012 -3488334,a08d0194 -3488338,a46e0192 -348833c,a06d0194 -3488340,97ae0014 -3488344,a48e019a -3488348,93ad0016 -348834c,a08d019c -3488350,a46e019a -3488354,10000012 -3488358,a06d019c -348835c,12000011 -3488360,90ed0890 -3488364,912f0884 -3488368,a08f0192 -348836c,916e0001 -3488370,a08e0193 -3488374,916d0002 -3488378,a08d0194 -348837c,a06f0192 -3488380,a06e0193 -3488384,a06d0194 -3488388,a08f019a -348838c,a08e019b -3488390,a08d019c -3488394,a06f019a -3488398,a06e019b -348839c,a06d019c -34883a0,90ed0890 -34883a4,11a0000d -34883a8,97ae0018 -34883ac,a48e0196 -34883b0,93ad001a -34883b4,a08d0198 -34883b8,a46e0196 -34883bc,a06d0198 -34883c0,97ae001c -34883c4,a48e019e -34883c8,93ad001e -34883cc,a08d01a0 -34883d0,a46e019e -34883d4,10000012 -34883d8,a06d01a0 -34883dc,52200011 -34883e0,8c420124 -34883e4,914f0887 -34883e8,a08f0196 -34883ec,918e0001 -34883f0,a08e0197 -34883f4,918d0002 -34883f8,a08d0198 -34883fc,a06f0196 -3488400,a06e0197 -3488404,a06d0198 -3488408,a08f019e -348840c,a08e019f -3488410,a08d01a0 -3488414,a06f019e -3488418,a06e019f -348841c,a06d01a0 -3488420,8c420124 -3488424,5440ffa9 -3488428,8c430130 -348842c,8fbf002c -3488430,8fb20028 -3488434,8fb10024 -3488438,8fb00020 -348843c,3e00008 -3488440,27bd0030 -3488444,27bdffd8 -3488448,afbf001c -348844c,f7b40020 -3488450,3c028040 -3488454,9042088f -3488458,1040000a -348845c,46006506 -3488460,24060009 -3488464,3c028041 -3488468,8c45b1cc -348846c,c10245a -3488470,27a40010 -3488474,93a20010 -3488478,93a30011 -348847c,10000006 -3488480,93a40012 -3488484,3c048040 -3488488,90820884 -348848c,24840884 -3488490,90830001 -3488494,90840002 -3488498,240500fa -348849c,14450043 -34884a0,642825 -34884a4,14a00041 -34884ac,3c028041 -34884b0,c4409e78 -34884b4,4600a002 -34884b8,3c028041 -34884bc,c4429e7c -34884c0,46020000 -34884c4,3c028041 -34884c8,c4429e80 -34884cc,4600103e -34884d4,45030005 -34884d8,46020001 -34884dc,4600000d -34884e0,44020000 -34884e4,10000006 -34884e8,304200ff -34884ec,4600000d -34884f0,44020000 -34884f4,3c038000 -34884f8,431025 -34884fc,304200ff -3488500,3c038041 -3488504,c4609e84 -3488508,4600a002 -348850c,3c038041 -3488510,c4629e7c -3488514,46020000 -3488518,3c038041 -348851c,c4629e80 -3488520,4600103e -3488528,45030005 -348852c,46020001 -3488530,4600000d -3488534,44030000 -3488538,10000006 -348853c,306300ff -3488540,4600000d -3488544,44030000 -3488548,3c048000 -348854c,641825 -3488550,306300ff -3488554,3c048041 -3488558,c4809e88 -348855c,4600a002 -3488560,3c048041 -3488564,c4829e8c -3488568,46020000 -348856c,3c048041 -3488570,c4829e80 -3488574,4600103e -348857c,45030005 -3488580,46020001 -3488584,4600000d -3488588,44040000 -348858c,10000040 -3488590,308400ff -3488594,4600000d -3488598,44040000 -348859c,3c058000 -34885a0,852025 -34885a4,1000003a -34885a8,308400ff -34885ac,44820000 -34885b4,46800020 -34885b8,46140002 -34885bc,3c028041 -34885c0,c4429e80 -34885c4,4600103e -34885cc,45030005 -34885d0,46020001 -34885d4,4600000d -34885d8,44020000 -34885dc,10000006 -34885e0,304200ff -34885e4,4600000d -34885e8,44020000 -34885ec,3c058000 -34885f0,451025 -34885f4,304200ff -34885f8,44830000 -3488600,46800020 -3488604,46140002 -3488608,3c038041 -348860c,c4629e80 -3488610,4600103e -3488618,45030005 -348861c,46020001 -3488620,4600000d -3488624,44030000 -3488628,10000006 -348862c,306300ff -3488630,4600000d -3488634,44030000 -3488638,3c058000 -348863c,651825 -3488640,306300ff -3488644,44840000 -348864c,46800020 -3488650,46140002 -3488654,3c048041 -3488658,c4829e80 -348865c,4600103e -3488664,45030005 -3488668,46020001 -348866c,4600000d -3488670,44040000 -3488674,10000006 -3488678,308400ff -348867c,4600000d -3488680,44040000 -3488684,3c058000 -3488688,852025 -348868c,308400ff -3488690,21600 -3488694,42200 -3488698,441025 -348869c,31c00 -34886a0,431025 -34886a4,344200ff -34886a8,8fbf001c -34886ac,d7b40020 -34886b0,3e00008 -34886b4,27bd0028 -34886b8,27bdffd8 -34886bc,afbf0024 -34886c0,afb20020 -34886c4,afb1001c -34886c8,afb00018 -34886cc,3c02801c -34886d0,344284a0 -34886d4,90421cda -34886d8,24030004 -34886dc,10430015 -34886e0,2c430005 -34886e4,50600006 -34886e8,2442fffb -34886ec,24030002 -34886f0,50430008 -34886f4,3c028040 -34886f8,10000013 -34886fc,3c028040 -3488700,304200fb -3488704,54400010 -3488708,3c028040 -348870c,10000005 -3488710,3c028040 -3488714,90500891 -3488718,3c028040 -348871c,1000000d -3488720,90510892 -3488724,90500893 -3488728,3c028040 -348872c,10000009 -3488730,90510894 -3488734,3c028040 -3488738,90500895 -348873c,3c028040 -3488740,10000004 -3488744,90510896 -3488748,90500897 -348874c,3c028040 -3488750,90510898 -3488754,2111025 -3488758,1040005b -348875c,8fbf0024 -3488760,3c128041 -3488764,2406000e -3488768,8e45b1cc -348876c,c10245a -3488770,27a40010 -3488774,2406000c -3488778,8e45b1cc -348877c,c10245a -3488780,27a40014 -3488784,1200000a -3488788,3c02801c -348878c,344284a0 -3488790,90431cda -3488794,318c0 -3488798,3c02800f -348879c,431021 -34887a0,97a30010 -34887a4,a4438214 -34887a8,93a30012 -34887ac,a0438216 -34887b0,1220000a -34887b4,3c02801c -34887b8,344284a0 -34887bc,90431cda -34887c0,318c0 -34887c4,3c02800f -34887c8,431021 -34887cc,97a30014 -34887d0,a4438218 -34887d4,93a30016 -34887d8,a043821a -34887dc,12000010 -34887e0,3c02801d -34887e4,3c02801c -34887e8,344284a0 -34887ec,97a30010 -34887f0,a4431cf0 -34887f4,93a30012 -34887f8,a0431cf2 -34887fc,97a30010 -3488800,a4431d04 -3488804,93a30012 -3488808,a0431d06 -348880c,97a30010 -3488810,a4431d18 -3488814,93a30012 -3488818,a0431d1a -348881c,3c02801d -3488820,3442aa30 -3488824,8c42067c -3488828,10400027 -348882c,8fbf0024 -3488830,8c430130 -3488834,10600025 -3488838,8fb20020 -348883c,12000010 -3488840,24430234 -3488844,93a40010 -3488848,44840000 -3488850,46800020 -3488854,e4400234 -3488858,93a20011 -348885c,44820000 -3488864,46800020 -3488868,e4600004 -348886c,93a20012 -3488870,44820000 -3488878,46800020 -348887c,e4600008 -3488880,12200011 -3488884,8fbf0024 -3488888,93a20014 -348888c,44820000 -3488894,46800020 -3488898,e4600010 -348889c,93a20015 -34888a0,44820000 -34888a8,46800020 -34888ac,e4600014 -34888b0,93a20016 -34888b4,44820000 -34888bc,46800020 -34888c0,e4600018 -34888c4,8fbf0024 -34888c8,8fb20020 -34888cc,8fb1001c -34888d0,8fb00018 -34888d4,3e00008 -34888d8,27bd0028 -34888dc,27bdffe8 -34888e0,afbf0014 -34888e4,3c038041 -34888e8,8c62b1cc -34888ec,24420001 -34888f0,c101fe2 -34888f4,ac62b1cc -34888f8,c10200b -3488900,c102074 -3488908,c1021ae -3488910,8fbf0014 -3488914,3e00008 -3488918,27bd0018 -348891c,27bdffe8 -3488920,afbf0014 -3488924,801025 -3488928,2c430193 -348892c,10600006 -3488930,a02025 -3488934,210c0 -3488938,3c03800f -348893c,34638ff8 -3488940,10000008 -3488944,431021 -3488948,2442fe6e -348894c,21080 -3488950,2403fff8 -3488954,431024 -3488958,3c038040 -348895c,24630c9c -3488960,621021 -3488964,8c450000 -3488968,8c460004 -348896c,3c028000 -3488970,24420df0 -3488974,40f809 -3488978,c53023 -348897c,8fbf0014 -3488980,3e00008 -3488984,27bd0018 -3488988,27bdffe8 -348898c,afbf0014 -3488990,801025 -3488994,a02025 -3488998,a4450000 -348899c,c102247 -34889a0,8c450004 -34889a4,8fbf0014 -34889a8,3e00008 -34889ac,27bd0018 -34889b0,27bdffe8 -34889b4,afbf0014 -34889b8,afb00010 -34889bc,3c028041 -34889c0,2442c23c -34889c4,24450040 -34889c8,94430000 -34889cc,1064000b -34889d0,408025 -34889d4,54600006 -34889d8,24420008 -34889dc,802825 -34889e0,c102262 -34889e4,402025 -34889e8,10000005 -34889ec,2001025 -34889f0,5445fff6 -34889f4,94430000 -34889f8,8025 -34889fc,2001025 -3488a00,8fbf0014 -3488a04,8fb00010 -3488a08,3e00008 -3488a0c,27bd0018 -3488a10,3c03801c -3488a14,346384a0 -3488a18,8c620000 -3488a1c,8c860004 -3488a20,8c4502d0 -3488a24,24a70008 -3488a28,ac4702d0 -3488a2c,3c02db06 -3488a30,24420018 -3488a34,aca20000 -3488a38,aca60004 -3488a3c,8c650000 -3488a40,8c840004 -3488a44,8ca302c0 -3488a48,24660008 -3488a4c,aca602c0 -3488a50,ac620000 -3488a54,3e00008 -3488a58,ac640004 -3488a5c,27bdffe0 -3488a60,afbf0014 -3488a64,f7b40018 -3488a68,3c02800a -3488a6c,3442a78c -3488a70,40f809 -3488a74,46006506 -3488a78,2442000c -3488a7c,2025 -3488a80,1000000a -3488a84,2405000c -3488a88,c4600000 -3488a8c,46140002 -3488a90,e4600000 -3488a94,24630004 -3488a98,5462fffc -3488a9c,c4600000 -3488aa0,24840004 -3488aa4,10850003 -3488aa8,24420010 -3488aac,1000fff6 -3488ab0,2443fff4 -3488ab4,8fbf0014 -3488ab8,d7b40018 -3488abc,3e00008 -3488ac0,27bd0020 -3488ac4,27bdffd8 -3488ac8,afbf0024 -3488acc,afb30020 -3488ad0,afb2001c -3488ad4,afb10018 -3488ad8,afb00014 -3488adc,809825 -3488ae0,a09025 -3488ae4,c08025 -3488ae8,3c118002 -3488aec,26222438 -3488af0,3025 -3488af4,2002825 -3488af8,40f809 -3488afc,2402025 -3488b00,26312554 -3488b04,3025 -3488b08,2002825 -3488b0c,220f809 -3488b10,2402025 -3488b14,2602825 -3488b18,3c028005 -3488b1c,244270c0 -3488b20,40f809 -3488b24,2002025 -3488b28,8fbf0024 -3488b2c,8fb30020 -3488b30,8fb2001c -3488b34,8fb10018 -3488b38,8fb00014 -3488b3c,3e00008 -3488b40,27bd0028 -3488b44,44860000 -3488b48,24020063 -3488b4c,54820005 -3488b50,84a20000 -3488b54,3c028041 -3488b58,c4429e94 -3488b5c,3e00008 -3488b60,46020002 -3488b64,240300f1 -3488b68,54430008 -3488b6c,24030015 -3488b70,24020046 -3488b74,1082000d -3488b78,2402002f -3488b7c,1482000e -3488b80,3c028041 -3488b84,3e00008 -3488b88,c4409e90 -3488b8c,1443000a -3488b90,24020011 -3488b94,90a3001d -3488b98,14620007 -3488b9c,3c028041 -3488ba0,c4429e94 -3488ba4,3e00008 -3488ba8,46020002 -3488bac,3c028041 -3488bb0,3e00008 -3488bb4,c4409e90 -3488bb8,3e00008 -3488bc0,27bdffd8 -3488bc4,afbf001c -3488bc8,afb20018 -3488bcc,afb10014 -3488bd0,afb00010 -3488bd4,f7b40020 -3488bd8,48202 -3488bdc,afa40028 -3488be0,a08825 -3488be4,c09025 -3488be8,4487a000 -3488bec,108600 -3488bf0,108603 -3488bf4,c10226c -3488bf8,42402 -3488bfc,c102284 -3488c00,402025 -3488c04,4406a000 -3488c08,2202825 -3488c0c,c1022d1 -3488c10,2002025 -3488c14,c102297 -3488c18,46000306 -3488c1c,2604ffff -3488c20,2403025 -3488c24,2202825 -3488c28,42600 -3488c2c,c1022b1 -3488c30,42603 -3488c34,8fbf001c -3488c38,8fb20018 -3488c3c,8fb10014 -3488c40,8fb00010 -3488c44,d7b40020 -3488c48,3e00008 -3488c4c,27bd0028 -3488c50,27bdffe0 -3488c54,afbf001c -3488c58,afb10018 -3488c5c,afb00014 -3488c60,3c108041 -3488c64,2610c23c -3488c68,26110040 -3488c6c,a6000000 -3488c70,c1026ed -3488c74,24041e70 -3488c78,ae020004 -3488c7c,26100008 -3488c80,5611fffb -3488c84,a6000000 -3488c88,8fbf001c -3488c8c,8fb10018 -3488c90,8fb00014 -3488c94,3e00008 -3488c98,27bd0020 -3488c9c,3c028041 -3488ca0,a440c23c -3488ca4,2442c23c -3488ca8,a4400008 -3488cac,a4400010 -3488cb0,a4400018 -3488cb4,a4400020 -3488cb8,a4400028 -3488cbc,a4400030 -3488cc0,3e00008 -3488cc4,a4400038 -3488cc8,27bdffe8 -3488ccc,afbf0014 -3488cd0,afb00010 -3488cd4,afa5001c -3488cd8,10a0000e -3488cdc,afa60020 -3488ce0,808025 -3488ce4,93a40023 -3488ce8,50800002 -3488cec,97a40020 -3488cf0,3084ffff -3488cf4,c101ea5 -3488cfc,c101e96 -3488d00,402025 -3488d04,94430004 -3488d08,a6030000 -3488d0c,80420006 -3488d10,a2020002 -3488d14,8fbf0014 -3488d18,8fb00010 -3488d1c,3e00008 -3488d20,27bd0018 -3488d24,27bdffe0 -3488d28,afbf001c -3488d2c,afb00018 -3488d30,808025 -3488d34,30e700ff -3488d38,90c600a5 -3488d3c,c10199c -3488d40,27a40010 -3488d44,8fa50010 -3488d48,8fa60014 -3488d4c,c102332 -3488d50,2002025 -3488d54,8fbf001c -3488d58,8fb00018 -3488d5c,3e00008 -3488d60,27bd0020 -3488d64,27bdffd8 -3488d68,afbf0024 -3488d6c,afb10020 -3488d70,afb0001c -3488d74,808025 -3488d78,a08825 -3488d7c,3c028041 -3488d80,8c429e38 -3488d84,afa20010 -3488d88,3825 -3488d8c,a03025 -3488d90,802825 -3488d94,c102349 -3488d98,27a40010 -3488d9c,3c028041 -3488da0,8c479e98 -3488da4,2203025 -3488da8,2002825 -3488dac,c1022f0 -3488db0,8fa40010 -3488db4,8fbf0024 -3488db8,8fb10020 -3488dbc,8fb0001c -3488dc0,3e00008 -3488dc4,27bd0028 -3488dc8,27bdffd8 -3488dcc,afbf0024 -3488dd0,afb10020 -3488dd4,afb0001c -3488dd8,808025 -3488ddc,9083001d -3488de0,24020011 -3488de4,10620007 -3488de8,a08825 -3488dec,3c028001 -3488df0,24423268 -3488df4,40f809 -3488dfc,10000010 -3488e00,8fbf0024 -3488e04,3c028041 -3488e08,8c429e3c -3488e0c,afa20010 -3488e10,3825 -3488e14,a03025 -3488e18,802825 -3488e1c,c102349 -3488e20,27a40010 -3488e24,3c028041 -3488e28,8c479e98 -3488e2c,2203025 -3488e30,2002825 -3488e34,c1022f0 -3488e38,8fa40010 -3488e3c,8fbf0024 -3488e40,8fb10020 -3488e44,8fb0001c -3488e48,3e00008 -3488e4c,27bd0028 -3488e50,27bdffd8 -3488e54,afbf0024 -3488e58,afb10020 -3488e5c,afb0001c -3488e60,808025 -3488e64,a08825 -3488e68,3c028041 -3488e6c,8c429e40 -3488e70,afa20010 -3488e74,2407004f -3488e78,a03025 -3488e7c,802825 -3488e80,c102349 -3488e84,27a40010 -3488e88,3c028041 -3488e8c,8c479e9c -3488e90,2203025 -3488e94,2002825 -3488e98,c1022f0 -3488e9c,8fa40010 -3488ea0,8fbf0024 -3488ea4,8fb10020 -3488ea8,8fb0001c -3488eac,3e00008 -3488eb0,27bd0028 -3488eb4,27bdffd8 -3488eb8,afbf0024 -3488ebc,afb10020 -3488ec0,afb0001c -3488ec4,808025 -3488ec8,a08825 -3488ecc,3c028041 -3488ed0,8c429e44 -3488ed4,afa20010 -3488ed8,3825 -3488edc,a03025 -3488ee0,802825 -3488ee4,c102349 -3488ee8,27a40010 -3488eec,3c028041 -3488ef0,8c479ea0 -3488ef4,2203025 -3488ef8,2002825 -3488efc,c1022f0 -3488f00,8fa40010 -3488f04,8fbf0024 -3488f08,8fb10020 -3488f0c,8fb0001c -3488f10,3e00008 -3488f14,27bd0028 -3488f18,27bdffd8 -3488f1c,afbf0024 -3488f20,afb10020 -3488f24,afb0001c -3488f28,808025 -3488f2c,a08825 -3488f30,3c028041 -3488f34,8c429e48 -3488f38,afa20010 -3488f3c,2407000c -3488f40,a03025 -3488f44,802825 -3488f48,c102349 -3488f4c,27a40010 -3488f50,3c028041 -3488f54,8c479ea4 -3488f58,2203025 -3488f5c,2002825 -3488f60,c1022f0 -3488f64,8fa40010 -3488f68,8fbf0024 -3488f6c,8fb10020 -3488f70,8fb0001c -3488f74,3e00008 -3488f78,27bd0028 -3488f7c,27bdffd0 -3488f80,afbf002c -3488f84,afb10028 -3488f88,afb00024 -3488f8c,808025 -3488f90,afa00010 -3488f94,afa00014 -3488f98,9482001c -3488f9c,24030001 -3488fa0,14430008 -3488fa4,a08825 -3488fa8,24070015 -3488fac,90a600a5 -3488fb0,802825 -3488fb4,c10199c -3488fb8,27a40010 -3488fbc,10000012 -3488fc0,afa00018 -3488fc4,24030007 -3488fc8,14430008 -3488fcc,24030a0c -3488fd0,24070058 -3488fd4,90a600a5 -3488fd8,802825 -3488fdc,c10199c -3488fe0,27a40010 -3488fe4,10000008 -3488fe8,afa00018 -3488fec,54430006 -3488ff0,afa00018 -3488ff4,3c050010 -3488ff8,34a5010a -3488ffc,c101974 -3489000,27a40010 -3489004,afa00018 -3489008,8fa50010 -348900c,8fa60014 -3489010,c102332 -3489014,27a40018 -3489018,97a20018 -348901c,10400008 -3489020,2203025 -3489024,3c028041 -3489028,8c479e90 -348902c,2002825 -3489030,c1022f0 -3489034,8fa40018 -3489038,10000005 -348903c,8fbf002c -3489040,2002825 -3489044,c1022b1 -3489048,82040141 -348904c,8fbf002c -3489050,8fb10028 -3489054,8fb00024 -3489058,3e00008 -348905c,27bd0030 -3489060,27bdffd0 -3489064,afbf002c -3489068,afb10028 -348906c,afb00024 -3489070,808025 -3489074,afa00010 -3489078,afa00014 -348907c,9482001c -3489080,10400004 -3489084,a08825 -3489088,24030005 -348908c,54430007 -3489090,afa00018 -3489094,24070034 -3489098,922600a5 -348909c,2002825 -34890a0,c10199c -34890a4,27a40010 -34890a8,afa00018 -34890ac,8fa50010 -34890b0,8fa60014 -34890b4,c102332 -34890b8,27a40018 -34890bc,97a20018 -34890c0,10400008 -34890c4,2203025 -34890c8,3c028041 -34890cc,8c479e90 -34890d0,2002825 -34890d4,c1022f0 -34890d8,8fa40018 -34890dc,10000005 -34890e0,8fbf002c -34890e4,2002825 -34890e8,c1022b1 -34890ec,82040147 -34890f0,8fbf002c -34890f4,8fb10028 -34890f8,8fb00024 -34890fc,3e00008 -3489100,27bd0030 -3489104,27bdffd8 -3489108,afbf0024 -348910c,afb10020 -3489110,afb0001c -3489114,808025 -3489118,a08825 -348911c,3c028041 -3489120,8c429e38 -3489124,afa20010 -3489128,2407003e -348912c,a03025 -3489130,802825 -3489134,c102349 -3489138,27a40010 -348913c,3c028041 -3489140,8c479e90 -3489144,2203025 -3489148,2002825 -348914c,c1022f0 -3489150,8fa40010 -3489154,8fbf0024 -3489158,8fb10020 -348915c,8fb0001c -3489160,3e00008 -3489164,27bd0028 -3489168,801025 -348916c,14c00002 -3489170,a6001b -3489174,7000d -3489178,2810 -348917c,3812 -3489180,3c03aaaa -3489184,3463aaab -3489188,e30019 -348918c,1810 -3489190,31882 -3489194,32040 -3489198,831821 -348919c,31840 -34891a0,e31823 -34891a4,44850000 -34891a8,4a10004 -34891ac,468000a1 -34891b0,3c048041 -34891b4,d4809eb0 -34891b8,46201080 -34891bc,462010a0 -34891c0,44860000 -34891c4,4c10004 -34891c8,46800021 -34891cc,3c048041 -34891d0,d4849eb0 -34891d4,46240000 -34891d8,46200020 -34891dc,46001083 -34891e0,3c048041 -34891e4,c4849ea8 -34891e8,46022101 -34891ec,24640001 -34891f0,3c068041 -34891f4,24c69e4c -34891f8,32840 -34891fc,a32821 -3489200,c52821 -3489204,90a50001 -3489208,44850000 -3489210,46800020 -3489214,46040002 -3489218,42840 -348921c,a42821 -3489220,c53021 -3489224,90c50001 -3489228,44853000 -3489230,468031a0 -3489234,46023182 -3489238,46060000 -348923c,3c058041 -3489240,c4a69eac -3489244,4600303e -348924c,45030005 -3489250,46060001 -3489254,4600000d -3489258,44050000 -348925c,10000006 -3489260,30a700ff -3489264,4600000d -3489268,44050000 -348926c,3c068000 -3489270,a62825 -3489274,30a700ff -3489278,3c068041 -348927c,24c69e4c -3489280,32840 -3489284,a32821 -3489288,c52821 -348928c,90a50002 -3489290,44850000 -3489298,46800020 -348929c,46040002 -34892a0,42840 -34892a4,a42821 -34892a8,c53021 -34892ac,90c50002 -34892b0,44853000 -34892b8,468031a0 -34892bc,46023182 -34892c0,46060000 -34892c4,3c058041 -34892c8,c4a69eac -34892cc,4600303e -34892d4,45030005 -34892d8,46060001 -34892dc,4600000d -34892e0,44050000 -34892e4,10000006 -34892e8,30a600ff -34892ec,4600000d -34892f0,44050000 -34892f4,3c068000 -34892f8,a62825 -34892fc,30a600ff -3489300,32840 -3489304,a31821 -3489308,3c088041 -348930c,25089e4c -3489310,681821 -3489314,90650000 -3489318,44850000 -3489320,46800020 -3489324,46040002 -3489328,41840 -348932c,641821 -3489330,681821 -3489334,90630000 -3489338,44832000 -3489340,46802120 -3489344,46022082 -3489348,46020000 -348934c,3c038041 -3489350,c4629eac -3489354,4600103e -348935c,45030005 -3489360,46020001 -3489364,4600000d -3489368,44030000 -348936c,10000006 -3489370,a0430000 -3489374,4600000d -3489378,44030000 -348937c,3c048000 -3489380,641825 -3489384,a0430000 -3489388,a0470001 -348938c,3e00008 -3489390,a0460002 -3489394,3c028011 -3489398,3442a5d0 -348939c,24030140 -34893a0,a4431424 -34893a4,90440032 -34893a8,41840 -34893ac,641821 -34893b0,31900 -34893b4,3e00008 -34893b8,a0430033 -34893bc,24a20002 -34893c0,24a50082 -34893c4,24065700 -34893c8,24070004 -34893cc,9443fffe -34893d0,50660008 -34893d4,24420004 -34893d8,50600006 -34893dc,24420004 -34893e0,94430000 -34893e4,2c630004 -34893e8,54600001 -34893ec,a4470000 -34893f0,24420004 -34893f4,5445fff6 -34893f8,9443fffe -34893fc,3e00008 -3489404,27bdffe8 -3489408,afbf0014 -348940c,c1026ed -3489410,24040400 -3489414,3c038041 -3489418,ac62b1d0 -348941c,3c038041 -3489420,ac62b1d4 -3489424,8fbf0014 -3489428,3e00008 -348942c,27bd0018 -3489430,80820000 -3489434,10400026 -3489438,24870001 -348943c,3c038041 -3489440,8c68b1d0 -3489444,25080400 -3489448,3c038041 -348944c,8c63b1d4 -3489450,5825 -3489454,3c0aff00 -3489458,254a0fff -348945c,30c60fff -3489460,240df000 -3489464,3c098041 -3489468,2529a060 -348946c,240c0001 -3489470,68202b -3489474,54800005 -3489478,a0620000 -348947c,11600014 -3489480,3c028041 -3489484,3e00008 -3489488,ac43b1d4 -348948c,30a40fff -3489490,42300 -3489494,8c620000 -3489498,4a1024 -348949c,441025 -34894a0,4d1024 -34894a4,461025 -34894a8,ac620000 -34894ac,24630004 -34894b0,95220004 -34894b4,a22821 -34894b8,24e70001 -34894bc,80e2ffff -34894c0,1440ffeb -34894c4,1805825 -34894c8,3c028041 -34894cc,ac43b1d4 -34894d0,3e00008 -34894d8,27bdffb8 -34894dc,afbf0044 -34894e0,afbe0040 -34894e4,afb7003c -34894e8,afb60038 -34894ec,afb50034 -34894f0,afb40030 -34894f4,afb3002c -34894f8,afb20028 -34894fc,afb10024 -3489500,afb00020 -3489504,80a825 -3489508,b025 -348950c,9025 -3489510,3c138041 -3489514,2673a060 -3489518,3c178041 -348951c,3c148041 -3489520,3c1e38e3 -3489524,24070012 -3489528,2c03025 -348952c,2602825 -3489530,c101b99 -3489534,2a02025 -3489538,8ef0b1d0 -348953c,8e82b1d4 -3489540,202102b -3489544,50400026 -3489548,26520001 -348954c,37d18e39 -3489550,82020000 -3489554,2002825 -3489558,2442ffe0 -348955c,510018 -3489560,1810 -3489564,31883 -3489568,227c3 -348956c,641823 -3489570,14720016 -3489574,26100004 -3489578,8ca70000 -348957c,73b02 -3489580,510018 -3489584,1810 -3489588,31883 -348958c,641823 -3489590,330c0 -3489594,c33021 -3489598,63040 -348959c,96630006 -34895a0,afa30018 -34895a4,96630004 -34895a8,afa30014 -34895ac,8ca30000 -34895b0,30630fff -34895b4,afa30010 -34895b8,30e70fff -34895bc,463023 -34895c0,2602825 -34895c4,c101c01 -34895c8,2a02025 -34895cc,8e82b1d4 -34895d0,202102b -34895d4,5440ffdf -34895d8,82020000 -34895dc,26520001 -34895e0,24020006 -34895e4,1642ffcf -34895e8,26d60012 -34895ec,3c028041 -34895f0,8c43b1d0 -34895f4,3c028041 -34895f8,ac43b1d4 -34895fc,8fbf0044 -3489600,8fbe0040 -3489604,8fb7003c -3489608,8fb60038 -348960c,8fb50034 -3489610,8fb40030 -3489614,8fb3002c -3489618,8fb20028 -348961c,8fb10024 -3489620,8fb00020 -3489624,3e00008 -3489628,27bd0048 -348962c,3c028041 -3489630,24030001 -3489634,ac43b1dc -3489638,3c038041 -348963c,8c62b1e0 -3489640,2c440006 -3489644,50800001 -3489648,24020005 -348964c,3e00008 -3489650,ac62b1e0 -3489654,27bdffb8 -3489658,afbf0044 -348965c,afbe0040 -3489660,afb6003c -3489664,afb50038 -3489668,afb40034 -348966c,afb30030 -3489670,afb2002c -3489674,afb10028 -3489678,afb00024 -348967c,3a0f025 -3489680,3c028041 -3489684,9442b1d8 -3489688,10400133 -348968c,3a0a825 -3489690,3c02801d -3489694,3442aa30 -3489698,8c42066c -348969c,3c033000 -34896a0,24630483 -34896a4,431024 -34896a8,1440012b -34896ac,808025 -34896b0,3c02801c -34896b4,344284a0 -34896b8,8c430008 -34896bc,3c02800f -34896c0,8c4213ec -34896c4,54620125 -34896c8,2a0e825 -34896cc,3c028011 -34896d0,3442a5d0 -34896d4,8c47135c -34896d8,14e0011f -34896dc,3c02800e -34896e0,3442f1b0 -34896e4,8c420000 -34896e8,30420020 -34896ec,1440011a -34896f0,3c028041 -34896f4,8c43b1dc -34896f8,24020001 -34896fc,1062000a -3489700,3c02801c -3489704,344284a0 -3489708,3c030001 -348970c,431021 -3489710,94430934 -3489714,24020006 -3489718,54620110 -348971c,2a0e825 -3489720,10000009 -3489724,3c038041 -3489728,344284a0 -348972c,3c030001 -3489730,431021 -3489734,94430934 -3489738,24020006 -348973c,14620007 -3489740,3c028041 -3489744,3c038041 -3489748,8c62b1e0 -348974c,3042001f -3489750,ac62b1e0 -3489754,10000022 -3489758,241300ff -348975c,8c42b1e0 -3489760,2c430006 -3489764,1060000a -3489768,2c43006a -348976c,29a00 -3489770,2629823 -3489774,3c02cccc -3489778,3442cccd -348977c,2620019 -3489780,9810 -3489784,139882 -3489788,10000015 -348978c,327300ff -3489790,14600013 -3489794,241300ff -3489798,2c4300ba -348979c,1060000b -34897a0,21a00 -34897a4,621023 -34897a8,24429769 -34897ac,3c03cccc -34897b0,3463cccd -34897b4,430019 -34897b8,1010 -34897bc,29982 -34897c0,139827 -34897c4,10000006 -34897c8,327300ff -34897cc,3c028041 -34897d0,ac40b1dc -34897d4,3c028041 -34897d8,100000df -34897dc,ac40b1e0 -34897e0,3c038041 -34897e4,8c62b1e0 -34897e8,24420001 -34897ec,ac62b1e0 -34897f0,3c028011 -34897f4,3442a5d0 -34897f8,8c4808c4 -34897fc,19000011 -3489800,1001025 -3489804,e05025 -3489808,3c056666 -348980c,24a56667 -3489810,254a0001 -3489814,401825 -3489818,450018 -348981c,2010 -3489820,42083 -3489824,217c3 -3489828,2863000a -348982c,1060fff8 -3489830,821023 -3489834,15400005 -3489838,3c028041 -348983c,10000002 -3489840,240a0001 +3487d90,240200c8 +3487d94,3e00008 +3487d9c,27bdffe8 +3487da0,afbf0014 +3487da4,c1026e9 +3487dac,c101c40 +3487db4,c102502 +3487dbc,c101911 +3487dc4,c102315 +3487dcc,8fbf0014 +3487dd0,3e00008 +3487dd4,27bd0018 +3487dd8,27bdffe8 +3487ddc,afbf0014 +3487de0,c101aa7 +3487de8,c100fa4 +3487df0,c102238 +3487df8,c101cbc +3487e00,c101431 +3487e08,c1018f8 +3487e10,8fbf0014 +3487e14,3e00008 +3487e18,27bd0018 +3487e1c,27bdffe8 +3487e20,afbf0014 +3487e24,afb00010 +3487e28,3c10801c +3487e2c,361084a0 +3487e30,8e040000 +3487e34,c101199 +3487e38,248402a8 +3487e3c,8e040000 +3487e40,c102596 +3487e44,248402a8 +3487e48,8fbf0014 +3487e4c,8fb00010 +3487e50,3e00008 +3487e54,27bd0018 +3487e58,27bdffe8 +3487e5c,afbf0014 +3487e60,c1018ff +3487e68,c1026e4 +3487e70,c102328 +3487e78,c10142b +3487e80,8fbf0014 +3487e84,3e00008 +3487e88,27bd0018 +3487e8c,3c02801c +3487e90,344284a0 +3487e94,3c030001 +3487e98,431021 +3487e9c,84430988 +3487ea0,14600022 +3487ea4,3c02801c +3487ea8,344284a0 +3487eac,3c030001 +3487eb0,431021 +3487eb4,84420992 +3487eb8,14400014 +3487ebc,21840 +3487ec0,3c028011 +3487ec4,3442a5d0 +3487ec8,8c420004 +3487ecc,14400009 +3487ed0,3c028011 +3487ed4,3442a5d0 +3487ed8,8c4300a0 +3487edc,3c020001 +3487ee0,3442c007 +3487ee4,621824 +3487ee8,14600026 +3487eec,24020001 +3487ef0,3c028011 +3487ef4,3442a5d0 +3487ef8,8c4200a0 +3487efc,21382 +3487f00,30420007 +3487f04,3e00008 +3487f08,2102b +3487f0c,621821 +3487f10,3c028011 +3487f14,3442a5d0 +3487f18,8c4200a0 +3487f1c,621006 +3487f20,30420007 +3487f24,3e00008 +3487f28,2102b +3487f2c,344284a0 +3487f30,3c040001 +3487f34,441021 +3487f38,84440992 +3487f3c,1480000a +3487f40,3c028011 +3487f44,24020003 +3487f48,14620007 +3487f4c,3c028011 +3487f50,3442a5d0 +3487f54,8c42009c +3487f58,3c03000c +3487f5c,431024 +3487f60,3e00008 +3487f64,2102b +3487f68,3442a5d0 +3487f6c,9442009c +3487f70,42080 +3487f74,2463ffff +3487f78,832021 +3487f7c,821007 +3487f80,30420001 +3487f84,3e00008 +3487f8c,27bdffe0 +3487f90,afbf001c +3487f94,3c028040 +3487f98,9042088b +3487f9c,10400010 +3487fa0,3c028040 +3487fa4,2406000c +3487fa8,3c028041 +3487fac,8c45b1d4 +3487fb0,c10245b +3487fb4,27a40010 +3487fb8,3c028011 +3487fbc,97a30010 +3487fc0,a4435dd2 +3487fc4,93a30012 +3487fc8,a0435dd4 +3487fcc,97a30010 +3487fd0,a4435dda +3487fd4,93a30012 +3487fd8,a0435ddc +3487fdc,3c028040 +3487fe0,9042088c +3487fe4,10400010 +3487fe8,8fbf001c +3487fec,2406000a +3487ff0,3c028041 +3487ff4,8c45b1d4 +3487ff8,c10245b +3487ffc,27a40010 +3488000,3c028011 +3488004,97a30010 +3488008,a4435dce +348800c,93a30012 +3488010,a0435dd0 +3488014,97a30010 +3488018,a4435dd6 +348801c,93a30012 +3488020,a0435dd8 +3488024,8fbf001c +3488028,3e00008 +348802c,27bd0020 +3488030,3c02801d +3488034,3442aa30 +3488038,8c420678 +348803c,10400063 +3488044,8c430130 +3488048,10600060 +3488050,8c4201c8 +3488054,2c43001f +3488058,1060005c +3488060,27bdffd8 +3488064,afbf0024 +3488068,afb10020 +348806c,afb0001c +3488070,280c0 +3488074,2028023 +3488078,108080 +348807c,2028023 +3488080,108100 +3488084,3c028011 +3488088,2028021 +348808c,3c028040 +3488090,9042088d +3488094,10400018 +3488098,2610572c +348809c,3c118041 +34880a0,24060006 +34880a4,8e25b1d4 +34880a8,c10245b +34880ac,27a40010 +34880b0,93a20010 +34880b4,a2020192 +34880b8,93a20011 +34880bc,a2020193 +34880c0,93a20012 +34880c4,a2020194 +34880c8,8e25b1d4 +34880cc,24060006 +34880d0,24a5000c +34880d4,c10245b +34880d8,27a40010 +34880dc,93a20010 +34880e0,a202019a +34880e4,93a20011 +34880e8,a202019b +34880ec,93a20012 +34880f0,1000000c +34880f4,a202019c +34880f8,3c028040 +34880fc,9044087e +3488100,a2040192 +3488104,2442087e +3488108,90430001 +348810c,a2030193 +3488110,90420002 +3488114,a2020194 +3488118,a204019a +348811c,a203019b +3488120,a202019c +3488124,3c028040 +3488128,9042088e +348812c,10400018 +3488130,3c028040 +3488134,3c118041 +3488138,24060005 +348813c,8e25b1d4 +3488140,c10245b +3488144,27a40010 +3488148,93a20010 +348814c,a2020196 +3488150,93a20011 +3488154,a2020197 +3488158,93a20012 +348815c,a2020198 +3488160,8e25b1d4 +3488164,24060005 +3488168,24a5000a +348816c,c10245b +3488170,27a40010 +3488174,93a20010 +3488178,a202019e +348817c,93a20011 +3488180,a202019f +3488184,93a20012 +3488188,1000000b +348818c,a20201a0 +3488190,90440881 +3488194,a2040196 +3488198,24420881 +348819c,90430001 +34881a0,a2030197 +34881a4,90420002 +34881a8,a2020198 +34881ac,a204019e +34881b0,a203019f +34881b4,a20201a0 +34881b8,8fbf0024 +34881bc,8fb10020 +34881c0,8fb0001c +34881c4,3e00008 +34881c8,27bd0028 +34881cc,3e00008 +34881d4,27bdffd0 +34881d8,afbf002c +34881dc,afb20028 +34881e0,afb10024 +34881e4,afb00020 +34881e8,3c028040 +34881ec,90430884 +34881f0,240200fa +34881f4,14620008 +34881f8,24100001 +34881fc,3c028040 +3488200,24420884 +3488204,90500001 +3488208,90420002 +348820c,2028025 +3488210,321000ff +3488214,10802b +3488218,3c028040 +348821c,90430887 +3488220,240200fa +3488224,14620008 +3488228,24110001 +348822c,3c028040 +3488230,24420887 +3488234,90510001 +3488238,90420002 +348823c,2228825 +3488240,323100ff +3488244,11882b +3488248,3c128041 +348824c,24060009 +3488250,8e45b1d4 +3488254,c10245b +3488258,27a40010 +348825c,8e45b1d4 +3488260,24060009 +3488264,24a50012 +3488268,c10245b +348826c,27a40014 +3488270,24060007 +3488274,8e45b1d4 +3488278,c10245b +348827c,27a40018 +3488280,8e45b1d4 +3488284,24060007 +3488288,24a5000e +348828c,c10245b +3488290,27a4001c +3488294,3c02801c +3488298,344284a0 +348829c,8c421c4c +34882a0,10400064 +34882a4,8fbf002c +34882a8,240500da +34882ac,3c068011 +34882b0,24c65c3c +34882b4,3c088040 +34882b8,3c078040 +34882bc,3c0a8040 +34882c0,254c0887 +34882c4,3c098040 +34882c8,252b0884 +34882cc,8c430130 +34882d0,50600055 +34882d4,8c420124 +34882d8,84430000 +34882dc,54650052 +34882e0,8c420124 +34882e4,8c43016c +34882e8,320c0 +34882ec,832023 +34882f0,42080 +34882f4,832023 +34882f8,42100 +34882fc,2484faf0 +3488300,862021 +3488304,8c4d0170 +3488308,d18c0 +348830c,6d1823 +3488310,31880 +3488314,6d1823 +3488318,31900 +348831c,2463faf0 +3488320,910d088f +3488324,11a0000e +3488328,661821 +348832c,97ae0010 +3488330,a48e0192 +3488334,93ad0012 +3488338,a08d0194 +348833c,a46e0192 +3488340,a06d0194 +3488344,97ae0014 +3488348,a48e019a +348834c,93ad0016 +3488350,a08d019c +3488354,a46e019a +3488358,10000012 +348835c,a06d019c +3488360,12000011 +3488364,90ed0890 +3488368,912f0884 +348836c,a08f0192 +3488370,916e0001 +3488374,a08e0193 +3488378,916d0002 +348837c,a08d0194 +3488380,a06f0192 +3488384,a06e0193 +3488388,a06d0194 +348838c,a08f019a +3488390,a08e019b +3488394,a08d019c +3488398,a06f019a +348839c,a06e019b +34883a0,a06d019c +34883a4,90ed0890 +34883a8,11a0000d +34883ac,97ae0018 +34883b0,a48e0196 +34883b4,93ad001a +34883b8,a08d0198 +34883bc,a46e0196 +34883c0,a06d0198 +34883c4,97ae001c +34883c8,a48e019e +34883cc,93ad001e +34883d0,a08d01a0 +34883d4,a46e019e +34883d8,10000012 +34883dc,a06d01a0 +34883e0,52200011 +34883e4,8c420124 +34883e8,914f0887 +34883ec,a08f0196 +34883f0,918e0001 +34883f4,a08e0197 +34883f8,918d0002 +34883fc,a08d0198 +3488400,a06f0196 +3488404,a06e0197 +3488408,a06d0198 +348840c,a08f019e +3488410,a08e019f +3488414,a08d01a0 +3488418,a06f019e +348841c,a06e019f +3488420,a06d01a0 +3488424,8c420124 +3488428,5440ffa9 +348842c,8c430130 +3488430,8fbf002c +3488434,8fb20028 +3488438,8fb10024 +348843c,8fb00020 +3488440,3e00008 +3488444,27bd0030 +3488448,27bdffd8 +348844c,afbf001c +3488450,f7b40020 +3488454,3c028040 +3488458,9042088f +348845c,1040000a +3488460,46006506 +3488464,24060009 +3488468,3c028041 +348846c,8c45b1d4 +3488470,c10245b +3488474,27a40010 +3488478,93a20010 +348847c,93a30011 +3488480,10000006 +3488484,93a40012 +3488488,3c048040 +348848c,90820884 +3488490,24840884 +3488494,90830001 +3488498,90840002 +348849c,240500fa +34884a0,14450043 +34884a4,642825 +34884a8,14a00041 +34884b0,3c028041 +34884b4,c4409e7c +34884b8,4600a002 +34884bc,3c028041 +34884c0,c4429e80 +34884c4,46020000 +34884c8,3c028041 +34884cc,c4429e84 +34884d0,4600103e +34884d8,45030005 +34884dc,46020001 +34884e0,4600000d +34884e4,44020000 +34884e8,10000006 +34884ec,304200ff +34884f0,4600000d +34884f4,44020000 +34884f8,3c038000 +34884fc,431025 +3488500,304200ff +3488504,3c038041 +3488508,c4609e88 +348850c,4600a002 +3488510,3c038041 +3488514,c4629e80 +3488518,46020000 +348851c,3c038041 +3488520,c4629e84 +3488524,4600103e +348852c,45030005 +3488530,46020001 +3488534,4600000d +3488538,44030000 +348853c,10000006 +3488540,306300ff +3488544,4600000d +3488548,44030000 +348854c,3c048000 +3488550,641825 +3488554,306300ff +3488558,3c048041 +348855c,c4809e8c +3488560,4600a002 +3488564,3c048041 +3488568,c4829e90 +348856c,46020000 +3488570,3c048041 +3488574,c4829e84 +3488578,4600103e +3488580,45030005 +3488584,46020001 +3488588,4600000d +348858c,44040000 +3488590,10000040 +3488594,308400ff +3488598,4600000d +348859c,44040000 +34885a0,3c058000 +34885a4,852025 +34885a8,1000003a +34885ac,308400ff +34885b0,44820000 +34885b8,46800020 +34885bc,46140002 +34885c0,3c028041 +34885c4,c4429e84 +34885c8,4600103e +34885d0,45030005 +34885d4,46020001 +34885d8,4600000d +34885dc,44020000 +34885e0,10000006 +34885e4,304200ff +34885e8,4600000d +34885ec,44020000 +34885f0,3c058000 +34885f4,451025 +34885f8,304200ff +34885fc,44830000 +3488604,46800020 +3488608,46140002 +348860c,3c038041 +3488610,c4629e84 +3488614,4600103e +348861c,45030005 +3488620,46020001 +3488624,4600000d +3488628,44030000 +348862c,10000006 +3488630,306300ff +3488634,4600000d +3488638,44030000 +348863c,3c058000 +3488640,651825 +3488644,306300ff +3488648,44840000 +3488650,46800020 +3488654,46140002 +3488658,3c048041 +348865c,c4829e84 +3488660,4600103e +3488668,45030005 +348866c,46020001 +3488670,4600000d +3488674,44040000 +3488678,10000006 +348867c,308400ff +3488680,4600000d +3488684,44040000 +3488688,3c058000 +348868c,852025 +3488690,308400ff +3488694,21600 +3488698,42200 +348869c,441025 +34886a0,31c00 +34886a4,431025 +34886a8,344200ff +34886ac,8fbf001c +34886b0,d7b40020 +34886b4,3e00008 +34886b8,27bd0028 +34886bc,27bdffd8 +34886c0,afbf0024 +34886c4,afb20020 +34886c8,afb1001c +34886cc,afb00018 +34886d0,3c02801c +34886d4,344284a0 +34886d8,90421cda +34886dc,24030004 +34886e0,10430015 +34886e4,2c430005 +34886e8,50600006 +34886ec,2442fffb +34886f0,24030002 +34886f4,50430008 +34886f8,3c028040 +34886fc,10000013 +3488700,3c028040 +3488704,304200fb +3488708,54400010 +348870c,3c028040 +3488710,10000005 +3488714,3c028040 +3488718,90500891 +348871c,3c028040 +3488720,1000000d +3488724,90510892 +3488728,90500893 +348872c,3c028040 +3488730,10000009 +3488734,90510894 +3488738,3c028040 +348873c,90500895 +3488740,3c028040 +3488744,10000004 +3488748,90510896 +348874c,90500897 +3488750,3c028040 +3488754,90510898 +3488758,2111025 +348875c,1040005b +3488760,8fbf0024 +3488764,3c128041 +3488768,2406000e +348876c,8e45b1d4 +3488770,c10245b +3488774,27a40010 +3488778,2406000c +348877c,8e45b1d4 +3488780,c10245b +3488784,27a40014 +3488788,1200000a +348878c,3c02801c +3488790,344284a0 +3488794,90431cda +3488798,318c0 +348879c,3c02800f +34887a0,431021 +34887a4,97a30010 +34887a8,a4438214 +34887ac,93a30012 +34887b0,a0438216 +34887b4,1220000a +34887b8,3c02801c +34887bc,344284a0 +34887c0,90431cda +34887c4,318c0 +34887c8,3c02800f +34887cc,431021 +34887d0,97a30014 +34887d4,a4438218 +34887d8,93a30016 +34887dc,a043821a +34887e0,12000010 +34887e4,3c02801d +34887e8,3c02801c +34887ec,344284a0 +34887f0,97a30010 +34887f4,a4431cf0 +34887f8,93a30012 +34887fc,a0431cf2 +3488800,97a30010 +3488804,a4431d04 +3488808,93a30012 +348880c,a0431d06 +3488810,97a30010 +3488814,a4431d18 +3488818,93a30012 +348881c,a0431d1a +3488820,3c02801d +3488824,3442aa30 +3488828,8c42067c +348882c,10400027 +3488830,8fbf0024 +3488834,8c430130 +3488838,10600025 +348883c,8fb20020 +3488840,12000010 +3488844,24430234 +3488848,93a40010 +348884c,44840000 +3488854,46800020 +3488858,e4400234 +348885c,93a20011 +3488860,44820000 +3488868,46800020 +348886c,e4600004 +3488870,93a20012 +3488874,44820000 +348887c,46800020 +3488880,e4600008 +3488884,12200011 +3488888,8fbf0024 +348888c,93a20014 +3488890,44820000 +3488898,46800020 +348889c,e4600010 +34888a0,93a20015 +34888a4,44820000 +34888ac,46800020 +34888b0,e4600014 +34888b4,93a20016 +34888b8,44820000 +34888c0,46800020 +34888c4,e4600018 +34888c8,8fbf0024 +34888cc,8fb20020 +34888d0,8fb1001c +34888d4,8fb00018 +34888d8,3e00008 +34888dc,27bd0028 +34888e0,27bdffe8 +34888e4,afbf0014 +34888e8,3c038041 +34888ec,8c62b1d4 +34888f0,24420001 +34888f4,c101fe3 +34888f8,ac62b1d4 +34888fc,c10200c +3488904,c102075 +348890c,c1021af +3488914,8fbf0014 +3488918,3e00008 +348891c,27bd0018 +3488920,27bdffe8 +3488924,afbf0014 +3488928,801025 +348892c,2c430193 +3488930,10600006 +3488934,a02025 +3488938,210c0 +348893c,3c03800f +3488940,34638ff8 +3488944,10000008 +3488948,431021 +348894c,2442fe6e +3488950,21080 +3488954,2403fff8 +3488958,431024 +348895c,3c038040 +3488960,24630c9c +3488964,621021 +3488968,8c450000 +348896c,8c460004 +3488970,3c028000 +3488974,24420df0 +3488978,40f809 +348897c,c53023 +3488980,8fbf0014 +3488984,3e00008 +3488988,27bd0018 +348898c,27bdffe8 +3488990,afbf0014 +3488994,801025 +3488998,a02025 +348899c,a4450000 +34889a0,c102248 +34889a4,8c450004 +34889a8,8fbf0014 +34889ac,3e00008 +34889b0,27bd0018 +34889b4,27bdffe8 +34889b8,afbf0014 +34889bc,afb00010 +34889c0,3c028041 +34889c4,2442c244 +34889c8,24450040 +34889cc,94430000 +34889d0,1064000b +34889d4,408025 +34889d8,54600006 +34889dc,24420008 +34889e0,802825 +34889e4,c102263 +34889e8,402025 +34889ec,10000005 +34889f0,2001025 +34889f4,5445fff6 +34889f8,94430000 +34889fc,8025 +3488a00,2001025 +3488a04,8fbf0014 +3488a08,8fb00010 +3488a0c,3e00008 +3488a10,27bd0018 +3488a14,3c03801c +3488a18,346384a0 +3488a1c,8c620000 +3488a20,8c860004 +3488a24,8c4502d0 +3488a28,24a70008 +3488a2c,ac4702d0 +3488a30,3c02db06 +3488a34,24420018 +3488a38,aca20000 +3488a3c,aca60004 +3488a40,8c650000 +3488a44,8c840004 +3488a48,8ca302c0 +3488a4c,24660008 +3488a50,aca602c0 +3488a54,ac620000 +3488a58,3e00008 +3488a5c,ac640004 +3488a60,27bdffe0 +3488a64,afbf0014 +3488a68,f7b40018 +3488a6c,3c02800a +3488a70,3442a78c +3488a74,40f809 +3488a78,46006506 +3488a7c,2442000c +3488a80,2025 +3488a84,1000000a +3488a88,2405000c +3488a8c,c4600000 +3488a90,46140002 +3488a94,e4600000 +3488a98,24630004 +3488a9c,5462fffc +3488aa0,c4600000 +3488aa4,24840004 +3488aa8,10850003 +3488aac,24420010 +3488ab0,1000fff6 +3488ab4,2443fff4 +3488ab8,8fbf0014 +3488abc,d7b40018 +3488ac0,3e00008 +3488ac4,27bd0020 +3488ac8,27bdffd8 +3488acc,afbf0024 +3488ad0,afb30020 +3488ad4,afb2001c +3488ad8,afb10018 +3488adc,afb00014 +3488ae0,809825 +3488ae4,a09025 +3488ae8,c08025 +3488aec,3c118002 +3488af0,26222438 +3488af4,3025 +3488af8,2002825 +3488afc,40f809 +3488b00,2402025 +3488b04,26312554 +3488b08,3025 +3488b0c,2002825 +3488b10,220f809 +3488b14,2402025 +3488b18,2602825 +3488b1c,3c028005 +3488b20,244270c0 +3488b24,40f809 +3488b28,2002025 +3488b2c,8fbf0024 +3488b30,8fb30020 +3488b34,8fb2001c +3488b38,8fb10018 +3488b3c,8fb00014 +3488b40,3e00008 +3488b44,27bd0028 +3488b48,44860000 +3488b4c,24020063 +3488b50,54820005 +3488b54,84a20000 +3488b58,3c028041 +3488b5c,c4429e98 +3488b60,3e00008 +3488b64,46020002 +3488b68,240300f1 +3488b6c,54430008 +3488b70,24030015 +3488b74,24020046 +3488b78,1082000d +3488b7c,2402002f +3488b80,1482000e +3488b84,3c028041 +3488b88,3e00008 +3488b8c,c4409e94 +3488b90,1443000a +3488b94,24020011 +3488b98,90a3001d +3488b9c,14620007 +3488ba0,3c028041 +3488ba4,c4429e98 +3488ba8,3e00008 +3488bac,46020002 +3488bb0,3c028041 +3488bb4,3e00008 +3488bb8,c4409e94 +3488bbc,3e00008 +3488bc4,27bdffd8 +3488bc8,afbf001c +3488bcc,afb20018 +3488bd0,afb10014 +3488bd4,afb00010 +3488bd8,f7b40020 +3488bdc,48202 +3488be0,afa40028 +3488be4,a08825 +3488be8,c09025 +3488bec,4487a000 +3488bf0,108600 +3488bf4,108603 +3488bf8,c10226d +3488bfc,42402 +3488c00,c102285 +3488c04,402025 +3488c08,4406a000 +3488c0c,2202825 +3488c10,c1022d2 +3488c14,2002025 +3488c18,c102298 +3488c1c,46000306 +3488c20,2604ffff +3488c24,2403025 +3488c28,2202825 +3488c2c,42600 +3488c30,c1022b2 +3488c34,42603 +3488c38,8fbf001c +3488c3c,8fb20018 +3488c40,8fb10014 +3488c44,8fb00010 +3488c48,d7b40020 +3488c4c,3e00008 +3488c50,27bd0028 +3488c54,27bdffe0 +3488c58,afbf001c +3488c5c,afb10018 +3488c60,afb00014 +3488c64,3c108041 +3488c68,2610c244 +3488c6c,26110040 +3488c70,a6000000 +3488c74,c1026ee +3488c78,24041e70 +3488c7c,ae020004 +3488c80,26100008 +3488c84,5611fffb +3488c88,a6000000 +3488c8c,8fbf001c +3488c90,8fb10018 +3488c94,8fb00014 +3488c98,3e00008 +3488c9c,27bd0020 +3488ca0,3c028041 +3488ca4,a440c244 +3488ca8,2442c244 +3488cac,a4400008 +3488cb0,a4400010 +3488cb4,a4400018 +3488cb8,a4400020 +3488cbc,a4400028 +3488cc0,a4400030 +3488cc4,3e00008 +3488cc8,a4400038 +3488ccc,27bdffe8 +3488cd0,afbf0014 +3488cd4,afb00010 +3488cd8,afa5001c +3488cdc,10a0000e +3488ce0,afa60020 +3488ce4,808025 +3488ce8,93a40023 +3488cec,50800002 +3488cf0,97a40020 +3488cf4,3084ffff +3488cf8,c101ea6 +3488d00,c101e97 +3488d04,402025 +3488d08,94430004 +3488d0c,a6030000 +3488d10,80420006 +3488d14,a2020002 +3488d18,8fbf0014 +3488d1c,8fb00010 +3488d20,3e00008 +3488d24,27bd0018 +3488d28,27bdffe0 +3488d2c,afbf001c +3488d30,afb00018 +3488d34,808025 +3488d38,30e700ff +3488d3c,90c600a5 +3488d40,c10199c +3488d44,27a40010 +3488d48,8fa50010 +3488d4c,8fa60014 +3488d50,c102333 +3488d54,2002025 +3488d58,8fbf001c +3488d5c,8fb00018 +3488d60,3e00008 +3488d64,27bd0020 +3488d68,27bdffd8 +3488d6c,afbf0024 +3488d70,afb10020 +3488d74,afb0001c +3488d78,808025 +3488d7c,a08825 +3488d80,3c028041 +3488d84,8c429e3c +3488d88,afa20010 +3488d8c,3825 +3488d90,a03025 +3488d94,802825 +3488d98,c10234a +3488d9c,27a40010 +3488da0,3c028041 +3488da4,8c479e9c +3488da8,2203025 +3488dac,2002825 +3488db0,c1022f1 +3488db4,8fa40010 +3488db8,8fbf0024 +3488dbc,8fb10020 +3488dc0,8fb0001c +3488dc4,3e00008 +3488dc8,27bd0028 +3488dcc,27bdffd8 +3488dd0,afbf0024 +3488dd4,afb10020 +3488dd8,afb0001c +3488ddc,808025 +3488de0,9083001d +3488de4,24020011 +3488de8,10620007 +3488dec,a08825 +3488df0,3c028001 +3488df4,24423268 +3488df8,40f809 +3488e00,10000010 +3488e04,8fbf0024 +3488e08,3c028041 +3488e0c,8c429e40 +3488e10,afa20010 +3488e14,3825 +3488e18,a03025 +3488e1c,802825 +3488e20,c10234a +3488e24,27a40010 +3488e28,3c028041 +3488e2c,8c479e9c +3488e30,2203025 +3488e34,2002825 +3488e38,c1022f1 +3488e3c,8fa40010 +3488e40,8fbf0024 +3488e44,8fb10020 +3488e48,8fb0001c +3488e4c,3e00008 +3488e50,27bd0028 +3488e54,27bdffd8 +3488e58,afbf0024 +3488e5c,afb10020 +3488e60,afb0001c +3488e64,808025 +3488e68,a08825 +3488e6c,3c028041 +3488e70,8c429e44 +3488e74,afa20010 +3488e78,2407004f +3488e7c,a03025 +3488e80,802825 +3488e84,c10234a +3488e88,27a40010 +3488e8c,3c028041 +3488e90,8c479ea0 +3488e94,2203025 +3488e98,2002825 +3488e9c,c1022f1 +3488ea0,8fa40010 +3488ea4,8fbf0024 +3488ea8,8fb10020 +3488eac,8fb0001c +3488eb0,3e00008 +3488eb4,27bd0028 +3488eb8,27bdffd8 +3488ebc,afbf0024 +3488ec0,afb10020 +3488ec4,afb0001c +3488ec8,808025 +3488ecc,a08825 +3488ed0,3c028041 +3488ed4,8c429e48 +3488ed8,afa20010 +3488edc,3825 +3488ee0,a03025 +3488ee4,802825 +3488ee8,c10234a +3488eec,27a40010 +3488ef0,3c028041 +3488ef4,8c479ea4 +3488ef8,2203025 +3488efc,2002825 +3488f00,c1022f1 +3488f04,8fa40010 +3488f08,8fbf0024 +3488f0c,8fb10020 +3488f10,8fb0001c +3488f14,3e00008 +3488f18,27bd0028 +3488f1c,27bdffd8 +3488f20,afbf0024 +3488f24,afb10020 +3488f28,afb0001c +3488f2c,808025 +3488f30,a08825 +3488f34,3c028041 +3488f38,8c429e4c +3488f3c,afa20010 +3488f40,2407000c +3488f44,a03025 +3488f48,802825 +3488f4c,c10234a +3488f50,27a40010 +3488f54,3c028041 +3488f58,8c479ea8 +3488f5c,2203025 +3488f60,2002825 +3488f64,c1022f1 +3488f68,8fa40010 +3488f6c,8fbf0024 +3488f70,8fb10020 +3488f74,8fb0001c +3488f78,3e00008 +3488f7c,27bd0028 +3488f80,27bdffd0 +3488f84,afbf002c +3488f88,afb10028 +3488f8c,afb00024 +3488f90,808025 +3488f94,afa00010 +3488f98,afa00014 +3488f9c,9482001c +3488fa0,24030001 +3488fa4,14430008 +3488fa8,a08825 +3488fac,24070015 +3488fb0,90a600a5 +3488fb4,802825 +3488fb8,c10199c +3488fbc,27a40010 +3488fc0,10000012 +3488fc4,afa00018 +3488fc8,24030007 +3488fcc,14430008 +3488fd0,24030a0c +3488fd4,24070058 +3488fd8,90a600a5 +3488fdc,802825 +3488fe0,c10199c +3488fe4,27a40010 +3488fe8,10000008 +3488fec,afa00018 +3488ff0,54430006 +3488ff4,afa00018 +3488ff8,3c050010 +3488ffc,34a5010a +3489000,c101974 +3489004,27a40010 +3489008,afa00018 +348900c,8fa50010 +3489010,8fa60014 +3489014,c102333 +3489018,27a40018 +348901c,97a20018 +3489020,10400008 +3489024,2203025 +3489028,3c028041 +348902c,8c479e94 +3489030,2002825 +3489034,c1022f1 +3489038,8fa40018 +348903c,10000005 +3489040,8fbf002c +3489044,2002825 +3489048,c1022b2 +348904c,82040141 +3489050,8fbf002c +3489054,8fb10028 +3489058,8fb00024 +348905c,3e00008 +3489060,27bd0030 +3489064,27bdffd0 +3489068,afbf002c +348906c,afb10028 +3489070,afb00024 +3489074,808025 +3489078,afa00010 +348907c,afa00014 +3489080,9482001c +3489084,10400004 +3489088,a08825 +348908c,24030005 +3489090,54430007 +3489094,afa00018 +3489098,24070034 +348909c,922600a5 +34890a0,2002825 +34890a4,c10199c +34890a8,27a40010 +34890ac,afa00018 +34890b0,8fa50010 +34890b4,8fa60014 +34890b8,c102333 +34890bc,27a40018 +34890c0,97a20018 +34890c4,10400008 +34890c8,2203025 +34890cc,3c028041 +34890d0,8c479e94 +34890d4,2002825 +34890d8,c1022f1 +34890dc,8fa40018 +34890e0,10000005 +34890e4,8fbf002c +34890e8,2002825 +34890ec,c1022b2 +34890f0,82040147 +34890f4,8fbf002c +34890f8,8fb10028 +34890fc,8fb00024 +3489100,3e00008 +3489104,27bd0030 +3489108,27bdffd8 +348910c,afbf0024 +3489110,afb10020 +3489114,afb0001c +3489118,808025 +348911c,a08825 +3489120,3c028041 +3489124,8c429e3c +3489128,afa20010 +348912c,2407003e +3489130,a03025 +3489134,802825 +3489138,c10234a +348913c,27a40010 +3489140,3c028041 +3489144,8c479e94 +3489148,2203025 +348914c,2002825 +3489150,c1022f1 +3489154,8fa40010 +3489158,8fbf0024 +348915c,8fb10020 +3489160,8fb0001c +3489164,3e00008 +3489168,27bd0028 +348916c,801025 +3489170,14c00002 +3489174,a6001b +3489178,7000d +348917c,2810 +3489180,3812 +3489184,3c03aaaa +3489188,3463aaab +348918c,e30019 +3489190,1810 +3489194,31882 +3489198,32040 +348919c,831821 +34891a0,31840 +34891a4,e31823 +34891a8,44850000 +34891ac,4a10004 +34891b0,468000a1 +34891b4,3c048041 +34891b8,d4809eb8 +34891bc,46201080 +34891c0,462010a0 +34891c4,44860000 +34891c8,4c10004 +34891cc,46800021 +34891d0,3c048041 +34891d4,d4849eb8 +34891d8,46240000 +34891dc,46200020 +34891e0,46001083 +34891e4,3c048041 +34891e8,c4849eac +34891ec,46022101 +34891f0,24640001 +34891f4,3c068041 +34891f8,24c69e50 +34891fc,32840 +3489200,a32821 +3489204,c52821 +3489208,90a50001 +348920c,44850000 +3489214,46800020 +3489218,46040002 +348921c,42840 +3489220,a42821 +3489224,c53021 +3489228,90c50001 +348922c,44853000 +3489234,468031a0 +3489238,46023182 +348923c,46060000 +3489240,3c058041 +3489244,c4a69eb0 +3489248,4600303e +3489250,45030005 +3489254,46060001 +3489258,4600000d +348925c,44050000 +3489260,10000006 +3489264,30a700ff +3489268,4600000d +348926c,44050000 +3489270,3c068000 +3489274,a62825 +3489278,30a700ff +348927c,3c068041 +3489280,24c69e50 +3489284,32840 +3489288,a32821 +348928c,c52821 +3489290,90a50002 +3489294,44850000 +348929c,46800020 +34892a0,46040002 +34892a4,42840 +34892a8,a42821 +34892ac,c53021 +34892b0,90c50002 +34892b4,44853000 +34892bc,468031a0 +34892c0,46023182 +34892c4,46060000 +34892c8,3c058041 +34892cc,c4a69eb0 +34892d0,4600303e +34892d8,45030005 +34892dc,46060001 +34892e0,4600000d +34892e4,44050000 +34892e8,10000006 +34892ec,30a600ff +34892f0,4600000d +34892f4,44050000 +34892f8,3c068000 +34892fc,a62825 +3489300,30a600ff +3489304,32840 +3489308,a31821 +348930c,3c088041 +3489310,25089e50 +3489314,681821 +3489318,90650000 +348931c,44850000 +3489324,46800020 +3489328,46040002 +348932c,41840 +3489330,641821 +3489334,681821 +3489338,90630000 +348933c,44832000 +3489344,46802120 +3489348,46022082 +348934c,46020000 +3489350,3c038041 +3489354,c4629eb0 +3489358,4600103e +3489360,45030005 +3489364,46020001 +3489368,4600000d +348936c,44030000 +3489370,10000006 +3489374,a0430000 +3489378,4600000d +348937c,44030000 +3489380,3c048000 +3489384,641825 +3489388,a0430000 +348938c,a0470001 +3489390,3e00008 +3489394,a0460002 +3489398,3c028011 +348939c,3442a5d0 +34893a0,24030140 +34893a4,a4431424 +34893a8,90440032 +34893ac,41840 +34893b0,641821 +34893b4,31900 +34893b8,3e00008 +34893bc,a0430033 +34893c0,24a20002 +34893c4,24a50082 +34893c8,24065700 +34893cc,24070004 +34893d0,9443fffe +34893d4,50660008 +34893d8,24420004 +34893dc,50600006 +34893e0,24420004 +34893e4,94430000 +34893e8,2c630004 +34893ec,54600001 +34893f0,a4470000 +34893f4,24420004 +34893f8,5445fff6 +34893fc,9443fffe +3489400,3e00008 +3489408,27bdffe8 +348940c,afbf0014 +3489410,c1026ee +3489414,24040400 +3489418,3c038041 +348941c,ac62b1d8 +3489420,3c038041 +3489424,ac62b1dc +3489428,8fbf0014 +348942c,3e00008 +3489430,27bd0018 +3489434,80820000 +3489438,10400026 +348943c,24870001 +3489440,3c038041 +3489444,8c68b1d8 +3489448,25080400 +348944c,3c038041 +3489450,8c63b1dc +3489454,5825 +3489458,3c0aff00 +348945c,254a0fff +3489460,30c60fff +3489464,240df000 +3489468,3c098041 +348946c,2529a068 +3489470,240c0001 +3489474,68202b +3489478,54800005 +348947c,a0620000 +3489480,11600014 +3489484,3c028041 +3489488,3e00008 +348948c,ac43b1dc +3489490,30a40fff +3489494,42300 +3489498,8c620000 +348949c,4a1024 +34894a0,441025 +34894a4,4d1024 +34894a8,461025 +34894ac,ac620000 +34894b0,24630004 +34894b4,95220004 +34894b8,a22821 +34894bc,24e70001 +34894c0,80e2ffff +34894c4,1440ffeb +34894c8,1805825 +34894cc,3c028041 +34894d0,ac43b1dc +34894d4,3e00008 +34894dc,27bdffb8 +34894e0,afbf0044 +34894e4,afbe0040 +34894e8,afb7003c +34894ec,afb60038 +34894f0,afb50034 +34894f4,afb40030 +34894f8,afb3002c +34894fc,afb20028 +3489500,afb10024 +3489504,afb00020 +3489508,80a825 +348950c,b025 +3489510,9025 +3489514,3c138041 +3489518,2673a068 +348951c,3c178041 +3489520,3c148041 +3489524,3c1e38e3 +3489528,24070012 +348952c,2c03025 +3489530,2602825 +3489534,c101b9a +3489538,2a02025 +348953c,8ef0b1d8 +3489540,8e82b1dc +3489544,202102b +3489548,50400026 +348954c,26520001 +3489550,37d18e39 +3489554,82020000 +3489558,2002825 +348955c,2442ffe0 +3489560,510018 +3489564,1810 +3489568,31883 +348956c,227c3 +3489570,641823 +3489574,14720016 +3489578,26100004 +348957c,8ca70000 +3489580,73b02 +3489584,510018 +3489588,1810 +348958c,31883 +3489590,641823 +3489594,330c0 +3489598,c33021 +348959c,63040 +34895a0,96630006 +34895a4,afa30018 +34895a8,96630004 +34895ac,afa30014 +34895b0,8ca30000 +34895b4,30630fff +34895b8,afa30010 +34895bc,30e70fff +34895c0,463023 +34895c4,2602825 +34895c8,c101c02 +34895cc,2a02025 +34895d0,8e82b1dc +34895d4,202102b +34895d8,5440ffdf +34895dc,82020000 +34895e0,26520001 +34895e4,24020006 +34895e8,1642ffcf +34895ec,26d60012 +34895f0,3c028041 +34895f4,8c43b1d8 +34895f8,3c028041 +34895fc,ac43b1dc +3489600,8fbf0044 +3489604,8fbe0040 +3489608,8fb7003c +348960c,8fb60038 +3489610,8fb50034 +3489614,8fb40030 +3489618,8fb3002c +348961c,8fb20028 +3489620,8fb10024 +3489624,8fb00020 +3489628,3e00008 +348962c,27bd0048 +3489630,3c028041 +3489634,24030001 +3489638,ac43b1e4 +348963c,3c038041 +3489640,8c62b1e8 +3489644,2c440006 +3489648,50800001 +348964c,24020005 +3489650,3e00008 +3489654,ac62b1e8 +3489658,27bdffb8 +348965c,afbf0044 +3489660,afbe0040 +3489664,afb6003c +3489668,afb50038 +348966c,afb40034 +3489670,afb30030 +3489674,afb2002c +3489678,afb10028 +348967c,afb00024 +3489680,3a0f025 +3489684,3c028041 +3489688,9442b1e0 +348968c,10400133 +3489690,3a0a825 +3489694,3c02801d +3489698,3442aa30 +348969c,8c42066c +34896a0,3c033000 +34896a4,24630483 +34896a8,431024 +34896ac,1440012b +34896b0,808025 +34896b4,3c02801c +34896b8,344284a0 +34896bc,8c430008 +34896c0,3c02800f +34896c4,8c4213ec +34896c8,54620125 +34896cc,2a0e825 +34896d0,3c028011 +34896d4,3442a5d0 +34896d8,8c47135c +34896dc,14e0011f +34896e0,3c02800e +34896e4,3442f1b0 +34896e8,8c420000 +34896ec,30420020 +34896f0,1440011a +34896f4,3c028041 +34896f8,8c43b1e4 +34896fc,24020001 +3489700,1062000a +3489704,3c02801c +3489708,344284a0 +348970c,3c030001 +3489710,431021 +3489714,94430934 +3489718,24020006 +348971c,54620110 +3489720,2a0e825 +3489724,10000009 +3489728,3c038041 +348972c,344284a0 +3489730,3c030001 +3489734,431021 +3489738,94430934 +348973c,24020006 +3489740,14620007 +3489744,3c028041 +3489748,3c038041 +348974c,8c62b1e8 +3489750,3042001f +3489754,ac62b1e8 +3489758,10000022 +348975c,241300ff +3489760,8c42b1e8 +3489764,2c430006 +3489768,1060000a +348976c,2c43006a +3489770,29a00 +3489774,2629823 +3489778,3c02cccc +348977c,3442cccd +3489780,2620019 +3489784,9810 +3489788,139882 +348978c,10000015 +3489790,327300ff +3489794,14600013 +3489798,241300ff +348979c,2c4300ba +34897a0,1060000b +34897a4,21a00 +34897a8,621023 +34897ac,24429769 +34897b0,3c03cccc +34897b4,3463cccd +34897b8,430019 +34897bc,1010 +34897c0,29982 +34897c4,139827 +34897c8,10000006 +34897cc,327300ff +34897d0,3c028041 +34897d4,ac40b1e4 +34897d8,3c028041 +34897dc,100000df +34897e0,ac40b1e8 +34897e4,3c038041 +34897e8,8c62b1e8 +34897ec,24420001 +34897f0,ac62b1e8 +34897f4,3c028011 +34897f8,3442a5d0 +34897fc,8c4808c4 +3489800,19000011 +3489804,1001025 +3489808,e05025 +348980c,3c056666 +3489810,24a56667 +3489814,254a0001 +3489818,401825 +348981c,450018 +3489820,2010 +3489824,42083 +3489828,217c3 +348982c,2863000a +3489830,1060fff8 +3489834,821023 +3489838,15400005 +348983c,3c028041 +3489840,10000002 3489844,240a0001 -3489848,3c028041 -348984c,9445b17a -3489850,18a00010 -3489854,a01025 -3489858,3c066666 -348985c,24c66667 -3489860,24e70001 -3489864,401825 -3489868,460018 -348986c,2010 -3489870,42083 -3489874,217c3 -3489878,2863000a -348987c,1060fff8 -3489880,821023 -3489884,54e00005 -3489888,1473821 -348988c,10000002 -3489890,24070001 +3489848,240a0001 +348984c,3c028041 +3489850,9445b182 +3489854,18a00010 +3489858,a01025 +348985c,3c066666 +3489860,24c66667 +3489864,24e70001 +3489868,401825 +348986c,460018 +3489870,2010 +3489874,42083 +3489878,217c3 +348987c,2863000a +3489880,1060fff8 +3489884,821023 +3489888,54e00005 +348988c,1473821 +3489890,10000002 3489894,24070001 -3489898,1473821 -348989c,24f40001 -34898a0,3c028041 -34898a4,2442a060 -34898a8,94430004 -34898ac,740018 -34898b0,2012 -34898b4,3c038041 -34898b8,2463a040 -34898bc,94660004 -34898c0,862021 -34898c4,497c2 -34898c8,2449021 -34898cc,129043 -34898d0,129023 -34898d4,265200a0 -34898d8,94420006 -34898dc,44820000 -34898e4,46800021 -34898e8,3c028041 -34898ec,d4469eb8 -34898f0,46260002 -34898f4,3c028041 -34898f8,d4429ec0 -34898fc,46201001 -3489900,3c028041 -3489904,d4449ec8 -3489908,46240000 -348990c,4620000d -3489910,44060000 -3489914,94620006 -3489918,44820000 -3489920,46800021 -3489924,46260002 -3489928,46201081 -348992c,3c028041 -3489930,d4409ed0 -3489934,46201080 -3489938,46241080 -348993c,4620100d -3489940,44110000 -3489944,24e20009 -3489948,210c2 -348994c,210c0 -3489950,3a2e823 -3489954,27a40020 -3489958,941021 -348995c,19400015 -3489960,a0400000 -3489964,2549ffff -3489968,894821 -348996c,806025 -3489970,3c0b6666 -3489974,256b6667 -3489978,10b0018 -348997c,1810 -3489980,31883 -3489984,817c3 -3489988,621823 -348998c,31080 -3489990,431021 -3489994,21040 -3489998,1021023 -348999c,24420030 -34899a0,a1220000 -34899a4,604025 -34899a8,1201025 -34899ac,144cfff2 -34899b0,2529ffff -34899b4,8a1021 -34899b8,2403002f -34899bc,a0430000 -34899c0,147102a -34899c4,10400012 -34899c8,873821 -34899cc,8a5021 -34899d0,3c086666 -34899d4,25086667 -34899d8,a80018 -34899dc,1810 -34899e0,31883 -34899e4,517c3 -34899e8,621823 -34899ec,31080 -34899f0,431021 -34899f4,21040 -34899f8,a21023 -34899fc,24420030 -3489a00,a0e20000 -3489a04,24e7ffff -3489a08,14eafff3 -3489a0c,602825 -3489a10,8e020008 -3489a14,24430008 -3489a18,ae030008 -3489a1c,3c03de00 -3489a20,ac430000 -3489a24,3c038041 -3489a28,2463a0b0 -3489a2c,ac430004 -3489a30,8e020008 -3489a34,24430008 -3489a38,ae030008 -3489a3c,3c03e700 -3489a40,ac430000 -3489a44,ac400004 -3489a48,8e020008 -3489a4c,24430008 -3489a50,ae030008 -3489a54,3c03fc11 -3489a58,34639623 -3489a5c,ac430000 -3489a60,3c03ff2f -3489a64,3463ffff -3489a68,ac430004 -3489a6c,8e030008 -3489a70,24620008 -3489a74,ae020008 -3489a78,3c16fa00 -3489a7c,ac760000 -3489a80,3c02dad3 -3489a84,24420b00 -3489a88,2621025 -3489a8c,ac620004 -3489a90,c10250c -3489a94,2402825 -3489a98,3c028041 -3489a9c,9442a064 -3489aa0,540018 -3489aa4,a012 -3489aa8,292a021 -3489aac,8e020008 -3489ab0,24430008 -3489ab4,ae030008 -3489ab8,ac560000 -3489abc,3c03f4ec -3489ac0,24633000 -3489ac4,2639825 -3489ac8,ac530004 -3489acc,3c028041 -3489ad0,8c46b1e0 -3489ad4,63042 -3489ad8,24070001 -3489adc,30c6000f -3489ae0,3c128041 -3489ae4,2645a040 -3489ae8,c101b99 -3489aec,2002025 -3489af0,2645a040 -3489af4,94a20006 -3489af8,afa20018 -3489afc,94a20004 -3489b00,afa20014 -3489b04,afb10010 -3489b08,2803825 -3489b0c,3025 -3489b10,c101c01 -3489b14,2002025 -3489b18,c102536 -3489b1c,2002025 -3489b20,8e020008 -3489b24,24430008 -3489b28,ae030008 -3489b2c,3c03e900 -3489b30,ac430000 -3489b34,ac400004 -3489b38,8e020008 -3489b3c,24430008 -3489b40,ae030008 -3489b44,3c03df00 -3489b48,ac430000 -3489b4c,ac400004 -3489b50,10000002 -3489b54,2a0e825 +3489898,24070001 +348989c,1473821 +34898a0,24f40001 +34898a4,3c028041 +34898a8,2442a068 +34898ac,94430004 +34898b0,740018 +34898b4,2012 +34898b8,3c038041 +34898bc,2463a048 +34898c0,94660004 +34898c4,862021 +34898c8,497c2 +34898cc,2449021 +34898d0,129043 +34898d4,129023 +34898d8,265200a0 +34898dc,94420006 +34898e0,44820000 +34898e8,46800021 +34898ec,3c028041 +34898f0,d4469ec0 +34898f4,46260002 +34898f8,3c028041 +34898fc,d4429ec8 +3489900,46201001 +3489904,3c028041 +3489908,d4449ed0 +348990c,46240000 +3489910,4620000d +3489914,44060000 +3489918,94620006 +348991c,44820000 +3489924,46800021 +3489928,46260002 +348992c,46201081 +3489930,3c028041 +3489934,d4409ed8 +3489938,46201080 +348993c,46241080 +3489940,4620100d +3489944,44110000 +3489948,24e20009 +348994c,210c2 +3489950,210c0 +3489954,3a2e823 +3489958,27a40020 +348995c,941021 +3489960,19400015 +3489964,a0400000 +3489968,2549ffff +348996c,894821 +3489970,806025 +3489974,3c0b6666 +3489978,256b6667 +348997c,10b0018 +3489980,1810 +3489984,31883 +3489988,817c3 +348998c,621823 +3489990,31080 +3489994,431021 +3489998,21040 +348999c,1021023 +34899a0,24420030 +34899a4,a1220000 +34899a8,604025 +34899ac,1201025 +34899b0,144cfff2 +34899b4,2529ffff +34899b8,8a1021 +34899bc,2403002f +34899c0,a0430000 +34899c4,147102a +34899c8,10400012 +34899cc,873821 +34899d0,8a5021 +34899d4,3c086666 +34899d8,25086667 +34899dc,a80018 +34899e0,1810 +34899e4,31883 +34899e8,517c3 +34899ec,621823 +34899f0,31080 +34899f4,431021 +34899f8,21040 +34899fc,a21023 +3489a00,24420030 +3489a04,a0e20000 +3489a08,24e7ffff +3489a0c,14eafff3 +3489a10,602825 +3489a14,8e020008 +3489a18,24430008 +3489a1c,ae030008 +3489a20,3c03de00 +3489a24,ac430000 +3489a28,3c038041 +3489a2c,2463a0b8 +3489a30,ac430004 +3489a34,8e020008 +3489a38,24430008 +3489a3c,ae030008 +3489a40,3c03e700 +3489a44,ac430000 +3489a48,ac400004 +3489a4c,8e020008 +3489a50,24430008 +3489a54,ae030008 +3489a58,3c03fc11 +3489a5c,34639623 +3489a60,ac430000 +3489a64,3c03ff2f +3489a68,3463ffff +3489a6c,ac430004 +3489a70,8e030008 +3489a74,24620008 +3489a78,ae020008 +3489a7c,3c16fa00 +3489a80,ac760000 +3489a84,3c02dad3 +3489a88,24420b00 +3489a8c,2621025 +3489a90,ac620004 +3489a94,c10250d +3489a98,2402825 +3489a9c,3c028041 +3489aa0,9442a06c +3489aa4,540018 +3489aa8,a012 +3489aac,292a021 +3489ab0,8e020008 +3489ab4,24430008 +3489ab8,ae030008 +3489abc,ac560000 +3489ac0,3c03f4ec +3489ac4,24633000 +3489ac8,2639825 +3489acc,ac530004 +3489ad0,3c028041 +3489ad4,8c46b1e8 +3489ad8,63042 +3489adc,24070001 +3489ae0,30c6000f +3489ae4,3c128041 +3489ae8,2645a048 +3489aec,c101b9a +3489af0,2002025 +3489af4,2645a048 +3489af8,94a20006 +3489afc,afa20018 +3489b00,94a20004 +3489b04,afa20014 +3489b08,afb10010 +3489b0c,2803825 +3489b10,3025 +3489b14,c101c02 +3489b18,2002025 +3489b1c,c102537 +3489b20,2002025 +3489b24,8e020008 +3489b28,24430008 +3489b2c,ae030008 +3489b30,3c03e900 +3489b34,ac430000 +3489b38,ac400004 +3489b3c,8e020008 +3489b40,24430008 +3489b44,ae030008 +3489b48,3c03df00 +3489b4c,ac430000 +3489b50,ac400004 +3489b54,10000002 3489b58,2a0e825 -3489b5c,3c0e825 -3489b60,8fbf0044 -3489b64,8fbe0040 -3489b68,8fb6003c -3489b6c,8fb50038 -3489b70,8fb40034 -3489b74,8fb30030 -3489b78,8fb2002c -3489b7c,8fb10028 -3489b80,8fb00024 -3489b84,3e00008 -3489b88,27bd0048 -3489b8c,3c028040 -3489b90,a040307c -3489b94,3c028040 -3489b98,3e00008 -3489b9c,ac403080 -3489ba0,3c038041 -3489ba4,3c028050 -3489ba8,24420000 -3489bac,3e00008 -3489bb0,ac62b1e4 -3489bb4,3082000f -3489bb8,10400009 -3489bbc,3c038041 -3489bc0,417c3 -3489bc4,21702 -3489bc8,821821 -3489bcc,3063000f -3489bd0,431023 -3489bd4,24420010 -3489bd8,822021 -3489bdc,3c038041 -3489be0,8c62b1e4 -3489be4,442021 -3489be8,3e00008 -3489bec,ac64b1e4 -3489bf0,27bdffe8 -3489bf4,afbf0014 -3489bf8,afb00010 -3489bfc,808025 -3489c00,c1026ed -3489c04,8c840008 -3489c08,402025 -3489c0c,ae020000 -3489c10,8e060008 -3489c14,3c028000 -3489c18,24420df0 -3489c1c,40f809 -3489c20,8e050004 -3489c24,8fbf0014 -3489c28,8fb00010 -3489c2c,3e00008 -3489c30,27bd0018 -3489c34,33c2 -3489c38,664399c4 -3489c3c,cc45ffc6 -3489c40,ff47ffc8 -3489c44,ff49e0ca -3489c48,c24ba3cc -3489c4c,854d660d -3489c50,440f2200 -3489c54,85d1a352 -3489c58,c2d3e045 -3489c5c,1010101 +3489b5c,2a0e825 +3489b60,3c0e825 +3489b64,8fbf0044 +3489b68,8fbe0040 +3489b6c,8fb6003c +3489b70,8fb50038 +3489b74,8fb40034 +3489b78,8fb30030 +3489b7c,8fb2002c +3489b80,8fb10028 +3489b84,8fb00024 +3489b88,3e00008 +3489b8c,27bd0048 +3489b90,3c028040 +3489b94,a040307c +3489b98,3c028040 +3489b9c,3e00008 +3489ba0,ac403080 +3489ba4,3c038041 +3489ba8,3c028050 +3489bac,24420000 +3489bb0,3e00008 +3489bb4,ac62b1ec +3489bb8,3082000f +3489bbc,10400009 +3489bc0,3c038041 +3489bc4,417c3 +3489bc8,21702 +3489bcc,821821 +3489bd0,3063000f +3489bd4,431023 +3489bd8,24420010 +3489bdc,822021 +3489be0,3c038041 +3489be4,8c62b1ec +3489be8,442021 +3489bec,3e00008 +3489bf0,ac64b1ec +3489bf4,27bdffe8 +3489bf8,afbf0014 +3489bfc,afb00010 +3489c00,808025 +3489c04,c1026ee +3489c08,8c840008 +3489c0c,402025 +3489c10,ae020000 +3489c14,8e060008 +3489c18,3c028000 +3489c1c,24420df0 +3489c20,40f809 +3489c24,8e050004 +3489c28,8fbf0014 +3489c2c,8fb00010 +3489c30,3e00008 +3489c34,27bd0018 +3489c38,33c2 +3489c3c,664399c4 +3489c40,cc45ffc6 +3489c44,ff47ffc8 +3489c48,ff49e0ca +3489c4c,c24ba3cc +3489c50,854d660d +3489c54,440f2200 +3489c58,85d1a352 +3489c5c,c2d3e045 3489c60,1010101 3489c64,1010101 3489c68,1010101 3489c6c,1010101 -3489c88,1010000 -3489c90,1010101 -3489c94,1000101 -3489c98,10101 -3489c9c,10000 -3489ca0,2b242525 -3489ca4,26262626 -3489ca8,27272727 +3489c70,1010101 +3489c8c,1010000 +3489c94,1010101 +3489c98,1000101 +3489c9c,10101 +3489ca0,10000 +3489ca4,2b242525 +3489ca8,26262626 3489cac,27272727 -3489cb0,500080d -3489cb4,1051508 -3489cb8,d01052a -3489cbc,80d0127 -3489cc0,f080b01 -3489cc4,4d510b02 -3489ccc,97ff6350 -3489cd0,45ff5028 -3489cd4,57456397 -3489cd8,ff5e45ff -3489cdc,9f006545 -3489ce0,ff63ff6c -3489ce4,45fff063 -3489ce8,7345ffff -3489cec,ff503aff -3489cf0,ffff573a -3489cf4,ffffff5e -3489cf8,3affffff -3489cfc,653affff -3489d00,ff6c3aff -3489d04,ffff733a -3489d08,5a0c00 -3489d0c,720c0096 -3489d10,c009618 -3489d14,1652a00 -3489d18,4e2a005a -3489d1c,2a000000 -3489d20,c004e00 -3489d24,c015a00 -3489d28,c026600 -3489d2c,c037200 -3489d30,c047e00 -3489d34,c058a00 -3489d38,c064e0c -3489d3c,75a0c -3489d40,c09660c -3489d44,a720c -3489d48,c0c7e0c -3489d4c,c0d8a0c -3489d50,c0e4e18 -3489d54,c0f5a18 -3489d58,c106618 -3489d5c,c117218 -3489d60,c127e18 -3489d64,c138a18 -3489d68,ffff +3489cb0,27272727 +3489cb4,500080d +3489cb8,1051508 +3489cbc,d01052a +3489cc0,80d0127 +3489cc4,f080b01 +3489cc8,4d510b02 +3489cd0,97ff6350 +3489cd4,45ff5028 +3489cd8,57456397 +3489cdc,ff5e45ff +3489ce0,9f006545 +3489ce4,ff63ff6c +3489ce8,45fff063 +3489cec,7345ffff +3489cf0,ff503aff +3489cf4,ffff573a +3489cf8,ffffff5e +3489cfc,3affffff +3489d00,653affff +3489d04,ff6c3aff +3489d08,ffff733a +3489d0c,5a0c00 +3489d10,720c0096 +3489d14,c009618 +3489d18,1652a00 +3489d1c,4e2a005a +3489d20,2a000000 +3489d24,c004e00 +3489d28,c015a00 +3489d2c,c026600 +3489d30,c037200 +3489d34,c047e00 +3489d38,c058a00 +3489d3c,c064e0c +3489d40,75a0c +3489d44,c09660c +3489d48,a720c +3489d4c,c0c7e0c +3489d50,c0d8a0c +3489d54,c0e4e18 +3489d58,c0f5a18 +3489d5c,c106618 +3489d60,c117218 +3489d64,c127e18 +3489d68,c138a18 3489d6c,ffff 3489d70,ffff 3489d74,ffff @@ -10291,1699 +10292,1698 @@ ef4f9c,0 3489d94,ffff 3489d98,ffff 3489d9c,ffff -3489da0,c3b7e2a -3489da4,c3c8a2a -3489da8,c3d962a -3489dac,ffff -3489db0,c3e7e36 -3489db4,b3f8b37 -3489db8,b409737 -3489dbc,ffff -3489dc0,c417e42 -3489dc4,c428a42 -3489dc8,c439642 -3489dcc,ffff -3489dd0,c447e4f -3489dd4,c458a4f -3489dd8,c46964f -3489ddc,ffff -3489de0,c149600 -3489de4,ffff -3489de8,2c061b31 -3489dec,2c072931 -3489df0,2c083731 -3489df4,2a096f51 -3489df8,2c0a722a -3489dfc,ffff -3489e00,2c00370a -3489e04,2c01371a -3489e08,2c022922 -3489e0c,2c031b1a -3489e10,2c041b0a -3489e14,2c052902 -3489e18,ffff +3489da0,ffff +3489da4,c3b7e2a +3489da8,c3c8a2a +3489dac,c3d962a +3489db0,ffff +3489db4,c3e7e36 +3489db8,b3f8b37 +3489dbc,b409737 +3489dc0,ffff +3489dc4,c417e42 +3489dc8,c428a42 +3489dcc,c439642 +3489dd0,ffff +3489dd4,c447e4f +3489dd8,c458a4f +3489ddc,c46964f +3489de0,ffff +3489de4,c149600 +3489de8,ffff +3489dec,2c061b31 +3489df0,2c072931 +3489df4,2c083731 +3489df8,2a096f51 +3489dfc,2c0a722a +3489e00,ffff +3489e04,2c00370a +3489e08,2c01371a +3489e0c,2c022922 +3489e10,2c031b1a +3489e14,2c041b0a +3489e18,2c052902 3489e1c,ffff -3489e20,8040a080 -3489e24,8040a070 -3489e28,8040a000 -3489e2c,c8ff6482 -3489e30,82ffff64 -3489e34,64ff5aff -3489e38,bd1400 -3489e3c,aa0200 -3489e40,bd1300 -3489e44,15c6300 -3489e48,de2f00 -3489e4c,e01010e0 +3489e20,ffff +3489e24,8040a088 +3489e28,8040a078 +3489e2c,8040a008 +3489e30,c8ff6482 +3489e34,82ffff64 +3489e38,64ff5aff +3489e3c,bd1400 +3489e40,aa0200 +3489e44,bd1300 +3489e48,15c6300 +3489e4c,de2f00 3489e50,e01010e0 -3489e54,1010e0e0 +3489e54,e01010e0 3489e58,1010e0e0 -3489e5c,10e0e010 -3489e60,10000000 -3489e64,4d510000 -3489e68,4e6f726d -3489e6c,616c0000 -3489e70,bdcccccd -3489e74,3dcccccd -3489e78,43510000 -3489e7c,41100000 -3489e80,4f000000 -3489e84,42080000 -3489e88,c20c0000 -3489e8c,420c0000 -3489e90,3f800000 -3489e94,3f000000 -3489e98,41c80000 -3489e9c,3fa00000 -3489ea0,40000000 -3489ea4,40200000 -3489ea8,3f800000 -3489eac,4f000000 -3489eb0,41f00000 -3489eb8,3ff80000 -3489ec0,406e0000 -3489ec8,3ff00000 -3489ed0,40080000 -3489ed8,80409c34 -3489edc,10203 -3489ee0,4050607 -3489ee4,ffffffff -3489ee8,ffff0000 -3489eec,ff00ff -3489ef0,3c000064 -3489ef4,ffff8200 -3489ef8,c832ffc8 -3489efc,c8000000 -3489f00,104465 -3489f04,6b750000 -3489f0c,110446f -3489f10,646f6e67 -3489f14,6f000000 -3489f18,2104a61 -3489f1c,62750000 -3489f24,3d0466f -3489f28,72657374 -3489f30,4d04669 -3489f34,72650000 -3489f3c,5d05761 -3489f40,74657200 -3489f48,7d05368 -3489f4c,61646f77 -3489f54,6d05370 -3489f58,69726974 -3489f60,890426f -3489f64,74570000 -3489f6c,9104963 -3489f70,65000000 -3489f78,ca04765 -3489f7c,7275646f -3489f84,b804754 -3489f88,47000000 -3489f90,dc04761 -3489f94,6e6f6e00 -3489f9c,2 -3489fa4,3f800000 -3489fb0,1 -3489fb4,30006 -3489fb8,70009 -3489fbc,b000e -3489fc0,f0010 -3489fc4,110019 -3489fc8,1a002b -3489fcc,2c002e -3489fd0,300032 -3489fd4,35003c -3489fd8,400041 -3489fdc,460051 -3489fe0,540109 -3489fe4,10b010c -3489fe8,10e010f -3489fec,1100113 -3489ff4,100010 -3489ff8,a0301 -3489ffc,1000000 -348a004,100010 -348a008,20002 -348a00c,2000000 -348a014,80008 -348a018,a0301 -348a01c,1000000 -348a024,100010 -348a028,30301 -348a02c,1000000 -348a034,100018 -348a038,10301 -348a03c,1000000 -348a044,100010 -348a048,100301 -348a04c,1000000 -348a054,200020 -348a058,10302 -348a05c,2000000 -348a064,8000e -348a068,5f0301 -348a06c,1000000 -348a074,180018 -348a078,140003 -348a07c,4000000 -348a084,200020 -348a088,5a0003 -348a08c,4000000 -348a094,100010 -348a098,60301 -348a09c,1000000 -348a0a4,100010 -348a0a8,30003 -348a0ac,4000000 -348a0b0,e7000000 -348a0b8,d9000000 -348a0c0,ed000000 -348a0c4,5003c0 -348a0c8,ef002cf0 -348a0cc,504244 -348a0d0,df000000 -348a0ec,4d8e0032 -348a0f0,ce2001 -348a0f4,80407ce8 -348a0f8,80407778 -348a0fc,ffffffff -348a100,4d8c0034 -348a104,bb1201 -348a108,80407b20 -348a10c,80407778 -348a110,ffffffff -348a114,4d090033 -348a118,d92801 -348a11c,80407b20 -348a120,80407778 -348a124,ffffffff -348a128,53030031 -348a12c,e93500 -348a130,80407b20 -348a134,80407778 -348a138,ffffffff -348a13c,53060030 -348a140,e73300 -348a144,80407b20 -348a148,80407778 -348a14c,ffffffff -348a150,530e0035 -348a154,e83400 -348a158,80407b20 -348a15c,80407778 -348a160,ffffffff -348a164,4d000037 -348a168,c71b01 -348a16c,80407b20 -348a170,80407778 -348a174,ffffffff -348a178,530a0036 -348a17c,dd2d00 -348a180,80407b20 -348a184,80407778 -348a188,ffffffff -348a18c,530b004f -348a190,dd2e00 -348a194,80407b20 -348a198,80407778 -348a19c,ffffffff -348a1a0,530f0039 -348a1a4,ea3600 -348a1a8,80407b20 -348a1ac,80407778 -348a1b0,ffffffff -348a1b4,53230069 -348a1b8,ef3b00 -348a1bc,80407b20 -348a1c0,804079dc -348a1c4,ffffffff -348a1c8,5308003a -348a1cc,de2f00 -348a1d0,80407b20 -348a1d4,80407778 -348a1d8,ffffffff -348a1dc,53110038 -348a1e0,f64100 -348a1e4,80407b20 -348a1e8,80407778 -348a1ec,ffffffff -348a1f0,532f0002 -348a1f4,1095e00 -348a1f8,80407b20 -348a1fc,80407778 -348a200,ffffffff -348a204,53140042 -348a208,c60100 -348a20c,80407b20 -348a210,80407778 -348a214,ffffffff -348a218,53150043 -348a21c,eb3800 -348a220,80407b20 -348a224,80407778 -348a228,ffffffff -348a22c,53160044 -348a230,eb3700 -348a234,80407b20 -348a238,80407778 -348a23c,ffffffff -348a240,53170045 -348a244,eb3900 -348a248,80407b20 -348a24c,80407778 -348a250,ffffffff -348a254,53180046 -348a258,c60100 -348a25c,80407b20 -348a260,80407778 -348a264,ffffffff -348a268,531a0098 -348a26c,df3000 -348a270,80407b20 -348a274,80407778 -348a278,ffffffff -348a27c,531b0099 -348a280,10b4500 -348a284,80407d28 -348a288,80407778 -348a28c,ffffffff -348a290,53100048 -348a294,f33e01 -348a298,80407b20 -348a29c,80407778 -348a2a0,ffffffff -348a2a4,53250010 -348a2a8,1364f00 -348a2ac,80407b20 -348a2b0,80407778 -348a2b4,ffffffff -348a2b8,53260011 -348a2bc,1353200 -348a2c0,80407b20 -348a2c4,80407778 -348a2c8,ffffffff -348a2cc,5322000b -348a2d0,1094400 -348a2d4,80407b20 -348a2d8,80407778 -348a2dc,ffffffff -348a2e0,53240012 -348a2e4,1343100 -348a2e8,80407b20 -348a2ec,80407778 -348a2f0,ffffffff -348a2f4,53270013 -348a2f8,1375000 -348a2fc,80407b20 -348a300,80407778 -348a304,ffffffff -348a308,532b0017 -348a30c,1385100 -348a310,80407b20 -348a314,80407778 -348a318,ffffffff -348a31c,532d9001 -348a320,da2900 -348a324,80407b20 -348a328,80407778 -348a32c,ffffffff -348a330,532e000b -348a334,1094400 -348a338,80407b20 -348a33c,80407778 -348a340,ffffffff -348a344,53300003 -348a348,1415400 -348a34c,80407b20 -348a350,80407778 -348a354,ffffffff -348a358,53310004 -348a35c,1405300 -348a360,80407b20 -348a364,80407778 -348a368,ffffffff -348a36c,53320005 -348a370,f54000 -348a374,80407b20 -348a378,80407778 -348a37c,ffffffff -348a380,53330008 -348a384,1435600 -348a388,80407b20 -348a38c,80407778 -348a390,ffffffff -348a394,53340009 -348a398,1465700 -348a39c,80407b20 -348a3a0,80407778 -348a3a4,ffffffff -348a3a8,5335000d -348a3ac,1495a00 -348a3b0,80407b20 -348a3b4,80407778 -348a3b8,ffffffff -348a3bc,5336000e -348a3c0,13f5200 -348a3c4,80407b20 -348a3c8,80407778 -348a3cc,ffffffff -348a3d0,5337000a -348a3d4,1425500 -348a3d8,80407b20 -348a3dc,80407778 -348a3e0,ffffffff -348a3e4,533b00a4 -348a3e8,18d7400 -348a3ec,80407b20 -348a3f0,80407778 -348a3f4,ffffffff -348a3f8,533d004b -348a3fc,f84300 -348a400,80407b20 -348a404,80407778 -348a408,ffffffff -348a40c,533e004c -348a410,cb1d01 -348a414,80407b20 -348a418,80407778 -348a41c,ffffffff -348a420,533f004d -348a424,dc2c01 -348a428,80407b20 -348a42c,80407778 -348a430,ffffffff -348a434,5340004e -348a438,ee3a00 -348a43c,80407b20 -348a440,80407778 -348a444,ffffffff -348a448,53420050 -348a44c,f23c00 -348a450,80407b20 -348a454,80407778 -348a458,ffffffff -348a45c,53430051 -348a460,f23d00 -348a464,80407b20 -348a468,80407778 -348a46c,ffffffff -348a470,53450053 -348a474,1184700 -348a478,80407b20 -348a47c,80407778 -348a480,ffffffff -348a484,53460054 -348a488,1575f00 -348a48c,80407b20 -348a490,80407778 -348a494,ffffffff -348a498,534b0056 -348a49c,be1600 -348a4a0,80407b20 -348a4a4,80407778 -348a4a8,ffffffff -348a4ac,534c0057 -348a4b0,be1700 -348a4b4,80407b20 -348a4b8,80407778 -348a4bc,ffffffff -348a4c0,534d0058 -348a4c4,bf1800 -348a4c8,80407b20 -348a4cc,80407778 -348a4d0,ffffffff -348a4d4,534e0059 -348a4d8,bf1900 -348a4dc,80407b20 -348a4e0,80407778 -348a4e4,ffffffff -348a4e8,534f005a -348a4ec,bf1a00 -348a4f0,80407b20 -348a4f4,80407778 -348a4f8,ffffffff -348a4fc,5351005b -348a500,12d4900 -348a504,80407b20 -348a508,80407778 -348a50c,ffffffff -348a510,5352005c -348a514,12d4a00 -348a518,80407b20 -348a51c,80407778 -348a520,ffffffff -348a524,535300cd -348a528,db2a00 -348a52c,80407b20 -348a530,80407778 -348a534,ffffffff -348a538,535400ce -348a53c,db2b00 -348a540,80407b20 -348a544,80407778 -348a548,ffffffff -348a54c,536f0068 -348a550,c82100 -348a554,80407b20 -348a558,80407778 -348a55c,ffffffff -348a560,5370007b -348a564,d72400 -348a568,80407b20 -348a56c,80407778 -348a570,ffffffff -348a574,5341004a -348a578,10e4600 -348a57c,80407b20 -348a580,80407958 -348a584,ffffffff -348a588,4d5800dc -348a58c,1194801 -348a590,80407d04 -348a594,80407778 -348a598,ffffffff -348a59c,3d7200c6 -348a5a0,bd1301 -348a5a4,80407b20 -348a5a8,80407780 -348a5ac,ffffffff -348a5b0,3e7a00c2 -348a5b4,bd1401 -348a5b8,80407b20 -348a5bc,80407780 -348a5c0,ffffffff -348a5c4,537400c7 -348a5c8,b90a02 -348a5cc,80407b20 -348a5d0,80407778 -348a5d4,ffffffff -348a5d8,53750067 -348a5dc,b80b01 -348a5e0,80407b20 -348a5e4,80407778 -348a5e8,ffffffff -348a5ec,53760066 -348a5f0,c81c01 -348a5f4,80407b20 -348a5f8,80407778 -348a5fc,ffffffff -348a600,53770060 -348a604,aa0203 -348a608,80407b20 -348a60c,80407778 -348a610,ffffffff -348a614,53780052 -348a618,cd1e01 -348a61c,80407b20 -348a620,80407778 -348a624,ffffffff -348a628,53790052 -348a62c,cd1f01 -348a630,80407b20 -348a634,80407778 -348a638,ffffffff -348a63c,5356005e -348a640,d12200 -348a644,80407b20 -348a648,804079b0 -348a64c,1ffff -348a650,5357005f -348a654,d12300 -348a658,80407b20 -348a65c,804079b0 -348a660,2ffff -348a664,5321009a -348a668,da2900 -348a66c,80407b20 -348a670,80407778 -348a674,ffffffff -348a678,4d830055 -348a67c,b70901 -348a680,80407b20 -348a684,80407778 -348a688,ffffffff -348a68c,4d9200e6 -348a690,d82501 -348a694,80407ccc -348a698,80407778 -348a69c,ffffffff -348a6a0,4d9300e6 -348a6a4,d82601 -348a6a8,80407ccc -348a6ac,80407778 -348a6b0,ffffffff -348a6b4,4d9400e6 -348a6b8,d82701 -348a6bc,80407ccc -348a6c0,80407778 -348a6c4,ffffffff -348a6c8,4d84006f -348a6cc,17f6d01 -348a6d0,80407b20 -348a6d4,80407778 -348a6d8,ffffffff -348a6dc,4d8500cc -348a6e0,17f6e01 -348a6e4,80407b20 -348a6e8,80407778 -348a6ec,ffffffff -348a6f0,4d8600f0 -348a6f4,17f6f01 -348a6f8,80407b20 -348a6fc,80407778 -348a700,ffffffff -348a704,3d7200c6 -348a708,bd1301 -348a70c,80407b20 -348a710,80407780 -348a714,ffffffff -348a718,53820098 -348a71c,df3000 -348a720,80407b20 -348a724,80407778 -348a728,ffffffff -348a72c,53280014 -348a730,1505b00 -348a734,80407b20 -348a738,80407778 -348a73c,ffffffff -348a740,53290015 -348a744,1515c00 -348a748,80407b20 -348a74c,80407778 -348a750,ffffffff -348a754,532a0016 -348a758,1525d00 -348a75c,80407b20 -348a760,80407778 -348a764,ffffffff -348a768,53500079 -348a76c,1475800 -348a770,80407b20 -348a774,80407778 -348a778,ffffffff -348a77c,4d8700f1 -348a780,17f7101 -348a784,80407b20 -348a788,80407778 -348a78c,ffffffff -348a790,4d8800f2 -348a794,17f7201 -348a798,80407b20 -348a79c,80407778 -348a7a0,ffffffff -348a7a4,533d000c -348a7a8,f84300 -348a7ac,80407b20 -348a7b0,80407880 -348a7b4,ffffffff -348a7b8,53040070 -348a7bc,1586000 -348a7c0,80407b20 -348a7c4,80407778 -348a7c8,ffffffff -348a7cc,530c0071 -348a7d0,1586100 -348a7d4,80407b20 -348a7d8,80407778 -348a7dc,ffffffff -348a7e0,53120072 -348a7e4,1586200 -348a7e8,80407b20 -348a7ec,80407778 -348a7f0,ffffffff -348a7f4,5b7100b4 -348a7f8,15c6301 -348a7fc,80407b20 -348a800,80407778 -348a804,ffffffff -348a808,530500ad -348a80c,15d6400 -348a810,80407b20 -348a814,80407778 -348a818,ffffffff -348a81c,530d00ae -348a820,15d6500 -348a824,80407b20 -348a828,80407778 -348a82c,ffffffff -348a830,531300af -348a834,15d6600 -348a838,80407b20 -348a83c,80407778 -348a840,ffffffff -348a844,53470007 -348a848,17b6c00 -348a84c,80407b20 -348a850,80407778 -348a854,ffffffff -348a858,53480007 -348a85c,17b6c00 -348a860,80407b20 -348a864,80407778 -348a868,ffffffff -348a86c,4d8a0037 -348a870,c71b01 -348a874,80407b20 -348a878,80407778 -348a87c,ffffffff -348a880,4d8b0037 -348a884,c71b01 -348a888,80407b20 -348a88c,80407778 -348a890,ffffffff -348a894,4d8c0034 -348a898,bb1201 -348a89c,80407b20 -348a8a0,80407778 -348a8a4,ffffffff -348a8a8,4d8d0034 -348a8ac,bb1201 -348a8b0,80407b20 -348a8b4,80407778 -348a8b8,ffffffff -348a8bc,4d020032 -348a8c0,ce2001 -348a8c4,80407ce8 -348a8c8,80407778 -348a8cc,ffffffff -348a8d0,4d8f0032 -348a8d4,ce2001 -348a8d8,80407ce8 -348a8dc,80407778 -348a8e0,ffffffff -348a8e4,4d900032 -348a8e8,ce2001 -348a8ec,80407ce8 -348a8f0,80407778 -348a8f4,ffffffff -348a8f8,4d910032 -348a8fc,ce2001 -348a900,80407ce8 -348a904,80407778 -348a908,ffffffff -348a90c,4d9500dc -348a910,1194801 -348a914,80407d04 -348a918,80407778 -348a91c,ffffffff -348a920,4d960033 -348a924,d92801 -348a928,80407b20 -348a92c,80407778 -348a930,ffffffff -348a934,4d970033 -348a938,d92801 -348a93c,80407b20 -348a940,80407778 -348a944,ffffffff -348a948,53190047 -348a94c,f43f00 -348a950,80407b20 -348a954,80407778 -348a958,ffffffff -348a95c,531d007a -348a960,1746800 -348a964,80407b20 -348a968,80407778 -348a96c,ffffffff -348a970,531c005d -348a974,1736700 -348a978,80407b20 -348a97c,80407778 -348a980,ffffffff -348a984,53200097 -348a988,1766a00 -348a98c,80407b20 -348a990,80407778 -348a994,ffffffff -348a998,531e00f9 -348a99c,1767000 -348a9a0,80407b20 -348a9a4,80407778 -348a9a8,ffffffff -348a9ac,537700f3 -348a9b0,aa0201 -348a9b4,80407b20 -348a9b8,80407778 -348a9bc,ffffffff -348a9c0,4d8400f4 -348a9c4,17f6d01 -348a9c8,80407b20 -348a9cc,80407778 -348a9d0,ffffffff -348a9d4,4d8500f5 -348a9d8,17f6e01 -348a9dc,80407b20 -348a9e0,80407778 -348a9e4,ffffffff -348a9e8,4d8600f6 -348a9ec,17f6f01 -348a9f0,80407b20 -348a9f4,80407778 -348a9f8,ffffffff -348a9fc,4d8700f7 -348aa00,17f7101 -348aa04,80407b20 -348aa08,80407778 -348aa0c,ffffffff -348aa10,537a00fa -348aa14,bd1401 -348aa18,80407b20 -348aa1c,80407780 -348aa20,ffffffff -348aa24,53980090 -348aa28,c71b01 -348aa2c,80407b20 -348aa30,80407778 -348aa34,ffffffff -348aa38,53990091 -348aa3c,c71b01 -348aa40,80407b20 -348aa44,80407778 -348aa48,ffffffff -348aa4c,539a00a7 -348aa50,bb1201 -348aa54,80407b20 -348aa58,80407778 -348aa5c,ffffffff -348aa60,539b00a8 -348aa64,bb1201 -348aa68,80407b20 -348aa6c,80407778 -348aa70,ffffffff -348aa74,5349006c -348aa78,17b7300 -348aa7c,80407b20 -348aa80,80407778 -348aa84,ffffffff -348aa88,53419002 -348aa90,80407b20 -348aa94,8040797c -348aa98,ffffffff -348aad8,ffffffff -348aadc,dd2d00 -348aae0,80407b28 -348aae4,80407778 -348aae8,ffffffff -348aaec,ffffffff -348aaf0,1475800 -348aaf4,80407b3c -348aaf8,80407778 -348aafc,ffffffff -348ab00,ffffffff -348ab04,bf1800 -348ab08,80407b68 -348ab0c,80407778 -348ab10,ffffffff -348ab14,ffffffff -348ab18,e93500 -348ab1c,80407b94 -348ab20,80407778 -348ab24,ffffffff -348ab28,ffffffff -348ab2c,e73300 -348ab30,80407bbc -348ab34,80407778 -348ab38,ffffffff -348ab3c,ffffffff -348ab40,d12200 -348ab44,80407bec -348ab48,80407778 -348ab4c,ffffffff -348ab50,ffffffff -348ab54,db2a00 -348ab58,80407c1c -348ab5c,80407778 -348ab60,ffffffff -348ab64,ffffffff -348ab68,bb1201 -348ab6c,80407c34 -348ab70,80407778 -348ab74,ffffffff -348ab78,ffffffff -348ab7c,c71b01 -348ab80,80407c50 -348ab84,80407778 -348ab88,ffffffff -348ab8c,ffffffff -348ab90,d92800 -348ab94,80407c7c -348ab98,80407778 -348ab9c,ffffffff -348aba0,ffffffff -348aba4,cd1e00 -348aba8,80407c6c -348abac,80407778 -348abb0,ffffffff -348abb4,ffffffff -348abb8,10e4600 -348abbc,80407cac -348abc0,80407778 -348abc4,ffffffff -348abc8,53410043 -348abcc,c60100 -348abd0,80407b20 -348abd4,8040788c -348abd8,15ffff -348abdc,53410044 -348abe0,c60100 -348abe4,80407b20 -348abe8,8040788c -348abec,16ffff -348abf0,53410045 -348abf4,c60100 -348abf8,80407b20 -348abfc,8040788c -348ac00,17ffff -348ac04,53410046 -348ac08,1776b00 -348ac0c,80407b20 -348ac10,8040788c -348ac14,18ffff -348ac18,53410047 -348ac1c,f43f00 -348ac20,80407b20 -348ac24,8040788c -348ac28,19ffff -348ac2c,5341005d -348ac30,1736700 -348ac34,80407b20 -348ac38,8040788c -348ac3c,1cffff -348ac40,5341007a -348ac44,1746800 -348ac48,80407b20 -348ac4c,8040788c -348ac50,1dffff -348ac54,534100f9 -348ac58,1767000 -348ac5c,80407b20 -348ac60,8040788c -348ac64,1effff -348ac68,53410097 -348ac6c,1766a00 -348ac70,80407b20 -348ac74,8040788c -348ac78,20ffff -348ac7c,53410006 -348ac80,b90a02 -348ac84,80407b20 -348ac88,804078c4 -348ac8c,10003 -348ac90,5341001c -348ac94,b90a02 -348ac98,80407b20 -348ac9c,804078c4 -348aca0,10004 -348aca4,5341001d -348aca8,b90a02 -348acac,80407b20 -348acb0,804078c4 -348acb4,10005 -348acb8,5341001e -348acbc,b90a02 -348acc0,80407b20 -348acc4,804078c4 -348acc8,10006 -348accc,5341002a -348acd0,b90a02 -348acd4,80407b20 -348acd8,804078c4 -348acdc,10007 -348ace0,53410061 -348ace4,b90a02 -348ace8,80407b20 -348acec,804078c4 -348acf0,1000a -348acf4,53410062 -348acf8,b80b01 -348acfc,80407b20 -348ad00,804078c4 -348ad04,20000 -348ad08,53410063 -348ad0c,b80b01 -348ad10,80407b20 -348ad14,804078c4 -348ad18,20001 -348ad1c,53410064 -348ad20,b80b01 -348ad24,80407b20 -348ad28,804078c4 -348ad2c,20002 -348ad30,53410065 -348ad34,b80b01 -348ad38,80407b20 -348ad3c,804078c4 -348ad40,20003 -348ad44,5341007c -348ad48,b80b01 -348ad4c,80407b20 -348ad50,804078c4 -348ad54,20004 -348ad58,5341007d -348ad5c,b80b01 -348ad60,80407b20 -348ad64,804078c4 -348ad68,20005 -348ad6c,5341007e -348ad70,b80b01 -348ad74,80407b20 -348ad78,804078c4 -348ad7c,20006 -348ad80,5341007f -348ad84,b80b01 -348ad88,80407b20 -348ad8c,804078c4 -348ad90,20007 -348ad94,534100a2 -348ad98,b80b01 -348ad9c,80407b20 -348ada0,804078c4 -348ada4,20008 -348ada8,53410087 -348adac,b80b01 -348adb0,80407b20 -348adb4,804078c4 -348adb8,20009 -348adbc,53410088 -348adc0,c81c01 -348adc4,80407b20 -348adc8,804078c4 -348adcc,40000 -348add0,53410089 -348add4,c81c01 -348add8,80407b20 -348addc,804078c4 -348ade0,40001 -348ade4,5341008a -348ade8,c81c01 -348adec,80407b20 -348adf0,804078c4 -348adf4,40002 -348adf8,5341008b -348adfc,c81c01 -348ae00,80407b20 -348ae04,804078c4 -348ae08,40003 -348ae0c,5341008c -348ae10,c81c01 -348ae14,80407b20 -348ae18,804078c4 -348ae1c,40004 -348ae20,5341008e -348ae24,c81c01 -348ae28,80407b20 -348ae2c,804078c4 -348ae30,40005 -348ae34,5341008f -348ae38,c81c01 -348ae3c,80407b20 -348ae40,804078c4 -348ae44,40006 -348ae48,534100a3 -348ae4c,c81c01 -348ae50,80407b20 -348ae54,804078c4 -348ae58,40007 -348ae5c,534100a5 -348ae60,c81c01 -348ae64,80407b20 -348ae68,804078c4 -348ae6c,40008 -348ae70,53410092 -348ae74,c81c01 -348ae78,80407b20 -348ae7c,804078c4 -348ae80,40009 -348ae84,53410093 -348ae88,aa0203 -348ae8c,80407b20 -348ae90,804078d8 -348ae94,3ffff -348ae98,53410094 -348ae9c,aa0203 -348aea0,80407b20 -348aea4,804078d8 -348aea8,4ffff -348aeac,53410095 -348aeb0,aa0203 -348aeb4,80407b20 -348aeb8,804078d8 -348aebc,5ffff -348aec0,534100a6 -348aec4,aa0203 -348aec8,80407b20 -348aecc,804078d8 -348aed0,6ffff -348aed4,534100a9 -348aed8,aa0203 -348aedc,80407b20 -348aee0,804078d8 -348aee4,7ffff -348aee8,5341009b -348aeec,aa0203 -348aef0,80407b20 -348aef4,804078d8 -348aef8,8ffff -348aefc,5341009f -348af00,aa0203 -348af04,80407b20 -348af08,804078d8 -348af0c,bffff -348af10,534100a0 -348af14,aa0203 -348af18,80407b20 -348af1c,804078d8 -348af20,cffff -348af24,534100a1 -348af28,aa0203 -348af2c,80407b20 -348af30,804078d8 -348af34,dffff -348af38,534100e9 -348af3c,1941300 -348af40,80407b20 -348af44,804078fc -348af48,ffffffff -348af4c,534100e4 -348af50,cd1e00 -348af54,80407b20 -348af58,80407918 -348af5c,ffffffff -348af60,534100e8 -348af64,cd1f00 -348af68,80407b20 -348af6c,80407934 -348af70,ffffffff -348af74,53410073 -348af78,b60300 -348af7c,80407b20 -348af80,80407964 -348af84,6ffff -348af88,53410074 -348af8c,b60400 -348af90,80407b20 -348af94,80407964 -348af98,7ffff -348af9c,53410075 -348afa0,b60500 -348afa4,80407b20 -348afa8,80407964 -348afac,8ffff -348afb0,53410076 -348afb4,b60600 -348afb8,80407b20 -348afbc,80407964 -348afc0,9ffff -348afc4,53410077 -348afc8,b60700 -348afcc,80407b20 -348afd0,80407964 -348afd4,affff -348afd8,53410078 -348afdc,b60800 -348afe0,80407b20 -348afe4,80407964 -348afe8,bffff -348afec,534100d4 -348aff0,b60400 -348aff4,80407b20 -348aff8,80407964 -348affc,cffff -348b000,534100d2 -348b004,b60600 -348b008,80407b20 -348b00c,80407964 -348b010,dffff -348b014,534100d1 -348b018,b60300 -348b01c,80407b20 -348b020,80407964 -348b024,effff -348b028,534100d3 -348b02c,b60800 -348b030,80407b20 -348b034,80407964 -348b038,fffff -348b03c,534100d5 -348b040,b60500 -348b044,80407b20 -348b048,80407964 -348b04c,10ffff -348b050,534100d6 -348b054,b60700 -348b058,80407b20 -348b05c,80407964 -348b060,11ffff -348b064,534100f8 -348b068,d12300 -348b06c,80407b20 -348b070,80407848 -348b074,3ffff -348b078,53149099 -348b07c,10b4500 -348b080,80407b20 -348b084,80407778 -348b088,ffffffff -348b08c,53419048 -348b090,f33e00 -348b094,80407b20 -348b098,80407998 -348b09c,ffffffff -348b0a0,53419003 -348b0a4,1933500 -348b0a8,80407b20 -348b0ac,8040778c -348b0b0,ffffffff -348b0b4,53419097 -348b0b8,ef3b00 -348b0bc,80407b20 -348b0c0,80407778 -348b0c4,ffffffff -348b0c8,4d419098 -348b0cc,ef3b01 -348b0d0,80407b20 -348b0d4,80407778 -348b0d8,ffffffff -348b0e0,1 -348b0e4,1 -348b0e8,d -348b0ec,41200000 -348b0f0,41200000 -348b0f4,8040a080 -348b0f8,8040a070 -348b100,df000000 -348b108,80112f1a -348b10c,80112f14 -348b110,80112f0e -348b114,80112f08 -348b118,8011320a -348b11c,80113204 -348b120,801131fe -348b124,801131f8 -348b128,801131f2 -348b12c,801131ec -348b130,801131e6 -348b134,801131e0 -348b138,8012be1e -348b13c,8012be20 -348b140,8012be1c -348b144,8012be12 -348b148,8012be14 -348b14c,8012be10 -348b150,801c7672 -348b154,801c767a -348b158,801c7950 -348b15c,8011bd50 -348b160,8011bd38 -348b164,801d8b9e -348b168,801d8b92 -348b16c,c80000 -348b174,ff0046 -348b178,32ffff -348c2c0,db000 -348c2c4,db000 +3489e5c,1010e0e0 +3489e60,10e0e010 +3489e64,10000000 +3489e68,4d510000 +3489e6c,4e6f726d +3489e70,616c0000 +3489e74,bdcccccd +3489e78,3dcccccd +3489e7c,43510000 +3489e80,41100000 +3489e84,4f000000 +3489e88,42080000 +3489e8c,c20c0000 +3489e90,420c0000 +3489e94,3f800000 +3489e98,3f000000 +3489e9c,41c80000 +3489ea0,3fa00000 +3489ea4,40000000 +3489ea8,40200000 +3489eac,3f800000 +3489eb0,4f000000 +3489eb8,41f00000 +3489ec0,3ff80000 +3489ec8,406e0000 +3489ed0,3ff00000 +3489ed8,40080000 +3489ee0,80409c38 +3489ee4,10203 +3489ee8,4050607 +3489eec,ffffffff +3489ef0,ffff0000 +3489ef4,ff00ff +3489ef8,3c000064 +3489efc,ffff8200 +3489f00,c832ffc8 +3489f04,c8000000 +3489f08,104465 +3489f0c,6b750000 +3489f14,110446f +3489f18,646f6e67 +3489f1c,6f000000 +3489f20,2104a61 +3489f24,62750000 +3489f2c,3d0466f +3489f30,72657374 +3489f38,4d04669 +3489f3c,72650000 +3489f44,5d05761 +3489f48,74657200 +3489f50,7d05368 +3489f54,61646f77 +3489f5c,6d05370 +3489f60,69726974 +3489f68,890426f +3489f6c,74570000 +3489f74,9104963 +3489f78,65000000 +3489f80,ca04765 +3489f84,7275646f +3489f8c,b804754 +3489f90,47000000 +3489f98,dc04761 +3489f9c,6e6f6e00 +3489fa4,2 +3489fac,3f800000 +3489fb8,1 +3489fbc,30006 +3489fc0,70009 +3489fc4,b000e +3489fc8,f0010 +3489fcc,110019 +3489fd0,1a002b +3489fd4,2c002e +3489fd8,300032 +3489fdc,35003c +3489fe0,400041 +3489fe4,460051 +3489fe8,540109 +3489fec,10b010c +3489ff0,10e010f +3489ff4,1100113 +3489ffc,100010 +348a000,a0301 +348a004,1000000 +348a00c,100010 +348a010,20002 +348a014,2000000 +348a01c,80008 +348a020,a0301 +348a024,1000000 +348a02c,100010 +348a030,30301 +348a034,1000000 +348a03c,100018 +348a040,10301 +348a044,1000000 +348a04c,100010 +348a050,100301 +348a054,1000000 +348a05c,200020 +348a060,10302 +348a064,2000000 +348a06c,8000e +348a070,5f0301 +348a074,1000000 +348a07c,180018 +348a080,140003 +348a084,4000000 +348a08c,200020 +348a090,5a0003 +348a094,4000000 +348a09c,100010 +348a0a0,60301 +348a0a4,1000000 +348a0ac,100010 +348a0b0,30003 +348a0b4,4000000 +348a0b8,e7000000 +348a0c0,d9000000 +348a0c8,ed000000 +348a0cc,5003c0 +348a0d0,ef002cf0 +348a0d4,504244 +348a0d8,df000000 +348a0f4,4d8e0032 +348a0f8,ce2001 +348a0fc,80407cec +348a100,8040777c +348a104,ffffffff +348a108,4d8c0034 +348a10c,bb1201 +348a110,80407b24 +348a114,8040777c +348a118,ffffffff +348a11c,4d090033 +348a120,d92801 +348a124,80407b24 +348a128,8040777c +348a12c,ffffffff +348a130,53030031 +348a134,e93500 +348a138,80407b24 +348a13c,8040777c +348a140,ffffffff +348a144,53060030 +348a148,e73300 +348a14c,80407b24 +348a150,8040777c +348a154,ffffffff +348a158,530e0035 +348a15c,e83400 +348a160,80407b24 +348a164,8040777c +348a168,ffffffff +348a16c,4d000037 +348a170,c71b01 +348a174,80407b24 +348a178,8040777c +348a17c,ffffffff +348a180,530a0036 +348a184,dd2d00 +348a188,80407b24 +348a18c,8040777c +348a190,ffffffff +348a194,530b004f +348a198,dd2e00 +348a19c,80407b24 +348a1a0,8040777c +348a1a4,ffffffff +348a1a8,530f0039 +348a1ac,ea3600 +348a1b0,80407b24 +348a1b4,8040777c +348a1b8,ffffffff +348a1bc,53230069 +348a1c0,ef3b00 +348a1c4,80407b24 +348a1c8,804079e0 +348a1cc,ffffffff +348a1d0,5308003a +348a1d4,de2f00 +348a1d8,80407b24 +348a1dc,8040777c +348a1e0,ffffffff +348a1e4,53110038 +348a1e8,f64100 +348a1ec,80407b24 +348a1f0,8040777c +348a1f4,ffffffff +348a1f8,532f0002 +348a1fc,1095e00 +348a200,80407b24 +348a204,8040777c +348a208,ffffffff +348a20c,53140042 +348a210,c60100 +348a214,80407b24 +348a218,8040777c +348a21c,ffffffff +348a220,53150043 +348a224,eb3800 +348a228,80407b24 +348a22c,8040777c +348a230,ffffffff +348a234,53160044 +348a238,eb3700 +348a23c,80407b24 +348a240,8040777c +348a244,ffffffff +348a248,53170045 +348a24c,eb3900 +348a250,80407b24 +348a254,8040777c +348a258,ffffffff +348a25c,53180046 +348a260,c60100 +348a264,80407b24 +348a268,8040777c +348a26c,ffffffff +348a270,531a0098 +348a274,df3000 +348a278,80407b24 +348a27c,8040777c +348a280,ffffffff +348a284,531b0099 +348a288,10b4500 +348a28c,80407d2c +348a290,8040777c +348a294,ffffffff +348a298,53100048 +348a29c,f33e01 +348a2a0,80407b24 +348a2a4,8040777c +348a2a8,ffffffff +348a2ac,53250010 +348a2b0,1364f00 +348a2b4,80407b24 +348a2b8,8040777c +348a2bc,ffffffff +348a2c0,53260011 +348a2c4,1353200 +348a2c8,80407b24 +348a2cc,8040777c +348a2d0,ffffffff +348a2d4,5322000b +348a2d8,1094400 +348a2dc,80407b24 +348a2e0,8040777c +348a2e4,ffffffff +348a2e8,53240012 +348a2ec,1343100 +348a2f0,80407b24 +348a2f4,8040777c +348a2f8,ffffffff +348a2fc,53270013 +348a300,1375000 +348a304,80407b24 +348a308,8040777c +348a30c,ffffffff +348a310,532b0017 +348a314,1385100 +348a318,80407b24 +348a31c,8040777c +348a320,ffffffff +348a324,532d9001 +348a328,da2900 +348a32c,80407b24 +348a330,8040777c +348a334,ffffffff +348a338,532e000b +348a33c,1094400 +348a340,80407b24 +348a344,8040777c +348a348,ffffffff +348a34c,53300003 +348a350,1415400 +348a354,80407b24 +348a358,8040777c +348a35c,ffffffff +348a360,53310004 +348a364,1405300 +348a368,80407b24 +348a36c,8040777c +348a370,ffffffff +348a374,53320005 +348a378,f54000 +348a37c,80407b24 +348a380,8040777c +348a384,ffffffff +348a388,53330008 +348a38c,1435600 +348a390,80407b24 +348a394,8040777c +348a398,ffffffff +348a39c,53340009 +348a3a0,1465700 +348a3a4,80407b24 +348a3a8,8040777c +348a3ac,ffffffff +348a3b0,5335000d +348a3b4,1495a00 +348a3b8,80407b24 +348a3bc,8040777c +348a3c0,ffffffff +348a3c4,5336000e +348a3c8,13f5200 +348a3cc,80407b24 +348a3d0,8040777c +348a3d4,ffffffff +348a3d8,5337000a +348a3dc,1425500 +348a3e0,80407b24 +348a3e4,8040777c +348a3e8,ffffffff +348a3ec,533b00a4 +348a3f0,18d7400 +348a3f4,80407b24 +348a3f8,8040777c +348a3fc,ffffffff +348a400,533d004b +348a404,f84300 +348a408,80407b24 +348a40c,8040777c +348a410,ffffffff +348a414,533e004c +348a418,cb1d01 +348a41c,80407b24 +348a420,8040777c +348a424,ffffffff +348a428,533f004d +348a42c,dc2c01 +348a430,80407b24 +348a434,8040777c +348a438,ffffffff +348a43c,5340004e +348a440,ee3a00 +348a444,80407b24 +348a448,8040777c +348a44c,ffffffff +348a450,53420050 +348a454,f23c00 +348a458,80407b24 +348a45c,8040777c +348a460,ffffffff +348a464,53430051 +348a468,f23d00 +348a46c,80407b24 +348a470,8040777c +348a474,ffffffff +348a478,53450053 +348a47c,1184700 +348a480,80407b24 +348a484,8040777c +348a488,ffffffff +348a48c,53460054 +348a490,1575f00 +348a494,80407b24 +348a498,8040777c +348a49c,ffffffff +348a4a0,534b0056 +348a4a4,be1600 +348a4a8,80407b24 +348a4ac,8040777c +348a4b0,ffffffff +348a4b4,534c0057 +348a4b8,be1700 +348a4bc,80407b24 +348a4c0,8040777c +348a4c4,ffffffff +348a4c8,534d0058 +348a4cc,bf1800 +348a4d0,80407b24 +348a4d4,8040777c +348a4d8,ffffffff +348a4dc,534e0059 +348a4e0,bf1900 +348a4e4,80407b24 +348a4e8,8040777c +348a4ec,ffffffff +348a4f0,534f005a +348a4f4,bf1a00 +348a4f8,80407b24 +348a4fc,8040777c +348a500,ffffffff +348a504,5351005b +348a508,12d4900 +348a50c,80407b24 +348a510,8040777c +348a514,ffffffff +348a518,5352005c +348a51c,12d4a00 +348a520,80407b24 +348a524,8040777c +348a528,ffffffff +348a52c,535300cd +348a530,db2a00 +348a534,80407b24 +348a538,8040777c +348a53c,ffffffff +348a540,535400ce +348a544,db2b00 +348a548,80407b24 +348a54c,8040777c +348a550,ffffffff +348a554,536f0068 +348a558,c82100 +348a55c,80407b24 +348a560,8040777c +348a564,ffffffff +348a568,5370007b +348a56c,d72400 +348a570,80407b24 +348a574,8040777c +348a578,ffffffff +348a57c,5341004a +348a580,10e4600 +348a584,80407b24 +348a588,8040795c +348a58c,ffffffff +348a590,4d5800dc +348a594,1194801 +348a598,80407d08 +348a59c,8040777c +348a5a0,ffffffff +348a5a4,3d7200c6 +348a5a8,bd1301 +348a5ac,80407b24 +348a5b0,80407784 +348a5b4,ffffffff +348a5b8,3e7a00c2 +348a5bc,bd1401 +348a5c0,80407b24 +348a5c4,80407784 +348a5c8,ffffffff +348a5cc,537400c7 +348a5d0,b90a02 +348a5d4,80407b24 +348a5d8,8040777c +348a5dc,ffffffff +348a5e0,53750067 +348a5e4,b80b01 +348a5e8,80407b24 +348a5ec,8040777c +348a5f0,ffffffff +348a5f4,53760066 +348a5f8,c81c01 +348a5fc,80407b24 +348a600,8040777c +348a604,ffffffff +348a608,53770060 +348a60c,aa0203 +348a610,80407b24 +348a614,8040777c +348a618,ffffffff +348a61c,53780052 +348a620,cd1e01 +348a624,80407b24 +348a628,8040777c +348a62c,ffffffff +348a630,53790052 +348a634,cd1f01 +348a638,80407b24 +348a63c,8040777c +348a640,ffffffff +348a644,5356005e +348a648,d12200 +348a64c,80407b24 +348a650,804079b4 +348a654,1ffff +348a658,5357005f +348a65c,d12300 +348a660,80407b24 +348a664,804079b4 +348a668,2ffff +348a66c,5321009a +348a670,da2900 +348a674,80407b24 +348a678,8040777c +348a67c,ffffffff +348a680,4d830055 +348a684,b70901 +348a688,80407b24 +348a68c,8040777c +348a690,ffffffff +348a694,4d9200e6 +348a698,d82501 +348a69c,80407cd0 +348a6a0,8040777c +348a6a4,ffffffff +348a6a8,4d9300e6 +348a6ac,d82601 +348a6b0,80407cd0 +348a6b4,8040777c +348a6b8,ffffffff +348a6bc,4d9400e6 +348a6c0,d82701 +348a6c4,80407cd0 +348a6c8,8040777c +348a6cc,ffffffff +348a6d0,4d84006f +348a6d4,17f6d01 +348a6d8,80407b24 +348a6dc,8040777c +348a6e0,ffffffff +348a6e4,4d8500cc +348a6e8,17f6e01 +348a6ec,80407b24 +348a6f0,8040777c +348a6f4,ffffffff +348a6f8,4d8600f0 +348a6fc,17f6f01 +348a700,80407b24 +348a704,8040777c +348a708,ffffffff +348a70c,3d7200c6 +348a710,bd1301 +348a714,80407b24 +348a718,80407784 +348a71c,ffffffff +348a720,53820098 +348a724,df3000 +348a728,80407b24 +348a72c,8040777c +348a730,ffffffff +348a734,53280014 +348a738,1505b00 +348a73c,80407b24 +348a740,8040777c +348a744,ffffffff +348a748,53290015 +348a74c,1515c00 +348a750,80407b24 +348a754,8040777c +348a758,ffffffff +348a75c,532a0016 +348a760,1525d00 +348a764,80407b24 +348a768,8040777c +348a76c,ffffffff +348a770,53500079 +348a774,1475800 +348a778,80407b24 +348a77c,8040777c +348a780,ffffffff +348a784,4d8700f1 +348a788,17f7101 +348a78c,80407b24 +348a790,8040777c +348a794,ffffffff +348a798,4d8800f2 +348a79c,17f7201 +348a7a0,80407b24 +348a7a4,8040777c +348a7a8,ffffffff +348a7ac,533d000c +348a7b0,f84300 +348a7b4,80407b24 +348a7b8,80407884 +348a7bc,ffffffff +348a7c0,53040070 +348a7c4,1586000 +348a7c8,80407b24 +348a7cc,8040777c +348a7d0,ffffffff +348a7d4,530c0071 +348a7d8,1586100 +348a7dc,80407b24 +348a7e0,8040777c +348a7e4,ffffffff +348a7e8,53120072 +348a7ec,1586200 +348a7f0,80407b24 +348a7f4,8040777c +348a7f8,ffffffff +348a7fc,5b7100b4 +348a800,15c6301 +348a804,80407b24 +348a808,8040777c +348a80c,ffffffff +348a810,530500ad +348a814,15d6400 +348a818,80407b24 +348a81c,8040777c +348a820,ffffffff +348a824,530d00ae +348a828,15d6500 +348a82c,80407b24 +348a830,8040777c +348a834,ffffffff +348a838,531300af +348a83c,15d6600 +348a840,80407b24 +348a844,8040777c +348a848,ffffffff +348a84c,53470007 +348a850,17b6c00 +348a854,80407b24 +348a858,8040777c +348a85c,ffffffff +348a860,53480007 +348a864,17b6c00 +348a868,80407b24 +348a86c,8040777c +348a870,ffffffff +348a874,4d8a0037 +348a878,c71b01 +348a87c,80407b24 +348a880,8040777c +348a884,ffffffff +348a888,4d8b0037 +348a88c,c71b01 +348a890,80407b24 +348a894,8040777c +348a898,ffffffff +348a89c,4d8c0034 +348a8a0,bb1201 +348a8a4,80407b24 +348a8a8,8040777c +348a8ac,ffffffff +348a8b0,4d8d0034 +348a8b4,bb1201 +348a8b8,80407b24 +348a8bc,8040777c +348a8c0,ffffffff +348a8c4,4d020032 +348a8c8,ce2001 +348a8cc,80407cec +348a8d0,8040777c +348a8d4,ffffffff +348a8d8,4d8f0032 +348a8dc,ce2001 +348a8e0,80407cec +348a8e4,8040777c +348a8e8,ffffffff +348a8ec,4d900032 +348a8f0,ce2001 +348a8f4,80407cec +348a8f8,8040777c +348a8fc,ffffffff +348a900,4d910032 +348a904,ce2001 +348a908,80407cec +348a90c,8040777c +348a910,ffffffff +348a914,4d9500dc +348a918,1194801 +348a91c,80407d08 +348a920,8040777c +348a924,ffffffff +348a928,4d960033 +348a92c,d92801 +348a930,80407b24 +348a934,8040777c +348a938,ffffffff +348a93c,4d970033 +348a940,d92801 +348a944,80407b24 +348a948,8040777c +348a94c,ffffffff +348a950,53190047 +348a954,f43f00 +348a958,80407b24 +348a95c,8040777c +348a960,ffffffff +348a964,531d007a +348a968,1746800 +348a96c,80407b24 +348a970,8040777c +348a974,ffffffff +348a978,531c005d +348a97c,1736700 +348a980,80407b24 +348a984,8040777c +348a988,ffffffff +348a98c,53200097 +348a990,1766a00 +348a994,80407b24 +348a998,8040777c +348a99c,ffffffff +348a9a0,531e00f9 +348a9a4,1767000 +348a9a8,80407b24 +348a9ac,8040777c +348a9b0,ffffffff +348a9b4,537700f3 +348a9b8,aa0201 +348a9bc,80407b24 +348a9c0,8040777c +348a9c4,ffffffff +348a9c8,4d8400f4 +348a9cc,17f6d01 +348a9d0,80407b24 +348a9d4,8040777c +348a9d8,ffffffff +348a9dc,4d8500f5 +348a9e0,17f6e01 +348a9e4,80407b24 +348a9e8,8040777c +348a9ec,ffffffff +348a9f0,4d8600f6 +348a9f4,17f6f01 +348a9f8,80407b24 +348a9fc,8040777c +348aa00,ffffffff +348aa04,4d8700f7 +348aa08,17f7101 +348aa0c,80407b24 +348aa10,8040777c +348aa14,ffffffff +348aa18,537a00fa +348aa1c,bd1401 +348aa20,80407b24 +348aa24,80407784 +348aa28,ffffffff +348aa2c,53980090 +348aa30,c71b01 +348aa34,80407b24 +348aa38,8040777c +348aa3c,ffffffff +348aa40,53990091 +348aa44,c71b01 +348aa48,80407b24 +348aa4c,8040777c +348aa50,ffffffff +348aa54,539a00a7 +348aa58,bb1201 +348aa5c,80407b24 +348aa60,8040777c +348aa64,ffffffff +348aa68,539b00a8 +348aa6c,bb1201 +348aa70,80407b24 +348aa74,8040777c +348aa78,ffffffff +348aa7c,5349006c +348aa80,17b7300 +348aa84,80407b24 +348aa88,8040777c +348aa8c,ffffffff +348aa90,53419002 +348aa98,80407b24 +348aa9c,80407980 +348aaa0,ffffffff +348aae0,ffffffff +348aae4,dd2d00 +348aae8,80407b2c +348aaec,8040777c +348aaf0,ffffffff +348aaf4,ffffffff +348aaf8,1475800 +348aafc,80407b40 +348ab00,8040777c +348ab04,ffffffff +348ab08,ffffffff +348ab0c,bf1800 +348ab10,80407b6c +348ab14,8040777c +348ab18,ffffffff +348ab1c,ffffffff +348ab20,e93500 +348ab24,80407b98 +348ab28,8040777c +348ab2c,ffffffff +348ab30,ffffffff +348ab34,e73300 +348ab38,80407bc0 +348ab3c,8040777c +348ab40,ffffffff +348ab44,ffffffff +348ab48,d12200 +348ab4c,80407bf0 +348ab50,8040777c +348ab54,ffffffff +348ab58,ffffffff +348ab5c,db2a00 +348ab60,80407c20 +348ab64,8040777c +348ab68,ffffffff +348ab6c,ffffffff +348ab70,bb1201 +348ab74,80407c38 +348ab78,8040777c +348ab7c,ffffffff +348ab80,ffffffff +348ab84,c71b01 +348ab88,80407c54 +348ab8c,8040777c +348ab90,ffffffff +348ab94,ffffffff +348ab98,d92800 +348ab9c,80407c80 +348aba0,8040777c +348aba4,ffffffff +348aba8,ffffffff +348abac,cd1e00 +348abb0,80407c70 +348abb4,8040777c +348abb8,ffffffff +348abbc,ffffffff +348abc0,10e4600 +348abc4,80407cb0 +348abc8,8040777c +348abcc,ffffffff +348abd0,53410043 +348abd4,c60100 +348abd8,80407b24 +348abdc,80407890 +348abe0,15ffff +348abe4,53410044 +348abe8,c60100 +348abec,80407b24 +348abf0,80407890 +348abf4,16ffff +348abf8,53410045 +348abfc,c60100 +348ac00,80407b24 +348ac04,80407890 +348ac08,17ffff +348ac0c,53410046 +348ac10,1776b00 +348ac14,80407b24 +348ac18,80407890 +348ac1c,18ffff +348ac20,53410047 +348ac24,f43f00 +348ac28,80407b24 +348ac2c,80407890 +348ac30,19ffff +348ac34,5341005d +348ac38,1736700 +348ac3c,80407b24 +348ac40,80407890 +348ac44,1cffff +348ac48,5341007a +348ac4c,1746800 +348ac50,80407b24 +348ac54,80407890 +348ac58,1dffff +348ac5c,534100f9 +348ac60,1767000 +348ac64,80407b24 +348ac68,80407890 +348ac6c,1effff +348ac70,53410097 +348ac74,1766a00 +348ac78,80407b24 +348ac7c,80407890 +348ac80,20ffff +348ac84,53410006 +348ac88,b90a02 +348ac8c,80407b24 +348ac90,804078c8 +348ac94,10003 +348ac98,5341001c +348ac9c,b90a02 +348aca0,80407b24 +348aca4,804078c8 +348aca8,10004 +348acac,5341001d +348acb0,b90a02 +348acb4,80407b24 +348acb8,804078c8 +348acbc,10005 +348acc0,5341001e +348acc4,b90a02 +348acc8,80407b24 +348accc,804078c8 +348acd0,10006 +348acd4,5341002a +348acd8,b90a02 +348acdc,80407b24 +348ace0,804078c8 +348ace4,10007 +348ace8,53410061 +348acec,b90a02 +348acf0,80407b24 +348acf4,804078c8 +348acf8,1000a +348acfc,53410062 +348ad00,b80b01 +348ad04,80407b24 +348ad08,804078c8 +348ad0c,20000 +348ad10,53410063 +348ad14,b80b01 +348ad18,80407b24 +348ad1c,804078c8 +348ad20,20001 +348ad24,53410064 +348ad28,b80b01 +348ad2c,80407b24 +348ad30,804078c8 +348ad34,20002 +348ad38,53410065 +348ad3c,b80b01 +348ad40,80407b24 +348ad44,804078c8 +348ad48,20003 +348ad4c,5341007c +348ad50,b80b01 +348ad54,80407b24 +348ad58,804078c8 +348ad5c,20004 +348ad60,5341007d +348ad64,b80b01 +348ad68,80407b24 +348ad6c,804078c8 +348ad70,20005 +348ad74,5341007e +348ad78,b80b01 +348ad7c,80407b24 +348ad80,804078c8 +348ad84,20006 +348ad88,5341007f +348ad8c,b80b01 +348ad90,80407b24 +348ad94,804078c8 +348ad98,20007 +348ad9c,534100a2 +348ada0,b80b01 +348ada4,80407b24 +348ada8,804078c8 +348adac,20008 +348adb0,53410087 +348adb4,b80b01 +348adb8,80407b24 +348adbc,804078c8 +348adc0,20009 +348adc4,53410088 +348adc8,c81c01 +348adcc,80407b24 +348add0,804078c8 +348add4,40000 +348add8,53410089 +348addc,c81c01 +348ade0,80407b24 +348ade4,804078c8 +348ade8,40001 +348adec,5341008a +348adf0,c81c01 +348adf4,80407b24 +348adf8,804078c8 +348adfc,40002 +348ae00,5341008b +348ae04,c81c01 +348ae08,80407b24 +348ae0c,804078c8 +348ae10,40003 +348ae14,5341008c +348ae18,c81c01 +348ae1c,80407b24 +348ae20,804078c8 +348ae24,40004 +348ae28,5341008e +348ae2c,c81c01 +348ae30,80407b24 +348ae34,804078c8 +348ae38,40005 +348ae3c,5341008f +348ae40,c81c01 +348ae44,80407b24 +348ae48,804078c8 +348ae4c,40006 +348ae50,534100a3 +348ae54,c81c01 +348ae58,80407b24 +348ae5c,804078c8 +348ae60,40007 +348ae64,534100a5 +348ae68,c81c01 +348ae6c,80407b24 +348ae70,804078c8 +348ae74,40008 +348ae78,53410092 +348ae7c,c81c01 +348ae80,80407b24 +348ae84,804078c8 +348ae88,40009 +348ae8c,53410093 +348ae90,aa0203 +348ae94,80407b24 +348ae98,804078dc +348ae9c,3ffff +348aea0,53410094 +348aea4,aa0203 +348aea8,80407b24 +348aeac,804078dc +348aeb0,4ffff +348aeb4,53410095 +348aeb8,aa0203 +348aebc,80407b24 +348aec0,804078dc +348aec4,5ffff +348aec8,534100a6 +348aecc,aa0203 +348aed0,80407b24 +348aed4,804078dc +348aed8,6ffff +348aedc,534100a9 +348aee0,aa0203 +348aee4,80407b24 +348aee8,804078dc +348aeec,7ffff +348aef0,5341009b +348aef4,aa0203 +348aef8,80407b24 +348aefc,804078dc +348af00,8ffff +348af04,5341009f +348af08,aa0203 +348af0c,80407b24 +348af10,804078dc +348af14,bffff +348af18,534100a0 +348af1c,aa0203 +348af20,80407b24 +348af24,804078dc +348af28,cffff +348af2c,534100a1 +348af30,aa0203 +348af34,80407b24 +348af38,804078dc +348af3c,dffff +348af40,534100e9 +348af44,1941300 +348af48,80407b24 +348af4c,80407900 +348af50,ffffffff +348af54,534100e4 +348af58,cd1e00 +348af5c,80407b24 +348af60,8040791c +348af64,ffffffff +348af68,534100e8 +348af6c,cd1f00 +348af70,80407b24 +348af74,80407938 +348af78,ffffffff +348af7c,53410073 +348af80,b60300 +348af84,80407b24 +348af88,80407968 +348af8c,6ffff +348af90,53410074 +348af94,b60400 +348af98,80407b24 +348af9c,80407968 +348afa0,7ffff +348afa4,53410075 +348afa8,b60500 +348afac,80407b24 +348afb0,80407968 +348afb4,8ffff +348afb8,53410076 +348afbc,b60600 +348afc0,80407b24 +348afc4,80407968 +348afc8,9ffff +348afcc,53410077 +348afd0,b60700 +348afd4,80407b24 +348afd8,80407968 +348afdc,affff +348afe0,53410078 +348afe4,b60800 +348afe8,80407b24 +348afec,80407968 +348aff0,bffff +348aff4,534100d4 +348aff8,b60400 +348affc,80407b24 +348b000,80407968 +348b004,cffff +348b008,534100d2 +348b00c,b60600 +348b010,80407b24 +348b014,80407968 +348b018,dffff +348b01c,534100d1 +348b020,b60300 +348b024,80407b24 +348b028,80407968 +348b02c,effff +348b030,534100d3 +348b034,b60800 +348b038,80407b24 +348b03c,80407968 +348b040,fffff +348b044,534100d5 +348b048,b60500 +348b04c,80407b24 +348b050,80407968 +348b054,10ffff +348b058,534100d6 +348b05c,b60700 +348b060,80407b24 +348b064,80407968 +348b068,11ffff +348b06c,534100f8 +348b070,d12300 +348b074,80407b24 +348b078,8040784c +348b07c,3ffff +348b080,53149099 +348b084,10b4500 +348b088,80407b24 +348b08c,8040777c +348b090,ffffffff +348b094,53419048 +348b098,f33e00 +348b09c,80407b24 +348b0a0,8040799c +348b0a4,ffffffff +348b0a8,53419003 +348b0ac,1933500 +348b0b0,80407b24 +348b0b4,80407790 +348b0b8,ffffffff +348b0bc,53419097 +348b0c0,ef3b00 +348b0c4,80407b24 +348b0c8,8040777c +348b0cc,ffffffff +348b0d0,4d419098 +348b0d4,ef3b01 +348b0d8,80407b24 +348b0dc,8040777c +348b0e0,ffffffff +348b0e8,1 +348b0ec,1 +348b0f0,d +348b0f4,41200000 +348b0f8,41200000 +348b0fc,8040a088 +348b100,8040a078 +348b108,df000000 +348b110,80112f1a +348b114,80112f14 +348b118,80112f0e +348b11c,80112f08 +348b120,8011320a +348b124,80113204 +348b128,801131fe +348b12c,801131f8 +348b130,801131f2 +348b134,801131ec +348b138,801131e6 +348b13c,801131e0 +348b140,8012be1e +348b144,8012be20 +348b148,8012be1c +348b14c,8012be12 +348b150,8012be14 +348b154,8012be10 +348b158,801c7672 +348b15c,801c767a +348b160,801c7950 +348b164,8011bd50 +348b168,8011bd38 +348b16c,801d8b9e +348b170,801d8b92 +348b174,c80000 +348b17c,ff0046 +348b180,32ffff 348c2c8,db000 -348c2cc,cb000 -348c2d0,cb000 -348c2d4,ca000 -348c2dc,db000 -348c2e0,db000 -348c2f8,e8ac00 -348c2fc,e8ac00 +348c2cc,db000 +348c2d0,db000 +348c2d4,cb000 +348c2d8,cb000 +348c2dc,ca000 +348c2e4,db000 +348c2e8,db000 348c300,e8ac00 348c304,e8ac00 -348c32c,d77d0 -348c330,2e3ab0 -348c334,7d0c90 -348c338,8ffffffd -348c33c,c96e00 -348c340,2e4ac00 -348c344,effffff4 -348c348,ab0e500 -348c34c,c95e000 -348c350,e59c000 -348c368,79000 -348c36c,5ceeb40 -348c370,cc8a990 -348c374,da79000 -348c378,8ecb400 -348c37c,4adda0 -348c380,797e2 -348c384,c88aae0 -348c388,6ceed70 -348c38c,79000 -348c390,79000 -348c3a0,6dea0000 -348c3a4,c94d6000 -348c3a8,c94d6033 -348c3ac,6deb6bc6 -348c3b0,8cb600 -348c3b4,7ca4cec4 -348c3b8,3109c3bb -348c3bc,9c3bb -348c3c0,2ced4 -348c3d8,4cefb00 -348c3dc,ad50000 -348c3e0,8e30000 -348c3e4,9ec0000 -348c3e8,7e4db0ab -348c3ec,bb05e8aa -348c3f0,bc008ed6 -348c3f4,7e936ed0 -348c3f8,8ded9ea -348c410,ca000 -348c414,ca000 +348c308,e8ac00 +348c30c,e8ac00 +348c334,d77d0 +348c338,2e3ab0 +348c33c,7d0c90 +348c340,8ffffffd +348c344,c96e00 +348c348,2e4ac00 +348c34c,effffff4 +348c350,ab0e500 +348c354,c95e000 +348c358,e59c000 +348c370,79000 +348c374,5ceeb40 +348c378,cc8a990 +348c37c,da79000 +348c380,8ecb400 +348c384,4adda0 +348c388,797e2 +348c38c,c88aae0 +348c390,6ceed70 +348c394,79000 +348c398,79000 +348c3a8,6dea0000 +348c3ac,c94d6000 +348c3b0,c94d6033 +348c3b4,6deb6bc6 +348c3b8,8cb600 +348c3bc,7ca4cec4 +348c3c0,3109c3bb +348c3c4,9c3bb +348c3c8,2ced4 +348c3e0,4cefb00 +348c3e4,ad50000 +348c3e8,8e30000 +348c3ec,9ec0000 +348c3f0,7e4db0ab +348c3f4,bb05e8aa +348c3f8,bc008ed6 +348c3fc,7e936ed0 +348c400,8ded9ea 348c418,ca000 348c41c,ca000 -348c440,c900 -348c444,7e200 -348c448,cb000 -348c44c,e8000 -348c450,6f3000 -348c454,8e0000 -348c458,8e0000 -348c45c,6f4000 -348c460,e8000 -348c464,cb000 -348c468,7e200 -348c46c,c900 -348c478,bb0000 -348c47c,5e4000 -348c480,ca000 -348c484,ad000 -348c488,7e100 -348c48c,6f400 -348c490,6f400 -348c494,7e100 -348c498,ad000 -348c49c,ca000 -348c4a0,5e4000 -348c4a4,bb0000 -348c4b8,a8000 -348c4bc,c8a8ab0 -348c4c0,3beda10 -348c4c4,3beda10 -348c4c8,c8a8ab0 -348c4cc,a8000 -348c4f4,ca000 -348c4f8,ca000 +348c420,ca000 +348c424,ca000 +348c448,c900 +348c44c,7e200 +348c450,cb000 +348c454,e8000 +348c458,6f3000 +348c45c,8e0000 +348c460,8e0000 +348c464,6f4000 +348c468,e8000 +348c46c,cb000 +348c470,7e200 +348c474,c900 +348c480,bb0000 +348c484,5e4000 +348c488,ca000 +348c48c,ad000 +348c490,7e100 +348c494,6f400 +348c498,6f400 +348c49c,7e100 +348c4a0,ad000 +348c4a4,ca000 +348c4a8,5e4000 +348c4ac,bb0000 +348c4c0,a8000 +348c4c4,c8a8ab0 +348c4c8,3beda10 +348c4cc,3beda10 +348c4d0,c8a8ab0 +348c4d4,a8000 348c4fc,ca000 -348c500,affffff8 +348c500,ca000 348c504,ca000 -348c508,ca000 +348c508,affffff8 348c50c,ca000 -348c544,dd000 -348c548,ec000 -348c54c,4f8000 -348c550,9d0000 -348c574,dffb00 -348c5b4,ec000 -348c5b8,ec000 -348c5d0,bc0 -348c5d4,4e60 -348c5d8,bc00 -348c5dc,3e800 -348c5e0,ad000 -348c5e4,1e9000 -348c5e8,9e2000 -348c5ec,da0000 -348c5f0,7e30000 -348c5f4,cb00000 -348c5f8,6e500000 -348c608,3ceeb00 -348c60c,bd57e90 -348c610,e900bd0 -348c614,5f7009e0 -348c618,6f6cb9e0 +348c510,ca000 +348c514,ca000 +348c54c,dd000 +348c550,ec000 +348c554,4f8000 +348c558,9d0000 +348c57c,dffb00 +348c5bc,ec000 +348c5c0,ec000 +348c5d8,bc0 +348c5dc,4e60 +348c5e0,bc00 +348c5e4,3e800 +348c5e8,ad000 +348c5ec,1e9000 +348c5f0,9e2000 +348c5f4,da0000 +348c5f8,7e30000 +348c5fc,cb00000 +348c600,6e500000 +348c610,3ceeb00 +348c614,bd57e90 +348c618,e900bd0 348c61c,5f7009e0 -348c620,e900bd0 -348c624,bd57e90 -348c628,3ceeb00 -348c640,affe000 -348c644,8e000 -348c648,8e000 +348c620,6f6cb9e0 +348c624,5f7009e0 +348c628,e900bd0 +348c62c,bd57e90 +348c630,3ceeb00 +348c648,affe000 348c64c,8e000 348c650,8e000 348c654,8e000 348c658,8e000 348c65c,8e000 -348c660,8ffffe0 -348c678,8deea00 -348c67c,c837e90 -348c680,cc0 -348c684,2ea0 -348c688,bd20 -348c68c,bd400 -348c690,bd4000 -348c694,bd40000 -348c698,2fffffd0 -348c6b0,7ceea00 -348c6b4,c837e90 -348c6b8,cb0 -348c6bc,27e90 -348c6c0,bffb00 -348c6c4,27da0 -348c6c8,ad0 -348c6cc,5c627db0 -348c6d0,9deeb30 -348c6e8,2de00 -348c6ec,bde00 -348c6f0,7d9e00 -348c6f4,2d79e00 -348c6f8,bb09e00 -348c6fc,6e409e00 -348c700,9ffffff7 -348c704,9e00 -348c708,9e00 -348c720,cffff50 -348c724,ca00000 -348c728,ca00000 -348c72c,ceeea00 -348c730,38e90 -348c734,bc0 -348c738,bc0 -348c73c,5c638e90 -348c740,9deda00 -348c758,aeec30 -348c75c,ae83980 -348c760,e900000 -348c764,4faeec40 -348c768,6fd55dc0 -348c76c,5f9009e0 -348c770,e9009e0 -348c774,cd55dc0 -348c778,3ceec40 -348c790,5fffffd0 -348c794,da0 -348c798,7e40 -348c79c,cc00 -348c7a0,4e800 -348c7a4,ad000 -348c7a8,da000 -348c7ac,8e4000 -348c7b0,cc0000 -348c7c8,5ceec30 -348c7cc,dc45db0 -348c7d0,e900bd0 -348c7d4,bc45d90 -348c7d8,4dffc20 -348c7dc,1db45cc0 -348c7e0,5f6009e0 -348c7e4,2eb35cd0 -348c7e8,7deec50 -348c800,6deeb00 -348c804,db37e90 -348c808,5f500bd0 -348c80c,5f500be0 -348c810,db37ee0 -348c814,6dedbe0 -348c818,bc0 -348c81c,9749e70 -348c820,5ded800 -348c840,ec000 -348c844,ec000 -348c854,ec000 -348c858,ec000 -348c878,ec000 -348c87c,ec000 -348c88c,dd000 -348c890,ec000 -348c894,4f8000 -348c898,9d0000 -348c8b0,29c8 -348c8b4,7bed93 -348c8b8,8dda4000 -348c8bc,8dda4000 -348c8c0,7bec93 -348c8c4,29c8 -348c8ec,affffff8 -348c8f8,affffff8 -348c920,ac810000 -348c924,4adeb600 -348c928,6add6 -348c92c,6add6 -348c930,4adeb600 -348c934,ac810000 -348c950,4beec30 -348c954,9a46ea0 -348c958,1da0 -348c95c,2cd30 -348c960,cc100 -348c964,e9000 +348c660,8e000 +348c664,8e000 +348c668,8ffffe0 +348c680,8deea00 +348c684,c837e90 +348c688,cc0 +348c68c,2ea0 +348c690,bd20 +348c694,bd400 +348c698,bd4000 +348c69c,bd40000 +348c6a0,2fffffd0 +348c6b8,7ceea00 +348c6bc,c837e90 +348c6c0,cb0 +348c6c4,27e90 +348c6c8,bffb00 +348c6cc,27da0 +348c6d0,ad0 +348c6d4,5c627db0 +348c6d8,9deeb30 +348c6f0,2de00 +348c6f4,bde00 +348c6f8,7d9e00 +348c6fc,2d79e00 +348c700,bb09e00 +348c704,6e409e00 +348c708,9ffffff7 +348c70c,9e00 +348c710,9e00 +348c728,cffff50 +348c72c,ca00000 +348c730,ca00000 +348c734,ceeea00 +348c738,38e90 +348c73c,bc0 +348c740,bc0 +348c744,5c638e90 +348c748,9deda00 +348c760,aeec30 +348c764,ae83980 +348c768,e900000 +348c76c,4faeec40 +348c770,6fd55dc0 +348c774,5f9009e0 +348c778,e9009e0 +348c77c,cd55dc0 +348c780,3ceec40 +348c798,5fffffd0 +348c79c,da0 +348c7a0,7e40 +348c7a4,cc00 +348c7a8,4e800 +348c7ac,ad000 +348c7b0,da000 +348c7b4,8e4000 +348c7b8,cc0000 +348c7d0,5ceec30 +348c7d4,dc45db0 +348c7d8,e900bd0 +348c7dc,bc45d90 +348c7e0,4dffc20 +348c7e4,1db45cc0 +348c7e8,5f6009e0 +348c7ec,2eb35cd0 +348c7f0,7deec50 +348c808,6deeb00 +348c80c,db37e90 +348c810,5f500bd0 +348c814,5f500be0 +348c818,db37ee0 +348c81c,6dedbe0 +348c820,bc0 +348c824,9749e70 +348c828,5ded800 +348c848,ec000 +348c84c,ec000 +348c85c,ec000 +348c860,ec000 +348c880,ec000 +348c884,ec000 +348c894,dd000 +348c898,ec000 +348c89c,4f8000 +348c8a0,9d0000 +348c8b8,29c8 +348c8bc,7bed93 +348c8c0,8dda4000 +348c8c4,8dda4000 +348c8c8,7bec93 +348c8cc,29c8 +348c8f4,affffff8 +348c900,affffff8 +348c928,ac810000 +348c92c,4adeb600 +348c930,6add6 +348c934,6add6 +348c938,4adeb600 +348c93c,ac810000 +348c958,4beec30 +348c95c,9a46ea0 +348c960,1da0 +348c964,2cd30 +348c968,cc100 348c96c,e9000 -348c970,e9000 -348c988,1aeed70 -348c98c,cd739e4 -348c990,7e2000c9 -348c994,ba0aeeca -348c998,d76e64da -348c99c,d69c00aa +348c974,e9000 +348c978,e9000 +348c990,1aeed70 +348c994,cd739e4 +348c998,7e2000c9 +348c99c,ba0aeeca 348c9a0,d76e64da -348c9a4,ba0aeeca -348c9a8,6e400000 -348c9ac,ad83000 -348c9b0,8dee90 -348c9c0,3ed000 -348c9c4,9de600 -348c9c8,cbcb00 -348c9cc,3e8ad00 -348c9d0,8e26f60 -348c9d4,cc00ea0 -348c9d8,2effffd0 -348c9dc,8e5008f5 -348c9e0,cd0001ea -348c9f8,effec40 -348c9fc,e905dc0 -348ca00,e900ae0 +348c9a4,d69c00aa +348c9a8,d76e64da +348c9ac,ba0aeeca +348c9b0,6e400000 +348c9b4,ad83000 +348c9b8,8dee90 +348c9c8,3ed000 +348c9cc,9de600 +348c9d0,cbcb00 +348c9d4,3e8ad00 +348c9d8,8e26f60 +348c9dc,cc00ea0 +348c9e0,2effffd0 +348c9e4,8e5008f5 +348c9e8,cd0001ea +348ca00,effec40 348ca04,e905dc0 -348ca08,efffd50 -348ca0c,e904bd2 -348ca10,e9005f6 -348ca14,e904be3 -348ca18,effed80 -348ca30,9ded80 -348ca34,8e936b0 -348ca38,db00000 -348ca3c,3f900000 -348ca40,5f700000 -348ca44,1e900000 -348ca48,db00000 -348ca4c,8e947b0 -348ca50,9ded80 -348ca68,5ffed800 -348ca6c,5f65ae80 -348ca70,5f600cd0 -348ca74,5f6009e0 -348ca78,5f6009f0 +348ca08,e900ae0 +348ca0c,e905dc0 +348ca10,efffd50 +348ca14,e904bd2 +348ca18,e9005f6 +348ca1c,e904be3 +348ca20,effed80 +348ca38,9ded80 +348ca3c,8e936b0 +348ca40,db00000 +348ca44,3f900000 +348ca48,5f700000 +348ca4c,1e900000 +348ca50,db00000 +348ca54,8e947b0 +348ca58,9ded80 +348ca70,5ffed800 +348ca74,5f65ae80 +348ca78,5f600cd0 348ca7c,5f6009e0 -348ca80,5f600cd0 -348ca84,5f65ae80 -348ca88,5ffed800 -348caa0,dffffe0 -348caa4,db00000 -348caa8,db00000 +348ca80,5f6009f0 +348ca84,5f6009e0 +348ca88,5f600cd0 +348ca8c,5f65ae80 +348ca90,5ffed800 +348caa8,dffffe0 348caac,db00000 -348cab0,dffffc0 +348cab0,db00000 348cab4,db00000 -348cab8,db00000 +348cab8,dffffc0 348cabc,db00000 -348cac0,dfffff0 -348cad8,bfffff4 -348cadc,bd00000 -348cae0,bd00000 +348cac0,db00000 +348cac4,db00000 +348cac8,dfffff0 +348cae0,bfffff4 348cae4,bd00000 -348cae8,bffffc0 +348cae8,bd00000 348caec,bd00000 -348caf0,bd00000 +348caf0,bffffc0 348caf4,bd00000 348caf8,bd00000 -348cb10,1aeed60 -348cb14,be738a0 -348cb18,4e900000 -348cb1c,8f400000 -348cb20,9f10bff2 -348cb24,7f4007f2 -348cb28,4e9007f2 -348cb2c,be739f2 -348cb30,1beed90 -348cb48,5f6009e0 -348cb4c,5f6009e0 +348cafc,bd00000 +348cb00,bd00000 +348cb18,1aeed60 +348cb1c,be738a0 +348cb20,4e900000 +348cb24,8f400000 +348cb28,9f10bff2 +348cb2c,7f4007f2 +348cb30,4e9007f2 +348cb34,be739f2 +348cb38,1beed90 348cb50,5f6009e0 348cb54,5f6009e0 -348cb58,5fffffe0 +348cb58,5f6009e0 348cb5c,5f6009e0 -348cb60,5f6009e0 +348cb60,5fffffe0 348cb64,5f6009e0 348cb68,5f6009e0 -348cb80,dffffb0 -348cb84,db000 -348cb88,db000 +348cb6c,5f6009e0 +348cb70,5f6009e0 +348cb88,dffffb0 348cb8c,db000 348cb90,db000 348cb94,db000 348cb98,db000 348cb9c,db000 -348cba0,dffffb0 -348cbb8,cfff40 -348cbbc,7f40 -348cbc0,7f40 +348cba0,db000 +348cba4,db000 +348cba8,dffffb0 +348cbc0,cfff40 348cbc4,7f40 348cbc8,7f40 -348cbcc,7f30 -348cbd0,75009e00 -348cbd4,8d64dc00 -348cbd8,2beec500 -348cbf0,5f6009e7 -348cbf4,5f609e70 -348cbf8,5f69e700 -348cbfc,5fbe8000 -348cc00,5fedb000 -348cc04,5f87e800 -348cc08,5f60ae40 -348cc0c,5f601dc0 -348cc10,5f6006ea -348cc28,cc00000 -348cc2c,cc00000 +348cbcc,7f40 +348cbd0,7f40 +348cbd4,7f30 +348cbd8,75009e00 +348cbdc,8d64dc00 +348cbe0,2beec500 +348cbf8,5f6009e7 +348cbfc,5f609e70 +348cc00,5f69e700 +348cc04,5fbe8000 +348cc08,5fedb000 +348cc0c,5f87e800 +348cc10,5f60ae40 +348cc14,5f601dc0 +348cc18,5f6006ea 348cc30,cc00000 348cc34,cc00000 348cc38,cc00000 348cc3c,cc00000 348cc40,cc00000 348cc44,cc00000 -348cc48,cfffff7 -348cc60,afa00cf8 -348cc64,aed02ee8 -348cc68,add59be8 -348cc6c,adaac8e8 -348cc70,ad5de1e8 -348cc74,ad0db0e8 -348cc78,ad0000e8 -348cc7c,ad0000e8 +348cc48,cc00000 +348cc4c,cc00000 +348cc50,cfffff7 +348cc68,afa00cf8 +348cc6c,aed02ee8 +348cc70,add59be8 +348cc74,adaac8e8 +348cc78,ad5de1e8 +348cc7c,ad0db0e8 348cc80,ad0000e8 -348cc98,5fc008e0 -348cc9c,5fe608e0 -348cca0,5fcb08e0 -348cca4,5f7e48e0 -348cca8,5f5ca8e0 -348ccac,5f57e8e0 -348ccb0,5f50dce0 -348ccb4,5f509ee0 -348ccb8,5f502ee0 -348ccd0,4ceeb20 -348ccd4,cd56ea0 -348ccd8,3e800ae0 -348ccdc,7f5008f2 -348cce0,7f4008f4 +348cc84,ad0000e8 +348cc88,ad0000e8 +348cca0,5fc008e0 +348cca4,5fe608e0 +348cca8,5fcb08e0 +348ccac,5f7e48e0 +348ccb0,5f5ca8e0 +348ccb4,5f57e8e0 +348ccb8,5f50dce0 +348ccbc,5f509ee0 +348ccc0,5f502ee0 +348ccd8,4ceeb20 +348ccdc,cd56ea0 +348cce0,3e800ae0 348cce4,7f5008f2 -348cce8,3e800ae0 -348ccec,cd56eb0 -348ccf0,4ceeb20 -348cd08,dffed60 -348cd0c,db05ce2 -348cd10,db006f6 -348cd14,db006f6 -348cd18,db05ce2 -348cd1c,dffed60 -348cd20,db00000 -348cd24,db00000 +348cce8,7f4008f4 +348ccec,7f5008f2 +348ccf0,3e800ae0 +348ccf4,cd56eb0 +348ccf8,4ceeb20 +348cd10,dffed60 +348cd14,db05ce2 +348cd18,db006f6 +348cd1c,db006f6 +348cd20,db05ce2 +348cd24,dffed60 348cd28,db00000 -348cd40,4ceeb20 -348cd44,cd56ea0 -348cd48,3e800ae0 -348cd4c,7f5008f2 -348cd50,7f4008f4 -348cd54,7f5008f1 -348cd58,3e800ad0 -348cd5c,cd56ea0 -348cd60,4cefc20 -348cd64,ae50 -348cd68,c80 -348cd78,5ffeeb20 -348cd7c,5f717eb0 -348cd80,5f700cd0 -348cd84,5f716ea0 -348cd88,5fffea00 -348cd8c,5f72ae40 -348cd90,5f700db0 -348cd94,5f7008e5 -348cd98,5f7000db -348cdb0,6ceeb30 -348cdb4,dc45a90 -348cdb8,4f600000 -348cdbc,ec60000 -348cdc0,5ceeb40 -348cdc4,6cc0 -348cdc8,8e0 -348cdcc,c735cd0 -348cdd0,8deec50 -348cde8,cffffffb -348cdec,db000 -348cdf0,db000 +348cd2c,db00000 +348cd30,db00000 +348cd48,4ceeb20 +348cd4c,cd56ea0 +348cd50,3e800ae0 +348cd54,7f5008f2 +348cd58,7f4008f4 +348cd5c,7f5008f1 +348cd60,3e800ad0 +348cd64,cd56ea0 +348cd68,4cefc20 +348cd6c,ae50 +348cd70,c80 +348cd80,5ffeeb20 +348cd84,5f717eb0 +348cd88,5f700cd0 +348cd8c,5f716ea0 +348cd90,5fffea00 +348cd94,5f72ae40 +348cd98,5f700db0 +348cd9c,5f7008e5 +348cda0,5f7000db +348cdb8,6ceeb30 +348cdbc,dc45a90 +348cdc0,4f600000 +348cdc4,ec60000 +348cdc8,5ceeb40 +348cdcc,6cc0 +348cdd0,8e0 +348cdd4,c735cd0 +348cdd8,8deec50 +348cdf0,cffffffb 348cdf4,db000 348cdf8,db000 348cdfc,db000 348ce00,db000 348ce04,db000 348ce08,db000 -348ce20,4f7009e0 -348ce24,4f7009e0 +348ce0c,db000 +348ce10,db000 348ce28,4f7009e0 348ce2c,4f7009e0 348ce30,4f7009e0 -348ce34,3f7009e0 -348ce38,2e700ad0 -348ce3c,dc45dc0 -348ce40,5ceec40 -348ce58,ad0003e8 -348ce5c,6f5008e3 -348ce60,e900bc0 -348ce64,bc00d90 -348ce68,8e15e40 -348ce6c,2e7ad00 -348ce70,cbca00 -348ce74,9de600 -348ce78,3ed000 -348ce90,e80000ad -348ce94,da0000cb -348ce98,cb0000da -348ce9c,ac0ec0e8 -348cea0,8d6de1e5 -348cea4,6e9bd8e0 -348cea8,1ec8acd0 -348ceac,de37ec0 -348ceb0,cd00ea0 -348cec8,6e7007e7 -348cecc,ad21db0 -348ced0,2daad20 -348ced4,7ee700 -348ced8,3ee200 -348cedc,bdda00 -348cee0,7e67e60 -348cee4,3ea00bd0 -348cee8,bd2004e9 -348cf00,ae2005e8 -348cf04,2da00cc0 -348cf08,7e57e50 -348cf0c,ccda00 -348cf10,4ed200 -348cf14,db000 -348cf18,db000 +348ce34,4f7009e0 +348ce38,4f7009e0 +348ce3c,3f7009e0 +348ce40,2e700ad0 +348ce44,dc45dc0 +348ce48,5ceec40 +348ce60,ad0003e8 +348ce64,6f5008e3 +348ce68,e900bc0 +348ce6c,bc00d90 +348ce70,8e15e40 +348ce74,2e7ad00 +348ce78,cbca00 +348ce7c,9de600 +348ce80,3ed000 +348ce98,e80000ad +348ce9c,da0000cb +348cea0,cb0000da +348cea4,ac0ec0e8 +348cea8,8d6de1e5 +348ceac,6e9bd8e0 +348ceb0,1ec8acd0 +348ceb4,de37ec0 +348ceb8,cd00ea0 +348ced0,6e7007e7 +348ced4,ad21db0 +348ced8,2daad20 +348cedc,7ee700 +348cee0,3ee200 +348cee4,bdda00 +348cee8,7e67e60 +348ceec,3ea00bd0 +348cef0,bd2004e9 +348cf08,ae2005e8 +348cf0c,2da00cc0 +348cf10,7e57e50 +348cf14,ccda00 +348cf18,4ed200 348cf1c,db000 348cf20,db000 -348cf38,efffff8 -348cf3c,bd3 -348cf40,7e70 -348cf44,3ea00 -348cf48,bd100 -348cf4c,8e5000 -348cf50,4e90000 -348cf54,cc00000 -348cf58,1ffffffa -348cf68,4ffc00 -348cf6c,4f5000 -348cf70,4f5000 +348cf24,db000 +348cf28,db000 +348cf40,efffff8 +348cf44,bd3 +348cf48,7e70 +348cf4c,3ea00 +348cf50,bd100 +348cf54,8e5000 +348cf58,4e90000 +348cf5c,cc00000 +348cf60,1ffffffa +348cf70,4ffc00 348cf74,4f5000 348cf78,4f5000 348cf7c,4f5000 @@ -11992,21 +11992,21 @@ ef4f9c,0 348cf88,4f5000 348cf8c,4f5000 348cf90,4f5000 -348cf94,4ffc00 -348cfa8,6e500000 -348cfac,cb00000 -348cfb0,7e30000 -348cfb4,da0000 -348cfb8,9e2000 -348cfbc,1e9000 -348cfc0,ad000 -348cfc4,3e800 -348cfc8,bc00 -348cfcc,4e60 -348cfd0,bc0 -348cfd8,dfe000 -348cfdc,8e000 -348cfe0,8e000 +348cf94,4f5000 +348cf98,4f5000 +348cf9c,4ffc00 +348cfb0,6e500000 +348cfb4,cb00000 +348cfb8,7e30000 +348cfbc,da0000 +348cfc0,9e2000 +348cfc4,1e9000 +348cfc8,ad000 +348cfcc,3e800 +348cfd0,bc00 +348cfd4,4e60 +348cfd8,bc0 +348cfe0,dfe000 348cfe4,8e000 348cfe8,8e000 348cfec,8e000 @@ -12015,253 +12015,253 @@ ef4f9c,0 348cff8,8e000 348cffc,8e000 348d000,8e000 -348d004,dfe000 -348d018,5ed200 -348d01c,dcdb00 -348d020,ad25e80 -348d024,7e5007e5 -348d07c,fffffffd -348d084,2ca0000 -348d088,2c9000 -348d0c8,5ceeb10 -348d0cc,b936da0 -348d0d0,bc0 -348d0d4,8deffc0 -348d0d8,3e930bd0 -348d0dc,4f827ed0 -348d0e0,aeedbd0 -348d0f0,d900000 -348d0f4,d900000 +348d004,8e000 +348d008,8e000 +348d00c,dfe000 +348d020,5ed200 +348d024,dcdb00 +348d028,ad25e80 +348d02c,7e5007e5 +348d084,fffffffd +348d08c,2ca0000 +348d090,2c9000 +348d0d0,5ceeb10 +348d0d4,b936da0 +348d0d8,bc0 +348d0dc,8deffc0 +348d0e0,3e930bd0 +348d0e4,4f827ed0 +348d0e8,aeedbd0 348d0f8,d900000 348d0fc,d900000 -348d100,dbdec40 -348d104,de65dc0 -348d108,db008e0 -348d10c,da007f2 +348d100,d900000 +348d104,d900000 +348d108,dbdec40 +348d10c,de65dc0 348d110,db008e0 -348d114,de64db0 -348d118,dbdec40 -348d138,8ded70 -348d13c,7e936a0 -348d140,cc00000 -348d144,db00000 +348d114,da007f2 +348d118,db008e0 +348d11c,de64db0 +348d120,dbdec40 +348d140,8ded70 +348d144,7e936a0 348d148,cc00000 -348d14c,7e936a0 -348d150,8ded70 -348d160,bc0 -348d164,bc0 +348d14c,db00000 +348d150,cc00000 +348d154,7e936a0 +348d158,8ded70 348d168,bc0 348d16c,bc0 -348d170,5dedcc0 -348d174,dc48ec0 -348d178,5f600cc0 -348d17c,7f300bc0 +348d170,bc0 +348d174,bc0 +348d178,5dedcc0 +348d17c,dc48ec0 348d180,5f600cc0 -348d184,dc48ec0 -348d188,5dedcc0 -348d1a8,3beec30 -348d1ac,cd54cc0 -348d1b0,4f6007e0 -348d1b4,6ffffff3 -348d1b8,4f500000 -348d1bc,cc538c0 -348d1c0,3beec60 -348d1d0,5ded0 -348d1d4,cb200 -348d1d8,d9000 -348d1dc,e8000 -348d1e0,dffffd0 +348d184,7f300bc0 +348d188,5f600cc0 +348d18c,dc48ec0 +348d190,5dedcc0 +348d1b0,3beec30 +348d1b4,cd54cc0 +348d1b8,4f6007e0 +348d1bc,6ffffff3 +348d1c0,4f500000 +348d1c4,cc538c0 +348d1c8,3beec60 +348d1d8,5ded0 +348d1dc,cb200 +348d1e0,d9000 348d1e4,e8000 -348d1e8,e8000 +348d1e8,dffffd0 348d1ec,e8000 348d1f0,e8000 348d1f4,e8000 348d1f8,e8000 -348d218,5dedcc0 -348d21c,dc48ec0 -348d220,5f600cc0 -348d224,7f300bc0 +348d1fc,e8000 +348d200,e8000 +348d220,5dedcc0 +348d224,dc48ec0 348d228,5f600cc0 -348d22c,dc48ec0 -348d230,5dedcb0 -348d234,ca0 -348d238,9947e60 -348d23c,4cee900 -348d240,da00000 -348d244,da00000 +348d22c,7f300bc0 +348d230,5f600cc0 +348d234,dc48ec0 +348d238,5dedcb0 +348d23c,ca0 +348d240,9947e60 +348d244,4cee900 348d248,da00000 348d24c,da00000 -348d250,dbded40 -348d254,de65da0 -348d258,db00bc0 -348d25c,da00bc0 -348d260,da00bc0 +348d250,da00000 +348d254,da00000 +348d258,dbded40 +348d25c,de65da0 +348d260,db00bc0 348d264,da00bc0 348d268,da00bc0 -348d278,bc000 -348d288,9ffc000 -348d28c,bc000 -348d290,bc000 +348d26c,da00bc0 +348d270,da00bc0 +348d280,bc000 +348d290,9ffc000 348d294,bc000 348d298,bc000 348d29c,bc000 -348d2a0,effffe0 -348d2b0,7e000 -348d2c0,7ffe000 -348d2c4,7e000 -348d2c8,7e000 +348d2a0,bc000 +348d2a4,bc000 +348d2a8,effffe0 +348d2b8,7e000 +348d2c8,7ffe000 348d2cc,7e000 348d2d0,7e000 348d2d4,7e000 348d2d8,7e000 348d2dc,7e000 -348d2e0,1bd000 -348d2e4,dfe7000 -348d2e8,bc00000 -348d2ec,bc00000 +348d2e0,7e000 +348d2e4,7e000 +348d2e8,1bd000 +348d2ec,dfe7000 348d2f0,bc00000 348d2f4,bc00000 -348d2f8,bc03dc2 -348d2fc,bc3db00 -348d300,bddc000 -348d304,bfce500 -348d308,bd0cd10 -348d30c,bc03db0 -348d310,bc007e8 -348d320,eff4000 -348d324,5f4000 -348d328,5f4000 +348d2f8,bc00000 +348d2fc,bc00000 +348d300,bc03dc2 +348d304,bc3db00 +348d308,bddc000 +348d30c,bfce500 +348d310,bd0cd10 +348d314,bc03db0 +348d318,bc007e8 +348d328,eff4000 348d32c,5f4000 348d330,5f4000 348d334,5f4000 348d338,5f4000 348d33c,5f4000 -348d340,4f5000 -348d344,ea000 -348d348,8efb0 -348d368,8dddaec0 -348d36c,8e4dc5e4 -348d370,8d0cb0e6 -348d374,8d0ba0e7 -348d378,8d0ba0e7 +348d340,5f4000 +348d344,5f4000 +348d348,4f5000 +348d34c,ea000 +348d350,8efb0 +348d370,8dddaec0 +348d374,8e4dc5e4 +348d378,8d0cb0e6 348d37c,8d0ba0e7 348d380,8d0ba0e7 -348d3a0,dbded40 -348d3a4,de65da0 -348d3a8,db00bc0 -348d3ac,da00bc0 -348d3b0,da00bc0 +348d384,8d0ba0e7 +348d388,8d0ba0e7 +348d3a8,dbded40 +348d3ac,de65da0 +348d3b0,db00bc0 348d3b4,da00bc0 348d3b8,da00bc0 -348d3d8,4ceeb20 -348d3dc,cd56da0 -348d3e0,1e700ad0 -348d3e4,5f6008e0 +348d3bc,da00bc0 +348d3c0,da00bc0 +348d3e0,4ceeb20 +348d3e4,cd56da0 348d3e8,1e700ad0 -348d3ec,cd46db0 -348d3f0,4ceeb20 -348d410,dbdec30 -348d414,de65db0 -348d418,db009e0 -348d41c,da007e0 -348d420,db008e0 -348d424,de65db0 -348d428,dbeec40 -348d42c,d900000 -348d430,d900000 +348d3ec,5f6008e0 +348d3f0,1e700ad0 +348d3f4,cd46db0 +348d3f8,4ceeb20 +348d418,dbdec30 +348d41c,de65db0 +348d420,db009e0 +348d424,da007e0 +348d428,db008e0 +348d42c,de65db0 +348d430,dbeec40 348d434,d900000 -348d448,4cedcc0 -348d44c,cc47ec0 -348d450,1e700cc0 -348d454,5f600bc0 -348d458,2e700cc0 -348d45c,cc47ec0 -348d460,5cedbc0 -348d464,ac0 -348d468,ac0 +348d438,d900000 +348d43c,d900000 +348d450,4cedcc0 +348d454,cc47ec0 +348d458,1e700cc0 +348d45c,5f600bc0 +348d460,2e700cc0 +348d464,cc47ec0 +348d468,5cedbc0 348d46c,ac0 -348d480,ccdef9 -348d484,ce8300 -348d488,cb0000 -348d48c,ca0000 -348d490,ca0000 +348d470,ac0 +348d474,ac0 +348d488,ccdef9 +348d48c,ce8300 +348d490,cb0000 348d494,ca0000 348d498,ca0000 -348d4b8,4ceea10 -348d4bc,bd45b60 -348d4c0,bd40000 -348d4c4,3bddb20 -348d4c8,4da0 -348d4cc,b945ea0 -348d4d0,5ceeb20 -348d4e8,8e0000 -348d4ec,8e0000 -348d4f0,6fffffb0 +348d49c,ca0000 +348d4a0,ca0000 +348d4c0,4ceea10 +348d4c4,bd45b60 +348d4c8,bd40000 +348d4cc,3bddb20 +348d4d0,4da0 +348d4d4,b945ea0 +348d4d8,5ceeb20 +348d4f0,8e0000 348d4f4,8e0000 -348d4f8,8e0000 +348d4f8,6fffffb0 348d4fc,8e0000 348d500,8e0000 -348d504,6e7000 -348d508,befb0 -348d528,da00bc0 -348d52c,da00bc0 +348d504,8e0000 +348d508,8e0000 +348d50c,6e7000 +348d510,befb0 348d530,da00bc0 348d534,da00bc0 348d538,da00bc0 -348d53c,bd47ec0 -348d540,5dedbc0 -348d560,6e3007e3 -348d564,d900bc0 -348d568,ad01e80 -348d56c,5e48e20 -348d570,dacb00 -348d574,9de700 -348d578,3ee000 -348d598,e80000ac -348d59c,ca0000ca -348d5a0,ac0db0e7 -348d5a4,6e3dd5e2 -348d5a8,eabcad0 -348d5ac,ce79eb0 -348d5b0,ae15f80 -348d5d0,3da00bc0 -348d5d4,6e69e40 -348d5d8,9ee700 -348d5dc,2ed000 -348d5e0,ccda00 -348d5e4,9e46e70 -348d5e8,6e7009e4 -348d608,6e5005e5 -348d60c,da00bd0 -348d610,9e00e90 -348d614,3e78e30 -348d618,cccc00 -348d61c,7ee700 -348d620,de000 -348d624,da000 -348d628,8e5000 -348d62c,dea0000 -348d640,bffffc0 -348d644,5e70 -348d648,3d900 -348d64c,cb000 -348d650,bd2000 -348d654,9e40000 -348d658,dffffc0 -348d668,6dea0 -348d66c,bd300 -348d670,cb000 -348d674,cb000 -348d678,5ea000 -348d67c,bfd2000 -348d680,7e9000 -348d684,db000 -348d688,cb000 -348d68c,cb000 -348d690,bd400 -348d694,5dea0 -348d6a0,ca000 -348d6a4,ca000 +348d53c,da00bc0 +348d540,da00bc0 +348d544,bd47ec0 +348d548,5dedbc0 +348d568,6e3007e3 +348d56c,d900bc0 +348d570,ad01e80 +348d574,5e48e20 +348d578,dacb00 +348d57c,9de700 +348d580,3ee000 +348d5a0,e80000ac +348d5a4,ca0000ca +348d5a8,ac0db0e7 +348d5ac,6e3dd5e2 +348d5b0,eabcad0 +348d5b4,ce79eb0 +348d5b8,ae15f80 +348d5d8,3da00bc0 +348d5dc,6e69e40 +348d5e0,9ee700 +348d5e4,2ed000 +348d5e8,ccda00 +348d5ec,9e46e70 +348d5f0,6e7009e4 +348d610,6e5005e5 +348d614,da00bd0 +348d618,9e00e90 +348d61c,3e78e30 +348d620,cccc00 +348d624,7ee700 +348d628,de000 +348d62c,da000 +348d630,8e5000 +348d634,dea0000 +348d648,bffffc0 +348d64c,5e70 +348d650,3d900 +348d654,cb000 +348d658,bd2000 +348d65c,9e40000 +348d660,dffffc0 +348d670,6dea0 +348d674,bd300 +348d678,cb000 +348d67c,cb000 +348d680,5ea000 +348d684,bfd2000 +348d688,7e9000 +348d68c,db000 +348d690,cb000 +348d694,cb000 +348d698,bd400 +348d69c,5dea0 348d6a8,ca000 348d6ac,ca000 348d6b0,ca000 @@ -12273,340 +12273,340 @@ ef4f9c,0 348d6c8,ca000 348d6cc,ca000 348d6d0,ca000 -348d6d8,bed3000 -348d6dc,4e9000 -348d6e0,da000 -348d6e4,ca000 -348d6e8,bc400 -348d6ec,5efa0 -348d6f0,bd500 -348d6f4,cb000 -348d6f8,da000 -348d6fc,da000 -348d700,5e8000 -348d704,bec2000 -348d728,5ded83a7 -348d72c,9838dec3 -348d7a0,7f024429 -348d7a4,3c334133 -348d7a8,41334633 -348d7ac,44297f02 -348d7dc,5409 -348d7e0,4dc548ff -348d7e4,41ff43ff -348d7e8,47ff49ff -348d7ec,43ff20c5 -348d7f0,c0000 -348d81c,3f75 -348d820,49ff33ff -348d824,28ff2dff -348d828,33ff39ff -348d82c,3cff00ff -348d830,770000 -348d85c,329d -348d860,37ff1bff -348d864,21ff28ff -348d868,2fff35ff -348d86c,3cff00ff -348d870,9d0000 -348d89c,329e -348d8a0,35ff21ff -348d8a4,28ff06ff -348d8a8,9ff3cff -348d8ac,42ff00ff -348d8b0,9e0000 -348d8dc,359e -348d8e0,39ff27ff -348d8e4,2eff00ff -348d8e8,2ff42ff -348d8ec,48ff00ff -348d8f0,9e0000 -348d91c,3a9e -348d920,3eff2eff -348d924,35ff00ff -348d928,dff48ff -348d92c,4dff00ff -348d930,9e0000 -348d95c,3e9e -348d960,42ff35ff -348d964,3bff1bff -348d968,27ff4dff -348d96c,53ff00ff -348d970,9e0000 -348d99c,439e -348d9a0,47ff3bff -348d9a4,41ff47ff -348d9a8,4dff52ff -348d9ac,58ff00ff -348d9b0,9e0000 -348d9dc,4d9e -348d9e0,4dff41ff -348d9e4,47ff4dff -348d9e8,52ff57ff -348d9ec,5cff00ff -348d9f0,9e0000 -348da0c,3f04474f -348da10,3e663e66 -348da14,43664666 -348da18,48664d66 -348da1c,57665bc5 -348da20,53ff47ff -348da24,4dff52ff -348da28,57ff5cff -348da2c,60ff0eff -348da30,19c56666 -348da34,66666466 -348da38,61665f66 -348da3c,5c665a66 -348da40,504f3f04 -348da48,6605 -348da4c,4ec34bff -348da50,41ff41ff -348da54,45ff48ff -348da58,4cff4fff -348da5c,55ff59ff -348da60,4fff4dff -348da64,52ff57ff -348da68,5cff60ff -348da6c,64ff61ff -348da70,67ff66ff -348da74,64ff62ff -348da78,60ff5dff -348da7c,5bff57ff -348da80,49ff0ec3 -348da84,50000 -348da88,3958 -348da8c,44ff31ff -348da90,20ff25ff -348da94,2bff31ff -348da98,38ff3eff -348da9c,44ff49ff -348daa0,4dff52ff -348daa4,57ff5cff -348daa8,60ff64ff -348daac,68ff67ff -348dab0,64ff60ff -348dab4,5cff58ff -348dab8,53ff4eff -348dabc,48ff43ff -348dac0,32ff00ff -348dac4,580000 -348dac8,2f71 -348dacc,36ff1dff -348dad0,1fff26ff -348dad4,2dff34ff -348dad8,3aff41ff -348dadc,47ff4cff -348dae0,52ff57ff -348dae4,5cff60ff -348dae8,64ff68ff -348daec,67ff64ff -348daf0,60ff5bff -348daf4,57ff51ff -348daf8,4cff46ff -348dafc,40ff3aff -348db00,27ff00ff -348db04,710000 -348db08,2f71 -348db0c,36ff21ff -348db10,16ff00ff -348db14,ff00ff -348db18,2cff47ff -348db1c,4cff52ff -348db20,57ff5cff -348db24,60ff64ff -348db28,67ff67ff -348db2c,64ff60ff -348db30,5bff57ff -348db34,52ff0dff -348db38,ff00ff -348db3c,aff33ff -348db40,21ff00ff -348db44,710000 -348db48,3371 -348db4c,3aff28ff -348db50,22ff0fff -348db54,13ff19ff -348db58,39ff4cff -348db5c,52ff57ff -348db60,5bff60ff -348db64,64ff67ff -348db68,67ff64ff -348db6c,60ff5cff -348db70,57ff52ff -348db74,4cff1dff -348db78,12ff14ff -348db7c,19ff2dff -348db80,1bff00ff -348db84,710000 -348db88,3871 -348db8c,3dff2fff -348db90,33ff3aff -348db94,40ff46ff -348db98,4cff51ff -348db9c,57ff5bff -348dba0,60ff64ff -348dba4,67ff68ff -348dba8,64ff60ff -348dbac,5cff57ff -348dbb0,52ff4cff -348dbb4,47ff41ff -348dbb8,3aff34ff -348dbbc,2dff26ff -348dbc0,12ff00ff -348dbc4,710000 -348dbc8,3569 -348dbcc,37ff33ff -348dbd0,3aff40ff -348dbd4,46ff4cff -348dbd8,51ff57ff -348dbdc,5bff60ff -348dbe0,64ff67ff -348dbe4,68ff64ff -348dbe8,60ff5cff -348dbec,57ff52ff -348dbf0,4dff47ff -348dbf4,41ff3aff -348dbf8,34ff2dff -348dbfc,26ff1fff -348dc00,6ff00ff -348dc04,690000 -348dc08,1e21 -348dc0c,2f600ff -348dc10,ff00ff -348dc14,ff00ff +348d6d4,ca000 +348d6d8,ca000 +348d6e0,bed3000 +348d6e4,4e9000 +348d6e8,da000 +348d6ec,ca000 +348d6f0,bc400 +348d6f4,5efa0 +348d6f8,bd500 +348d6fc,cb000 +348d700,da000 +348d704,da000 +348d708,5e8000 +348d70c,bec2000 +348d730,5ded83a7 +348d734,9838dec3 +348d7a8,7f024429 +348d7ac,3c334133 +348d7b0,41334633 +348d7b4,44297f02 +348d7e4,5409 +348d7e8,4dc548ff +348d7ec,41ff43ff +348d7f0,47ff49ff +348d7f4,43ff20c5 +348d7f8,c0000 +348d824,3f75 +348d828,49ff33ff +348d82c,28ff2dff +348d830,33ff39ff +348d834,3cff00ff +348d838,770000 +348d864,329d +348d868,37ff1bff +348d86c,21ff28ff +348d870,2fff35ff +348d874,3cff00ff +348d878,9d0000 +348d8a4,329e +348d8a8,35ff21ff +348d8ac,28ff06ff +348d8b0,9ff3cff +348d8b4,42ff00ff +348d8b8,9e0000 +348d8e4,359e +348d8e8,39ff27ff +348d8ec,2eff00ff +348d8f0,2ff42ff +348d8f4,48ff00ff +348d8f8,9e0000 +348d924,3a9e +348d928,3eff2eff +348d92c,35ff00ff +348d930,dff48ff +348d934,4dff00ff +348d938,9e0000 +348d964,3e9e +348d968,42ff35ff +348d96c,3bff1bff +348d970,27ff4dff +348d974,53ff00ff +348d978,9e0000 +348d9a4,439e +348d9a8,47ff3bff +348d9ac,41ff47ff +348d9b0,4dff52ff +348d9b4,58ff00ff +348d9b8,9e0000 +348d9e4,4d9e +348d9e8,4dff41ff +348d9ec,47ff4dff +348d9f0,52ff57ff +348d9f4,5cff00ff +348d9f8,9e0000 +348da14,3f04474f +348da18,3e663e66 +348da1c,43664666 +348da20,48664d66 +348da24,57665bc5 +348da28,53ff47ff +348da2c,4dff52ff +348da30,57ff5cff +348da34,60ff0eff +348da38,19c56666 +348da3c,66666466 +348da40,61665f66 +348da44,5c665a66 +348da48,504f3f04 +348da50,6605 +348da54,4ec34bff +348da58,41ff41ff +348da5c,45ff48ff +348da60,4cff4fff +348da64,55ff59ff +348da68,4fff4dff +348da6c,52ff57ff +348da70,5cff60ff +348da74,64ff61ff +348da78,67ff66ff +348da7c,64ff62ff +348da80,60ff5dff +348da84,5bff57ff +348da88,49ff0ec3 +348da8c,50000 +348da90,3958 +348da94,44ff31ff +348da98,20ff25ff +348da9c,2bff31ff +348daa0,38ff3eff +348daa4,44ff49ff +348daa8,4dff52ff +348daac,57ff5cff +348dab0,60ff64ff +348dab4,68ff67ff +348dab8,64ff60ff +348dabc,5cff58ff +348dac0,53ff4eff +348dac4,48ff43ff +348dac8,32ff00ff +348dacc,580000 +348dad0,2f71 +348dad4,36ff1dff +348dad8,1fff26ff +348dadc,2dff34ff +348dae0,3aff41ff +348dae4,47ff4cff +348dae8,52ff57ff +348daec,5cff60ff +348daf0,64ff68ff +348daf4,67ff64ff +348daf8,60ff5bff +348dafc,57ff51ff +348db00,4cff46ff +348db04,40ff3aff +348db08,27ff00ff +348db0c,710000 +348db10,2f71 +348db14,36ff21ff +348db18,16ff00ff +348db1c,ff00ff +348db20,2cff47ff +348db24,4cff52ff +348db28,57ff5cff +348db2c,60ff64ff +348db30,67ff67ff +348db34,64ff60ff +348db38,5bff57ff +348db3c,52ff0dff +348db40,ff00ff +348db44,aff33ff +348db48,21ff00ff +348db4c,710000 +348db50,3371 +348db54,3aff28ff +348db58,22ff0fff +348db5c,13ff19ff +348db60,39ff4cff +348db64,52ff57ff +348db68,5bff60ff +348db6c,64ff67ff +348db70,67ff64ff +348db74,60ff5cff +348db78,57ff52ff +348db7c,4cff1dff +348db80,12ff14ff +348db84,19ff2dff +348db88,1bff00ff +348db8c,710000 +348db90,3871 +348db94,3dff2fff +348db98,33ff3aff +348db9c,40ff46ff +348dba0,4cff51ff +348dba4,57ff5bff +348dba8,60ff64ff +348dbac,67ff68ff +348dbb0,64ff60ff +348dbb4,5cff57ff +348dbb8,52ff4cff +348dbbc,47ff41ff +348dbc0,3aff34ff +348dbc4,2dff26ff +348dbc8,12ff00ff +348dbcc,710000 +348dbd0,3569 +348dbd4,37ff33ff +348dbd8,3aff40ff +348dbdc,46ff4cff +348dbe0,51ff57ff +348dbe4,5bff60ff +348dbe8,64ff67ff +348dbec,68ff64ff +348dbf0,60ff5cff +348dbf4,57ff52ff +348dbf8,4dff47ff +348dbfc,41ff3aff +348dc00,34ff2dff +348dc04,26ff1fff +348dc08,6ff00ff +348dc0c,690000 +348dc10,1e21 +348dc14,2f600ff 348dc18,ff00ff -348dc1c,2ff1eff -348dc20,60ff68ff -348dc24,64ff60ff -348dc28,5cff57ff -348dc2c,52ff2cff -348dc30,6ff00ff -348dc34,ff00ff -348dc38,ff00ff +348dc1c,ff00ff +348dc20,ff00ff +348dc24,2ff1eff +348dc28,60ff68ff +348dc2c,64ff60ff +348dc30,5cff57ff +348dc34,52ff2cff +348dc38,6ff00ff 348dc3c,ff00ff -348dc40,ff00f6 -348dc44,210000 -348dc4c,3b00ae -348dc50,cc00cc -348dc54,cc00cc +348dc40,ff00ff +348dc44,ff00ff +348dc48,ff00f6 +348dc4c,210000 +348dc54,3b00ae 348dc58,cc00cc -348dc5c,cc03ec -348dc60,62ff64ff -348dc64,60ff5cff -348dc68,57ff52ff -348dc6c,4dff00ff -348dc70,ec00cc -348dc74,cc00cc -348dc78,cc00cc +348dc5c,cc00cc +348dc60,cc00cc +348dc64,cc03ec +348dc68,62ff64ff +348dc6c,60ff5cff +348dc70,57ff52ff +348dc74,4dff00ff +348dc78,ec00cc 348dc7c,cc00cc -348dc80,ae003b -348dc9c,5f9e -348dca0,65ff60ff -348dca4,5cff57ff -348dca8,52ff4dff -348dcac,47ff00ff -348dcb0,9e0000 -348dcdc,659e -348dce0,63ff5cff -348dce4,57ff52ff -348dce8,4dff47ff -348dcec,41ff00ff -348dcf0,9e0000 -348dd1c,649e -348dd20,61ff58ff -348dd24,53ff35ff -348dd28,31ff41ff -348dd2c,3bff00ff -348dd30,9e0000 -348dd5c,609e -348dd60,5eff53ff -348dd64,4dff00ff -348dd68,ff3bff -348dd6c,35ff00ff -348dd70,9e0000 -348dd9c,5d9e -348dda0,5bff4dff -348dda4,48ff00ff -348dda8,6ff35ff -348ddac,2eff00ff -348ddb0,9e0000 -348dddc,5a9e -348dde0,57ff48ff -348dde4,42ff03ff -348dde8,cff2eff -348ddec,28ff00ff -348ddf0,9e0000 -348de1c,559e -348de20,53ff42ff -348de24,3cff2dff -348de28,28ff28ff -348de2c,1fff00ff -348de30,9e0000 -348de5c,4b91 -348de60,44ff33ff -348de64,35ff2fff -348de68,28ff1fff -348de6c,7ff00ff -348de70,900000 -348de9c,1229 -348dea0,f700ff -348dea4,ff00ff -348dea8,ff00ff -348deac,ff00f8 -348deb0,2e0000 -348dee0,30008c -348dee4,990099 -348dee8,990099 -348deec,8c0030 -348df48,f0f0f0f0 -348df4c,f0f0f0f0 +348dc80,cc00cc +348dc84,cc00cc +348dc88,ae003b +348dca4,5f9e +348dca8,65ff60ff +348dcac,5cff57ff +348dcb0,52ff4dff +348dcb4,47ff00ff +348dcb8,9e0000 +348dce4,659e +348dce8,63ff5cff +348dcec,57ff52ff +348dcf0,4dff47ff +348dcf4,41ff00ff +348dcf8,9e0000 +348dd24,649e +348dd28,61ff58ff +348dd2c,53ff35ff +348dd30,31ff41ff +348dd34,3bff00ff +348dd38,9e0000 +348dd64,609e +348dd68,5eff53ff +348dd6c,4dff00ff +348dd70,ff3bff +348dd74,35ff00ff +348dd78,9e0000 +348dda4,5d9e +348dda8,5bff4dff +348ddac,48ff00ff +348ddb0,6ff35ff +348ddb4,2eff00ff +348ddb8,9e0000 +348dde4,5a9e +348dde8,57ff48ff +348ddec,42ff03ff +348ddf0,cff2eff +348ddf4,28ff00ff +348ddf8,9e0000 +348de24,559e +348de28,53ff42ff +348de2c,3cff2dff +348de30,28ff28ff +348de34,1fff00ff +348de38,9e0000 +348de64,4b91 +348de68,44ff33ff +348de6c,35ff2fff +348de70,28ff1fff +348de74,7ff00ff +348de78,900000 +348dea4,1229 +348dea8,f700ff +348deac,ff00ff +348deb0,ff00ff +348deb4,ff00f8 +348deb8,2e0000 +348dee8,30008c +348deec,990099 +348def0,990099 +348def4,8c0030 348df50,f0f0f0f0 348df54,f0f0f0f0 348df58,f0f0f0f0 348df5c,f0f0f0f0 -348df60,dff0f0f0 +348df60,f0f0f0f0 348df64,f0f0f0f0 -348df68,f0f0f0f0 -348df6c,f0f0f0df -348df70,dff0f0f0 -348df74,f0f0f0f0 -348df78,f0f0f0f0 -348df7c,f0f0f0df -348df80,dfcff0f0 -348df84,f0f0f0f0 -348df88,f0f0f0f0 -348df8c,f0f0cfcf -348df90,cfcff0f0 -348df94,f0f0f0f0 -348df98,f0f0f0f0 -348df9c,f0f0cfcf -348dfa0,cfcfcff0 -348dfa4,f0f0f0f0 -348dfa8,f0f0f0f0 -348dfac,f0cfcfcf -348dfb0,cfcfcff0 -348dfb4,f0f0f0f0 -348dfb8,f0f0f0f0 -348dfbc,f0cfcfcf -348dfc0,cfcfcfcf -348dfc4,f0f0f0f0 -348dfc8,f0f0f0f0 -348dfcc,cfcfcfcf -348dfd0,cfbfbfbf -348dfd4,f0f0f0f0 -348dfd8,f0f0f0f0 -348dfdc,bfbfbfbf -348dfe0,bfbfbfbf -348dfe4,f0f0f0f0 -348dfe8,f0f0f0bf -348dfec,bfbfbfbf -348dff0,bfbfbfbf -348dff4,bff0f0f0 -348dff8,f0f0f0bf -348dffc,bfbff0f0 -348e000,f0f0f0f0 -348e004,f0f0f0f0 +348df68,dff0f0f0 +348df6c,f0f0f0f0 +348df70,f0f0f0f0 +348df74,f0f0f0df +348df78,dff0f0f0 +348df7c,f0f0f0f0 +348df80,f0f0f0f0 +348df84,f0f0f0df +348df88,dfcff0f0 +348df8c,f0f0f0f0 +348df90,f0f0f0f0 +348df94,f0f0cfcf +348df98,cfcff0f0 +348df9c,f0f0f0f0 +348dfa0,f0f0f0f0 +348dfa4,f0f0cfcf +348dfa8,cfcfcff0 +348dfac,f0f0f0f0 +348dfb0,f0f0f0f0 +348dfb4,f0cfcfcf +348dfb8,cfcfcff0 +348dfbc,f0f0f0f0 +348dfc0,f0f0f0f0 +348dfc4,f0cfcfcf +348dfc8,cfcfcfcf +348dfcc,f0f0f0f0 +348dfd0,f0f0f0f0 +348dfd4,cfcfcfcf +348dfd8,cfbfbfbf +348dfdc,f0f0f0f0 +348dfe0,f0f0f0f0 +348dfe4,bfbfbfbf +348dfe8,bfbfbfbf +348dfec,f0f0f0f0 +348dff0,f0f0f0bf +348dff4,bfbfbfbf +348dff8,bfbfbfbf +348dffc,bff0f0f0 +348e000,f0f0f0bf +348e004,bfbff0f0 348e008,f0f0f0f0 348e00c,f0f0f0f0 348e010,f0f0f0f0 @@ -12628,49 +12628,49 @@ ef4f9c,0 348e050,f0f0f0f0 348e054,f0f0f0f0 348e058,f0f0f0f0 -348e05c,f0f0f0cf -348e060,cff0f0f0 -348e064,f0f0f0f0 -348e068,f0f0f0f0 -348e06c,f0f0f0cf -348e070,cfcff0f0 -348e074,f0f0f0f0 -348e078,f0f0f0f0 -348e07c,f0f0bfcf -348e080,cfcff0f0 -348e084,f0f0f0f0 -348e088,f0f0f0f0 -348e08c,f0f0bfcf -348e090,cfcff0f0 -348e094,f0f0f0f0 -348e098,f0f0f0f0 -348e09c,f0bfcfbf -348e0a0,bfbfbff0 -348e0a4,f0f0f0f0 -348e0a8,f0f0f0f0 -348e0ac,f0bfbfbf -348e0b0,bfbfbff0 -348e0b4,f0f0f0f0 -348e0b8,f0f0f0f0 -348e0bc,bfbfbfbf -348e0c0,bfbfbfbf -348e0c4,f0f0f0f0 -348e0c8,f0f0f0f0 -348e0cc,bfbfbfbf -348e0d0,bfbfbfbf -348e0d4,f0f0f0f0 -348e0d8,f0f0f0f0 -348e0dc,bfbfbfbf -348e0e0,bfbfbfaf -348e0e4,f0f0f0f0 -348e0e8,f0f0f0af -348e0ec,bfbfbfbf -348e0f0,afafaff0 -348e0f4,f0f0f0f0 -348e0f8,f0f0f0bf -348e0fc,bfbfaff0 -348e100,f0f0f0f0 -348e104,f0f0f0f0 +348e05c,f0f0f0f0 +348e060,f0f0f0f0 +348e064,f0f0f0cf +348e068,cff0f0f0 +348e06c,f0f0f0f0 +348e070,f0f0f0f0 +348e074,f0f0f0cf +348e078,cfcff0f0 +348e07c,f0f0f0f0 +348e080,f0f0f0f0 +348e084,f0f0bfcf +348e088,cfcff0f0 +348e08c,f0f0f0f0 +348e090,f0f0f0f0 +348e094,f0f0bfcf +348e098,cfcff0f0 +348e09c,f0f0f0f0 +348e0a0,f0f0f0f0 +348e0a4,f0bfcfbf +348e0a8,bfbfbff0 +348e0ac,f0f0f0f0 +348e0b0,f0f0f0f0 +348e0b4,f0bfbfbf +348e0b8,bfbfbff0 +348e0bc,f0f0f0f0 +348e0c0,f0f0f0f0 +348e0c4,bfbfbfbf +348e0c8,bfbfbfbf +348e0cc,f0f0f0f0 +348e0d0,f0f0f0f0 +348e0d4,bfbfbfbf +348e0d8,bfbfbfbf +348e0dc,f0f0f0f0 +348e0e0,f0f0f0f0 +348e0e4,bfbfbfbf +348e0e8,bfbfbfaf +348e0ec,f0f0f0f0 +348e0f0,f0f0f0af +348e0f4,bfbfbfbf +348e0f8,afafaff0 +348e0fc,f0f0f0f0 +348e100,f0f0f0bf +348e104,bfbfaff0 348e108,f0f0f0f0 348e10c,f0f0f0f0 348e110,f0f0f0f0 @@ -12692,53 +12692,53 @@ ef4f9c,0 348e150,f0f0f0f0 348e154,f0f0f0f0 348e158,f0f0f0f0 -348e15c,f0f0f0ef -348e160,eff0f0f0 -348e164,f0f0f0f0 -348e168,f0f0f0f0 -348e16c,f0f0f0ef -348e170,bfbff0f0 -348e174,f0f0f0f0 -348e178,f0f0f0f0 -348e17c,f0f0dfdf -348e180,bfbff0f0 -348e184,f0f0f0f0 -348e188,f0f0f0f0 -348e18c,f0f0dfbf -348e190,afaff0f0 -348e194,f0f0f0f0 -348e198,f0f0f0f0 -348e19c,f0dfdfaf -348e1a0,afafaff0 -348e1a4,f0f0f0f0 -348e1a8,f0f0f0f0 -348e1ac,f0dfafaf -348e1b0,afafaff0 -348e1b4,f0f0f0f0 -348e1b8,f0f0f0f0 -348e1bc,dfdfafaf -348e1c0,afafaff0 -348e1c4,f0f0f0f0 -348e1c8,f0f0f0f0 -348e1cc,dfdfafaf -348e1d0,afafaf9f -348e1d4,f0f0f0f0 -348e1d8,f0f0f0f0 -348e1dc,cfafafaf -348e1e0,afaf9f9f -348e1e4,f0f0f0f0 -348e1e8,f0f0f0cf -348e1ec,cfafafaf -348e1f0,9f9ff0f0 -348e1f4,f0f0f0f0 -348e1f8,f0f0f0cf -348e1fc,afafaf9f -348e200,f0f0f0f0 -348e204,f0f0f0f0 -348e208,f0f0f0cf -348e20c,aff0f0f0 -348e210,f0f0f0f0 -348e214,f0f0f0f0 +348e15c,f0f0f0f0 +348e160,f0f0f0f0 +348e164,f0f0f0ef +348e168,eff0f0f0 +348e16c,f0f0f0f0 +348e170,f0f0f0f0 +348e174,f0f0f0ef +348e178,bfbff0f0 +348e17c,f0f0f0f0 +348e180,f0f0f0f0 +348e184,f0f0dfdf +348e188,bfbff0f0 +348e18c,f0f0f0f0 +348e190,f0f0f0f0 +348e194,f0f0dfbf +348e198,afaff0f0 +348e19c,f0f0f0f0 +348e1a0,f0f0f0f0 +348e1a4,f0dfdfaf +348e1a8,afafaff0 +348e1ac,f0f0f0f0 +348e1b0,f0f0f0f0 +348e1b4,f0dfafaf +348e1b8,afafaff0 +348e1bc,f0f0f0f0 +348e1c0,f0f0f0f0 +348e1c4,dfdfafaf +348e1c8,afafaff0 +348e1cc,f0f0f0f0 +348e1d0,f0f0f0f0 +348e1d4,dfdfafaf +348e1d8,afafaf9f +348e1dc,f0f0f0f0 +348e1e0,f0f0f0f0 +348e1e4,cfafafaf +348e1e8,afaf9f9f +348e1ec,f0f0f0f0 +348e1f0,f0f0f0cf +348e1f4,cfafafaf +348e1f8,9f9ff0f0 +348e1fc,f0f0f0f0 +348e200,f0f0f0cf +348e204,afafaf9f +348e208,f0f0f0f0 +348e20c,f0f0f0f0 +348e210,f0f0f0cf +348e214,aff0f0f0 348e218,f0f0f0f0 348e21c,f0f0f0f0 348e220,f0f0f0f0 @@ -12756,57 +12756,57 @@ ef4f9c,0 348e250,f0f0f0f0 348e254,f0f0f0f0 348e258,f0f0f0f0 -348e25c,f0f0f0ff -348e260,ff9ff0f0 -348e264,f0f0f0f0 -348e268,f0f0f0f0 -348e26c,f0f0ffff -348e270,ff9ff0f0 -348e274,f0f0f0f0 -348e278,f0f0f0f0 -348e27c,f0f0ffff -348e280,9f9ff0f0 -348e284,f0f0f0f0 -348e288,f0f0f0f0 -348e28c,f0f0ffff -348e290,9f9ff0f0 -348e294,f0f0f0f0 -348e298,f0f0f0f0 -348e29c,f0efef9f -348e2a0,9f9f9ff0 -348e2a4,f0f0f0f0 -348e2a8,f0f0f0f0 -348e2ac,f0efef9f -348e2b0,9f9f8ff0 -348e2b4,f0f0f0f0 -348e2b8,f0f0f0f0 -348e2bc,f0efef9f -348e2c0,9f8f8ff0 -348e2c4,f0f0f0f0 -348e2c8,f0f0f0f0 -348e2cc,efef9f9f -348e2d0,8f8f8ff0 -348e2d4,f0f0f0f0 -348e2d8,f0f0f0f0 -348e2dc,efef9f8f -348e2e0,8f8f8ff0 -348e2e4,f0f0f0f0 -348e2e8,f0f0f0ef -348e2ec,efef8f8f -348e2f0,8f8ff0f0 -348e2f4,f0f0f0f0 -348e2f8,f0f0f0ef -348e2fc,ef8f8f8f -348e300,f0f0f0f0 -348e304,f0f0f0f0 -348e308,f0f0f0ef -348e30c,ef8f8ff0 -348e310,f0f0f0f0 -348e314,f0f0f0f0 +348e25c,f0f0f0f0 +348e260,f0f0f0f0 +348e264,f0f0f0ff +348e268,ff9ff0f0 +348e26c,f0f0f0f0 +348e270,f0f0f0f0 +348e274,f0f0ffff +348e278,ff9ff0f0 +348e27c,f0f0f0f0 +348e280,f0f0f0f0 +348e284,f0f0ffff +348e288,9f9ff0f0 +348e28c,f0f0f0f0 +348e290,f0f0f0f0 +348e294,f0f0ffff +348e298,9f9ff0f0 +348e29c,f0f0f0f0 +348e2a0,f0f0f0f0 +348e2a4,f0efef9f +348e2a8,9f9f9ff0 +348e2ac,f0f0f0f0 +348e2b0,f0f0f0f0 +348e2b4,f0efef9f +348e2b8,9f9f8ff0 +348e2bc,f0f0f0f0 +348e2c0,f0f0f0f0 +348e2c4,f0efef9f +348e2c8,9f8f8ff0 +348e2cc,f0f0f0f0 +348e2d0,f0f0f0f0 +348e2d4,efef9f9f +348e2d8,8f8f8ff0 +348e2dc,f0f0f0f0 +348e2e0,f0f0f0f0 +348e2e4,efef9f8f +348e2e8,8f8f8ff0 +348e2ec,f0f0f0f0 +348e2f0,f0f0f0ef +348e2f4,efef8f8f +348e2f8,8f8ff0f0 +348e2fc,f0f0f0f0 +348e300,f0f0f0ef +348e304,ef8f8f8f +348e308,f0f0f0f0 +348e30c,f0f0f0f0 +348e310,f0f0f0ef +348e314,ef8f8ff0 348e318,f0f0f0f0 -348e31c,8ff0f0f0 +348e31c,f0f0f0f0 348e320,f0f0f0f0 -348e324,f0f0f0f0 +348e324,8ff0f0f0 348e328,f0f0f0f0 348e32c,f0f0f0f0 348e330,f0f0f0f0 @@ -12820,57 +12820,57 @@ ef4f9c,0 348e350,f0f0f0f0 348e354,f0f0f0f0 348e358,f0f0f0f0 -348e35c,f0f0f0ff -348e360,ff7ff0f0 -348e364,f0f0f0f0 -348e368,f0f0f0f0 -348e36c,f0f0ffff -348e370,ff7ff0f0 -348e374,f0f0f0f0 -348e378,f0f0f0f0 -348e37c,f0f0ffff -348e380,ff7ff0f0 -348e384,f0f0f0f0 -348e388,f0f0f0f0 -348e38c,f0f0ffff -348e390,7f7ff0f0 -348e394,f0f0f0f0 -348e398,f0f0f0f0 -348e39c,f0ffffff -348e3a0,7f7ff0f0 -348e3a4,f0f0f0f0 -348e3a8,f0f0f0f0 -348e3ac,f0ffffff -348e3b0,7f7ff0f0 -348e3b4,f0f0f0f0 -348e3b8,f0f0f0f0 -348e3bc,f0ffff7f -348e3c0,7f7f7ff0 -348e3c4,f0f0f0f0 -348e3c8,f0f0f0f0 -348e3cc,ffffff7f -348e3d0,7f7f6ff0 -348e3d4,f0f0f0f0 -348e3d8,f0f0f0f0 -348e3dc,ffffff7f -348e3e0,7f6f6ff0 -348e3e4,f0f0f0f0 -348e3e8,f0f0f0f0 -348e3ec,ffffff7f -348e3f0,7f6ff0f0 -348e3f4,f0f0f0f0 -348e3f8,f0f0f0f0 -348e3fc,ffff7f7f +348e35c,f0f0f0f0 +348e360,f0f0f0f0 +348e364,f0f0f0ff +348e368,ff7ff0f0 +348e36c,f0f0f0f0 +348e370,f0f0f0f0 +348e374,f0f0ffff +348e378,ff7ff0f0 +348e37c,f0f0f0f0 +348e380,f0f0f0f0 +348e384,f0f0ffff +348e388,ff7ff0f0 +348e38c,f0f0f0f0 +348e390,f0f0f0f0 +348e394,f0f0ffff +348e398,7f7ff0f0 +348e39c,f0f0f0f0 +348e3a0,f0f0f0f0 +348e3a4,f0ffffff +348e3a8,7f7ff0f0 +348e3ac,f0f0f0f0 +348e3b0,f0f0f0f0 +348e3b4,f0ffffff +348e3b8,7f7ff0f0 +348e3bc,f0f0f0f0 +348e3c0,f0f0f0f0 +348e3c4,f0ffff7f +348e3c8,7f7f7ff0 +348e3cc,f0f0f0f0 +348e3d0,f0f0f0f0 +348e3d4,ffffff7f +348e3d8,7f7f6ff0 +348e3dc,f0f0f0f0 +348e3e0,f0f0f0f0 +348e3e4,ffffff7f +348e3e8,7f6f6ff0 +348e3ec,f0f0f0f0 +348e3f0,f0f0f0f0 +348e3f4,ffffff7f +348e3f8,7f6ff0f0 +348e3fc,f0f0f0f0 348e400,f0f0f0f0 -348e404,f0f0f0f0 -348e408,f0f0f0ff -348e40c,ffff7ff0 -348e410,f0f0f0f0 -348e414,f0f0f0f0 +348e404,ffff7f7f +348e408,f0f0f0f0 +348e40c,f0f0f0f0 +348e410,f0f0f0ff +348e414,ffff7ff0 348e418,f0f0f0f0 -348e41c,fffff0f0 +348e41c,f0f0f0f0 348e420,f0f0f0f0 -348e424,f0f0f0f0 +348e424,fffff0f0 348e428,f0f0f0f0 348e42c,f0f0f0f0 348e430,f0f0f0f0 @@ -12884,61 +12884,61 @@ ef4f9c,0 348e450,f0f0f0f0 348e454,f0f0f0f0 348e458,f0f0f0f0 -348e45c,f0f0ffff -348e460,ff5ff0f0 -348e464,f0f0f0f0 -348e468,f0f0f0f0 -348e46c,f0f0ffff -348e470,ff5ff0f0 -348e474,f0f0f0f0 -348e478,f0f0f0f0 -348e47c,f0f0ffff -348e480,ff5ff0f0 -348e484,f0f0f0f0 -348e488,f0f0f0f0 -348e48c,f0f0ffff -348e490,ff5ff0f0 -348e494,f0f0f0f0 -348e498,f0f0f0f0 -348e49c,f0f0ffff -348e4a0,ff5ff0f0 -348e4a4,f0f0f0f0 -348e4a8,f0f0f0f0 -348e4ac,f0ffffff -348e4b0,5f5ff0f0 -348e4b4,f0f0f0f0 -348e4b8,f0f0f0f0 -348e4bc,f0ffffff -348e4c0,5f5ff0f0 -348e4c4,f0f0f0f0 -348e4c8,f0f0f0f0 -348e4cc,f0ffffff -348e4d0,5f5ff0f0 -348e4d4,f0f0f0f0 -348e4d8,f0f0f0f0 -348e4dc,f0ffffff -348e4e0,5f5ff0f0 -348e4e4,f0f0f0f0 -348e4e8,f0f0f0f0 -348e4ec,ffffffff -348e4f0,5ff0f0f0 -348e4f4,f0f0f0f0 -348e4f8,f0f0f0f0 -348e4fc,ffffff5f -348e500,5ff0f0f0 -348e504,f0f0f0f0 -348e508,f0f0f0f0 -348e50c,ffffff5f +348e45c,f0f0f0f0 +348e460,f0f0f0f0 +348e464,f0f0ffff +348e468,ff5ff0f0 +348e46c,f0f0f0f0 +348e470,f0f0f0f0 +348e474,f0f0ffff +348e478,ff5ff0f0 +348e47c,f0f0f0f0 +348e480,f0f0f0f0 +348e484,f0f0ffff +348e488,ff5ff0f0 +348e48c,f0f0f0f0 +348e490,f0f0f0f0 +348e494,f0f0ffff +348e498,ff5ff0f0 +348e49c,f0f0f0f0 +348e4a0,f0f0f0f0 +348e4a4,f0f0ffff +348e4a8,ff5ff0f0 +348e4ac,f0f0f0f0 +348e4b0,f0f0f0f0 +348e4b4,f0ffffff +348e4b8,5f5ff0f0 +348e4bc,f0f0f0f0 +348e4c0,f0f0f0f0 +348e4c4,f0ffffff +348e4c8,5f5ff0f0 +348e4cc,f0f0f0f0 +348e4d0,f0f0f0f0 +348e4d4,f0ffffff +348e4d8,5f5ff0f0 +348e4dc,f0f0f0f0 +348e4e0,f0f0f0f0 +348e4e4,f0ffffff +348e4e8,5f5ff0f0 +348e4ec,f0f0f0f0 +348e4f0,f0f0f0f0 +348e4f4,ffffffff +348e4f8,5ff0f0f0 +348e4fc,f0f0f0f0 +348e500,f0f0f0f0 +348e504,ffffff5f +348e508,5ff0f0f0 +348e50c,f0f0f0f0 348e510,f0f0f0f0 -348e514,f0f0f0f0 +348e514,ffffff5f 348e518,f0f0f0f0 -348e51c,ffffff5f +348e51c,f0f0f0f0 348e520,f0f0f0f0 -348e524,f0f0f0f0 +348e524,ffffff5f 348e528,f0f0f0f0 -348e52c,f0f0fff0 +348e52c,f0f0f0f0 348e530,f0f0f0f0 -348e534,f0f0f0f0 +348e534,f0f0fff0 348e538,f0f0f0f0 348e53c,f0f0f0f0 348e540,f0f0f0f0 @@ -12948,61 +12948,61 @@ ef4f9c,0 348e550,f0f0f0f0 348e554,f0f0f0f0 348e558,f0f0f0f0 -348e55c,f0f0ffff -348e560,fffff0f0 -348e564,f0f0f0f0 -348e568,f0f0f0f0 -348e56c,f0f0ffff -348e570,fffff0f0 -348e574,f0f0f0f0 -348e578,f0f0f0f0 -348e57c,f0f0ffff -348e580,ff3ff0f0 -348e584,f0f0f0f0 -348e588,f0f0f0f0 -348e58c,f0f0ffff -348e590,ff3ff0f0 -348e594,f0f0f0f0 -348e598,f0f0f0f0 -348e59c,f0f0ffff -348e5a0,ff3ff0f0 -348e5a4,f0f0f0f0 -348e5a8,f0f0f0f0 -348e5ac,f0f0ffff -348e5b0,ff3ff0f0 -348e5b4,f0f0f0f0 -348e5b8,f0f0f0f0 -348e5bc,f0f0ffff -348e5c0,ff3ff0f0 -348e5c4,f0f0f0f0 -348e5c8,f0f0f0f0 -348e5cc,f0ffffff -348e5d0,ff3ff0f0 -348e5d4,f0f0f0f0 -348e5d8,f0f0f0f0 -348e5dc,f0ffffff -348e5e0,fff0f0f0 -348e5e4,f0f0f0f0 -348e5e8,f0f0f0f0 -348e5ec,f0ffffff -348e5f0,fff0f0f0 -348e5f4,f0f0f0f0 -348e5f8,f0f0f0f0 -348e5fc,f0ffffff -348e600,fff0f0f0 -348e604,f0f0f0f0 -348e608,f0f0f0f0 -348e60c,f0ffffff -348e610,fff0f0f0 -348e614,f0f0f0f0 -348e618,f0f0f0f0 -348e61c,f0ffffff -348e620,fff0f0f0 -348e624,f0f0f0f0 -348e628,f0f0f0f0 -348e62c,f0f0f0ff +348e55c,f0f0f0f0 +348e560,f0f0f0f0 +348e564,f0f0ffff +348e568,fffff0f0 +348e56c,f0f0f0f0 +348e570,f0f0f0f0 +348e574,f0f0ffff +348e578,fffff0f0 +348e57c,f0f0f0f0 +348e580,f0f0f0f0 +348e584,f0f0ffff +348e588,ff3ff0f0 +348e58c,f0f0f0f0 +348e590,f0f0f0f0 +348e594,f0f0ffff +348e598,ff3ff0f0 +348e59c,f0f0f0f0 +348e5a0,f0f0f0f0 +348e5a4,f0f0ffff +348e5a8,ff3ff0f0 +348e5ac,f0f0f0f0 +348e5b0,f0f0f0f0 +348e5b4,f0f0ffff +348e5b8,ff3ff0f0 +348e5bc,f0f0f0f0 +348e5c0,f0f0f0f0 +348e5c4,f0f0ffff +348e5c8,ff3ff0f0 +348e5cc,f0f0f0f0 +348e5d0,f0f0f0f0 +348e5d4,f0ffffff +348e5d8,ff3ff0f0 +348e5dc,f0f0f0f0 +348e5e0,f0f0f0f0 +348e5e4,f0ffffff +348e5e8,fff0f0f0 +348e5ec,f0f0f0f0 +348e5f0,f0f0f0f0 +348e5f4,f0ffffff +348e5f8,fff0f0f0 +348e5fc,f0f0f0f0 +348e600,f0f0f0f0 +348e604,f0ffffff +348e608,fff0f0f0 +348e60c,f0f0f0f0 +348e610,f0f0f0f0 +348e614,f0ffffff +348e618,fff0f0f0 +348e61c,f0f0f0f0 +348e620,f0f0f0f0 +348e624,f0ffffff +348e628,fff0f0f0 +348e62c,f0f0f0f0 348e630,f0f0f0f0 -348e634,f0f0f0f0 +348e634,f0f0f0ff 348e638,f0f0f0f0 348e63c,f0f0f0f0 348e640,f0f0f0f0 @@ -13012,62 +13012,62 @@ ef4f9c,0 348e650,f0f0f0f0 348e654,f0f0f0f0 348e658,f0f0f0f0 -348e65c,f0f0ffff -348e660,fffff0f0 -348e664,f0f0f0f0 -348e668,f0f0f0f0 -348e66c,f0f0ffff -348e670,fffff0f0 -348e674,f0f0f0f0 -348e678,f0f0f0f0 -348e67c,f0f0ffff -348e680,fffff0f0 -348e684,f0f0f0f0 -348e688,f0f0f0f0 -348e68c,f0f0ffff -348e690,fffff0f0 -348e694,f0f0f0f0 -348e698,f0f0f0f0 -348e69c,f0f0ffff -348e6a0,fffff0f0 -348e6a4,f0f0f0f0 -348e6a8,f0f0f0f0 -348e6ac,f0f0ffff -348e6b0,fffff0f0 -348e6b4,f0f0f0f0 -348e6b8,f0f0f0f0 -348e6bc,f0f0ffff -348e6c0,fffff0f0 -348e6c4,f0f0f0f0 -348e6c8,f0f0f0f0 -348e6cc,f0f0ffff -348e6d0,fffff0f0 -348e6d4,f0f0f0f0 -348e6d8,f0f0f0f0 -348e6dc,f0f0ffff -348e6e0,fffff0f0 -348e6e4,f0f0f0f0 -348e6e8,f0f0f0f0 -348e6ec,f0f0ffff -348e6f0,fffff0f0 -348e6f4,f0f0f0f0 -348e6f8,f0f0f0f0 -348e6fc,f0f0ffff -348e700,fffff0f0 -348e704,f0f0f0f0 -348e708,f0f0f0f0 -348e70c,f0f0ffff -348e710,fffff0f0 -348e714,f0f0f0f0 -348e718,f0f0f0f0 -348e71c,f0f0ffff -348e720,fffff0f0 -348e724,f0f0f0f0 -348e728,f0f0f0f0 -348e72c,f0f0ffff -348e730,fffff0f0 -348e734,f0f0f0f0 -348e738,f0f0f0f0 +348e65c,f0f0f0f0 +348e660,f0f0f0f0 +348e664,f0f0ffff +348e668,fffff0f0 +348e66c,f0f0f0f0 +348e670,f0f0f0f0 +348e674,f0f0ffff +348e678,fffff0f0 +348e67c,f0f0f0f0 +348e680,f0f0f0f0 +348e684,f0f0ffff +348e688,fffff0f0 +348e68c,f0f0f0f0 +348e690,f0f0f0f0 +348e694,f0f0ffff +348e698,fffff0f0 +348e69c,f0f0f0f0 +348e6a0,f0f0f0f0 +348e6a4,f0f0ffff +348e6a8,fffff0f0 +348e6ac,f0f0f0f0 +348e6b0,f0f0f0f0 +348e6b4,f0f0ffff +348e6b8,fffff0f0 +348e6bc,f0f0f0f0 +348e6c0,f0f0f0f0 +348e6c4,f0f0ffff +348e6c8,fffff0f0 +348e6cc,f0f0f0f0 +348e6d0,f0f0f0f0 +348e6d4,f0f0ffff +348e6d8,fffff0f0 +348e6dc,f0f0f0f0 +348e6e0,f0f0f0f0 +348e6e4,f0f0ffff +348e6e8,fffff0f0 +348e6ec,f0f0f0f0 +348e6f0,f0f0f0f0 +348e6f4,f0f0ffff +348e6f8,fffff0f0 +348e6fc,f0f0f0f0 +348e700,f0f0f0f0 +348e704,f0f0ffff +348e708,fffff0f0 +348e70c,f0f0f0f0 +348e710,f0f0f0f0 +348e714,f0f0ffff +348e718,fffff0f0 +348e71c,f0f0f0f0 +348e720,f0f0f0f0 +348e724,f0f0ffff +348e728,fffff0f0 +348e72c,f0f0f0f0 +348e730,f0f0f0f0 +348e734,f0f0ffff +348e738,fffff0f0 348e73c,f0f0f0f0 348e740,f0f0f0f0 348e744,f0f0f0f0 @@ -13076,62 +13076,62 @@ ef4f9c,0 348e750,f0f0f0f0 348e754,f0f0f0f0 348e758,f0f0f0f0 -348e75c,f0f0ffff -348e760,fffff0f0 -348e764,f0f0f0f0 -348e768,f0f0f0f0 -348e76c,f0f0ffff -348e770,fffff0f0 -348e774,f0f0f0f0 -348e778,f0f0f0f0 -348e77c,f0f03fff -348e780,fffff0f0 -348e784,f0f0f0f0 -348e788,f0f0f0f0 -348e78c,f0f03fff -348e790,fffff0f0 -348e794,f0f0f0f0 -348e798,f0f0f0f0 -348e79c,f0f03fff -348e7a0,fffff0f0 -348e7a4,f0f0f0f0 -348e7a8,f0f0f0f0 -348e7ac,f0f03fff -348e7b0,fffff0f0 -348e7b4,f0f0f0f0 -348e7b8,f0f0f0f0 -348e7bc,f0f03fff -348e7c0,fffff0f0 -348e7c4,f0f0f0f0 -348e7c8,f0f0f0f0 -348e7cc,f0f03fff -348e7d0,fffffff0 -348e7d4,f0f0f0f0 -348e7d8,f0f0f0f0 -348e7dc,f0f0f0ff -348e7e0,fffffff0 -348e7e4,f0f0f0f0 -348e7e8,f0f0f0f0 -348e7ec,f0f0f0ff -348e7f0,fffffff0 -348e7f4,f0f0f0f0 -348e7f8,f0f0f0f0 -348e7fc,f0f0f0ff -348e800,fffffff0 -348e804,f0f0f0f0 -348e808,f0f0f0f0 -348e80c,f0f0f0ff -348e810,fffffff0 -348e814,f0f0f0f0 -348e818,f0f0f0f0 -348e81c,f0f0f0ff -348e820,fffffff0 -348e824,f0f0f0f0 -348e828,f0f0f0f0 +348e75c,f0f0f0f0 +348e760,f0f0f0f0 +348e764,f0f0ffff +348e768,fffff0f0 +348e76c,f0f0f0f0 +348e770,f0f0f0f0 +348e774,f0f0ffff +348e778,fffff0f0 +348e77c,f0f0f0f0 +348e780,f0f0f0f0 +348e784,f0f03fff +348e788,fffff0f0 +348e78c,f0f0f0f0 +348e790,f0f0f0f0 +348e794,f0f03fff +348e798,fffff0f0 +348e79c,f0f0f0f0 +348e7a0,f0f0f0f0 +348e7a4,f0f03fff +348e7a8,fffff0f0 +348e7ac,f0f0f0f0 +348e7b0,f0f0f0f0 +348e7b4,f0f03fff +348e7b8,fffff0f0 +348e7bc,f0f0f0f0 +348e7c0,f0f0f0f0 +348e7c4,f0f03fff +348e7c8,fffff0f0 +348e7cc,f0f0f0f0 +348e7d0,f0f0f0f0 +348e7d4,f0f03fff +348e7d8,fffffff0 +348e7dc,f0f0f0f0 +348e7e0,f0f0f0f0 +348e7e4,f0f0f0ff +348e7e8,fffffff0 +348e7ec,f0f0f0f0 +348e7f0,f0f0f0f0 +348e7f4,f0f0f0ff +348e7f8,fffffff0 +348e7fc,f0f0f0f0 +348e800,f0f0f0f0 +348e804,f0f0f0ff +348e808,fffffff0 +348e80c,f0f0f0f0 +348e810,f0f0f0f0 +348e814,f0f0f0ff +348e818,fffffff0 +348e81c,f0f0f0f0 +348e820,f0f0f0f0 +348e824,f0f0f0ff +348e828,fffffff0 348e82c,f0f0f0f0 -348e830,fff0f0f0 +348e830,f0f0f0f0 348e834,f0f0f0f0 -348e838,f0f0f0f0 +348e838,fff0f0f0 348e83c,f0f0f0f0 348e840,f0f0f0f0 348e844,f0f0f0f0 @@ -13140,62 +13140,62 @@ ef4f9c,0 348e850,f0f0f0f0 348e854,f0f0f0f0 348e858,f0f0f0f0 -348e85c,f0f05fff -348e860,fffff0f0 -348e864,f0f0f0f0 -348e868,f0f0f0f0 -348e86c,f0f05fff -348e870,fffff0f0 -348e874,f0f0f0f0 -348e878,f0f0f0f0 -348e87c,f0f05fff -348e880,fffff0f0 -348e884,f0f0f0f0 -348e888,f0f0f0f0 -348e88c,f0f05fff -348e890,fffff0f0 -348e894,f0f0f0f0 -348e898,f0f0f0f0 -348e89c,f0f05fff -348e8a0,fffff0f0 -348e8a4,f0f0f0f0 -348e8a8,f0f0f0f0 -348e8ac,f0f05f5f -348e8b0,fffffff0 -348e8b4,f0f0f0f0 -348e8b8,f0f0f0f0 -348e8bc,f0f05f5f -348e8c0,fffffff0 -348e8c4,f0f0f0f0 -348e8c8,f0f0f0f0 -348e8cc,f0f05f5f -348e8d0,fffffff0 -348e8d4,f0f0f0f0 -348e8d8,f0f0f0f0 -348e8dc,f0f05f5f -348e8e0,fffffff0 -348e8e4,f0f0f0f0 -348e8e8,f0f0f0f0 -348e8ec,f0f0f05f -348e8f0,ffffffff -348e8f4,f0f0f0f0 -348e8f8,f0f0f0f0 -348e8fc,f0f0f05f -348e900,5fffffff -348e904,f0f0f0f0 -348e908,f0f0f0f0 +348e85c,f0f0f0f0 +348e860,f0f0f0f0 +348e864,f0f05fff +348e868,fffff0f0 +348e86c,f0f0f0f0 +348e870,f0f0f0f0 +348e874,f0f05fff +348e878,fffff0f0 +348e87c,f0f0f0f0 +348e880,f0f0f0f0 +348e884,f0f05fff +348e888,fffff0f0 +348e88c,f0f0f0f0 +348e890,f0f0f0f0 +348e894,f0f05fff +348e898,fffff0f0 +348e89c,f0f0f0f0 +348e8a0,f0f0f0f0 +348e8a4,f0f05fff +348e8a8,fffff0f0 +348e8ac,f0f0f0f0 +348e8b0,f0f0f0f0 +348e8b4,f0f05f5f +348e8b8,fffffff0 +348e8bc,f0f0f0f0 +348e8c0,f0f0f0f0 +348e8c4,f0f05f5f +348e8c8,fffffff0 +348e8cc,f0f0f0f0 +348e8d0,f0f0f0f0 +348e8d4,f0f05f5f +348e8d8,fffffff0 +348e8dc,f0f0f0f0 +348e8e0,f0f0f0f0 +348e8e4,f0f05f5f +348e8e8,fffffff0 +348e8ec,f0f0f0f0 +348e8f0,f0f0f0f0 +348e8f4,f0f0f05f +348e8f8,ffffffff +348e8fc,f0f0f0f0 +348e900,f0f0f0f0 +348e904,f0f0f05f +348e908,5fffffff 348e90c,f0f0f0f0 -348e910,5fffffff +348e910,f0f0f0f0 348e914,f0f0f0f0 -348e918,f0f0f0f0 +348e918,5fffffff 348e91c,f0f0f0f0 -348e920,5fffffff +348e920,f0f0f0f0 348e924,f0f0f0f0 -348e928,f0f0f0f0 +348e928,5fffffff 348e92c,f0f0f0f0 -348e930,f0fff0f0 +348e930,f0f0f0f0 348e934,f0f0f0f0 -348e938,f0f0f0f0 +348e938,f0fff0f0 348e93c,f0f0f0f0 348e940,f0f0f0f0 348e944,f0f0f0f0 @@ -13204,58 +13204,58 @@ ef4f9c,0 348e950,f0f0f0f0 348e954,f0f0f0f0 348e958,f0f0f0f0 -348e95c,f0f07fff -348e960,fff0f0f0 -348e964,f0f0f0f0 -348e968,f0f0f0f0 -348e96c,f0f07fff -348e970,fffff0f0 -348e974,f0f0f0f0 -348e978,f0f0f0f0 -348e97c,f0f07fff -348e980,fffff0f0 -348e984,f0f0f0f0 -348e988,f0f0f0f0 -348e98c,f0f07f7f -348e990,fffff0f0 -348e994,f0f0f0f0 -348e998,f0f0f0f0 -348e99c,f0f07f7f -348e9a0,fffffff0 -348e9a4,f0f0f0f0 -348e9a8,f0f0f0f0 -348e9ac,f0f07f7f -348e9b0,fffffff0 -348e9b4,f0f0f0f0 -348e9b8,f0f0f0f0 -348e9bc,f07f7f7f -348e9c0,7ffffff0 -348e9c4,f0f0f0f0 -348e9c8,f0f0f0f0 -348e9cc,f06f7f7f -348e9d0,7fffffff -348e9d4,f0f0f0f0 -348e9d8,f0f0f0f0 -348e9dc,f06f6f7f -348e9e0,7fffffff -348e9e4,f0f0f0f0 -348e9e8,f0f0f0f0 -348e9ec,f0f06f7f -348e9f0,7fffffff -348e9f4,f0f0f0f0 -348e9f8,f0f0f0f0 +348e95c,f0f0f0f0 +348e960,f0f0f0f0 +348e964,f0f07fff +348e968,fff0f0f0 +348e96c,f0f0f0f0 +348e970,f0f0f0f0 +348e974,f0f07fff +348e978,fffff0f0 +348e97c,f0f0f0f0 +348e980,f0f0f0f0 +348e984,f0f07fff +348e988,fffff0f0 +348e98c,f0f0f0f0 +348e990,f0f0f0f0 +348e994,f0f07f7f +348e998,fffff0f0 +348e99c,f0f0f0f0 +348e9a0,f0f0f0f0 +348e9a4,f0f07f7f +348e9a8,fffffff0 +348e9ac,f0f0f0f0 +348e9b0,f0f0f0f0 +348e9b4,f0f07f7f +348e9b8,fffffff0 +348e9bc,f0f0f0f0 +348e9c0,f0f0f0f0 +348e9c4,f07f7f7f +348e9c8,7ffffff0 +348e9cc,f0f0f0f0 +348e9d0,f0f0f0f0 +348e9d4,f06f7f7f +348e9d8,7fffffff +348e9dc,f0f0f0f0 +348e9e0,f0f0f0f0 +348e9e4,f06f6f7f +348e9e8,7fffffff +348e9ec,f0f0f0f0 +348e9f0,f0f0f0f0 +348e9f4,f0f06f7f +348e9f8,7fffffff 348e9fc,f0f0f0f0 -348ea00,7f7fffff +348ea00,f0f0f0f0 348ea04,f0f0f0f0 -348ea08,f0f0f0f0 +348ea08,7f7fffff 348ea0c,f0f0f0f0 -348ea10,f07fffff -348ea14,fff0f0f0 -348ea18,f0f0f0f0 -348ea1c,f0f0f0f0 -348ea20,f0f0ffff +348ea10,f0f0f0f0 +348ea14,f0f0f0f0 +348ea18,f07fffff +348ea1c,fff0f0f0 +348ea20,f0f0f0f0 348ea24,f0f0f0f0 -348ea28,f0f0f0f0 +348ea28,f0f0ffff 348ea2c,f0f0f0f0 348ea30,f0f0f0f0 348ea34,f0f0f0f0 @@ -13268,58 +13268,58 @@ ef4f9c,0 348ea50,f0f0f0f0 348ea54,f0f0f0f0 348ea58,f0f0f0f0 -348ea5c,f0f09fff -348ea60,fff0f0f0 -348ea64,f0f0f0f0 -348ea68,f0f0f0f0 -348ea6c,f0f09fff -348ea70,fffff0f0 -348ea74,f0f0f0f0 -348ea78,f0f0f0f0 -348ea7c,f0f09f9f -348ea80,fffff0f0 -348ea84,f0f0f0f0 -348ea88,f0f0f0f0 -348ea8c,f0f09f9f -348ea90,fffff0f0 -348ea94,f0f0f0f0 -348ea98,f0f0f0f0 -348ea9c,f09f9f9f -348eaa0,9fffeff0 -348eaa4,f0f0f0f0 -348eaa8,f0f0f0f0 -348eaac,f08f9f9f -348eab0,9fefeff0 -348eab4,f0f0f0f0 -348eab8,f0f0f0f0 -348eabc,f08f8f9f -348eac0,9fefeff0 -348eac4,f0f0f0f0 -348eac8,f0f0f0f0 -348eacc,f08f8f8f -348ead0,9f9fefef -348ead4,f0f0f0f0 -348ead8,f0f0f0f0 -348eadc,f08f8f8f -348eae0,8f9fefef -348eae4,f0f0f0f0 -348eae8,f0f0f0f0 -348eaec,f0f08f8f -348eaf0,8f8fefef -348eaf4,eff0f0f0 -348eaf8,f0f0f0f0 -348eafc,f0f0f0f0 -348eb00,8f8f8fef -348eb04,eff0f0f0 -348eb08,f0f0f0f0 -348eb0c,f0f0f0f0 -348eb10,f08f8fef -348eb14,eff0f0f0 -348eb18,f0f0f0f0 -348eb1c,f0f0f0f0 -348eb20,f0f0f08f +348ea5c,f0f0f0f0 +348ea60,f0f0f0f0 +348ea64,f0f09fff +348ea68,fff0f0f0 +348ea6c,f0f0f0f0 +348ea70,f0f0f0f0 +348ea74,f0f09fff +348ea78,fffff0f0 +348ea7c,f0f0f0f0 +348ea80,f0f0f0f0 +348ea84,f0f09f9f +348ea88,fffff0f0 +348ea8c,f0f0f0f0 +348ea90,f0f0f0f0 +348ea94,f0f09f9f +348ea98,fffff0f0 +348ea9c,f0f0f0f0 +348eaa0,f0f0f0f0 +348eaa4,f09f9f9f +348eaa8,9fffeff0 +348eaac,f0f0f0f0 +348eab0,f0f0f0f0 +348eab4,f08f9f9f +348eab8,9fefeff0 +348eabc,f0f0f0f0 +348eac0,f0f0f0f0 +348eac4,f08f8f9f +348eac8,9fefeff0 +348eacc,f0f0f0f0 +348ead0,f0f0f0f0 +348ead4,f08f8f8f +348ead8,9f9fefef +348eadc,f0f0f0f0 +348eae0,f0f0f0f0 +348eae4,f08f8f8f +348eae8,8f9fefef +348eaec,f0f0f0f0 +348eaf0,f0f0f0f0 +348eaf4,f0f08f8f +348eaf8,8f8fefef +348eafc,eff0f0f0 +348eb00,f0f0f0f0 +348eb04,f0f0f0f0 +348eb08,8f8f8fef +348eb0c,eff0f0f0 +348eb10,f0f0f0f0 +348eb14,f0f0f0f0 +348eb18,f08f8fef +348eb1c,eff0f0f0 +348eb20,f0f0f0f0 348eb24,f0f0f0f0 -348eb28,f0f0f0f0 +348eb28,f0f0f08f 348eb2c,f0f0f0f0 348eb30,f0f0f0f0 348eb34,f0f0f0f0 @@ -13332,55 +13332,55 @@ ef4f9c,0 348eb50,f0f0f0f0 348eb54,f0f0f0f0 348eb58,f0f0f0f0 -348eb5c,f0f0f0ef -348eb60,eff0f0f0 -348eb64,f0f0f0f0 -348eb68,f0f0f0f0 -348eb6c,f0f0bfbf -348eb70,eff0f0f0 -348eb74,f0f0f0f0 -348eb78,f0f0f0f0 -348eb7c,f0f0bfbf -348eb80,dfdff0f0 -348eb84,f0f0f0f0 -348eb88,f0f0f0f0 -348eb8c,f0f0afbf -348eb90,bfdff0f0 -348eb94,f0f0f0f0 -348eb98,f0f0f0f0 -348eb9c,f0afafaf -348eba0,afdfdff0 -348eba4,f0f0f0f0 -348eba8,f0f0f0f0 -348ebac,f0afafaf -348ebb0,afafdff0 -348ebb4,f0f0f0f0 -348ebb8,f0f0f0f0 -348ebbc,f0afafaf -348ebc0,afafdfdf -348ebc4,f0f0f0f0 -348ebc8,f0f0f0f0 -348ebcc,9fafafaf -348ebd0,afafdfdf -348ebd4,f0f0f0f0 -348ebd8,f0f0f0f0 -348ebdc,9f9fafaf -348ebe0,afafafcf -348ebe4,f0f0f0f0 -348ebe8,f0f0f0f0 -348ebec,f0f09f9f -348ebf0,afafafcf -348ebf4,cff0f0f0 -348ebf8,f0f0f0f0 -348ebfc,f0f0f0f0 -348ec00,9fafafaf -348ec04,cff0f0f0 -348ec08,f0f0f0f0 -348ec0c,f0f0f0f0 -348ec10,f0f0f0af -348ec14,cff0f0f0 -348ec18,f0f0f0f0 -348ec1c,f0f0f0f0 +348eb5c,f0f0f0f0 +348eb60,f0f0f0f0 +348eb64,f0f0f0ef +348eb68,eff0f0f0 +348eb6c,f0f0f0f0 +348eb70,f0f0f0f0 +348eb74,f0f0bfbf +348eb78,eff0f0f0 +348eb7c,f0f0f0f0 +348eb80,f0f0f0f0 +348eb84,f0f0bfbf +348eb88,dfdff0f0 +348eb8c,f0f0f0f0 +348eb90,f0f0f0f0 +348eb94,f0f0afbf +348eb98,bfdff0f0 +348eb9c,f0f0f0f0 +348eba0,f0f0f0f0 +348eba4,f0afafaf +348eba8,afdfdff0 +348ebac,f0f0f0f0 +348ebb0,f0f0f0f0 +348ebb4,f0afafaf +348ebb8,afafdff0 +348ebbc,f0f0f0f0 +348ebc0,f0f0f0f0 +348ebc4,f0afafaf +348ebc8,afafdfdf +348ebcc,f0f0f0f0 +348ebd0,f0f0f0f0 +348ebd4,9fafafaf +348ebd8,afafdfdf +348ebdc,f0f0f0f0 +348ebe0,f0f0f0f0 +348ebe4,9f9fafaf +348ebe8,afafafcf +348ebec,f0f0f0f0 +348ebf0,f0f0f0f0 +348ebf4,f0f09f9f +348ebf8,afafafcf +348ebfc,cff0f0f0 +348ec00,f0f0f0f0 +348ec04,f0f0f0f0 +348ec08,9fafafaf +348ec0c,cff0f0f0 +348ec10,f0f0f0f0 +348ec14,f0f0f0f0 +348ec18,f0f0f0af +348ec1c,cff0f0f0 348ec20,f0f0f0f0 348ec24,f0f0f0f0 348ec28,f0f0f0f0 @@ -13396,51 +13396,51 @@ ef4f9c,0 348ec50,f0f0f0f0 348ec54,f0f0f0f0 348ec58,f0f0f0f0 -348ec5c,f0f0f0cf -348ec60,cff0f0f0 -348ec64,f0f0f0f0 -348ec68,f0f0f0f0 -348ec6c,f0f0cfcf -348ec70,cff0f0f0 -348ec74,f0f0f0f0 -348ec78,f0f0f0f0 -348ec7c,f0f0cfcf -348ec80,cfbff0f0 -348ec84,f0f0f0f0 -348ec88,f0f0f0f0 -348ec8c,f0f0cfcf -348ec90,cfbff0f0 -348ec94,f0f0f0f0 -348ec98,f0f0f0f0 -348ec9c,f0bfbfbf -348eca0,cfcfbff0 -348eca4,f0f0f0f0 -348eca8,f0f0f0f0 -348ecac,f0bfbfbf -348ecb0,bfbfbff0 -348ecb4,f0f0f0f0 -348ecb8,f0f0f0f0 -348ecbc,bfbfbfbf -348ecc0,bfbfbfbf -348ecc4,f0f0f0f0 -348ecc8,f0f0f0f0 -348eccc,bfbfbfbf -348ecd0,bfbfbfbf -348ecd4,f0f0f0f0 -348ecd8,f0f0f0f0 -348ecdc,afafbfbf -348ece0,bfbfbfbf -348ece4,f0f0f0f0 -348ece8,f0f0f0f0 -348ecec,f0afafaf -348ecf0,bfbfbfbf -348ecf4,aff0f0f0 -348ecf8,f0f0f0f0 -348ecfc,f0f0f0f0 -348ed00,f0afbfbf -348ed04,bff0f0f0 -348ed08,f0f0f0f0 -348ed0c,f0f0f0f0 +348ec5c,f0f0f0f0 +348ec60,f0f0f0f0 +348ec64,f0f0f0cf +348ec68,cff0f0f0 +348ec6c,f0f0f0f0 +348ec70,f0f0f0f0 +348ec74,f0f0cfcf +348ec78,cff0f0f0 +348ec7c,f0f0f0f0 +348ec80,f0f0f0f0 +348ec84,f0f0cfcf +348ec88,cfbff0f0 +348ec8c,f0f0f0f0 +348ec90,f0f0f0f0 +348ec94,f0f0cfcf +348ec98,cfbff0f0 +348ec9c,f0f0f0f0 +348eca0,f0f0f0f0 +348eca4,f0bfbfbf +348eca8,cfcfbff0 +348ecac,f0f0f0f0 +348ecb0,f0f0f0f0 +348ecb4,f0bfbfbf +348ecb8,bfbfbff0 +348ecbc,f0f0f0f0 +348ecc0,f0f0f0f0 +348ecc4,bfbfbfbf +348ecc8,bfbfbfbf +348eccc,f0f0f0f0 +348ecd0,f0f0f0f0 +348ecd4,bfbfbfbf +348ecd8,bfbfbfbf +348ecdc,f0f0f0f0 +348ece0,f0f0f0f0 +348ece4,afafbfbf +348ece8,bfbfbfbf +348ecec,f0f0f0f0 +348ecf0,f0f0f0f0 +348ecf4,f0afafaf +348ecf8,bfbfbfbf +348ecfc,aff0f0f0 +348ed00,f0f0f0f0 +348ed04,f0f0f0f0 +348ed08,f0afbfbf +348ed0c,bff0f0f0 348ed10,f0f0f0f0 348ed14,f0f0f0f0 348ed18,f0f0f0f0 @@ -13460,51 +13460,51 @@ ef4f9c,0 348ed50,f0f0f0f0 348ed54,f0f0f0f0 348ed58,f0f0f0f0 -348ed5c,f0f0f0df +348ed5c,f0f0f0f0 348ed60,f0f0f0f0 -348ed64,f0f0f0f0 +348ed64,f0f0f0df 348ed68,f0f0f0f0 -348ed6c,f0f0f0df -348ed70,dff0f0f0 -348ed74,f0f0f0f0 -348ed78,f0f0f0f0 -348ed7c,f0f0cfdf -348ed80,dff0f0f0 -348ed84,f0f0f0f0 -348ed88,f0f0f0f0 -348ed8c,f0f0cfcf -348ed90,cfcff0f0 -348ed94,f0f0f0f0 -348ed98,f0f0f0f0 -348ed9c,f0cfcfcf -348eda0,cfcff0f0 -348eda4,f0f0f0f0 -348eda8,f0f0f0f0 -348edac,f0cfcfcf -348edb0,cfcfcff0 -348edb4,f0f0f0f0 -348edb8,f0f0f0f0 -348edbc,cfcfcfcf -348edc0,cfcfcff0 -348edc4,f0f0f0f0 -348edc8,f0f0f0f0 -348edcc,bfbfcfcf -348edd0,cfcfcfcf -348edd4,f0f0f0f0 -348edd8,f0f0f0f0 -348eddc,bfbfbfbf -348ede0,bfbfbfbf -348ede4,f0f0f0f0 -348ede8,f0f0f0bf -348edec,bfbfbfbf -348edf0,bfbfbfbf -348edf4,bff0f0f0 -348edf8,f0f0f0f0 -348edfc,f0f0f0f0 -348ee00,f0f0bfbf -348ee04,bff0f0f0 -348ee08,f0f0f0f0 -348ee0c,f0f0f0f0 +348ed6c,f0f0f0f0 +348ed70,f0f0f0f0 +348ed74,f0f0f0df +348ed78,dff0f0f0 +348ed7c,f0f0f0f0 +348ed80,f0f0f0f0 +348ed84,f0f0cfdf +348ed88,dff0f0f0 +348ed8c,f0f0f0f0 +348ed90,f0f0f0f0 +348ed94,f0f0cfcf +348ed98,cfcff0f0 +348ed9c,f0f0f0f0 +348eda0,f0f0f0f0 +348eda4,f0cfcfcf +348eda8,cfcff0f0 +348edac,f0f0f0f0 +348edb0,f0f0f0f0 +348edb4,f0cfcfcf +348edb8,cfcfcff0 +348edbc,f0f0f0f0 +348edc0,f0f0f0f0 +348edc4,cfcfcfcf +348edc8,cfcfcff0 +348edcc,f0f0f0f0 +348edd0,f0f0f0f0 +348edd4,bfbfcfcf +348edd8,cfcfcfcf +348eddc,f0f0f0f0 +348ede0,f0f0f0f0 +348ede4,bfbfbfbf +348ede8,bfbfbfbf +348edec,f0f0f0f0 +348edf0,f0f0f0bf +348edf4,bfbfbfbf +348edf8,bfbfbfbf +348edfc,bff0f0f0 +348ee00,f0f0f0f0 +348ee04,f0f0f0f0 +348ee08,f0f0bfbf +348ee0c,bff0f0f0 348ee10,f0f0f0f0 348ee14,f0f0f0f0 348ee18,f0f0f0f0 @@ -13524,47 +13524,47 @@ ef4f9c,0 348ee50,f0f0f0f0 348ee54,f0f0f0f0 348ee58,f0f0f0f0 -348ee5c,f0f0f0df -348ee60,dff0f0f0 -348ee64,f0f0f0f0 -348ee68,f0f0f0f0 -348ee6c,f0f0f0df -348ee70,dff0f0f0 -348ee74,f0f0f0f0 -348ee78,f0f0f0f0 -348ee7c,f0f0dfdf -348ee80,dfdff0f0 -348ee84,f0f0f0f0 -348ee88,f0f0f0f0 -348ee8c,f0f0dfdf -348ee90,dfdff0f0 -348ee94,f0f0f0f0 -348ee98,f0f0f0f0 -348ee9c,f0f0cfcf -348eea0,cfcff0f0 -348eea4,f0f0f0f0 -348eea8,f0f0f0f0 -348eeac,f0cfcfcf -348eeb0,cfcfcff0 -348eeb4,f0f0f0f0 -348eeb8,f0f0f0f0 -348eebc,f0cfcfcf -348eec0,cfcfcff0 -348eec4,f0f0f0f0 -348eec8,f0f0f0f0 -348eecc,cfcfcfcf -348eed0,cfcfcfcf -348eed4,f0f0f0f0 -348eed8,f0f0f0f0 -348eedc,cfcfcfcf -348eee0,cfcfcfcf -348eee4,f0f0f0f0 -348eee8,f0f0f0bf -348eeec,bfbfbfbf -348eef0,bfbfbfbf -348eef4,bff0f0f0 -348eef8,f0f0f0f0 -348eefc,f0f0f0f0 +348ee5c,f0f0f0f0 +348ee60,f0f0f0f0 +348ee64,f0f0f0df +348ee68,dff0f0f0 +348ee6c,f0f0f0f0 +348ee70,f0f0f0f0 +348ee74,f0f0f0df +348ee78,dff0f0f0 +348ee7c,f0f0f0f0 +348ee80,f0f0f0f0 +348ee84,f0f0dfdf +348ee88,dfdff0f0 +348ee8c,f0f0f0f0 +348ee90,f0f0f0f0 +348ee94,f0f0dfdf +348ee98,dfdff0f0 +348ee9c,f0f0f0f0 +348eea0,f0f0f0f0 +348eea4,f0f0cfcf +348eea8,cfcff0f0 +348eeac,f0f0f0f0 +348eeb0,f0f0f0f0 +348eeb4,f0cfcfcf +348eeb8,cfcfcff0 +348eebc,f0f0f0f0 +348eec0,f0f0f0f0 +348eec4,f0cfcfcf +348eec8,cfcfcff0 +348eecc,f0f0f0f0 +348eed0,f0f0f0f0 +348eed4,cfcfcfcf +348eed8,cfcfcfcf +348eedc,f0f0f0f0 +348eee0,f0f0f0f0 +348eee4,cfcfcfcf +348eee8,cfcfcfcf +348eeec,f0f0f0f0 +348eef0,f0f0f0bf +348eef4,bfbfbfbf +348eef8,bfbfbfbf +348eefc,bff0f0f0 348ef00,f0f0f0f0 348ef04,f0f0f0f0 348ef08,f0f0f0f0 @@ -13583,3 +13583,5 @@ ef4f9c,0 348ef3c,f0f0f0f0 348ef40,f0f0f0f0 348ef44,f0f0f0f0 +348ef48,f0f0f0f0 +348ef4c,f0f0f0f0 diff --git a/worlds/oot/data/generated/symbols.json b/worlds/oot/data/generated/symbols.json index 6c9081f9..d2a26767 100644 --- a/worlds/oot/data/generated/symbols.json +++ b/worlds/oot/data/generated/symbols.json @@ -38,21 +38,22 @@ "CFG_TEXT_CURSOR_COLOR": "03480866", "CHAIN_HBA_REWARDS": "03483950", "CHEST_SIZE_MATCH_CONTENTS": "034826F0", - "COMPLETE_MASK_QUEST": "0348B1C9", + "COMPLETE_MASK_QUEST": "0348B1D1", "COOP_CONTEXT": "03480020", "COOP_VERSION": "03480020", "COSMETIC_CONTEXT": "03480844", "COSMETIC_FORMAT_VERSION": "03480844", "CURRENT_GROTTO_ID": "03482E82", + "DEATH_LINK": "0348002A", "DEBUG_OFFSET": "034828A0", "DISABLE_TIMERS": "03480CDC", - "DPAD_TEXTURE": "0348D748", + "DPAD_TEXTURE": "0348D750", "DUNGEONS_SHUFFLED": "03480CDE", "EXTENDED_OBJECT_TABLE": "03480C9C", "EXTERN_DAMAGE_MULTIPLYER": "03482CB1", "FAST_BUNNY_HOOD_ENABLED": "03480CE0", "FAST_CHESTS": "03480CD6", - "FONT_TEXTURE": "0348C280", + "FONT_TEXTURE": "0348C288", "FREE_SCARECROW_ENABLED": "03480CCC", "GET_CHEST_OVERRIDE_COLOR_WRAPPER": "03482720", "GET_CHEST_OVERRIDE_SIZE_WRAPPER": "034826F4", @@ -68,12 +69,12 @@ "LACS_CONDITION_COUNT": "03480CD2", "MALON_GAVE_ICETRAP": "0348368C", "MALON_TEXT_ID": "03480CDB", - "MAX_RUPEES": "0348B1CB", + "MAX_RUPEES": "0348B1D3", "MOVED_ADULT_KING_ZORA": "03482FFC", - "NO_ESCAPE_SEQUENCE": "0348B194", + "NO_ESCAPE_SEQUENCE": "0348B19C", "NO_FOG_STATE": "03480CDD", "OCARINAS_SHUFFLED": "03480CD5", - "OPEN_KAKARIKO": "0348B1CA", + "OPEN_KAKARIKO": "0348B1D2", "OUTGOING_ITEM": "03480030", "OUTGOING_KEY": "0348002C", "OUTGOING_PLAYER": "03480032", @@ -96,88 +97,88 @@ "SPEED_MULTIPLIER": "03482760", "START_TWINROVA_FIGHT": "0348307C", "TIME_TRAVEL_SAVED_EQUIPS": "03481A64", - "TRIFORCE_ICON_TEXTURE": "0348DF48", + "TRIFORCE_ICON_TEXTURE": "0348DF50", "TWINROVA_ACTION_TIMER": "03483080", "WINDMILL_SONG_ID": "03480CD9", "WINDMILL_TEXT_ID": "03480CDA", - "a_button": "0348B158", - "a_note_b": "0348B144", - "a_note_font_glow_base": "0348B12C", - "a_note_font_glow_max": "0348B128", - "a_note_g": "0348B148", - "a_note_glow_base": "0348B134", - "a_note_glow_max": "0348B130", - "a_note_r": "0348B14C", - "active_item_action_id": "0348B1AC", - "active_item_fast_chest": "0348B19C", - "active_item_graphic_id": "0348B1A0", - "active_item_object_id": "0348B1A4", - "active_item_row": "0348B1B0", - "active_item_text_id": "0348B1A8", - "active_override": "0348B1B8", - "active_override_is_outgoing": "0348B1B4", - "b_button": "0348B154", - "beating_dd": "0348B160", - "beating_no_dd": "0348B168", - "c_button": "0348B150", - "c_note_b": "0348B138", - "c_note_font_glow_base": "0348B11C", - "c_note_font_glow_max": "0348B118", - "c_note_g": "0348B13C", - "c_note_glow_base": "0348B124", - "c_note_glow_max": "0348B120", - "c_note_r": "0348B140", - "cfg_dungeon_info_enable": "0348B0E4", - "cfg_dungeon_info_mq_enable": "0348B188", - "cfg_dungeon_info_mq_need_map": "0348B184", - "cfg_dungeon_info_reward_enable": "0348B0E0", - "cfg_dungeon_info_reward_need_altar": "0348B17C", - "cfg_dungeon_info_reward_need_compass": "0348B180", - "cfg_dungeon_is_mq": "0348B1E8", - "cfg_dungeon_rewards": "03489EDC", - "cfg_file_select_hash": "0348B190", - "cfg_item_overrides": "0348B23C", - "defaultDDHeart": "0348B16C", - "defaultHeart": "0348B174", - "dpad_sprite": "0348A050", - "dummy_actor": "0348B1C0", - "dungeon_count": "0348B0E8", - "dungeons": "03489F00", - "empty_dlist": "0348B100", - "extern_ctxt": "03489F9C", - "font_sprite": "0348A060", - "freecam_modes": "03489C5C", - "hash_sprites": "0348B0F4", - "hash_symbols": "03489FB0", - "heap_next": "0348B1E4", - "heart_sprite": "03489FF0", - "icon_sprites": "03489E20", - "item_digit_sprite": "0348A010", - "item_overrides_count": "0348B1C4", - "item_table": "0348A0D8", - "items_sprite": "0348A080", - "key_rupee_clock_sprite": "0348A020", - "last_fog_distance": "0348B0EC", - "linkhead_skull_sprite": "0348A000", - "medal_colors": "03489EEC", - "medals_sprite": "0348A090", - "normal_dd": "0348B15C", - "normal_no_dd": "0348B164", - "object_slots": "0348C23C", - "pending_freezes": "0348B1C8", - "pending_item_queue": "0348B224", - "quest_items_sprite": "0348A070", - "rupee_colors": "03489E2C", - "satisified_pending_frames": "0348B198", - "scene_fog_distance": "0348B0F0", - "setup_db": "0348A0B0", - "song_note_sprite": "0348A030", - "stones_sprite": "0348A0A0", - "text_cursor_border_base": "0348B10C", - "text_cursor_border_max": "0348B108", - "text_cursor_inner_base": "0348B114", - "text_cursor_inner_max": "0348B110", - "triforce_hunt_enabled": "0348B1D8", - "triforce_pieces_requied": "0348B17A", - "triforce_sprite": "0348A040" + "a_button": "0348B160", + "a_note_b": "0348B14C", + "a_note_font_glow_base": "0348B134", + "a_note_font_glow_max": "0348B130", + "a_note_g": "0348B150", + "a_note_glow_base": "0348B13C", + "a_note_glow_max": "0348B138", + "a_note_r": "0348B154", + "active_item_action_id": "0348B1B4", + "active_item_fast_chest": "0348B1A4", + "active_item_graphic_id": "0348B1A8", + "active_item_object_id": "0348B1AC", + "active_item_row": "0348B1B8", + "active_item_text_id": "0348B1B0", + "active_override": "0348B1C0", + "active_override_is_outgoing": "0348B1BC", + "b_button": "0348B15C", + "beating_dd": "0348B168", + "beating_no_dd": "0348B170", + "c_button": "0348B158", + "c_note_b": "0348B140", + "c_note_font_glow_base": "0348B124", + "c_note_font_glow_max": "0348B120", + "c_note_g": "0348B144", + "c_note_glow_base": "0348B12C", + "c_note_glow_max": "0348B128", + "c_note_r": "0348B148", + "cfg_dungeon_info_enable": "0348B0EC", + "cfg_dungeon_info_mq_enable": "0348B190", + "cfg_dungeon_info_mq_need_map": "0348B18C", + "cfg_dungeon_info_reward_enable": "0348B0E8", + "cfg_dungeon_info_reward_need_altar": "0348B184", + "cfg_dungeon_info_reward_need_compass": "0348B188", + "cfg_dungeon_is_mq": "0348B1F0", + "cfg_dungeon_rewards": "03489EE4", + "cfg_file_select_hash": "0348B198", + "cfg_item_overrides": "0348B244", + "defaultDDHeart": "0348B174", + "defaultHeart": "0348B17C", + "dpad_sprite": "0348A058", + "dummy_actor": "0348B1C8", + "dungeon_count": "0348B0F0", + "dungeons": "03489F08", + "empty_dlist": "0348B108", + "extern_ctxt": "03489FA4", + "font_sprite": "0348A068", + "freecam_modes": "03489C60", + "hash_sprites": "0348B0FC", + "hash_symbols": "03489FB8", + "heap_next": "0348B1EC", + "heart_sprite": "03489FF8", + "icon_sprites": "03489E24", + "item_digit_sprite": "0348A018", + "item_overrides_count": "0348B1CC", + "item_table": "0348A0E0", + "items_sprite": "0348A088", + "key_rupee_clock_sprite": "0348A028", + "last_fog_distance": "0348B0F4", + "linkhead_skull_sprite": "0348A008", + "medal_colors": "03489EF4", + "medals_sprite": "0348A098", + "normal_dd": "0348B164", + "normal_no_dd": "0348B16C", + "object_slots": "0348C244", + "pending_freezes": "0348B1D0", + "pending_item_queue": "0348B22C", + "quest_items_sprite": "0348A078", + "rupee_colors": "03489E30", + "satisified_pending_frames": "0348B1A0", + "scene_fog_distance": "0348B0F8", + "setup_db": "0348A0B8", + "song_note_sprite": "0348A038", + "stones_sprite": "0348A0A8", + "text_cursor_border_base": "0348B114", + "text_cursor_border_max": "0348B110", + "text_cursor_inner_base": "0348B11C", + "text_cursor_inner_max": "0348B118", + "triforce_hunt_enabled": "0348B1E0", + "triforce_pieces_requied": "0348B182", + "triforce_sprite": "0348A048" } \ No newline at end of file diff --git a/worlds/ror2/Rules.py b/worlds/ror2/Rules.py index df207b5e..08706480 100644 --- a/worlds/ror2/Rules.py +++ b/worlds/ror2/Rules.py @@ -42,4 +42,4 @@ def set_rules(world: MultiWorld, player: int): set_rule(world.get_location("Victory", player), lambda state: state._ror_has_items(player, 5 * items_per_level) and state.has("Beat Level Five", player)) - world.completion_condition[player] = lambda state: state.has("Victory", player) \ No newline at end of file + world.completion_condition[player] = lambda state: state.has("Victory", player) diff --git a/worlds/sm/Items.py b/worlds/sm/Items.py new file mode 100644 index 00000000..ff8970b6 --- /dev/null +++ b/worlds/sm/Items.py @@ -0,0 +1,14 @@ +from worlds.sm.variaRandomizer.rando.Items import ItemManager + +items_start_id = 83000 + +def gen_special_id(): + special_id_value_start = 32 + while True: + yield special_id_value_start + special_id_value_start += 1 + +gen_run = gen_special_id() + +lookup_id_to_name = dict((items_start_id + (value.Id if value.Id != None else next(gen_run)), value.Name) for key, value in ItemManager.Items.items()) +lookup_name_to_id = {item_name: item_id for item_id, item_name in lookup_id_to_name.items()} \ No newline at end of file diff --git a/worlds/sm/Locations.py b/worlds/sm/Locations.py new file mode 100644 index 00000000..4e80ab00 --- /dev/null +++ b/worlds/sm/Locations.py @@ -0,0 +1,14 @@ +from worlds.sm.variaRandomizer.graph.location import locationsDict + +locations_start_id = 82000 + +def gen_boss_id(): + boss_id_value_start = 256 + while True: + yield boss_id_value_start + boss_id_value_start += 1 + +gen_run = gen_boss_id() + +lookup_id_to_name = dict((locations_start_id + (value.Id if value.Id != None else next(gen_run)), key) for key, value in locationsDict.items()) +lookup_name_to_id = {location_name: location_id for location_id, location_name in lookup_id_to_name.items()} \ No newline at end of file diff --git a/worlds/sm/Options.py b/worlds/sm/Options.py new file mode 100644 index 00000000..16ccb5d2 --- /dev/null +++ b/worlds/sm/Options.py @@ -0,0 +1,237 @@ +import typing +from Options import Choice, Range, OptionDict, OptionList, Option, Toggle, DefaultOnToggle, DeathLink + +class StartItemsRemovesFromPool(Toggle): + displayname = "StartItems Removes From Item Pool" + +class Preset(Choice): + """choose one of the preset or specify "varia_custom" to use varia_custom_preset option or specify "custom" to use custom_preset option""" + displayname = "Preset" + option_newbie = 0 + option_casual = 1 + option_regular = 2 + option_veteran = 3 + option_expert = 4 + option_master = 5 + option_samus = 6 + option_Season_Races = 7 + option_SMRAT2021 = 8 + option_solution = 9 + option_custom = 10 + option_varia_custom = 11 + default = 2 + +class StartLocation(Choice): + displayname = "Start Location" + option_Ceres = 0 + option_Landing_Site = 1 + option_Gauntlet_Top = 2 + option_Green_Brinstar_Elevator = 3 + option_Big_Pink = 4 + option_Etecoons_Supers = 5 + option_Wrecked_Ship_Main = 6 + option_Firefleas_Top = 7 + option_Business_Center = 8 + option_Bubble_Mountain = 9 + option_Mama_Turtle = 10 + option_Watering_Hole = 11 + option_Aqueduct = 12 + option_Red_Brinstar_Elevator = 13 + option_Golden_Four = 14 + default = 1 + +class MaxDifficulty(Choice): + displayname = "Maximum Difficulty" + option_easy = 0 + option_medium = 1 + option_hard = 2 + option_harder = 3 + option_hardcore = 4 + option_mania = 5 + option_infinity = 6 + default = 4 + +class MorphPlacement(Choice): + displayname = "Morph Placement" + option_early = 0 + option_normal = 1 + default = 0 + +class SuitsRestriction(DefaultOnToggle): + displayname = "Suits Restriction" + +class StrictMinors(Toggle): + displayname = "Strict Minors" + +class MissileQty(Range): + displayname = "Missile Quantity" + range_start = 10 + range_end = 90 + default = 30 + +class SuperQty(Range): + displayname = "Super Quantity" + range_start = 10 + range_end = 90 + default = 20 + +class PowerBombQty(Range): + displayname = "Power Bomb Quantity" + range_start = 10 + range_end = 90 + default = 10 + +class MinorQty(Range): + displayname = "Minor Quantity" + range_start = 7 + range_end = 100 + default = 100 + +class EnergyQty(Choice): + displayname = "Energy Quantity" + option_ultra_sparse = 0 + option_sparse = 1 + option_medium = 2 + option_vanilla = 3 + default = 3 + +class AreaRandomization(Choice): + displayname = "Area Randomization" + option_off = 0 + option_light = 1 + option_on = 2 + alias_false = 0 + alias_true = 2 + default = 0 + +class AreaLayout(Toggle): + displayname = "Area Layout" + +class DoorsColorsRando(Toggle): + displayname = "Doors Colors Rando" + +class AllowGreyDoors(Toggle): + displayname = "Allow Grey Doors" + +class BossRandomization(Toggle): + displayname = "Boss Randomization" + +class FunCombat(Toggle): + displayname = "Fun Combat" + +class FunMovement(Toggle): + displayname = "Fun Movement" + +class FunSuits(Toggle): + displayname = "Fun Suits" + +class LayoutPatches(DefaultOnToggle): + displayname = "Layout Patches" + +class VariaTweaks(Toggle): + displayname = "Varia Tweaks" + +class NerfedCharge(Toggle): + displayname = "Nerfed Charge" + +class GravityBehaviour(Choice): + displayname = "Gravity Behaviour" + option_Vanilla = 0 + option_Balanced = 1 + option_Progressive = 2 + default = 1 + +class ElevatorsDoorsSpeed(DefaultOnToggle): + displayname = "Elevators doors speed" + +class SpinJumpRestart(Toggle): + displayname = "Spin Jump Restart" + +class InfiniteSpaceJump(Toggle): + displayname = "Infinite Space Jump" + +class RefillBeforeSave(Toggle): + displayname = "Refill Before Save" + +class Hud(Toggle): + displayname = "Hud" + +class Animals(Toggle): + displayname = "Animals" + +class NoMusic(Toggle): + displayname = "No Music" + +class RandomMusic(Toggle): + displayname = "Random Music" + +class CustomPreset(OptionDict): + """ + see https://randommetroidsolver.pythonanywhere.com/presets for detailed info on each preset settings + knows: each skill (know) has a pair [can use, perceived difficulty using one of 1, 5, 10, 25, 50 or 100 each one matching a max_difficulty] + settings: hard rooms, hellruns and bosses settings + controller: predefined controller mapping and moon walk setting + """ + displayname = "Custom Preset" + default = { "knows": {}, + "settings": {}, + "controller": {} + } + +class VariaCustomPreset(OptionList): + """use an entry from the preset list on https://randommetroidsolver.pythonanywhere.com/presets""" + displayname = "Varia Custom Preset" + default = {} + + +sm_options: typing.Dict[str, type(Option)] = { + "start_inventory_removes_from_pool": StartItemsRemovesFromPool, + "preset": Preset, + "start_location": StartLocation, + "death_link": DeathLink, + #"majors_split": "Full", + #"scav_num_locs": "10", + #"scav_randomized": "off", + #"scav_escape": "off", + "max_difficulty": MaxDifficulty, + #"progression_speed": "medium", + #"progression_difficulty": "normal", + "morph_placement": MorphPlacement, + "suits_restriction": SuitsRestriction, + #"hide_items": "off", + "strict_minors": StrictMinors, + "missile_qty": MissileQty, + "super_qty": SuperQty, + "power_bomb_qty": PowerBombQty, + "minor_qty": MinorQty, + "energy_qty": EnergyQty, + "area_randomization": AreaRandomization, + "area_layout": AreaLayout, + "doors_colors_rando": DoorsColorsRando, + "allow_grey_doors": AllowGreyDoors, + "boss_randomization": BossRandomization, + #"minimizer": "off", + #"minimizer_qty": "45", + #"minimizer_tourian": "off", + #"escape_rando": "off", + #"remove_escape_enemies": "off", + "fun_combat": FunCombat, + "fun_movement": FunMovement, + "fun_suits": FunSuits, + "layout_patches": LayoutPatches, + "varia_tweaks": VariaTweaks, + "nerfed_charge": NerfedCharge, + "gravity_behaviour": GravityBehaviour, + #"item_sounds": "on", + "elevators_doors_speed": ElevatorsDoorsSpeed, + "spin_jump_restart": SpinJumpRestart, + #"rando_speed": "off", + "infinite_space_jump": InfiniteSpaceJump, + "refill_before_save": RefillBeforeSave, + "hud": Hud, + "animals": Animals, + "no_music": NoMusic, + "random_music": RandomMusic, + "custom_preset": CustomPreset, + "varia_custom_preset": VariaCustomPreset + } diff --git a/worlds/sm/Regions.py b/worlds/sm/Regions.py new file mode 100644 index 00000000..a3f37692 --- /dev/null +++ b/worlds/sm/Regions.py @@ -0,0 +1,41 @@ +def create_regions(self, world, player: int): + from . import create_region + from BaseClasses import Entrance + from logic.logic import Logic + from graph.vanilla.graph_locations import locationsDict + + regions = [] + for accessPoint in Logic.accessPoints: + regions.append(create_region( self, + world, + player, + accessPoint.Name, + None, + [accessPoint.Name + "->" + key for key in accessPoint.intraTransitions.keys()])) + + world.regions += regions + + # create a region for each location and link each to what the location has access + # we make them one way so that the filler (and spoiler log) doesnt try to use those region as intermediary path + # this is required in AP because a location cant have multiple parent regions + locationRegions = [] + for locationName, value in locationsDict.items(): + locationRegions.append(create_region( self, + world, + player, + locationName, + [locationName])) + for key in value.AccessFrom.keys(): + currentRegion =world.get_region(key, player) + currentRegion.exits.append(Entrance(player, key + "->"+ locationName, currentRegion)) + + world.regions += locationRegions + #create entrances + regionConcat = regions + locationRegions + for region in regionConcat: + for exit in region.exits: + exit.connect(world.get_region(exit.name[exit.name.find("->") + 2:], player)) + + world.regions += [ + create_region(self, world, player, 'Menu', None, ['StartAP']) + ] diff --git a/worlds/sm/Rom.py b/worlds/sm/Rom.py new file mode 100644 index 00000000..efddc17a --- /dev/null +++ b/worlds/sm/Rom.py @@ -0,0 +1,30 @@ +import Utils +from Patch import read_rom + +JAP10HASH = '21f3e98df4780ee1c667b84e57d88675' + +import hashlib +import os + + +def get_base_rom_bytes(file_name: str = "") -> bytes: + base_rom_bytes = getattr(get_base_rom_bytes, "base_rom_bytes", None) + if not base_rom_bytes: + file_name = get_base_rom_path(file_name) + base_rom_bytes = bytes(read_rom(open(file_name, "rb"))) + + basemd5 = hashlib.md5() + basemd5.update(base_rom_bytes) + if JAP10HASH != basemd5.hexdigest(): + raise Exception('Supplied Base Rom does not match known MD5 for JAP(1.0) release. ' + 'Get the correct game and version, then dump it') + get_base_rom_bytes.base_rom_bytes = base_rom_bytes + return base_rom_bytes + +def get_base_rom_path(file_name: str = "") -> str: + options = Utils.get_options() + if not file_name: + file_name = options["sm_options"]["rom_file"] + if not os.path.exists(file_name): + file_name = Utils.local_path(file_name) + return file_name \ No newline at end of file diff --git a/worlds/sm/Rules.py b/worlds/sm/Rules.py new file mode 100644 index 00000000..84849f52 --- /dev/null +++ b/worlds/sm/Rules.py @@ -0,0 +1,40 @@ +from ..generic.Rules import set_rule, add_rule + +from graph.vanilla.graph_locations import locationsDict +from graph.graph_utils import vanillaTransitions, getAccessPoint +from logic.logic import Logic +from rom.rom_patches import RomPatches +from utils.doorsmanager import DoorsManager + +def evalSMBool(smbool, maxDiff): + return smbool.bool == True and smbool.difficulty <= maxDiff + +def add_accessFrom_rule(location, player, accessFrom): + add_rule(location, lambda state: any((state.can_reach(accessName, player=player) and evalSMBool(rule(state.smbm[player]), state.smbm[player].maxDiff)) for accessName, rule in accessFrom.items())) + +def add_postAvailable_rule(location, player, func): + add_rule(location, lambda state: evalSMBool(func(state.smbm[player]), state.smbm[player].maxDiff)) + +def set_available_rule(location, player, func): + set_rule(location, lambda state: evalSMBool(func(state.smbm[player]), state.smbm[player].maxDiff)) + +def set_entrance_rule(entrance, player, func): + set_rule(entrance, lambda state: evalSMBool(func(state.smbm[player]), state.smbm[player].maxDiff)) + +def add_entrance_rule(entrance, player, func): + add_rule(entrance, lambda state: evalSMBool(func(state.smbm[player]), state.smbm[player].maxDiff)) + +def set_rules(world, player): + world.completion_condition[player] = lambda state: state.has('Mother Brain', player) + + for key, value in locationsDict.items(): + location = world.get_location(key, player) + set_available_rule(location, player, value.Available) + if value.AccessFrom is not None: + add_accessFrom_rule(location, player, value.AccessFrom) + if value.PostAvailable is not None: + add_postAvailable_rule(location, player, value.PostAvailable) + + for accessPoint in Logic.accessPoints: + for key, value1 in accessPoint.intraTransitions.items(): + set_entrance_rule(world.get_entrance(accessPoint.Name + "->" + key, player), player, value1) diff --git a/worlds/sm/__init__.py b/worlds/sm/__init__.py new file mode 100644 index 00000000..db156dcd --- /dev/null +++ b/worlds/sm/__init__.py @@ -0,0 +1,511 @@ +import logging +import copy +import os +import threading +from typing import Set + +logger = logging.getLogger("Super Metroid") + +from .Locations import lookup_name_to_id as locations_lookup_name_to_id +from .Items import lookup_name_to_id as items_lookup_name_to_id +from .Regions import create_regions +from .Rules import set_rules, add_entrance_rule +from .Options import sm_options +from .Rom import get_base_rom_path +import Utils + +from BaseClasses import Region, Entrance, Location, MultiWorld, Item, RegionType, CollectionState +from ..AutoWorld import World +import Patch + +from logic.smboolmanager import SMBoolManager +from graph.vanilla.graph_locations import locationsDict +from graph.graph_utils import getAccessPoint +from rando.ItemLocContainer import ItemLocation +from rando.Items import ItemManager +from utils.parameters import * +from logic.logic import Logic +from randomizer import VariaRandomizer + + +class SMWorld(World): + game: str = "Super Metroid" + topology_present = True + data_version = 1 + options = sm_options + item_names: Set[str] = frozenset(items_lookup_name_to_id) + location_names: Set[str] = frozenset(locations_lookup_name_to_id) + item_name_to_id = items_lookup_name_to_id + location_name_to_id = locations_lookup_name_to_id + + remote_items: bool = False + remote_start_inventory: bool = False + + itemManager: ItemManager + + locations = {} + + Logic.factory('vanilla') + + def __init__(self, world: MultiWorld, player: int): + self.rom_name_available_event = threading.Event() + super().__init__(world, player) + + def __new__(cls, world, player): + + # Add necessary objects to CollectionState on initialization + orig_init = CollectionState.__init__ + orig_copy = CollectionState.copy + + def sm_init(self, parent: MultiWorld): + if (hasattr(parent, "state")): # for unit tests where MultiWorld is instanciated before worlds + self.smbm = {player: SMBoolManager(player, parent.state.smbm[player].maxDiff) for player in parent.get_game_players("Super Metroid")} + orig_init(self, parent) + + + def sm_copy(self): + ret = orig_copy(self) + ret.smbm = {player: copy.deepcopy(self.smbm[player]) for player in self.world.get_game_players("Super Metroid")} + return ret + + CollectionState.__init__ = sm_init + CollectionState.copy = sm_copy + + if world: + world.state.smbm = {} + + return super().__new__(cls) + + def generate_early(self): + Logic.factory('vanilla') + + self.variaRando = VariaRandomizer(self.world, get_base_rom_path(), self.player) + self.world.state.smbm[self.player] = SMBoolManager(self.player, self.variaRando.maxDifficulty) + + # keeps Nothing items local so no player will ever pickup Nothing + # doing so reduces contribution of this world to the Multiworld the more Nothing there is though + self.world.local_items[self.player].value.add('Nothing') + + if (self.variaRando.args.morphPlacement == "early"): + self.world.local_items[self.player].value.add('Morph') + + def generate_basic(self): + itemPool = self.variaRando.container.itemPool + self.startItems = [variaItem for item in self.world.precollected_items[self.player] for variaItem in ItemManager.Items.values() if variaItem.Name == item.name] + if self.world.start_inventory_removes_from_pool[self.player]: + for item in self.startItems: + if (item in itemPool): + itemPool.remove(item) + + missingPool = 105 - len(itemPool) + 1 + for i in range(1, missingPool): + itemPool.append(ItemManager.Items['Nothing']) + + # Generate item pool + pool = [] + self.locked_items = {} + weaponCount = [0, 0, 0] + for item in itemPool: + isAdvancement = True + if item.Type == 'Missile': + if weaponCount[0] < 3: + weaponCount[0] += 1 + else: + isAdvancement = False + elif item.Type == 'Super': + if weaponCount[1] < 2: + weaponCount[1] += 1 + else: + isAdvancement = False + elif item.Type == 'PowerBomb': + if weaponCount[2] < 3: + weaponCount[2] += 1 + else: + isAdvancement = False + elif item.Type == 'Nothing': + isAdvancement = False + + itemClass = ItemManager.Items[item.Type].Class + smitem = SMItem(item.Name, isAdvancement, item.Type, None if itemClass == 'Boss' else self.item_name_to_id[item.Name], player = self.player) + if itemClass == 'Boss': + self.locked_items[item.Name] = smitem + else: + pool.append(smitem) + + self.world.itempool += pool + + for (location, item) in self.locked_items.items(): + self.world.get_location(location, self.player).place_locked_item(item) + self.world.get_location(location, self.player).address = None + + startAP = self.world.get_entrance('StartAP', self.player) + startAP.connect(self.world.get_region(self.variaRando.args.startLocation, self.player)) + + for src, dest in self.variaRando.randoExec.areaGraph.InterAreaTransitions: + src_region = self.world.get_region(src.Name, self.player) + dest_region = self.world.get_region(dest.Name, self.player) + src_region.exits.append(Entrance(self.player, src.Name + "->" + dest.Name, src_region)) + srcDestEntrance = self.world.get_entrance(src.Name + "->" + dest.Name, self.player) + srcDestEntrance.connect(dest_region) + add_entrance_rule(self.world.get_entrance(src.Name + "->" + dest.Name, self.player), self.player, getAccessPoint(src.Name).traverse) + + def set_rules(self): + set_rules(self.world, self.player) + + + def create_regions(self): + create_locations(self, self.player) + create_regions(self, self.world, self.player) + + def getWord(self, w): + return (w & 0x00FF, (w & 0xFF00) >> 8) + + # used for remote location Credits Spoiler of local items + class DummyLocation: + def __init__(self, name): + self.Name = name + + def isBoss(self): + return False + + def convertToROMItemName(self, itemName): + charMap = { "A" : 0x3CE0, + "B" : 0x3CE1, + "C" : 0x3CE2, + "D" : 0x3CE3, + "E" : 0x3CE4, + "F" : 0x3CE5, + "G" : 0x3CE6, + "H" : 0x3CE7, + "I" : 0x3CE8, + "J" : 0x3CE9, + "K" : 0x3CEA, + "L" : 0x3CEB, + "M" : 0x3CEC, + "N" : 0x3CED, + "O" : 0x3CEE, + "P" : 0x3CEF, + "Q" : 0x3CF0, + "R" : 0x3CF1, + "S" : 0x3CF2, + "T" : 0x3CF3, + "U" : 0x3CF4, + "V" : 0x3CF5, + "W" : 0x3CF6, + "X" : 0x3CF7, + "Y" : 0x3CF8, + "Z" : 0x3CF9, + " " : 0x3C4E, + "!" : 0x3CFF, + "?" : 0x3CFE, + "'" : 0x3CFD, + "," : 0x3CFB, + "." : 0x3CFA, + "-" : 0x3CCF, + "_" : 0x000E, + "1" : 0x3C00, + "2" : 0x3C01, + "3" : 0x3C02, + "4" : 0x3C03, + "5" : 0x3C04, + "6" : 0x3C05, + "7" : 0x3C06, + "8" : 0x3C07, + "9" : 0x3C08, + "0" : 0x3C09, + "%" : 0x3C0A} + data = [] + + itemName = itemName.upper()[:26] + itemName = itemName.strip() + itemName = itemName.center(26, " ") + itemName = "___" + itemName + "___" + + for char in itemName: + (w0, w1) = self.getWord(charMap.get(char, 0x3C4E)) + data.append(w0) + data.append(w1) + return data + + def APPatchRom(self, romPatcher): + multiWorldLocations = {} + multiWorldItems = {} + idx = 0 + itemId = 0 + for itemLoc in self.world.get_locations(): + if itemLoc.player == self.player and locationsDict[itemLoc.name].Id != None: + if itemLoc.item.type in ItemManager.Items: + itemId = ItemManager.Items[itemLoc.item.type].Id + else: + itemId = ItemManager.Items['ArchipelagoItem'].Id + idx + multiWorldItems[0x029EA3 + idx*64] = self.convertToROMItemName(itemLoc.item.name) + idx += 1 + (w0, w1) = self.getWord(0 if itemLoc.item.player == self.player else 1) + (w2, w3) = self.getWord(itemId) + (w4, w5) = self.getWord(itemLoc.item.player - 1) + (w6, w7) = self.getWord(0 if itemLoc.item.advancement else 1) + multiWorldLocations[0x1C6000 + locationsDict[itemLoc.name].Id*8] = [w0, w1, w2, w3, w4, w5, w6, w7] + + + itemSprites = ["off_world_prog_item.bin", "off_world_item.bin"] + idx = 0 + offworldSprites = {} + for fileName in itemSprites: + with open(Utils.local_path("lib", "worlds", "sm", "data", "custom_sprite", fileName) if Utils.is_frozen() else Utils.local_path("worlds", "sm", "data", "custom_sprite", fileName), 'rb') as stream: + buffer = bytearray(stream.read()) + offworldSprites[0x027882 + 10*(21 + idx) + 2] = buffer[0:8] + offworldSprites[0x049100 + idx*256] = buffer[8:264] + idx += 1 + + openTourianGreyDoors = {0x07C823 + 5: [0x0C], 0x07C831 + 5: [0x0C]} + + deathLink = {0x277f04: [int(self.world.death_link[self.player])]} + patchDict = { 'MultiWorldLocations': multiWorldLocations, + 'MultiWorldItems': multiWorldItems, + 'offworldSprites': offworldSprites, + 'openTourianGreyDoors': openTourianGreyDoors, + 'deathLink': deathLink} + romPatcher.applyIPSPatchDict(patchDict) + + playerNames = {} + for p in range(1, self.world.players + 1): + playerNames[0x1C5000 + (p - 1) * 16] = self.world.player_name[p][:16].upper().center(16).encode() + playerNames[0x1C5000 + (self.world.players) * 16] = "Archipelago".upper().center(16).encode() + + romPatcher.applyIPSPatch('PlayerName', { 'PlayerName': playerNames }) + + # set rom name + # 21 bytes + from Main import __version__ + self.romName = bytearray(f'SM{__version__.replace(".", "")[0:3]}_{self.player}_{self.world.seed:11}\0', 'utf8')[:21] + self.romName.extend([0] * (21 - len(self.romName))) + romPatcher.applyIPSPatch('ROMName', { 'ROMName': {0x1C4F00 : self.romName, 0x007FC0 : self.romName} }) + + startItemROMAddressBase = 0x2FD8B9 + + # current, base value or bitmask, max, base value or bitmask + startItemROMDict = {'ETank': [0x8, 0x64, 0xA, 0x64], + 'Missile': [0xC, 0x5, 0xE, 0x5], + 'Super': [0x10, 0x5, 0x12, 0x5], + 'PowerBomb': [0x14, 0x5, 0x16, 0x5], + 'Reserve': [0x1A, 0x64, 0x18, 0x64], + 'Morph': [0x2, 0x4, 0x0, 0x4], + 'Bomb': [0x3, 0x10, 0x1, 0x10], + 'SpringBall': [0x2, 0x2, 0x0, 0x2], + 'HiJump': [0x3, 0x1, 0x1, 0x1], + 'Varia': [0x2, 0x1, 0x0, 0x1], + 'Gravity': [0x2, 0x20, 0x0, 0x20], + 'SpeedBooster': [0x3, 0x20, 0x1, 0x20], + 'SpaceJump': [0x3, 0x2, 0x1, 0x2], + 'ScrewAttack': [0x2, 0x8, 0x0, 0x8], + 'Charge': [0x7, 0x10, 0x5, 0x10], + 'Ice': [0x6, 0x2, 0x4, 0x2], + 'Wave': [0x6, 0x1, 0x4, 0x1], + 'Spazer': [0x6, 0x4, 0x4, 0x4], + 'Plasma': [0x6, 0x8, 0x4, 0x8], + 'Grapple': [0x3, 0x40, 0x1, 0x40], + 'XRayScope': [0x3, 0x80, 0x1, 0x80] + } + mergedData = {} + hasETank = False + hasSpazer = False + hasPlasma = False + for startItem in self.startItems: + item = startItem.Type + if item == 'ETank': hasETank = True + if item == 'Spazer': hasSpazer = True + if item == 'Plasma': hasPlasma = True + if (item in ['ETank', 'Missile', 'Super', 'PowerBomb', 'Reserve']): + (currentValue, currentBase, maxValue, maxBase) = startItemROMDict[item] + if (startItemROMAddressBase + currentValue) in mergedData: + mergedData[startItemROMAddressBase + currentValue] += currentBase + mergedData[startItemROMAddressBase + maxValue] += maxBase + else: + mergedData[startItemROMAddressBase + currentValue] = currentBase + mergedData[startItemROMAddressBase + maxValue] = maxBase + else: + (collected, currentBitmask, equipped, maxBitmask) = startItemROMDict[item] + if (startItemROMAddressBase + collected) in mergedData: + mergedData[startItemROMAddressBase + collected] |= currentBitmask + mergedData[startItemROMAddressBase + equipped] |= maxBitmask + else: + mergedData[startItemROMAddressBase + collected] = currentBitmask + mergedData[startItemROMAddressBase + equipped] = maxBitmask + + if hasETank: + mergedData[startItemROMAddressBase + 0x8] += 99 + mergedData[startItemROMAddressBase + 0xA] += 99 + + if hasSpazer and hasPlasma: + mergedData[startItemROMAddressBase + 0x4] &= ~0x4 + + for key, value in mergedData.items(): + if (key - startItemROMAddressBase > 7): + (w0, w1) = self.getWord(value) + mergedData[key] = [w0, w1] + else: + mergedData[key] = [value] + + + startItemPatch = { 'startItemPatch': mergedData } + romPatcher.applyIPSPatch('startItemPatch', startItemPatch) + + romPatcher.commitIPS() + + itemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type if itemLoc.item.type in ItemManager.Items else 'ArchipelagoItem'], locationsDict[itemLoc.name], True) for itemLoc in self.world.get_locations() if itemLoc.player == self.player] + romPatcher.writeItemsLocs(itemLocs) + + itemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type], locationsDict[itemLoc.name] if itemLoc.name in locationsDict and itemLoc.player == self.player else self.DummyLocation(self.world.get_player_name(itemLoc.player) + " " + itemLoc.name), True) for itemLoc in self.world.get_locations() if itemLoc.item.player == self.player] + progItemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type], locationsDict[itemLoc.name] if itemLoc.name in locationsDict and itemLoc.player == self.player else self.DummyLocation(self.world.get_player_name(itemLoc.player) + " " + itemLoc.name), True) for itemLoc in self.world.get_locations() if itemLoc.item.player == self.player and itemLoc.item.advancement == True] + # progItemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type if itemLoc.item.type in ItemManager.Items else 'ArchipelagoItem'], locationsDict[itemLoc.name], True) for itemLoc in self.world.get_locations() if itemLoc.player == self.player and itemLoc.item.player == self.player and itemLoc.item.advancement == True] + + # romPatcher.writeSplitLocs(self.variaRando.args.majorsSplit, itemLocs, progItemLocs) + romPatcher.writeSpoiler(itemLocs, progItemLocs) + romPatcher.writeRandoSettings(self.variaRando.randoExec.randoSettings, itemLocs) + + def generate_output(self, output_directory: str): + try: + outfilebase = 'AP_' + self.world.seed_name + outfilepname = f'_P{self.player}' + outfilepname += f"_{self.world.player_name[self.player].replace(' ', '_')}" \ + + outputFilename = os.path.join(output_directory, f'{outfilebase}{outfilepname}.sfc') + self.variaRando.PatchRom(outputFilename, self.APPatchRom) + + self.write_crc(outputFilename) + + Patch.create_patch_file(outputFilename, player=self.player, player_name=self.world.player_name[self.player], game=Patch.GAME_SM) + os.unlink(outputFilename) + self.rom_name = self.romName + except: + raise + finally: + self.rom_name_available_event.set() # make sure threading continues and errors are collected + + def checksum_mirror_sum(self, start, length, mask = 0x800000): + while (not(length & mask) and mask): + mask >>= 1 + + part1 = sum(start[:mask]) & 0xFFFF + part2 = 0 + + next_length = length - mask + if next_length: + part2 = self.checksum_mirror_sum(start[mask:], next_length, mask >> 1) + + while (next_length < mask): + next_length += next_length + part2 += part2 + + length = mask + mask + + return (part1 + part2) & 0xFFFF + + def write_bytes(self, buffer, startaddress: int, values): + buffer[startaddress:startaddress + len(values)] = values + + def write_crc(self, romName): + with open(romName, 'rb') as stream: + buffer = bytearray(stream.read()) + crc = self.checksum_mirror_sum(buffer, len(buffer)) + inv = crc ^ 0xFFFF + self.write_bytes(buffer, 0x7FDC, [inv & 0xFF, (inv >> 8) & 0xFF, crc & 0xFF, (crc >> 8) & 0xFF]) + with open(romName, 'wb') as outfile: + outfile.write(buffer) + + def modify_multidata(self, multidata: dict): + import base64 + # wait for self.rom_name to be available. + self.rom_name_available_event.wait() + rom_name = getattr(self, "rom_name", None) + # we skip in case of error, so that the original error in the output thread is the one that gets raised + if rom_name: + new_name = base64.b64encode(bytes(self.rom_name)).decode() + payload = multidata["connect_names"][self.world.player_name[self.player]] + multidata["connect_names"][new_name] = payload + del (multidata["connect_names"][self.world.player_name[self.player]]) + + + def fill_slot_data(self): + slot_data = {} + return slot_data + + def collect(self, state: CollectionState, item: Item) -> bool: + state.smbm[item.player].addItem(item.type) + if item.advancement: + state.prog_items[item.name, item.player] += 1 + return True # indicate that a logical state change has occured + return False + + def create_item(self, name: str) -> Item: + item = next(x for x in ItemManager.Items.values() if x.Name == name) + return SMItem(item.Name, True, item.Type, self.item_name_to_id[item.Name], player = self.player) + + def pre_fill(self): + if (self.variaRando.args.morphPlacement == "early") and next((item for item in self.world.itempool if item.player == self.player and item.name == "Morph Ball"), False): + viable = [] + for location in self.world.get_locations(): + if location.player == self.player \ + and location.item is None \ + and location.can_reach(self.world.state): + viable.append(location) + self.world.random.shuffle(viable) + key = self.world.create_item("Morph Ball", self.player) + loc = viable.pop() + loc.place_locked_item(key) + self.world.itempool[:] = [item for item in self.world.itempool if + item.player != self.player or + item.name != "Morph Ball"] + + def post_fill(self): + # increase maxDifficulty if only bosses is too difficult to beat game + new_state = CollectionState(self.world) + for item in self.world.itempool: + if item.player == self.player: + new_state.collect(item, True) + new_state.sweep_for_events() + if (any(not self.world.get_location(bossLoc, self.player).can_reach(new_state) for bossLoc in self.locked_items)): + if (self.variaRando.randoExec.setup.services.onlyBossesLeft(self.variaRando.randoExec.setup.startAP, self.variaRando.randoExec.setup.container)): + self.world.state.smbm[self.player].maxDiff = infinity + + @classmethod + def stage_fill_hook(cls, world, progitempool, nonexcludeditempool, localrestitempool, nonlocalrestitempool, + restitempool, fill_locations): + if world.get_game_players("Super Metroid"): + progitempool.sort( + key=lambda item: 1 if (item.name == 'Morph Ball') else 0) + +def create_locations(self, player: int): + for name, id in locations_lookup_name_to_id.items(): + self.locations[name] = SMLocation(player, name, id) + +def create_region(self, world: MultiWorld, player: int, name: str, locations=None, exits=None): + ret = Region(name, RegionType.LightWorld, name, player) + ret.world = world + if locations: + for loc in locations: + location = self.locations[loc] + location.parent_region = ret + ret.locations.append(location) + if exits: + for exit in exits: + ret.exits.append(Entrance(player, exit, ret)) + return ret + + +class SMLocation(Location): + game: str = "Super Metroid" + + def __init__(self, player: int, name: str, address=None, parent=None): + super(SMLocation, self).__init__(player, name, address, parent) + + def can_fill(self, state: CollectionState, item: Item, check_access=True) -> bool: + return self.always_allow(state, item) or (self.item_rule(item) and (not check_access or self.can_reach(state))) + + +class SMItem(Item): + game = "Super Metroid" + + def __init__(self, name, advancement, type, code, player: int = None): + super(SMItem, self).__init__(name, advancement, code, player) + self.type = type diff --git a/worlds/sm/data/custom_sprite/off_world_item.bin b/worlds/sm/data/custom_sprite/off_world_item.bin new file mode 100644 index 00000000..92e4a1d0 Binary files /dev/null and b/worlds/sm/data/custom_sprite/off_world_item.bin differ diff --git a/worlds/sm/data/custom_sprite/off_world_prog_item.bin b/worlds/sm/data/custom_sprite/off_world_prog_item.bin new file mode 100644 index 00000000..3af0d350 Binary files /dev/null and b/worlds/sm/data/custom_sprite/off_world_prog_item.bin differ diff --git a/worlds/sm/variaRandomizer/LICENSE b/worlds/sm/variaRandomizer/LICENSE new file mode 100644 index 00000000..9cecc1d4 --- /dev/null +++ b/worlds/sm/variaRandomizer/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/worlds/sm/variaRandomizer/__init__.py b/worlds/sm/variaRandomizer/__init__.py new file mode 100644 index 00000000..6aed1dfd --- /dev/null +++ b/worlds/sm/variaRandomizer/__init__.py @@ -0,0 +1,3 @@ +import sys +import os +sys.path.append(os.path.dirname(os.path.abspath(__file__))) \ No newline at end of file diff --git a/worlds/sm/variaRandomizer/graph/__init__.py b/worlds/sm/variaRandomizer/graph/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/worlds/sm/variaRandomizer/graph/graph.py b/worlds/sm/variaRandomizer/graph/graph.py new file mode 100644 index 00000000..0f88678f --- /dev/null +++ b/worlds/sm/variaRandomizer/graph/graph.py @@ -0,0 +1,413 @@ +import copy, logging +from operator import attrgetter +import utils.log +from logic.smbool import SMBool, smboolFalse +from utils.parameters import infinity +from logic.helpers import Bosses + +class Path(object): + __slots__ = ( 'path', 'pdiff', 'distance' ) + + def __init__(self, path, pdiff, distance): + self.path = path + self.pdiff = pdiff + self.distance = distance + +class AccessPoint(object): + # name : AccessPoint name + # graphArea : graph area the node is located in + # transitions : intra-area transitions + # traverse: traverse function, will be wand to the added transitions + # exitInfo : dict carrying vanilla door information : 'DoorPtr': door address, 'direction', 'cap', 'screen', 'bitFlag', 'distanceToSpawn', 'doorAsmPtr' : door properties + # entryInfo : dict carrying forced samus X/Y position with keys 'SamusX' and 'SamusY'. + # (to be updated after reading vanillaTransitions and gather entry info from matching exit door) + # roomInfo : dict with 'RoomPtr' : room address, 'area' + # shortName : short name for the credits + # internal : if true, shall not be used for connecting areas + def __init__(self, name, graphArea, transitions, + traverse=lambda sm: SMBool(True), + exitInfo=None, entryInfo=None, roomInfo=None, + internal=False, boss=False, escape=False, + start=None, + dotOrientation='w'): + self.Name = name + self.GraphArea = graphArea + self.ExitInfo = exitInfo + self.EntryInfo = entryInfo + self.RoomInfo = roomInfo + self.Internal = internal + self.Boss = boss + self.Escape = escape + self.Start = start + self.DotOrientation = dotOrientation + self.intraTransitions = self.sortTransitions(transitions) + self.transitions = copy.copy(self.intraTransitions) + self.traverse = traverse + self.distance = 0 + # inter-area connection + self.ConnectedTo = None + + def __copy__(self): + exitInfo = copy.deepcopy(self.ExitInfo) if self.ExitInfo is not None else None + entryInfo = copy.deepcopy(self.EntryInfo) if self.EntryInfo is not None else None + roomInfo = copy.deepcopy(self.RoomInfo) if self.RoomInfo is not None else None + start = copy.deepcopy(self.Start) if self.Start is not None else None + # in any case, do not copy connections + return AccessPoint(self.Name, self.GraphArea, self.intraTransitions, self.traverse, + exitInfo, entryInfo, roomInfo, + self.Internal, self.Boss, self.Escape, + start, self.DotOrientation) + + def __str__(self): + return "[" + self.GraphArea + "] " + self.Name + + def __repr__(self): + return self.Name + + def sortTransitions(self, transitions=None): + # sort transitions before the loop in getNewAvailNodes. + # as of python3.7 insertion order is guaranteed in dictionaires. + if transitions is None: + transitions = self.transitions + return { key: transitions[key] for key in sorted(transitions.keys()) } + + # connect to inter-area access point + def connect(self, destName): + self.disconnect() + if self.Internal is False: + self.transitions[destName] = self.traverse + self.ConnectedTo = destName + else: + raise RuntimeError("Cannot add an internal access point as inter-are transition") + self.transitions = self.sortTransitions() + + def disconnect(self): + if self.ConnectedTo is not None: + if self.ConnectedTo not in self.intraTransitions: + del self.transitions[self.ConnectedTo] + else: + self.transitions[self.ConnectedTo] = self.intraTransitions[self.ConnectedTo] + self.ConnectedTo = None + + # tells if this node is to connect areas together + def isArea(self): + return not self.Internal and not self.Boss and not self.Escape + + # used by the solver to get area and boss APs + def isInternal(self): + return self.Internal or self.Escape + + def isLoop(self): + return self.ConnectedTo == self.Name + +class AccessGraph(object): + __slots__ = ( 'log', 'accessPoints', 'InterAreaTransitions', + 'EscapeAttributes', 'apCache', '_useCache', + 'availAccessPoints' ) + + def __init__(self, accessPointList, transitions, dotFile=None): + self.log = utils.log.get('Graph') + self.accessPoints = {} + self.InterAreaTransitions = [] + self.EscapeAttributes = { + 'Timer': None, + 'Animals': None + } + for ap in accessPointList: + self.addAccessPoint(ap) + for srcName, dstName in transitions: + self.addTransition(srcName, dstName) + if dotFile is not None: + self.toDot(dotFile) + self.apCache = {} + self._useCache = False + # store the avail access points to display in vcr + self.availAccessPoints = {} + + def useCache(self, use): + self._useCache = use + if self._useCache: + self.resetCache() + + def resetCache(self): + self.apCache = {} + + def printGraph(self): + if self.log.getEffectiveLevel() == logging.DEBUG: + self.log("Area graph:") + for s, d in self.InterAreaTransitions: + self.log("{} -> {}".format(s.Name, d.Name)) + + def addAccessPoint(self, ap): + ap.distance = 0 + self.accessPoints[ap.Name] = ap + + def toDot(self, dotFile): + colors = ['red', 'blue', 'green', 'yellow', 'skyblue', 'violet', 'orange', + 'lawngreen', 'crimson', 'chocolate', 'turquoise', 'tomato', + 'navyblue', 'darkturquoise', 'green', 'blue', 'maroon', 'magenta', + 'bisque', 'coral', 'chartreuse', 'chocolate', 'cyan'] + with open(dotFile, "w") as f: + f.write("digraph {\n") + f.write('size="30,30!";\n') + f.write('rankdir=LR;\n') + f.write('ranksep=2.2;\n') + f.write('overlap=scale;\n') + f.write('edge [dir="both",arrowhead="box",arrowtail="box",arrowsize=0.5,fontsize=7,style=dotted];\n') + f.write('node [shape="box",fontsize=10];\n') + for area in set([ap.GraphArea for ap in self.accessPoints.values()]): + f.write(area + ";\n") # TODO area long name and color + drawn = [] + i = 0 + for src, dst in self.InterAreaTransitions: + if src.Name in drawn: + continue + f.write('%s:%s -> %s:%s [taillabel="%s",headlabel="%s",color=%s];\n' % (src.GraphArea, src.DotOrientation, dst.GraphArea, dst.DotOrientation, src.Name, dst.Name, colors[i])) + drawn += [src.Name,dst.Name] + i += 1 + f.write("}\n") + + def addTransition(self, srcName, dstName, both=True): + src = self.accessPoints[srcName] + dst = self.accessPoints[dstName] + src.connect(dstName) + self.InterAreaTransitions.append((src, dst)) + if both is True: + self.addTransition(dstName, srcName, False) + + # availNodes: all already available nodes + # nodesToCheck: nodes we have to check transitions for + # smbm: smbm to test logic on. if None, discard logic check, assume we can reach everything + # maxDiff: difficulty limit + # return newly opened access points + def getNewAvailNodes(self, availNodes, nodesToCheck, smbm, maxDiff, item=None): + newAvailNodes = {} + # with python >= 3.6 the insertion order in a dict is keeps when looping on the keys, + # so we no longer have to sort them. + for src in nodesToCheck: + for dstName in src.transitions: + dst = self.accessPoints[dstName] + if dst in availNodes or dst in newAvailNodes: + continue + if smbm is not None: + if self._useCache == True and (src, dst, item) in self.apCache: + diff = self.apCache[(src, dst, item)] + else: + tFunc = src.transitions[dstName] + diff = tFunc(smbm) + if self._useCache == True: + self.apCache[(src, dst, item)] = diff + else: + diff = SMBool(True) + if diff.bool and diff.difficulty <= maxDiff: + if src.GraphArea == dst.GraphArea: + dst.distance = src.distance + 0.01 + else: + dst.distance = src.distance + 1 + newAvailNodes[dst] = { 'difficulty': diff, 'from': src } + + #self.log.debug("{} -> {}: {}".format(src.Name, dstName, diff)) + return newAvailNodes + + # rootNode: starting AccessPoint instance + # smbm: smbm to test logic on. if None, discard logic check, assume we can reach everything + # maxDiff: difficulty limit. + # smbm: if None, discard logic check, assume we can reach everything + # return available AccessPoint list + def getAvailableAccessPoints(self, rootNode, smbm, maxDiff, item=None): + availNodes = { rootNode : { 'difficulty' : SMBool(True, 0), 'from' : None } } + newAvailNodes = availNodes + rootNode.distance = 0 + while len(newAvailNodes) > 0: + newAvailNodes = self.getNewAvailNodes(availNodes, newAvailNodes, smbm, maxDiff, item) + availNodes.update(newAvailNodes) + return availNodes + + # gets path from the root AP used to compute availAps + def getPath(self, dstAp, availAps): + path = [] + root = dstAp + while root != None: + path = [root] + path + root = availAps[root]['from'] + + return path + + def getAvailAPPaths(self, availAccessPoints, locsAPs): + paths = {} + for ap in availAccessPoints: + if ap.Name in locsAPs: + path = self.getPath(ap, availAccessPoints) + pdiff = SMBool.wandmax(*(availAccessPoints[ap]['difficulty'] for ap in path)) + paths[ap.Name] = Path(path, pdiff, len(path)) + return paths + + def getSortedAPs(self, paths, locAccessFrom): + ret = [] + + for apName in locAccessFrom: + path = paths.get(apName, None) + if path is None: + continue + difficulty = paths[apName].pdiff.difficulty + ret.append((difficulty if difficulty != -1 else infinity, path.distance, apName)) + ret.sort() + return [apName for diff, dist, apName in ret] + + # locations: locations to check + # items: collected items + # maxDiff: difficulty limit + # rootNode: starting AccessPoint + # return available locations list, also stores difficulty in locations + def getAvailableLocations(self, locations, smbm, maxDiff, rootNode='Landing Site'): + rootAp = self.accessPoints[rootNode] + self.availAccessPoints = self.getAvailableAccessPoints(rootAp, smbm, maxDiff) + availAreas = set([ap.GraphArea for ap in self.availAccessPoints.keys()]) + availLocs = [] + + # get all the current locations APs first to only compute these paths + locsAPs = set() + for loc in locations: + for ap in loc.AccessFrom: + locsAPs.add(ap) + + # sort availAccessPoints based on difficulty to take easier paths first + availAPPaths = self.getAvailAPPaths(self.availAccessPoints, locsAPs) + + for loc in locations: + if loc.GraphArea not in availAreas: + loc.distance = 30000 + loc.difficulty = smboolFalse + #if loc.Name == "Kraid": + # print("loc: {} locDiff is area nok".format(loc.Name)) + continue + + locAPs = self.getSortedAPs(availAPPaths, loc.AccessFrom) + if len(locAPs) == 0: + loc.distance = 40000 + loc.difficulty = smboolFalse + #if loc.Name == "Kraid": + # print("loc: {} no aps".format(loc.Name)) + continue + + for apName in locAPs: + if apName == None: + loc.distance = 20000 + loc.difficulty = smboolFalse + #if loc.Name == "Kraid": + # print("loc: {} ap is none".format(loc.Name)) + break + + tFunc = loc.AccessFrom[apName] + ap = self.accessPoints[apName] + tdiff = tFunc(smbm) + #if loc.Name == "Kraid": + # print("{} root: {} ap: {}".format(loc.Name, rootNode, apName)) + if tdiff.bool == True and tdiff.difficulty <= maxDiff: + diff = loc.Available(smbm) + if diff.bool == True: + path = availAPPaths[apName].path + #if loc.Name == "Kraid": + # print("{} path: {}".format(loc.Name, [a.Name for a in path])) + pdiff = availAPPaths[apName].pdiff + (allDiff, locDiff) = self.computeLocDiff(tdiff, diff, pdiff) + if allDiff.bool == True and allDiff.difficulty <= maxDiff: + loc.distance = ap.distance + 1 + loc.accessPoint = apName + loc.difficulty = allDiff + loc.path = path + # used only by solver + loc.pathDifficulty = pdiff + loc.locDifficulty = locDiff + availLocs.append(loc) + #if loc.Name == "Kraid": + # print("{} diff: {} tdiff: {} pdiff: {}".format(loc.Name, diff, tdiff, pdiff)) + break + else: + loc.distance = 1000 + tdiff.difficulty + loc.difficulty = smboolFalse + #if loc.Name == "Kraid": + # print("loc: {} allDiff is false".format(loc.Name)) + else: + loc.distance = 1000 + tdiff.difficulty + loc.difficulty = smboolFalse + #if loc.Name == "Kraid": + # print("loc: {} allDiff is false".format(loc.Name)) + else: + loc.distance = 10000 + tdiff.difficulty + loc.difficulty = smboolFalse + #if loc.Name == "Kraid": + # print("loc: {} tdiff is false".format(loc.Name)) + + if loc.difficulty is None: + #if loc.Name == "Kraid": + # print("loc: {} no difficulty in loc".format(loc.Name)) + loc.distance = 100000 + loc.difficulty = smboolFalse + + #if loc.Name == "Kraid": + # print("loc: {}: {}".format(loc.Name, loc)) + + #print("availableLocs: {}".format([loc.Name for loc in availLocs])) + return availLocs + + # test access from an access point to another, given an optional item + def canAccess(self, smbm, srcAccessPointName, destAccessPointName, maxDiff, item=None): + if item is not None: + smbm.addItem(item) + #print("canAccess: item: {}, src: {}, dest: {}".format(item, srcAccessPointName, destAccessPointName)) + destAccessPoint = self.accessPoints[destAccessPointName] + srcAccessPoint = self.accessPoints[srcAccessPointName] + availAccessPoints = self.getAvailableAccessPoints(srcAccessPoint, smbm, maxDiff, item) + can = destAccessPoint in availAccessPoints + # if not can: + # self.log.debug("canAccess KO: avail = {}".format([ap.Name for ap in availAccessPoints.keys()])) + if item is not None: + smbm.removeItem(item) + #print("canAccess: {}".format(can)) + return can + + # returns a list of AccessPoint instances from srcAccessPointName to destAccessPointName + # (not including source ap) + # or None if no possible path + def accessPath(self, smbm, srcAccessPointName, destAccessPointName, maxDiff): + destAccessPoint = self.accessPoints[destAccessPointName] + srcAccessPoint = self.accessPoints[srcAccessPointName] + availAccessPoints = self.getAvailableAccessPoints(srcAccessPoint, smbm, maxDiff) + if destAccessPoint not in availAccessPoints: + return None + return self.getPath(destAccessPoint, availAccessPoints) + + # gives theoretically accessible APs in the graph (no logic check) + def getAccessibleAccessPoints(self, rootNode='Landing Site'): + rootAp = self.accessPoints[rootNode] + inBossChk = lambda ap: ap.Boss and ap.Name.endswith("In") + allAreas = {dst.GraphArea for (src, dst) in self.InterAreaTransitions if not inBossChk(dst) and not dst.isLoop()} + self.log.debug("allAreas="+str(allAreas)) + nonBossAPs = [ap for ap in self.getAvailableAccessPoints(rootAp, None, 0) if ap.GraphArea in allAreas] + bossesAPs = [self.accessPoints[boss+'RoomIn'] for boss in Bosses.Golden4()] + [self.accessPoints['Draygon Room Bottom']] + return nonBossAPs + bossesAPs + + # gives theoretically accessible locations within a base list + # returns locations with accessible GraphArea in this graph (no logic considered) + def getAccessibleLocations(self, locations, rootNode='Landing Site'): + availAccessPoints = self.getAccessibleAccessPoints(rootNode) + self.log.debug("availAccessPoints="+str([ap.Name for ap in availAccessPoints])) + return [loc for loc in locations if any(ap.Name in loc.AccessFrom for ap in availAccessPoints)] + +class AccessGraphSolver(AccessGraph): + def computeLocDiff(self, tdiff, diff, pdiff): + # tdiff: difficulty from the location's access point to the location's room + # diff: difficulty to reach the item in the location's room + # pdiff: difficulty of the path from the current access point to the location's access point + # in output we need the global difficulty but we also need to separate pdiff and (tdiff + diff) + + locDiff = SMBool.wandmax(tdiff, diff) + allDiff = SMBool.wandmax(locDiff, pdiff) + + return (allDiff, locDiff) + +class AccessGraphRando(AccessGraph): + def computeLocDiff(self, tdiff, diff, pdiff): + allDiff = SMBool.wandmax(tdiff, diff, pdiff) + return (allDiff, None) diff --git a/worlds/sm/variaRandomizer/graph/graph_utils.py b/worlds/sm/variaRandomizer/graph/graph_utils.py new file mode 100644 index 00000000..75d494a6 --- /dev/null +++ b/worlds/sm/variaRandomizer/graph/graph_utils.py @@ -0,0 +1,575 @@ +import copy +import random +from logic.logic import Logic +from utils.parameters import Knows +from graph.location import locationsDict +from rom.rom import snes_to_pc +import utils.log + +# order expected by ROM patches +graphAreas = [ + "Ceres", + "Crateria", + "GreenPinkBrinstar", + "RedBrinstar", + "WreckedShip", + "Kraid", + "Norfair", + "Crocomire", + "LowerNorfair", + "WestMaridia", + "EastMaridia", + "Tourian" +] + +vanillaTransitions = [ + ('Lower Mushrooms Left', 'Green Brinstar Elevator'), + ('Morph Ball Room Left', 'Green Hill Zone Top Right'), + ('Moat Right', 'West Ocean Left'), + ('Keyhunter Room Bottom', 'Red Brinstar Elevator'), + ('Noob Bridge Right', 'Red Tower Top Left'), + ('Crab Maze Left', 'Le Coude Right'), + ('Kronic Boost Room Bottom Left', 'Lava Dive Right'), + ('Crocomire Speedway Bottom', 'Crocomire Room Top'), + ('Three Muskateers Room Left', 'Single Chamber Top Right'), + ('Warehouse Entrance Left', 'East Tunnel Right'), + ('East Tunnel Top Right', 'Crab Hole Bottom Left'), + ('Caterpillar Room Top Right', 'Red Fish Room Left'), + ('Glass Tunnel Top', 'Main Street Bottom'), + ('Green Pirates Shaft Bottom Right', 'Golden Four'), + ('Warehouse Entrance Right', 'Warehouse Zeela Room Left'), + ('Crab Shaft Right', 'Aqueduct Top Left') +] + +vanillaBossesTransitions = [ + ('KraidRoomOut', 'KraidRoomIn'), + ('PhantoonRoomOut', 'PhantoonRoomIn'), + ('DraygonRoomOut', 'DraygonRoomIn'), + ('RidleyRoomOut', 'RidleyRoomIn') +] + +# vanilla escape transition in first position +vanillaEscapeTransitions = [ + ('Tourian Escape Room 4 Top Right', 'Climb Bottom Left'), + ('Brinstar Pre-Map Room Right', 'Green Brinstar Main Shaft Top Left'), + ('Wrecked Ship Map Room', 'Basement Left'), + ('Norfair Map Room', 'Business Center Mid Left'), + ('Maridia Map Room', 'Crab Hole Bottom Right') +] + +vanillaEscapeAnimalsTransitions = [ + ('Flyway Right 0', 'Bomb Torizo Room Left'), + ('Flyway Right 1', 'Bomb Torizo Room Left'), + ('Flyway Right 2', 'Bomb Torizo Room Left'), + ('Flyway Right 3', 'Bomb Torizo Room Left'), + ('Bomb Torizo Room Left Animals', 'Flyway Right') +] + +escapeSource = 'Tourian Escape Room 4 Top Right' +escapeTargets = ['Green Brinstar Main Shaft Top Left', 'Basement Left', 'Business Center Mid Left', 'Crab Hole Bottom Right'] + +locIdsByAreaAddresses = { + "Ceres": snes_to_pc(0xA1F568), + "Crateria": snes_to_pc(0xA1F569), + "GreenPinkBrinstar": snes_to_pc(0xA1F57B), + "RedBrinstar": snes_to_pc(0xA1F58C), + "WreckedShip": snes_to_pc(0xA1F592), + "Kraid": snes_to_pc(0xA1F59E), + "Norfair": snes_to_pc(0xA1F5A2), + "Crocomire": snes_to_pc(0xA1F5B2), + "LowerNorfair": snes_to_pc(0xA1F5B8), + "WestMaridia": snes_to_pc(0xA1F5C3), + "EastMaridia": snes_to_pc(0xA1F5CB), + "Tourian": snes_to_pc(0xA1F5D7) +} + +def getAccessPoint(apName, apList=None): + if apList is None: + apList = Logic.accessPoints + return next(ap for ap in apList if ap.Name == apName) + +class GraphUtils: + log = utils.log.get('GraphUtils') + + def getStartAccessPointNames(): + return [ap.Name for ap in Logic.accessPoints if ap.Start is not None] + + def getStartAccessPointNamesCategory(): + ret = {'regular': [], 'custom': [], 'area': []} + for ap in Logic.accessPoints: + if ap.Start == None: + continue + elif 'areaMode' in ap.Start and ap.Start['areaMode'] == True: + ret['area'].append(ap.Name) + elif GraphUtils.isStandardStart(ap.Name): + ret['regular'].append(ap.Name) + else: + ret['custom'].append(ap.Name) + return ret + + def isStandardStart(startApName): + return startApName == 'Ceres' or startApName == 'Landing Site' + + def getPossibleStartAPs(areaMode, maxDiff, morphPlacement, player): + ret = [] + refused = {} + allStartAPs = GraphUtils.getStartAccessPointNames() + for apName in allStartAPs: + start = getAccessPoint(apName).Start + ok = True + cause = "" + if 'knows' in start: + for k in start['knows']: + if not Knows.knowsDict[player].knows(k, maxDiff): + ok = False + cause += Knows.desc[k]['display']+" is not known. " + break + if 'areaMode' in start and start['areaMode'] != areaMode: + ok = False + cause += "Start location available only with area randomization enabled. " + if 'forcedEarlyMorph' in start and start['forcedEarlyMorph'] == True and morphPlacement == 'late': + ok = False + cause += "Start location unavailable with late morph placement. " + if ok: + ret.append(apName) + else: + refused[apName] = cause + return ret, refused + + def updateLocClassesStart(startGraphArea, split, possibleMajLocs, preserveMajLocs, nLocs): + locs = locationsDict + preserveMajLocs = [locs[locName] for locName in preserveMajLocs if locs[locName].isClass(split)] + possLocs = [locs[locName] for locName in possibleMajLocs][:nLocs] + GraphUtils.log.debug("possLocs="+str([loc.Name for loc in possLocs])) + candidates = [loc for loc in locs.values() if loc.GraphArea == startGraphArea and loc.isClass(split) and loc not in preserveMajLocs] + remLocs = [loc for loc in locs.values() if loc not in possLocs and loc not in candidates and loc.isClass(split)] + newLocs = [] + while len(newLocs) < nLocs: + if len(candidates) == 0: + candidates = remLocs + loc = possLocs.pop(random.randint(0,len(possLocs)-1)) + newLocs.append(loc) + loc.setClass([split]) + if not loc in preserveMajLocs: + GraphUtils.log.debug("newMajor="+loc.Name) + loc = candidates.pop(random.randint(0,len(candidates)-1)) + loc.setClass(["Minor"]) + GraphUtils.log.debug("replaced="+loc.Name) + + def getGraphPatches(startApName): + ap = getAccessPoint(startApName) + return ap.Start['patches'] if 'patches' in ap.Start else [] + + def createBossesTransitions(): + transitions = vanillaBossesTransitions + def isVanilla(): + for t in vanillaBossesTransitions: + if t not in transitions: + return False + return True + while isVanilla(): + transitions = [] + srcs = [] + dsts = [] + for (src,dst) in vanillaBossesTransitions: + srcs.append(src) + dsts.append(dst) + while len(srcs) > 0: + src = srcs.pop(random.randint(0,len(srcs)-1)) + dst = dsts.pop(random.randint(0,len(dsts)-1)) + transitions.append((src,dst)) + return transitions + + def createAreaTransitions(lightAreaRando=False): + if lightAreaRando: + return GraphUtils.createLightAreaTransitions() + else: + return GraphUtils.createRegularAreaTransitions() + + def createRegularAreaTransitions(apList=None, apPred=None): + if apList is None: + apList = Logic.accessPoints + if apPred is None: + apPred = lambda ap: ap.isArea() + tFrom = [] + tTo = [] + apNames = [ap.Name for ap in apList if apPred(ap) == True] + transitions = [] + + def findTo(trFrom): + ap = getAccessPoint(trFrom, apList) + fromArea = ap.GraphArea + targets = [apName for apName in apNames if apName not in tTo and getAccessPoint(apName, apList).GraphArea != fromArea] + if len(targets) == 0: # fallback if no area transition is found + targets = [apName for apName in apNames if apName != ap.Name] + if len(targets) == 0: # extreme fallback: loop on itself + targets = [ap.Name] + return random.choice(targets) + + def addTransition(src, dst): + tFrom.append(src) + tTo.append(dst) + + while len(apNames) > 0: + sources = [apName for apName in apNames if apName not in tFrom] + src = random.choice(sources) + dst = findTo(src) + transitions.append((src, dst)) + addTransition(src, dst) + addTransition(dst, src) + toRemove = [apName for apName in apNames if apName in tFrom and apName in tTo] + for apName in toRemove: + apNames.remove(apName) + return transitions + + def getAPs(apPredicate, apList=None): + if apList is None: + apList = Logic.accessPoints + return [ap for ap in apList if apPredicate(ap) == True] + + def loopUnusedTransitions(transitions, apList=None): + if apList is None: + apList = Logic.accessPoints + usedAPs = set() + for (src,dst) in transitions: + usedAPs.add(getAccessPoint(src, apList)) + usedAPs.add(getAccessPoint(dst, apList)) + unusedAPs = [ap for ap in apList if not ap.isInternal() and ap not in usedAPs] + for ap in unusedAPs: + transitions.append((ap.Name, ap.Name)) + + def createMinimizerTransitions(startApName, locLimit): + if startApName == 'Ceres': + startApName = 'Landing Site' + startAp = getAccessPoint(startApName) + def getNLocs(locsPredicate, locList=None): + if locList is None: + locList = Logic.locations + # leave out bosses and count post boss locs systematically + return len([loc for loc in locList if locsPredicate(loc) == True and not loc.SolveArea.endswith(" Boss") and not loc.isBoss()]) + availAreas = list(sorted({ap.GraphArea for ap in Logic.accessPoints if ap.GraphArea != startAp.GraphArea and getNLocs(lambda loc: loc.GraphArea == ap.GraphArea) > 0})) + areas = [startAp.GraphArea] + GraphUtils.log.debug("availAreas: {}".format(availAreas)) + GraphUtils.log.debug("areas: {}".format(areas)) + inBossCheck = lambda ap: ap.Boss and ap.Name.endswith("In") + nLocs = 0 + transitions = [] + usedAPs = [] + trLimit = 5 + locLimit -= 3 # 3 "post boss" locs will always be available, and are filtered out in getNLocs + def openTransitions(): + nonlocal areas, inBossCheck, usedAPs + return GraphUtils.getAPs(lambda ap: ap.GraphArea in areas and not ap.isInternal() and not inBossCheck(ap) and not ap in usedAPs) + while nLocs < locLimit or len(openTransitions()) < trLimit: + GraphUtils.log.debug("openTransitions="+str([ap.Name for ap in openTransitions()])) + fromAreas = availAreas + if nLocs >= locLimit: + GraphUtils.log.debug("not enough open transitions") + # we just need transitions, avoid adding a huge area + fromAreas = [] + n = trLimit - len(openTransitions()) + while len(fromAreas) == 0: + fromAreas = [area for area in availAreas if len(GraphUtils.getAPs(lambda ap: not ap.isInternal())) > n] + n -= 1 + minLocs = min([getNLocs(lambda loc: loc.GraphArea == area) for area in fromAreas]) + fromAreas = [area for area in fromAreas if getNLocs(lambda loc: loc.GraphArea == area) == minLocs] + elif len(openTransitions()) <= 1: # dont' get stuck by adding dead ends + fromAreas = [area for area in fromAreas if len(GraphUtils.getAPs(lambda ap: ap.GraphArea == area and not ap.isInternal())) > 1] + nextArea = random.choice(fromAreas) + GraphUtils.log.debug("nextArea="+str(nextArea)) + apCheck = lambda ap: not ap.isInternal() and not inBossCheck(ap) and ap not in usedAPs + possibleSources = GraphUtils.getAPs(lambda ap: ap.GraphArea in areas and apCheck(ap)) + possibleTargets = GraphUtils.getAPs(lambda ap: ap.GraphArea == nextArea and apCheck(ap)) + src = random.choice(possibleSources) + dst = random.choice(possibleTargets) + usedAPs += [src,dst] + GraphUtils.log.debug("add transition: (src: {}, dst: {})".format(src.Name, dst.Name)) + transitions.append((src.Name,dst.Name)) + availAreas.remove(nextArea) + areas.append(nextArea) + GraphUtils.log.debug("areas: {}".format(areas)) + nLocs = getNLocs(lambda loc:loc.GraphArea in areas) + GraphUtils.log.debug("nLocs: {}".format(nLocs)) + # we picked the areas, add transitions (bosses and tourian first) + sourceAPs = openTransitions() + random.shuffle(sourceAPs) + targetAPs = GraphUtils.getAPs(lambda ap: (inBossCheck(ap) or ap.Name == "Golden Four") and not ap in usedAPs) + random.shuffle(targetAPs) + assert len(sourceAPs) >= len(targetAPs), "Minimizer: less source than target APs" + while len(targetAPs) > 0: + transitions.append((sourceAPs.pop().Name, targetAPs.pop().Name)) + transitions += GraphUtils.createRegularAreaTransitions(sourceAPs, lambda ap: not ap.isInternal()) + GraphUtils.log.debug("FINAL MINIMIZER transitions: {}".format(transitions)) + GraphUtils.loopUnusedTransitions(transitions) + GraphUtils.log.debug("FINAL MINIMIZER nLocs: "+str(nLocs+3)) + GraphUtils.log.debug("FINAL MINIMIZER areas: "+str(areas)) + return transitions + + def createLightAreaTransitions(): + # group APs by area + aps = {} + totalCount = 0 + for ap in Logic.accessPoints: + if not ap.isArea(): + continue + if not ap.GraphArea in aps: + aps[ap.GraphArea] = {'totalCount': 0, 'transCount': {}, 'apNames': []} + aps[ap.GraphArea]['apNames'].append(ap.Name) + # count number of vanilla transitions between each area + for (srcName, destName) in vanillaTransitions: + srcAP = getAccessPoint(srcName) + destAP = getAccessPoint(destName) + aps[srcAP.GraphArea]['transCount'][destAP.GraphArea] = aps[srcAP.GraphArea]['transCount'].get(destAP.GraphArea, 0) + 1 + aps[srcAP.GraphArea]['totalCount'] += 1 + aps[destAP.GraphArea]['transCount'][srcAP.GraphArea] = aps[destAP.GraphArea]['transCount'].get(srcAP.GraphArea, 0) + 1 + aps[destAP.GraphArea]['totalCount'] += 1 + totalCount += 1 + + transitions = [] + while totalCount > 0: + # choose transition + srcArea = random.choice(list(aps.keys())) + srcName = random.choice(aps[srcArea]['apNames']) + src = getAccessPoint(srcName) + destArea = random.choice(list(aps[src.GraphArea]['transCount'].keys())) + destName = random.choice(aps[destArea]['apNames']) + transitions.append((srcName, destName)) + + # update counts + totalCount -= 1 + aps[srcArea]['totalCount'] -= 1 + aps[destArea]['totalCount'] -= 1 + aps[srcArea]['transCount'][destArea] -= 1 + if aps[srcArea]['transCount'][destArea] == 0: + del aps[srcArea]['transCount'][destArea] + aps[destArea]['transCount'][srcArea] -= 1 + if aps[destArea]['transCount'][srcArea] == 0: + del aps[destArea]['transCount'][srcArea] + aps[srcArea]['apNames'].remove(srcName) + aps[destArea]['apNames'].remove(destName) + + if aps[srcArea]['totalCount'] == 0: + del aps[srcArea] + if aps[destArea]['totalCount'] == 0: + del aps[destArea] + + return transitions + + def getVanillaExit(apName): + allVanillaTransitions = vanillaTransitions + vanillaBossesTransitions + vanillaEscapeTransitions + for (src,dst) in allVanillaTransitions: + if apName == src: + return dst + if apName == dst: + return src + return None + + def isEscapeAnimals(apName): + return 'Flyway Right' in apName or 'Bomb Torizo Room Left' in apName + + # gets dict like + # (RoomPtr, (vanilla entry screen X, vanilla entry screen Y)): AP + def getRooms(): + rooms = {} + for ap in Logic.accessPoints: + if ap.Internal == True: + continue + # special ap for random escape animals surprise + if GraphUtils.isEscapeAnimals(ap.Name): + continue + + roomPtr = ap.RoomInfo['RoomPtr'] + + vanillaExitName = GraphUtils.getVanillaExit(ap.Name) + # special ap for random escape animals surprise + if GraphUtils.isEscapeAnimals(vanillaExitName): + continue + + connAP = getAccessPoint(vanillaExitName) + entryInfo = connAP.ExitInfo + rooms[(roomPtr, entryInfo['screen'], entryInfo['direction'])] = ap + rooms[(roomPtr, entryInfo['screen'], (ap.EntryInfo['SamusX'], ap.EntryInfo['SamusY']))] = ap + # for boss rando with incompatible ridley transition, also register this one + if ap.Name == 'RidleyRoomIn': + rooms[(roomPtr, (0x0, 0x1), 0x5)] = ap + rooms[(roomPtr, (0x0, 0x1), (0xbf, 0x198))] = ap + + return rooms + + def escapeAnimalsTransitions(graph, possibleTargets, firstEscape): + n = len(possibleTargets) + assert (n < 4 and firstEscape is not None) or (n <= 4 and firstEscape is None), "Invalid possibleTargets list: " + str(possibleTargets) + # first get our list of 4 entries for escape patch + if n >= 1: + # get actual animals: pick one of the remaining targets + animalsAccess = possibleTargets.pop() + graph.EscapeAttributes['Animals'] = animalsAccess + # we now have at most 3 targets left, fill up to fill cycling 4 targets for animals suprise + possibleTargets.append('Climb Bottom Left') + if firstEscape is not None: + possibleTargets.append(firstEscape) + poss = possibleTargets[:] + while len(possibleTargets) < 4: + possibleTargets.append(poss.pop(random.randint(0, len(poss)-1))) + else: + # failsafe: if not enough targets left, abort and do vanilla animals + animalsAccess = 'Flyway Right' + possibleTargets = ['Bomb Torizo Room Left'] * 4 + GraphUtils.log.debug("escapeAnimalsTransitions. animalsAccess="+animalsAccess) + assert len(possibleTargets) == 4, "Invalid possibleTargets list: " + str(possibleTargets) + # actually add the 4 connections for successive escapes challenge + basePtr = 0xADAC + btDoor = getAccessPoint('Flyway Right') + for i in range(len(possibleTargets)): + ap = copy.copy(btDoor) + ap.Name += " " + str(i) + ap.ExitInfo['DoorPtr'] = basePtr + i*24 + graph.addAccessPoint(ap) + target = possibleTargets[i] + graph.addTransition(ap.Name, target) + # add the connection for animals access + bt = getAccessPoint('Bomb Torizo Room Left') + btCpy = copy.copy(bt) + btCpy.Name += " Animals" + btCpy.ExitInfo['DoorPtr'] = 0xAE00 + graph.addAccessPoint(btCpy) + graph.addTransition(animalsAccess, btCpy.Name) + + def isHorizontal(dir): + # up: 0x3, 0x7 + # down: 0x2, 0x6 + # left: 0x1, 0x5 + # right: 0x0, 0x4 + return dir in [0x1, 0x5, 0x0, 0x4] + + def removeCap(dir): + if dir < 4: + return dir + return dir - 4 + + def getDirection(src, dst): + exitDir = src.ExitInfo['direction'] + entryDir = dst.EntryInfo['direction'] + # compatible transition + if exitDir == entryDir: + return exitDir + # if incompatible but horizontal we keep entry dir (looks more natural) + if GraphUtils.isHorizontal(exitDir) and GraphUtils.isHorizontal(entryDir): + return entryDir + # otherwise keep exit direction and remove cap + return GraphUtils.removeCap(exitDir) + + def getBitFlag(srcArea, dstArea, origFlag): + flags = origFlag + if srcArea == dstArea: + flags &= 0xBF + else: + flags |= 0x40 + return flags + + def getDoorConnections(graph, areas=True, bosses=False, + escape=True, escapeAnimals=True): + transitions = [] + if areas: + transitions += vanillaTransitions + if bosses: + transitions += vanillaBossesTransitions + if escape: + transitions += vanillaEscapeTransitions + if escapeAnimals: + transitions += vanillaEscapeAnimalsTransitions + for srcName, dstName in transitions: + src = graph.accessPoints[srcName] + dst = graph.accessPoints[dstName] + dst.EntryInfo.update(src.ExitInfo) + src.EntryInfo.update(dst.ExitInfo) + connections = [] + for src, dst in graph.InterAreaTransitions: + if not (escape and src.Escape and dst.Escape): + # area only + if not bosses and src.Boss: + continue + # boss only + if not areas and not src.Boss: + continue + # no random escape + if not escape and src.Escape: + continue + + conn = {} + conn['ID'] = str(src) + ' -> ' + str(dst) + # remove duplicates (loop transitions) + if any(c['ID'] == conn['ID'] for c in connections): + continue +# print(conn['ID']) + # where to write + conn['DoorPtr'] = src.ExitInfo['DoorPtr'] + # door properties + conn['RoomPtr'] = dst.RoomInfo['RoomPtr'] + conn['doorAsmPtr'] = dst.EntryInfo['doorAsmPtr'] + if 'exitAsmPtr' in src.ExitInfo: + conn['exitAsmPtr'] = src.ExitInfo['exitAsmPtr'] + conn['direction'] = GraphUtils.getDirection(src, dst) + conn['bitFlag'] = GraphUtils.getBitFlag(src.RoomInfo['area'], dst.RoomInfo['area'], + dst.EntryInfo['bitFlag']) + conn['cap'] = dst.EntryInfo['cap'] + conn['screen'] = dst.EntryInfo['screen'] + if conn['direction'] != src.ExitInfo['direction']: # incompatible transition + conn['distanceToSpawn'] = 0 + conn['SamusX'] = dst.EntryInfo['SamusX'] + conn['SamusY'] = dst.EntryInfo['SamusY'] + if dst.Name == 'RidleyRoomIn': # special case: spawn samus on ridley platform + conn['screen'] = (0x0, 0x1) + else: + conn['distanceToSpawn'] = dst.EntryInfo['distanceToSpawn'] + if 'song' in dst.EntryInfo: + conn['song'] = dst.EntryInfo['song'] + conn['songs'] = dst.RoomInfo['songs'] + connections.append(conn) + return connections + + def getDoorsPtrs2Aps(): + ret = {} + for ap in Logic.accessPoints: + if ap.Internal == True: + continue + ret[ap.ExitInfo["DoorPtr"]] = ap.Name + return ret + + def getAps2DoorsPtrs(): + ret = {} + for ap in Logic.accessPoints: + if ap.Internal == True: + continue + ret[ap.Name] = ap.ExitInfo["DoorPtr"] + return ret + + def getTransitions(addresses): + # build address -> name dict + doorsPtrs = GraphUtils.getDoorsPtrs2Aps() + + transitions = [] + # (src.ExitInfo['DoorPtr'], dst.ExitInfo['DoorPtr']) + for (srcDoorPtr, destDoorPtr) in addresses: + transitions.append((doorsPtrs[srcDoorPtr], doorsPtrs[destDoorPtr])) + + return transitions + + def hasMixedTransitions(areaTransitions, bossTransitions): + vanillaAPs = [] + for (src, dest) in vanillaTransitions: + vanillaAPs += [src, dest] + + vanillaBossesAPs = [] + for (src, dest) in vanillaBossesTransitions: + vanillaBossesAPs += [src, dest] + + for (src, dest) in areaTransitions: + if src in vanillaBossesAPs or dest in vanillaBossesAPs: + return True + + for (src, dest) in bossTransitions: + if src in vanillaAPs or dest in vanillaAPs: + return True + + return False diff --git a/worlds/sm/variaRandomizer/graph/location.py b/worlds/sm/variaRandomizer/graph/location.py new file mode 100644 index 00000000..f60158c1 --- /dev/null +++ b/worlds/sm/variaRandomizer/graph/location.py @@ -0,0 +1,1532 @@ +from utils.parameters import infinity +import copy + +class Location: + graph_slots = ( + 'distance', 'accessPoint', 'difficulty', 'path', + 'pathDifficulty', 'locDifficulty' ) + + rando_slots = ( + 'restricted', ) + + solver_slots = ( + 'itemName', 'comeBack', 'areaWeight' ) + + __slots__ = graph_slots + rando_slots + solver_slots + + def __init__( + self, distance=None, accessPoint=None, + difficulty=None, path=None, pathDifficulty=None, + locDifficulty=None, restricted=None, itemName=None, + itemType=None, comeBack=None, areaWeight=None): + self.distance = distance + self.accessPoint = accessPoint + self.difficulty = difficulty + self.path = path + self.pathDifficulty = pathDifficulty + self.locDifficulty = locDifficulty + self.restricted = restricted + self.itemName = itemName + self.itemType = itemType + self.comeBack = comeBack + self.areaWeight = areaWeight + + def isMajor(self): + return self._isMajor + + def isChozo(self): + return self._isChozo + + def isMinor(self): + return self._isMinor + + def isBoss(self): + return self._isBoss + + def isScavenger(self): + return self._isScavenger + + def isClass(self, _class): + return _class in self.Class + + def setClass(self, _class): + self.Class = _class + self._isChozo = 'Chozo' in _class + self._isMajor = 'Major' in _class + self._isMinor = 'Minor' in _class + self._isBoss = 'Boss' in _class + self._isScavenger = 'Scavenger' in _class + + def evalPostAvailable(self, smbm): + if self.difficulty.bool == True and self.PostAvailable is not None: + smbm.addItem(self.itemName) + postAvailable = self.PostAvailable(smbm) + smbm.removeItem(self.itemName) + + self.difficulty = self.difficulty & postAvailable + + def evalComeBack(self, smbm, areaGraph, ap): + if self.difficulty.bool == True: + # check if we can come back to given ap from the location + self.comeBack = areaGraph.canAccess(smbm, self.accessPoint, ap, infinity, self.itemName) + + def json(self): + # to return after plando rando + ret = {'Name': self.Name, 'accessPoint': self.accessPoint} + if self.difficulty is not None: + ret['difficulty'] = self.difficulty.json() + return ret + + def __repr__(self): + return "Location({}: {})".format(self.Name, + '. '.join( + (repr(getattr(self, slot)) for slot in Location.__slots__ if getattr(self, slot) is not None))) + + def __copy__(self): + d = self.difficulty + difficulty = copy.copy(d) if d is not None else None + ret = type(self)( + self.distance, self.accessPoint, difficulty, self.path, + self.pathDifficulty, self.locDifficulty, self.restricted, + self.itemName, self.itemType, self.comeBack, + self.areaWeight) + ret.AccessFrom = self.AccessFrom + ret.Available = self.Available + ret.PostAvailable = self.PostAvailable + ret.setClass(self.Class) + + return ret + + def __eq__(self, other): + return self.Name == other.Name + +def define_location( + Area, GraphArea, SolveArea, Name, Class, CanHidden, Address, Id, + Visibility, Room, VanillaItemType=None, AccessFrom=None, Available=None, PostAvailable=None, HUD=None): + name = Name.replace(' ', '').replace(',', '') + 'Location' + subclass = type(name, (Location,), { + 'Area': Area, + 'GraphArea': GraphArea, + 'SolveArea': SolveArea, + 'Name': Name, + 'Class': Class, + 'CanHidden': CanHidden, + 'Address': Address, + 'Id': Id, + 'Visibility': Visibility, + 'Room': Room, + 'VanillaItemType': VanillaItemType, + 'HUD': HUD, + 'AccessFrom': AccessFrom, + 'Available': Available, + 'PostAvailable': PostAvailable, + '_isMajor': 'Major' in Class, + '_isChozo': 'Chozo' in Class, + '_isMinor': 'Minor' in Class, + '_isBoss': 'Boss' in Class, + '_isScavenger': 'Scavenger' in Class + }) + return subclass() + +# all the items locations with the prerequisites to access them +locationsDict = { +###### MAJORS + "Energy Tank, Gauntlet": +define_location( + Area="Crateria", + GraphArea="Crateria", + SolveArea="Crateria Gauntlet", + Name="Energy Tank, Gauntlet", + Class=["Major", "Chozo"], + CanHidden=False, + Address=0x78264, + Id=0x5, + Visibility="Visible", + Room='Gauntlet Energy Tank Room', +), + "Bomb": +define_location( + Area="Crateria", + GraphArea="Crateria", + SolveArea="Crateria Bombs", + Name="Bomb", + Address=0x78404, + Id=0x7, + Class=["Major", "Chozo", "Scavenger"], + CanHidden=False, + Visibility="Chozo", + Room='Bomb Torizo Room', + VanillaItemType='Bomb', + HUD=1, +), + "Energy Tank, Terminator": +define_location( + Area="Crateria", + GraphArea="Crateria", + SolveArea="Crateria Terminator", + Name="Energy Tank, Terminator", + Class=["Major"], + CanHidden=False, + Address=0x78432, + Id=0x8, + Visibility="Visible", + Room='Terminator Room', +), + "Reserve Tank, Brinstar": +define_location( + Area="Brinstar", + GraphArea="GreenPinkBrinstar", + SolveArea="Green Brinstar Reserve", + Name="Reserve Tank, Brinstar", + Class=["Major", "Chozo"], + CanHidden=False, + Address=0x7852C, + Id=0x11, + Visibility="Chozo", + Room='Brinstar Reserve Tank Room', +), + "Charge Beam": +define_location( + Area="Brinstar", + GraphArea="GreenPinkBrinstar", + SolveArea="Pink Brinstar", + Name="Charge Beam", + Class=["Major", "Chozo", "Scavenger"], + CanHidden=False, + Address=0x78614, + Id=0x17, + Visibility="Chozo", + Room='Big Pink', + VanillaItemType='Charge', + HUD=2, +), + "Morphing Ball": +define_location( + Area="Brinstar", + GraphArea="Crateria", + SolveArea="Blue Brinstar", + Name="Morphing Ball", + Class=["Major", "Chozo", "Scavenger"], + CanHidden=False, + Address=0x786DE, + Id=0x1a, + Visibility="Visible", + Room='Morph Ball Room', + VanillaItemType='Morph', + HUD=0, +), + "Energy Tank, Brinstar Ceiling": +define_location( + Area="Brinstar", + GraphArea="Crateria", + SolveArea="Blue Brinstar", + Name="Energy Tank, Brinstar Ceiling", + Class=["Major"], + CanHidden=False, + Address=0x7879E, + Id=0x1d, + Visibility="Hidden", + Room='Blue Brinstar Energy Tank Room', +), + "Energy Tank, Etecoons": +define_location( + Area="Brinstar", + GraphArea="GreenPinkBrinstar", + SolveArea="Green Brinstar", + Name="Energy Tank, Etecoons", + Class=["Major"], + CanHidden=True, + Address=0x787C2, + Id=0x1e, + Visibility="Visible", + Room='Etecoon Energy Tank Room', +), + "Energy Tank, Waterway": +define_location( + Area="Brinstar", + GraphArea="GreenPinkBrinstar", + SolveArea="Pink Brinstar", + Name="Energy Tank, Waterway", + Class=["Major"], + CanHidden=True, + Address=0x787FA, + Id=0x21, + Visibility="Visible", + Room='Waterway Energy Tank Room', +), + "Energy Tank, Brinstar Gate": +define_location( + Area="Brinstar", + GraphArea="GreenPinkBrinstar", + SolveArea="Pink Brinstar", + Name="Energy Tank, Brinstar Gate", + Class=["Major"], + CanHidden=True, + Address=0x78824, + Id=0x23, + Visibility="Visible", + Room='Hopper Energy Tank Room', +), + "X-Ray Scope": +define_location( + Area="Brinstar", + GraphArea="RedBrinstar", + SolveArea="Red Brinstar", + Name="X-Ray Scope", + Class=["Major", "Chozo", "Scavenger"], + CanHidden=False, + Address=0x78876, + Id=0x26, + Visibility="Chozo", + Room='X-Ray Scope Room', + VanillaItemType='XRayScope', + HUD=10, +), + "Spazer": +define_location( + Area="Brinstar", + GraphArea="RedBrinstar", + SolveArea="Red Brinstar", + Name="Spazer", + Class=["Major", "Chozo", "Scavenger"], + CanHidden=False, + Address=0x7896E, + Id=0x2a, + Visibility="Chozo", + Room='Spazer Room', + VanillaItemType='Spazer', + HUD=3, +), + "Energy Tank, Kraid": +define_location( + Area="Brinstar", + GraphArea="Kraid", + SolveArea="Kraid", + Name="Energy Tank, Kraid", + Class=["Major"], + CanHidden=False, + Address=0x7899C, + Id=0x2b, + Visibility="Hidden", + Room='Warehouse Energy Tank Room', +), + "Kraid": +define_location( + Area="Brinstar", + GraphArea="Kraid", + SolveArea="Kraid Boss", + Name="Kraid", + Class=["Boss"], + CanHidden=False, + Address=0xB055B055, + Id=None, + Visibility="Hidden", + Room='Kraid Room', +), + "Varia Suit": +define_location( + Area="Brinstar", + GraphArea="Kraid", + SolveArea="Kraid Boss", + Name="Varia Suit", + Class=["Major", "Chozo", "Scavenger"], + CanHidden=False, + Address=0x78ACA, + Id=0x30, + Visibility="Chozo", + Room='Varia Suit Room', + VanillaItemType='Varia', + HUD=4, +), + "Ice Beam": +define_location( + Area="Norfair", + GraphArea="Norfair", + SolveArea="Norfair Ice", + Name="Ice Beam", + Class=["Major", "Chozo", "Scavenger"], + CanHidden=False, + Address=0x78B24, + Id=0x32, + Visibility="Chozo", + Room='Ice Beam Room', + VanillaItemType='Ice', + HUD=6, +), + "Energy Tank, Crocomire": +define_location( + Area="Norfair", + GraphArea="Crocomire", + SolveArea="Crocomire", + Name="Energy Tank, Crocomire", + Class=["Major"], + CanHidden=True, + Address=0x78BA4, + Id=0x34, + Visibility="Visible", + Room="Crocomire's Room", +), + "Hi-Jump Boots": +define_location( + Area="Norfair", + GraphArea="Norfair", + SolveArea="Norfair Entrance", + Name="Hi-Jump Boots", + Class=["Major", "Chozo", "Scavenger"], + CanHidden=False, + Address=0x78BAC, + Id=0x35, + Visibility="Chozo", + Room='Hi Jump Boots Room', + VanillaItemType='HiJump', + HUD=5, +), + "Grapple Beam": +define_location( + Area="Norfair", + GraphArea="Crocomire", + SolveArea="Crocomire", + Name="Grapple Beam", + Class=["Major", "Chozo", "Scavenger"], + CanHidden=False, + Address=0x78C36, + Id=0x3c, + Visibility="Chozo", + Room='Grapple Beam Room', + VanillaItemType='Grapple', + HUD=9, +), + "Reserve Tank, Norfair": +define_location( + Area="Norfair", + GraphArea="Norfair", + SolveArea="Bubble Norfair Reserve", + Name="Reserve Tank, Norfair", + Class=["Major"], + CanHidden=False, + Address=0x78C3E, + Id=0x3d, + Visibility="Chozo", + Room='Norfair Reserve Tank Room', +), + "Speed Booster": +define_location( + Area="Norfair", + GraphArea="Norfair", + SolveArea="Bubble Norfair Speed", + Name="Speed Booster", + Class=["Major", "Chozo", "Scavenger"], + CanHidden=False, + Address=0x78C82, + Id=0x42, + Visibility="Chozo", + Room='Speed Booster Room', + VanillaItemType='SpeedBooster', + HUD=7, +), + "Wave Beam": +define_location( + Area="Norfair", + GraphArea="Norfair", + SolveArea="Bubble Norfair Wave", + Name="Wave Beam", + Class=["Major", "Chozo", "Scavenger"], + CanHidden=False, + Address=0x78CCA, + Id=0x44, + Visibility="Chozo", + Room='Wave Beam Room', + VanillaItemType='Wave', + HUD=8, +), + "Ridley": +define_location( + Area="LowerNorfair", + GraphArea="LowerNorfair", + SolveArea="Ridley Boss", + Name="Ridley", + Class=["Boss"], + CanHidden=False, + Address=0xB055B056, + Id=None, + Visibility="Hidden", + Room="Ridley's Room", +), + "Energy Tank, Ridley": +define_location( + Area="LowerNorfair", + GraphArea="LowerNorfair", + SolveArea="Ridley Boss", + Name="Energy Tank, Ridley", + Class=["Major", "Chozo"], + CanHidden=False, + Address=0x79108, + Id=0x4e, + Visibility="Hidden", + Room='Ridley Tank Room', +), + "Screw Attack": +define_location( + Area="LowerNorfair", + GraphArea="LowerNorfair", + SolveArea="Lower Norfair Screw Attack", + Name="Screw Attack", + Class=["Major", "Chozo", "Scavenger"], + CanHidden=False, + Address=0x79110, + Id=0x4f, + Visibility="Chozo", + Room='Screw Attack Room', + VanillaItemType='ScrewAttack', + HUD=15, +), + "Energy Tank, Firefleas": +define_location( + Area="LowerNorfair", + GraphArea="LowerNorfair", + SolveArea="Lower Norfair After Amphitheater", + Name="Energy Tank, Firefleas", + Class=["Major"], + CanHidden=True, + Address=0x79184, + Id=0x50, + Visibility="Visible", + Room='Lower Norfair Fireflea Room', +), + "Reserve Tank, Wrecked Ship": +define_location( + Area="WreckedShip", + GraphArea="WreckedShip", + SolveArea="WreckedShip Gravity", + Name="Reserve Tank, Wrecked Ship", + Class=["Major"], + CanHidden=False, + Address=0x7C2E9, + Id=0x81, + Visibility="Chozo", + Room='Bowling Alley', +), + "Energy Tank, Wrecked Ship": +define_location( + Area="WreckedShip", + GraphArea="WreckedShip", + SolveArea="WreckedShip Back", + Name="Energy Tank, Wrecked Ship", + Class=["Major", "Chozo"], + CanHidden=True, + Address=0x7C337, + Id=0x84, + Visibility="Visible", + Room='Wrecked Ship Energy Tank Room', +), + "Phantoon": +define_location( + Area="WreckedShip", + GraphArea="WreckedShip", + SolveArea="Phantoon Boss", + Name="Phantoon", + Class=["Boss"], + CanHidden=False, + Address=0xB055B057, + Id=None, + Visibility="Hidden", + Room="Phantoon's Room", +), + "Right Super, Wrecked Ship": +define_location( + Area="WreckedShip", + GraphArea="WreckedShip", + SolveArea="WreckedShip Main", + Name="Right Super, Wrecked Ship", + Class=["Major", "Chozo"], + CanHidden=True, + Address=0x7C365, + Id=0x86, + Visibility="Visible", + Room='Wrecked Ship East Super Room', +), + "Gravity Suit": +define_location( + Area="WreckedShip", + GraphArea="WreckedShip", + SolveArea="WreckedShip Gravity", + Name="Gravity Suit", + Class=["Major", "Scavenger"], + CanHidden=False, + Address=0x7C36D, + Id=0x87, + Visibility="Chozo", + Room='Gravity Suit Room', + VanillaItemType='Gravity', + HUD=11, +), + "Energy Tank, Mama turtle": +define_location( + Area="Maridia", + GraphArea="WestMaridia", + SolveArea="Maridia Green", + Name="Energy Tank, Mama turtle", + Class=["Major"], + CanHidden=True, + Address=0x7C47D, + Id=0x8a, + Visibility="Visible", + Room='Mama Turtle Room', +), + "Plasma Beam": +define_location( + Area="Maridia", + GraphArea="EastMaridia", + SolveArea="Maridia Forgotten Highway", + Name="Plasma Beam", + Class=["Major", "Chozo", "Scavenger"], + CanHidden=False, + Address=0x7C559, + Id=0x8f, + Visibility="Chozo", + Room='Plasma Room', + VanillaItemType='Plasma', + HUD=14, +), + "Reserve Tank, Maridia": +define_location( + Area="Maridia", + GraphArea="EastMaridia", + SolveArea="Left Sandpit", + Name="Reserve Tank, Maridia", + Class=["Major"], + CanHidden=False, + Address=0x7C5E3, + Id=0x91, + Visibility="Chozo", + Room='West Sand Hole', +), + "Spring Ball": +define_location( + Area="Maridia", + GraphArea="EastMaridia", + SolveArea="Maridia Sandpits", + Name="Spring Ball", + Class=["Major", "Chozo", "Scavenger"], + CanHidden=False, + Address=0x7C6E5, + Id=0x96, + Visibility="Chozo", + Room='Spring Ball Room', + VanillaItemType='SpringBall', + HUD=13, +), + "Energy Tank, Botwoon": +define_location( + Area="Maridia", + GraphArea="EastMaridia", + SolveArea="Maridia Pink Top", + Name="Energy Tank, Botwoon", + Class=["Major"], + CanHidden=True, + Address=0x7C755, + Id=0x98, + Visibility="Visible", + Room='Botwoon Energy Tank Room', +), + "Draygon": +define_location( + Area="Maridia", + GraphArea="EastMaridia", + SolveArea="Draygon Boss", + Name="Draygon", + Class=["Boss"], + CanHidden=False, + Address=0xB055B058, + Id=None, + Visibility="Hidden", + Room="Draygon's Room", +), + "Space Jump": +define_location( + Area="Maridia", + GraphArea="EastMaridia", + SolveArea="Draygon Boss", + Name="Space Jump", + Class=["Major", "Chozo", "Scavenger"], + CanHidden=False, + Address=0x7C7A7, + Id=0x9a, + Visibility="Chozo", + Room='Space Jump Room', + VanillaItemType='SpaceJump', + HUD=12, +), + "Mother Brain": +define_location( + Area="Tourian", + GraphArea="Tourian", + SolveArea="Tourian", + Name="Mother Brain", + Class=["Boss"], + Address=0xB055B059, + Id=None, + Visibility="Hidden", + CanHidden=False, + Room='Mother Brain Room', +), +###### MINORS + "Power Bomb (Crateria surface)": +define_location( + Area="Crateria", + GraphArea="Crateria", + SolveArea="Crateria Landing Site", + Name="Power Bomb (Crateria surface)", + Class=["Minor"], + CanHidden=True, + Address=0x781CC, + Id=0x0, + Visibility="Visible", + Room='Crateria Power Bomb Room', +), + "Missile (outside Wrecked Ship bottom)": +define_location( + Area="Crateria", + GraphArea="WreckedShip", + SolveArea="WreckedShip Bottom", + Name="Missile (outside Wrecked Ship bottom)", + Class=["Minor"], + CanHidden=True, + Address=0x781E8, + Id=0x1, + Visibility="Visible", + Room='West Ocean', +), + "Missile (outside Wrecked Ship top)": +define_location( + Area="Crateria", + GraphArea="WreckedShip", + SolveArea="WreckedShip Top", + Name="Missile (outside Wrecked Ship top)", + Class=["Minor"], + CanHidden=False, + Address=0x781EE, + Id=0x2, + Visibility="Hidden", + Room='West Ocean', +), + "Missile (outside Wrecked Ship middle)": +define_location( + Area="Crateria", + GraphArea="WreckedShip", + SolveArea="WreckedShip Top", + Name="Missile (outside Wrecked Ship middle)", + CanHidden=True, + Class=["Minor"], + Address=0x781F4, + Id=0x3, + Visibility="Visible", + Room='West Ocean', +), + "Missile (Crateria moat)": +define_location( + Area="Crateria", + GraphArea="Crateria", + SolveArea="Crateria Landing Site", + Name="Missile (Crateria moat)", + Class=["Minor"], + CanHidden=False, + Address=0x78248, + Id=0x4, + Visibility="Visible", + Room='The Moat', +), + "Missile (Crateria bottom)": +define_location( + Area="Crateria", + GraphArea="Crateria", + SolveArea="Crateria Landing Site", + Name="Missile (Crateria bottom)", + Class=["Minor"], + CanHidden=True, + Address=0x783EE, + Id=0x6, + Visibility="Visible", + Room='Pit Room', +), + "Missile (Crateria gauntlet right)": +define_location( + Area="Crateria", + GraphArea="Crateria", + SolveArea="Crateria Gauntlet", + Name="Missile (Crateria gauntlet right)", + Class=["Minor"], + CanHidden=True, + Address=0x78464, + Id=0x9, + Visibility="Visible", + Room='Green Pirates Shaft', +), + "Missile (Crateria gauntlet left)": +define_location( + Area="Crateria", + GraphArea="Crateria", + SolveArea="Crateria Gauntlet", + Name="Missile (Crateria gauntlet left)", + Class=["Minor"], + CanHidden=True, + Address=0x7846A, + Id=0xa, + Visibility="Visible", + Room='Green Pirates Shaft', +), + "Super Missile (Crateria)": +define_location( + Area="Crateria", + GraphArea="Crateria", + SolveArea="Crateria Landing Site", + Name="Super Missile (Crateria)", + Class=["Minor"], + CanHidden=True, + Address=0x78478, + Id=0xb, + Visibility="Visible", + Room='Crateria Super Room', +), + "Missile (Crateria middle)": +define_location( + Area="Crateria", + GraphArea="Crateria", + SolveArea="Crateria Landing Site", + Name="Missile (Crateria middle)", + Class=["Minor", "Chozo"], + CanHidden=True, + Address=0x78486, + Id=0xc, + Visibility="Visible", + Room='The Final Missile', +), + "Power Bomb (green Brinstar bottom)": +define_location( + Area="Brinstar", + GraphArea="GreenPinkBrinstar", + SolveArea="Green Brinstar", + Name="Power Bomb (green Brinstar bottom)", + Class=["Minor"], + CanHidden=False, + Address=0x784AC, + Id=0xd, + Visibility="Chozo", + Room='Green Brinstar Main Shaft', +), + "Super Missile (pink Brinstar)": +define_location( + Area="Brinstar", + GraphArea="GreenPinkBrinstar", + SolveArea="Pink Brinstar", + Name="Super Missile (pink Brinstar)", + Class=["Minor", "Chozo"], + CanHidden=False, + Address=0x784E4, + Id=0xe, + Visibility="Chozo", + Room='Spore Spawn Super Room', +), + "Missile (green Brinstar below super missile)": +define_location( + Area="Brinstar", + GraphArea="GreenPinkBrinstar", + SolveArea="Green Brinstar", + Name="Missile (green Brinstar below super missile)", + Class=["Minor"], + CanHidden=False, + Address=0x78518, + Id=0xf, + Visibility="Visible", + Room='Early Supers Room', +), + "Super Missile (green Brinstar top)": +define_location( + Area="Brinstar", + GraphArea="GreenPinkBrinstar", + SolveArea="Green Brinstar Reserve", + Name="Super Missile (green Brinstar top)", + Class=["Minor"], + CanHidden=True, + Address=0x7851E, + Id=0x10, + Visibility="Visible", + Room='Early Supers Room', +), + "Missile (green Brinstar behind missile)": +define_location( + Area="Brinstar", + GraphArea="GreenPinkBrinstar", + SolveArea="Green Brinstar Reserve", + Name="Missile (green Brinstar behind missile)", + Class=["Minor"], + CanHidden=False, + Address=0x78532, + Id=0x12, + Visibility="Hidden", + Room='Brinstar Reserve Tank Room', +), + "Missile (green Brinstar behind reserve tank)": +define_location( + Area="Brinstar", + GraphArea="GreenPinkBrinstar", + SolveArea="Green Brinstar Reserve", + Name="Missile (green Brinstar behind reserve tank)", + Class=["Minor"], + CanHidden=True, + Address=0x78538, + Id=0x13, + Visibility="Visible", + Room='Brinstar Reserve Tank Room', +), + "Missile (pink Brinstar top)": +define_location( + Area="Brinstar", + GraphArea="GreenPinkBrinstar", + SolveArea="Pink Brinstar", + Name="Missile (pink Brinstar top)", + Class=["Minor"], + CanHidden=True, + Address=0x78608, + Id=0x15, + Visibility="Visible", + Room='Big Pink', +), + "Missile (pink Brinstar bottom)": +define_location( + Area="Brinstar", + GraphArea="GreenPinkBrinstar", + SolveArea="Pink Brinstar", + Name="Missile (pink Brinstar bottom)", + Class=["Minor"], + CanHidden=True, + Address=0x7860E, + Id=0x16, + Visibility="Visible", + Room='Big Pink', +), + "Power Bomb (pink Brinstar)": +define_location( + Area="Brinstar", + GraphArea="GreenPinkBrinstar", + SolveArea="Pink Brinstar", + Name="Power Bomb (pink Brinstar)", + Class=["Minor"], + CanHidden=True, + Address=0x7865C, + Id=0x18, + Visibility="Visible", + Room='Pink Brinstar Power Bomb Room', +), + "Missile (green Brinstar pipe)": +define_location( + Area="Brinstar", + GraphArea="GreenPinkBrinstar", + SolveArea="Brinstar Hills", + Name="Missile (green Brinstar pipe)", + Class=["Minor"], + CanHidden=True, + Address=0x78676, + Id=0x19, + Visibility="Visible", + Room='Green Hill Zone', +), + "Power Bomb (blue Brinstar)": +define_location( + Area="Brinstar", + GraphArea="Crateria", + SolveArea="Blue Brinstar", + Name="Power Bomb (blue Brinstar)", + Class=["Minor"], + CanHidden=True, + Address=0x7874C, + Id=0x1b, + Visibility="Visible", + Room='Morph Ball Room', +), + "Missile (blue Brinstar middle)": +define_location( + Area="Brinstar", + GraphArea="Crateria", + SolveArea="Blue Brinstar", + Name="Missile (blue Brinstar middle)", + Address=0x78798, + Id=0x1c, + Class=["Minor"], + CanHidden=True, + Visibility="Visible", + Room='Blue Brinstar Energy Tank Room', +), + "Super Missile (green Brinstar bottom)": +define_location( + Area="Brinstar", + GraphArea="GreenPinkBrinstar", + SolveArea="Green Brinstar", + Name="Super Missile (green Brinstar bottom)", + Class=["Minor"], + CanHidden=True, + Address=0x787D0, + Id=0x1f, + Visibility="Visible", + Room='Etecoon Super Room', +), + "Missile (blue Brinstar bottom)": +define_location( + Area="Brinstar", + GraphArea="Crateria", + SolveArea="Blue Brinstar", + Name="Missile (blue Brinstar bottom)", + Class=["Minor", "Chozo"], + CanHidden=False, + Address=0x78802, + Id=0x22, + Visibility="Chozo", + Room='First Missile Room', +), + "Missile (blue Brinstar top)": +define_location( + Area="Brinstar", + GraphArea="Crateria", + SolveArea="Blue Brinstar", + Name="Missile (blue Brinstar top)", + Class=["Minor"], + CanHidden=True, + Address=0x78836, + Id=0x24, + Visibility="Visible", + Room='Billy Mays Room', +), + "Missile (blue Brinstar behind missile)": +define_location( + Area="Brinstar", + GraphArea="Crateria", + SolveArea="Blue Brinstar", + Name="Missile (blue Brinstar behind missile)", + Class=["Minor"], + CanHidden=False, + Address=0x7883C, + Id=0x25, + Visibility="Hidden", + Room='Billy Mays Room', +), + "Power Bomb (red Brinstar sidehopper room)": +define_location( + Area="Brinstar", + GraphArea="RedBrinstar", + SolveArea="Red Brinstar Top", + Name="Power Bomb (red Brinstar sidehopper room)", + Class=["Minor"], + CanHidden=True, + Address=0x788CA, + Id=0x27, + Visibility="Visible", + Room='Beta Power Bomb Room', +), + "Power Bomb (red Brinstar spike room)": +define_location( + Area="Brinstar", + GraphArea="RedBrinstar", + SolveArea="Red Brinstar Top", + Name="Power Bomb (red Brinstar spike room)", + Class=["Minor", "Chozo"], + CanHidden=False, + Address=0x7890E, + Id=0x28, + Visibility="Chozo", + Room='Alpha Power Bomb Room', +), + "Missile (red Brinstar spike room)": +define_location( + Area="Brinstar", + GraphArea="RedBrinstar", + SolveArea="Red Brinstar Top", + Name="Missile (red Brinstar spike room)", + Class=["Minor"], + CanHidden=True, + Address=0x78914, + Id=0x29, + Visibility="Visible", + Room='Alpha Power Bomb Room', +), + "Missile (Kraid)": +define_location( + Area="Brinstar", + GraphArea="Kraid", + SolveArea="Kraid", + Name="Missile (Kraid)", + Class=["Minor"], + CanHidden=False, + Address=0x789EC, + Id=0x2c, + Visibility="Hidden", + Room='Warehouse Keyhunter Room', +), + "Missile (lava room)": +define_location( + Area="Norfair", + GraphArea="Norfair", + SolveArea="Norfair Entrance", + Name="Missile (lava room)", + Class=["Minor"], + CanHidden=False, + Address=0x78AE4, + Id=0x31, + Visibility="Hidden", + Room='Cathedral', +), + "Missile (below Ice Beam)": +define_location( + Area="Norfair", + GraphArea="Norfair", + SolveArea="Norfair Ice", + Name="Missile (below Ice Beam)", + Class=["Minor"], + CanHidden=False, + Address=0x78B46, + Id=0x33, + Visibility="Hidden", + Room='Crumble Shaft', +), + "Missile (above Crocomire)": +define_location( + Area="Norfair", + GraphArea="Norfair", + SolveArea="Norfair Grapple Escape", + Name="Missile (above Crocomire)", + Class=["Minor"], + CanHidden=False, + Address=0x78BC0, + Id=0x36, + Visibility="Visible", + Room='Crocomire Escape', +), + "Missile (Hi-Jump Boots)": +define_location( + Area="Norfair", + GraphArea="Norfair", + SolveArea="Norfair Entrance", + Name="Missile (Hi-Jump Boots)", + Class=["Minor"], + CanHidden=True, + Address=0x78BE6, + Id=0x37, + Visibility="Visible", + Room='Hi Jump Energy Tank Room', +), + "Energy Tank (Hi-Jump Boots)": +define_location( + Area="Norfair", + GraphArea="Norfair", + SolveArea="Norfair Entrance", + Name="Energy Tank (Hi-Jump Boots)", + CanHidden=True, + Class=["Minor"], + Address=0x78BEC, + Id=0x38, + Visibility="Visible", + Room='Hi Jump Energy Tank Room', +), + "Power Bomb (Crocomire)": +define_location( + Area="Norfair", + GraphArea="Crocomire", + SolveArea="Crocomire", + Name="Power Bomb (Crocomire)", + Class=["Minor"], + CanHidden=True, + Address=0x78C04, + Id=0x39, + Visibility="Visible", + Room='Post Crocomire Power Bomb Room', +), + "Missile (below Crocomire)": +define_location( + Area="Norfair", + GraphArea="Crocomire", + SolveArea="Crocomire", + Name="Missile (below Crocomire)", + Class=["Minor"], + CanHidden=True, + Address=0x78C14, + Id=0x3a, + Visibility="Visible", + Room='Post Crocomire Missile Room', +), + "Missile (Grapple Beam)": +define_location( + Area="Norfair", + GraphArea="Crocomire", + SolveArea="Crocomire", + Name="Missile (Grapple Beam)", + Class=["Minor"], + CanHidden=True, + Address=0x78C2A, + Id=0x3b, + Visibility="Visible", + Room='Post Crocomire Jump Room', +), + "Missile (Norfair Reserve Tank)": +define_location( + Area="Norfair", + GraphArea="Norfair", + SolveArea="Bubble Norfair Reserve", + Name="Missile (Norfair Reserve Tank)", + Class=["Minor"], + CanHidden=False, + Address=0x78C44, + Id=0x3e, + Visibility="Hidden", + Room='Norfair Reserve Tank Room', +), + "Missile (bubble Norfair green door)": +define_location( + Area="Norfair", + GraphArea="Norfair", + SolveArea="Bubble Norfair Reserve", + Name="Missile (bubble Norfair green door)", + Class=["Minor"], + CanHidden=True, + Address=0x78C52, + Id=0x3f, + Visibility="Visible", + Room='Green Bubbles Missile Room', +), + "Missile (bubble Norfair)": +define_location( + Area="Norfair", + GraphArea="Norfair", + SolveArea="Bubble Norfair Bottom", + Name="Missile (bubble Norfair)", + Class=["Minor"], + CanHidden=True, + Address=0x78C66, + Id=0x40, + Visibility="Visible", + Room='Bubble Mountain', +), + "Missile (Speed Booster)": +define_location( + Area="Norfair", + GraphArea="Norfair", + SolveArea="Bubble Norfair Speed", + Name="Missile (Speed Booster)", + Class=["Minor"], + CanHidden=False, + Address=0x78C74, + Id=0x41, + Visibility="Hidden", + Room='Speed Booster Hall', +), + "Missile (Wave Beam)": +define_location( + Area="Norfair", + GraphArea="Norfair", + SolveArea="Bubble Norfair Wave", + Name="Missile (Wave Beam)", + Class=["Minor"], + CanHidden=True, + Address=0x78CBC, + Id=0x43, + Visibility="Visible", + Room='Double Chamber', +), + "Missile (Gold Torizo)": +define_location( + Area="LowerNorfair", + GraphArea="LowerNorfair", + SolveArea="Lower Norfair Screw Attack", + Name="Missile (Gold Torizo)", + Class=["Minor"], + CanHidden=True, + Address=0x78E6E, + Id=0x46, + Visibility="Visible", + Room="Golden Torizo's Room", +), + "Super Missile (Gold Torizo)": +define_location( + Area="LowerNorfair", + GraphArea="LowerNorfair", + SolveArea="Lower Norfair Screw Attack", + Name="Super Missile (Gold Torizo)", + Class=["Minor"], + CanHidden=False, + Address=0x78E74, + Id=0x47, + Visibility="Hidden", + Room="Golden Torizo's Room", +), + "Missile (Mickey Mouse room)": +define_location( + Area="LowerNorfair", + GraphArea="LowerNorfair", + SolveArea="Lower Norfair Before Amphitheater", + Name="Missile (Mickey Mouse room)", + Class=["Minor"], + CanHidden=True, + Address=0x78F30, + Id=0x49, + Visibility="Visible", + Room='Mickey Mouse Room', +), + "Missile (lower Norfair above fire flea room)": +define_location( + Area="LowerNorfair", + GraphArea="LowerNorfair", + SolveArea="Lower Norfair After Amphitheater", + Name="Missile (lower Norfair above fire flea room)", + Class=["Minor"], + CanHidden=True, + Address=0x78FCA, + Id=0x4a, + Visibility="Visible", + Room='Lower Norfair Spring Ball Maze Room', +), + "Power Bomb (lower Norfair above fire flea room)": +define_location( + Area="LowerNorfair", + GraphArea="LowerNorfair", + SolveArea="Lower Norfair After Amphitheater", + Name="Power Bomb (lower Norfair above fire flea room)", + Class=["Minor"], + CanHidden=False, + Address=0x78FD2, + Id=0x4b, + Visibility="Visible", + Room='Lower Norfair Escape Power Bomb Room', +), + "Power Bomb (Power Bombs of shame)": +define_location( + Area="LowerNorfair", + GraphArea="LowerNorfair", + SolveArea="Lower Norfair After Amphitheater", + Name="Power Bomb (Power Bombs of shame)", + Class=["Minor"], + CanHidden=True, + Address=0x790C0, + Id=0x4c, + Visibility="Visible", + Room='Wasteland', +), + "Missile (lower Norfair near Wave Beam)": +define_location( + Area="LowerNorfair", + GraphArea="LowerNorfair", + SolveArea="Lower Norfair After Amphitheater", + Name="Missile (lower Norfair near Wave Beam)", + Class=["Minor"], + CanHidden=True, + Address=0x79100, + Id=0x4d, + Visibility="Visible", + Room="Three Muskateers' Room", +), + "Missile (Wrecked Ship middle)": +define_location( + Area="WreckedShip", + GraphArea="WreckedShip", + SolveArea="WreckedShip Main", + Name="Missile (Wrecked Ship middle)", + Class=["Minor"], + CanHidden=True, + Address=0x7C265, + Id=0x80, + Visibility="Visible", + Room='Wrecked Ship Main Shaft', +), + "Missile (Gravity Suit)": +define_location( + Area="WreckedShip", + GraphArea="WreckedShip", + SolveArea="WreckedShip Gravity", + Name="Missile (Gravity Suit)", + Class=["Minor", "Chozo"], + CanHidden=False, + Address=0x7C2EF, + Id=0x82, + Visibility="Visible", + Room='Bowling Alley', +), + "Missile (Wrecked Ship top)": +define_location( + Area="WreckedShip", + GraphArea="WreckedShip", + SolveArea="WreckedShip Top", + Name="Missile (Wrecked Ship top)", + Class=["Minor"], + CanHidden=True, + Address=0x7C319, + Id=0x83, + Visibility="Visible", + Room='Wrecked Ship East Missile Room', +), + "Super Missile (Wrecked Ship left)": +define_location( + Area="WreckedShip", + GraphArea="WreckedShip", + SolveArea="WreckedShip Main", + Name="Super Missile (Wrecked Ship left)", + Class=["Minor"], + CanHidden=True, + Address=0x7C357, + Id=0x85, + Visibility="Visible", + Room='Wrecked Ship West Super Room', +), + "Missile (green Maridia shinespark)": +define_location( + Area="Maridia", + GraphArea="WestMaridia", + SolveArea="Maridia Green", + Name="Missile (green Maridia shinespark)", + Class=["Minor"], + CanHidden=False, + Address=0x7C437, + Id=0x88, + Visibility="Visible", + Room='Main Street', +), + "Super Missile (green Maridia)": +define_location( + Area="Maridia", + GraphArea="WestMaridia", + SolveArea="Maridia Green", + Name="Super Missile (green Maridia)", + Class=["Minor"], + CanHidden=True, + Address=0x7C43D, + Id=0x89, + Visibility="Visible", + Room='Main Street', +), + "Missile (green Maridia tatori)": +define_location( + Area="Maridia", + GraphArea="WestMaridia", + SolveArea="Maridia Green", + Name="Missile (green Maridia tatori)", + Class=["Minor"], + CanHidden=False, + Address=0x7C483, + Id=0x8b, + Visibility="Hidden", + Room='Mama Turtle Room', +), + "Super Missile (yellow Maridia)": +define_location( + Area="Maridia", + GraphArea="WestMaridia", + SolveArea="Maridia Pink Bottom", + Name="Super Missile (yellow Maridia)", + Class=["Minor"], + CanHidden=True, + Address=0x7C4AF, + Id=0x8c, + Visibility="Visible", + Room='Watering Hole', +), + "Missile (yellow Maridia super missile)": +define_location( + Area="Maridia", + GraphArea="WestMaridia", + SolveArea="Maridia Pink Bottom", + Name="Missile (yellow Maridia super missile)", + Class=["Minor"], + CanHidden=True, + Address=0x7C4B5, + Id=0x8d, + Visibility="Visible", + Room='Watering Hole', +), + "Missile (yellow Maridia false wall)": +define_location( + Area="Maridia", + GraphArea="WestMaridia", + SolveArea="Maridia Pink Bottom", + Name="Missile (yellow Maridia false wall)", + Class=["Minor"], + CanHidden=True, + Address=0x7C533, + Id=0x8e, + Visibility="Visible", + Room='Pseudo Plasma Spark Room', +), + "Missile (left Maridia sand pit room)": +define_location( + Area="Maridia", + GraphArea="EastMaridia", + SolveArea="Left Sandpit", + Name="Missile (left Maridia sand pit room)", + Class=["Minor"], + CanHidden=True, + Address=0x7C5DD, + Id=0x90, + Visibility="Visible", + Room='West Sand Hole', +), + "Missile (right Maridia sand pit room)": +define_location( + Area="Maridia", + GraphArea="EastMaridia", + SolveArea="Right Sandpit", + Name="Missile (right Maridia sand pit room)", + Class=["Minor"], + CanHidden=True, + Address=0x7C5EB, + Id=0x92, + Visibility="Visible", + Room='East Sand Hole', +), + "Power Bomb (right Maridia sand pit room)": +define_location( + Area="Maridia", + GraphArea="EastMaridia", + SolveArea="Right Sandpit", + Name="Power Bomb (right Maridia sand pit room)", + Class=["Minor"], + CanHidden=True, + Address=0x7C5F1, + Id=0x93, + Visibility="Visible", + Room='East Sand Hole', +), + "Missile (pink Maridia)": +define_location( + Area="Maridia", + GraphArea="EastMaridia", + SolveArea="Maridia Pink Bottom", + Name="Missile (pink Maridia)", + Address=0x7C603, + Id=0x94, + Class=["Minor"], + CanHidden=True, + Visibility="Visible", + Room='Aqueduct', +), + "Super Missile (pink Maridia)": +define_location( + Area="Maridia", + GraphArea="EastMaridia", + SolveArea="Maridia Pink Bottom", + Name="Super Missile (pink Maridia)", + Class=["Minor"], + CanHidden=True, + Address=0x7C609, + Id=0x95, + Visibility="Visible", + Room='Aqueduct', +), + "Missile (Draygon)": +define_location( + Area="Maridia", + GraphArea="EastMaridia", + SolveArea="Maridia Pink Top", + Name="Missile (Draygon)", + Class=["Minor"], + CanHidden=False, + Address=0x7C74D, + Id=0x97, + Visibility="Hidden", + Room='The Precious Room', +) +} diff --git a/worlds/sm/variaRandomizer/graph/vanilla/__init__.py b/worlds/sm/variaRandomizer/graph/vanilla/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/worlds/sm/variaRandomizer/graph/vanilla/graph_access.py b/worlds/sm/variaRandomizer/graph/vanilla/graph_access.py new file mode 100644 index 00000000..eebff84c --- /dev/null +++ b/worlds/sm/variaRandomizer/graph/vanilla/graph_access.py @@ -0,0 +1,758 @@ +from graph.graph import AccessPoint +from utils.parameters import Settings +from rom.rom_patches import RomPatches +from logic.smbool import SMBool +from logic.helpers import Bosses +from logic.cache import Cache + +# all access points and traverse functions +accessPoints = [ + ### Ceres Station + AccessPoint('Ceres', 'Ceres', { + 'Landing Site': lambda sm: SMBool(True) + }, internal=True, + start={'spawn': 0xfffe, 'doors':[0x32], 'patches':[RomPatches.BlueBrinstarBlueDoor], 'solveArea': "Crateria Landing Site"}), + ### Crateria and Blue Brinstar + AccessPoint('Landing Site', 'Crateria', { + 'Lower Mushrooms Left': Cache.ldeco(lambda sm: sm.wand(sm.canPassTerminatorBombWall(), + sm.canPassCrateriaGreenPirates())), + 'Keyhunter Room Bottom': Cache.ldeco(lambda sm: sm.traverse('LandingSiteRight')), + 'Blue Brinstar Elevator Bottom': lambda sm: SMBool(True) + }, internal=True, + start={'spawn': 0x0000, 'doors':[0x32], 'patches':[RomPatches.BlueBrinstarBlueDoor], 'solveArea': "Crateria Landing Site"}), + AccessPoint('Blue Brinstar Elevator Bottom', 'Crateria', { + 'Morph Ball Room Left': lambda sm: sm.canUsePowerBombs(), + 'Landing Site': lambda sm: SMBool(True) + }, internal=True), + AccessPoint('Gauntlet Top', 'Crateria', { + 'Green Pirates Shaft Bottom Right': Cache.ldeco(lambda sm: sm.wand(sm.haveItem('Morph'), sm.canPassCrateriaGreenPirates())) + }, internal=True, + start={'spawn': 0x0006, 'solveArea': "Crateria Gauntlet", 'save':"Save_Gauntlet", 'forcedEarlyMorph':True}), + AccessPoint('Lower Mushrooms Left', 'Crateria', { + 'Landing Site': Cache.ldeco(lambda sm: sm.wand(sm.canPassTerminatorBombWall(False), + sm.canPassCrateriaGreenPirates())), + 'Green Pirates Shaft Bottom Right': lambda sm: SMBool(True) + }, roomInfo = {'RoomPtr':0x9969, "area": 0x0, 'songs':[0x997a]}, + exitInfo = {'DoorPtr':0x8c22, 'direction': 0x5, "cap": (0xe, 0x6), "bitFlag": 0x0, + "screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x36, 'SamusY':0x88, 'song': 0x9}, + dotOrientation = 'nw'), + AccessPoint('Green Pirates Shaft Bottom Right', 'Crateria', { + 'Lower Mushrooms Left': lambda sm: SMBool(True) + }, traverse = Cache.ldeco(lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.AreaRandoMoreBlueDoors), + sm.traverse('GreenPiratesShaftBottomRight'))), + roomInfo = {'RoomPtr':0x99bd, "area": 0x0, 'songs':[0x99ce]}, + # the doorAsmPtr 7FE00 is set by the g4_skip.ips patch, we have to call it + exitInfo = {'DoorPtr':0x8c52, 'direction': 0x4, "cap": (0x1, 0x6), "bitFlag": 0x0, + "screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0xfe00}, + entryInfo = {'SamusX':0xcc, 'SamusY':0x688, 'song': 0x9}, + dotOrientation = 'e'), + AccessPoint('Moat Right', 'Crateria', { + 'Moat Left': lambda sm: sm.canPassMoatReverse() + }, roomInfo = {'RoomPtr':0x95ff, "area": 0x0, 'songs':[0x9610]}, + exitInfo = {'DoorPtr':0x8aea, 'direction': 0x4, "cap": (0x1, 0x46), "bitFlag": 0x0, + "screen": (0x0, 0x4), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x1cf, 'SamusY':0x88, 'song': 0xc}, + dotOrientation = 'ne'), + AccessPoint('Moat Left', 'Crateria', { + 'Keyhunter Room Bottom': lambda sm: SMBool(True), + 'Moat Right': lambda sm: sm.canPassMoatFromMoat() + }, internal=True), + AccessPoint('Keyhunter Room Bottom', 'Crateria', { + 'Moat Left': Cache.ldeco(lambda sm: sm.traverse('KihunterRight')), + 'Moat Right': Cache.ldeco(lambda sm: sm.wand(sm.traverse('KihunterRight'), sm.canPassMoat())), + 'Landing Site': lambda sm: SMBool(True) + }, traverse = Cache.ldeco(lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.AreaRandoMoreBlueDoors), + sm.traverse('KihunterBottom'))), + roomInfo = { 'RoomPtr':0x948c, "area": 0x0, 'songs':[0x949d] }, + exitInfo = {'DoorPtr':0x8a42, 'direction': 0x6, "cap": (0x6, 0x2), "bitFlag": 0x0, + "screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x14c, 'SamusY':0x2b8, 'song': 0xc}, + dotOrientation = 'se'), + AccessPoint('Morph Ball Room Left', 'Crateria', { + 'Blue Brinstar Elevator Bottom': lambda sm: sm.canUsePowerBombs() + }, roomInfo = { 'RoomPtr':0x9e9f, "area": 0x1}, + exitInfo = {'DoorPtr':0x8e9e, 'direction': 0x5, "cap": (0x1e, 0x6), "bitFlag": 0x0, + "screen": (0x1, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x34, 'SamusY':0x288}, + dotOrientation = 'sw'), + # Escape APs + AccessPoint('Climb Bottom Left', 'Crateria', { + 'Landing Site': lambda sm: SMBool(True) + }, roomInfo = {'RoomPtr':0x96ba, "area": 0x0}, + exitInfo = {'DoorPtr':0x8b6e, 'direction': 0x5, "cap": (0x2e, 0x16), "bitFlag": 0x0, + "screen": (0x2, 0x1), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x34, 'SamusY':0x888}, + escape = True, + dotOrientation = 'ne'), + AccessPoint('Flyway Right', 'Crateria', {}, + roomInfo = {'RoomPtr':0x9879, "area": 0x0}, + exitInfo = {'DoorPtr':0x8bc2, 'direction': 0x4, "cap": (0x1, 0x6), "bitFlag": 0x0, + "screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000, + "exitAsmPtr": 0xf030}, # setup_next_escape in rando_escape.asm + entryInfo = {'SamusX':0xffff, 'SamusY':0xffff}, # unused + escape = True), + AccessPoint('Bomb Torizo Room Left', 'Crateria', {}, + roomInfo = {'RoomPtr':0x9804, "area": 0x0}, + exitInfo = {'DoorPtr':0x8baa, 'direction': 0x5, "cap": (0x2e, 0x6), "bitFlag": 0x0, + "screen": (0x2, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x34, 'SamusY':0xb8}, + escape = True), + ### Green and Pink Brinstar + AccessPoint('Green Brinstar Elevator', 'GreenPinkBrinstar', { + 'Big Pink': Cache.ldeco(lambda sm: sm.wand(sm.canPassDachoraRoom(), + sm.traverse('MainShaftBottomRight'))), + 'Etecoons Bottom': lambda sm: sm.canAccessEtecoons() + }, roomInfo = {'RoomPtr':0x9938, "area": 0x0}, + exitInfo = {'DoorPtr':0x8bfe, 'direction': 0x4, "cap": (0x1, 0x6), "bitFlag": 0x0, + "screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0xcc, 'SamusY':0x88}, + start = {'spawn': 0x0108, 'doors':[0x1f, 0x21, 0x26], 'patches':[RomPatches.BrinReserveBlueDoors], 'solveArea': "Green Brinstar"}, # XXX test if it would be better in brin reserve room with custom save + dotOrientation = 'ne'), + AccessPoint('Big Pink', 'GreenPinkBrinstar', { + 'Green Hill Zone Top Right': Cache.ldeco(lambda sm: sm.wand(sm.haveItem('Morph'), + sm.traverse('BigPinkBottomRight'))), + 'Green Brinstar Elevator': lambda sm: sm.canPassDachoraRoom() + }, internal=True, start={'spawn': 0x0100, 'solveArea': "Pink Brinstar"}), + AccessPoint('Green Hill Zone Top Right', 'GreenPinkBrinstar', { + 'Noob Bridge Right': lambda sm: SMBool(True), + 'Big Pink': Cache.ldeco(lambda sm: sm.haveItem('Morph')) + }, traverse=Cache.ldeco(lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.AreaRandoBlueDoors), sm.traverse('GreenHillZoneTopRight'))), + roomInfo = {'RoomPtr':0x9e52, "area": 0x1 }, + exitInfo = {'DoorPtr':0x8e86, 'direction': 0x4, "cap": (0x1, 0x26), "bitFlag": 0x0, + "screen": (0x0, 0x2), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x1c7, 'SamusY':0x88}, + dotOrientation = 'e'), + AccessPoint('Noob Bridge Right', 'GreenPinkBrinstar', { + 'Green Hill Zone Top Right': Cache.ldeco(lambda sm: sm.wor(sm.haveItem('Wave'), + sm.wor(sm.canBlueGateGlitch(), + RomPatches.has(sm.player, RomPatches.AreaRandoGatesOther)))) + }, traverse=Cache.ldeco(lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.AreaRandoBlueDoors), sm.traverse('NoobBridgeRight'))), + roomInfo = {'RoomPtr':0x9fba, "area": 0x1 }, + exitInfo = {'DoorPtr':0x8f0a, 'direction': 0x4, "cap": (0x1, 0x46), "bitFlag": 0x0, + "screen": (0x0, 0x4), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x5ce, 'SamusY':0x88}, + dotOrientation = 'se'), + AccessPoint('Green Brinstar Main Shaft Top Left', 'GreenPinkBrinstar', { + 'Green Brinstar Elevator': lambda sm: SMBool(True) + }, roomInfo = {'RoomPtr':0x9ad9, "area": 0x1}, + exitInfo = {'DoorPtr':0x8cb2, 'direction': 0x5, "cap": (0x2e, 0x6), "bitFlag": 0x0, + "screen": (0x2, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x34, 'SamusY':0x488}, + escape = True, + dotOrientation = 'ne'), + AccessPoint('Brinstar Pre-Map Room Right', 'GreenPinkBrinstar', { + }, roomInfo = {'RoomPtr':0x9b9d, "area": 0x1}, + exitInfo = {'DoorPtr':0x8d42, 'direction': 0x4, "cap": (0x1, 0x46), "bitFlag": 0x0, + "screen": (0x0, 0x4), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0xffff, 'SamusY':0xffff}, # unused + escape = True, + dotOrientation = 'ne'), + AccessPoint('Etecoons Supers', 'GreenPinkBrinstar', { + 'Etecoons Bottom': lambda sm: SMBool(True) + }, internal=True, + start={'spawn': 0x0107, 'doors':[0x34], 'patches':[RomPatches.EtecoonSupersBlueDoor], + 'save':"Save_Etecoons" ,'solveArea': "Green Brinstar", + 'forcedEarlyMorph':True, 'needsPreRando': True}), + AccessPoint('Etecoons Bottom', 'GreenPinkBrinstar', { + 'Etecoons Supers': Cache.ldeco(lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.EtecoonSupersBlueDoor), + sm.traverse('EtecoonEnergyTankLeft'))), + 'Green Brinstar Elevator': lambda sm: sm.canUsePowerBombs() + }, internal=True), + ### Wrecked Ship + AccessPoint('West Ocean Left', 'WreckedShip', { + 'Wrecked Ship Main': Cache.ldeco(lambda sm: sm.traverse('WestOceanRight')) + }, roomInfo = {'RoomPtr':0x93fe, "area": 0x0}, + exitInfo = {'DoorPtr':0x89ca, 'direction': 0x5, "cap": (0x1e, 0x6), "bitFlag": 0x0, + "screen": (0x1, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x34, 'SamusY':0x488}, + dotOrientation = 'w'), + AccessPoint('Wrecked Ship Main', 'WreckedShip', { + 'West Ocean Left': lambda sm: SMBool(True), + 'Wrecked Ship Back': Cache.ldeco(lambda sm: sm.wor(sm.wand(Bosses.bossDead(sm, 'Phantoon'), + sm.canPassSpongeBath()), + sm.wand(sm.wnot(Bosses.bossDead(sm, 'Phantoon')), + RomPatches.has(sm.player, RomPatches.SpongeBathBlueDoor)))), + 'PhantoonRoomOut': Cache.ldeco(lambda sm: sm.wand(sm.traverse('WreckedShipMainShaftBottom'), sm.canPassBombPassages())) + }, internal=True, + start={'spawn':0x0300, + 'doors':[0x83,0x8b], 'patches':[RomPatches.SpongeBathBlueDoor, RomPatches.WsEtankBlueDoor], + 'solveArea': "WreckedShip Main", + 'needsPreRando':True}), + AccessPoint('Wrecked Ship Back', 'WreckedShip', { + 'Wrecked Ship Main': lambda sm: SMBool(True), + 'Crab Maze Left': Cache.ldeco(lambda sm: sm.canPassForgottenHighway(True)) + }, internal=True), + AccessPoint('Crab Maze Left', 'WreckedShip', { + 'Wrecked Ship Back': Cache.ldeco(lambda sm: sm.canPassForgottenHighway(False)) + }, traverse=Cache.ldeco(lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.AreaRandoBlueDoors), + sm.traverse('LeCoudeBottom'))), # it is not exactly coude's door + # but it's equivalent in vanilla anyway + roomInfo = {'RoomPtr':0x957d, "area": 0x0, 'songs':[0x958e]}, + exitInfo = {'DoorPtr':0x8aae, 'direction': 0x5, "cap": (0xe, 0x6), "bitFlag": 0x0, + "screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x34, 'SamusY':0x188, 'song': 0xc}, + dotOrientation = 'e'), + AccessPoint('PhantoonRoomOut', 'WreckedShip', { + 'Wrecked Ship Main': lambda sm: sm.canPassBombPassages() + }, boss = True, + roomInfo = {'RoomPtr':0xcc6f, "area": 0x3}, + exitInfo = {'DoorPtr':0xa2ac, 'direction': 0x4, "cap": (0x1, 0x6), "bitFlag": 0x0, + "screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0}, + entryInfo = {'SamusX':0x49f, 'SamusY':0xb8}, + traverse=lambda sm: sm.canOpenEyeDoors(), + dotOrientation = 's'), + AccessPoint('PhantoonRoomIn', 'WreckedShip', {}, + boss = True, + roomInfo = {'RoomPtr':0xcd13, "area": 0x3}, + exitInfo = {'DoorPtr':0xa2c4, 'direction': 0x5, "cap": (0x4e, 0x6), "bitFlag": 0x0, + "screen": (0x4, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0xe1fe, + "exitAsmPtr": 0xf7f0}, + entryInfo = {'SamusX':0x2e, 'SamusY':0xb8}, + dotOrientation = 's'), + AccessPoint('Basement Left', 'WreckedShip', { + 'Wrecked Ship Main': lambda sm: SMBool(True) + }, roomInfo = {'RoomPtr':0xcc6f, "area": 0x3}, + exitInfo = {'DoorPtr':0xa2a0, 'direction': 0x5, "cap": (0xe, 0x6), "bitFlag": 0x0, + "screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x2e, 'SamusY':0x88}, + escape = True, + dotOrientation = 'ne'), + AccessPoint('Wrecked Ship Map Room', 'WreckedShip', { + }, roomInfo = {'RoomPtr':0xcccb, "area": 0x3}, + exitInfo = {'DoorPtr':0xa2b8, 'direction': 0x4, "cap": (0x1, 0x6), "bitFlag": 0x0, + "screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0xffff, 'SamusY':0xffff}, # unused + escape = True, + dotOrientation = 'ne'), + ### Lower Norfair + AccessPoint('Lava Dive Right', 'LowerNorfair', { + 'LN Entrance': lambda sm: sm.canPassLavaPit() + }, roomInfo = {'RoomPtr':0xaf14, "area": 0x2, 'songs':[0xaf25]}, + exitInfo = {'DoorPtr':0x96d2, 'direction': 0x4, "cap": (0x11, 0x26), "bitFlag": 0x0, + "screen": (0x1, 0x2), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x3d0, 'SamusY':0x88, 'song': 0x15}, + dotOrientation = 'w'), + AccessPoint('LN Entrance', 'LowerNorfair', { + 'Lava Dive Right': lambda sm: sm.canPassLavaPitReverse(), + 'LN Above GT': lambda sm: sm.canPassLowerNorfairChozo(), + 'Screw Attack Bottom': Cache.ldeco(lambda sm: sm.wand(sm.canUsePowerBombs(), + sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']), + sm.canGreenGateGlitch(), + sm.canDestroyBombWalls())), + 'Firefleas': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']), + sm.canPassWorstRoom(), + sm.canUsePowerBombs())) + }, internal=True), + AccessPoint('LN Above GT', 'LowerNorfair', { + 'Screw Attack Bottom': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']), + sm.enoughStuffGT())) + }, internal=True), + AccessPoint('Screw Attack Bottom', 'LowerNorfair', { + 'LN Entrance': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']), + sm.canExitScrewAttackArea(), + sm.haveItem('Super'), + sm.canUsePowerBombs())) + }, internal=True), + AccessPoint('Firefleas', 'LowerNorfair', { + 'LN Entrance': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']), + sm.canPassAmphitheaterReverse(), + sm.canPassWorstRoomPirates(), + sm.canUsePowerBombs())), + 'Three Muskateers Room Left': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']), + sm.haveItem('Morph'), + # check for only 3 ki hunters this way + sm.canPassRedKiHunters())), + 'Ridley Zone': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']), + sm.traverse('WastelandLeft'), + sm.traverse('RedKihunterShaftBottom'), + sm.canGetBackFromRidleyZone(), + sm.canPassRedKiHunters(), + sm.canPassWastelandDessgeegas(), + sm.canPassNinjaPirates())), + 'Screw Attack Bottom': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']), + sm.canPassAmphitheaterReverse(), + sm.canDestroyBombWalls(), + sm.canGreenGateGlitch())), + 'Firefleas Top': Cache.ldeco(lambda sm: sm.wand(sm.canPassBombPassages(), + sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']))) + }, internal=True), + AccessPoint('Firefleas Top', 'LowerNorfair', { + # this weird condition basically says: "if we start here, give heat protection" + 'Firefleas': Cache.ldeco(lambda sm: sm.wor(sm.wnot(RomPatches.has(sm.player, RomPatches.LowerNorfairPBRoomHeatDisable)), + sm.heatProof())) + }, internal=True, + start={'spawn':0x0207, + 'rom_patches': ['LN_PB_Heat_Disable', 'LN_Firefleas_Remove_Fune','firefleas_shot_block.ips'], + 'patches':[RomPatches.LowerNorfairPBRoomHeatDisable, RomPatches.FirefleasRemoveFune], + 'knows': ["FirefleasWalljump"], + 'save': "Save_Firefleas", 'needsPreRando': True, + 'solveArea': "Lower Norfair After Amphitheater", + 'forcedEarlyMorph':True}), + AccessPoint('Ridley Zone', 'LowerNorfair', { + 'Firefleas': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']), + sm.canGetBackFromRidleyZone(), + sm.canPassWastelandDessgeegas(), + sm.canPassRedKiHunters())), + 'RidleyRoomOut': Cache.ldeco(lambda sm: sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main'])) + }, internal=True), + AccessPoint('Three Muskateers Room Left', 'LowerNorfair', { + 'Firefleas': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']), + sm.haveItem('Morph'), + sm.canPassThreeMuskateers())) + }, roomInfo = {'RoomPtr':0xb656, "area": 0x2}, + exitInfo = {'DoorPtr':0x9a4a, 'direction': 0x5, "cap": (0x5e, 0x6), "bitFlag": 0x0, + "screen": (0x5, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x134, 'SamusY':0x88}, + dotOrientation = 'n'), + AccessPoint('RidleyRoomOut', 'LowerNorfair', { + 'Ridley Zone': Cache.ldeco(lambda sm: sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main'])) + }, boss = True, + roomInfo = {'RoomPtr':0xb37a, "area": 0x2}, + exitInfo = {'DoorPtr':0x98ca, 'direction': 0x5, "cap": (0xe, 0x6), "bitFlag": 0x0, + "screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0}, + entryInfo = {'SamusX':0x2e, 'SamusY':0x98}, + traverse=Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']), + sm.canOpenEyeDoors())), + dotOrientation = 'e'), + AccessPoint('RidleyRoomIn', 'LowerNorfair', {}, + boss = True, + roomInfo = {'RoomPtr':0xb32e, "area": 0x2}, + exitInfo = {'DoorPtr':0x98be, 'direction': 0x4, "cap": (0x1, 0x6), "bitFlag": 0x0, + "screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0}, + entryInfo = {'SamusX':0xbf, 'SamusY':0x198}, # on Ridley's platform. entry screen has to be changed (see getDoorConnections) + dotOrientation = 'e'), + ### Kraid + AccessPoint('Warehouse Zeela Room Left', 'Kraid', { + 'KraidRoomOut': lambda sm: sm.canPassBombPassages() + }, roomInfo = {'RoomPtr': 0xa471, "area": 0x1, 'songs':[0xa482]}, + exitInfo = {'DoorPtr': 0x913e, 'direction': 0x5, "cap": (0x2e, 0x6), "bitFlag": 0x0, + "screen": (0x2, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0xbd3f}, + entryInfo = {'SamusX':0x34, 'SamusY':0x88, 'song':0x12}, + dotOrientation = 'w'), + AccessPoint('KraidRoomOut', 'Kraid', { + 'Warehouse Zeela Room Left': lambda sm: sm.canPassBombPassages() + }, boss = True, + roomInfo = {'RoomPtr':0xa56b, "area": 0x1, + # put red brin song in both pre-kraid rooms, + # (vanilla music only makes sense if kraid is + # vanilla) + "songs":[0xa57c,0xa537,0xa551]}, + exitInfo = {'DoorPtr':0x91b6, 'direction': 0x4, "cap": (0x1, 0x16), "bitFlag": 0x0, + "screen": (0x0, 0x1), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0}, + entryInfo = {'SamusX':0x1cd, 'SamusY':0x188, 'song':0x12}, + traverse=lambda sm: sm.canOpenEyeDoors(), + dotOrientation = 'e'), + AccessPoint('KraidRoomIn', 'Kraid', {}, + boss = True, + roomInfo = {'RoomPtr':0xa59f, "area": 0x1}, + exitInfo = {'DoorPtr':0x91ce, 'direction': 0x5, "cap": (0x1e, 0x16), "bitFlag": 0x0, + "screen": (0x1, 0x1), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0}, + entryInfo = {'SamusX':0x34, 'SamusY':0x188}, + dotOrientation = 'e'), + ### Norfair + AccessPoint('Warehouse Entrance Left', 'Norfair', { + 'Warehouse Entrance Right': lambda sm: sm.canAccessKraidsLair(), + 'Business Center': lambda sm: SMBool(True) + }, roomInfo = {'RoomPtr':0xa6a1, "area": 0x1}, + exitInfo = {'DoorPtr':0x922e, 'direction': 0x5, "cap": (0xe, 0x16), "bitFlag": 0x40, + "screen": (0x0, 0x1), "distanceToSpawn": 0x8000, "doorAsmPtr": 0xbdd1}, + entryInfo = {'SamusX':0x34, 'SamusY':0x88}, + dotOrientation = 'sw'), + AccessPoint('Warehouse Entrance Right', 'Norfair', { + 'Warehouse Entrance Left': Cache.ldeco(lambda sm: sm.haveItem('Super')) + }, roomInfo = {'RoomPtr': 0xa6a1, "area": 0x1}, + exitInfo = {'DoorPtr': 0x923a, 'direction': 0x4, "cap": (0x1, 0x6), "bitFlag": 0x0, + "screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX': 0x2c7, 'SamusY': 0x98}, + dotOrientation = 'nw'), + AccessPoint('Business Center', 'Norfair', { + 'Cathedral': Cache.ldeco(lambda sm: sm.canEnterCathedral(Settings.hellRunsTable['MainUpperNorfair']['Norfair Entrance -> Cathedral Missiles']['mult'])), + 'Bubble Mountain': Cache.ldeco(# go through cathedral + lambda sm: sm.wand(sm.traverse('CathedralRight'), + sm.canEnterCathedral(Settings.hellRunsTable['MainUpperNorfair']['Norfair Entrance -> Bubble']['mult']))), + 'Bubble Mountain Bottom': Cache.ldeco(lambda sm: sm.haveItem('SpeedBooster')), # frog speedway + 'Crocomire Speedway Bottom': Cache.ldeco(lambda sm: sm.wor(sm.wand(sm.haveItem('SpeedBooster'), # frog speedway + sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Norfair Entrance -> Croc via Frog w/Wave' if sm.haveItem('Wave') else 'Norfair Entrance -> Croc via Frog']), + sm.wor(sm.canBlueGateGlitch(), + sm.haveItem('Wave'))), + # below ice + sm.wand(sm.traverse('BusinessCenterTopLeft'), + sm.haveItem('SpeedBooster'), + sm.canUsePowerBombs(), + sm.canHellRun(**Settings.hellRunsTable['Ice']['Norfair Entrance -> Croc via Ice'])))), + 'Warehouse Entrance Left': lambda sm: SMBool(True) + }, internal=True, + start={'spawn':0x0208, 'doors':[0x4d], 'patches':[RomPatches.HiJumpAreaBlueDoor], 'solveArea': "Norfair Entrance", 'needsPreRando':True}), + AccessPoint('Single Chamber Top Right', 'Norfair', { + 'Bubble Mountain Top': Cache.ldeco(lambda sm: sm.wand(sm.canDestroyBombWalls(), + sm.haveItem('Morph'), + sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Single Chamber <-> Bubble Mountain']))) + }, roomInfo = {'RoomPtr':0xad5e, "area": 0x2}, + exitInfo = {'DoorPtr':0x95fa, 'direction': 0x4, "cap": (0x11, 0x6), "bitFlag": 0x0, + "screen": (0x1, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x5cf, 'SamusY':0x88}, + dotOrientation = 'ne'), + AccessPoint('Cathedral', 'Norfair', { + 'Business Center': Cache.ldeco(lambda sm: sm.canExitCathedral(Settings.hellRunsTable['MainUpperNorfair']['Bubble -> Cathedral Missiles'])), + 'Bubble Mountain': Cache.ldeco(lambda sm: sm.wand(sm.traverse('CathedralRight'), + sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Norfair Entrance -> Cathedral Missiles']))) + }, internal=True), + AccessPoint('Kronic Boost Room Bottom Left', 'Norfair', { + 'Bubble Mountain Bottom': Cache.ldeco(lambda sm: sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Single Chamber <-> Bubble Mountain'])), + 'Bubble Mountain Top': Cache.ldeco(lambda sm: sm.wand(sm.haveItem('Morph'), + sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Kronic Boost Room -> Bubble Mountain Top']))), # go all the way around + 'Crocomire Speedway Bottom': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Kronic Boost Room <-> Croc']), + sm.wor(sm.haveItem('Wave'), + sm.canBlueGateGlitch()))), + }, traverse=Cache.ldeco(lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.AreaRandoBlueDoors), sm.traverse('KronicBoostBottomLeft'))), + roomInfo = {'RoomPtr':0xae74, "area": 0x2, 'songs':[0xae85]}, + exitInfo = {'DoorPtr':0x967e, 'direction': 0x5, "cap": (0x3e, 0x6), "bitFlag": 0x0, + "screen": (0x3, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x134, 'SamusY':0x288, 'song': 0x15}, + dotOrientation = 'se'), + AccessPoint('Crocomire Speedway Bottom', 'Norfair', { + 'Business Center': Cache.ldeco(lambda sm: sm.wor(sm.wand(sm.canPassFrogSpeedwayRightToLeft(), + sm.canHellRun(**Settings.hellRunsTable['Ice']['Croc -> Norfair Entrance'])), + sm.wand(sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Croc -> Norfair Entrance']), + sm.canGrappleEscape(), + sm.haveItem('Super')))), + 'Bubble Mountain Bottom': Cache.ldeco(lambda sm: sm.canHellRun(**Settings.hellRunsTable['Ice']['Croc -> Bubble Mountain'])), + 'Kronic Boost Room Bottom Left': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Kronic Boost Room <-> Croc']), + sm.haveItem('Morph'))) + }, traverse=Cache.ldeco(lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.CrocBlueDoors), sm.traverse('CrocomireSpeedwayBottom'))), + roomInfo = {'RoomPtr':0xa923, "area": 0x2}, + exitInfo = {'DoorPtr':0x93d2, 'direction': 0x6, "cap": (0x36, 0x2), "bitFlag": 0x0, + "screen": (0x3, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0xc57, 'SamusY':0x2b8}, + dotOrientation = 'se'), + AccessPoint('Bubble Mountain', 'Norfair', { + 'Business Center': lambda sm: sm.canExitCathedral(Settings.hellRunsTable['MainUpperNorfair']['Bubble -> Norfair Entrance']), + 'Bubble Mountain Top': lambda sm: sm.canClimbBubbleMountain(), + 'Cathedral': Cache.ldeco(lambda sm: sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Bubble -> Cathedral Missiles'])), + 'Bubble Mountain Bottom': lambda sm: sm.canPassBombPassages() + }, internal=True, + start={'spawn':0x0201, 'doors':[0x54,0x55], 'patches':[RomPatches.SpeedAreaBlueDoors], 'knows':['BubbleMountainWallJump'], 'solveArea': "Bubble Norfair Bottom"}), + AccessPoint('Bubble Mountain Top', 'Norfair', { + 'Kronic Boost Room Bottom Left': Cache.ldeco(# go all the way around + lambda sm: sm.wand(sm.haveItem('Morph'), + sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Bubble -> Kronic Boost Room wo/Bomb']))), + 'Single Chamber Top Right': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Single Chamber <-> Bubble Mountain']), + sm.canDestroyBombWalls(), + sm.haveItem('Morph'), + RomPatches.has(sm.player, RomPatches.SingleChamberNoCrumble))), + 'Bubble Mountain': lambda sm: SMBool(True), + # all the way around + 'Bubble Mountain Bottom': Cache.ldeco(lambda sm: sm.wand(sm.haveItem('Morph'), + sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Bubble Top <-> Bubble Bottom']))) + }, internal=True), + AccessPoint('Bubble Mountain Bottom', 'Norfair', { + 'Bubble Mountain': lambda sm: sm.canPassBombPassages(), + 'Crocomire Speedway Bottom': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Bubble -> Croc']), + sm.wor(sm.canBlueGateGlitch(), + sm.haveItem('Wave')))), + 'Kronic Boost Room Bottom Left': Cache.ldeco(lambda sm: sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Bubble -> Kronic Boost Room'])), + 'Business Center': lambda sm: sm.canPassFrogSpeedwayRightToLeft(), + # all the way around + 'Bubble Mountain Top': Cache.ldeco(lambda sm: sm.wand(sm.haveItem('Morph'), + sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Bubble Top <-> Bubble Bottom']))) + }, internal=True), + AccessPoint('Business Center Mid Left', 'Norfair', { + 'Warehouse Entrance Left': lambda sm: SMBool(True) + }, roomInfo = {'RoomPtr':0xa7de, "area": 0x2}, + exitInfo = {'DoorPtr':0x9306, 'direction': 0x5, "cap": (0xe, 0x6), "bitFlag": 0x0, + "screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x34, 'SamusY':0x488}, + escape = True, + dotOrientation = 'ne'), + AccessPoint('Norfair Map Room', 'Norfair', { + }, roomInfo = {'RoomPtr':0xb0b4, "area": 0x2}, + exitInfo = {'DoorPtr':0x97c2, 'direction': 0x4, "cap": (0x1, 0x46), "bitFlag": 0x0, + "screen": (0x0, 0x4), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0xffff, 'SamusY':0xffff}, # unused + escape = True, + dotOrientation = 'ne'), + ### Croc + AccessPoint('Crocomire Room Top', 'Crocomire', { + }, traverse=Cache.ldeco(lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.CrocBlueDoors), sm.enoughStuffCroc())), + roomInfo = {'RoomPtr':0xa98d, "area": 0x2, 'songs':[0xa9bd]}, + exitInfo = {'DoorPtr':0x93ea, 'direction': 0x7, "cap": (0xc6, 0x2d), "bitFlag": 0x0, + "screen": (0xc, 0x2), "distanceToSpawn": 0x1c0, "doorAsmPtr": 0x0000, + "exitAsmPtr": 0xf7f0}, + entryInfo = {'SamusX':0x383, 'SamusY':0x98, 'song': 0x15}, + dotOrientation = 'se'), + ### West Maridia + AccessPoint('Main Street Bottom', 'WestMaridia', { + 'Red Fish Room Left': Cache.ldeco(lambda sm: sm.wand(sm.canGoUpMtEverest(), + sm.haveItem('Morph'))), + 'Crab Hole Bottom Left': Cache.ldeco(lambda sm: sm.wand(sm.haveItem('Morph'), + sm.canTraverseCrabTunnelLeftToRight())), + # this transition leads to EastMaridia directly + 'Oasis Bottom': Cache.ldeco(lambda sm: sm.wand(sm.wnot(RomPatches.has(sm.player, RomPatches.MaridiaSandWarp)), + sm.traverse('MainStreetBottomRight'), + sm.wor(sm.haveItem('Super'), + RomPatches.has(sm.player, RomPatches.AreaRandoGatesOther)), + sm.canTraverseWestSandHallLeftToRight())), + 'Crab Shaft Left': lambda sm: sm.canPassMtEverest() + }, roomInfo = {'RoomPtr':0xcfc9, "area": 0x4}, + exitInfo = {'DoorPtr':0xa39c, 'direction': 0x6, "cap": (0x6, 0x2), "bitFlag": 0x0, + "screen": (0x0, 0x0), "distanceToSpawn": 0x170, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x14a, 'SamusY':0x7a8}, + dotOrientation = 's'), + AccessPoint('Mama Turtle', 'WestMaridia', { + 'Main Street Bottom': lambda sm: sm.canJumpUnderwater() + }, internal=True, + start = {'spawn': 0x0406, 'solveArea': "Maridia Green", + 'save':"Save_Mama", 'needsPreRando':True, + 'patches':[RomPatches.MamaTurtleBlueDoor], + 'rom_patches':['mama_save.ips'], 'doors': [0x8e]}), + AccessPoint('Crab Hole Bottom Left', 'WestMaridia', { + 'Main Street Bottom': Cache.ldeco(lambda sm: sm.wand(sm.canExitCrabHole(), + sm.wor(sm.canGreenGateGlitch(), + RomPatches.has(sm.player, RomPatches.AreaRandoGatesOther)))), + # this transition leads to EastMaridia directly + 'Oasis Bottom': Cache.ldeco(lambda sm: sm.wand(sm.wnot(RomPatches.has(sm.player, RomPatches.MaridiaSandWarp)), + sm.canExitCrabHole(), + sm.canTraverseWestSandHallLeftToRight())) + }, roomInfo = {'RoomPtr':0xd21c, "area": 0x4}, + exitInfo = {'DoorPtr':0xa510, 'direction': 0x5, + "cap": (0x3e, 0x6), "screen": (0x3, 0x0), "bitFlag": 0x0, + "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x28, 'SamusY':0x188}, + dotOrientation = 'se'), + AccessPoint('Red Fish Room Left', 'WestMaridia', { + 'Main Street Bottom': Cache.ldeco(lambda sm: sm.haveItem('Morph')) # just go down + }, roomInfo = {'RoomPtr':0xd104, "area": 0x4}, + exitInfo = {'DoorPtr':0xa480, 'direction': 0x5, "cap": (0x2e, 0x36), "bitFlag": 0x40, + "screen": (0x2, 0x3), "distanceToSpawn": 0x8000, "doorAsmPtr": 0xe367}, + entryInfo = {'SamusX':0x34, 'SamusY':0x88}, + dotOrientation = 'w'), + AccessPoint('Crab Shaft Left', 'WestMaridia', { + 'Main Street Bottom': lambda sm: SMBool(True), # fall down + 'Beach': lambda sm: sm.canDoOuterMaridia(), + 'Crab Shaft Right': lambda sm: SMBool(True) + }, internal=True), + AccessPoint('Watering Hole', 'WestMaridia', { + 'Beach': lambda sm: sm.haveItem('Morph'), + 'Watering Hole Bottom': lambda sm: SMBool(True) + }, internal=True, + start = {'spawn': 0x0407, 'solveArea': "Maridia Pink Bottom", 'save':"Save_Watering_Hole", + 'patches':[RomPatches.MaridiaTubeOpened], 'rom_patches':['wh_open_tube.ips'], + 'forcedEarlyMorph':True}), + AccessPoint('Watering Hole Bottom', 'WestMaridia', { + 'Watering Hole': lambda sm: sm.canJumpUnderwater() + }, internal=True), + AccessPoint('Beach', 'WestMaridia', { + 'Crab Shaft Left': lambda sm: SMBool(True), # fall down + 'Watering Hole': Cache.ldeco(lambda sm: sm.wand(sm.wor(sm.canPassBombPassages(), + sm.canUseSpringBall()), + sm.canDoOuterMaridia())) + }, internal=True), + AccessPoint('Crab Shaft Right', 'WestMaridia', { + 'Crab Shaft Left': lambda sm: sm.canJumpUnderwater() + }, traverse=Cache.ldeco(lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.CrabShaftBlueDoor), + sm.traverse('CrabShaftRight'))), + roomInfo = {'RoomPtr':0xd1a3, "area": 0x4}, + exitInfo = {'DoorPtr':0xa4c8, 'direction': 0x4, "cap": (0x1, 0x16), "bitFlag": 0x0, + "screen": (0x0, 0x1), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0}, + entryInfo = {'SamusX':0x1ca, 'SamusY':0x388}, + dotOrientation = 'e'), + # escape APs + AccessPoint('Crab Hole Bottom Right', 'WestMaridia', { + 'Crab Hole Bottom Left': lambda sm: SMBool(True) + }, roomInfo = {'RoomPtr':0xd21c, "area": 0x4}, + exitInfo = {'DoorPtr':0xa51c, 'direction': 0x4, "cap": (0x1, 0x6), "bitFlag": 0x0, + "screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0xd7, 'SamusY':0x188}, + escape = True, + dotOrientation = 'ne'), + AccessPoint('Maridia Map Room', 'WestMaridia', { + }, roomInfo = {'RoomPtr':0xd3b6, "area": 0x4}, + exitInfo = {'DoorPtr':0xa5e8, 'direction': 0x5, "cap": (0xe, 0x16), "bitFlag": 0x0, + "screen": (0x0, 0x1), "distanceToSpawn": 0x8000, "doorAsmPtr": 0xe356}, + entryInfo = {'SamusX':0xffff, 'SamusY':0xffff}, # unused + escape = True, + dotOrientation = 'ne'), + ### East Maridia + AccessPoint('Aqueduct Top Left', 'EastMaridia', { + 'Aqueduct Bottom': lambda sm: sm.canUsePowerBombs() + }, roomInfo = {'RoomPtr':0xd5a7, "area": 0x4}, + exitInfo = {'DoorPtr':0xa708, 'direction': 0x5, "cap": (0x1e, 0x36), "bitFlag": 0x0, + "screen": (0x1, 0x3), "distanceToSpawn": 0x8000, "doorAsmPtr": 0xe398}, + entryInfo = {'SamusX':0x34, 'SamusY':0x188}, + dotOrientation = 'w'), + AccessPoint('Aqueduct Bottom', 'EastMaridia', { + 'Aqueduct Top Left': Cache.ldeco(lambda sm: sm.wand(sm.canDestroyBombWallsUnderwater(), # top left bomb blocks + sm.canJumpUnderwater())), + 'Post Botwoon': Cache.ldeco(lambda sm: sm.wand(sm.canJumpUnderwater(), + sm.canDefeatBotwoon())), # includes botwoon hallway conditions + 'Left Sandpit': lambda sm: sm.canAccessSandPits(), + 'Right Sandpit': lambda sm: sm.canAccessSandPits(), + 'Aqueduct': Cache.ldeco(lambda sm: sm.wand(sm.wor(sm.haveItem('SpeedBooster'), + sm.wand(sm.knowsSnailClip(), + sm.haveItem('Morph'))), + sm.haveItem('Gravity'))) + }, internal=True), + AccessPoint('Aqueduct', 'EastMaridia', { + 'Aqueduct Bottom': lambda sm: SMBool(True) # go down + }, internal=True, + start = {'spawn': 0x0405, 'solveArea': "Maridia Pink Bottom", + 'save':"Save_Aqueduct", 'needsPreRando':True, + 'doors': [0x96]}), + AccessPoint('Post Botwoon', 'EastMaridia', { + 'Aqueduct Bottom': Cache.ldeco(lambda sm: sm.wor(sm.wand(sm.canJumpUnderwater(), # can't access the sand pits from the right side of the room + sm.haveItem('Morph')), + sm.wand(sm.haveItem('Gravity'), + sm.haveItem('SpeedBooster')))), + 'Colosseum Top Right': lambda sm: sm.canBotwoonExitToColosseum(), + 'Toilet Top': Cache.ldeco(lambda sm: sm.wand(sm.canReachCacatacAlleyFromBotowoon(), + sm.canPassCacatacAlley())) + }, internal=True), + AccessPoint('West Sand Hall Left', 'EastMaridia', { + # XXX there might be some tech to do this suitless, but HJ+ice is not enough + 'Oasis Bottom': Cache.ldeco(lambda sm: sm.haveItem('Gravity')), + 'Aqueduct Bottom': Cache.ldeco(lambda sm: RomPatches.has(sm.player, RomPatches.MaridiaSandWarp)), + # this goes directly to WestMaridia + 'Main Street Bottom': Cache.ldeco(lambda sm: sm.wand(sm.wnot(RomPatches.has(sm.player, RomPatches.MaridiaSandWarp)), + sm.wor(sm.canGreenGateGlitch(), + RomPatches.has(sm.player, RomPatches.AreaRandoGatesOther)))), + # this goes directly to WestMaridia + 'Crab Hole Bottom Left': Cache.ldeco(lambda sm: sm.wand(sm.wnot(RomPatches.has(sm.player, RomPatches.MaridiaSandWarp)), + sm.haveItem('Morph'))) + }, internal=True), + AccessPoint('Left Sandpit', 'EastMaridia', { + 'West Sand Hall Left': lambda sm: sm.canAccessSandPits(), + 'Oasis Bottom': lambda sm: sm.canAccessSandPits() + }, internal=True), + AccessPoint('Oasis Bottom', 'EastMaridia', { + 'Toilet Top': Cache.ldeco(lambda sm: sm.wand(sm.traverse('OasisTop'), sm.canDestroyBombWallsUnderwater())), + 'West Sand Hall Left': lambda sm: sm.canAccessSandPits() + }, internal=True), + AccessPoint('Right Sandpit', 'EastMaridia', { + 'Oasis Bottom': lambda sm: sm.canAccessSandPits() + }, internal=True), + AccessPoint('Le Coude Right', 'EastMaridia', { + 'Toilet Top': lambda sm: SMBool(True) + }, roomInfo = {'RoomPtr':0x95a8, "area": 0x0}, + exitInfo = {'DoorPtr':0x8aa2, 'direction': 0x4, "cap": (0x1, 0x16), "bitFlag": 0x0, + "screen": (0x0, 0x1), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0xd1, 'SamusY':0x88}, + dotOrientation = 'ne'), + AccessPoint('Toilet Top', 'EastMaridia', { + 'Oasis Bottom': Cache.ldeco(lambda sm: sm.wand(sm.traverse('PlasmaSparkBottom'), sm.canDestroyBombWallsUnderwater())), + 'Le Coude Right': lambda sm: SMBool(True), + 'Colosseum Top Right': Cache.ldeco(lambda sm: sm.wand(Bosses.bossDead(sm, 'Draygon'), + # suitless could be possible with this but unreasonable: https://youtu.be/rtLwytH-u8o + sm.haveItem('Gravity'), + sm.haveItem('Morph'))) + }, internal=True), + AccessPoint('Colosseum Top Right', 'EastMaridia', { + 'Post Botwoon': lambda sm: sm.canColosseumToBotwoonExit(), + 'Precious Room Top': Cache.ldeco(lambda sm: sm.traverse('ColosseumBottomRight')), # go down + }, internal = True), + AccessPoint('Precious Room Top', 'EastMaridia', { + 'Colosseum Top Right': lambda sm: sm.canClimbColosseum(), + 'DraygonRoomOut': lambda sm: SMBool(True) # go down + }, internal = True), + # boss APs + AccessPoint('DraygonRoomOut', 'EastMaridia', { + 'Precious Room Top': lambda sm: sm.canExitPreciousRoom() + }, boss = True, + roomInfo = {'RoomPtr':0xd78f, "area": 0x4, "songs":[0xd7a5]}, + exitInfo = {'DoorPtr':0xa840, 'direction': 0x5, "cap": (0x1e, 0x6), "bitFlag": 0x0, + "screen": (0x1, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0}, + entryInfo = {'SamusX':0x34, 'SamusY':0x288, 'song':0x1b}, + traverse=lambda sm: sm.canOpenEyeDoors(), + dotOrientation = 'e'), + AccessPoint('DraygonRoomIn', 'EastMaridia', { + 'Draygon Room Bottom': Cache.ldeco(lambda sm: sm.wor(Bosses.bossDead(sm, "Draygon"), + sm.wand(sm.canFightDraygon(), + sm.enoughStuffsDraygon()))) + }, boss = True, + roomInfo = {'RoomPtr':0xda60, "area": 0x4}, + exitInfo = {'DoorPtr':0xa96c, 'direction': 0x4, "cap": (0x1, 0x26), "bitFlag": 0x0, + "screen": (0x0, 0x2), "distanceToSpawn": 0x8000, "doorAsmPtr": 0xe3d9, + "exitAsmPtr": 0xf7f0}, + entryInfo = {'SamusX':0x1c8, 'SamusY':0x88}, + dotOrientation = 'e'), + AccessPoint('Draygon Room Bottom', 'EastMaridia', { + 'DraygonRoomIn': Cache.ldeco(lambda sm: sm.wand(Bosses.bossDead(sm, 'Draygon'), sm.canExitDraygon())) + }, internal = True), + ### Red Brinstar. Main nodes: Red Tower Top Left, East Tunnel Right + AccessPoint('Red Tower Top Left', 'RedBrinstar', { + # go up + 'Red Brinstar Elevator': lambda sm: sm.canClimbRedTower(), + 'Caterpillar Room Top Right': Cache.ldeco(lambda sm: sm.wand(sm.canPassRedTowerToMaridiaNode(), + sm.canClimbRedTower())), + # go down + 'East Tunnel Right': lambda sm: SMBool(True) + }, roomInfo = {'RoomPtr':0xa253, "area": 0x1}, + exitInfo = {'DoorPtr':0x902a, 'direction': 0x5, "cap": (0x5e, 0x6), "bitFlag": 0x0, + "screen": (0x5, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x2f, 'SamusY':0x488}, + dotOrientation = 'w'), + AccessPoint('Caterpillar Room Top Right', 'RedBrinstar', { + 'Red Brinstar Elevator': lambda sm: sm.canPassMaridiaToRedTowerNode() + }, roomInfo = {'RoomPtr':0xa322, "area": 0x1}, + exitInfo = {'DoorPtr':0x90c6, 'direction': 0x4, "cap": (0x1, 0x6), "bitFlag": 0x40, + "screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0xbdaf}, + entryInfo = {'SamusX':0x2cd, 'SamusY':0x388}, + dotOrientation = 'ne'), + AccessPoint('Red Brinstar Elevator', 'RedBrinstar', { + 'Caterpillar Room Top Right': lambda sm: sm.canPassRedTowerToMaridiaNode(), + 'Red Tower Top Left': Cache.ldeco(lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.HellwayBlueDoor), sm.traverse('RedTowerElevatorLeft'))) + }, traverse=Cache.ldeco(lambda sm:sm.wor(RomPatches.has(sm.player, RomPatches.RedTowerBlueDoors), sm.traverse('RedBrinstarElevatorTop'))), + roomInfo = {'RoomPtr':0x962a, "area": 0x0}, + exitInfo = {'DoorPtr':0x8af6, 'direction': 0x7, "cap": (0x16, 0x2d), "bitFlag": 0x0, + "screen": (0x1, 0x2), "distanceToSpawn": 0x1c0, "doorAsmPtr": 0xb9f1}, + entryInfo = {'SamusX':0x80, 'SamusY':0x58}, + start={'spawn':0x010a, 'doors':[0x3c], 'patches':[RomPatches.HellwayBlueDoor], 'solveArea': "Red Brinstar Top", 'areaMode':True}, + dotOrientation = 'n'), + AccessPoint('East Tunnel Right', 'RedBrinstar', { + 'East Tunnel Top Right': lambda sm: SMBool(True), # handled by room traverse function + 'Glass Tunnel Top': Cache.ldeco(lambda sm: sm.wand(sm.canUsePowerBombs(), + sm.wor(sm.haveItem('Gravity'), + sm.haveItem('HiJump')))), + 'Red Tower Top Left': lambda sm: sm.canClimbBottomRedTower() + }, roomInfo = {'RoomPtr':0xcf80, "area": 0x4}, + exitInfo = {'DoorPtr':0xa384, 'direction': 0x4, "cap": (0x1, 0x6), "bitFlag": 0x40, + "screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0xce, 'SamusY':0x188}, + dotOrientation = 'se'), + AccessPoint('East Tunnel Top Right', 'RedBrinstar', { + 'East Tunnel Right': Cache.ldeco(lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.AreaRandoGatesBase), + sm.haveItem('Super'))) + }, traverse=Cache.ldeco(lambda sm: RomPatches.has(sm.player, RomPatches.AreaRandoGatesBase)), + roomInfo = {'RoomPtr':0xcf80, "area": 0x4}, + exitInfo = {'DoorPtr':0xa390, 'direction': 0x4, "cap": (0x1, 0x16), "bitFlag": 0x0, + "screen": (0x0, 0x1), "distanceToSpawn": 0x8000, "doorAsmPtr": 0xe356}, + entryInfo = {'SamusX':0x3c6, 'SamusY':0x88}, + dotOrientation = 'e'), + AccessPoint('Glass Tunnel Top', 'RedBrinstar', { + 'East Tunnel Right': Cache.ldeco(lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.MaridiaTubeOpened), + sm.canUsePowerBombs())) + }, traverse=Cache.ldeco(lambda sm: sm.wand(sm.wor(sm.haveItem('Gravity'), + sm.haveItem('HiJump')), + sm.wor(RomPatches.has(sm.player, RomPatches.MaridiaTubeOpened), + sm.canUsePowerBombs()))), + roomInfo = {'RoomPtr':0xcefb, "area": 0x4}, + exitInfo = {'DoorPtr':0xa330, 'direction': 0x7, "cap": (0x16, 0x7d), "bitFlag": 0x0, + "screen": (0x1, 0x7), "distanceToSpawn": 0x200, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x81, 'SamusY':0x78}, + dotOrientation = 's'), + ### Tourian + AccessPoint('Golden Four', 'Tourian', {}, + roomInfo = {'RoomPtr':0xa5ed, "area": 0x0}, + exitInfo = {'DoorPtr':0x91e6, 'direction': 0x5, "cap": (0xe, 0x66), "bitFlag": 0x0, + "screen": (0x0, 0x6), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000}, + entryInfo = {'SamusX':0x34, 'SamusY':0x88}, + start={'spawn':0x0007, 'solveArea': "Tourian", "save": "Save_G4", 'areaMode':True}, + dotOrientation = 'w'), + AccessPoint('Tourian Escape Room 4 Top Right', 'Tourian', {}, + roomInfo = {'RoomPtr':0xdede, "area": 0x5}, + exitInfo = {'DoorPtr':0xab34, 'direction': 0x4, "cap": (0x1, 0x86), "bitFlag": 0x40, + "screen": (0x0, 0x8), "distanceToSpawn": 0x8000, "doorAsmPtr": 0xe4cf}, + entryInfo = {'SamusX':0xffff, 'SamusY':0xffff}, # unused + escape = True, + dotOrientation = 'ne'), +] diff --git a/worlds/sm/variaRandomizer/graph/vanilla/graph_helpers.py b/worlds/sm/variaRandomizer/graph/vanilla/graph_helpers.py new file mode 100644 index 00000000..f189a476 --- /dev/null +++ b/worlds/sm/variaRandomizer/graph/vanilla/graph_helpers.py @@ -0,0 +1,766 @@ +from math import ceil + +from logic.smbool import SMBool +from logic.helpers import Helpers, Bosses +from logic.cache import Cache +from rom.rom_patches import RomPatches +from graph.graph_utils import getAccessPoint +from utils.parameters import Settings + +class HelpersGraph(Helpers): + def __init__(self, smbm): + self.smbm = smbm + + def canEnterAndLeaveGauntletQty(self, nPB, nTanksSpark): + sm = self.smbm + # EXPLAINED: to access Gauntlet Entrance from Landing site we can either: + # -fly to it (infinite bomb jumps or space jump) + # -shinespark to it + # -wall jump with high jump boots + # -wall jump without high jump boots + # then inside it to break the bomb wals: + # -use screw attack (easy way) + # -use power bombs + # -use bombs + # -perform a simple short charge on the way in + # and use power bombs on the way out + return sm.wand(sm.wor(sm.canFly(), + sm.haveItem('SpeedBooster'), + sm.wand(sm.knowsHiJumpGauntletAccess(), + sm.haveItem('HiJump')), + sm.knowsHiJumpLessGauntletAccess()), + sm.wor(sm.haveItem('ScrewAttack'), + sm.wor(sm.wand(sm.energyReserveCountOkHardRoom('Gauntlet'), + sm.wand(sm.canUsePowerBombs(), + sm.wor(sm.itemCountOk('PowerBomb', nPB), + sm.wand(sm.haveItem('SpeedBooster'), + sm.energyReserveCountOk(nTanksSpark))))), + sm.wand(sm.energyReserveCountOkHardRoom('Gauntlet', 0.51), + sm.canUseBombs())))) + + @Cache.decorator + def canEnterAndLeaveGauntlet(self): + sm = self.smbm + return sm.wor(sm.wand(sm.canShortCharge(), + sm.canEnterAndLeaveGauntletQty(2, 2)), + sm.canEnterAndLeaveGauntletQty(2, 3)) + + def canPassTerminatorBombWall(self, fromLandingSite=True): + sm = self.smbm + return sm.wor(sm.wand(sm.haveItem('SpeedBooster'), + sm.wor(SMBool(not fromLandingSite, 0), sm.knowsSimpleShortCharge(), sm.knowsShortCharge())), + sm.canDestroyBombWalls()) + + @Cache.decorator + def canPassCrateriaGreenPirates(self): + sm = self.smbm + return sm.wor(sm.canPassBombPassages(), + sm.haveMissileOrSuper(), + sm.energyReserveCountOk(1), + sm.wor(sm.haveItem('Charge'), + sm.haveItem('Ice'), + sm.haveItem('Wave'), + sm.wor(sm.haveItem('Spazer'), + sm.haveItem('Plasma'), + sm.haveItem('ScrewAttack')))) + + # from blue brin elevator + @Cache.decorator + def canAccessBillyMays(self): + sm = self.smbm + return sm.wand(sm.wor(RomPatches.has(sm.player, RomPatches.BlueBrinstarBlueDoor), + sm.traverse('ConstructionZoneRight')), + sm.canUsePowerBombs(), + sm.wor(sm.knowsBillyMays(), + sm.haveItem('Gravity'), + sm.haveItem('SpaceJump'))) + + @Cache.decorator + def canAccessKraidsLair(self): + sm = self.smbm + # EXPLAINED: access the upper right platform with either: + # -hijump boots (easy regular way) + # -fly (space jump or infinite bomb jump) + # -know how to wall jump on the platform without the hijump boots + return sm.wand(sm.haveItem('Super'), + sm.wor(sm.haveItem('HiJump'), + sm.canFly(), + sm.knowsEarlyKraid())) + + @Cache.decorator + def canPassMoat(self): + sm = self.smbm + # EXPLAINED: In the Moat we can either: + # -use grapple or space jump (easy way) + # -do a continuous wall jump (https://www.youtube.com/watch?v=4HVhTwwax6g) + # -do a diagonal bomb jump from the middle platform (https://www.youtube.com/watch?v=5NRqQ7RbK3A&t=10m58s) + # -do a short charge from the Keyhunter room (https://www.youtube.com/watch?v=kFAYji2gFok) + # -do a gravity jump from below the right platform + # -do a mock ball and a bounce ball (https://www.youtube.com/watch?v=WYxtRF--834) + # -with gravity, either hijump or IBJ + return sm.wor(sm.haveItem('Grapple'), + sm.haveItem('SpaceJump'), + sm.knowsContinuousWallJump(), + sm.wand(sm.knowsDiagonalBombJump(), sm.canUseBombs()), + sm.canSimpleShortCharge(), + sm.wand(sm.haveItem('Gravity'), + sm.wor(sm.knowsGravityJump(), + sm.haveItem('HiJump'), + sm.canInfiniteBombJump())), + sm.wand(sm.knowsMockballWs(), sm.canUseSpringBall())) + + @Cache.decorator + def canPassMoatFromMoat(self): + sm = self.smbm + return sm.wor(sm.haveItem('Grapple'), + sm.haveItem('SpaceJump'), + sm.wand(sm.knowsDiagonalBombJump(), sm.canUseBombs()), + sm.wand(sm.haveItem('Gravity'), + sm.wor(sm.knowsGravityJump(), + sm.haveItem('HiJump'), + sm.canInfiniteBombJump()))) + + @Cache.decorator + def canPassMoatReverse(self): + sm = self.smbm + return sm.wor(RomPatches.has(sm.player, RomPatches.MoatShotBlock), + sm.haveItem('Grapple'), + sm.haveItem('SpaceJump'), + sm.haveItem('Gravity'), + sm.canPassBombPassages()) + + @Cache.decorator + def canPassSpongeBath(self): + sm = self.smbm + return sm.wor(sm.wand(sm.canPassBombPassages(), + sm.knowsSpongeBathBombJump()), + sm.wand(sm.haveItem('HiJump'), + sm.knowsSpongeBathHiJump()), + sm.haveItem('Gravity'), + sm.haveItem('SpaceJump'), + sm.wand(sm.haveItem('SpeedBooster'), + sm.knowsSpongeBathSpeed()), + sm.canSpringBallJump()) + + @Cache.decorator + def canPassBowling(self): + sm = self.smbm + return sm.wand(Bosses.bossDead(sm, 'Phantoon'), + sm.wor(SMBool(sm.getDmgReduction()[0] >= 2), + sm.energyReserveCountOk(1), + sm.haveItem("SpaceJump"), + sm.haveItem("Grapple"))) + + @Cache.decorator + def canAccessEtecoons(self): + sm = self.smbm + return sm.wor(sm.canUsePowerBombs(), + sm.wand(sm.knowsMoondance(), sm.canUseBombs(), sm.traverse('MainShaftBottomRight'))) + + @Cache.decorator + def canKillBeetoms(self): + sm = self.smbm + # can technically be killed with bomb, but it's harder + return sm.wor(sm.haveMissileOrSuper(), sm.canUsePowerBombs(), sm.haveItem('ScrewAttack')) + + # the water zone east of WS + def canPassForgottenHighway(self, fromWs): + sm = self.smbm + suitless = sm.wand(sm.haveItem('HiJump'), sm.knowsGravLessLevel1()) + if fromWs is True and RomPatches.has(sm.player, RomPatches.EastOceanPlatforms).bool is False: + suitless = sm.wand(suitless, + sm.wor(sm.canSpringBallJump(), # two sbj on the far right + # to break water line and go through the door on the right + sm.haveItem('SpaceJump'))) + return sm.wand(sm.wor(sm.haveItem('Gravity'), + suitless), + sm.haveItem('Morph')) # for crab maze + + @Cache.decorator + def canExitCrabHole(self): + sm = self.smbm + return sm.wand(sm.haveItem('Morph'), # morph to exit the hole + sm.wor(sm.wand(sm.haveItem('Gravity'), # even with gravity you need some way to climb... + sm.wor(sm.haveItem('Ice'), # ...on crabs... + sm.wand(sm.haveItem('HiJump'), sm.knowsMaridiaWallJumps()), # ...or by jumping + sm.knowsGravityJump(), + sm.canFly())), + sm.wand(sm.haveItem('Ice'), sm.canDoSuitlessOuterMaridia()), # climbing crabs + sm.canDoubleSpringBallJump())) + + # bottom sandpits with the evirs except west sand hall left to right + @Cache.decorator + def canTraverseSandPits(self): + sm = self.smbm + return sm.wor(sm.haveItem('Gravity'), + sm.wand(sm.knowsGravLessLevel3(), + sm.haveItem('HiJump'), + sm.haveItem('Ice'))) + + @Cache.decorator + def canTraverseWestSandHallLeftToRight(self): + sm = self.smbm + return sm.haveItem('Gravity') # FIXME find suitless condition + + @Cache.decorator + def canPassMaridiaToRedTowerNode(self): + sm = self.smbm + return sm.wand(sm.haveItem('Morph'), + sm.wor(RomPatches.has(sm.player, RomPatches.AreaRandoGatesBase), + sm.haveItem('Super'))) + + @Cache.decorator + def canPassRedTowerToMaridiaNode(self): + sm = self.smbm + return sm.wand(sm.haveItem('Morph'), + RomPatches.has(sm.player, RomPatches.AreaRandoGatesBase)) + + def canEnterCathedral(self, mult=1.0): + sm = self.smbm + return sm.wand(sm.traverse('CathedralEntranceRight'), + sm.wor(sm.wand(sm.canHellRun('MainUpperNorfair', mult), + sm.wor(sm.wor(RomPatches.has(sm.player, RomPatches.CathedralEntranceWallJump), + sm.haveItem('HiJump'), + sm.canFly()), + sm.wor(sm.haveItem('SpeedBooster'), # spark + sm.canSpringBallJump()))), + sm.wand(sm.canHellRun('MainUpperNorfair', 0.5*mult), + sm.haveItem('Morph'), + sm.knowsNovaBoost()))) + + @Cache.decorator + def canClimbBubbleMountain(self): + sm = self.smbm + return sm.wor(sm.haveItem('HiJump'), + sm.canFly(), + sm.haveItem('Ice'), + sm.knowsBubbleMountainWallJump()) + + @Cache.decorator + def canHellRunToSpeedBooster(self): + sm = self.smbm + return sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Bubble -> Speed Booster w/Speed' if sm.haveItem('SpeedBooster') else 'Bubble -> Speed Booster']) + + @Cache.decorator + def canAccessDoubleChamberItems(self): + sm = self.smbm + hellRun = Settings.hellRunsTable['MainUpperNorfair']['Bubble -> Wave'] + return sm.wor(sm.wand(sm.traverse('SingleChamberRight'), + sm.canHellRun(**hellRun)), + sm.wand(sm.wor(sm.haveItem('HiJump'), + sm.canSimpleShortCharge(), + sm.canFly(), + sm.knowsDoubleChamberWallJump()), + sm.canHellRun(hellRun['hellRun'], hellRun['mult']*0.8, hellRun['minE']))) + + def canExitCathedral(self, hellRun): + # from top: can use bomb/powerbomb jumps + # from bottom: can do a shinespark or use space jump + # can do it with highjump + wall jump + # can do it with only two wall jumps (the first one is delayed like on alcatraz) + # can do it with a spring ball jump from wall + sm = self.smbm + return sm.wand(sm.wor(sm.canHellRun(**hellRun), + sm.heatProof()), + sm.wor(sm.wor(sm.canPassBombPassages(), + sm.haveItem("SpeedBooster")), + sm.wor(sm.haveItem("SpaceJump"), + sm.haveItem("HiJump"), + sm.knowsWallJumpCathedralExit(), + sm.wand(sm.knowsSpringBallJumpFromWall(), sm.canUseSpringBall())))) + + @Cache.decorator + def canGrappleEscape(self): + sm = self.smbm + return sm.wor(sm.wor(sm.haveItem('SpaceJump'), + sm.wand(sm.canInfiniteBombJump(), # IBJ from lava...either have grav or freeze the enemy there if hellrunning (otherwise single DBJ at the end) + sm.wor(sm.heatProof(), + sm.haveItem('Gravity'), + sm.haveItem('Ice')))), + sm.haveItem('Grapple'), + sm.wand(sm.haveItem('SpeedBooster'), + sm.wor(sm.haveItem('HiJump'), # jump from the blocks below + sm.knowsShortCharge())), # spark from across the grapple blocks + sm.wand(sm.haveItem('HiJump'), sm.canSpringBallJump())) # jump from the blocks below + + @Cache.decorator + def canPassFrogSpeedwayRightToLeft(self): + sm = self.smbm + return sm.wor(sm.haveItem('SpeedBooster'), + sm.wand(sm.knowsFrogSpeedwayWithoutSpeed(), + sm.haveItem('Wave'), + sm.wor(sm.haveItem('Spazer'), + sm.haveItem('Plasma')))) + + @Cache.decorator + def canEnterNorfairReserveAreaFromBubbleMoutain(self): + sm = self.smbm + return sm.wand(sm.traverse('BubbleMountainTopLeft'), + sm.wor(sm.canFly(), + sm.haveItem('Ice'), + sm.wand(sm.haveItem('HiJump'), + sm.knowsGetAroundWallJump()), + sm.wand(sm.canUseSpringBall(), + sm.knowsSpringBallJumpFromWall()))) + + @Cache.decorator + def canEnterNorfairReserveAreaFromBubbleMoutainTop(self): + sm = self.smbm + return sm.wand(sm.traverse('BubbleMountainTopLeft'), + sm.wor(sm.haveItem('Grapple'), + sm.haveItem('SpaceJump'), + sm.knowsNorfairReserveDBoost())) + + @Cache.decorator + def canPassLavaPit(self): + sm = self.smbm + nTanks4Dive = 8 / sm.getDmgReduction()[0] + if sm.haveItem('HiJump').bool == False: + nTanks4Dive = ceil(nTanks4Dive * 1.25) + return sm.wand(sm.wor(sm.wand(sm.haveItem('Gravity'), sm.haveItem('SpaceJump')), + sm.wand(sm.knowsGravityJump(), sm.haveItem('Gravity'), sm.wor(sm.haveItem('HiJump'), sm.knowsLavaDive())), + sm.wand(sm.wor(sm.wand(sm.knowsLavaDive(), sm.haveItem('HiJump')), + sm.knowsLavaDiveNoHiJump()), + sm.energyReserveCountOk(nTanks4Dive))), + sm.canUsePowerBombs()) # power bomb blocks left and right of LN entrance without any items before + + @Cache.decorator + def canPassLavaPitReverse(self): + sm = self.smbm + nTanks = 2 + if sm.heatProof().bool == False: + nTanks = 6 + return sm.energyReserveCountOk(nTanks) + + @Cache.decorator + def canPassLowerNorfairChozo(self): + sm = self.smbm + # to require one more CF if no heat protection because of distance to cover, wait times, acid... + return sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Entrance -> GT via Chozo']), + sm.canUsePowerBombs(), + sm.wor(RomPatches.has(sm.player, RomPatches.LNChozoSJCheckDisabled), sm.haveItem('SpaceJump'))) + + @Cache.decorator + def canExitScrewAttackArea(self): + sm = self.smbm + + return sm.wand(sm.canDestroyBombWalls(), + sm.wor(sm.canFly(), + sm.wand(sm.haveItem('HiJump'), + sm.haveItem('SpeedBooster'), + sm.wor(sm.wand(sm.haveItem('ScrewAttack'), sm.knowsScrewAttackExit()), + sm.knowsScrewAttackExitWithoutScrew())), + sm.wand(sm.canUseSpringBall(), + sm.knowsSpringBallJumpFromWall()), + sm.wand(sm.canSimpleShortCharge(), # fight GT and spark out + sm.enoughStuffGT()))) + + @Cache.decorator + def canPassWorstRoom(self): + sm = self.smbm + return sm.wand(sm.canDestroyBombWalls(), + sm.canPassWorstRoomPirates(), + sm.wor(sm.canFly(), + sm.wand(sm.knowsWorstRoomIceCharge(), sm.haveItem('Ice'), sm.canFireChargedShots()), + sm.wor(sm.wand(sm.knowsGetAroundWallJump(), sm.haveItem('HiJump')), + sm.knowsWorstRoomWallJump()), + sm.wand(sm.knowsSpringBallJumpFromWall(), sm.canUseSpringBall()))) + + # checks mix of super missiles/health + def canGoThroughLowerNorfairEnemy(self, nmyHealth, nbNmy, nmyHitDmg, supDmg=300.0): + sm = self.smbm + # supers only + if sm.itemCount('Super')*5*supDmg >= nbNmy*nmyHealth: + return SMBool(True, 0, items=['Super']) + + # - or with taking damage as well? + (dmgRed, redItems) = sm.getDmgReduction(envDmg=False) + dmg = nmyHitDmg / dmgRed + if sm.heatProof() and (sm.itemCount('Super')*5*supDmg)/nmyHealth + (sm.energyReserveCount()*100 - 2)/dmg >= nbNmy: + # require heat proof as long as taking damage is necessary. + # display all the available energy in the solver. + return sm.wand(sm.heatProof(), SMBool(True, 0, items=redItems+['Super', '{}-ETank - {}-Reserve'.format(self.smbm.itemCount('ETank'), self.smbm.itemCount('Reserve'))])) + + return sm.knowsDodgeLowerNorfairEnemies() + + def canKillRedKiHunters(self, n): + sm = self.smbm + return sm.wor(sm.haveItem('Plasma'), + sm.haveItem('ScrewAttack'), + sm.wand(sm.heatProof(), # this takes a loooong time ... + sm.wor(sm.haveItem('Spazer'), + sm.haveItem('Ice'), + sm.wand(sm.haveItem('Charge'), + sm.haveItem('Wave')))), + sm.canGoThroughLowerNorfairEnemy(1800.0, float(n), 200.0)) + + @Cache.decorator + def canPassThreeMuskateers(self): + sm = self.smbm + return sm.canKillRedKiHunters(6) + + @Cache.decorator + def canPassRedKiHunters(self): + sm = self.smbm + return sm.canKillRedKiHunters(3) + + @Cache.decorator + def canPassWastelandDessgeegas(self): + sm = self.smbm + return sm.wor(sm.haveItem('Plasma'), + sm.haveItem('ScrewAttack'), + sm.wand(sm.heatProof(), # this takes a loooong time ... + sm.wor(sm.haveItem('Spazer'), + sm.wand(sm.haveItem('Charge'), + sm.haveItem('Wave')))), + sm.itemCountOk('PowerBomb', 4), + sm.canGoThroughLowerNorfairEnemy(800.0, 3.0, 160.0)) + + @Cache.decorator + def canPassNinjaPirates(self): + sm = self.smbm + return sm.wor(sm.itemCountOk('Missile', 10), + sm.itemCountOk('Super', 2), + sm.haveItem('Plasma'), + sm.wor(sm.haveItem('Spazer'), + sm.wand(sm.haveItem('Charge'), + sm.wor(sm.haveItem('Wave'), + sm.haveItem('Ice')))), + sm.canShortCharge()) # echoes kill + + @Cache.decorator + def canPassWorstRoomPirates(self): + sm = self.smbm + return sm.wor(sm.haveItem('ScrewAttack'), + sm.itemCountOk('Missile', 6), + sm.itemCountOk('Super', 3), + sm.wand(sm.canFireChargedShots(), sm.haveItem('Plasma')), + sm.wand(sm.haveItem('Charge'), + sm.wor(sm.haveItem('Spazer'), + sm.haveItem('Wave'), + sm.haveItem('Ice'))), + sm.knowsDodgeLowerNorfairEnemies()) + + # go though the pirates room filled with acid + @Cache.decorator + def canPassAmphitheaterReverse(self): + sm = self.smbm + dmgRed = sm.getDmgReduction()[0] + nTanksGrav = 4 * 4/dmgRed + nTanksNoGrav = 6 * 4/dmgRed + return sm.wor(sm.wand(sm.haveItem('Gravity'), + sm.energyReserveCountOk(nTanksGrav)), + sm.wand(sm.energyReserveCountOk(nTanksNoGrav), + sm.knowsLavaDive())) # should be a good enough skill filter for acid wall jumps with no grav... + + @Cache.decorator + def canGetBackFromRidleyZone(self): + sm = self.smbm + return sm.wand(sm.canUsePowerBombs(), + sm.wor(sm.canUseSpringBall(), + sm.canUseBombs(), + sm.itemCountOk('PowerBomb', 2), + sm.haveItem('ScrewAttack'), + sm.canShortCharge()), # speedball + # in escape you don't have PBs and can't shoot bomb blocks in long tunnels + # in wasteland and ki hunter room + sm.wnot(sm.canUseHyperBeam())) + + @Cache.decorator + def canClimbRedTower(self): + sm = self.smbm + return sm.wor(sm.knowsRedTowerClimb(), + sm.haveItem('Ice'), + sm.haveItem('SpaceJump')) + + @Cache.decorator + def canClimbBottomRedTower(self): + sm = self.smbm + return sm.wor(RomPatches.has(sm.player, RomPatches.RedTowerLeftPassage), + sm.haveItem('HiJump'), + sm.haveItem('Ice'), + sm.canFly(), + sm.canShortCharge()) + + @Cache.decorator + def canGoUpMtEverest(self): + sm = self.smbm + return sm.wor(sm.wand(sm.haveItem('Gravity'), + sm.wor(sm.haveItem('Grapple'), + sm.haveItem('SpeedBooster'), + sm.canFly(), + sm.wand(sm.knowsGravityJump(), + sm.wor(sm.haveItem('HiJump'), + sm.knowsMtEverestGravJump())))), + sm.wand(sm.canDoSuitlessOuterMaridia(), + sm.haveItem('Grapple'))) + + @Cache.decorator + def canPassMtEverest(self): + sm = self.smbm + return sm.wor(sm.wand(sm.haveItem('Gravity'), + sm.wor(sm.haveItem('Grapple'), + sm.haveItem('SpeedBooster'), + sm.canFly(), + sm.knowsGravityJump())), + sm.wand(sm.canDoSuitlessOuterMaridia(), + sm.wor(sm.haveItem('Grapple'), + sm.wand(sm.haveItem('Ice'), sm.knowsTediousMountEverest(), sm.haveItem('Super')), + sm.canDoubleSpringBallJump()))) + + @Cache.decorator + def canJumpUnderwater(self): + sm = self.smbm + return sm.wor(sm.haveItem('Gravity'), + sm.wand(sm.knowsGravLessLevel1(), + sm.haveItem('HiJump'))) + + @Cache.decorator + def canDoSuitlessOuterMaridia(self): + sm = self.smbm + return sm.wand(sm.knowsGravLessLevel1(), + sm.haveItem('HiJump'), + sm.wor(sm.haveItem('Ice'), + sm.canSpringBallJump())) + + @Cache.decorator + def canDoOuterMaridia(self): + sm = self.smbm + return sm.wor(sm.haveItem('Gravity'), + sm.canDoSuitlessOuterMaridia()) + + @Cache.decorator + def canPassBotwoonHallway(self): + sm = self.smbm + return sm.wor(sm.wand(sm.haveItem('SpeedBooster'), + sm.haveItem('Gravity')), + sm.wand(sm.knowsMochtroidClip(), sm.haveItem('Ice')), + sm.canCrystalFlashClip()) + + @Cache.decorator + def canDefeatBotwoon(self): + sm = self.smbm + return sm.wand(sm.enoughStuffBotwoon(), + sm.canPassBotwoonHallway()) + + # the sandpits from aqueduct + @Cache.decorator + def canAccessSandPits(self): + sm = self.smbm + return sm.wor(sm.haveItem('Gravity'), + sm.wand(sm.haveItem('HiJump'), + sm.knowsGravLessLevel3())) + + @Cache.decorator + def canReachCacatacAlleyFromBotowoon(self): + sm = self.smbm + return sm.wor(sm.haveItem('Gravity'), + sm.wand(sm.knowsGravLessLevel2(), + sm.haveItem("HiJump"), + sm.wor(sm.haveItem('Grapple'), + sm.haveItem('Ice'), + sm.canDoubleSpringBallJump()))) + + @Cache.decorator + def canPassCacatacAlley(self): + sm = self.smbm + return sm.wand(Bosses.bossDead(sm, 'Draygon'), + sm.haveItem('Morph'), + sm.wor(sm.haveItem('Gravity'), + sm.wand(sm.knowsGravLessLevel2(), + sm.haveItem('HiJump'), + sm.haveItem('SpaceJump')))) + + @Cache.decorator + def canGoThroughColosseumSuitless(self): + sm = self.smbm + return sm.wor(sm.haveItem('Grapple'), + sm.haveItem('SpaceJump'), + sm.wand(sm.haveItem('Ice'), + sm.energyReserveCountOk(int(7.0/sm.getDmgReduction(False)[0])), # mochtroid dmg + sm.knowsBotwoonToDraygonWithIce())) + + @Cache.decorator + def canBotwoonExitToColosseum(self): + sm = self.smbm + # traverse Botwoon Energy Tank Room + return sm.wand(sm.wor(sm.wand(sm.haveItem('Gravity'), sm.haveItem('SpeedBooster')), + sm.wand(sm.haveItem('Morph'), sm.canJumpUnderwater())), + # after Botwoon Energy Tank Room + sm.wor(sm.haveItem('Gravity'), + sm.wand(sm.knowsGravLessLevel2(), + sm.haveItem("HiJump"), + # get to top right door + sm.wor(sm.haveItem('Grapple'), + sm.haveItem('Ice'), # climb mochtroids + sm.wand(sm.canDoubleSpringBallJump(), + sm.haveItem('SpaceJump'))), + sm.canGoThroughColosseumSuitless()))) + + @Cache.decorator + def canColosseumToBotwoonExit(self): + sm = self.smbm + return sm.wor(sm.haveItem('Gravity'), + sm.wand(sm.knowsGravLessLevel2(), + sm.haveItem("HiJump"), + sm.canGoThroughColosseumSuitless())) + + @Cache.decorator + def canClimbColosseum(self): + sm = self.smbm + return sm.wor(sm.haveItem('Gravity'), + sm.wand(sm.knowsGravLessLevel2(), + sm.haveItem("HiJump"), + sm.wor(sm.haveItem('Grapple'), + sm.haveItem('Ice'), + sm.knowsPreciousRoomGravJumpExit()))) + + @Cache.decorator + def canClimbWestSandHole(self): + sm = self.smbm + return sm.wor(sm.haveItem('Gravity'), + sm.wand(sm.haveItem('HiJump'), + sm.knowsGravLessLevel3(), + sm.wor(sm.haveItem('SpaceJump'), + sm.canSpringBallJump(), + sm.knowsWestSandHoleSuitlessWallJumps()))) + + @Cache.decorator + def canAccessItemsInWestSandHole(self): + sm = self.smbm + return sm.wor(sm.wand(sm.haveItem('HiJump'), # vanilla strat + sm.canUseSpringBall()), + sm.wand(sm.haveItem('SpaceJump'), # alternate strat with possible double bomb jump but no difficult wj + sm.wor(sm.canUseSpringBall(), + sm.canUseBombs())), + sm.wand(sm.canPassBombPassages(), # wjs and/or 3 tile mid air morph + sm.knowsMaridiaWallJumps())) + + @Cache.decorator + def getDraygonConnection(self): + return getAccessPoint('DraygonRoomOut').ConnectedTo + + @Cache.decorator + def isVanillaDraygon(self): + return SMBool(self.getDraygonConnection() == 'DraygonRoomIn') + + @Cache.decorator + def canUseCrocRoomToChargeSpeed(self): + sm = self.smbm + crocRoom = getAccessPoint('Crocomire Room Top') + speedway = getAccessPoint('Crocomire Speedway Bottom') + return sm.wand(SMBool(crocRoom.ConnectedTo == 'Crocomire Speedway Bottom'), + crocRoom.traverse(sm), + speedway.traverse(sm)) + + @Cache.decorator + def canFightDraygon(self): + sm = self.smbm + return sm.wor(sm.haveItem('Gravity'), + sm.wand(sm.haveItem('HiJump'), + sm.wor(sm.knowsGravLessLevel2(), + sm.knowsGravLessLevel3()))) + + @Cache.decorator + def canDraygonCrystalFlashSuit(self): + sm = self.smbm + return sm.wand(sm.canCrystalFlash(), + sm.knowsDraygonRoomCrystalFlash(), + # ask for 4 PB pack as an ugly workaround for + # a rando bug which can place a PB at space + # jump to "get you out" (this check is in + # PostAvailable condition of the Dray/Space + # Jump locs) + sm.itemCountOk('PowerBomb', 4)) + + @Cache.decorator + def canExitDraygonRoomWithGravity(self): + sm = self.smbm + return sm.wand(sm.haveItem('Gravity'), + sm.wor(sm.canFly(), + sm.knowsGravityJump(), + sm.wand(sm.haveItem('HiJump'), + sm.haveItem('SpeedBooster')))) + + @Cache.decorator + def canGrappleExitDraygon(self): + sm = self.smbm + return sm.wand(sm.haveItem('Grapple'), + sm.knowsDraygonRoomGrappleExit()) + + @Cache.decorator + def canExitDraygonVanilla(self): + sm = self.smbm + # to get out of draygon room: + # with gravity but without highjump/bomb/space jump: gravity jump + # to exit draygon room: grapple or crystal flash (for free shine spark) + # to exit precious room: spring ball jump, xray scope glitch or stored spark + return sm.wor(sm.canExitDraygonRoomWithGravity(), + sm.wand(sm.canDraygonCrystalFlashSuit(), + # use the spark either to exit draygon room or precious room + sm.wor(sm.canGrappleExitDraygon(), + sm.wand(sm.haveItem('XRayScope'), + sm.knowsPreciousRoomXRayExit()), + sm.canSpringBallJump())), + # spark-less exit (no CF) + sm.wand(sm.canGrappleExitDraygon(), + sm.wor(sm.wand(sm.haveItem('XRayScope'), + sm.knowsPreciousRoomXRayExit()), + sm.canSpringBallJump())), + sm.canDoubleSpringBallJump()) + + @Cache.decorator + def canExitDraygonRandomized(self): + sm = self.smbm + # disregard precious room + return sm.wor(sm.canExitDraygonRoomWithGravity(), + sm.canDraygonCrystalFlashSuit(), + sm.canGrappleExitDraygon(), + sm.canDoubleSpringBallJump()) + + @Cache.decorator + def canExitDraygon(self): + sm = self.smbm + if self.isVanillaDraygon(): + return self.canExitDraygonVanilla() + else: + return self.canExitDraygonRandomized() + + @Cache.decorator + def canExitPreciousRoomVanilla(self): + return SMBool(True) # handled by canExitDraygonVanilla + + @Cache.decorator + def canExitPreciousRoomRandomized(self): + sm = self.smbm + suitlessRoomExit = sm.canSpringBallJump() + if suitlessRoomExit.bool == False: + if self.getDraygonConnection() == 'KraidRoomIn': + suitlessRoomExit = sm.canShortCharge() # charge spark in kraid's room + elif self.getDraygonConnection() == 'RidleyRoomIn': + suitlessRoomExit = sm.wand(sm.haveItem('XRayScope'), # get doorstuck in compatible transition + sm.knowsPreciousRoomXRayExit()) + return sm.wor(sm.wand(sm.haveItem('Gravity'), + sm.wor(sm.canFly(), + sm.knowsGravityJump(), + sm.haveItem('HiJump'))), + suitlessRoomExit) + + @Cache.decorator + def canExitPreciousRoom(self): + if self.isVanillaDraygon(): + return self.canExitPreciousRoomVanilla() + else: + return self.canExitPreciousRoomRandomized() + + @Cache.decorator + def canPassDachoraRoom(self): + sm = self.smbm + return sm.wor(sm.haveItem('SpeedBooster'), sm.canDestroyBombWalls()) + + @Cache.decorator + def canTraverseCrabTunnelLeftToRight(self): + sm = self.smbm + return sm.wand(sm.traverse('MainStreetBottomRight'), + sm.wor(sm.haveItem('Super'), + RomPatches.has(sm.player, RomPatches.AreaRandoGatesOther))) diff --git a/worlds/sm/variaRandomizer/graph/vanilla/graph_locations.py b/worlds/sm/variaRandomizer/graph/vanilla/graph_locations.py new file mode 100644 index 00000000..b8a1d3f4 --- /dev/null +++ b/worlds/sm/variaRandomizer/graph/vanilla/graph_locations.py @@ -0,0 +1,994 @@ +from logic.helpers import Bosses +from utils.parameters import Settings +from rom.rom_patches import RomPatches +from logic.smbool import SMBool +from graph.location import locationsDict + +locationsDict["Energy Tank, Gauntlet"].AccessFrom = { + 'Landing Site': lambda sm: SMBool(True) +} +locationsDict["Energy Tank, Gauntlet"].Available = ( + lambda sm: sm.wor(sm.canEnterAndLeaveGauntlet(), + sm.wand(sm.canShortCharge(), + sm.canEnterAndLeaveGauntletQty(1, 0)), # thanks ponk! https://youtu.be/jil5zTBCF1s + sm.canDoLowGauntlet()) +) +locationsDict["Bomb"].AccessFrom = { + 'Landing Site': lambda sm: SMBool(True) +} +locationsDict["Bomb"].Available = ( + lambda sm: sm.wand(sm.haveItem('Morph'), + sm.traverse('FlywayRight')) +) +locationsDict["Bomb"].PostAvailable = ( + lambda sm: sm.wor(sm.knowsAlcatrazEscape(), + sm.canPassBombPassages()) +) +locationsDict["Energy Tank, Terminator"].AccessFrom = { + 'Landing Site': lambda sm: sm.canPassTerminatorBombWall(), + 'Lower Mushrooms Left': lambda sm: sm.canPassCrateriaGreenPirates(), + 'Gauntlet Top': lambda sm: sm.haveItem('Morph') +} +locationsDict["Energy Tank, Terminator"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Reserve Tank, Brinstar"].AccessFrom = { + 'Green Brinstar Elevator': lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.BrinReserveBlueDoors), sm.traverse('MainShaftRight')) +} +locationsDict["Reserve Tank, Brinstar"].Available = ( + lambda sm: sm.wand(sm.wor(sm.canMockball(), + sm.haveItem('SpeedBooster')), + sm.wor(RomPatches.has(sm.player, RomPatches.BrinReserveBlueDoors), sm.traverse('EarlySupersRight'))) +) +locationsDict["Charge Beam"].AccessFrom = { + 'Big Pink': lambda sm: SMBool(True) +} +locationsDict["Charge Beam"].Available = ( + lambda sm: sm.canPassBombPassages() +) +locationsDict["Morphing Ball"].AccessFrom = { + 'Blue Brinstar Elevator Bottom': lambda sm: SMBool(True) +} +locationsDict["Morphing Ball"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Energy Tank, Brinstar Ceiling"].AccessFrom = { + 'Blue Brinstar Elevator Bottom': lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.BlueBrinstarBlueDoor), sm.traverse('ConstructionZoneRight')) +} +locationsDict["Energy Tank, Brinstar Ceiling"].Available = ( + + lambda sm: sm.wor(sm.knowsCeilingDBoost(), + sm.canFly(), + sm.wor(sm.haveItem('HiJump'), + sm.haveItem('Ice'), + sm.wand(sm.canUsePowerBombs(), + sm.haveItem('SpeedBooster')), + sm.canSimpleShortCharge())) +) +locationsDict["Energy Tank, Etecoons"].AccessFrom = { + 'Etecoons Bottom': lambda sm: SMBool(True) +} +locationsDict["Energy Tank, Etecoons"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Energy Tank, Waterway"].AccessFrom = { + 'Big Pink': lambda sm: SMBool(True) +} +locationsDict["Energy Tank, Waterway"].Available = ( + lambda sm: sm.wand(sm.canUsePowerBombs(), + sm.traverse('BigPinkBottomLeft'), + sm.haveItem('SpeedBooster'), + sm.wor(sm.haveItem('Gravity'), + sm.canSimpleShortCharge())) # from the blocks above the water +) +locationsDict["Energy Tank, Brinstar Gate"].AccessFrom = { + 'Big Pink': lambda sm: SMBool(True) +} +locationsDict["Energy Tank, Brinstar Gate"].Available = ( + lambda sm: sm.wand(sm.traverse('BigPinkRight'), + sm.wor(sm.haveItem('Wave'), + sm.wand(sm.haveItem('Super'), + sm.haveItem('HiJump'), + sm.knowsReverseGateGlitch()), + sm.wand(sm.haveItem('Super'), + sm.knowsReverseGateGlitchHiJumpLess()))) +) +locationsDict["X-Ray Scope"].AccessFrom = { + 'Red Tower Top Left': lambda sm: SMBool(True) +} +locationsDict["X-Ray Scope"].Available = ( + lambda sm: sm.wand(sm.canUsePowerBombs(), + sm.traverse('RedTowerLeft'), + sm.traverse('RedBrinstarFirefleaLeft'), + sm.wor(sm.haveItem('Grapple'), + sm.haveItem('SpaceJump'), + sm.wand(sm.energyReserveCountOkHardRoom('X-Ray'), + sm.wor(sm.knowsXrayDboost(), + sm.wand(sm.haveItem('Ice'), + sm.wor(sm.haveItem('HiJump'), sm.knowsXrayIce())), + sm.canInfiniteBombJump(), + sm.wand(sm.haveItem('HiJump'), + sm.wor(sm.haveItem('SpeedBooster'), + sm.canSpringBallJump())))))) +) +locationsDict["Spazer"].AccessFrom = { + 'East Tunnel Right': lambda sm: SMBool(True) +} +locationsDict["Spazer"].Available = ( + lambda sm: sm.wand(sm.traverse('BelowSpazerTopRight'), + sm.wor(sm.canPassBombPassages(), + sm.wand(sm.haveItem('Morph'), + RomPatches.has(sm.player, RomPatches.SpazerShotBlock)))) +) +locationsDict["Energy Tank, Kraid"].AccessFrom = { + 'Warehouse Zeela Room Left': lambda sm: SMBool(True) +} +locationsDict["Energy Tank, Kraid"].Available = ( + lambda sm: sm.wand(Bosses.bossDead(sm, 'Kraid'), + # kill the beetoms to unlock the door to get out + sm.canKillBeetoms()) +) +locationsDict["Kraid"].AccessFrom = { + 'KraidRoomIn': lambda sm: SMBool(True) +} +locationsDict["Kraid"].Available = ( + lambda sm: sm.enoughStuffsKraid() +) +locationsDict["Varia Suit"].AccessFrom = { + 'KraidRoomIn': lambda sm: SMBool(True) +} +locationsDict["Varia Suit"].Available = ( + lambda sm: Bosses.bossDead(sm, 'Kraid') +) +locationsDict["Ice Beam"].AccessFrom = { + 'Business Center': lambda sm: sm.traverse('BusinessCenterTopLeft') +} +locationsDict["Ice Beam"].Available = ( + lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['Ice']['Norfair Entrance -> Ice Beam']), + sm.wor(sm.canPassBombPassages(), # to exit, or if you fail entrance + sm.wand(sm.haveItem('Ice'), # harder strat + sm.haveItem('Morph'), + sm.knowsIceEscape())), + sm.wor(sm.wand(sm.haveItem('Morph'), + sm.knowsMockball()), + sm.haveItem('SpeedBooster'))) +) +locationsDict["Energy Tank, Crocomire"].AccessFrom = { + 'Crocomire Room Top': lambda sm: SMBool(True) +} +locationsDict["Energy Tank, Crocomire"].Available = ( + lambda sm: sm.wand(sm.enoughStuffCroc(), + sm.wor(sm.haveItem('Grapple'), + sm.haveItem('SpaceJump'), + sm.energyReserveCountOk(3/sm.getDmgReduction()[0]))) +) +locationsDict["Hi-Jump Boots"].AccessFrom = { + 'Business Center': lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.HiJumpAreaBlueDoor), sm.traverse('BusinessCenterBottomLeft')) +} +locationsDict["Hi-Jump Boots"].Available = ( + lambda sm: sm.haveItem('Morph') +) +locationsDict["Hi-Jump Boots"].PostAvailable = ( + lambda sm: sm.wor(sm.canPassBombPassages(), + sm.wand(sm.haveItem('Morph'), RomPatches.has(sm.player, RomPatches.HiJumpShotBlock))) +) +locationsDict["Grapple Beam"].AccessFrom = { + 'Crocomire Room Top': lambda sm: SMBool(True) +} +locationsDict["Grapple Beam"].Available = ( + lambda sm: sm.wand(sm.enoughStuffCroc(), + sm.wor(sm.wand(sm.haveItem('Morph'), + sm.canFly()), + sm.wand(sm.haveItem('SpeedBooster'), + sm.wor(sm.knowsShortCharge(), + sm.canUsePowerBombs())), + sm.wand(sm.haveItem('Morph'), + sm.wor(sm.haveItem('SpeedBooster'), + sm.canSpringBallJump()), + sm.haveItem('HiJump')), # jump from the yellow plateform ennemy + sm.canGreenGateGlitch())) +) +locationsDict["Grapple Beam"].PostAvailable = ( + lambda sm: sm.wor(sm.haveItem('Morph'), # regular exit + sm.wand(sm.haveItem('Super'), # grapple escape reverse + sm.wor(sm.canFly(), # Grapple Tutorial Room 2 + sm.haveItem('HiJump'), + sm.haveItem('Grapple')), + sm.wor(sm.haveItem('Gravity'), # Grapple Tutorial Room 3 + sm.haveItem('SpaceJump'), + sm.haveItem('Grapple')))) +) +locationsDict["Reserve Tank, Norfair"].AccessFrom = { + 'Bubble Mountain': lambda sm: sm.canEnterNorfairReserveAreaFromBubbleMoutain(), + 'Bubble Mountain Top': lambda sm: sm.canEnterNorfairReserveAreaFromBubbleMoutainTop(), +} +locationsDict["Reserve Tank, Norfair"].Available = ( + lambda sm: sm.wand(sm.haveItem('Morph'), sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Bubble -> Norfair Reserve'])) +) +locationsDict["Speed Booster"].AccessFrom = { + 'Bubble Mountain Top': lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.SpeedAreaBlueDoors), + sm.wand(sm.traverse('BubbleMountainTopRight'), + sm.traverse('SpeedBoosterHallRight'))) +} +locationsDict["Speed Booster"].Available = ( + lambda sm: sm.canHellRunToSpeedBooster() +) +locationsDict["Wave Beam"].AccessFrom = { + 'Bubble Mountain Top': lambda sm: sm.canAccessDoubleChamberItems() +} +locationsDict["Wave Beam"].Available = ( + lambda sm: sm.traverse('DoubleChamberRight') +) +locationsDict["Wave Beam"].PostAvailable = ( + lambda sm: sm.wor(sm.haveItem('Morph'), # exit through lower passage under the spikes + sm.wand(sm.wor(sm.haveItem('SpaceJump'), # exit through blue gate + sm.haveItem('Grapple')), + sm.wor(sm.wand(sm.canBlueGateGlitch(), sm.heatProof()), # hell run + green gate glitch is too much + sm.haveItem('Wave')))) +) +locationsDict["Ridley"].AccessFrom = { + 'RidleyRoomIn': lambda sm: SMBool(True) +} +locationsDict["Ridley"].Available = ( + lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']), sm.enoughStuffsRidley()) +) +locationsDict["Energy Tank, Ridley"].AccessFrom = { + 'RidleyRoomIn': lambda sm: sm.haveItem('Ridley') +} +locationsDict["Energy Tank, Ridley"].Available = ( + lambda sm: sm.haveItem('Morph') +) +locationsDict["Screw Attack"].AccessFrom = { + 'Screw Attack Bottom': lambda sm: SMBool(True) +} +locationsDict["Screw Attack"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Screw Attack"].PostAvailable = ( + lambda sm: sm.canExitScrewAttackArea() +) +locationsDict["Energy Tank, Firefleas"].AccessFrom = { + 'Firefleas': lambda sm: SMBool(True) +} +locationsDict["Energy Tank, Firefleas"].Available = ( + lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.FirefleasRemoveFune), + # get past the fune + sm.haveItem('Super'), + sm.canPassBombPassages(), + sm.canUseSpringBall()) +) +locationsDict["Energy Tank, Firefleas"].PostAvailable = ( + lambda sm: sm.wor(sm.knowsFirefleasWalljump(), + sm.wor(sm.haveItem('Ice'), + sm.haveItem('HiJump'), + sm.canFly(), + sm.canSpringBallJump())) +) +locationsDict["Reserve Tank, Wrecked Ship"].AccessFrom = { + 'Wrecked Ship Main': lambda sm: SMBool(True) +} +locationsDict["Reserve Tank, Wrecked Ship"].Available = ( + lambda sm: sm.wand(sm.canUsePowerBombs(), + sm.haveItem('SpeedBooster'), + sm.canPassBowling()) +) +locationsDict["Energy Tank, Wrecked Ship"].AccessFrom = { + 'Wrecked Ship Back': lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.WsEtankBlueDoor), + sm.traverse('ElectricDeathRoomTopLeft')) +} +locationsDict["Energy Tank, Wrecked Ship"].Available = ( + lambda sm: sm.wor(Bosses.bossDead(sm, 'Phantoon'), + RomPatches.has(sm.player, RomPatches.WsEtankPhantoonAlive)) +) +locationsDict["Phantoon"].AccessFrom = { + 'PhantoonRoomIn': lambda sm: SMBool(True) +} +locationsDict["Phantoon"].Available = ( + lambda sm: sm.enoughStuffsPhantoon() +) +locationsDict["Right Super, Wrecked Ship"].AccessFrom = { + 'Wrecked Ship Main': lambda sm: Bosses.bossDead(sm, 'Phantoon') +} +locationsDict["Right Super, Wrecked Ship"].Available = ( + lambda sm: sm.canPassBombPassages() +) +locationsDict["Gravity Suit"].AccessFrom = { + 'Wrecked Ship Main': lambda sm: SMBool(True) +} +locationsDict["Gravity Suit"].Available = ( + lambda sm: sm.wand(sm.canPassBombPassages(), + sm.canPassBowling()) +) +locationsDict["Energy Tank, Mama turtle"].AccessFrom = { + 'Main Street Bottom': lambda sm: sm.wand(sm.canDoOuterMaridia(), + sm.wor(sm.traverse('FishTankRight'), + RomPatches.has(sm.player, RomPatches.MamaTurtleBlueDoor)), + sm.wor(sm.wor(sm.canFly(), + sm.wand(sm.haveItem('Gravity'), + sm.haveItem('SpeedBooster')), + sm.wand(sm.haveItem('HiJump'), + sm.haveItem('SpeedBooster'), + sm.knowsHiJumpMamaTurtle())), + sm.wor(sm.wand(sm.canUseSpringBall(), + sm.wor(sm.wand(sm.haveItem('HiJump'), + sm.knowsSpringBallJump()), + sm.knowsSpringBallJumpFromWall())), + sm.haveItem('Grapple')))), + 'Mama Turtle': lambda sm: SMBool(True) +} +locationsDict["Energy Tank, Mama turtle"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Plasma Beam"].AccessFrom = { + 'Toilet Top': lambda sm: SMBool(True) +} +locationsDict["Plasma Beam"].Available = ( + lambda sm: Bosses.bossDead(sm, 'Draygon') +) +locationsDict["Plasma Beam"].PostAvailable = ( + lambda sm: sm.wand(sm.wor(sm.wand(sm.canShortCharge(), + sm.knowsKillPlasmaPiratesWithSpark()), + sm.wand(sm.canFireChargedShots(), + sm.knowsKillPlasmaPiratesWithCharge(), + # 160/80/40 dmg * 4 ground plasma pirates + # => 640/320/160 damage take required + # check below is 1099/599/299 (give margin for taking dmg a bit) + # (* 4 for nerfed charge, since you need to take hits 4 times instead of one) + sm.energyReserveCountOk(int(10.0 * sm.getPiratesPseudoScrewCoeff()/sm.getDmgReduction(False)[0]))), + sm.haveItem('ScrewAttack'), + sm.haveItem('Plasma')), + sm.wor(sm.canFly(), + sm.wand(sm.haveItem('HiJump'), + sm.knowsGetAroundWallJump()), + sm.canShortCharge(), + sm.wand(sm.canSpringBallJump(), + sm.knowsSpringBallJumpFromWall()))) +) +locationsDict["Reserve Tank, Maridia"].AccessFrom = { + 'Left Sandpit': lambda sm: sm.canClimbWestSandHole() +} +locationsDict["Reserve Tank, Maridia"].Available = ( + lambda sm: sm.canAccessItemsInWestSandHole() +) +locationsDict["Spring Ball"].AccessFrom = { + 'Oasis Bottom': lambda sm: sm.canTraverseSandPits() +} +locationsDict["Spring Ball"].Available = ( + lambda sm: sm.wand(sm.canUsePowerBombs(), # in Shaktool room to let Shaktool access the sand blocks + sm.wor(sm.wand(sm.haveItem('Ice'), # puyo clip + sm.wor(sm.wand(sm.haveItem('Gravity'), + sm.knowsPuyoClip()), + sm.wand(sm.haveItem('Gravity'), + sm.haveItem('XRayScope'), + sm.knowsPuyoClipXRay()), + sm.knowsSuitlessPuyoClip())), + sm.wand(sm.haveItem('Grapple'), # go through grapple block + sm.wor(sm.wand(sm.haveItem('Gravity'), + sm.wor(sm.wor(sm.wand(sm.haveItem('HiJump'), sm.knowsAccessSpringBallWithHiJump()), + sm.haveItem('SpaceJump')), + sm.knowsAccessSpringBallWithGravJump(), + sm.wand(sm.haveItem('Bomb'), + sm.wor(sm.knowsAccessSpringBallWithBombJumps(), + sm.wand(sm.haveItem('SpringBall'), + sm.knowsAccessSpringBallWithSpringBallBombJumps()))), + sm.wand(sm.haveItem('SpringBall'), sm.knowsAccessSpringBallWithSpringBallJump()))), + sm.wand(sm.haveItem('SpaceJump'), sm.knowsAccessSpringBallWithFlatley()))), + sm.wand(sm.haveItem('XRayScope'), sm.knowsAccessSpringBallWithXRayClimb()), # XRay climb + sm.canCrystalFlashClip()), + sm.wor(sm.haveItem('Gravity'), sm.canUseSpringBall())) # acess the item in spring ball room +) +locationsDict["Spring Ball"].PostAvailable = ( + lambda sm: sm.wor(sm.wand(sm.haveItem('Gravity'), + sm.wor(sm.haveItem('HiJump'), + sm.canFly(), + sm.knowsMaridiaWallJumps())), + sm.canSpringBallJump()) +) +locationsDict["Energy Tank, Botwoon"].AccessFrom = { + 'Post Botwoon': lambda sm: sm.canJumpUnderwater() +} +locationsDict["Energy Tank, Botwoon"].Available = ( + lambda sm: sm.haveItem('Morph') +) +locationsDict["Draygon"].AccessFrom = { + 'Draygon Room Bottom': lambda sm: SMBool(True) +} +locationsDict["Draygon"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Space Jump"].AccessFrom = { + 'Draygon Room Bottom': lambda sm: SMBool(True) +} +locationsDict["Space Jump"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Space Jump"].PostAvailable = ( + lambda sm: Bosses.bossDead(sm, 'Draygon') +) +locationsDict["Mother Brain"].AccessFrom = { + 'Golden Four': lambda sm: Bosses.allBossesDead(sm) +} +locationsDict["Mother Brain"].Available = ( + lambda sm: sm.enoughStuffTourian() +) +locationsDict["Power Bomb (Crateria surface)"].AccessFrom = { + 'Landing Site': lambda sm: SMBool(True) +} +locationsDict["Power Bomb (Crateria surface)"].Available = ( + lambda sm: sm.wand(sm.traverse('LandingSiteTopRight'), + sm.wor(sm.haveItem('SpeedBooster'), + sm.canFly())) +) +locationsDict["Missile (outside Wrecked Ship bottom)"].AccessFrom = { + 'West Ocean Left': lambda sm: SMBool(True) +} +locationsDict["Missile (outside Wrecked Ship bottom)"].Available = ( + lambda sm: sm.haveItem('Morph') +) +locationsDict["Missile (outside Wrecked Ship bottom)"].PostAvailable = ( + lambda sm: sm.canPassBombPassages() +) +locationsDict["Missile (outside Wrecked Ship top)"].AccessFrom = { + 'Wrecked Ship Main': lambda sm: SMBool(True) +} +locationsDict["Missile (outside Wrecked Ship top)"].Available = ( + lambda sm: Bosses.bossDead(sm, 'Phantoon') +) +locationsDict["Missile (outside Wrecked Ship middle)"].AccessFrom = { + 'Wrecked Ship Main': lambda sm: SMBool(True) +} +locationsDict["Missile (outside Wrecked Ship middle)"].Available = ( + lambda sm: sm.wand(sm.haveItem('Super'), sm.haveItem('Morph'), Bosses.bossDead(sm, 'Phantoon')) +) +locationsDict["Missile (Crateria moat)"].AccessFrom = { + 'Moat Left': lambda sm: SMBool(True) +} +locationsDict["Missile (Crateria moat)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Missile (Crateria bottom)"].AccessFrom = { + 'Landing Site': lambda sm: SMBool(True) +} +locationsDict["Missile (Crateria bottom)"].Available = ( + lambda sm: sm.wor(sm.canDestroyBombWalls(), + sm.wand(sm.haveItem('SpeedBooster'), + sm.knowsOldMBWithSpeed())) +) +locationsDict["Missile (Crateria gauntlet right)"].AccessFrom = { + 'Landing Site': lambda sm: sm.wor(sm.wand(sm.canEnterAndLeaveGauntlet(), + sm.canPassBombPassages()), + sm.canDoLowGauntlet()), + 'Gauntlet Top': lambda sm: SMBool(True) +} +locationsDict["Missile (Crateria gauntlet right)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Missile (Crateria gauntlet left)"].AccessFrom = { + 'Landing Site': lambda sm: sm.wor(sm.wand(sm.canEnterAndLeaveGauntlet(), + sm.canPassBombPassages()), + sm.canDoLowGauntlet()), + 'Gauntlet Top': lambda sm: SMBool(True) +} +locationsDict["Missile (Crateria gauntlet left)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Super Missile (Crateria)"].AccessFrom = { + 'Landing Site': lambda sm: SMBool(True) +} +locationsDict["Super Missile (Crateria)"].Available = ( + lambda sm: sm.wand(sm.canPassBombPassages(), + sm.traverse("ClimbRight"), + sm.haveItem('SpeedBooster'), + # reserves are hard to trigger midspark when not having ETanks + sm.wor(sm.wand(sm.energyReserveCountOk(2), sm.itemCountOk('ETank', 1)), # need energy to get out + sm.wand(sm.itemCountOk('ETank', 1), + sm.wor(sm.haveItem('Grapple'), # use grapple/space or dmg protection to get out + sm.haveItem('SpaceJump'), + sm.heatProof()))), + sm.wor(sm.haveItem('Ice'), + sm.wand(sm.canSimpleShortCharge(), sm.canUsePowerBombs()))) # there's also a dboost involved in simple short charge or you have to kill the yellow enemies with some power bombs +) +locationsDict["Missile (Crateria middle)"].AccessFrom = { + 'Landing Site': lambda sm: SMBool(True) +} +locationsDict["Missile (Crateria middle)"].Available = ( + lambda sm: sm.canPassBombPassages() +) +locationsDict["Power Bomb (green Brinstar bottom)"].AccessFrom = { + 'Etecoons Bottom': lambda sm: SMBool(True) +} +locationsDict["Power Bomb (green Brinstar bottom)"].Available = ( + lambda sm: sm.wand(sm.haveItem('Morph'), + sm.canKillBeetoms()) +) +locationsDict["Super Missile (pink Brinstar)"].AccessFrom = { + 'Big Pink': lambda sm: SMBool(True) +} +locationsDict["Super Missile (pink Brinstar)"].Available = ( + lambda sm: sm.wor(sm.wand(sm.traverse('BigPinkTopRight'), + sm.enoughStuffSporeSpawn()), + # back way into spore spawn + sm.wand(sm.canOpenGreenDoors(), + sm.canPassBombPassages())) +) +locationsDict["Super Missile (pink Brinstar)"].PostAvailable = ( + lambda sm: sm.wand(sm.canOpenGreenDoors(), + sm.canPassBombPassages()) +) +locationsDict["Missile (green Brinstar below super missile)"].AccessFrom = { + 'Green Brinstar Elevator': lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.BrinReserveBlueDoors), sm.traverse('MainShaftRight')) +} +locationsDict["Missile (green Brinstar below super missile)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Missile (green Brinstar below super missile)"].PostAvailable = ( + lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.EarlySupersShotBlock), sm.canPassBombPassages()) +) +locationsDict["Super Missile (green Brinstar top)"].AccessFrom = { + 'Green Brinstar Elevator': lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.BrinReserveBlueDoors), sm.traverse('MainShaftRight')) +} +locationsDict["Super Missile (green Brinstar top)"].Available = ( + lambda sm: sm.wor(sm.canMockball(), + sm.haveItem('SpeedBooster')) +) +locationsDict["Missile (green Brinstar behind missile)"].AccessFrom = { + 'Green Brinstar Elevator': lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.BrinReserveBlueDoors), sm.traverse('MainShaftRight')) +} +locationsDict["Missile (green Brinstar behind missile)"].Available = ( + lambda sm: sm.wand(sm.haveItem('Morph'), + sm.wor(sm.canMockball(), + sm.haveItem('SpeedBooster')), + sm.traverse('EarlySupersRight'), + sm.wor(sm.canPassBombPassages(), + sm.wand(sm.knowsRonPopeilScrew(), + sm.haveItem('ScrewAttack')))) +) +locationsDict["Missile (green Brinstar behind reserve tank)"].AccessFrom = { + 'Green Brinstar Elevator': lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.BrinReserveBlueDoors), sm.traverse('MainShaftRight')) +} +locationsDict["Missile (green Brinstar behind reserve tank)"].Available = ( + lambda sm: sm.wand(sm.traverse('EarlySupersRight'), + sm.haveItem('Morph'), + sm.wor(sm.canMockball(), + sm.haveItem('SpeedBooster'))) +) +locationsDict["Missile (pink Brinstar top)"].AccessFrom = { + 'Big Pink': lambda sm: SMBool(True) +} +locationsDict["Missile (pink Brinstar top)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Missile (pink Brinstar bottom)"].AccessFrom = { + 'Big Pink': lambda sm: SMBool(True) +} +locationsDict["Missile (pink Brinstar bottom)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Power Bomb (pink Brinstar)"].AccessFrom = { + 'Big Pink': lambda sm: SMBool(True) +} +locationsDict["Power Bomb (pink Brinstar)"].Available = ( + lambda sm: sm.wand(sm.canUsePowerBombs(), + sm.haveItem('Super')) +) +locationsDict["Missile (green Brinstar pipe)"].AccessFrom = { + 'Green Hill Zone Top Right': lambda sm: SMBool(True) +} +locationsDict["Missile (green Brinstar pipe)"].Available = ( + lambda sm: sm.haveItem('Morph') +) +locationsDict["Power Bomb (blue Brinstar)"].AccessFrom = { + 'Blue Brinstar Elevator Bottom': lambda sm: sm.canUsePowerBombs(), + 'Morph Ball Room Left': lambda sm: sm.wor(sm.canPassBombPassages(), + sm.wand(sm.haveItem('Morph'), + sm.canShortCharge())) # speedball +} +locationsDict["Power Bomb (blue Brinstar)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Missile (blue Brinstar middle)"].AccessFrom = { + 'Blue Brinstar Elevator Bottom': lambda sm: SMBool(True) +} +locationsDict["Missile (blue Brinstar middle)"].Available = ( + lambda sm: sm.wand(sm.wor(RomPatches.has(sm.player, RomPatches.BlueBrinstarMissile), sm.haveItem('Morph')), + sm.wor(RomPatches.has(sm.player, RomPatches.BlueBrinstarBlueDoor), sm.traverse('ConstructionZoneRight'))) +) +locationsDict["Super Missile (green Brinstar bottom)"].AccessFrom = { + 'Etecoons Supers': lambda sm: SMBool(True) +} +locationsDict["Super Missile (green Brinstar bottom)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Missile (blue Brinstar bottom)"].AccessFrom = { + 'Blue Brinstar Elevator Bottom': lambda sm: SMBool(True) +} +locationsDict["Missile (blue Brinstar bottom)"].Available = ( + lambda sm: sm.haveItem('Morph') +) +locationsDict["Missile (blue Brinstar top)"].AccessFrom = { + 'Blue Brinstar Elevator Bottom': lambda sm: SMBool(True) +} +locationsDict["Missile (blue Brinstar top)"].Available = ( + lambda sm: sm.canAccessBillyMays() +) +locationsDict["Missile (blue Brinstar behind missile)"].AccessFrom = { + 'Blue Brinstar Elevator Bottom': lambda sm: SMBool(True) +} +locationsDict["Missile (blue Brinstar behind missile)"].Available = ( + lambda sm: sm.canAccessBillyMays() +) +locationsDict["Power Bomb (red Brinstar sidehopper room)"].AccessFrom = { + 'Red Brinstar Elevator': lambda sm: SMBool(True) +} +locationsDict["Power Bomb (red Brinstar sidehopper room)"].Available = ( + lambda sm: sm.wand(sm.traverse('RedTowerElevatorTopLeft'), + sm.canUsePowerBombs()) +) +locationsDict["Power Bomb (red Brinstar spike room)"].AccessFrom = { + 'Red Brinstar Elevator': lambda sm: SMBool(True) +} +locationsDict["Power Bomb (red Brinstar spike room)"].Available = ( + lambda sm: sm.traverse('RedTowerElevatorBottomLeft') +) +locationsDict["Missile (red Brinstar spike room)"].AccessFrom = { + 'Red Brinstar Elevator': lambda sm: SMBool(True) +} +locationsDict["Missile (red Brinstar spike room)"].Available = ( + lambda sm: sm.wand(sm.traverse('RedTowerElevatorBottomLeft'), + sm.canUsePowerBombs()) +) +locationsDict["Missile (Kraid)"].AccessFrom = { + 'Warehouse Zeela Room Left': lambda sm: SMBool(True) +} +locationsDict["Missile (Kraid)"].Available = ( + lambda sm: sm.canUsePowerBombs() +) +locationsDict["Missile (lava room)"].AccessFrom = { + 'Cathedral': lambda sm: SMBool(True) +} +locationsDict["Missile (lava room)"].Available = ( + lambda sm: sm.haveItem('Morph') +) +locationsDict["Missile (below Ice Beam)"].AccessFrom = { + 'Business Center': lambda sm: sm.wand(sm.traverse('BusinessCenterTopLeft'), + sm.canUsePowerBombs(), + sm.canHellRun(**Settings.hellRunsTable['Ice']['Norfair Entrance -> Ice Beam']), + sm.wor(sm.wand(sm.haveItem('Morph'), + sm.knowsMockball()), + sm.haveItem('SpeedBooster'))), + 'Crocomire Speedway Bottom': lambda sm: sm.wand(sm.canUseCrocRoomToChargeSpeed(), + sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Croc -> Ice Missiles']), + sm.haveItem('SpeedBooster'), + sm.knowsIceMissileFromCroc()) +} +locationsDict["Missile (below Ice Beam)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Missile (above Crocomire)"].AccessFrom = { + 'Crocomire Speedway Bottom': lambda sm: sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Croc -> Grapple Escape Missiles']) +} +locationsDict["Missile (above Crocomire)"].Available = ( + lambda sm: sm.canGrappleEscape() +) +locationsDict["Missile (Hi-Jump Boots)"].AccessFrom = { + 'Business Center': lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.HiJumpAreaBlueDoor), sm.traverse('BusinessCenterBottomLeft')) +} +locationsDict["Missile (Hi-Jump Boots)"].Available = ( + lambda sm: sm.haveItem('Morph') +) +locationsDict["Missile (Hi-Jump Boots)"].PostAvailable = ( + lambda sm: sm.wor(sm.canPassBombPassages(), + sm.wand(RomPatches.has(sm.player, RomPatches.HiJumpShotBlock), sm.haveItem('Morph'))) +) +locationsDict["Energy Tank (Hi-Jump Boots)"].AccessFrom = { + 'Business Center': lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.HiJumpAreaBlueDoor), sm.traverse('BusinessCenterBottomLeft')) +} +locationsDict["Energy Tank (Hi-Jump Boots)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Power Bomb (Crocomire)"].AccessFrom = { + 'Crocomire Room Top': lambda sm: SMBool(True) +} +locationsDict["Power Bomb (Crocomire)"].Available = ( + lambda sm: sm.wand(sm.traverse('PostCrocomireUpperLeft'), + sm.enoughStuffCroc(), + sm.wor(sm.wor(sm.canFly(), + sm.haveItem('Grapple'), + sm.wand(sm.haveItem('SpeedBooster'), + sm.wor(sm.heatProof(), + sm.energyReserveCountOk(1)))), # spark from the room before + sm.wor(sm.haveItem('HiJump'), # run and jump from yellow platform + sm.wand(sm.haveItem('Ice'), + sm.knowsCrocPBsIce()), + sm.knowsCrocPBsDBoost()))) +) +locationsDict["Missile (below Crocomire)"].AccessFrom = { + 'Crocomire Room Top': lambda sm: SMBool(True) +} +locationsDict["Missile (below Crocomire)"].Available = ( + lambda sm: sm.wand(sm.traverse('PostCrocomireShaftRight'), sm.enoughStuffCroc(), sm.haveItem('Morph')) +) +locationsDict["Missile (Grapple Beam)"].AccessFrom = { + 'Crocomire Room Top': lambda sm: SMBool(True) +} +locationsDict["Missile (Grapple Beam)"].Available = ( + lambda sm: sm.wand(sm.enoughStuffCroc(), + sm.wor(sm.wor(sm.wand(sm.haveItem('Morph'), # from below + sm.canFly()), + sm.wand(sm.haveItem('SpeedBooster'), + sm.wor(sm.knowsShortCharge(), + sm.canUsePowerBombs()))), + sm.wand(sm.canGreenGateGlitch(), # from grapple room + sm.canFly()))) # TODO::test if accessible with a spark (short charge), and how many etanks required +) +locationsDict["Missile (Grapple Beam)"].PostAvailable = ( + lambda sm: sm.wor(sm.haveItem('Morph'), # normal exit + sm.wand(sm.haveItem('Super'), # go back to grapple room + sm.wor(sm.haveItem('SpaceJump'), + sm.wand(sm.haveItem('SpeedBooster'), sm.haveItem('HiJump'))))) # jump from the yellow plateform ennemy +) +locationsDict["Missile (Norfair Reserve Tank)"].AccessFrom = { + 'Bubble Mountain': lambda sm: sm.canEnterNorfairReserveAreaFromBubbleMoutain(), + 'Bubble Mountain Top': lambda sm: sm.canEnterNorfairReserveAreaFromBubbleMoutainTop() +} +locationsDict["Missile (Norfair Reserve Tank)"].Available = ( + lambda sm: sm.wand(sm.haveItem('Morph'), sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Bubble -> Norfair Reserve'])) +) +locationsDict["Missile (bubble Norfair green door)"].AccessFrom = { + 'Bubble Mountain': lambda sm: sm.canEnterNorfairReserveAreaFromBubbleMoutain(), + 'Bubble Mountain Top': lambda sm: sm.canEnterNorfairReserveAreaFromBubbleMoutainTop() +} +locationsDict["Missile (bubble Norfair green door)"].Available = ( + lambda sm: sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Bubble -> Norfair Reserve Missiles']) +) +locationsDict["Missile (bubble Norfair)"].AccessFrom = { + 'Bubble Mountain': lambda sm: SMBool(True) +} +locationsDict["Missile (bubble Norfair)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Missile (Speed Booster)"].AccessFrom = { + 'Bubble Mountain Top': lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.SpeedAreaBlueDoors), + sm.traverse('BubbleMountainTopRight')) +} +locationsDict["Missile (Speed Booster)"].Available = ( + lambda sm: sm.canHellRunToSpeedBooster() +) +locationsDict["Missile (Wave Beam)"].AccessFrom = { + 'Bubble Mountain Top': lambda sm: sm.canAccessDoubleChamberItems() +} +locationsDict["Missile (Wave Beam)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Missile (Gold Torizo)"].AccessFrom = { + 'LN Above GT': lambda sm: SMBool(True) +} +locationsDict["Missile (Gold Torizo)"].Available = ( + lambda sm: sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']) +) +locationsDict["Missile (Gold Torizo)"].PostAvailable = ( + lambda sm: sm.enoughStuffGT() +) +locationsDict["Super Missile (Gold Torizo)"].AccessFrom = { + 'Screw Attack Bottom': lambda sm: SMBool(True) +} +locationsDict["Super Missile (Gold Torizo)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Super Missile (Gold Torizo)"].PostAvailable = ( + lambda sm: sm.enoughStuffGT() +) +locationsDict["Missile (Mickey Mouse room)"].AccessFrom = { + 'LN Entrance': lambda sm: sm.wand(sm.canUsePowerBombs(), sm.canPassWorstRoom()), +} +locationsDict["Missile (Mickey Mouse room)"].Available = ( + lambda sm: sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']) +) +locationsDict["Missile (lower Norfair above fire flea room)"].AccessFrom = { + 'Firefleas': lambda sm: sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']) +} +locationsDict["Missile (lower Norfair above fire flea room)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Power Bomb (lower Norfair above fire flea room)"].AccessFrom = { + 'Firefleas Top': lambda sm: SMBool(True) +} +locationsDict["Power Bomb (lower Norfair above fire flea room)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Power Bomb (Power Bombs of shame)"].AccessFrom = { + 'Ridley Zone': lambda sm: sm.canUsePowerBombs() +} +locationsDict["Power Bomb (Power Bombs of shame)"].Available = ( + lambda sm: sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']) +) +locationsDict["Missile (lower Norfair near Wave Beam)"].AccessFrom = { + 'Firefleas': lambda sm: SMBool(True) +} +locationsDict["Missile (lower Norfair near Wave Beam)"].Available = ( + lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']), + sm.canDestroyBombWalls(), + sm.haveItem('Morph')) +) +locationsDict["Missile (Wrecked Ship middle)"].AccessFrom = { + 'Wrecked Ship Main': lambda sm: SMBool(True) +} +locationsDict["Missile (Wrecked Ship middle)"].Available = ( + lambda sm: sm.canPassBombPassages() +) +locationsDict["Missile (Gravity Suit)"].AccessFrom = { + 'Wrecked Ship Main': lambda sm: SMBool(True) +} +locationsDict["Missile (Gravity Suit)"].Available = ( + lambda sm: sm.wand(sm.canPassBowling(), + sm.canPassBombPassages()) +) +locationsDict["Missile (Wrecked Ship top)"].AccessFrom = { + 'Wrecked Ship Main': lambda sm: SMBool(True) +} +locationsDict["Missile (Wrecked Ship top)"].Available = ( + lambda sm: Bosses.bossDead(sm, 'Phantoon') +) +locationsDict["Super Missile (Wrecked Ship left)"].AccessFrom = { + 'Wrecked Ship Main': lambda sm: SMBool(True) +} +locationsDict["Super Missile (Wrecked Ship left)"].Available = ( + lambda sm: Bosses.bossDead(sm, 'Phantoon') +) +locationsDict["Missile (green Maridia shinespark)"].AccessFrom = { + 'Main Street Bottom': lambda sm: SMBool(True) +} +locationsDict["Missile (green Maridia shinespark)"].Available = ( + lambda sm: sm.wand(sm.haveItem('Gravity'), + sm.haveItem('SpeedBooster'), + sm.wor(sm.wand(sm.traverse('MainStreetBottomRight'), # run from room on the right + sm.wor(RomPatches.has(sm.player, RomPatches.AreaRandoGatesOther), + sm.haveItem('Super')), + sm.itemCountOk('ETank', 1)), # etank for the spark since sparking from low ground + sm.canSimpleShortCharge())) # run from above +) +locationsDict["Super Missile (green Maridia)"].AccessFrom = { + 'Main Street Bottom': lambda sm: sm.canDoOuterMaridia() +} +locationsDict["Super Missile (green Maridia)"].Available = ( + lambda sm: sm.haveItem('Morph') +) +locationsDict["Missile (green Maridia tatori)"].AccessFrom = { + 'Main Street Bottom': lambda sm: sm.wand(sm.wor(sm.traverse('FishTankRight'), + RomPatches.has(sm.player, RomPatches.MamaTurtleBlueDoor)), + sm.canDoOuterMaridia()), + 'Mama Turtle': lambda sm: SMBool(True) +} +locationsDict["Missile (green Maridia tatori)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Super Missile (yellow Maridia)"].AccessFrom = { + 'Watering Hole Bottom': lambda sm: SMBool(True) +} +locationsDict["Super Missile (yellow Maridia)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Missile (yellow Maridia super missile)"].AccessFrom = { + 'Watering Hole Bottom': lambda sm: SMBool(True) +} +locationsDict["Missile (yellow Maridia super missile)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Missile (yellow Maridia false wall)"].AccessFrom = { + 'Beach': lambda sm: SMBool(True) +} +locationsDict["Missile (yellow Maridia false wall)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Missile (left Maridia sand pit room)"].AccessFrom = { + 'Left Sandpit': lambda sm: sm.canClimbWestSandHole() +} +locationsDict["Missile (left Maridia sand pit room)"].Available = ( + lambda sm: sm.canAccessItemsInWestSandHole() +) +locationsDict["Missile (right Maridia sand pit room)"].AccessFrom = { + 'Right Sandpit': lambda sm: SMBool(True) +} +locationsDict["Missile (right Maridia sand pit room)"].Available = ( + lambda sm: sm.wor(sm.haveItem('Gravity'), + sm.wand(sm.haveItem('HiJump'), + sm.knowsGravLessLevel3())) +) +locationsDict["Power Bomb (right Maridia sand pit room)"].AccessFrom = { + 'Right Sandpit': lambda sm: sm.haveItem('Morph') +} +locationsDict["Power Bomb (right Maridia sand pit room)"].Available = ( + lambda sm: sm.wor(sm.haveItem('Gravity'), + sm.wand(sm.knowsGravLessLevel3(), + sm.haveItem('HiJump'), + sm.canSpringBallJump())) # https://www.youtube.com/watch?v=7LYYxphRRT0 +) +locationsDict["Missile (pink Maridia)"].AccessFrom = { + 'Aqueduct': lambda sm: SMBool(True) +} +locationsDict["Missile (pink Maridia)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Super Missile (pink Maridia)"].AccessFrom = { + 'Aqueduct': lambda sm: SMBool(True) +} +locationsDict["Super Missile (pink Maridia)"].Available = ( + lambda sm: SMBool(True) +) +locationsDict["Missile (Draygon)"].AccessFrom = { + 'Precious Room Top': lambda sm: SMBool(True) +} +locationsDict["Missile (Draygon)"].Available = ( + lambda sm: SMBool(True) +) + +# TODO::use the dict in solver/randomizer +# create the list that the solver/randomizer use +locations = [loc for loc in locationsDict.values()] + +class LocationsHelper: + # used by FillerRandom to know how many front fill steps it must perform + def getRandomFillHelp(startLocation): + helpByAp = { + "Firefleas Top": 3, + "Aqueduct": 1, + "Mama Turtle": 1, + "Watering Hole": 2, + "Etecoons Supers": 2, + "Gauntlet Top":1, + "Bubble Mountain":1 + } + return helpByAp[startLocation] if startLocation in helpByAp else 0 + + # for a given start AP, gives: + # - locations that can be used as majors/chozo in the start area + # - locations to preserve in the split + # - number of necessary majors locations to add in the start area, + # - number of necessary chozo locations to add in the start area + # locs are taken in the first n in the list + def getStartMajors(startLocation): + majLocsByAp = { + 'Gauntlet Top': ([ + "Missile (Crateria gauntlet right)", + "Missile (Crateria gauntlet left)" + ], ["Energy Tank, Terminator"], 1, 2), + 'Green Brinstar Elevator': ([ + "Missile (green Brinstar below super missile)" + ], ["Reserve Tank, Brinstar"], 1, 1), + 'Big Pink': ([ + "Missile (pink Brinstar top)", + "Missile (pink Brinstar bottom)" + ], ["Charge Beam"], 1, 2), + 'Etecoons Supers': ([ + "Energy Tank, Etecoons", + "Super Missile (green Brinstar bottom)", + ], ["Energy Tank, Etecoons"], 1, 2), + 'Firefleas Top': ([ + "Power Bomb (lower Norfair above fire flea room)", + "Energy Tank, Firefleas", + "Missile (lower Norfair near Wave Beam)", + "Missile (lower Norfair above fire flea room)" + ], ["Energy Tank, Firefleas"], 3, 4), + 'Business Center': ([ + "Energy Tank (Hi-Jump Boots)", + ], ["Hi-Jump Boots"], 1, 1), + 'Bubble Mountain': ([ + "Missile (bubble Norfair)" + ], ["Speed Booster", "Wave Beam"], 1, 1), + 'Mama Turtle': ([ + "Energy Tank, Mama turtle", + "Missile (green Maridia tatori)", + "Super Missile (green Maridia)" + ], ["Energy Tank, Mama turtle"], 2, 3), + 'Watering Hole': ([ + "Missile (yellow Maridia super missile)", + "Super Missile (yellow Maridia)", + "Missile (yellow Maridia false wall)" + ], [], 2, 3), + 'Aqueduct': ([ + "Missile (pink Maridia)", + "Super Missile (pink Maridia)", + "Missile (right Maridia sand pit room)" + ], ["Reserve Tank, Maridia"], 2, 3) + } + return majLocsByAp[startLocation] if startLocation in majLocsByAp else ([],[],0,0) diff --git a/worlds/sm/variaRandomizer/logic/__init__.py b/worlds/sm/variaRandomizer/logic/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/worlds/sm/variaRandomizer/logic/cache.py b/worlds/sm/variaRandomizer/logic/cache.py new file mode 100644 index 00000000..6e54fd33 --- /dev/null +++ b/worlds/sm/variaRandomizer/logic/cache.py @@ -0,0 +1,63 @@ +# the caching decorator for helpers functions +class VersionedCache(object): + __slots__ = ( 'cache', 'masterCache', 'nextSlot', 'size') + + def __init__(self): + self.cache = [] + self.masterCache = {} + self.nextSlot = 0 + self.size = 0 + + def reset(self): + # reinit the whole cache + self.masterCache = {} + self.update(0) + + def update(self, newKey): + cache = self.masterCache.get(newKey, None) + if cache is None: + cache = [ None ] * self.size + self.masterCache[newKey] = cache + self.cache = cache + + def decorator(self, func): + return self._decorate(func.__name__, self._new_slot(), func) + + # for lambdas + def ldeco(self, func): + return self._decorate(func.__code__, self._new_slot(), func) + + def _new_slot(self): + slot = self.nextSlot + self.nextSlot += 1 + self.size += 1 + return slot + + def _decorate(self, name, slot, func): + def _decorator(arg): + #ret = self.cache[slot] + #if ret is not None: + # return ret + #else: + ret = func(arg) + # self.cache[slot] = ret + return ret + return _decorator + +Cache = VersionedCache() + +class RequestCache(object): + def __init__(self): + self.results = {} + + def request(self, request, *args): + return ''.join([request] + [str(arg) for arg in args]) + + def store(self, request, result): + self.results[request] = result + + def get(self, request): + return self.results[request] if request in self.results else None + + def reset(self): + self.results.clear() diff --git a/worlds/sm/variaRandomizer/logic/helpers.py b/worlds/sm/variaRandomizer/logic/helpers.py new file mode 100644 index 00000000..72d2e4be --- /dev/null +++ b/worlds/sm/variaRandomizer/logic/helpers.py @@ -0,0 +1,831 @@ + +import math + +from logic.cache import Cache +from logic.smbool import SMBool, smboolFalse +from utils.parameters import Settings, easy, medium, diff2text +from rom.rom_patches import RomPatches +from utils.utils import normalizeRounding + + +class Helpers(object): + def __init__(self, smbm): + self.smbm = smbm + + # return bool + def haveItemCount(self, item, count): + return self.smbm.itemCount(item) >= count + + # return integer + @Cache.decorator + def energyReserveCount(self): + return self.smbm.itemCount('ETank') + self.smbm.itemCount('Reserve') + + def energyReserveCountOkDiff(self, difficulties, mult=1.0): + if difficulties is None or len(difficulties) == 0: + return smboolFalse + + def f(difficulty): + return self.smbm.energyReserveCountOk(normalizeRounding(difficulty[0] / mult), difficulty=difficulty[1]) + + result = f(difficulties[0]) + for difficulty in difficulties[1:]: + result = self.smbm.wor(result, f(difficulty)) + return result + + def energyReserveCountOkHellRun(self, hellRunName, mult=1.0): + difficulties = Settings.hellRuns[hellRunName] + result = self.energyReserveCountOkDiff(difficulties, mult) + + if result == True: + result.knows = [hellRunName+'HellRun'] + + return result + + # gives damage reduction factor with the current suits + # envDmg : if true (default) will return environmental damage reduction + def getDmgReduction(self, envDmg=True): + ret = 1.0 + sm = self.smbm + hasVaria = sm.haveItem('Varia') + hasGrav = sm.haveItem('Gravity') + items = [] + if RomPatches.has(sm.player, RomPatches.NoGravityEnvProtection): + if hasVaria: + items = ['Varia'] + if envDmg: + ret = 4.0 + else: + ret = 2.0 + if hasGrav and not envDmg: + ret = 4.0 + items = ['Gravity'] + elif RomPatches.has(sm.player, RomPatches.ProgressiveSuits): + if hasVaria: + items.append('Varia') + ret *= 2 + if hasGrav: + items.append('Gravity') + ret *= 2 + else: + if hasVaria: + ret = 2.0 + items = ['Varia'] + if hasGrav: + ret = 4.0 + items = ['Gravity'] + return (ret, items) + + # higher values for mult means room is that much "easier" (HP mult) + def energyReserveCountOkHardRoom(self, roomName, mult=1.0): + difficulties = Settings.hardRooms[roomName] + (dmgRed, items) = self.getDmgReduction() + mult *= dmgRed + result = self.energyReserveCountOkDiff(difficulties, mult) + + if result == True: + result.knows = ['HardRoom-'+roomName] + if dmgRed != 1.0: + result._items.append(items) + return result + + @Cache.decorator + def heatProof(self): + sm = self.smbm + return sm.wor(sm.haveItem('Varia'), + sm.wand(sm.wnot(RomPatches.has(sm.player, RomPatches.NoGravityEnvProtection)), + sm.wnot(RomPatches.has(sm.player, RomPatches.ProgressiveSuits)), + sm.haveItem('Gravity'))) + + # helper here because we can't define "sublambdas" in locations + def getPiratesPseudoScrewCoeff(self): + sm = self.smbm + ret = 1.0 + if RomPatches.has(sm.player, RomPatches.NerfedCharge).bool == True: + ret = 4.0 + return ret + + @Cache.decorator + def canFireChargedShots(self): + sm = self.smbm + return sm.wor(sm.haveItem('Charge'), RomPatches.has(sm.player, RomPatches.NerfedCharge)) + + # higher values for mult means hell run is that much "easier" (HP mult) + def canHellRun(self, hellRun, mult=1.0, minE=2): + sm = self.smbm + + items = [] + isHeatProof = sm.heatProof() + if isHeatProof == True: + return isHeatProof + if sm.wand(RomPatches.has(sm.player, RomPatches.ProgressiveSuits), sm.haveItem('Gravity')).bool == True: + # half heat protection + mult *= 2.0 + minE /= 2.0 + items.append('Gravity') + if self.energyReserveCount() >= minE: + if hellRun != 'LowerNorfair': + ret = self.energyReserveCountOkHellRun(hellRun, mult) + if ret.bool == True: + ret._items.append(items) + return ret + else: + tanks = self.energyReserveCount() + multCF = mult + if tanks >= 14: + multCF *= 2.0 + nCF = int(math.ceil(2/multCF)) + ret = sm.wand(self.energyReserveCountOkHellRun(hellRun, mult), + self.canCrystalFlash(nCF)) + if ret.bool == True: + if sm.haveItem('Gravity') == True: + ret.difficulty *= 0.7 + ret._items.append('Gravity') + elif sm.haveItem('ScrewAttack') == True: + ret.difficulty *= 0.7 + ret._items.append('ScrewAttack') + #nPB = self.smbm.itemCount('PowerBomb') + #print("canHellRun LN. tanks=" + str(tanks) + ", nCF=" + str(nCF) + ", nPB=" + str(nPB) + ", mult=" + str(mult) + ", heatProof=" + str(isHeatProof.bool) + ", ret=" + str(ret)) + return ret + else: + return smboolFalse + + @Cache.decorator + def canMockball(self): + sm = self.smbm + return sm.wand(sm.haveItem('Morph'), + sm.knowsMockball()) + + @Cache.decorator + def canFly(self): + sm = self.smbm + return sm.wor(sm.haveItem('SpaceJump'), + sm.canInfiniteBombJump()) + + @Cache.decorator + def canSimpleShortCharge(self): + sm = self.smbm + return sm.wand(sm.haveItem('SpeedBooster'), + sm.wor(sm.knowsSimpleShortCharge(), + sm.knowsShortCharge())) + + @Cache.decorator + def canShortCharge(self): + sm = self.smbm + return sm.wand(sm.haveItem('SpeedBooster'), sm.knowsShortCharge()) + + @Cache.decorator + def canUseBombs(self): + sm = self.smbm + return sm.wand(sm.haveItem('Morph'), sm.haveItem('Bomb')) + + @Cache.decorator + def canInfiniteBombJump(self): + sm = self.smbm + return sm.wand(sm.haveItem('Morph'), sm.haveItem('Bomb'), sm.knowsInfiniteBombJump()) + + @Cache.decorator + def canInfiniteBombJumpSuitless(self): + sm = self.smbm + return sm.wand(sm.haveItem('Morph'), sm.haveItem('Bomb'), sm.knowsInfiniteBombJumpSuitless()) + + @Cache.decorator + def haveMissileOrSuper(self): + sm = self.smbm + return sm.wor(sm.haveItem('Missile'), sm.haveItem('Super')) + + @Cache.decorator + def canOpenRedDoors(self): + sm = self.smbm + return sm.wor(sm.wand(sm.wnot(RomPatches.has(sm.player, RomPatches.RedDoorsMissileOnly)), + sm.haveMissileOrSuper()), + sm.haveItem('Missile')) + + @Cache.decorator + def canOpenEyeDoors(self): + sm = self.smbm + return sm.wor(RomPatches.has(sm.player, RomPatches.NoGadoras), + sm.haveMissileOrSuper()) + + @Cache.decorator + def canOpenGreenDoors(self): + return self.smbm.haveItem('Super') + + @Cache.decorator + def canGreenGateGlitch(self): + sm = self.smbm + return sm.wand(sm.haveItem('Super'), + sm.knowsGreenGateGlitch()) + + @Cache.decorator + def canBlueGateGlitch(self): + sm = self.smbm + return sm.wand(sm.haveMissileOrSuper(), + sm.knowsGreenGateGlitch()) + @Cache.decorator + def canUsePowerBombs(self): + sm = self.smbm + return sm.wand(sm.haveItem('Morph'), sm.haveItem('PowerBomb')) + + canOpenYellowDoors = canUsePowerBombs + + @Cache.decorator + def canUseSpringBall(self): + sm = self.smbm + return sm.wand(sm.haveItem('Morph'), + sm.haveItem('SpringBall')) + + @Cache.decorator + def canSpringBallJump(self): + sm = self.smbm + return sm.wand(sm.canUseSpringBall(), + sm.knowsSpringBallJump()) + + @Cache.decorator + def canDoubleSpringBallJump(self): + sm = self.smbm + return sm.wand(sm.canUseSpringBall(), + sm.haveItem('HiJump'), + sm.knowsDoubleSpringBallJump()) + + @Cache.decorator + def canSpringBallJumpFromWall(self): + sm = self.smbm + return sm.wand(sm.canUseSpringBall(), + sm.knowsSpringBallJumpFromWall()) + + @Cache.decorator + def canDestroyBombWalls(self): + sm = self.smbm + return sm.wor(sm.wand(sm.haveItem('Morph'), + sm.wor(sm.haveItem('Bomb'), + sm.haveItem('PowerBomb'))), + sm.haveItem('ScrewAttack')) + + @Cache.decorator + def canDestroyBombWallsUnderwater(self): + sm = self.smbm + return sm.wor(sm.wand(sm.haveItem('Gravity'), + sm.canDestroyBombWalls()), + sm.wand(sm.haveItem('Morph'), + sm.wor(sm.haveItem('Bomb'), + sm.haveItem('PowerBomb')))) + + @Cache.decorator + def canPassBombPassages(self): + sm = self.smbm + return sm.wor(sm.canUseBombs(), + sm.canUsePowerBombs()) + + @Cache.decorator + def canMorphJump(self): + # small hop in morph ball form + sm = self.smbm + return sm.wor(sm.canPassBombPassages(), sm.haveItem('SpringBall')) + + def canCrystalFlash(self, n=1): + sm = self.smbm + return sm.wand(sm.canUsePowerBombs(), + sm.itemCountOk('Missile', 2*n), + sm.itemCountOk('Super', 2*n), + sm.itemCountOk('PowerBomb', 2*n+1)) + + @Cache.decorator + def canCrystalFlashClip(self): + sm = self.smbm + return sm.wand(sm.canCrystalFlash(), + sm.wor(sm.wand(sm.haveItem('Gravity'), + sm.canUseBombs(), + sm.knowsCrystalFlashClip()), + sm.wand(sm.knowsSuitlessCrystalFlashClip(), + sm.itemCountOk('PowerBomb', 4)))) + + @Cache.decorator + def canDoLowGauntlet(self): + sm = self.smbm + return sm.wand(sm.canShortCharge(), + sm.canUsePowerBombs(), + sm.itemCountOk('ETank', 1), + sm.knowsLowGauntlet()) + + @Cache.decorator + def canUseHyperBeam(self): + sm = self.smbm + return sm.haveItem('Hyper') + + @Cache.decorator + def getBeamDamage(self): + sm = self.smbm + standardDamage = 20 + + if sm.wand(sm.haveItem('Ice'), + sm.haveItem('Wave'), + sm.haveItem('Plasma')) == True: + standardDamage = 300 + elif sm.wand(sm.haveItem('Wave'), + sm.haveItem('Plasma')) == True: + standardDamage = 250 + elif sm.wand(sm.haveItem('Ice'), + sm.haveItem('Plasma')) == True: + standardDamage = 200 + elif sm.haveItem('Plasma') == True: + standardDamage = 150 + elif sm.wand(sm.haveItem('Ice'), + sm.haveItem('Wave'), + sm.haveItem('Spazer')) == True: + standardDamage = 100 + elif sm.wand(sm.haveItem('Wave'), + sm.haveItem('Spazer')) == True: + standardDamage = 70 + elif sm.wand(sm.haveItem('Ice'), + sm.haveItem('Spazer')) == True: + standardDamage = 60 + elif sm.wand(sm.haveItem('Ice'), + sm.haveItem('Wave')) == True: + standardDamage = 60 + elif sm.haveItem('Wave') == True: + standardDamage = 50 + elif sm.haveItem('Spazer') == True: + standardDamage = 40 + elif sm.haveItem('Ice') == True: + standardDamage = 30 + + return standardDamage + + # returns a tuple with : + # + # - a floating point number : 0 if boss is unbeatable with + # current equipment, and an ammo "margin" (ex : 1.5 means we have 50% + # more firepower than absolutely necessary). Useful to compute boss + # difficulty when not having charge. If player has charge, the actual + # value is not useful, and is guaranteed to be > 2. + # + # - estimation of the fight duration in seconds (well not really, it + # is if you fire and land shots perfectly and constantly), giving info + # to compute boss fight difficulty + def canInflictEnoughDamages(self, bossEnergy, doubleSuper=False, charge=True, power=False, givesDrops=True, ignoreMissiles=False, ignoreSupers=False): + # TODO: handle special beam attacks ? (http://deanyd.net/sm/index.php?title=Charge_Beam_Combos) + sm = self.smbm + items = [] + + # http://deanyd.net/sm/index.php?title=Damage + standardDamage = 0 + if sm.canFireChargedShots().bool == True and charge == True: + standardDamage = self.getBeamDamage() + items.append('Charge') + # charge triples the damage + chargeDamage = standardDamage + if sm.haveItem('Charge').bool == True: + chargeDamage *= 3.0 + + # missile 100 damages, super missile 300 damages, PBs 200 dmg, 5 in each extension + missilesAmount = sm.itemCount('Missile') * 5 + if ignoreMissiles == True: + missilesDamage = 0 + else: + missilesDamage = missilesAmount * 100 + if missilesAmount > 0: + items.append('Missile') + supersAmount = sm.itemCount('Super') * 5 + if ignoreSupers == True: + oneSuper = 0 + else: + oneSuper = 300.0 + if supersAmount > 0: + items.append('Super') + if doubleSuper == True: + oneSuper *= 2 + supersDamage = supersAmount * oneSuper + powerDamage = 0 + powerAmount = 0 + if power == True and sm.haveItem('PowerBomb') == True: + powerAmount = sm.itemCount('PowerBomb') * 5 + powerDamage = powerAmount * 200 + items.append('PowerBomb') + + canBeatBoss = chargeDamage > 0 or givesDrops or (missilesDamage + supersDamage + powerDamage) >= bossEnergy + if not canBeatBoss: + return (0, 0, []) + + ammoMargin = (missilesDamage + supersDamage + powerDamage) / bossEnergy + if chargeDamage > 0: + ammoMargin += 2 + + missilesDPS = Settings.algoSettings['missilesPerSecond'] * 100.0 + supersDPS = Settings.algoSettings['supersPerSecond'] * 300.0 + if doubleSuper == True: + supersDPS *= 2 + if powerDamage > 0: + powerDPS = Settings.algoSettings['powerBombsPerSecond'] * 200.0 + else: + powerDPS = 0.0 + chargeDPS = chargeDamage * Settings.algoSettings['chargedShotsPerSecond'] + # print("chargeDPS=" + str(chargeDPS)) + dpsDict = { + missilesDPS: (missilesAmount, 100.0), + supersDPS: (supersAmount, oneSuper), + powerDPS: (powerAmount, 200.0), + # no boss will take more 10000 charged shots + chargeDPS: (10000, chargeDamage) + } + secs = 0 + for dps in sorted(dpsDict, reverse=True): + amount = dpsDict[dps][0] + one = dpsDict[dps][1] + if dps == 0 or one == 0 or amount == 0: + continue + fire = min(bossEnergy / one, amount) + secs += fire * (one / dps) + bossEnergy -= fire * one + if bossEnergy <= 0: + break + if bossEnergy > 0: + # print ('!! drops !! ') + secs += bossEnergy * Settings.algoSettings['missileDropsPerMinute'] * 100 / 60 + # print('ammoMargin = ' + str(ammoMargin) + ', secs = ' + str(secs)) + + return (ammoMargin, secs, items) + + # return diff score, or -1 if below minimum energy in diffTbl + def computeBossDifficulty(self, ammoMargin, secs, diffTbl, energyDiff=0): + sm = self.smbm + + # actual fight duration : + rate = None + if 'Rate' in diffTbl: + rate = float(diffTbl['Rate']) + if rate is None: + duration = 120.0 + else: + duration = secs / rate + # print('rate=' + str(rate) + ', duration=' + str(duration)) + (suitsCoeff, items) = sm.getDmgReduction(envDmg=False) + suitsCoeff /= 2.0 + energyCount = self.energyReserveCount() + energy = suitsCoeff * (1 + energyCount + energyDiff) +# print("energy="+str(energy)+", energyCount="+str(energyCount)+",energyDiff="+str(energyDiff)+",suitsCoeff="+str(suitsCoeff)) + + # add all energy in used items + items += sm.energyReserveCountOk(energyCount).items + + energyDict = None + if 'Energy' in diffTbl: + energyDict = diffTbl['Energy'] + difficulty = medium + # get difficulty by energy + if energyDict: + energyDict = {float(k):float(v) for k,v in energyDict.items()} + keyz = sorted(energyDict.keys()) + if len(keyz) > 0: + current = keyz[0] + if energy < current: + return (-1, []) + sup = None + difficulty = energyDict[current] + for k in keyz: + if k > energy: + sup=k + break + current = k + difficulty = energyDict[k] + # interpolate if we can + if energy > current and sup is not None: + difficulty += (energyDict[sup] - difficulty)/(sup - current) * (energy - current) + # print("energy=" + str(energy) + ", base diff=" + str(difficulty)) + # adjust by fight duration + difficulty *= (duration / 120) + # and by ammo margin + # only augment difficulty in case of no charge, don't lower it. + # if we have charge, ammoMargin will have a huge value (see canInflictEnoughDamages), + # so this does not apply + diffAdjust = (1 - (ammoMargin - Settings.algoSettings['ammoMarginIfNoCharge'])) + if diffAdjust > 1: + difficulty *= diffAdjust +# print("final diff: "+str(round(difficulty, 2))) + + return (round(difficulty, 2), items) + + @Cache.decorator + def enoughStuffSporeSpawn(self): + sm = self.smbm + return sm.wor(sm.haveItem('Missile'), sm.haveItem('Super'), sm.haveItem('Charge')) + + @Cache.decorator + def enoughStuffCroc(self): + sm = self.smbm + # say croc has ~5000 energy, and ignore its useless drops + (ammoMargin, secs, items) = self.canInflictEnoughDamages(5000, givesDrops=False) + if ammoMargin == 0: + return sm.wand(sm.knowsLowAmmoCroc(), + sm.wor(sm.itemCountOk("Missile", 2), + sm.wand(sm.haveItem('Missile'), + sm.haveItem('Super')))) + else: + return SMBool(True, easy, items=items) + + @Cache.decorator + def enoughStuffBotwoon(self): + sm = self.smbm + (ammoMargin, secs, items) = self.canInflictEnoughDamages(6000, givesDrops=False) + diff = SMBool(True, easy, [], items) + lowStuff = sm.knowsLowStuffBotwoon() + if ammoMargin == 0 and lowStuff.bool: + (ammoMargin, secs, items) = self.canInflictEnoughDamages(3500, givesDrops=False) + diff = SMBool(lowStuff.bool, lowStuff.difficulty, lowStuff.knows, items) + if ammoMargin == 0: + return smboolFalse + fight = sm.wor(sm.energyReserveCountOk(math.ceil(4/sm.getDmgReduction(envDmg=False)[0])), + lowStuff) + return sm.wandmax(fight, diff) + + @Cache.decorator + def enoughStuffGT(self): + sm = self.smbm + hasBeams = sm.wand(sm.haveItem('Charge'), sm.haveItem('Plasma')).bool + (ammoMargin, secs, items) = self.canInflictEnoughDamages(9000, ignoreMissiles=True, givesDrops=hasBeams) + diff = SMBool(True, easy, [], items) + lowStuff = sm.knowsLowStuffGT() + if ammoMargin == 0 and lowStuff.bool: + (ammoMargin, secs, items) = self.canInflictEnoughDamages(3000, ignoreMissiles=True) + diff = SMBool(lowStuff.bool, lowStuff.difficulty, lowStuff.knows, items) + if ammoMargin == 0: + return smboolFalse + fight = sm.wor(sm.energyReserveCountOk(math.ceil(8/sm.getDmgReduction(envDmg=False)[0])), + lowStuff) + return sm.wandmax(fight, diff) + + @Cache.decorator + def enoughStuffsRidley(self): + sm = self.smbm + if not sm.haveItem('Morph') and not sm.haveItem('ScrewAttack'): + return smboolFalse + (ammoMargin, secs, ammoItems) = self.canInflictEnoughDamages(18000, doubleSuper=True, power=True, givesDrops=False) + if ammoMargin == 0: + return smboolFalse + + # print('RIDLEY', ammoMargin, secs) + (diff, defenseItems) = self.computeBossDifficulty(ammoMargin, secs, + Settings.bossesDifficulty['Ridley']) + if diff < 0: + return smboolFalse + else: + return SMBool(True, diff, items=ammoItems+defenseItems) + + @Cache.decorator + def enoughStuffsKraid(self): + sm = self.smbm + (ammoMargin, secs, ammoItems) = self.canInflictEnoughDamages(1000) + if ammoMargin == 0: + return smboolFalse + #print('KRAID True ', ammoMargin, secs) + (diff, defenseItems) = self.computeBossDifficulty(ammoMargin, secs, + Settings.bossesDifficulty['Kraid']) + if diff < 0: + return smboolFalse + + return SMBool(True, diff, items=ammoItems+defenseItems) + + def adjustHealthDropDiff(self, difficulty): + (dmgRed, items) = self.getDmgReduction(envDmg=False) + # 2 is Varia suit, considered standard eqt for boss fights + # there's certainly a smarter way to do this but... + if dmgRed < 2: + difficulty *= Settings.algoSettings['dmgReductionDifficultyFactor'] + elif dmgRed > 2: + difficulty /= Settings.algoSettings['dmgReductionDifficultyFactor'] + return difficulty + + @Cache.decorator + def enoughStuffsDraygon(self): + sm = self.smbm + if not sm.haveItem('Morph') and not sm.haveItem('Gravity'): + return smboolFalse + # some ammo to destroy the turrets during the fight + if not sm.haveMissileOrSuper(): + return smboolFalse + (ammoMargin, secs, ammoItems) = self.canInflictEnoughDamages(6000) + # print('DRAY', ammoMargin, secs) + if ammoMargin > 0: + (diff, defenseItems) = self.computeBossDifficulty(ammoMargin, secs, + Settings.bossesDifficulty['Draygon']) + if diff < 0: + fight = smboolFalse + else: + fight = SMBool(True, diff, items=ammoItems+defenseItems) + if sm.haveItem('Gravity') == False: + fight.difficulty *= Settings.algoSettings['draygonNoGravityMalus'] + else: + fight._items.append('Gravity') + if not sm.haveItem('Morph'): + fight.difficulty *= Settings.algoSettings['draygonNoMorphMalus'] + if sm.haveItem('Gravity') and sm.haveItem('ScrewAttack'): + fight.difficulty /= Settings.algoSettings['draygonScrewBonus'] + fight.difficulty = self.adjustHealthDropDiff(fight.difficulty) + else: + fight = smboolFalse + # for grapple kill considers energy drained by wall socket + 2 spankings by Dray + # (original 99 energy used for rounding) + nTanksGrapple = (240/sm.getDmgReduction(envDmg=True)[0] + 2*160/sm.getDmgReduction(envDmg=False)[0])/100 + return sm.wor(fight, + sm.wand(sm.knowsDraygonGrappleKill(), + sm.haveItem('Grapple'), + sm.energyReserveCountOk(nTanksGrapple)), + sm.wand(sm.knowsMicrowaveDraygon(), + sm.haveItem('Plasma'), + sm.canFireChargedShots(), + sm.haveItem('XRayScope')), + sm.wand(sm.haveItem('Gravity'), + sm.energyReserveCountOk(3), + sm.knowsDraygonSparkKill(), + sm.haveItem('SpeedBooster'))) + + @Cache.decorator + def enoughStuffsPhantoon(self): + sm = self.smbm + (ammoMargin, secs, ammoItems) = self.canInflictEnoughDamages(2500, doubleSuper=True) + if ammoMargin == 0: + return smboolFalse + # print('PHANTOON', ammoMargin, secs) + (difficulty, defenseItems) = self.computeBossDifficulty(ammoMargin, secs, + Settings.bossesDifficulty['Phantoon']) + if difficulty < 0: + return smboolFalse + hasCharge = sm.canFireChargedShots() + hasScrew = sm.haveItem('ScrewAttack') + if hasScrew: + difficulty /= Settings.algoSettings['phantoonFlamesAvoidBonusScrew'] + defenseItems += hasScrew.items + elif hasCharge: + difficulty /= Settings.algoSettings['phantoonFlamesAvoidBonusCharge'] + defenseItems += hasCharge.items + elif not hasCharge and sm.itemCount('Missile') <= 2: # few missiles is harder + difficulty *= Settings.algoSettings['phantoonLowMissileMalus'] + difficulty = self.adjustHealthDropDiff(difficulty) + fight = SMBool(True, difficulty, items=ammoItems+defenseItems) + + return sm.wor(fight, + sm.wand(sm.knowsMicrowavePhantoon(), + sm.haveItem('Plasma'), + sm.canFireChargedShots(), + sm.haveItem('XRayScope'))) + + def mbEtankCheck(self): + sm = self.smbm + if sm.wor(RomPatches.has(sm.player, RomPatches.NerfedRainbowBeam), RomPatches.has(sm.player, RomPatches.TourianSpeedup)): + # "add" energy for difficulty calculations + energy = 2.8 if sm.haveItem('Varia') else 2.6 + return (True, energy) + nTanks = sm.energyReserveCount() + energyDiff = 0 + if sm.haveItem('Varia') == False: + # "remove" 3 etanks (accounting for rainbow beam damage without varia) + if nTanks < 6: + return (False, 0) + energyDiff = -3 + elif nTanks < 3: + return (False, 0) + return (True, energyDiff) + + @Cache.decorator + def enoughStuffsMotherbrain(self): + sm = self.smbm + # MB1 can't be hit by charge beam + (ammoMargin, secs, _) = self.canInflictEnoughDamages(3000, charge=False, givesDrops=False) + if ammoMargin == 0: + return smboolFalse + # requires 10-10 to break the glass + if sm.itemCount('Missile') <= 1 or sm.itemCount('Super') <= 1: + return smboolFalse + # we actually don't give a shit about MB1 difficulty, + # since we embark its health in the following calc + (ammoMargin, secs, ammoItems) = self.canInflictEnoughDamages(18000 + 3000, givesDrops=False) + if ammoMargin == 0: + return smboolFalse + (possible, energyDiff) = self.mbEtankCheck() + if possible == False: + return smboolFalse + # print('MB2', ammoMargin, secs) + #print("ammoMargin: {}, secs: {}, settings: {}, energyDiff: {}".format(ammoMargin, secs, Settings.bossesDifficulty['MotherBrain'], energyDiff)) + (diff, defenseItems) = self.computeBossDifficulty(ammoMargin, secs, Settings.bossesDifficulty['MotherBrain'], energyDiff) + if diff < 0: + return smboolFalse + return SMBool(True, diff, items=ammoItems+defenseItems) + + @Cache.decorator + def canPassMetroids(self): + sm = self.smbm + return sm.wor(sm.wand(sm.haveItem('Ice'), sm.haveMissileOrSuper()), + # to avoid leaving tourian to refill power bombs + sm.itemCountOk('PowerBomb', 3)) + + @Cache.decorator + def canPassZebetites(self): + sm = self.smbm + return sm.wor(sm.wand(sm.haveItem('Ice'), sm.knowsIceZebSkip()), + sm.wand(sm.haveItem('SpeedBooster'), sm.knowsSpeedZebSkip()), + # account for one zebetite, refill may be necessary + SMBool(self.canInflictEnoughDamages(1100, charge=False, givesDrops=False, ignoreSupers=True)[0] >= 1, 0)) + + @Cache.decorator + def enoughStuffTourian(self): + sm = self.smbm + ret = self.smbm.wand(sm.wor(RomPatches.has(sm.player, RomPatches.TourianSpeedup), + sm.wand(sm.canPassMetroids(), sm.canPassZebetites())), + sm.canOpenRedDoors(), + sm.enoughStuffsMotherbrain(), + sm.wor(RomPatches.has(sm.player, RomPatches.OpenZebetites), sm.haveItem('Morph'))) + return ret + +class Pickup: + def __init__(self, itemsPickup): + self.itemsPickup = itemsPickup + + def enoughMinors(self, smbm, minorLocations): + if self.itemsPickup == 'all': + return len(minorLocations) == 0 + else: + return True + + def enoughMajors(self, smbm, majorLocations): + if self.itemsPickup == 'all': + return len(majorLocations) == 0 + else: + return True + +class Bosses: + # bosses helpers to know if they are dead + areaBosses = { + # classic areas + 'Brinstar': 'Kraid', + 'Norfair': 'Ridley', + 'LowerNorfair': 'Ridley', + 'WreckedShip': 'Phantoon', + 'Maridia': 'Draygon', + # solver areas + 'Blue Brinstar': 'Kraid', + 'Brinstar Hills': 'Kraid', + 'Bubble Norfair': 'Ridley', + 'Bubble Norfair Bottom': 'Ridley', + 'Bubble Norfair Reserve': 'Ridley', + 'Bubble Norfair Speed': 'Ridley', + 'Bubble Norfair Wave': 'Ridley', + 'Draygon Boss': 'Draygon', + 'Green Brinstar': 'Kraid', + 'Green Brinstar Reserve': 'Kraid', + 'Kraid': 'Kraid', + 'Kraid Boss': 'Kraid', + 'Left Sandpit': 'Draygon', + 'Lower Norfair After Amphitheater': 'Ridley', + 'Lower Norfair Before Amphitheater': 'Ridley', + 'Lower Norfair Screw Attack': 'Ridley', + 'Maridia Forgotten Highway': 'Draygon', + 'Maridia Green': 'Draygon', + 'Maridia Pink Bottom': 'Draygon', + 'Maridia Pink Top': 'Draygon', + 'Maridia Sandpits': 'Draygon', + 'Norfair Entrance': 'Ridley', + 'Norfair Grapple Escape': 'Ridley', + 'Norfair Ice': 'Ridley', + 'Phantoon Boss': 'Phantoon', + 'Pink Brinstar': 'Kraid', + 'Red Brinstar': 'Kraid', + 'Red Brinstar Top': 'Kraid', + 'Ridley Boss': 'Ridley', + 'Right Sandpit': 'Draygon', + 'Warehouse': 'Kraid', + 'WreckedShip': 'Phantoon', + 'WreckedShip Back': 'Phantoon', + 'WreckedShip Bottom': 'Phantoon', + 'WreckedShip Gravity': 'Phantoon', + 'WreckedShip Main': 'Phantoon', + 'WreckedShip Top': 'Phantoon' + } + + @staticmethod + def Golden4(): + return ['Draygon', 'Kraid', 'Phantoon', 'Ridley'] + + @staticmethod + def bossDead(sm, boss): + return sm.haveItem(boss) + + @staticmethod + def areaBossDead(sm, area): + if area not in Bosses.areaBosses: + return True + return Bosses.bossDead(sm, Bosses.areaBosses[area]) + + @staticmethod + def allBossesDead(smbm): + return smbm.wand(Bosses.bossDead(smbm, 'Kraid'), + Bosses.bossDead(smbm, 'Phantoon'), + Bosses.bossDead(smbm, 'Draygon'), + Bosses.bossDead(smbm, 'Ridley')) + +def diffValue2txt(diff): + last = 0 + for d in sorted(diff2text.keys()): + if diff >= last and diff < d: + return diff2text[last] + last = d + return None diff --git a/worlds/sm/variaRandomizer/logic/logic.py b/worlds/sm/variaRandomizer/logic/logic.py new file mode 100644 index 00000000..5d47932b --- /dev/null +++ b/worlds/sm/variaRandomizer/logic/logic.py @@ -0,0 +1,26 @@ +# entry point for the logic implementation + +class Logic(object): + @staticmethod + def factory(implementation): + if implementation == 'vanilla': + from graph.vanilla.graph_helpers import HelpersGraph + from graph.vanilla.graph_access import accessPoints + from graph.vanilla.graph_locations import locations + from graph.vanilla.graph_locations import LocationsHelper + Logic.locations = locations + Logic.accessPoints = accessPoints + Logic.HelpersGraph = HelpersGraph + Logic.patches = implementation + Logic.LocationsHelper = LocationsHelper + elif implementation == 'rotation': + from graph.rotation.graph_helpers import HelpersGraph + from graph.rotation.graph_access import accessPoints + from graph.rotation.graph_locations import locations + from graph.rotation.graph_locations import LocationsHelper + Logic.locations = locations + Logic.accessPoints = accessPoints + Logic.HelpersGraph = HelpersGraph + Logic.patches = implementation + Logic.LocationsHelper = LocationsHelper + Logic.implementation = implementation diff --git a/worlds/sm/variaRandomizer/logic/smbool.py b/worlds/sm/variaRandomizer/logic/smbool.py new file mode 100644 index 00000000..b7f596cb --- /dev/null +++ b/worlds/sm/variaRandomizer/logic/smbool.py @@ -0,0 +1,122 @@ +def flatten(l): + if type(l) is list: + return [ y for x in l for y in flatten(x) ] + else: + return [ l ] + +# super metroid boolean +class SMBool: + __slots__ = ('bool', 'difficulty', '_knows', '_items') + def __init__(self, boolean, difficulty=0, knows=[], items=[]): + self.bool = boolean + self.difficulty = difficulty + self._knows = knows + self._items = items + + @property + def knows(self): + self._knows = list(set(flatten(self._knows))) + return self._knows + + @knows.setter + def knows(self, knows): + self._knows = knows + + @property + def items(self): + self._items = list(set(flatten(self._items))) + return self._items + + @items.setter + def items(self, items): + self._items = items + + def __repr__(self): + # to display the smbool as a string + return 'SMBool({}, {}, {}, {})'.format(self.bool, self.difficulty, sorted(self.knows), sorted(self.items)) + + def __getitem__(self, index): + # to acces the smbool as [0] for the bool and [1] for the difficulty. + # required when we load a json preset where the smbool is stored as a list, + # and we add missing smbools to it, so we have a mix of lists and smbools. + if index == 0: + return self.bool + elif index == 1: + return self.difficulty + + def __bool__(self): + # when used in boolean expressions (with and/or/not) (python3) + return self.bool + + def __eq__(self, other): + # for == + return self.bool == other + + def __ne__(self, other): + # for != + return self.bool != other + + def __lt__(self, other): + # for < + if self.bool and other.bool: + return self.difficulty < other.difficulty + else: + return self.bool + + def __copy__(self): + return SMBool(self.bool, self.difficulty, self._knows, self._items) + + def json(self): + # as we have slots instead of dict + return {'bool': self.bool, 'difficulty': self.difficulty, 'knows': self.knows, 'items': self.items} + + def wand(*args): + # looping here is faster than using "if ... in" construct + for smb in args: + if not smb.bool: + return smboolFalse + + difficulty = 0 + + for smb in args: + difficulty += smb.difficulty + + return SMBool(True, + difficulty, + [ smb._knows for smb in args ], + [ smb._items for smb in args ]) + + def wandmax(*args): + # looping here is faster than using "if ... in" construct + for smb in args: + if not smb.bool: + return smboolFalse + + difficulty = 0 + + for smb in args: + if smb.difficulty > difficulty: + difficulty = smb.difficulty + + return SMBool(True, + difficulty, + [ smb._knows for smb in args ], + [ smb._items for smb in args ]) + + def wor(*args): + # looping here is faster than using "if ... in" construct + for smb in args: + if smb.bool: + return min(args) + + return smboolFalse + + # negates boolean part of the SMBool + def wnot(a): + return smboolFalse if a.bool else SMBool(True, a.difficulty) + + __and__ = wand + __or__ = wor + __not__ = wnot + +smboolFalse = SMBool(False) diff --git a/worlds/sm/variaRandomizer/logic/smboolmanager.py b/worlds/sm/variaRandomizer/logic/smboolmanager.py new file mode 100644 index 00000000..b4ee2918 --- /dev/null +++ b/worlds/sm/variaRandomizer/logic/smboolmanager.py @@ -0,0 +1,241 @@ +# object to handle the smbools and optimize them + +from logic.cache import Cache +from logic.smbool import SMBool, smboolFalse +from logic.helpers import Bosses +from logic.logic import Logic +from utils.doorsmanager import DoorsManager +from utils.parameters import Knows, isKnows +import logging +import sys + +class SMBoolManager(object): + items = ['ETank', 'Missile', 'Super', 'PowerBomb', 'Bomb', 'Charge', 'Ice', 'HiJump', 'SpeedBooster', 'Wave', 'Spazer', 'SpringBall', 'Varia', 'Plasma', 'Grapple', 'Morph', 'Reserve', 'Gravity', 'XRayScope', 'SpaceJump', 'ScrewAttack', 'Nothing', 'NoEnergy', 'MotherBrain', 'Hyper'] + Bosses.Golden4() + countItems = ['Missile', 'Super', 'PowerBomb', 'ETank', 'Reserve'] + + def __init__(self, player=0, maxDiff=sys.maxsize): + self._items = { } + self._counts = { } + + self.player = player + self.maxDiff = maxDiff + + # cache related + self.cacheKey = 0 + self.computeItemsPositions() + Cache.reset() + Logic.factory('vanilla') + self.helpers = Logic.HelpersGraph(self) + self.doorsManager = DoorsManager() + self.createFacadeFunctions() + self.createKnowsFunctions(player) + self.resetItems() + + def computeItemsPositions(self): + # compute index in cache key for each items + self.itemsPositions = {} + maxBitsForCountItem = 7 # 128 values with 7 bits + for (i, item) in enumerate(self.countItems): + pos = i*maxBitsForCountItem + bitMask = (2<<(maxBitsForCountItem-1))-1 + bitMask = bitMask << pos + self.itemsPositions[item] = (pos, bitMask) + for (i, item) in enumerate(self.items, (i+1)*maxBitsForCountItem+1): + if item in self.countItems: + continue + self.itemsPositions[item] = (i, 1<> pos + if value != 0: + msg += " {}: {}".format(item, value) + print("items:{}".format(msg)) + + def isEmpty(self): + for item in self.items: + if self.haveItem(item): + return False + for item in self.countItems: + if self.itemCount(item) > 0: + return False + return True + + def getItems(self): + # get a dict of collected items and how many (to be displayed on the solver spoiler) + itemsDict = {} + for item in self.items: + itemsDict[item] = 1 if self._items[item] == True else 0 + for item in self.countItems: + itemsDict[item] = self._counts[item] + return itemsDict + + def withItem(self, item, func): + self.addItem(item) + ret = func(self) + self.removeItem(item) + return ret + + def resetItems(self): + self._items = { item : smboolFalse for item in self.items } + self._counts = { item : 0 for item in self.countItems } + + self.cacheKey = 0 + Cache.update(self.cacheKey) + + def addItem(self, item): + # a new item is available + self._items[item] = SMBool(True, items=[item]) + if self.isCountItem(item): + count = self._counts[item] + 1 + self._counts[item] = count + self.computeNewCacheKey(item, count) + else: + self.computeNewCacheKey(item, 1) + + Cache.update(self.cacheKey) + + def addItems(self, items): + if len(items) == 0: + return + for item in items: + self._items[item] = SMBool(True, items=[item]) + if self.isCountItem(item): + count = self._counts[item] + 1 + self._counts[item] = count + self.computeNewCacheKey(item, count) + else: + self.computeNewCacheKey(item, 1) + + Cache.update(self.cacheKey) + + def removeItem(self, item): + # randomizer removed an item (or the item was added to test a post available) + if self.isCountItem(item): + count = self._counts[item] - 1 + self._counts[item] = count + if count == 0: + self._items[item] = smboolFalse + self.computeNewCacheKey(item, count) + else: + self._items[item] = smboolFalse + self.computeNewCacheKey(item, 0) + + Cache.update(self.cacheKey) + + def createFacadeFunctions(self): + for fun in dir(self.helpers): + if fun != 'smbm' and fun[0:2] != '__': + setattr(self, fun, getattr(self.helpers, fun)) + + def traverse(self, doorName): + return self.doorsManager.traverse(self, doorName) + + def createKnowsFunctions(self, player): + # for each knows we have a function knowsKnows (ex: knowsAlcatrazEscape()) which + # take no parameter + for knows in Knows.__dict__: + if isKnows(knows): + if knows in Knows.knowsDict[player].__dict__: + setattr(self, 'knows'+knows, lambda knows=knows: SMBool(Knows.knowsDict[player].__dict__[knows].bool, + Knows.knowsDict[player].__dict__[knows].difficulty, + knows=[knows])) + else: + # if knows not in preset, use default values + setattr(self, 'knows'+knows, lambda knows=knows: SMBool(Knows.__dict__[knows].bool, + Knows.__dict__[knows].difficulty, + knows=[knows])) + + def isCountItem(self, item): + return item in self.countItems + + def itemCount(self, item): + # return integer + #self.state.item_count(item, self.player) + return self._counts[item] + + def haveItem(self, item): + #return self.state.has(item, self.player) + return self._items[item] + + wand = staticmethod(SMBool.wand) + wandmax = staticmethod(SMBool.wandmax) + wor = staticmethod(SMBool.wor) + wnot = staticmethod(SMBool.wnot) + + def itemCountOk(self, item, count, difficulty=0): + if self.itemCount(item) >= count: + if item in ['ETank', 'Reserve']: + item = str(count)+'-'+item + return SMBool(True, difficulty, items = [item]) + else: + return smboolFalse + + def energyReserveCountOk(self, count, difficulty=0): + if self.energyReserveCount() >= count: + nEtank = self.itemCount('ETank') + if nEtank > count: + nEtank = int(count) + items = str(nEtank)+'-ETank' + nReserve = self.itemCount('Reserve') + if nEtank < count: + nReserve = int(count) - nEtank + items += ' - '+str(nReserve)+'-Reserve' + return SMBool(True, difficulty, items = [items]) + else: + return smboolFalse + +class SMBoolManagerPlando(SMBoolManager): + def __init__(self): + super(SMBoolManagerPlando, self).__init__() + + def addItem(self, item): + # a new item is available + already = self.haveItem(item) + isCount = self.isCountItem(item) + if isCount or not already: + self._items[item] = SMBool(True, items=[item]) + else: + # handle duplicate major items (plandos) + self._items['dup_'+item] = True + if isCount: + count = self._counts[item] + 1 + self._counts[item] = count + self.computeNewCacheKey(item, count) + else: + self.computeNewCacheKey(item, 1) + + Cache.update(self.cacheKey) + + def removeItem(self, item): + # randomizer removed an item (or the item was added to test a post available) + if self.isCountItem(item): + count = self._counts[item] - 1 + self._counts[item] = count + if count == 0: + self._items[item] = smboolFalse + self.computeNewCacheKey(item, count) + else: + dup = 'dup_'+item + if self._items.get(dup, None) is None: + self._items[item] = smboolFalse + self.computeNewCacheKey(item, 0) + else: + del self._items[dup] + self.computeNewCacheKey(item, 1) + + Cache.update(self.cacheKey) diff --git a/worlds/sm/variaRandomizer/patches/__init__.py b/worlds/sm/variaRandomizer/patches/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/worlds/sm/variaRandomizer/patches/common/__init__.py b/worlds/sm/variaRandomizer/patches/common/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/worlds/sm/variaRandomizer/patches/common/ips/RakkiDoorSpeedups.IPS b/worlds/sm/variaRandomizer/patches/common/ips/RakkiDoorSpeedups.IPS new file mode 100644 index 00000000..ecb02d04 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/RakkiDoorSpeedups.IPS differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/animal_enemies.ips b/worlds/sm/variaRandomizer/patches/common/ips/animal_enemies.ips new file mode 100644 index 00000000..328975aa Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/animal_enemies.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/animals.ips b/worlds/sm/variaRandomizer/patches/common/ips/animals.ips new file mode 100644 index 00000000..b14705a3 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/animals.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/area_ids_alt.ips b/worlds/sm/variaRandomizer/patches/common/ips/area_ids_alt.ips new file mode 100644 index 00000000..99906925 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/area_ids_alt.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/area_rando_doors.ips b/worlds/sm/variaRandomizer/patches/common/ips/area_rando_doors.ips new file mode 100644 index 00000000..05491ebf Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/area_rando_doors.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/area_rando_layout.ips b/worlds/sm/variaRandomizer/patches/common/ips/area_rando_layout.ips new file mode 100644 index 00000000..caf0d5c7 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/area_rando_layout.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/area_rando_layout_base.ips b/worlds/sm/variaRandomizer/patches/common/ips/area_rando_layout_base.ips new file mode 100644 index 00000000..2de377f1 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/area_rando_layout_base.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/area_rando_warp_door.ips b/worlds/sm/variaRandomizer/patches/common/ips/area_rando_warp_door.ips new file mode 100644 index 00000000..748a82e5 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/area_rando_warp_door.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/basepatch.ips b/worlds/sm/variaRandomizer/patches/common/ips/basepatch.ips new file mode 100644 index 00000000..3877dbaa Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/basepatch.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/beam_doors_gfx.ips b/worlds/sm/variaRandomizer/patches/common/ips/beam_doors_gfx.ips new file mode 100644 index 00000000..00239095 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/beam_doors_gfx.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/beam_doors_plms.ips b/worlds/sm/variaRandomizer/patches/common/ips/beam_doors_plms.ips new file mode 100644 index 00000000..34f7bda6 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/beam_doors_plms.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/bomb_torizo.ips b/worlds/sm/variaRandomizer/patches/common/ips/bomb_torizo.ips new file mode 100644 index 00000000..3f7eda23 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/bomb_torizo.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/crab_shaft.ips b/worlds/sm/variaRandomizer/patches/common/ips/crab_shaft.ips new file mode 100644 index 00000000..94d682c9 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/crab_shaft.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/door_transition.ips b/worlds/sm/variaRandomizer/patches/common/ips/door_transition.ips new file mode 100644 index 00000000..5d9d85ef Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/door_transition.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/draygonimals.ips b/worlds/sm/variaRandomizer/patches/common/ips/draygonimals.ips new file mode 100644 index 00000000..aefedb69 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/draygonimals.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/east_ocean.ips b/worlds/sm/variaRandomizer/patches/common/ips/east_ocean.ips new file mode 100644 index 00000000..24c5e8ca Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/east_ocean.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/elevators_doors_speed.ips b/worlds/sm/variaRandomizer/patches/common/ips/elevators_doors_speed.ips new file mode 100644 index 00000000..3e08065b Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/elevators_doors_speed.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/escapimals.ips b/worlds/sm/variaRandomizer/patches/common/ips/escapimals.ips new file mode 100644 index 00000000..649bff33 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/escapimals.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/firefleas_shot_block.ips b/worlds/sm/variaRandomizer/patches/common/ips/firefleas_shot_block.ips new file mode 100644 index 00000000..956b29ff Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/firefleas_shot_block.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/g4_skip.ips b/worlds/sm/variaRandomizer/patches/common/ips/g4_skip.ips new file mode 100644 index 00000000..f75fbc5b Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/g4_skip.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/gameend.ips b/worlds/sm/variaRandomizer/patches/common/ips/gameend.ips new file mode 100644 index 00000000..25c2bbac Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/gameend.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/grey_door_animals.ips b/worlds/sm/variaRandomizer/patches/common/ips/grey_door_animals.ips new file mode 100644 index 00000000..c32e3aa5 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/grey_door_animals.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/itemsounds.ips b/worlds/sm/variaRandomizer/patches/common/ips/itemsounds.ips new file mode 100644 index 00000000..0ce63215 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/itemsounds.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/ln_chozo_platform.ips b/worlds/sm/variaRandomizer/patches/common/ips/ln_chozo_platform.ips new file mode 100644 index 00000000..66e48567 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/ln_chozo_platform.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/low_timer.ips b/worlds/sm/variaRandomizer/patches/common/ips/low_timer.ips new file mode 100644 index 00000000..18642014 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/low_timer.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/mama_save.ips b/worlds/sm/variaRandomizer/patches/common/ips/mama_save.ips new file mode 100644 index 00000000..87378d4e Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/mama_save.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/metalimals.ips b/worlds/sm/variaRandomizer/patches/common/ips/metalimals.ips new file mode 100644 index 00000000..6e2a181e Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/metalimals.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/minimizer_bosses.ips b/worlds/sm/variaRandomizer/patches/common/ips/minimizer_bosses.ips new file mode 100644 index 00000000..03b9b32a Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/minimizer_bosses.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/minimizer_tourian.ips b/worlds/sm/variaRandomizer/patches/common/ips/minimizer_tourian.ips new file mode 100644 index 00000000..bd40ab11 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/minimizer_tourian.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/nerfed_charge.ips b/worlds/sm/variaRandomizer/patches/common/ips/nerfed_charge.ips new file mode 100644 index 00000000..2e8ea17d Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/nerfed_charge.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/nerfed_rainbow_beam.ips b/worlds/sm/variaRandomizer/patches/common/ips/nerfed_rainbow_beam.ips new file mode 100644 index 00000000..bc614862 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/nerfed_rainbow_beam.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/no_demo.ips b/worlds/sm/variaRandomizer/patches/common/ips/no_demo.ips new file mode 100644 index 00000000..9ad09f45 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/no_demo.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/open_zebetites.ips b/worlds/sm/variaRandomizer/patches/common/ips/open_zebetites.ips new file mode 100644 index 00000000..afe339e8 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/open_zebetites.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/phantoonimals.ips b/worlds/sm/variaRandomizer/patches/common/ips/phantoonimals.ips new file mode 100644 index 00000000..b20bdf0f Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/phantoonimals.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/progressive_suits.ips b/worlds/sm/variaRandomizer/patches/common/ips/progressive_suits.ips new file mode 100644 index 00000000..42a46f0e Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/progressive_suits.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/rando_escape.ips b/worlds/sm/variaRandomizer/patches/common/ips/rando_escape.ips new file mode 100644 index 00000000..74dac2e6 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/rando_escape.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/rando_escape_ws_fix.ips b/worlds/sm/variaRandomizer/patches/common/ips/rando_escape_ws_fix.ips new file mode 100644 index 00000000..5c601422 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/rando_escape_ws_fix.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/rando_speed.ips b/worlds/sm/variaRandomizer/patches/common/ips/rando_speed.ips new file mode 100644 index 00000000..6dd0d467 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/rando_speed.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/random_music.ips b/worlds/sm/variaRandomizer/patches/common/ips/random_music.ips new file mode 100644 index 00000000..3668a27d Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/random_music.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/red_doors.ips b/worlds/sm/variaRandomizer/patches/common/ips/red_doors.ips new file mode 100644 index 00000000..9aa46440 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/red_doors.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/refill_before_save.ips b/worlds/sm/variaRandomizer/patches/common/ips/refill_before_save.ips new file mode 100644 index 00000000..47993327 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/refill_before_save.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/remove_elevators_doors_speed.ips b/worlds/sm/variaRandomizer/patches/common/ips/remove_elevators_doors_speed.ips new file mode 100644 index 00000000..1c60d267 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/remove_elevators_doors_speed.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/remove_itemsounds.ips b/worlds/sm/variaRandomizer/patches/common/ips/remove_itemsounds.ips new file mode 100644 index 00000000..778f87c4 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/remove_itemsounds.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/ridley_platform.ips b/worlds/sm/variaRandomizer/patches/common/ips/ridley_platform.ips new file mode 100644 index 00000000..b7686b03 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/ridley_platform.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/ridleyimals.ips b/worlds/sm/variaRandomizer/patches/common/ips/ridleyimals.ips new file mode 100644 index 00000000..db2cfe6e Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/ridleyimals.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/spinjumprestart.ips b/worlds/sm/variaRandomizer/patches/common/ips/spinjumprestart.ips new file mode 100644 index 00000000..e4b7c9e3 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/spinjumprestart.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/vanilla_music.ips b/worlds/sm/variaRandomizer/patches/common/ips/vanilla_music.ips new file mode 100644 index 00000000..d09bae09 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/vanilla_music.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/varia_hud.ips b/worlds/sm/variaRandomizer/patches/common/ips/varia_hud.ips new file mode 100644 index 00000000..5002a67c Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/varia_hud.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/ips/wh_open_tube.ips b/worlds/sm/variaRandomizer/patches/common/ips/wh_open_tube.ips new file mode 100644 index 00000000..b7ac09e5 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/common/ips/wh_open_tube.ips differ diff --git a/worlds/sm/variaRandomizer/patches/common/patches.py b/worlds/sm/variaRandomizer/patches/common/patches.py new file mode 100644 index 00000000..c61d53f4 --- /dev/null +++ b/worlds/sm/variaRandomizer/patches/common/patches.py @@ -0,0 +1,553 @@ +patches = { + "Removes_Gravity_Suit_heat_protection": { + 0x06e37d: [0x01], + 0x0869dd: [0x01]}, + "Mother_Brain_Cutscene_Edits": { + 0x148824: [0x01,0x00], + 0x148848: [0x01,0x00], + 0x148867: [0x01,0x00], + 0x14887f: [0x01,0x00], + 0x148bdb: [0x04,0x00], + 0x14897d: [0x10,0x00], + 0x1489af: [0x10,0x00], + 0x1489e1: [0x10,0x00], + 0x148a09: [0x10,0x00], + 0x148a31: [0x10,0x00], + 0x148a63: [0x10,0x00], + 0x148a95: [0x10,0x00], + 0x148b33: [0x10,0x00], + 0x148dc6: [0xb0], + 0x148b8d: [0x12,0x00], + 0x148d74: [0x00,0x00], + 0x148d86: [0x00,0x00], + 0x148daf: [0x00,0x01], + 0x148e51: [0x01,0x00], + 0x14b93a: [0x00,0x01], + 0x148eef: [0x0a,0x00], + 0x148f0f: [0x60,0x00], + 0x14af4e: [0x0a,0x00], + 0x14af0d: [0x0a,0x00], + 0x14b00d: [0x00,0x00], + 0x14b132: [0x40,0x00], + 0x14b16d: [0x00,0x00], + 0x14b19f: [0x20,0x00], + 0x14b1b2: [0x30,0x00], + 0x14b20c: [0x03,0x00]}, + "No_Music":{ + 0x278413: [0x6f]}, + "Escape_Rando_Enable_Enemies":{ + 0x10F000: [0x0, 0x0]}, + "Escape_Rando_Disable_Enemies":{ + 0x10F000: [0x1]}, + "Escape_Animals_Open_Brinstar": { + 0x784BD: [0x10] + }, + "Escape_Animals_Open_Norfair": { + 0x78B0B: [0x10] + }, + "Escape_Animals_Open_Maridia": { + 0x7C54C: [0x10] + }, + "Enable_Backup_Saves": { + 0xef20: [0x1] + }, + 'Escape_Scavenger' : { + 0x10F5FC: [0x1] + }, + # vanilla data to restore setup asm for plandos + "Escape_Animals_Disable": { + 0x79867: [0xb2, 0x91], + 0x798dc: [0xbb, 0x91] + }, + # with animals suprise make the bomb blocks at alcatraz disapear with event "Zebes timebomb set" instead of "critters escaped" + "Escape_Animals_Change_Event": { + 0x023B0A: [0x0E] + }, + "LN_Chozo_SpaceJump_Check_Disable": { + 0x2518f: [0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea] + }, + "LN_PB_Heat_Disable": { + 0x18878: [0x80, 0x00] + }, + "LN_Firefleas_Remove_Fune": { + 0x10ABC2: [0xff, 0x7f, 0xff, 0x7f], + }, + "WS_Main_Open_Grey": { + 0x10BE92: [0x0] + }, + "WS_Save_Active": { + 0x7ceb0: [0xC9] + }, + "WS_Etank": { + 0x7cc4d: [0x37, 0xc3], + 0x7cbfb: [0x23, 0xc3] + }, + "Phantoon_Eye_Door":{ + 0x7CCAF: [0x91, 0xC2] + }, + # has to be applied along with WS_Main_Open_Grey + "Sponge_Bath_Blinking_Door": { + 0x7C276: [0x0C], + 0x10CE69: [0x00] + }, + "Infinite_Space_Jump": { + 0x82493: [0x80, 0x0D] + }, + "SpriteSomething_Disable_Spin_Attack": { + 0xD93FE: [0x0, 0x0] + }, + # custom load points for non standard start APs + "Save_G4": { + # load point entry + 0x4527: [0xED, 0xA5, 0x16, 0x92, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xA8, 0x00, 0x60, 0x00], + # map icon X/Y + 0x1486f: [0x78, 0x00, 0x48, 0x00] + }, + "Save_Gauntlet": { + # load point entry + 0x4519: [0xBD, 0x99, 0x1A, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x50, 0x00], + # music in room state header + 0x799ce: [0x09], + # map icon X/Y + 0x1486b: [0x58, 0x00, 0x18, 0x00] + }, + "Save_Watering_Hole": { + # load point entry + 0x4979: [0x3B, 0xD1, 0x98, 0xA4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x88, 0x00, 0xD0, 0xFF], + # music in room state header + 0x7d14c: [0x1b, 0x06], + # map icon X/Y + 0x14a0f: [0x68, 0x00, 0x28, 0x00] + }, + "Save_Mama": { + # load point entry + 0x496B: [0x55, 0xD0, 0xE4, 0xA3, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x98, 0x00, 0xF0, 0xFF], + # music in room state header + 0x7d066: [0x1b, 0x06], + # map icon X/Y + 0x14a0b: [0x97, 0x00, 0x67, 0x00] + }, + "Save_Aqueduct": { + # load point entry + 0x495D: [0xA7, 0xD5, 0xD4, 0xA7, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x78, 0x00, 0x20, 0x00], + # map icon X/Y + 0x14a07: [0xc4, 0x00, 0x50, 0x00] + }, + "Save_Etecoons": { + # load point entry + 0x4631: [0x51, 0xA0, 0x3A, 0x8F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0xD0, 0xFF], + # music in room state header + 0x7a062: [0x0f, 0x05], + # map icon X/Y + 0x148d9: [0x28, 0x00, 0x58, 0x00] + }, + "Save_Firefleas": { + # load point entry + 0x473b: [0x5A, 0xB5, 0x9E, 0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00], + # music in room state header + 0x7b56b: [0x18, 0x05], + # map icon X/Y + 0x1493f: [0x28, 0x01, 0x38, 0x00] + }, + # custom load points for west maridia additional saves in area rando + "Save_Crab_Shaft": { + # load point entry + 0x4995: [0xa3, 0xd1, 0x68, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x78, 0x00, 0x60, 0x00], + # map icon X/Y + 0x14a17: [0x90, 0x00, 0x50, 0x00] + }, + "Save_Main_Street": { + 0x49a3: [0xC9, 0xCF, 0xD8, 0xA3, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x78, 0x00, 0x10, 0x00], + # map icon X/Y + 0x14a1b: [0x58, 0x00, 0x78, 0x00] + }, + # blinking doors for area APs + 'Blinking[Keyhunter Room Bottom]': { + 0x78228: [0x4e, 0xc8, 0x16, 0x2d, 0x0e, 0x8c], + 0x108F7B: [0x0] + }, + 'Blinking[Moat Right]': { + 0x1085E0: [0x0] + }, + 'Blinking[Morph Ball Room Left]': { + 0x78746: [0x48, 0xc8, 0x01, 0x26, 0x31, 0x8c], + 0x1093A8: [0x0] + }, + 'Blinking[Green Pirates Shaft Bottom Right]': { + 0x78470: [0x42, 0xc8, 0x0e, 0x66, 0x63, 0x8c], + 0x108572: [0x0] + }, + 'Blinking[Lower Mushrooms Left]': { + 0x108C0C: [0x0] + }, + 'Blinking[Golden Four]': { + 0x109F60: [0x0] + }, + 'Blinking[Green Brinstar Elevator]': { + 0x108585: [0x0] + }, + 'Blinking[Green Hill Zone Top Right]': { + 0x78670: [0x42, 0xc8, 0x1e, 0x06, 0x63, 0x8c], + 0x109D5B: [0x0] + }, + 'Blinking[Noob Bridge Right]': { + 0x787A6: [0x42, 0xc8, 0x5e, 0x06, 0x63, 0x8c], + 0x109325: [0x0] + }, + 'Blinking[Warehouse Zeela Room Left]': { + 0x109451: [0x0] + }, + 'Blinking[KraidRoomOut]': { + # removes gadora by ending PLM list + 0x78A1A: [0x42, 0xc8, 0x1e, 0x16, 0x63, 0x8c, 0x00, 0x00], + 0x10A056: [0x0] + }, + 'Blinking[Warehouse Entrance Right]': { + 0x1098F6: [0x0] + }, + 'Blinking[Warehouse Entrance Left]': { + 0x1098F6: [0x0] + }, + 'Blinking[Single Chamber Top Right]': { + 0x10B88E: [0x0] + }, + 'Blinking[Kronic Boost Room Bottom Left]': { + 0x78D4E: [0x48, 0xc8, 0x11, 0x26, 0x58, 0x8c], + 0x10B9D7: [0x0] + }, + 'Blinking[Three Muskateers Room Left]': { + 0x10BB0D: [0x0] + }, + 'Blinking[Lava Dive Right]': { + 0x10AD6B: [0x0] + }, + 'Blinking[RidleyRoomOut]': { + # removes gadora by ending PLM list + 0x78EA6: [0x48, 0xc8, 0x01, 0x06, 0x63, 0x8c, 0x00, 0x00], + 0x10B81B: [0x0] + }, + 'Blinking[West Ocean Left]': { + 0x1086F6: [0x0] + }, + 'Blinking[PhantoonRoomOut]': { + # removes gadora by ending PLM list + 0x7C29D: [0x42, 0xc8, 0x4e, 0x06, 0x63, 0x8c, 0x00, 0x00], + # zero needed enemy count for both room states + 0x10C3E5: [0x0], + 0x10C19B: [0x0] + }, + 'Blinking[Crab Maze Left]': { + 0x108B3A: [0x0] + }, + 'Blinking[Crab Hole Bottom Left]': { + 0x10DE59: [0x0] + }, + 'Blinking[Main Street Bottom]': { + 0x10DF2F: [0x0] + }, + 'Blinking[Red Fish Room Left]': { + 0x10D3EC: [0x0] + }, + 'Blinking[Le Coude Right]': { + 0x7823E: [0x42, 0xc8, 0x0e, 0x06, 0x63, 0x8c], + 0x1085DD: [0x0] + }, + 'Blinking[DraygonRoomOut]': { + # overwrites a gadoras PLMs and replace the rest with useless arrow PLMs + # (we cannot end the list because the item is after in the list) + 0x7C73B: [0x48, 0xc8, 0x01, 0x26, 0x63, 0x8c] + [0x3b, 0xb6, 0x31, 0x26, 0x00, 0x00]*2, + 0x10D111: [0x0] + }, + 'Blinking[East Tunnel Top Right]': { + 0x10D5E1: [0x0] + }, + 'Blinking[East Tunnel Right]': { + 0x10D5E1: [0x0] + }, + 'Blinking[Glass Tunnel Top]': { + 0x10D53B: [0x0] + }, + 'Blinking[Red Tower Top Left]': { + 0x109504: [0x0] + }, + 'Blinking[Caterpillar Room Top Right]': { + 0x10A0B9: [0x0] + }, + 'Blinking[Red Brinstar Elevator]': { + 0x78256: [0x54, 0xc8, 0x06, 0x02, 0x10, 0x8c], + 0x1089F1: [0x0] + }, + 'Blinking[Crocomire Speedway Bottom]': { + 0x78B96: [0x4e, 0xc8, 0xc6, 0x2d, 0x4e, 0x8c], + 0x10AA8C: [0x0] + }, + 'Blinking[Crocomire Room Top]': { + 0x78B9E: [0x54, 0xc8, 0x36, 0x02, 0x4f, 0x8c], + 0x10BB30: [0x0] + }, + 'Blinking[Below Botwoon Energy Tank Right]': { + 0x10DD9A: [0x0] + }, + 'Blinking[West Sand Hall Left]': { + 0x10DACF: [0x0] + }, + 'Blinking[Aqueduct Top Left]': { + 0x10D3A9: [0x0] + }, + 'Blinking[Crab Shaft Right]': { + 0x7C4FB: [0x42, 0xc8, 0x1e, 0x36, 0x8f, 0x8c], + 0x10D005: [0x0] + }, + 'Blinking[RidleyRoomIn]': { + 0x78E98: [0x42, 0xc8, 0x0e, 0x06, 0x5a, 0x8c], + 0x10A638: [0x0] + }, + 'Blinking[DraygonRoomIn]': { + 0x7C7BB: [0x42, 0xc8, 0x1e, 0x06, 0x9e, 0x8c], + 0x10D356: [0x0] + }, + 'Blinking[PhantoonRoomIn]': { + 0x7C2B3: [0x48, 0xc8, 0x01, 0x06, 0x86, 0x8c], + 0x10CD16: [0x0] + }, + 'Blinking[KraidRoomIn]': { + 0x78A34: [0x48, 0xc8, 0x01, 0x16, 0x47, 0x8c], + 0x109F37: [0x0] + }, +} + +additional_PLMs = { + # for escape rando seeds + "WS_Map_Grey_Door": { + 'room': 0Xcc6f, + 'plm_bytes_list': [ + [0x48, 0xc8, 0x1, 0x6, 0x61, 0x90] + ] + }, + "WS_Map_Grey_Door_Openable": { + 'room': 0Xcc6f, + 'plm_bytes_list': [ + [0x48, 0xc8, 0x1, 0x6, 0x61, 0x10] + ] + }, + # area/boss seeds + # has to be applied along with WS_Main_Open_Grey + "WS_Save_Blinking_Door": { + 'room': 0xcaf6, + 'state': 0xcb08, + 'plm_bytes_list': [ + [0x42, 0xC8, 0x4E, 0x36, 0x62, 0x0C] + ] + }, + # non standard start AP seeds (morph item is not in vanilla PLM list w/ zebes awake) + "Morph_Zebes_Awake": { + 'room': 0x9e9f, + 'state': 0x9ecb, + 'plm_bytes_list': [ + [0xff, 0xff, 0x45, 0x29, 0x1A, 0x00] + ], + 'locations': [("Morphing Ball", 0)] + }, + # seal west/east maridia connection in area rando + 'Maridia Sand Hall Seal': { + 'room': 0xd252, + 'plm_bytes_list': [ + [0x42, 0xc8, 0x0e, 0x06, 0x63, 0x90] + ] + }, + # custom save points for non standard start APs + "Save_G4": { + "room": 0xa5ed, + 'plm_bytes_list': [ + [0x6F, 0xB7, 0x3D, 0x0C, 0x07, 0x00] + ] + }, + "Save_Gauntlet": { + "room": 0x99bd, + 'plm_bytes_list': [ + [0x6F, 0xB7, 0x0C, 0x0A, 0x06, 0x00] + ] + }, + "Save_Watering_Hole": { + "room": 0xd13b, + 'plm_bytes_list': [ + [0x6F, 0xB7, 0x14, 0x0A, 0x07, 0x00] + ] + }, + "Save_Mama": { + "room": 0xd055, + 'plm_bytes_list': [ + [0x6F, 0xB7, 0x26, 0x0B, 0x06, 0x00] + ] + }, + "Save_Aqueduct": { + "room": 0xd5a7, + 'plm_bytes_list': [ + [0x6F, 0xB7, 0x59, 0x09, 0x05, 0x00] + ] + }, + "Save_Etecoons": { + "room": 0xa051, + 'plm_bytes_list': [ + [0x6F, 0xB7, 0x04, 0x0B, 0x07, 0x00] + ] + }, + "Save_Firefleas": { + "room": 0xb55a, + 'plm_bytes_list': [ + [0x6F, 0xB7, 0x07, 0x09, 0x07, 0x00] + ] + }, + # additional saves in west maridia for area rando + "Save_Crab_Shaft": { + "room": 0xd1a3, + 'plm_bytes_list': [ + [0x6F, 0xB7, 0x0D, 0x29, 0x09, 0x00] + ] + }, + "Save_Main_Street": { + "room": 0xcfc9, + 'plm_bytes_list': [ + [0x6F, 0xB7, 0x18, 0x59, 0x0A, 0x00] + ] + }, + # blinking doors for area APs + 'Blinking[Moat Right]': { + 'room': 0x95ff, + 'plm_bytes_list': [ + [0x42, 0xc8, 0x1e, 0x06, 0x63, 0x8c] + ] + }, + 'Blinking[Lower Mushrooms Left]': { + 'room': 0x9969, + 'plm_bytes_list': [ + [0x48, 0xc8, 0x01, 0x06, 0x63, 0x8c] + ] + }, + 'Blinking[Golden Four]': { + 'room': 0xa5ed, + 'plm_bytes_list': [ + [0x48, 0xc8, 0x01, 0x06, 0x63, 0x8c] + ] + }, + 'Blinking[Green Brinstar Elevator]': { + 'room': 0x9938, + 'plm_bytes_list': [ + [0x42, 0xc8, 0x0e, 0x06, 0x63, 0x8c] + ] + }, + 'Blinking[Warehouse Zeela Room Left]': { + 'room': 0xa471, + 'plm_bytes_list': [ + [0x48, 0xc8, 0x01, 0x06, 0x63, 0x8c] + ] + }, + 'Blinking[Warehouse Entrance Right]': { + 'room': 0xa6a1, + 'plm_bytes_list': [ + [0x48, 0xc8, 0x01, 0x06, 0x63, 0x8c] + ] + }, + 'Blinking[Warehouse Entrance Left]': { + 'room': 0xa6a1, + 'plm_bytes_list': [ + [0x42, 0xc8, 0x2e, 0x06, 0x63, 0x8c] + ] + }, + 'Blinking[Single Chamber Top Right]': { + 'room': 0xad5e, + 'plm_bytes_list': [ + [0x42, 0xc8, 0x5e, 0x06, 0x63, 0x8c] + ] + }, + 'Blinking[Three Muskateers Room Left]': { + 'room': 0xb656, + 'plm_bytes_list': [ + [0x48, 0xc8, 0x11, 0x06, 0x63, 0x8c] + ] + }, + 'Blinking[Lava Dive Right]': { + 'room': 0xaf14, + 'plm_bytes_list': [ + [0x42, 0xc8, 0x3e, 0x06, 0x63, 0x8c] + ] + }, + 'Blinking[West Ocean Left]': { + 'room': 0x93fe, + 'plm_bytes_list': [ + [0x48, 0xc8, 0x01, 0x46, 0x63, 0x8c] + ] + }, + 'Blinking[Crab Maze Left]': { + 'room': 0x957d, + 'plm_bytes_list': [ + [0x48, 0xc8, 0x01, 0x16, 0x63, 0x8c] + ] + }, + 'Blinking[Crab Hole Bottom Left]': { + 'room': 0xd21c, + 'plm_bytes_list': [ + [0x48, 0xc8, 0x01, 0x16, 0x63, 0x8c] + ] + }, + 'Blinking[Main Street Bottom]': { + 'room': 0xcfc9, + 'plm_bytes_list': [ + [0x4e, 0xc8, 0x16, 0x7d, 0x63, 0x8c] + ] + }, + 'Blinking[Red Fish Room Left]': { + 'room': 0xd104, + 'plm_bytes_list': [ + [0x48, 0xc8, 0x01, 0x06, 0x63, 0x8c] + ] + }, + 'Blinking[East Tunnel Top Right]': { + 'room': 0xcf80, + 'plm_bytes_list': [ + [0x42, 0xc8, 0x3e, 0x06, 0x63, 0x8c] + ] + }, + 'Blinking[East Tunnel Right]': { + 'room': 0xcf80, + 'plm_bytes_list': [ + [0x42, 0xc8, 0x0e, 0x16, 0x63, 0x8c] + ] + }, + 'Blinking[Glass Tunnel Top]': { + 'room': 0xcefb, + 'plm_bytes_list': [ + [0x54, 0xc8, 0x06, 0x02, 0x63, 0x8c] + ] + }, + 'Blinking[Red Tower Top Left]': { + 'room': 0xa253, + 'plm_bytes_list': [ + [0x48, 0xc8, 0x01, 0x46, 0x63, 0x8c] + ] + }, + 'Blinking[Caterpillar Room Top Right]': { + 'room': 0xa322, + 'plm_bytes_list': [ + [0x42, 0xc8, 0x2e, 0x36, 0x63, 0x8c] + ] + }, + 'Blinking[Below Botwoon Energy Tank Right]': { + 'room': 0xd6fd, + 'plm_bytes_list': [ + [0x42, 0xc8, 0x3e, 0x06, 0x63, 0x8c] + ] + }, + 'Blinking[West Sand Hall Left]': { + 'room': 0xd461, + 'plm_bytes_list': [ + [0x48, 0xc8, 0x01, 0x06, 0x63, 0x8c] + ] + }, + 'Blinking[Aqueduct Top Left]': { + 'room': 0xd5a7, + 'plm_bytes_list': [ + [0x48, 0xc8, 0x01, 0x16, 0x63, 0x8c] + ] + }, +} diff --git a/worlds/sm/variaRandomizer/patches/ips.pl b/worlds/sm/variaRandomizer/patches/ips.pl new file mode 100644 index 00000000..64c0185c --- /dev/null +++ b/worlds/sm/variaRandomizer/patches/ips.pl @@ -0,0 +1,77 @@ +#!/usr/bin/perl + +# ips.pl +# version 0.01 +# +# This is a quick hack to apply IPS patches. It is distributed under +# the terms of the GNU General Public License. + +if (@ARGV != 1) +{ + print "manger" +} + +open PAT, "$ARGV[0]" or die "Can't open $ARGV[1]"; + +read PAT, $data, 5; +die "Bad magic bytes in $ARGV[1]" if $data ne "PATCH"; +printf("'%s': {", $ARGV[0]); +my $first = 1; +while(1) +{ + read PAT, $data, 3 or die "Read error"; + if ($data eq "EOF") + { + printf("},\n"); + exit; + } + if($first == 1) { + $first = 2; + } else { + printf(",\n"); + } + # This is ugly, but unpack doesn't have anything that's + # very helpful for THREE-byte numbers. + $address = ord(substr($data,0,1))*256*256 + + ord(substr($data,1,1))*256 + + ord(substr($data,2,1)); + + read PAT, $data, 2 or die "Read error"; + $length = ord(substr($data,0,1))*256 + ord(substr($data,1,1)); + if ($length) + { + read(PAT, $data, $length) == $length or die "Read error"; + + my @chars = split("", $data); + + printf ("0x%lX: [", $address); + for(my $i=0; $i < $length; $i++) { + printf ("0x%lX", ord($chars[$i])); + if($i != $length - 1) { + printf(","); + } + if(($i % 38) == 0 && $i != 0) { + printf("\n"); + } + } + printf ("]"); + } + else # RLE mode + { + read PAT, $data, 2 or die "Read error"; + $length = ord(substr($data,0,1))*256 + ord(substr($data,1,1)); + read PAT, $byte, 1 or die "Read error"; + + printf ("0x%lX: [", $address); + for(my $i=0; $i < $length; $i++) { + printf ("0x%lX", ord($byte)); + if($i != $length - 1) { + printf(","); + } + if(($i % 38) == 0 && $i != 0) { + printf("\n"); + } + } + printf ("]"); + } +} diff --git a/worlds/sm/variaRandomizer/patches/patchaccess.py b/worlds/sm/variaRandomizer/patches/patchaccess.py new file mode 100644 index 00000000..857b99e8 --- /dev/null +++ b/worlds/sm/variaRandomizer/patches/patchaccess.py @@ -0,0 +1,43 @@ +import os, importlib +from logic.logic import Logic +from patches.common.patches import patches, additional_PLMs +from utils.parameters import appDir +from Utils import is_frozen + +class PatchAccess(object): + def __init__(self): + # load all ips patches + self.patchesPath = {} + commonDir = os.path.join(appDir, 'lib' if is_frozen() else '', 'worlds/sm/variaRandomizer/patches/common/ips/') + for patch in os.listdir(commonDir): + self.patchesPath[patch] = commonDir + logicDir = os.path.join(appDir, 'lib' if is_frozen() else '', 'worlds/sm/variaRandomizer/patches/{}/ips/'.format(Logic.patches)) + for patch in os.listdir(logicDir): + self.patchesPath[patch] = logicDir + + # load dict patches + self.dictPatches = patches + logicPatches = importlib.import_module("patches.{}.patches".format(Logic.patches)).patches + self.dictPatches.update(logicPatches) + + # load additional PLMs + self.additionalPLMs = additional_PLMs + logicPLMs = importlib.import_module("patches.{}.patches".format(Logic.patches)).additional_PLMs + self.additionalPLMs.update(logicPLMs) + + def getPatchPath(self, patch): + # is patch preloaded + if patch in self.patchesPath: + return os.path.join(self.patchesPath[patch], patch) + else: + # patchs from varia_repository used by the customizer for permalinks + if os.path.exists(patch): + return patch + else: + raise Exception("unknown patch: {}".format(patch)) + + def getDictPatches(self): + return self.dictPatches + + def getAdditionalPLMs(self): + return self.additionalPLMs diff --git a/worlds/sm/variaRandomizer/patches/vanilla/__init__.py b/worlds/sm/variaRandomizer/patches/vanilla/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/worlds/sm/variaRandomizer/patches/vanilla/ips/brinstar_map_room.ips b/worlds/sm/variaRandomizer/patches/vanilla/ips/brinstar_map_room.ips new file mode 100644 index 00000000..7e4cca51 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/vanilla/ips/brinstar_map_room.ips differ diff --git a/worlds/sm/variaRandomizer/patches/vanilla/ips/dachora.ips b/worlds/sm/variaRandomizer/patches/vanilla/ips/dachora.ips new file mode 100644 index 00000000..19dce7ee Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/vanilla/ips/dachora.ips differ diff --git a/worlds/sm/variaRandomizer/patches/vanilla/ips/early_super_bridge.ips b/worlds/sm/variaRandomizer/patches/vanilla/ips/early_super_bridge.ips new file mode 100644 index 00000000..ca31e0ed Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/vanilla/ips/early_super_bridge.ips differ diff --git a/worlds/sm/variaRandomizer/patches/vanilla/ips/high_jump.ips b/worlds/sm/variaRandomizer/patches/vanilla/ips/high_jump.ips new file mode 100644 index 00000000..54fbcb0a Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/vanilla/ips/high_jump.ips differ diff --git a/worlds/sm/variaRandomizer/patches/vanilla/ips/kraid_save.ips b/worlds/sm/variaRandomizer/patches/vanilla/ips/kraid_save.ips new file mode 100644 index 00000000..f05a6f76 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/vanilla/ips/kraid_save.ips differ diff --git a/worlds/sm/variaRandomizer/patches/vanilla/ips/mission_impossible.ips b/worlds/sm/variaRandomizer/patches/vanilla/ips/mission_impossible.ips new file mode 100644 index 00000000..1483777d Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/vanilla/ips/mission_impossible.ips differ diff --git a/worlds/sm/variaRandomizer/patches/vanilla/ips/moat.ips b/worlds/sm/variaRandomizer/patches/vanilla/ips/moat.ips new file mode 100644 index 00000000..2696ba98 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/vanilla/ips/moat.ips differ diff --git a/worlds/sm/variaRandomizer/patches/vanilla/ips/nova_boost_platform.ips b/worlds/sm/variaRandomizer/patches/vanilla/ips/nova_boost_platform.ips new file mode 100644 index 00000000..148f7aa1 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/vanilla/ips/nova_boost_platform.ips differ diff --git a/worlds/sm/variaRandomizer/patches/vanilla/ips/red_tower.ips b/worlds/sm/variaRandomizer/patches/vanilla/ips/red_tower.ips new file mode 100644 index 00000000..334bd959 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/vanilla/ips/red_tower.ips differ diff --git a/worlds/sm/variaRandomizer/patches/vanilla/ips/spazer.ips b/worlds/sm/variaRandomizer/patches/vanilla/ips/spazer.ips new file mode 100644 index 00000000..975670e9 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/vanilla/ips/spazer.ips differ diff --git a/worlds/sm/variaRandomizer/patches/vanilla/ips/spospo_save.ips b/worlds/sm/variaRandomizer/patches/vanilla/ips/spospo_save.ips new file mode 100644 index 00000000..9a7f1497 Binary files /dev/null and b/worlds/sm/variaRandomizer/patches/vanilla/ips/spospo_save.ips differ diff --git a/worlds/sm/variaRandomizer/patches/vanilla/patches.py b/worlds/sm/variaRandomizer/patches/vanilla/patches.py new file mode 100644 index 00000000..194abbb6 --- /dev/null +++ b/worlds/sm/variaRandomizer/patches/vanilla/patches.py @@ -0,0 +1,3 @@ +patches = {} + +additional_PLMs = {} diff --git a/worlds/sm/variaRandomizer/rando/Choice.py b/worlds/sm/variaRandomizer/rando/Choice.py new file mode 100644 index 00000000..3cb56677 --- /dev/null +++ b/worlds/sm/variaRandomizer/rando/Choice.py @@ -0,0 +1,213 @@ +import utils.log, random +from utils.utils import getRangeDict, chooseFromRange +from rando.ItemLocContainer import ItemLocation + +# helper object to choose item/loc +class Choice(object): + def __init__(self, restrictions): + self.restrictions = restrictions + self.settings = restrictions.settings + self.log = utils.log.get("Choice") + + # args are return from RandoServices.getPossiblePlacements + # return itemLoc dict, or None if no possible choice + def chooseItemLoc(self, itemLocDict, isProg): + return None + + def getItemList(self, itemLocDict): + return sorted([item for item in itemLocDict.keys()], key=lambda item: item.Type) + + def getLocList(self, itemLocDict, item): + return sorted(itemLocDict[item], key=lambda loc: loc.Name) + +# simple random choice, that chooses an item first, then a locatio to put it in +class ItemThenLocChoice(Choice): + def __init__(self, restrictions): + super(ItemThenLocChoice, self).__init__(restrictions) + + def chooseItemLoc(self, itemLocDict, isProg): + itemList = self.getItemList(itemLocDict) + item = self.chooseItem(itemList, isProg) + if item is None: + return None + locList = self.getLocList(itemLocDict, item) + loc = self.chooseLocation(locList, item, isProg) + if loc is None: + return None + return ItemLocation(item, loc) + + def chooseItem(self, itemList, isProg): + if len(itemList) == 0: + return None + if isProg: + return self.chooseItemProg(itemList) + else: + return self.chooseItemRandom(itemList) + + def chooseItemProg(self, itemList): + return self.chooseItemRandom(itemList) + + def chooseItemRandom(self, itemList): + return random.choice(itemList) + + def chooseLocation(self, locList, item, isProg): + if len(locList) == 0: + return None + if isProg: + return self.chooseLocationProg(locList, item) + else: + return self.chooseLocationRandom(locList) + + def chooseLocationProg(self, locList, item): + return self.chooseLocationRandom(locList) + + def chooseLocationRandom(self, locList): + return random.choice(locList) + +# Choice specialization for prog speed based filler +class ItemThenLocChoiceProgSpeed(ItemThenLocChoice): + def __init__(self, restrictions, progSpeedParams, distanceProp, services): + super(ItemThenLocChoiceProgSpeed, self).__init__(restrictions) + self.progSpeedParams = progSpeedParams + self.distanceProp = distanceProp + self.services = services + self.chooseItemFuncs = { + 'Random' : self.chooseItemRandom, + 'MinProgression' : self.chooseItemMinProgression, + 'MaxProgression' : self.chooseItemMaxProgression + } + self.chooseLocFuncs = { + 'Random' : self.chooseLocationRandom, + 'MinDiff' : self.chooseLocationMinDiff, + 'MaxDiff' : self.chooseLocationMaxDiff + } + + def currentLocations(self, item=None): + return self.services.currentLocations(self.ap, self.container, item=item) + + def processLateDoors(self, itemLocDict, ap, container): + doorBeams = self.restrictions.mandatoryBeams + def canOpenExtendedDoors(item): + return item.Category == 'Ammo' or item.Type in doorBeams + # exclude door items from itemLocDict + noDoorsLocDict = {item:locList for item,locList in itemLocDict.items() if not canOpenExtendedDoors(item) or container.sm.haveItem(item.Type)} + if len(noDoorsLocDict) > 0: + self.log.debug('processLateDoors. no doors') + itemLocDict.clear() + itemLocDict.update(noDoorsLocDict) + + def chooseItemLoc(self, itemLocDict, isProg, progressionItemLocs, ap, container): + # if late morph, redo the late morph check if morph is the + # only possibility since we can rollback + canRollback = len(container.currentItems) > 0 + if self.restrictions.isLateMorph() and canRollback and len(itemLocDict) == 1: + item, locList = list(itemLocDict.items())[0] + if item.Type == 'Morph': + morphLocs = self.restrictions.lateMorphCheck(container, locList) + if morphLocs is not None: + itemLocDict[item] = morphLocs + else: + return None + # if a boss is available, choose it right away + for item,locs in itemLocDict.items(): + if item.Category == 'Boss': + assert len(locs) == 1 and locs[0].Name == item.Name + return ItemLocation(item, locs[0]) + # late doors check for random door colors + if self.restrictions.isLateDoors() and random.random() < self.lateDoorsProb: + self.processLateDoors(itemLocDict, ap, container) + self.progressionItemLocs = progressionItemLocs + self.ap = ap + self.container = container + return super(ItemThenLocChoiceProgSpeed, self).chooseItemLoc(itemLocDict, isProg) + + def determineParameters(self, progSpeed=None, progDiff=None): + self.chooseLocRanges = getRangeDict(self.getChooseLocs(progDiff)) + self.chooseItemRanges = getRangeDict(self.getChooseItems(progSpeed)) + self.spreadProb = self.progSpeedParams.getSpreadFactor(progSpeed) + self.lateDoorsProb = self.progSpeedParams.getLateDoorsProb(progSpeed) + + def getChooseLocs(self, progDiff=None): + if progDiff is None: + progDiff = self.settings.progDiff + return self.progSpeedParams.getChooseLocDict(progDiff) + + def getChooseItems(self, progSpeed): + if progSpeed is None: + progSpeed = self.settings.progSpeed + return self.progSpeedParams.getChooseItemDict(progSpeed) + + def chooseItemProg(self, itemList): + ret = self.getChooseFunc(self.chooseItemRanges, self.chooseItemFuncs)(itemList) + self.log.debug('chooseItemProg. ret='+ret.Type) + return ret + + def chooseLocationProg(self, locs, item): + locs = self.getLocsSpreadProgression(locs) + random.shuffle(locs) + ret = self.getChooseFunc(self.chooseLocRanges, self.chooseLocFuncs)(locs) + self.log.debug('chooseLocationProg. ret='+ret.Name) + return ret + + # get choose function from a weighted dict + def getChooseFunc(self, rangeDict, funcDict): + v = chooseFromRange(rangeDict) + + return funcDict[v] + + def chooseItemMinProgression(self, items): + minNewLocs = 1000 + ret = None + + for item in items: + newLocs = len(self.currentLocations(item)) + if newLocs < minNewLocs: + minNewLocs = newLocs + ret = item + return ret + + def chooseItemMaxProgression(self, items): + maxNewLocs = 0 + ret = None + + for item in items: + newLocs = len(self.currentLocations(item)) + if newLocs > maxNewLocs: + maxNewLocs = newLocs + ret = item + return ret + + + def chooseLocationMaxDiff(self, availableLocations): + self.log.debug("MAX") + self.log.debug("chooseLocationMaxDiff: {}".format([(l.Name, l.difficulty) for l in availableLocations])) + return max(availableLocations, key=lambda loc:loc.difficulty.difficulty) + + def chooseLocationMinDiff(self, availableLocations): + self.log.debug("MIN") + self.log.debug("chooseLocationMinDiff: {}".format([(l.Name, l.difficulty) for l in availableLocations])) + return min(availableLocations, key=lambda loc:loc.difficulty.difficulty) + + def areaDistance(self, loc, otherLocs): + areas = [getattr(l, self.distanceProp) for l in otherLocs] + cnt = areas.count(getattr(loc, self.distanceProp)) + d = None + if cnt == 0: + d = 2 + else: + d = 1.0/cnt + return d + + def getLocsSpreadProgression(self, availableLocations): + split = self.restrictions.split + cond = lambda item: ((split == 'Full' and item.Class == 'Major') or split == item.Class) and item.Category != "Energy" + progLocs = [il.Location for il in self.progressionItemLocs if cond(il.Item)] + distances = [self.areaDistance(loc, progLocs) for loc in availableLocations] + maxDist = max(distances) + locs = [] + for i in range(len(availableLocations)): + loc = availableLocations[i] + d = distances[i] + if d == maxDist or random.random() >= self.spreadProb: + locs.append(loc) + return locs diff --git a/worlds/sm/variaRandomizer/rando/Filler.py b/worlds/sm/variaRandomizer/rando/Filler.py new file mode 100644 index 00000000..49e76e20 --- /dev/null +++ b/worlds/sm/variaRandomizer/rando/Filler.py @@ -0,0 +1,135 @@ + +import utils.log, copy, time, random + +from logic.cache import RequestCache +from rando.RandoServices import RandoServices +from rando.Choice import ItemThenLocChoice +from rando.RandoServices import ComebackCheckType +from rando.ItemLocContainer import ItemLocation, getItemLocationsStr +from utils.parameters import infinity +from logic.helpers import diffValue2txt +from graph.graph_utils import GraphUtils + +# base class for fillers. a filler responsibility is to fill a given +# ItemLocContainer while a certain condition is fulfilled (usually +# item pool is not empty). +# entry point is generateItems +class Filler(object): + def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity): + self.startAP = startAP + self.cache = RequestCache() + self.graph = graph + self.services = RandoServices(graph, restrictions, self.cache) + self.restrictions = restrictions + self.settings = restrictions.settings + self.endDate = endDate + self.baseContainer = emptyContainer + self.maxDiff = self.settings.maxDiff + self.log = utils.log.get('Filler') + + # reinit algo state + def initFiller(self): + self.ap = self.startAP + self.initContainer() + self.nSteps = 0 + self.errorMsg = "" + self.settings.maxDiff = self.maxDiff + self.startDate = time.process_time() + + # sets up container initial state + def initContainer(self): + self.container = copy.copy(self.baseContainer) + + # default continuation condition: item pool is not empty + def itemPoolCondition(self): + return not self.container.isPoolEmpty() + + # factory for step count condition + def createStepCountCondition(self, n): + return lambda: self.nSteps < n + + # calls step while condition is fulfilled and we did not hit runtime limit + # condition: continuation condition + # vcr: debug VCR object + # shall return (stuck, itemLoc dict list, progression itemLoc dict list) + def generateItems(self, condition=None, vcr=None): + self.vcr = vcr + self.initFiller() + if condition is None: + condition = self.itemPoolCondition + isStuck = False + date = self.startDate + while condition() and not isStuck and date <= self.endDate: + isStuck = not self.step() + if not isStuck: + self.nSteps += 1 + date = time.process_time() + if condition() or date > self.endDate: + isStuck = True + if date > self.endDate: + self.errorMsg = "Exceeded time limit of "+str(self.settings.runtimeLimit_s) +" seconds" + else: + self.errorMsg = "STUCK !\n"+self.container.dump() + else: + # check if some locations are above max diff and add relevant message + locs = self.container.getUsedLocs(lambda loc: loc.difficulty.difficulty > self.maxDiff) + aboveMaxDiffStr = '[ ' + ' ; '.join([loc.Name + ': ' + diffValue2txt(loc.difficulty.difficulty) for loc in locs]) + ' ]' + if aboveMaxDiffStr != '[ ]': + self.errorMsg += "\nMaximum difficulty could not be applied everywhere. Affected locations: {}".format(aboveMaxDiffStr) + isStuck = False + print('\n%d step(s) in %dms' % (self.nSteps, int((date-self.startDate)*1000))) + if self.vcr != None: + self.vcr.dump() + return (isStuck, self.container.itemLocations, self.getProgressionItemLocations()) + + # helper method to collect in item/location with logic. updates self.ap and VCR + def collect(self, itemLoc, container=None, pickup=True): + containerArg = container + if container is None: + container = self.container + location = itemLoc.Location + item = itemLoc.Item + pickup &= location.restricted is None or location.restricted == False + self.ap = self.services.collect(self.ap, container, itemLoc, pickup=pickup) + self.log.debug("AP="+self.ap) + if self.vcr is not None and containerArg is None: + self.vcr.addLocation(location.Name, item.Type) + + # called by generateItems at the end to knows which particulier + # item/locations were progression, if the info is available + def getProgressionItemLocations(self): + return [] + + # performs a fill step. can be multiple item/locations placement, + # not necessarily just one. + # return True if ok, False if stuck + def step(self): + pass + +# very simple front fill algorithm with no rollback and no "softlock checks" (== dessy algorithm) +class FrontFiller(Filler): + def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity): + super(FrontFiller, self).__init__(startAP, graph, restrictions, emptyContainer, endDate) + self.choice = ItemThenLocChoice(restrictions) + self.stdStart = GraphUtils.isStandardStart(self.startAP) + + def isEarlyGame(self): + n = 2 if self.stdStart else 3 + return len(self.container.currentItems) <= n + + # one item/loc per step + def step(self, onlyBossCheck=False): + self.cache.reset() + if not self.services.can100percent(self.ap, self.container): + comebackCheck = ComebackCheckType.ComebackWithoutItem if not self.isEarlyGame() else ComebackCheckType.NoCheck + (itemLocDict, isProg) = self.services.getPossiblePlacements(self.ap, self.container, comebackCheck) + else: + (itemLocDict, isProg) = self.services.getPossiblePlacementsNoLogic(self.container) + itemLoc = self.choice.chooseItemLoc(itemLocDict, isProg) + if itemLoc is None: + if onlyBossCheck == False and self.services.onlyBossesLeft(self.ap, self.container): + self.settings.maxDiff = infinity + return self.step(onlyBossCheck=True) + return False + self.collect(itemLoc) + return True diff --git a/worlds/sm/variaRandomizer/rando/GraphBuilder.py b/worlds/sm/variaRandomizer/rando/GraphBuilder.py new file mode 100644 index 00000000..3670550e --- /dev/null +++ b/worlds/sm/variaRandomizer/rando/GraphBuilder.py @@ -0,0 +1,134 @@ + +import utils.log, random, copy + +from graph.graph_utils import GraphUtils, vanillaTransitions, vanillaBossesTransitions, escapeSource, escapeTargets +from logic.logic import Logic +from graph.graph import AccessGraphRando as AccessGraph + +# creates graph and handles randomized escape +class GraphBuilder(object): + def __init__(self, graphSettings): + self.graphSettings = graphSettings + self.areaRando = graphSettings.areaRando + self.bossRando = graphSettings.bossRando + self.escapeRando = graphSettings.escapeRando + self.minimizerN = graphSettings.minimizerN + self.log = utils.log.get('GraphBuilder') + + # builds everything but escape transitions + def createGraph(self): + transitions = self.graphSettings.plandoRandoTransitions + if transitions is None: + transitions = [] + if self.minimizerN is not None: + transitions = GraphUtils.createMinimizerTransitions(self.graphSettings.startAP, self.minimizerN) + else: + if not self.bossRando: + transitions += vanillaBossesTransitions + else: + transitions += GraphUtils.createBossesTransitions() + if not self.areaRando: + transitions += vanillaTransitions + else: + transitions += GraphUtils.createAreaTransitions(self.graphSettings.lightAreaRando) + return AccessGraph(Logic.accessPoints, transitions, self.graphSettings.dotFile) + + # fills in escape transitions if escape rando is enabled + # scavEscape = None or (itemLocs, scavItemLocs) couple from filler + def escapeGraph(self, container, graph, maxDiff, scavEscape): + if not self.escapeRando: + return True + emptyContainer = copy.copy(container) + emptyContainer.resetCollected(reassignItemLocs=True) + dst = None + if scavEscape is None: + possibleTargets, dst, path = self.getPossibleEscapeTargets(emptyContainer, graph, maxDiff) + # update graph with escape transition + graph.addTransition(escapeSource, dst) + else: + possibleTargets, path = self.getScavengerEscape(emptyContainer, graph, maxDiff, scavEscape) + if path is None: + return False + # get timer value + self.escapeTimer(graph, path, self.areaRando or scavEscape is not None) + self.log.debug("escapeGraph: ({}, {}) timer: {}".format(escapeSource, dst, graph.EscapeAttributes['Timer'])) + # animals + GraphUtils.escapeAnimalsTransitions(graph, possibleTargets, dst) + return True + + def _getTargets(self, sm, graph, maxDiff): + possibleTargets = [target for target in escapeTargets if graph.accessPath(sm, target, 'Landing Site', maxDiff) is not None] + self.log.debug('_getTargets. targets='+str(possibleTargets)) + # failsafe + if len(possibleTargets) == 0: + self.log.debug("Can't randomize escape, fallback to vanilla") + possibleTargets.append('Climb Bottom Left') + random.shuffle(possibleTargets) + return possibleTargets + + def getPossibleEscapeTargets(self, emptyContainer, graph, maxDiff): + sm = emptyContainer.sm + # setup smbm with item pool + # Ice not usable because of hyper beam + # remove energy to avoid hell runs + # (will add bosses as well) + sm.addItems([item.Type for item in emptyContainer.itemPool if item.Type != 'Ice' and item.Category != 'Energy']) + sm.addItem('Hyper') + possibleTargets = self._getTargets(sm, graph, maxDiff) + # pick one + dst = possibleTargets.pop() + path = graph.accessPath(sm, dst, 'Landing Site', maxDiff) + return (possibleTargets, dst, path) + + def getScavengerEscape(self, emptyContainer, graph, maxDiff, scavEscape): + sm = emptyContainer.sm + itemLocs, lastScavItemLoc = scavEscape[0], scavEscape[1][-1] + # collect all item/locations up until last scav + for il in itemLocs: + emptyContainer.collect(il) + if il == lastScavItemLoc: + break + possibleTargets = self._getTargets(sm, graph, maxDiff) + path = graph.accessPath(sm, lastScavItemLoc.Location.accessPoint, 'Landing Site', maxDiff) + return (possibleTargets, path) + + # path: as returned by AccessGraph.accessPath + def escapeTimer(self, graph, path, compute): + if compute == True: + if path[0].Name == 'Climb Bottom Left': + graph.EscapeAttributes['Timer'] = None + return + traversedAreas = list(set([ap.GraphArea for ap in path])) + self.log.debug("escapeTimer path: " + str([ap.Name for ap in path])) + self.log.debug("escapeTimer traversedAreas: " + str(traversedAreas)) + # rough estimates of navigation within areas to reach "borders" + # (can obviously be completely off wrt to actual path, but on the generous side) + traversals = { + 'Crateria':90, + 'GreenPinkBrinstar':90, + 'WreckedShip':120, + 'LowerNorfair':135, + 'WestMaridia':75, + 'EastMaridia':100, + 'RedBrinstar':75, + 'Norfair': 120, + 'Kraid': 40, + 'Crocomire': 40, + # can't be on the path + 'Tourian': 0, + } + t = 90 if self.areaRando else 0 + for area in traversedAreas: + t += traversals[area] + t = max(t, 180) + else: + escapeTargetsTimer = { + 'Climb Bottom Left': None, # vanilla + 'Green Brinstar Main Shaft Top Left': 210, # brinstar + 'Basement Left': 210, # wrecked ship + 'Business Center Mid Left': 270, # norfair + 'Crab Hole Bottom Right': 270 # maridia + } + t = escapeTargetsTimer[path[0].Name] + self.log.debug("escapeTimer. t="+str(t)) + graph.EscapeAttributes['Timer'] = t diff --git a/worlds/sm/variaRandomizer/rando/ItemLocContainer.py b/worlds/sm/variaRandomizer/rando/ItemLocContainer.py new file mode 100644 index 00000000..8ddf2a87 --- /dev/null +++ b/worlds/sm/variaRandomizer/rando/ItemLocContainer.py @@ -0,0 +1,248 @@ + +import copy, utils.log + +from logic.smbool import SMBool, smboolFalse +from logic.smboolmanager import SMBoolManager +from collections import Counter + +class ItemLocation(object): + __slots__ = ( 'Item', 'Location', 'Accessible' ) + + def __init__(self, Item=None, Location=None, accessible=True): + self.Item = Item + self.Location = Location + self.Accessible = accessible + + def json(self): + return {'Item': self.Item.json(), 'Location': self.Location.json()} + +def getItemListStr(items): + return str(dict(Counter(["%s/%s" % (item.Type,item.Class) for item in items]))) + +def getLocListStr(locs): + return str([loc.Name for loc in locs]) + +def getItemLocStr(itemLoc): + return itemLoc.Item.Type + " at " + itemLoc.Location.Name + +def getItemLocationsStr(itemLocations): + return str([getItemLocStr(il) for il in itemLocations]) + +class ContainerSoftBackup(object): + def __init__(self, container): + self.itemLocations = container.itemLocations[:] + self.itemPool = container.itemPool[:] + self.unusedLocations = container.unusedLocations[:] + self.currentItems = container.currentItems[:] + + def restore(self, container, resetSM=True): + # avoid costly deep copies of locations + container.itemLocations = self.itemLocations[:] + container.itemPool = self.itemPool[:] + container.unusedLocations = self.unusedLocations[:] + container.currentItems = self.currentItems[:] + if resetSM: + container.sm.resetItems() + container.sm.addItems([it.Type for it in container.currentItems]) + +# Holds items yet to place (itemPool), locations yet to fill (unusedLocations), +# placed items/locations (itemLocations). +# If logic is needed, also holds a SMBoolManager (sm) and collected items so far +# (collectedItems) +class ItemLocContainer(object): + def __init__(self, sm, itemPool, locations): + self.sm = sm + self.itemLocations = [] + self.unusedLocations = locations + self.currentItems = [] + self.itemPool = itemPool + self.itemPoolBackup = None + self.unrestrictedItems = set() + self.log = utils.log.get('ItemLocContainer') + self.checkConsistency() + + def checkConsistency(self): + assert len(self.unusedLocations) == len(self.itemPool), "Item({})/Locs({}) count mismatch".format(len(self.itemPool), len(self.unusedLocations)) + + def __eq__(self, rhs): + eq = self.currentItems == rhs.currentItems + eq &= getLocListStr(self.unusedLocations) == getLocListStr(rhs.unusedLocations) + eq &= self.itemPool == rhs.itemPool + eq &= getItemLocationsStr(self.itemLocations) == getItemLocationsStr(rhs.itemLocations) + + return eq + + def __copy__(self): + locs = copy.copy(self.unusedLocations) + # we don't copy restriction state on purpose: it depends on + # outside context we don't want to bring to the copy + ret = ItemLocContainer(SMBoolManager(self.sm.player, self.sm.maxDiff), + self.itemPoolBackup[:] if self.itemPoolBackup != None else self.itemPool[:], + locs) + ret.currentItems = self.currentItems[:] + ret.unrestrictedItems = copy.copy(self.unrestrictedItems) + ret.itemLocations = [ ItemLocation( + il.Item, + copy.copy(il.Location) + ) for il in self.itemLocations ] + ret.sm.addItems([item.Type for item in ret.currentItems]) + return ret + + # create a new container based on slice predicates on items and + # locs. both predicates must result in a consistent container + # (same number of unused locations and not placed items) + def slice(self, itemPoolCond, locPoolCond): + assert self.itemPoolBackup is None, "Cannot slice a constrained container" + locs = self.getLocs(locPoolCond) + items = self.getItems(itemPoolCond) + cont = ItemLocContainer(self.sm, items, locs) + cont.currentItems = self.currentItems + cont.itemLocations = self.itemLocations + return copy.copy(cont) + + # transfer collected items/locations to another container + def transferCollected(self, dest): + dest.currentItems = self.currentItems[:] + dest.sm = SMBoolManager(self.sm.player, self.sm.maxDiff) + dest.sm.addItems([item.Type for item in dest.currentItems]) + dest.itemLocations = copy.copy(self.itemLocations) + dest.unrestrictedItems = copy.copy(self.unrestrictedItems) + + # reset collected items/locations. if reassignItemLocs is True, + # will re-fill itemPool and unusedLocations as they were before + # collection + def resetCollected(self, reassignItemLocs=False): + self.currentItems = [] + if reassignItemLocs == False: + self.itemLocations = [] + else: + while len(self.itemLocations) > 0: + il = self.itemLocations.pop() + self.itemPool.append(il.Item) + self.unusedLocations.append(il.Location) + self.unrestrictedItems = set() + self.sm.resetItems() + + def dump(self): + return "ItemPool(%d): %s\nLocPool(%d): %s\nCollected: %s" % (len(self.itemPool), getItemListStr(self.itemPool), len(self.unusedLocations), getLocListStr(self.unusedLocations), getItemListStr(self.currentItems)) + + # temporarily restrict item pool to items fulfilling predicate + def restrictItemPool(self, predicate): + assert self.itemPoolBackup is None, "Item pool already restricted" + self.itemPoolBackup = self.itemPool + self.itemPool = [item for item in self.itemPoolBackup if predicate(item)] + self.log.debug("restrictItemPool: "+getItemListStr(self.itemPool)) + + # remove a placed restriction + def unrestrictItemPool(self): + assert self.itemPoolBackup is not None, "No pool restriction to remove" + self.itemPool = self.itemPoolBackup + self.itemPoolBackup = None + self.log.debug("unrestrictItemPool: "+getItemListStr(self.itemPool)) + + def removeLocation(self, location): + if location in self.unusedLocations: + self.unusedLocations.remove(location) + + def removeItem(self, item): + self.itemPool.remove(item) + if self.itemPoolBackup is not None: + self.itemPoolBackup.remove(item) + + # collect an item at a location. if pickup is True, also affects logic (sm) and collectedItems + def collect(self, itemLocation, pickup=True): + item = itemLocation.Item + location = itemLocation.Location + if not location.restricted: + self.unrestrictedItems.add(item.Type) + if pickup == True: + self.currentItems.append(item) + self.sm.addItem(item.Type) + self.removeLocation(location) + self.itemLocations.append(itemLocation) + self.removeItem(item) + + def isPoolEmpty(self): + return len(self.itemPool) == 0 + + def getNextItemInPool(self, t): + return next((item for item in self.itemPool if item.Type == t), None) + + def getNextItemInPoolMatching(self, predicate): + return next((item for item in self.itemPool if predicate(item) == True), None) + + def hasItemTypeInPool(self, t): + return any(item.Type == t for item in self.itemPool) + + def hasItemInPool(self, predicate): + return any(predicate(item) == True for item in self.itemPool) + + def hasItemCategoryInPool(self, cat): + return any(item.Category == cat for item in self.itemPool) + + def getNextItemInPoolFromCategory(self, cat): + return next((item for item in self.itemPool if item.Category == cat), None) + + def getAllItemsInPoolFromCategory(self, cat): + return [item for item in self.itemPool if item.Category == cat] + + def countItemTypeInPool(self, t): + return sum(1 for item in self.itemPool if item.Type == t) + + def countItems(self, predicate): + return sum(1 for item in self.itemPool if predicate(item) == True) + + # gets the items pool in the form of a dicitionary whose keys are item types + # and values list of items of this type + def getPoolDict(self): + poolDict = {} + for item in self.itemPool: + if item.Type not in poolDict: + poolDict[item.Type] = [] + poolDict[item.Type].append(item) + return poolDict + + def getLocs(self, predicate): + return [loc for loc in self.unusedLocations if predicate(loc) == True] + + def getItems(self, predicate): + return [item for item in self.itemPool if predicate(item) == True] + + def getUsedLocs(self, predicate): + return [il.Location for il in self.itemLocations if predicate(il.Location) == True] + + def getItemLoc(self, loc): + for il in self.itemLocations: + if il.Location == loc: + return il + + def getCollectedItems(self, predicate): + return [item for item in self.currentItems if predicate(item) == True] + + def hasUnrestrictedLocWithItemType(self, itemType): + return itemType in self.unrestrictedItems + + def getLocsForSolver(self): + locs = [] + for il in self.itemLocations: + loc = il.Location + self.log.debug("getLocsForSolver: {}".format(loc.Name)) + # filter out restricted locations + if loc.restricted: + self.log.debug("getLocsForSolver: restricted, remove {}".format(loc.Name)) + continue + loc.itemName = il.Item.Type + locs.append(loc) + return locs + + def cleanLocsAfterSolver(self): + # restricted locs can have their difficulty set, which can cause them to be reported in the + # post randomization warning message about locs with diff > max diff. + for il in self.itemLocations: + loc = il.Location + if loc.restricted and loc.difficulty == True: + loc.difficulty = smboolFalse + + def getDistinctItems(self): + itemTypes = {item.Type for item in self.itemPool} + return [self.getNextItemInPool(itemType) for itemType in itemTypes] diff --git a/worlds/sm/variaRandomizer/rando/Items.py b/worlds/sm/variaRandomizer/rando/Items.py new file mode 100644 index 00000000..b7dacca4 --- /dev/null +++ b/worlds/sm/variaRandomizer/rando/Items.py @@ -0,0 +1,790 @@ +from utils.utils import randGaussBounds, getRangeDict, chooseFromRange +import utils.log, logging, copy, random + +class Item: + __slots__ = ( 'Category', 'Class', 'Name', 'Code', 'Type', 'BeamBits', 'ItemBits', 'Id' ) + + def __init__(self, Category, Class, Name, Type, Code=None, BeamBits=0, ItemBits=0, Id=None): + self.Category = Category + self.Class = Class + self.Code = Code + self.Name = Name + self.Type = Type + self.BeamBits = BeamBits + self.ItemBits = ItemBits + self.Id = Id + + def withClass(self, Class): + return Item(self.Category, Class, self.Name, self.Type, self.Code, self.BeamBits, self.ItemBits) + + def __eq__(self, other): + # used to remove an item from a list + return self.Type == other.Type and self.Class == other.Class + + def __hash__(self): + # as we define __eq__ we have to also define __hash__ to use items as dictionnary keys + # https://docs.python.org/3/reference/datamodel.html#object.__hash__ + return id(self) + + def __repr__(self): + return "Item({}, {}, {}, {}, {})".format(self.Category, + self.Class, self.Code, self.Name, self.Type) + + def json(self): + # as we have slots instead of dict + return {key : getattr(self, key, None) for key in self.__slots__} + +class ItemManager: + Items = { + 'ETank': Item( + Category='Energy', + Class='Major', + Code=0xf870, + Name="Energy Tank", + Type='ETank', + Id=0 + ), + 'Missile': Item( + Category='Ammo', + Class='Minor', + Code=0xf870, + Name="Missile", + Type='Missile', + Id=1 + ), + 'Super': Item( + Category='Ammo', + Class='Minor', + Code=0xf870, + Name="Super Missile", + Type='Super', + Id=2 + ), + 'PowerBomb': Item( + Category='Ammo', + Class='Minor', + Code=0xf870, + Name="Power Bomb", + Type='PowerBomb', + Id=3 + ), + 'Bomb': Item( + Category='Progression', + Class='Major', + Code=0xf870, + Name="Bomb", + Type='Bomb', + ItemBits=0x1000, + Id=4 + ), + 'Charge': Item( + Category='Beam', + Class='Major', + Code=0xf870, + Name="Charge Beam", + Type='Charge', + BeamBits=0x1000, + Id=5 + ), + 'Ice': Item( + Category='Progression', + Class='Major', + Code=0xf870, + Name="Ice Beam", + Type='Ice', + BeamBits=0x2, + Id=6 + ), + 'HiJump': Item( + Category='Progression', + Class='Major', + Code=0xf870, + Name="Hi-Jump Boots", + Type='HiJump', + ItemBits=0x100, + Id=7 + ), + 'SpeedBooster': Item( + Category='Progression', + Class='Major', + Code=0xf870, + Name="Speed Booster", + Type='SpeedBooster', + ItemBits=0x2000, + Id=8 + ), + 'Wave': Item( + Category='Beam', + Class='Major', + Code=0xf870, + Name="Wave Beam", + Type='Wave', + BeamBits=0x1, + Id=9 + ), + 'Spazer': Item( + Category='Beam', + Class='Major', + Code=0xf870, + Name="Spazer", + Type='Spazer', + BeamBits=0x4, + Id=10 + ), + 'SpringBall': Item( + Category='Misc', + Class='Major', + Code=0xf870, + Name="Spring Ball", + Type='SpringBall', + ItemBits=0x2, + Id=11 + ), + 'Varia': Item( + Category='Progression', + Class='Major', + Code=0xf870, + Name="Varia Suit", + Type='Varia', + ItemBits=0x1, + Id=12 + ), + 'Plasma': Item( + Category='Beam', + Class='Major', + Code=0xf870, + Name="Plasma Beam", + Type='Plasma', + BeamBits=0x8, + Id=15 + ), + 'Grapple': Item( + Category='Progression', + Class='Major', + Code=0xf870, + Name="Grappling Beam", + Type='Grapple', + ItemBits=0x4000, + Id=16 + ), + 'Morph': Item( + Category='Progression', + Class='Major', + Code=0xf870, + Name="Morph Ball", + Type='Morph', + ItemBits=0x4, + Id=19 + ), + 'Reserve': Item( + Category='Energy', + Class='Major', + Code=0xf870, + Name="Reserve Tank", + Type='Reserve', + Id=20 + ), + 'Gravity': Item( + Category='Progression', + Class='Major', + Code=0xf870, + Name="Gravity Suit", + Type='Gravity', + ItemBits=0x20, + Id=13 + ), + 'XRayScope': Item( + Category='Misc', + Class='Major', + Code=0xf870, + Name="X-Ray Scope", + Type='XRayScope', + ItemBits=0x8000, + Id=14 + ), + 'SpaceJump': Item( + Category='Progression', + Class='Major', + Code=0xf870, + Name="Space Jump", + Type='SpaceJump', + ItemBits=0x200, + Id=17 + ), + 'ScrewAttack': Item( + Category='Misc', + Class='Major', + Code=0xf870, + Name="Screw Attack", + Type='ScrewAttack', + ItemBits= 0x8, + Id=18 + ), + 'Nothing': Item( + Category='Nothing', + Class='Minor', + Code=0xbae9, # new nothing plm + Name="Nothing", + Type='Nothing', + Id=22 + ), + 'NoEnergy': Item( + Category='Nothing', + Class='Major', + Code=0xbae9, # see above + Name="No Energy", + Type='NoEnergy', + Id=23 + ), + 'Kraid': Item( + Category='Boss', + Class='Boss', + Name="Kraid", + Type='Kraid', + ), + 'Phantoon': Item( + Category='Boss', + Class='Boss', + Name="Phantoon", + Type='Phantoon' + ), + 'Draygon': Item( + Category='Boss', + Class='Boss', + Name="Draygon", + Type='Draygon', + ), + 'Ridley': Item( + Category='Boss', + Class='Boss', + Name="Ridley", + Type='Ridley', + ), + 'MotherBrain': Item( + Category='Boss', + Class='Boss', + Name="Mother Brain", + Type='MotherBrain', + ), + # used only during escape path check + 'Hyper': Item( + Category='Beam', + Class='Major', + Code=0xffff, + Name="Hyper Beam", + Type='Hyper', + ), + 'ArchipelagoItem': Item( + Category='ArchipelagoItem', + Class='Major', + Code=0xf870, + Name="Generic", + Type='ArchipelagoItem', + Id=21 + ) + } + + for itemType, item in Items.items(): + if item.Type != itemType: + raise RuntimeError("Wrong item type for {} (expected {})".format(item, itemType)) + + @staticmethod + def isBeam(item): + return item.BeamBits != 0 + + @staticmethod + def getItemTypeCode(item, itemVisibility): + if item.Category == 'Nothing': + if itemVisibility in ['Visible', 'Chozo']: + modifier = 0 + elif itemVisibility == 'Hidden': + modifier = 4 + else: + if itemVisibility == 'Visible': + modifier = 0 + elif itemVisibility == 'Chozo': + modifier = 4 + elif itemVisibility == 'Hidden': + modifier = 8 + + itemCode = item.Code + modifier + return itemCode + + def __init__(self, majorsSplit, qty, sm, nLocs, maxDiff): + self.qty = qty + self.sm = sm + self.majorsSplit = majorsSplit + self.nLocs = nLocs + self.maxDiff = maxDiff + self.majorClass = 'Chozo' if majorsSplit == 'Chozo' else 'Major' + self.itemPool = [] + + def newItemPool(self, addBosses=True): + self.itemPool = [] + if addBosses == True: + # for the bosses + for boss in ['Kraid', 'Phantoon', 'Draygon', 'Ridley', 'MotherBrain']: + self.addMinor(boss) + + def getItemPool(self): + return self.itemPool + + def setItemPool(self, pool): + self.itemPool = pool + + def addItem(self, itemType, itemClass=None): + self.itemPool.append(ItemManager.getItem(itemType, itemClass)) + + def addMinor(self, minorType): + self.addItem(minorType, 'Minor') + + # remove from pool an item of given type. item type has to be in original Items list. + def removeItem(self, itemType): + for idx, item in enumerate(self.itemPool): + if item.Type == itemType: + self.itemPool = self.itemPool[0:idx] + self.itemPool[idx+1:] + return item + + def removeForbiddenItems(self, forbiddenItems): + # the pool is the one managed by the Randomizer + for itemType in forbiddenItems: + self.removeItem(itemType) + self.addItem('NoEnergy', self.majorClass) + return self.itemPool + + @staticmethod + def getItem(itemType, itemClass=None): + if itemClass is None: + return copy.copy(ItemManager.Items[itemType]) + else: + return ItemManager.Items[itemType].withClass(itemClass) + + def createItemPool(self, exclude=None): + itemPoolGenerator = ItemPoolGenerator.factory(self.majorsSplit, self, self.qty, self.sm, exclude, self.nLocs, self.maxDiff) + self.itemPool = itemPoolGenerator.getItemPool() + + @staticmethod + def getProgTypes(): + return [item for item in ItemManager.Items if ItemManager.Items[item].Category == 'Progression'] + + def hasItemInPoolCount(self, itemName, count): + return len([item for item in self.itemPool if item.Type == itemName]) >= count + +class ItemPoolGenerator(object): + @staticmethod + def factory(majorsSplit, itemManager, qty, sm, exclude, nLocs, maxDiff): + if majorsSplit == 'Chozo': + return ItemPoolGeneratorChozo(itemManager, qty, sm, maxDiff) + elif majorsSplit == 'Plando': + return ItemPoolGeneratorPlando(itemManager, qty, sm, exclude, nLocs, maxDiff) + elif nLocs == 105: + if majorsSplit == "Scavenger": + return ItemPoolGeneratorScavenger(itemManager, qty, sm, maxDiff) + else: + return ItemPoolGeneratorMajors(itemManager, qty, sm, maxDiff) + else: + return ItemPoolGeneratorMinimizer(itemManager, qty, sm, nLocs, maxDiff) + + def __init__(self, itemManager, qty, sm, maxDiff): + self.itemManager = itemManager + self.qty = qty + self.sm = sm + self.maxItems = 105 # 100 item locs and 5 bosses + self.maxEnergy = 18 # 14E, 4R + self.maxDiff = maxDiff + self.log = utils.log.get('ItemPool') + + def isUltraSparseNoTanks(self): + # if low stuff botwoon is not known there is a hard energy req of one tank, even + # with both suits + lowStuffBotwoon = self.sm.knowsLowStuffBotwoon() + return random.random() < 0.5 and (lowStuffBotwoon.bool == True and lowStuffBotwoon.difficulty <= self.maxDiff) + + def calcMaxMinors(self): + pool = self.itemManager.getItemPool() + energy = [item for item in pool if item.Category == 'Energy'] + if len(energy) == 0: + self.maxMinors = 0.66*(self.maxItems - 5) # 5 for bosses + else: + # if energy has been placed, we can be as accurate as possible + self.maxMinors = self.maxItems - len(pool) + self.nbMinorsAlready + + def calcMaxAmmo(self): + self.nbMinorsAlready = 5 + # always add enough minors to pass zebetites (1100 damages) and mother brain 1 (3000 damages) + # accounting for missile refill. so 15-10, or 10-10 if ice zeb skip is known (Ice is always in item pool) + zebSkip = self.sm.knowsIceZebSkip() + if zebSkip.bool == False or zebSkip.difficulty > self.maxDiff: + self.log.debug("Add missile because ice zeb skip is not known") + self.itemManager.addMinor('Missile') + self.nbMinorsAlready += 1 + self.calcMaxMinors() + self.log.debug("maxMinors: "+str(self.maxMinors)) + self.minorLocations = max(0, self.maxMinors*self.qty['minors']/100.0 - self.nbMinorsAlready) + self.log.debug("minorLocations: {}".format(self.minorLocations)) + + # add ammo given quantity settings + def addAmmo(self): + self.calcMaxAmmo() + # we have to remove the minors already added + maxItems = min(len(self.itemManager.getItemPool()) + int(self.minorLocations), self.maxItems) + self.log.debug("maxItems: {}, (self.maxItems={})".format(maxItems, self.maxItems)) + ammoQty = self.qty['ammo'] + if not self.qty['strictMinors']: + rangeDict = getRangeDict(ammoQty) + self.log.debug("rangeDict: {}".format(rangeDict)) + while len(self.itemManager.getItemPool()) < maxItems: + item = chooseFromRange(rangeDict) + self.itemManager.addMinor(item) + else: + minorsTypes = ['Missile', 'Super', 'PowerBomb'] + totalProps = sum(ammoQty[m] for m in minorsTypes) + minorsByProp = sorted(minorsTypes, key=lambda m: ammoQty[m]) + totalMinorLocations = self.minorLocations + self.nbMinorsAlready + self.log.debug("totalMinorLocations: {}".format(totalMinorLocations)) + def ammoCount(ammo): + return float(len([item for item in self.itemManager.getItemPool() if item.Type == ammo])) + def targetRatio(ammo): + return round(float(ammoQty[ammo])/totalProps, 3) + def cmpRatio(ammo, ratio): + thisAmmo = ammoCount(ammo) + thisRatio = round(thisAmmo/totalMinorLocations, 3) + nextRatio = round((thisAmmo + 1)/totalMinorLocations, 3) + self.log.debug("{} current, next/target ratio: {}, {}/{}".format(ammo, thisRatio, nextRatio, ratio)) + return abs(nextRatio - ratio) < abs(thisRatio - ratio) + def fillAmmoType(ammo, checkRatio=True): + ratio = targetRatio(ammo) + self.log.debug("{}: target ratio: {}".format(ammo, ratio)) + while len(self.itemManager.getItemPool()) < maxItems and (not checkRatio or cmpRatio(ammo, ratio)): + self.log.debug("Add {}".format(ammo)) + self.itemManager.addMinor(ammo) + for m in minorsByProp: + fillAmmoType(m) + # now that the ratios have been matched as exactly as possible, we distribute the error + def getError(m, countOffset=0): + return abs((ammoCount(m)+countOffset)/totalMinorLocations - targetRatio(m)) + while len(self.itemManager.getItemPool()) < maxItems: + minNextError = 1000 + chosenAmmo = None + for m in minorsByProp: + nextError = getError(m, 1) + if nextError < minNextError: + minNextError = nextError + chosenAmmo = m + self.itemManager.addMinor(chosenAmmo) + # fill up the rest with blank items + for i in range(self.maxItems - maxItems): + self.itemManager.addMinor('Nothing') + +class ItemPoolGeneratorChozo(ItemPoolGenerator): + def addEnergy(self): + total = 18 + energyQty = self.qty['energy'] + if energyQty == 'ultra sparse': + # 0-1, remove reserve tank and two etanks, check if it also remove the last etank + self.itemManager.removeItem('Reserve') + self.itemManager.addItem('NoEnergy', 'Chozo') + self.itemManager.removeItem('ETank') + self.itemManager.addItem('NoEnergy', 'Chozo') + self.itemManager.removeItem('ETank') + self.itemManager.addItem('NoEnergy', 'Chozo') + if self.isUltraSparseNoTanks(): + # no etank nor reserve + self.itemManager.removeItem('ETank') + self.itemManager.addItem('NoEnergy', 'Chozo') + elif random.random() < 0.5: + # replace only etank with reserve + self.itemManager.removeItem('ETank') + self.itemManager.addItem('Reserve', 'Chozo') + + # complete up to 18 energies with nothing item + alreadyInPool = 4 + for i in range(total - alreadyInPool): + self.itemManager.addItem('Nothing', 'Minor') + elif energyQty == 'sparse': + # 4-6 + # already 3E and 1R + alreadyInPool = 4 + rest = randGaussBounds(2, 5) + if rest >= 1: + if random.random() < 0.5: + self.itemManager.addItem('Reserve', 'Minor') + else: + self.itemManager.addItem('ETank', 'Minor') + for i in range(rest-1): + self.itemManager.addItem('ETank', 'Minor') + # complete up to 18 energies with nothing item + for i in range(total - alreadyInPool - rest): + self.itemManager.addItem('Nothing', 'Minor') + elif energyQty == 'medium': + # 8-12 + # add up to 3 Reserves or ETanks (cannot add more than 3 reserves) + for i in range(3): + if random.random() < 0.5: + self.itemManager.addItem('Reserve', 'Minor') + else: + self.itemManager.addItem('ETank', 'Minor') + # 7 already in the pool (3 E, 1 R, + the previous 3) + alreadyInPool = 7 + rest = 1 + randGaussBounds(4, 3.7) + for i in range(rest): + self.itemManager.addItem('ETank', 'Minor') + # fill the rest with NoEnergy + for i in range(total - alreadyInPool - rest): + self.itemManager.addItem('Nothing', 'Minor') + else: + # add the vanilla 3 reserves and 13 Etanks + for i in range(3): + self.itemManager.addItem('Reserve', 'Minor') + for i in range(11): + self.itemManager.addItem('ETank', 'Minor') + + def getItemPool(self): + self.itemManager.newItemPool() + # 25 locs: 16 majors, 3 etanks, 1 reserve, 2 missile, 2 supers, 1 pb + for itemType in ['ETank', 'ETank', 'ETank', 'Reserve', 'Missile', 'Missile', 'Super', 'Super', 'PowerBomb', 'Bomb', 'Charge', 'Ice', 'HiJump', 'SpeedBooster', 'Wave', 'Spazer', 'SpringBall', 'Varia', 'Plasma', 'Grapple', 'Morph', 'Gravity', 'XRayScope', 'SpaceJump', 'ScrewAttack']: + self.itemManager.addItem(itemType, 'Chozo') + + self.addEnergy() + self.addAmmo() + + return self.itemManager.getItemPool() + +class ItemPoolGeneratorMajors(ItemPoolGenerator): + def __init__(self, itemManager, qty, sm, maxDiff): + super(ItemPoolGeneratorMajors, self).__init__(itemManager, qty, sm, maxDiff) + self.sparseRest = 1 + randGaussBounds(2, 5) + self.mediumRest = 3 + randGaussBounds(4, 3.7) + self.ultraSparseNoTanks = self.isUltraSparseNoTanks() + + def addNoEnergy(self): + self.itemManager.addItem('NoEnergy') + + def addEnergy(self): + total = self.maxEnergy + alreadyInPool = 2 + def getE(toAdd): + nonlocal total, alreadyInPool + d = total - alreadyInPool - toAdd + if d < 0: + toAdd += d + return toAdd + energyQty = self.qty['energy'] + if energyQty == 'ultra sparse': + # 0-1, add up to one energy (etank or reserve) + self.itemManager.removeItem('Reserve') + self.itemManager.removeItem('ETank') + self.addNoEnergy() + if self.ultraSparseNoTanks: + # no energy at all + self.addNoEnergy() + else: + if random.random() < 0.5: + self.itemManager.addItem('ETank') + else: + self.itemManager.addItem('Reserve') + + # complete with nothing item + for i in range(total - alreadyInPool): + self.addNoEnergy() + + elif energyQty == 'sparse': + # 4-6 + if random.random() < 0.5: + self.itemManager.addItem('Reserve') + else: + self.itemManager.addItem('ETank') + # 3 in the pool (1 E, 1 R + the previous one) + alreadyInPool = 3 + rest = self.sparseRest + for i in range(rest): + self.itemManager.addItem('ETank') + # complete with nothing item + for i in range(total - alreadyInPool - rest): + self.addNoEnergy() + + elif energyQty == 'medium': + # 8-12 + # add up to 3 Reserves or ETanks (cannot add more than 3 reserves) + alreadyInPool = 2 + n = getE(3) + for i in range(n): + if random.random() < 0.5: + self.itemManager.addItem('Reserve') + else: + self.itemManager.addItem('ETank') + alreadyInPool += n + rest = getE(self.mediumRest) + for i in range(rest): + self.itemManager.addItem('ETank') + # fill the rest with NoEnergy + for i in range(total - alreadyInPool - rest): + self.addNoEnergy() + else: + nE = getE(13) + alreadyInPool += nE + nR = getE(3) + alreadyInPool += nR + for i in range(nR): + self.itemManager.addItem('Reserve') + for i in range(nE): + self.itemManager.addItem('ETank') + for i in range(total - alreadyInPool): + self.addNoEnergy() + + def getItemPool(self): + self.itemManager.newItemPool() + + for itemType in ['ETank', 'Reserve', 'Bomb', 'Charge', 'Ice', 'HiJump', 'SpeedBooster', 'Wave', 'Spazer', 'SpringBall', 'Varia', 'Plasma', 'Grapple', 'Morph', 'Gravity', 'XRayScope', 'SpaceJump', 'ScrewAttack']: + self.itemManager.addItem(itemType, 'Major') + for itemType in ['Missile', 'Missile', 'Super', 'Super', 'PowerBomb']: + self.itemManager.addItem(itemType, 'Minor') + + self.addEnergy() + self.addAmmo() + + return self.itemManager.getItemPool() + +class ItemPoolGeneratorScavenger(ItemPoolGeneratorMajors): + def __init__(self, itemManager, qty, sm, maxDiff): + super(ItemPoolGeneratorScavenger, self).__init__(itemManager, qty, sm, maxDiff) + + def addNoEnergy(self): + self.itemManager.addItem('Nothing') + +class ItemPoolGeneratorMinimizer(ItemPoolGeneratorMajors): + def __init__(self, itemManager, qty, sm, nLocs, maxDiff): + super(ItemPoolGeneratorMinimizer, self).__init__(itemManager, qty, sm, maxDiff) + self.maxItems = nLocs + self.calcMaxAmmo() + nMajors = len([itemName for itemName,item in ItemManager.Items.items() if item.Class == 'Major' and item.Category != 'Energy']) + energyQty = self.qty['energy'] + if energyQty == 'medium': + if nLocs < 40: + self.maxEnergy = 5 + elif nLocs < 55: + self.maxEnergy = 6 + else: + self.maxEnergy = 5 + self.mediumRest + elif energyQty == 'vanilla': + if nLocs < 40: + self.maxEnergy = 6 + elif nLocs < 55: + self.maxEnergy = 8 + else: + self.maxEnergy = 8 + int(float(nLocs - 55)/50.0 * 8) + self.log.debug("maxEnergy: "+str(self.maxEnergy)) + maxItems = self.maxItems - 10 # remove bosses and minimal minore + self.maxEnergy = int(max(self.maxEnergy, maxItems - nMajors - self.minorLocations)) + if self.maxEnergy > 18: + self.maxEnergy = 18 + elif energyQty == 'ultra sparse': + self.maxEnergy = 0 if self.ultraSparseNoTanks else 1 + elif energyQty == 'sparse': + self.maxEnergy = 3 + self.sparseRest + self.log.debug("maxEnergy: "+str(self.maxEnergy)) + +class ItemPoolGeneratorPlando(ItemPoolGenerator): + def __init__(self, itemManager, qty, sm, exclude, nLocs, maxDiff): + super(ItemPoolGeneratorPlando, self).__init__(itemManager, qty, sm, maxDiff) + # in exclude dict: + # in alreadyPlacedItems: + # dict of 'itemType: count' of items already added in the plando. + # also a 'total: count' with the total number of items already added in the plando. + # in forbiddenItems: list of item forbidden in the pool + self.exclude = exclude + self.maxItems = nLocs + self.log.debug("maxItems: {}".format(self.maxItems)) + self.log.debug("exclude: {}".format(self.exclude)) + + def getItemPool(self): + exceptionMessage = "Too many items already placed by the plando or not enough available locations:" + self.itemManager.newItemPool(addBosses=False) + + # add the already placed items by the plando + for item, count in self.exclude['alreadyPlacedItems'].items(): + if item == 'total': + continue + itemClass = 'Major' + if item in ['Missile', 'Super', 'PowerBomb', 'Kraid', 'Phantoon', 'Draygon', 'Ridley', 'MotherBrain']: + itemClass = 'Minor' + for i in range(count): + self.itemManager.addItem(item, itemClass) + + remain = self.maxItems - self.exclude['alreadyPlacedItems']['total'] + self.log.debug("Plando: remain start: {}".format(remain)) + if remain > 0: + # add missing bosses + for boss in ['Kraid', 'Phantoon', 'Draygon', 'Ridley', 'MotherBrain']: + if self.exclude['alreadyPlacedItems'][boss] == 0: + self.itemManager.addItem(boss, 'Minor') + self.exclude['alreadyPlacedItems'][boss] = 1 + remain -= 1 + + self.log.debug("Plando: remain after bosses: {}".format(remain)) + if remain < 0: + raise Exception("{} can't add the remaining bosses".format(exceptionMessage)) + + # add missing majors + majors = [] + for itemType in ['Bomb', 'Charge', 'Ice', 'HiJump', 'SpeedBooster', 'Wave', 'Spazer', 'SpringBall', 'Varia', 'Plasma', 'Grapple', 'Morph', 'Gravity', 'XRayScope', 'SpaceJump', 'ScrewAttack']: + if self.exclude['alreadyPlacedItems'][itemType] == 0 and itemType not in self.exclude['forbiddenItems']: + self.itemManager.addItem(itemType, 'Major') + self.exclude['alreadyPlacedItems'][itemType] = 1 + majors.append(itemType) + remain -= 1 + + self.log.debug("Plando: remain after majors: {}".format(remain)) + if remain < 0: + raise Exception("{} can't add the remaining majors: {}".format(exceptionMessage, ', '.join(majors))) + + # add minimum minors to finish the game + for (itemType, minimum) in [('Missile', 3), ('Super', 2), ('PowerBomb', 1)]: + while self.exclude['alreadyPlacedItems'][itemType] < minimum and itemType not in self.exclude['forbiddenItems']: + self.itemManager.addItem(itemType, 'Minor') + self.exclude['alreadyPlacedItems'][itemType] += 1 + remain -= 1 + + self.log.debug("Plando: remain after minimum minors: {}".format(remain)) + if remain < 0: + raise Exception("{} can't add the minimum minors to finish the game".format(exceptionMessage)) + + # add energy + energyQty = self.qty['energy'] + limits = { + "sparse": [('ETank', 4), ('Reserve', 1)], + "medium": [('ETank', 8), ('Reserve', 2)], + "vanilla": [('ETank', 14), ('Reserve', 4)] + } + for (itemType, minimum) in limits[energyQty]: + while self.exclude['alreadyPlacedItems'][itemType] < minimum and itemType not in self.exclude['forbiddenItems']: + self.itemManager.addItem(itemType, 'Major') + self.exclude['alreadyPlacedItems'][itemType] += 1 + remain -= 1 + + self.log.debug("Plando: remain after energy: {}".format(remain)) + if remain < 0: + raise Exception("{} can't add energy".format(exceptionMessage)) + + # add ammo + nbMinorsAlready = self.exclude['alreadyPlacedItems']['Missile'] + self.exclude['alreadyPlacedItems']['Super'] + self.exclude['alreadyPlacedItems']['PowerBomb'] + minorLocations = max(0, 0.66*self.qty['minors'] - nbMinorsAlready) + maxItems = len(self.itemManager.getItemPool()) + int(minorLocations) + ammoQty = {itemType: qty for itemType, qty in self.qty['ammo'].items() if itemType not in self.exclude['forbiddenItems']} + if ammoQty: + rangeDict = getRangeDict(ammoQty) + while len(self.itemManager.getItemPool()) < maxItems and remain > 0: + item = chooseFromRange(rangeDict) + self.itemManager.addMinor(item) + remain -= 1 + + self.log.debug("Plando: remain after ammo: {}".format(remain)) + + # add nothing + while remain > 0: + self.itemManager.addMinor('Nothing') + remain -= 1 + + self.log.debug("Plando: remain after nothing: {}".format(remain)) + + return self.itemManager.getItemPool() diff --git a/worlds/sm/variaRandomizer/rando/MiniSolver.py b/worlds/sm/variaRandomizer/rando/MiniSolver.py new file mode 100644 index 00000000..818632a0 --- /dev/null +++ b/worlds/sm/variaRandomizer/rando/MiniSolver.py @@ -0,0 +1,63 @@ + +import utils.log, random + +from logic.smboolmanager import SMBoolManager +from utils.parameters import infinity + +class MiniSolver(object): + def __init__(self, startAP, areaGraph, restrictions): + self.startAP = startAP + self.areaGraph = areaGraph + self.restrictions = restrictions + self.settings = restrictions.settings + self.smbm = SMBoolManager() + self.log = utils.log.get('MiniSolver') + + # if True, does not mean it is actually beatable, unless you're sure of it from another source of information + # if False, it is certain it is not beatable + def isBeatable(self, itemLocations, maxDiff=None): + if maxDiff is None: + maxDiff = self.settings.maxDiff + minDiff = self.settings.minDiff + locations = [] + for il in itemLocations: + loc = il.Location + if loc.restricted: + continue + loc.itemName = il.Item.Type + loc.difficulty = None + locations.append(loc) + self.smbm.resetItems() + ap = self.startAP + onlyBossesLeft = -1 + hasOneLocAboveMinDiff = False + while True: + if not locations: + return hasOneLocAboveMinDiff + # only two loops to collect all remaining locations in only bosses left mode + if onlyBossesLeft >= 0: + onlyBossesLeft += 1 + if onlyBossesLeft > 2: + return False + self.areaGraph.getAvailableLocations(locations, self.smbm, maxDiff, ap) + post = [loc for loc in locations if loc.PostAvailable and loc.difficulty.bool == True] + for loc in post: + self.smbm.addItem(loc.itemName) + postAvailable = loc.PostAvailable(self.smbm) + self.smbm.removeItem(loc.itemName) + loc.difficulty = self.smbm.wand(loc.difficulty, postAvailable) + toCollect = [loc for loc in locations if loc.difficulty.bool == True and loc.difficulty.difficulty <= maxDiff] + if not toCollect: + # mini onlyBossesLeft + if maxDiff < infinity: + maxDiff = infinity + onlyBossesLeft = 0 + continue + return False + if not hasOneLocAboveMinDiff: + hasOneLocAboveMinDiff = any(loc.difficulty.difficulty >= minDiff for loc in locations) + self.smbm.addItems([loc.itemName for loc in toCollect]) + for loc in toCollect: + locations.remove(loc) + # if len(locations) > 0: + # ap = random.choice([loc.accessPoint for loc in locations]) diff --git a/worlds/sm/variaRandomizer/rando/RandoExec.py b/worlds/sm/variaRandomizer/rando/RandoExec.py new file mode 100644 index 00000000..bf440f04 --- /dev/null +++ b/worlds/sm/variaRandomizer/rando/RandoExec.py @@ -0,0 +1,93 @@ +import sys, random, time, utils.log + +from logic.logic import Logic +from graph.graph_utils import GraphUtils, getAccessPoint +from rando.Restrictions import Restrictions +from rando.RandoServices import RandoServices +from rando.GraphBuilder import GraphBuilder +from rando.RandoSetup import RandoSetup +from rando.Items import ItemManager +from rando.ItemLocContainer import ItemLocation +from utils.vcr import VCR +from utils.doorsmanager import DoorsManager + +# entry point for rando execution ("randomize" method) +class RandoExec(object): + def __init__(self, seedName, vcr, randoSettings, graphSettings, player): + self.errorMsg = "" + self.seedName = seedName + self.vcr = vcr + self.randoSettings = randoSettings + self.graphSettings = graphSettings + self.log = utils.log.get('RandoExec') + self.player = player + + # processes settings to : + # - create Restrictions and GraphBuilder objects + # - create graph and item loc container using a RandoSetup instance: in area rando, if it fails, iterate on possible graph layouts + # return container + def randomize(self): + vcr = VCR(self.seedName, 'rando') if self.vcr == True else None + self.errorMsg = "" + split = self.randoSettings.restrictions['MajorMinor'] + graphBuilder = GraphBuilder(self.graphSettings) + container = None + i = 0 + attempts = 500 if self.graphSettings.areaRando or self.graphSettings.doorsColorsRando or split == 'Scavenger' else 1 + now = time.process_time() + endDate = sys.maxsize + if self.randoSettings.runtimeLimit_s < endDate: + endDate = now + self.randoSettings.runtimeLimit_s + self.updateLocationsClass(split) + while container is None and i < attempts and now <= endDate: + self.restrictions = Restrictions(self.randoSettings) + if self.graphSettings.doorsColorsRando == True: + DoorsManager.randomize(self.graphSettings.allowGreyDoors, self.player) + self.areaGraph = graphBuilder.createGraph() + services = RandoServices(self.areaGraph, self.restrictions) + setup = RandoSetup(self.graphSettings, Logic.locations, services, self.player) + self.setup = setup + container = setup.createItemLocContainer(endDate, vcr) + if container is None: + sys.stdout.write('*') + sys.stdout.flush() + i += 1 + else: + self.errorMsg += '\n'.join(setup.errorMsgs) + now = time.process_time() + if container is None: + if self.graphSettings.areaRando: + self.errorMsg += "Could not find an area layout with these settings" + else: + self.errorMsg += "Unable to process settings" + self.areaGraph.printGraph() + return container + + def updateLocationsClass(self, split): + if split != 'Full' and split != 'Scavenger': + startAP = getAccessPoint(self.graphSettings.startAP) + possibleMajLocs, preserveMajLocs, nMaj, nChozo = Logic.LocationsHelper.getStartMajors(startAP.Name) + if split == 'Major': + n = nMaj + elif split == 'Chozo': + n = nChozo + GraphUtils.updateLocClassesStart(startAP.GraphArea, split, possibleMajLocs, preserveMajLocs, n) + + def postProcessItemLocs(self, itemLocs, hide): + # hide some items like in dessy's + if hide == True: + for itemLoc in itemLocs: + item = itemLoc.Item + loc = itemLoc.Location + if (item.Category != "Nothing" + and loc.CanHidden == True + and loc.Visibility == 'Visible'): + if bool(random.getrandbits(1)) == True: + loc.Visibility = 'Hidden' + # put nothing in unfilled locations + filledLocNames = [il.Location.Name for il in itemLocs] + unfilledLocs = [loc for loc in Logic.locations if loc.Name not in filledLocNames] + nothing = ItemManager.getItem('Nothing') + for loc in unfilledLocs: + loc.restricted = True + itemLocs.append(ItemLocation(nothing, loc, False)) diff --git a/worlds/sm/variaRandomizer/rando/RandoServices.py b/worlds/sm/variaRandomizer/rando/RandoServices.py new file mode 100644 index 00000000..bcb076d1 --- /dev/null +++ b/worlds/sm/variaRandomizer/rando/RandoServices.py @@ -0,0 +1,391 @@ + +import utils.log, copy, random, sys, logging +from enum import Enum, unique +from utils.parameters import infinity +from rando.ItemLocContainer import getLocListStr, getItemListStr, getItemLocStr, ItemLocation +from logic.helpers import Bosses + +# used to specify whether we want to come back from locations +@unique +class ComebackCheckType(Enum): + Undefined = 0 + # do not check whether we should come back + NoCheck = 1 + # come back with the placed item + JustComeback = 2 + # come back without the placed item + ComebackWithoutItem = 3 + +# collection of stateless services to be used mainly by fillers +class RandoServices(object): + def __init__(self, graph, restrictions, cache=None): + self.restrictions = restrictions + self.settings = restrictions.settings + self.areaGraph = graph + self.cache = cache + self.log = utils.log.get('RandoServices') + + # collect an item/loc with logic in a container from a given AP + # return new AP + def collect(self, ap, container, itemLoc, pickup=True): + if pickup == True: + # walk the graph to update AP + if self.cache: + self.cache.reset() + self.currentLocations(ap, container) + container.collect(itemLoc, pickup=pickup) + self.log.debug("COLLECT "+itemLoc.Item.Type+" at "+itemLoc.Location.Name) + sys.stdout.write('.') + sys.stdout.flush() + return itemLoc.Location.accessPoint if pickup == True else ap + + # gives all the possible theoretical locations for a given item + def possibleLocations(self, item, ap, emptyContainer, bossesKilled=True): + assert len(emptyContainer.currentItems) == 0, "Invalid call to possibleLocations. emptyContainer had collected items" + emptyContainer.sm.resetItems() + self.log.debug('possibleLocations. item='+item.Type) + if bossesKilled: + itemLambda = lambda it: it.Type != item.Type + else: + itemLambda = lambda it: it.Type != item.Type and it.Category != 'Boss' + allBut = emptyContainer.getItems(itemLambda) + self.log.debug('possibleLocations. allBut='+getItemListStr(allBut)) + emptyContainer.sm.addItems([it.Type for it in allBut]) + ret = [loc for loc in self.currentLocations(ap, emptyContainer, post=True) if self.restrictions.canPlaceAtLocation(item, loc, emptyContainer)] + self.log.debug('possibleLocations='+getLocListStr(ret)) + emptyContainer.sm.resetItems() + return ret + + # gives current accessible locations within a container from an AP, given an optional item. + # post: checks post available? + # diff: max difficulty to use (None for max diff from settings) + def currentLocations(self, ap, container, item=None, post=False, diff=None): + if self.cache is not None: + request = self.cache.request('currentLocations', ap, container, None if item is None else item.Type, post, diff) + ret = self.cache.get(request) + if ret is not None: + return ret + sm = container.sm + if diff is None: + diff = self.settings.maxDiff + itemType = None + if item is not None: + itemType = item.Type + sm.addItem(itemType) + ret = sorted(self.getAvailLocs(container, ap, diff), + key=lambda loc: loc.Name) + if post is True: + ret = [loc for loc in ret if self.locPostAvailable(sm, loc, itemType)] + if item is not None: + sm.removeItem(itemType) + if self.cache is not None: + self.cache.store(request, ret) + return ret + + def locPostAvailable(self, sm, loc, item): + if loc.PostAvailable is None: + return True + result = sm.withItem(item, loc.PostAvailable) if item is not None else loc.PostAvailable(sm) + return result.bool == True and result.difficulty <= self.settings.maxDiff + + def getAvailLocs(self, container, ap, diff): + sm = container.sm + locs = container.unusedLocations + return self.areaGraph.getAvailableLocations(locs, sm, diff, ap) + + # gives current accessible APs within a container from an AP, given an optional item. + def currentAccessPoints(self, ap, container, item=None): + if self.cache is not None: + request = self.cache.request('currentAccessPoints', ap, container, None if item is None else item.Type) + ret = self.cache.get(request) + if ret is not None: + return ret + sm = container.sm + if item is not None: + itemType = item.Type + sm.addItem(itemType) + nodes = sorted(self.areaGraph.getAvailableAccessPoints(self.areaGraph.accessPoints[ap], + sm, self.settings.maxDiff), + key=lambda ap: ap.Name) + if item is not None: + sm.removeItem(itemType) + if self.cache is not None: + self.cache.store(request, nodes) + + return nodes + + def isSoftlockPossible(self, container, ap, item, loc, comebackCheck): + sm = container.sm + # usually early game + if comebackCheck == ComebackCheckType.NoCheck: + return False + # some specific early/late game checks + if loc.Name == 'Bomb' or loc.Name == 'Mother Brain': + return False + # if the loc forces us to go to an area we can't come back from + comeBack = loc.accessPoint == ap or \ + self.areaGraph.canAccess(sm, loc.accessPoint, ap, self.settings.maxDiff, item.Type if item is not None else None) + if not comeBack: + self.log.debug("KO come back from " + loc.accessPoint + " to " + ap + " when trying to place " + ("None" if item is None else item.Type) + " at " + loc.Name) + return True +# else: +# self.log.debug("OK come back from " + loc.accessPoint + " to " + ap + " when trying to place " + item.Type + " at " + loc.Name) + if item is not None and comebackCheck == ComebackCheckType.ComebackWithoutItem and self.isProgression(item, ap, container): + # we know that loc is avail and post avail with the item + # if it is not post avail without it, then the item prevents the + # possible softlock + if not self.locPostAvailable(sm, loc, None): + return True + # item allows us to come back from a softlock possible zone + comeBackWithout = self.areaGraph.canAccess(sm, loc.accessPoint, + ap, + self.settings.maxDiff, + None) + if not comeBackWithout: + return True + + return False + + def fullComebackCheck(self, container, ap, item, loc, comebackCheck): + sm = container.sm + tmpItems = [] + # draygon special case: there are two locations, and we can + # place one item, but we might need both the item and the boss + # dead to get out + if loc.SolveArea == "Draygon Boss" and Bosses.bossDead(sm, 'Draygon').bool == False: + # temporary kill draygon + tmpItems.append('Draygon') + sm.addItems(tmpItems) + ret = self.locPostAvailable(sm, loc, item.Type if item is not None else None) and not self.isSoftlockPossible(container, ap, item, loc, comebackCheck) + for tmp in tmpItems: + sm.removeItem(tmp) + return ret + + def isProgression(self, item, ap, container): + sm = container.sm + # no need to test nothing items + if item.Category == 'Nothing': + return False + if self.cache is not None: + request = self.cache.request('isProgression', item.Type, ap, container) + ret = self.cache.get(request) + if ret is not None: + return ret + oldLocations = self.currentLocations(ap, container) + ret = any(self.restrictions.canPlaceAtLocation(item, loc, container) for loc in oldLocations) + if ret == True: + newLocations = [loc for loc in self.currentLocations(ap, container, item) if loc not in oldLocations] + ret = len(newLocations) > 0 and any(self.restrictions.isItemLocMatching(item, loc) for loc in newLocations) + self.log.debug('isProgression. item=' + item.Type + ', newLocs=' + str([loc.Name for loc in newLocations])) + if ret == False and len(newLocations) > 0 and self.restrictions.split == 'Major': + # in major/minor split, still consider minor locs as + # progression if not all types are distributed + ret = not sm.haveItem('Missile').bool \ + or not sm.haveItem('Super').bool \ + or not sm.haveItem('PowerBomb').bool + if self.cache is not None: + self.cache.store(request, ret) + return ret + + def getPlacementLocs(self, ap, container, comebackCheck, itemObj, locs): + return [loc for loc in locs if (itemObj is None or self.restrictions.canPlaceAtLocation(itemObj, loc, container)) and self.fullComebackCheck(container, ap, itemObj, loc, comebackCheck)] + + def processEarlyMorph(self, ap, container, comebackCheck, itemLocDict, curLocs): + morph = container.getNextItemInPool('Morph') + if morph is not None: + self.log.debug("processEarlyMorph. morph not placed yet") + morphLocItem = next((item for item in itemLocDict if item.Type == morph.Type), None) + if morphLocItem is not None: + morphLocs = itemLocDict[morphLocItem] + itemLocDict.clear() + itemLocDict[morphLocItem] = morphLocs + elif len(curLocs) >= 2: + self.log.debug("processEarlyMorph. early morph placement check") + # we have to place morph early, it's still not placed, and not detected as placeable + # let's see if we can place it anyway in the context of a combo + morphLocs = self.getPlacementLocs(ap, container, comebackCheck, morph, curLocs) + if len(morphLocs) > 0: + # copy our context to do some destructive checks + containerCpy = copy.copy(container) + # choose a morph item location in that context + morphItemLoc = ItemLocation( + morph, + random.choice(morphLocs) + ) + # acquire morph in new context and see if we can still open new locs + newAP = self.collect(ap, containerCpy, morphItemLoc) + (ild, poss) = self.getPossiblePlacements(newAP, containerCpy, comebackCheck) + if poss: + # it's possible, only offer morph as possibility + itemLocDict.clear() + itemLocDict[morph] = morphLocs + + def processLateMorph(self, container, itemLocDict): + morphLocItem = next((item for item in itemLocDict if item.Type == 'Morph'), None) + if morphLocItem is None or len(itemLocDict) == 1: + # no morph, or it is the only possibility: nothing to do + return + morphLocs = self.restrictions.lateMorphCheck(container, itemLocDict[morphLocItem]) + if morphLocs is not None: + itemLocDict[morphLocItem] = morphLocs + else: + del itemLocDict[morphLocItem] + + def processNoComeback(self, ap, container, itemLocDict): + comebackDict = {} + for item,locList in itemLocDict.items(): + comebackLocs = [loc for loc in locList if self.fullComebackCheck(container, ap, item, loc, ComebackCheckType.JustComeback)] + if len(comebackLocs) > 0: + comebackDict[item] = comebackLocs + if len(comebackDict) > 0: + itemLocDict.clear() + itemLocDict.update(comebackDict) + + def processPlacementRestrictions(self, ap, container, comebackCheck, itemLocDict, curLocs): + if self.restrictions.isEarlyMorph(): + self.processEarlyMorph(ap, container, comebackCheck, itemLocDict, curLocs) + elif self.restrictions.isLateMorph(): + self.processLateMorph(container, itemLocDict) + if comebackCheck == ComebackCheckType.NoCheck: + self.processNoComeback(ap, container, itemLocDict) + + # main logic function to be used by fillers. gives possible locations for each item. + # ap: AP to check from + # container: our item/loc container + # comebackCheck: how to check for comebacks (cf ComebackCheckType) + # return a dictionary with Item instances as keys and locations lists as values + def getPossiblePlacements(self, ap, container, comebackCheck): + curLocs = self.currentLocations(ap, container) + self.log.debug('getPossiblePlacements. nCurLocs='+str(len(curLocs))) + self.log.debug('getPossiblePlacements. curLocs='+getLocListStr(curLocs)) + self.log.debug('getPossiblePlacements. comebackCheck='+str(comebackCheck)) + sm = container.sm + poolDict = container.getPoolDict() + itemLocDict = {} + possibleProg = False + nonProgList = None + def getLocList(itemObj): + nonlocal curLocs + return self.getPlacementLocs(ap, container, comebackCheck, itemObj, curLocs) + def getNonProgLocList(): + nonlocal nonProgList + if nonProgList is None: + nonProgList = [loc for loc in self.currentLocations(ap, container) if self.fullComebackCheck(container, ap, None, loc, comebackCheck)] + self.log.debug("nonProgLocList="+str([loc.Name for loc in nonProgList])) + return [loc for loc in nonProgList if self.restrictions.canPlaceAtLocation(itemObj, loc, container)] + for itemType,items in sorted(poolDict.items()): + itemObj = items[0] + cont = True + prog = False + if self.isProgression(itemObj, ap, container): + cont = False + prog = True + elif not possibleProg: + cont = False + if cont: # ignore non prog items if a prog item has already been found + continue + # check possible locations for this item type +# self.log.debug('getPossiblePlacements. itemType=' + itemType + ', curLocs='+str([loc.Name for loc in curLocs])) + locations = getLocList(itemObj) if prog else getNonProgLocList() + if len(locations) == 0: + continue + if prog and not possibleProg: + possibleProg = True + itemLocDict = {} # forget all the crap ones we stored just in case +# self.log.debug('getPossiblePlacements. itemType=' + itemType + ', locs='+str([loc.Name for loc in locations])) + for item in items: + itemLocDict[item] = locations + self.processPlacementRestrictions(ap, container, comebackCheck, itemLocDict, curLocs) + self.printItemLocDict(itemLocDict) + self.log.debug('possibleProg='+str(possibleProg)) + return (itemLocDict, possibleProg) + + def printItemLocDict(self, itemLocDict): + if self.log.getEffectiveLevel() == logging.DEBUG: + debugDict = {} + for item, locList in itemLocDict.items(): + if item.Type not in debugDict: + debugDict[item.Type] = [loc.Name for loc in locList] + self.log.debug('itemLocDict='+str(debugDict)) + + # same as getPossiblePlacements, without any logic check + def getPossiblePlacementsNoLogic(self, container): + poolDict = container.getPoolDict() + itemLocDict = {} + def getLocList(itemObj, baseList): + return [loc for loc in baseList if self.restrictions.canPlaceAtLocation(itemObj, loc, container)] + for itemType,items in sorted(poolDict.items()): + itemObj = items[0] + locList = getLocList(itemObj, container.unusedLocations) + for item in items: + itemLocDict[item] = locList + self.printItemLocDict(itemLocDict) + return (itemLocDict, False) + + # check if bosses are blocking the last remaining locations. + # accurate most of the time, still a heuristic + def onlyBossesLeft(self, ap, container): + if self.settings.maxDiff == infinity: + return False + self.log.debug('onlyBossesLeft, diff=' + str(self.settings.maxDiff) + ", ap="+ap) + sm = container.sm + bossesLeft = container.getAllItemsInPoolFromCategory('Boss') + if len(bossesLeft) == 0: + return False + def getLocList(): + curLocs = self.currentLocations(ap, container) + self.log.debug('onlyBossesLeft, curLocs=' + getLocListStr(curLocs)) + return self.getPlacementLocs(ap, container, ComebackCheckType.JustComeback, None, curLocs) + prevLocs = getLocList() + self.log.debug("onlyBossesLeft. prevLocs="+getLocListStr(prevLocs)) + # fake kill remaining bosses and see if we can access the rest of the game + if self.cache is not None: + self.cache.reset() + for boss in bossesLeft: + self.log.debug('onlyBossesLeft. kill '+boss.Name) + sm.addItem(boss.Type) + # get bosses locations and newly accessible locations (for bosses that open up locs) + newLocs = getLocList() + self.log.debug("onlyBossesLeft. newLocs="+getLocListStr(newLocs)) + locs = newLocs + container.getLocs(lambda loc: loc.isBoss() and not loc in newLocs) + self.log.debug("onlyBossesLeft. locs="+getLocListStr(locs)) + ret = (len(locs) > len(prevLocs) and len(locs) == len(container.unusedLocations)) + # restore bosses killed state + for boss in bossesLeft: + self.log.debug('onlyBossesLeft. revive '+boss.Name) + sm.removeItem(boss.Type) + if self.cache is not None: + self.cache.reset() + self.log.debug("onlyBossesLeft? " +str(ret)) + return ret + + def canEndGame(self, container): + return not any(loc.Name == 'Mother Brain' for loc in container.unusedLocations) + + def can100percent(self, ap, container): + if not self.canEndGame(container): + return False + curLocs = self.currentLocations(ap, container, post=True) + return len(curLocs) == len(container.unusedLocations) + + def findStartupProgItemPair(self, ap, container): + self.log.debug("findStartupProgItemPair") + (itemLocDict, isProg) = self.getPossiblePlacements(ap, container, ComebackCheckType.NoCheck) + assert not isProg + items = list(itemLocDict.keys()) + random.shuffle(items) + for item in items: + cont = copy.copy(container) + loc = random.choice(itemLocDict[item]) + itemLoc1 = ItemLocation(item, loc) + self.log.debug("itemLoc1 attempt: "+getItemLocStr(itemLoc1)) + newAP = self.collect(ap, cont, itemLoc1) + if self.cache is not None: + self.cache.reset() + (ild, isProg) = self.getPossiblePlacements(newAP, cont, ComebackCheckType.NoCheck) + if isProg: + item2 = random.choice(list(ild.keys())) + itemLoc2 = ItemLocation(item2, random.choice(ild[item2])) + self.log.debug("itemLoc2: "+getItemLocStr(itemLoc2)) + return (itemLoc1, itemLoc2) + return None diff --git a/worlds/sm/variaRandomizer/rando/RandoSettings.py b/worlds/sm/variaRandomizer/rando/RandoSettings.py new file mode 100644 index 00000000..7699a5b6 --- /dev/null +++ b/worlds/sm/variaRandomizer/rando/RandoSettings.py @@ -0,0 +1,270 @@ + +import sys, random +from collections import defaultdict +from rando.Items import ItemManager +from utils.utils import getRangeDict, chooseFromRange +from rando.ItemLocContainer import ItemLocation + +# Holder for settings and a few utility functions related to them +# (especially for plando/rando). +# Holds settings not related to graph layout. +class RandoSettings(object): + def __init__(self, maxDiff, progSpeed, progDiff, qty, restrictions, + superFun, runtimeLimit_s, plandoSettings, minDiff): + self.progSpeed = progSpeed.lower() + self.progDiff = progDiff.lower() + self.maxDiff = maxDiff + self.qty = qty + self.restrictions = restrictions + self.superFun = superFun + self.runtimeLimit_s = runtimeLimit_s + if self.runtimeLimit_s <= 0: + self.runtimeLimit_s = sys.maxsize + self.plandoSettings = plandoSettings + self.minDiff = minDiff + + def getSuperFun(self): + return self.superFun[:] + + def updateSuperFun(self, superFun): + self.superFun = superFun[:] + + def isPlandoRando(self): + return self.plandoSettings is not None + + def getItemManager(self, smbm, nLocs): + if not self.isPlandoRando(): + return ItemManager(self.restrictions['MajorMinor'], self.qty, smbm, nLocs, self.maxDiff) + else: + return ItemManager('Plando', self.qty, smbm, nLocs, self.maxDiff) + + def getExcludeItems(self, locations): + if not self.isPlandoRando(): + return None + exclude = {'alreadyPlacedItems': defaultdict(int), 'forbiddenItems': []} + # locsItems is a dict {'loc name': 'item type'} + for locName,itemType in self.plandoSettings["locsItems"].items(): + if not any(loc.Name == locName for loc in locations): + continue + exclude['alreadyPlacedItems'][itemType] += 1 + exclude['alreadyPlacedItems']['total'] += 1 + + exclude['forbiddenItems'] = self.plandoSettings['forbiddenItems'] + + return exclude + + def collectAlreadyPlacedItemLocations(self, container): + if not self.isPlandoRando(): + return + for locName,itemType in self.plandoSettings["locsItems"].items(): + if not any(loc.Name == locName for loc in container.unusedLocations): + continue + item = container.getNextItemInPool(itemType) + assert item is not None, "Invalid plando item pool" + location = container.getLocs(lambda loc: loc.Name == locName)[0] + itemLoc = ItemLocation(item, location) + container.collect(itemLoc, pickup=False) + +# Holds settings and utiliy functions related to graph layout +class GraphSettings(object): + def __init__(self, startAP, areaRando, lightAreaRando, bossRando, escapeRando, minimizerN, dotFile, doorsColorsRando, allowGreyDoors, plandoRandoTransitions): + self.startAP = startAP + self.areaRando = areaRando + self.lightAreaRando = lightAreaRando + self.bossRando = bossRando + self.escapeRando = escapeRando + self.minimizerN = minimizerN + self.dotFile = dotFile + self.doorsColorsRando = doorsColorsRando + self.allowGreyDoors = allowGreyDoors + self.plandoRandoTransitions = plandoRandoTransitions + + def isMinimizer(self): + return self.minimizerN is not None + +# algo settings depending on prog speed (slowest to fastest+variable, +# other "speeds" are actually different algorithms) +class ProgSpeedParameters(object): + def __init__(self, restrictions, nLocs): + self.restrictions = restrictions + self.nLocs = nLocs + + def getVariableSpeed(self): + ranges = getRangeDict({ + 'slowest':7, + 'slow':20, + 'medium':35, + 'fast':27, + 'fastest':11 + }) + return chooseFromRange(ranges) + + def getMinorHelpProb(self, progSpeed): + if self.restrictions.split != 'Major': + return 0 + if progSpeed == 'slowest': + return 0.16 + elif progSpeed == 'slow': + return 0.33 + elif progSpeed == 'medium': + return 0.5 + return 1 + + def getLateDoorsProb(self, progSpeed): + if progSpeed == 'slowest': + return 1 + elif progSpeed == 'slow': + return 0.8 + elif progSpeed == 'medium': + return 0.66 + elif progSpeed == 'fast': + return 0.5 + elif progSpeed == 'fastest': + return 0.33 + return 0 + + def getItemLimit(self, progSpeed): + itemLimit = self.nLocs + if progSpeed == 'slow': + itemLimit = int(self.nLocs*0.209) # 21 for 105 + elif progSpeed == 'medium': + itemLimit = int(self.nLocs*0.095) # 9 for 105 + elif progSpeed == 'fast': + itemLimit = int(self.nLocs*0.057) # 5 for 105 + elif progSpeed == 'fastest': + itemLimit = int(self.nLocs*0.019) # 1 for 105 + minLimit = itemLimit - int(itemLimit/5) + maxLimit = itemLimit + int(itemLimit/5) + if minLimit == maxLimit: + itemLimit = minLimit + else: + itemLimit = random.randint(minLimit, maxLimit) + return itemLimit + + def getLocLimit(self, progSpeed): + locLimit = -1 + if progSpeed == 'slow': + locLimit = 1 + elif progSpeed == 'medium': + locLimit = 2 + elif progSpeed == 'fast': + locLimit = 3 + elif progSpeed == 'fastest': + locLimit = 4 + return locLimit + + def getProgressionItemTypes(self, progSpeed): + progTypes = ItemManager.getProgTypes() + if self.restrictions.isLateDoors(): + progTypes += ['Wave','Spazer','Plasma'] + progTypes.append('Charge') + if progSpeed == 'slowest': + return progTypes + else: + progTypes.remove('HiJump') + progTypes.remove('Charge') + if progSpeed == 'slow': + return progTypes + else: + progTypes.remove('Bomb') + progTypes.remove('Grapple') + if progSpeed == 'medium': + return progTypes + else: + if not self.restrictions.isLateDoors(): + progTypes.remove('Ice') + progTypes.remove('SpaceJump') + if progSpeed == 'fast': + return progTypes + else: + progTypes.remove('SpeedBooster') + if progSpeed == 'fastest': + return progTypes # only morph, varia, gravity + raise RuntimeError("Unknown prog speed " + progSpeed) + + def getPossibleSoftlockProb(self, progSpeed): + if progSpeed == 'slowest': + return 1 + if progSpeed == 'slow': + return 0.66 + if progSpeed == 'medium': + return 0.33 + if progSpeed == 'fast': + return 0.1 + if progSpeed == 'fastest': + return 0 + raise RuntimeError("Unknown prog speed " + progSpeed) + + def getChooseLocDict(self, progDiff): + if progDiff == 'normal': + return { + 'Random' : 1, + 'MinDiff' : 0, + 'MaxDiff' : 0 + } + elif progDiff == 'easier': + return { + 'Random' : 2, + 'MinDiff' : 1, + 'MaxDiff' : 0 + } + elif progDiff == 'harder': + return { + 'Random' : 2, + 'MinDiff' : 0, + 'MaxDiff' : 1 + } + + def getChooseItemDict(self, progSpeed): + if progSpeed == 'slowest': + return { + 'MinProgression' : 1, + 'Random' : 2, + 'MaxProgression' : 0 + } + elif progSpeed == 'slow': + return { + 'MinProgression' : 25, + 'Random' : 75, + 'MaxProgression' : 0 + } + elif progSpeed == 'medium': + return { + 'MinProgression' : 0, + 'Random' : 1, + 'MaxProgression' : 0 + } + elif progSpeed == 'fast': + return { + 'MinProgression' : 0, + 'Random' : 85, + 'MaxProgression' : 15 + } + elif progSpeed == 'fastest': + return { + 'MinProgression' : 0, + 'Random' : 2, + 'MaxProgression' : 1 + } + + def getSpreadFactor(self, progSpeed): + if progSpeed == 'slowest': + return 0.9 + elif progSpeed == 'slow': + return 0.7 + elif progSpeed == 'medium': + return 0.4 + elif progSpeed == 'fast': + return 0.1 + return 0 + + def getChozoSecondPhaseRestrictionProb(self, progSpeed): + if progSpeed == 'slowest': + return 0 + if progSpeed == 'slow': + return 0.16 + if progSpeed == 'medium': + return 0.5 + if progSpeed == 'fast': + return 0.9 + return 1 diff --git a/worlds/sm/variaRandomizer/rando/RandoSetup.py b/worlds/sm/variaRandomizer/rando/RandoSetup.py new file mode 100644 index 00000000..0a73ad1d --- /dev/null +++ b/worlds/sm/variaRandomizer/rando/RandoSetup.py @@ -0,0 +1,390 @@ +import copy, utils.log, random + +from utils.utils import randGaussBounds +from logic.smbool import SMBool, smboolFalse +from logic.smboolmanager import SMBoolManager +from logic.helpers import Bosses +from graph.graph_utils import getAccessPoint, GraphUtils +from rando.Filler import FrontFiller +from rando.ItemLocContainer import ItemLocContainer, getLocListStr, ItemLocation, getItemListStr +from rando.Restrictions import Restrictions +from utils.parameters import infinity + +# checks init conditions for the randomizer: processes super fun settings, graph, start location, special restrictions +# the entry point is createItemLocContainer +class RandoSetup(object): + def __init__(self, graphSettings, locations, services, player): + self.sm = SMBoolManager(player, services.settings.maxDiff) + self.settings = services.settings + self.graphSettings = graphSettings + self.startAP = graphSettings.startAP + self.superFun = self.settings.getSuperFun() + self.container = None + self.services = services + self.restrictions = services.restrictions + self.areaGraph = services.areaGraph + self.allLocations = locations + self.locations = self.areaGraph.getAccessibleLocations(locations, self.startAP) +# print("nLocs Setup: "+str(len(self.locations))) + self.itemManager = self.settings.getItemManager(self.sm, len(self.locations)) + self.forbiddenItems = [] + self.restrictedLocs = [] + self.lastRestricted = [] + self.bossesLocs = sorted(['Draygon', 'Kraid', 'Ridley', 'Phantoon', 'Mother Brain']) + self.suits = ['Varia', 'Gravity'] + # organized by priority + self.movementItems = ['SpaceJump', 'HiJump', 'SpeedBooster', 'Bomb', 'Grapple', 'SpringBall'] + # organized by priority + self.combatItems = ['ScrewAttack', 'Plasma', 'Wave', 'Spazer'] + # OMG + self.bossChecks = { + 'Kraid' : self.sm.enoughStuffsKraid, + 'Phantoon' : self.sm.enoughStuffsPhantoon, + 'Draygon' : self.sm.enoughStuffsDraygon, + 'Ridley' : self.sm.enoughStuffsRidley, + 'Mother Brain': self.sm.enoughStuffsMotherbrain + } + self.okay = lambda: SMBool(True, 0) + exclude = self.settings.getExcludeItems(self.locations) + # we have to use item manager only once, otherwise pool will change + self.itemManager.createItemPool(exclude) + self.basePool = self.itemManager.getItemPool()[:] + self.log = utils.log.get('RandoSetup') + if len(locations) != len(self.locations): + self.log.debug("inaccessible locations :"+getLocListStr([loc for loc in locations if loc not in self.locations])) + + # processes everything and returns an ItemLocContainer, or None if failed (invalid init conditions/settings) + def createItemLocContainer(self, endDate, vcr=None): + self.getForbidden() + self.log.debug("LAST CHECKPOOL") + if not self.checkPool(): + self.log.debug("createItemLocContainer: last checkPool fail") + return None + # reset restricted in locs from previous attempt + for loc in self.locations: + loc.restricted = False + for loc in self.restrictedLocs: + self.log.debug("createItemLocContainer: loc is restricted: {}".format(loc.Name)) + loc.restricted = True + self.checkDoorBeams() + self.container = ItemLocContainer(self.sm, self.getItemPool(), self.locations) + if self.restrictions.isLateMorph(): + self.restrictions.lateMorphInit(self.startAP, self.container, self.services) + isStdStart = GraphUtils.isStandardStart(self.startAP) + # ensure we have an area layout that can put morph outside start area + # TODO::allow for custom start which doesn't require morph early + if self.graphSettings.areaRando and isStdStart and not self.restrictions.suitsRestrictions and self.restrictions.lateMorphForbiddenArea is None: + self.container = None + self.log.debug("createItemLocContainer: checkLateMorph fail") + return None + # checkStart needs the container + if not self.checkStart(): + self.container = None + self.log.debug("createItemLocContainer: checkStart fail") + return None + self.settings.updateSuperFun(self.superFun) + return self.container + + def getRestrictionsDict(self): + itemTypes = {item.Type for item in self.container.itemPool if item.Category not in Restrictions.NoCheckCat} + allAreas = {loc.GraphArea for loc in self.locations} + items = [self.container.getNextItemInPool(itemType) for itemType in itemTypes] + restrictionDict = {} + for area in allAreas: + restrictionDict[area] = {} + for itemType in itemTypes: + restrictionDict[area][itemType] = set() + for item in items: + itemType = item.Type + poss = self.services.possibleLocations(item, self.startAP, self.container) + for loc in poss: + restrictionDict[loc.GraphArea][itemType].add(loc.Name) + if self.restrictions.isEarlyMorph() and GraphUtils.isStandardStart(self.startAP): + morphLocs = ['Morphing Ball'] + if self.restrictions.split in ['Full', 'Major']: + dboost = self.sm.knowsCeilingDBoost() + if dboost.bool == True and dboost.difficulty <= self.settings.maxDiff: + morphLocs.append('Energy Tank, Brinstar Ceiling') + for area, locDict in restrictionDict.items(): + if area == 'Crateria': + locDict['Morph'] = set(morphLocs) + else: + locDict['Morph'] = set() + return restrictionDict + + # fill up unreachable locations with "junk" to maximize the chance of the ROM + # to be finishable + def fillRestrictedLocations(self): + def getPred(itemType, loc): + return lambda item: (itemType is None or item.Type == itemType) and self.restrictions.canPlaceAtLocation(item, loc, self.container) + locs = self.restrictedLocs + self.log.debug("fillRestrictedLocations. locs="+getLocListStr(locs)) + for loc in locs: + itemLocation = ItemLocation(None, loc) + if self.container.hasItemInPool(getPred('Nothing', loc)): + itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('Nothing', loc)) + elif self.container.hasItemInPool(getPred('NoEnergy', loc)): + itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('NoEnergy', loc)) + elif self.container.countItems(getPred('Missile', loc)) > 3: + itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('Missile', loc)) + elif self.container.countItems(getPred('Super', loc)) > 2: + itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('Super', loc)) + elif self.container.countItems(getPred('PowerBomb', loc)) > 1: + itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('PowerBomb', loc)) + elif self.container.countItems(getPred('Reserve', loc)) > 1: + itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('Reserve', loc)) + elif self.container.countItems(getPred('ETank', loc)) > 3: + itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('ETank', loc)) + else: + raise RuntimeError("Cannot fill restricted locations") + self.log.debug("Fill: {}/{} at {}".format(itemLocation.Item.Type, itemLocation.Item.Class, itemLocation.Location.Name)) + self.container.collect(itemLocation, False) + + def getItemPool(self, forbidden=[]): + self.itemManager.setItemPool(self.basePool[:]) # reuse base pool to have constant base item set + return self.itemManager.removeForbiddenItems(self.forbiddenItems + forbidden) + + # if needed, do a simplified "pre-randomization" of a few items to check start AP/area layout validity + def checkStart(self): + ap = getAccessPoint(self.startAP) + if not self.graphSettings.areaRando or ap.Start is None or \ + (('needsPreRando' not in ap.Start or not ap.Start['needsPreRando']) and\ + ('areaMode' not in ap.Start or not ap.Start['areaMode'])): + return True + self.log.debug("********* PRE RANDO START") + container = copy.copy(self.container) + filler = FrontFiller(self.startAP, self.areaGraph, self.restrictions, container) + condition = filler.createStepCountCondition(4) + (isStuck, itemLocations, progItems) = filler.generateItems(condition) + self.log.debug("********* PRE RANDO END") + return not isStuck and len(self.services.currentLocations(filler.ap, filler.container)) > 0 + + # in door color rando, determine mandatory beams + def checkDoorBeams(self): + if self.restrictions.isLateDoors(): + doorBeams = ['Wave','Ice','Spazer','Plasma'] + self.restrictions.mandatoryBeams = [beam for beam in doorBeams if not self.checkPool(forbidden=[beam])] + self.log.debug("checkDoorBeams. mandatoryBeams="+str(self.restrictions.mandatoryBeams)) + + def checkPool(self, forbidden=None): + self.log.debug("checkPool. forbidden=" + str(forbidden) + ", self.forbiddenItems=" + str(self.forbiddenItems)) + if not self.graphSettings.isMinimizer() and not self.settings.isPlandoRando() and len(self.allLocations) > len(self.locations): + # invalid graph with looped areas + self.log.debug("checkPool: not all areas are connected, but minimizer param is off / not a plando rando") + return False + ret = True + if forbidden is not None: + pool = self.getItemPool(forbidden) + else: + pool = self.getItemPool() + # get restricted locs + totalAvailLocs = [] + comeBack = {} + try: + container = ItemLocContainer(self.sm, pool, self.locations) + except AssertionError as e: + # invalid graph altogether + self.log.debug("checkPool: AssertionError when creating ItemLocContainer: {}".format(e)) + return False + # restrict item pool in chozo: game should be finishable with chozo items only + contPool = [] + contPool += [item for item in pool if item in container.itemPool] + # give us everything and beat every boss to see what we can access + self.disableBossChecks() + self.sm.resetItems() + self.sm.addItems([item.Type for item in contPool]) # will add bosses as well + self.log.debug('pool={}'.format(getItemListStr(container.itemPool))) + locs = self.services.currentLocations(self.startAP, container, post=True) + self.areaGraph.useCache(True) + for loc in locs: + ap = loc.accessPoint + if ap not in comeBack: + # we chose Golden Four because it is always there. + # Start APs might not have comeback transitions + # possible start AP issues are handled in checkStart + comeBack[ap] = self.areaGraph.canAccess(self.sm, ap, 'Golden Four', self.settings.maxDiff) + if comeBack[ap]: + totalAvailLocs.append(loc) + self.areaGraph.useCache(False) + self.lastRestricted = [loc for loc in self.locations if loc not in totalAvailLocs] + self.log.debug("restricted=" + str([loc.Name for loc in self.lastRestricted])) + + # check if all inter-area APs can reach each other + interAPs = [ap for ap in self.areaGraph.getAccessibleAccessPoints(self.startAP) if not ap.isInternal() and not ap.isLoop()] + for startAp in interAPs: + availAccessPoints = self.areaGraph.getAvailableAccessPoints(startAp, self.sm, self.settings.maxDiff) + for ap in interAPs: + if not ap in availAccessPoints: + self.log.debug("checkPool: ap {} non accessible from {}".format(ap.Name, startAp.Name)) + ret = False + if not ret: + self.log.debug("checkPool. inter-area APs check failed") + # cleanup + self.sm.resetItems() + self.restoreBossChecks() + # check if we can reach/beat all bosses + if ret: + for loc in self.lastRestricted: + if loc.Name in self.bossesLocs: + ret = False + self.log.debug("unavail Boss: " + loc.Name) + if ret: + # revive bosses + self.sm.addItems([item.Type for item in contPool if item.Category != 'Boss']) + maxDiff = self.settings.maxDiff + # see if phantoon doesn't block himself, and if we can reach draygon if she's alive + ret = self.areaGraph.canAccess(self.sm, self.startAP, 'PhantoonRoomIn', maxDiff)\ + and self.areaGraph.canAccess(self.sm, self.startAP, 'DraygonRoomIn', maxDiff) + if ret: + # see if we can beat bosses with this equipment (infinity as max diff for a "onlyBossesLeft" type check + beatableBosses = sorted([loc.Name for loc in self.services.currentLocations(self.startAP, container, diff=infinity) if loc.isBoss()]) + self.log.debug("checkPool. beatableBosses="+str(beatableBosses)) + ret = beatableBosses == Bosses.Golden4() + if ret: + # check that we can then kill mother brain + self.sm.addItems(Bosses.Golden4()) + beatableMotherBrain = [loc.Name for loc in self.services.currentLocations(self.startAP, container, diff=infinity) if loc.Name == 'Mother Brain'] + ret = len(beatableMotherBrain) > 0 + self.log.debug("checkPool. beatable Mother Brain={}".format(ret)) + else: + self.log.debug('checkPool. locked by Phantoon or Draygon') + self.log.debug('checkPool. boss access sanity check: '+str(ret)) + + if self.restrictions.isChozo() or self.restrictions.isScavenger(): + # in chozo or scavenger, we cannot put other items than NoEnergy in the restricted locations, + # we would be forced to put majors in there, which can make seed generation fail: + # don't put more restricted major locations than removed major items + # FIXME something to do there for chozo/ultra sparse, it gives us up to 3 more spots for nothing items + restrictedLocs = self.restrictedLocs + [loc for loc in self.lastRestricted if loc not in self.restrictedLocs] + nRestrictedMajor = sum(1 for loc in restrictedLocs if self.restrictions.isLocMajor(loc)) + nNothingMajor = sum(1 for item in pool if self.restrictions.isItemMajor(item) and item.Category == 'Nothing') + ret &= nRestrictedMajor <= nNothingMajor + self.log.debug('checkPool. nRestrictedMajor='+str(nRestrictedMajor)+', nNothingMajor='+str(nNothingMajor)) + self.log.debug('checkPool. result: '+str(ret)) + return ret + + def disableBossChecks(self): + self.sm.enoughStuffsKraid = self.okay + self.sm.enoughStuffsPhantoon = self.okay + self.sm.enoughStuffsDraygon = self.okay + self.sm.enoughStuffsRidley = self.okay + def mbCheck(): + (possible, energyDiff) = self.sm.mbEtankCheck() + if possible == True: + return self.okay() + return smboolFalse + self.sm.enoughStuffsMotherbrain = mbCheck + + def restoreBossChecks(self): + self.sm.enoughStuffsKraid = self.bossChecks['Kraid'] + self.sm.enoughStuffsPhantoon = self.bossChecks['Phantoon'] + self.sm.enoughStuffsDraygon = self.bossChecks['Draygon'] + self.sm.enoughStuffsRidley = self.bossChecks['Ridley'] + self.sm.enoughStuffsMotherbrain = self.bossChecks['Mother Brain'] + + def addRestricted(self): + self.checkPool() + for r in self.lastRestricted: + if r not in self.restrictedLocs: + self.restrictedLocs.append(r) + + def getForbiddenItemsFromList(self, itemList): + self.log.debug('getForbiddenItemsFromList: ' + str(itemList)) + remove = [] + n = randGaussBounds(len(itemList)) + for i in range(n): + idx = random.randint(0, len(itemList) - 1) + item = itemList.pop(idx) + if item is not None: + remove.append(item) + return remove + + def addForbidden(self, removable): + forb = None + # it can take several tries if some item combination removal + # forbids access to more stuff than each individually + tries = 0 + while forb is None and tries < 100: + forb = self.getForbiddenItemsFromList(removable[:]) + self.log.debug("addForbidden. forb="+str(forb)) + if self.checkPool(forb) == False: + forb = None + tries += 1 + if forb is None: + # we couldn't find a combination, just pick an item + firstItem = next((itemType for itemType in removable if itemType is not None), None) + if firstItem is not None: + forb = [firstItem] + else: + forb = [] + self.forbiddenItems += forb + self.checkPool() + self.addRestricted() + return len(forb) + + def getForbiddenSuits(self): + self.log.debug("getForbiddenSuits BEGIN. forbidden="+str(self.forbiddenItems)+",ap="+self.startAP) + removableSuits = [suit for suit in self.suits if self.checkPool([suit])] + if 'Varia' in removableSuits and self.startAP in ['Bubble Mountain', 'Firefleas Top']: + # Varia has to be first item there, and checkPool can't detect it + removableSuits.remove('Varia') + self.log.debug("getForbiddenSuits removable="+str(removableSuits)) + if len(removableSuits) > 0: + # remove at least one + if self.addForbidden(removableSuits) == 0: + self.forbiddenItems.append(removableSuits.pop()) + self.checkPool() + self.addRestricted() + else: + self.superFun.remove('Suits') + self.log.debug("Super Fun : Could not remove any suit") + self.log.debug("getForbiddenSuits END. forbidden="+str(self.forbiddenItems)) + + def getForbiddenMovement(self): + self.log.debug("getForbiddenMovement BEGIN. forbidden="+str(self.forbiddenItems)) + removableMovement = [mvt for mvt in self.movementItems if self.checkPool([mvt])] + self.log.debug("getForbiddenMovement removable="+str(removableMovement)) + if len(removableMovement) > 0: + # remove at least the most important + self.forbiddenItems.append(removableMovement.pop(0)) + self.addForbidden(removableMovement + [None]) + else: + self.superFun.remove('Movement') + self.log.debug('Super Fun : Could not remove any movement item') + self.log.debug("getForbiddenMovement END. forbidden="+str(self.forbiddenItems)) + + def getForbiddenCombat(self): + self.log.debug("getForbiddenCombat BEGIN. forbidden="+str(self.forbiddenItems)) + removableCombat = [cbt for cbt in self.combatItems if self.checkPool([cbt])] + self.log.debug("getForbiddenCombat removable="+str(removableCombat)) + if len(removableCombat) > 0: + fake = [] # placeholders to avoid tricking the gaussian into removing too much stuff + if len(removableCombat) > 0: + # remove at least one if possible (will be screw or plasma) + self.forbiddenItems.append(removableCombat.pop(0)) + fake.append(None) + # if plasma is still available, remove it as well if we can + if len(removableCombat) > 0 and removableCombat[0] == 'Plasma' and self.checkPool([removableCombat[0]]): + self.forbiddenItems.append(removableCombat.pop(0)) + fake.append(None) + self.addForbidden(removableCombat + fake) + else: + self.superFun.remove('Combat') + self.log.debug('Super Fun : Could not remove any combat item') + self.log.debug("getForbiddenCombat END. forbidden="+str(self.forbiddenItems)) + + def getForbidden(self): + self.forbiddenItems = [] + self.restrictedLocs = [] + self.errorMsgs = [] + if 'Suits' in self.superFun: # impact on movement item + self.getForbiddenSuits() + if 'Movement' in self.superFun: + self.getForbiddenMovement() + if 'Combat' in self.superFun: + self.getForbiddenCombat() + # if no super fun, check that there's no restricted locations (for ultra sparse) + if len(self.superFun) == 0: + self.addRestricted() + self.log.debug("forbiddenItems: {}".format(self.forbiddenItems)) + self.log.debug("restrictedLocs: {}".format([loc.Name for loc in self.restrictedLocs])) diff --git a/worlds/sm/variaRandomizer/rando/Restrictions.py b/worlds/sm/variaRandomizer/rando/Restrictions.py new file mode 100644 index 00000000..2f932ecc --- /dev/null +++ b/worlds/sm/variaRandomizer/rando/Restrictions.py @@ -0,0 +1,194 @@ +import copy, random, utils.log + +from graph.graph_utils import getAccessPoint +from rando.ItemLocContainer import getLocListStr + +# Holds settings related to item placement restrictions. +# canPlaceAtLocation is the main entry point here +class Restrictions(object): + def __init__(self, settings): + self.log = utils.log.get('Restrictions') + self.settings = settings + # Item split : Major, Chozo, Full, Scavenger + self.split = settings.restrictions['MajorMinor'] + self.suitsRestrictions = settings.restrictions['Suits'] + self.scavLocs = None + self.scavIsVanilla = False + self.scavEscape = False + self.restrictionDictChecker = None + if self.split == 'Scavenger': + self.scavIsVanilla = settings.restrictions['ScavengerParams']['vanillaItems'] + self.scavEscape = settings.restrictions['ScavengerParams']['escape'] + # checker function chain used by canPlaceAtLocation + self.checkers = self.getCheckers() + self.static = {} + self.dynamic = {} + # only useful in door color rando + self.mandatoryBeams = [] + + def disable(self): + self.split = "Full" + self.suitsRestrictions = False + self.checkers = [] + + def setScavengerLocs(self, scavLocs): + self.scavLocs = scavLocs + self.log.debug("scavLocs="+getLocListStr(scavLocs)) + self.scavItemTypes = [loc.VanillaItemType for loc in scavLocs] + + def isEarlyMorph(self): + return self.settings.restrictions['Morph'] == 'early' + + def isLateMorph(self): + return self.settings.restrictions['Morph'] == 'late' + + def isLateDoors(self): + return self.settings.restrictions['doors'] == 'late' + + def isChozo(self): + return self.split == 'Chozo' + + def isScavenger(self): + return self.split == "Scavenger" + + def lateMorphInit(self, ap, emptyContainer, services): + assert self.isLateMorph() + morph = emptyContainer.getNextItemInPool('Morph') + assert morph is not None + locs = services.possibleLocations(morph, ap, emptyContainer, bossesKilled=False) + self.lateMorphLimit = len(locs) + self.log.debug('lateMorphInit. {} locs: {}'.format(self.lateMorphLimit, getLocListStr(locs))) + areas = {} + for loc in locs: + areas[loc.GraphArea] = areas.get(loc.GraphArea, 0) + 1 + self.log.debug('lateMorphLimit. areas: {}'.format(areas)) + if len(areas) > 1: + self.lateMorphForbiddenArea = getAccessPoint(ap).GraphArea + self.log.debug('lateMorphLimit. forbid start area: {}'.format(self.lateMorphForbiddenArea)) + else: + self.lateMorphForbiddenArea = None + + NoCheckCat = set(['Energy', 'Nothing', 'Boss']) + + def setPlacementRestrictions(self, restrictionDict): + self.log.debug("set placement restrictions") + self.log.debug(restrictionDict) + if self.restrictionDictChecker is not None: + self.checkers.remove(self.restrictionDictChecker) + self.restrictionDictChecker = None + if restrictionDict is None: + return + self.restrictionDictChecker = lambda item, loc, cont: item.Category in Restrictions.NoCheckCat\ + or (item.Category == 'Ammo' and cont.hasUnrestrictedLocWithItemType(item.Type))\ + or loc.Name in restrictionDict[loc.GraphArea][item.Type] + self.checkers.append(self.restrictionDictChecker) + + def isLocMajor(self, loc): + return not loc.isBoss() and (self.split == "Full" or loc.isClass(self.split)) + + def isLocMinor(self, loc): + return not loc.isBoss() and (self.split == "Full" or not loc.isClass(self.split)) + + def isItemMajor(self, item): + if self.split == "Full": + return True + elif self.split == 'Scavenger': + return not self.isItemMinor(item) + else: + return item.Class == self.split + + def isItemMinor(self, item): + if self.split == "Full": + return True + elif self.split == 'Scavenger': + return item.Class != "Major" or item.Category == "Energy" + else: + return item.Class == "Minor" + + def isItemLocMatching(self, item, loc): + if self.split == "Full": + return True + if loc.isClass(self.split): + return item.Class == self.split + else: + return item.Class == "Minor" + + # return True if we can keep morph as a possibility + def lateMorphCheck(self, container, possibleLocs): + # the closer we get to the limit the higher the chances of allowing morph + proba = random.randint(0, self.lateMorphLimit) + if self.split == 'Full': + nbItems = len(container.currentItems) + else: + nbItems = len([item for item in container.currentItems if self.split == item.Class]) + if proba > nbItems: + return None + if self.lateMorphForbiddenArea is not None: + morphLocs = [loc for loc in possibleLocs if loc.GraphArea != self.lateMorphForbiddenArea] + forbidden = len(morphLocs) == 0 + possibleLocs = morphLocs if not forbidden else None + return possibleLocs + + def isSuit(self, item): + return item.Type == 'Varia' or item.Type == 'Gravity' + + def getCheckers(self): + checkers = [] + self.log.debug("add bosses restriction") + checkers.append(lambda item, loc, cont: (item.Category != 'Boss' and not loc.isBoss()) or (item.Category == 'Boss' and item.Name == loc.Name)) + if self.split != 'Full': + if self.split != 'Scavenger': + self.log.debug("add majorsSplit restriction") + checkers.append(lambda item, loc, cont: self.isItemLocMatching(item, loc)) + else: + self.log.debug("add scavenger restriction") + baseScavCheck = lambda item, loc: ((loc.VanillaItemType is None and self.isItemMinor(item)) + or (loc.VanillaItemType is not None and self.isItemMajor(item))) + vanillaScavCheck = lambda item, loc: (self.scavLocs is None + or (loc not in self.scavLocs and item.Type not in self.scavItemTypes) + or (item.Type == loc.VanillaItemType and loc in self.scavLocs)) + nonVanillaScavCheck = lambda item, loc: (self.scavLocs is None + or loc not in self.scavLocs + or (loc in self.scavLocs and item.Category != 'Nothing')) + if self.scavIsVanilla: + checkers.append(lambda item, loc, cont: baseScavCheck(item, loc) and vanillaScavCheck(item, loc)) + else: + checkers.append(lambda item, loc, cont: baseScavCheck(item, loc) and nonVanillaScavCheck(item, loc)) + if self.suitsRestrictions: + self.log.debug("add suits restriction") + checkers.append(lambda item, loc, cont: not self.isSuit(item) or loc.GraphArea != 'Crateria') + return checkers + + # return bool telling whether we can place a given item at a given location + def canPlaceAtLocation(self, item, location, container): + ret = True + for chk in self.checkers: + ret = ret and chk(item, location, container) + if not ret: + break + + return ret + + ### Below : faster implementation tailored for random fill + + def precomputeRestrictions(self, container): + # precompute the values for canPlaceAtLocation. only for random filler. + # dict (loc name, item type) -> bool + items = container.getDistinctItems() + for item in items: + for location in container.unusedLocations: + self.static[(location.Name, item.Type)] = self.canPlaceAtLocation(item, location, container) + + container.unrestrictedItems = set(['Super', 'PowerBomb']) + for item in items: + if item.Type not in ['Super', 'PowerBomb']: + continue + for location in container.unusedLocations: + self.dynamic[(location.Name, item.Type)] = self.canPlaceAtLocation(item, location, container) + container.unrestrictedItems = set() + + def canPlaceAtLocationFast(self, itemType, locName, container): + if itemType in ['Super', 'PowerBomb'] and container.hasUnrestrictedLocWithItemType(itemType): + return self.dynamic.get((locName, itemType)) + else: + return self.static.get((locName, itemType)) diff --git a/worlds/sm/variaRandomizer/rando/__init__.py b/worlds/sm/variaRandomizer/rando/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/worlds/sm/variaRandomizer/rando/palettes.py b/worlds/sm/variaRandomizer/rando/palettes.py new file mode 100644 index 00000000..970ddd10 --- /dev/null +++ b/worlds/sm/variaRandomizer/rando/palettes.py @@ -0,0 +1,23489 @@ +palettes = { +131122: 0, +131123: 16, +131124: 186, +131125: 86, +131126: 178, +131127: 65, +131128: 71, +131129: 20, +131130: 3, +131131: 4, +131132: 21, +131133: 78, +131134: 112, +131135: 53, +131136: 203, +131137: 36, +131138: 104, +131139: 24, +131140: 127, +131141: 111, +131142: 248, +131143: 81, +131144: 14, +131145: 65, +131146: 31, +131147: 3, +131148: 218, +131149: 1, +131150: 245, +131151: 0, +131152: 99, +131153: 12, +131154: 0, +131155: 16, +131156: 219, +131157: 86, +131158: 211, +131159: 57, +131160: 71, +131161: 16, +131162: 3, +131163: 4, +131164: 54, +131165: 70, +131166: 145, +131167: 45, +131168: 236, +131169: 32, +131170: 137, +131171: 20, +131172: 155, +131173: 111, +131174: 21, +131175: 82, +131176: 44, +131177: 61, +131178: 59, +131179: 19, +131180: 246, +131181: 13, +131182: 242, +131183: 12, +131184: 99, +131185: 12, +131186: 0, +131187: 16, +131188: 251, +131189: 82, +131190: 244, +131191: 49, +131192: 103, +131193: 16, +131194: 2, +131195: 4, +131196: 118, +131197: 62, +131198: 178, +131199: 37, +131200: 45, +131201: 25, +131202: 169, +131203: 16, +131204: 150, +131205: 111, +131206: 81, +131207: 82, +131208: 106, +131209: 57, +131210: 86, +131211: 39, +131212: 19, +131213: 26, +131214: 15, +131215: 25, +131216: 99, +131217: 8, +131218: 0, +131219: 16, +131220: 28, +131221: 83, +131222: 21, +131223: 42, +131224: 103, +131225: 12, +131226: 2, +131227: 4, +131228: 151, +131229: 54, +131230: 211, +131231: 29, +131232: 78, +131233: 21, +131234: 202, +131235: 12, +131236: 178, +131237: 111, +131238: 110, +131239: 82, +131240: 136, +131241: 53, +131242: 114, +131243: 55, +131244: 47, +131245: 38, +131246: 12, +131247: 37, +131248: 99, +131249: 8, +131250: 0, +131251: 16, +131252: 92, +131253: 79, +131254: 86, +131255: 30, +131256: 136, +131257: 8, +131258: 1, +131259: 0, +131260: 184, +131261: 46, +131262: 19, +131263: 26, +131264: 111, +131265: 17, +131266: 235, +131267: 12, +131268: 173, +131269: 115, +131270: 170, +131271: 78, +131272: 166, +131273: 53, +131274: 141, +131275: 75, +131276: 75, +131277: 54, +131278: 9, +131279: 53, +131280: 67, +131281: 4, +131282: 0, +131283: 16, +131284: 125, +131285: 79, +131286: 119, +131287: 22, +131288: 136, +131289: 4, +131290: 1, +131291: 0, +131292: 217, +131293: 38, +131294: 52, +131295: 18, +131296: 144, +131297: 13, +131298: 12, +131299: 9, +131300: 201, +131301: 115, +131302: 199, +131303: 78, +131304: 196, +131305: 49, +131306: 169, +131307: 91, +131308: 103, +131309: 66, +131310: 6, +131311: 65, +131312: 67, +131313: 4, +131314: 0, +131315: 16, +131316: 157, +131317: 75, +131318: 152, +131319: 14, +131320: 168, +131321: 4, +131322: 0, +131323: 0, +131324: 25, +131325: 31, +131326: 85, +131327: 10, +131328: 209, +131329: 5, +131330: 44, +131331: 5, +131332: 196, +131333: 115, +131334: 3, +131335: 79, +131336: 2, +131337: 46, +131338: 196, +131339: 111, +131340: 132, +131341: 78, +131342: 35, +131343: 77, +131344: 67, +131345: 0, +131346: 0, +131347: 0, +131348: 190, +131349: 75, +131350: 185, +131351: 6, +131352: 168, +131353: 0, +131354: 0, +131355: 0, +131356: 58, +131357: 23, +131358: 118, +131359: 2, +131360: 242, +131361: 1, +131362: 77, +131363: 1, +131364: 224, +131365: 115, +131366: 32, +131367: 79, +131368: 32, +131369: 42, +131370: 224, +131371: 127, +131372: 160, +131373: 90, +131374: 32, +131375: 89, +131376: 67, +131377: 0, +131378: 0, +131379: 16, +131380: 21, +131381: 66, +131382: 13, +131383: 45, +131384: 2, +131385: 0, +131386: 0, +131387: 0, +131388: 112, +131389: 57, +131390: 203, +131391: 32, +131392: 38, +131393: 12, +131394: 3, +131395: 4, +131396: 58, +131397: 70, +131398: 179, +131399: 40, +131400: 9, +131401: 24, +131402: 127, +131403: 111, +131404: 253, +131405: 81, +131406: 19, +131407: 65, +131408: 99, +131409: 12, +131410: 0, +131411: 16, +131412: 54, +131413: 66, +131414: 46, +131415: 37, +131416: 2, +131417: 0, +131418: 0, +131419: 0, +131420: 145, +131421: 49, +131422: 236, +131423: 28, +131424: 71, +131425: 12, +131426: 36, +131427: 4, +131428: 86, +131429: 70, +131430: 208, +131431: 40, +131432: 40, +131433: 20, +131434: 123, +131435: 111, +131436: 249, +131437: 81, +131438: 240, +131439: 64, +131440: 99, +131441: 12, +131442: 0, +131443: 16, +131444: 86, +131445: 62, +131446: 79, +131447: 33, +131448: 2, +131449: 0, +131450: 0, +131451: 0, +131452: 209, +131453: 41, +131454: 13, +131455: 25, +131456: 136, +131457: 8, +131458: 36, +131459: 4, +131460: 147, +131461: 70, +131462: 14, +131463: 41, +131464: 70, +131465: 16, +131466: 118, +131467: 111, +131468: 245, +131469: 77, +131470: 238, +131471: 64, +131472: 66, +131473: 8, +131474: 0, +131475: 16, +131476: 119, +131477: 62, +131478: 112, +131479: 25, +131480: 2, +131481: 0, +131482: 0, +131483: 0, +131484: 242, +131485: 33, +131486: 46, +131487: 21, +131488: 169, +131489: 8, +131490: 69, +131491: 4, +131492: 175, +131493: 70, +131494: 43, +131495: 41, +131496: 101, +131497: 12, +131498: 114, +131499: 111, +131500: 241, +131501: 77, +131502: 203, +131503: 64, +131504: 66, +131505: 8, +131506: 0, +131507: 16, +131508: 183, +131509: 58, +131510: 177, +131511: 21, +131512: 3, +131513: 0, +131514: 0, +131515: 0, +131516: 19, +131517: 26, +131518: 110, +131519: 13, +131520: 202, +131521: 4, +131522: 102, +131523: 0, +131524: 203, +131525: 74, +131526: 104, +131527: 37, +131528: 132, +131529: 12, +131530: 77, +131531: 107, +131532: 12, +131533: 74, +131534: 200, +131535: 68, +131536: 33, +131537: 4, +131538: 0, +131539: 16, +131540: 216, +131541: 58, +131542: 210, +131543: 13, +131544: 3, +131545: 0, +131546: 0, +131547: 0, +131548: 52, +131549: 18, +131550: 143, +131551: 9, +131552: 235, +131553: 4, +131554: 135, +131555: 0, +131556: 231, +131557: 74, +131558: 133, +131559: 37, +131560: 163, +131561: 8, +131562: 73, +131563: 107, +131564: 8, +131565: 74, +131566: 165, +131567: 68, +131568: 33, +131569: 4, +131570: 0, +131571: 16, +131572: 248, +131573: 54, +131574: 243, +131575: 9, +131576: 3, +131577: 0, +131578: 0, +131579: 0, +131580: 116, +131581: 10, +131582: 176, +131583: 5, +131584: 44, +131585: 1, +131586: 135, +131587: 0, +131588: 36, +131589: 75, +131590: 195, +131591: 37, +131592: 193, +131593: 4, +131594: 68, +131595: 107, +131596: 4, +131597: 70, +131598: 163, +131599: 68, +131600: 0, +131601: 0, +131602: 0, +131603: 16, +131604: 25, +131605: 55, +131606: 20, +131607: 2, +131608: 3, +131609: 0, +131610: 0, +131611: 0, +131612: 149, +131613: 2, +131614: 209, +131615: 1, +131616: 77, +131617: 1, +131618: 168, +131619: 0, +131620: 64, +131621: 75, +131622: 224, +131623: 37, +131624: 224, +131625: 0, +131626: 64, +131627: 107, +131628: 0, +131629: 70, +131630: 128, +131631: 68, +131632: 0, +131633: 0, +1312391: 0, +1312392: 56, +1312393: 255, +1312394: 87, +1312395: 247, +1312396: 66, +1312397: 41, +1312398: 9, +1312399: 165, +1312400: 0, +1312401: 90, +1312402: 79, +1312403: 181, +1312404: 54, +1312405: 16, +1312406: 38, +1312407: 140, +1312408: 21, +1312409: 255, +1312410: 3, +1312411: 185, +1312412: 2, +1312413: 112, +1312414: 1, +1312415: 105, +1312416: 0, +1312417: 177, +1312418: 11, +1312419: 169, +1312420: 30, +1312421: 69, +1312422: 1, +890482: 251, +1314700: 0, +1314701: 56, +1314702: 178, +1314703: 114, +1314704: 199, +1314705: 113, +1314706: 97, +1314707: 36, +1314708: 64, +1314709: 24, +1314710: 142, +1314711: 122, +1314712: 11, +1314713: 102, +1314714: 3, +1314715: 77, +1314716: 0, +1314717: 73, +1314718: 224, +1314719: 127, +1314720: 128, +1314721: 126, +1314722: 224, +1314723: 68, +1314724: 32, +1314725: 44, +1314726: 0, +1314727: 0, +1314728: 0, +1314729: 0, +1314730: 0, +1314731: 0, +1315705: 0, +1315706: 56, +1315707: 156, +1315708: 75, +1315709: 148, +1315710: 54, +1315711: 41, +1315712: 9, +1315713: 66, +1315714: 0, +1315715: 24, +1315716: 59, +1315717: 82, +1315718: 42, +1315719: 173, +1315720: 25, +1315721: 107, +1315722: 17, +1315723: 90, +1315724: 127, +1315725: 192, +1315726: 126, +1315727: 224, +1315728: 109, +1315729: 224, +1315730: 84, +1315731: 29, +1315732: 0, +1315733: 20, +1315734: 0, +1315735: 10, +1315736: 0, +1316253: 0, +1316254: 56, +1316255: 156, +1316256: 75, +1316257: 148, +1316258: 54, +1316259: 41, +1316260: 9, +1316261: 66, +1316262: 0, +1316263: 24, +1316264: 59, +1316265: 82, +1316266: 42, +1316267: 173, +1316268: 25, +1316269: 107, +1316270: 17, +1316271: 90, +1316272: 127, +1316273: 192, +1316274: 126, +1316275: 224, +1316276: 109, +1316277: 224, +1316278: 84, +1316279: 29, +1316280: 0, +1316281: 20, +1316282: 0, +1316283: 10, +1316284: 0, +454107: 96, +1317292: 0, +1317293: 56, +1317294: 255, +1317295: 87, +1317296: 247, +1317297: 66, +1317298: 41, +1317299: 9, +1317300: 165, +1317301: 0, +1317302: 90, +1317303: 79, +1317304: 181, +1317305: 54, +1317306: 16, +1317307: 38, +1317308: 206, +1317309: 29, +1317310: 223, +1317311: 1, +1317312: 31, +1317313: 0, +1317314: 24, +1317315: 0, +1317316: 10, +1317317: 0, +1317318: 185, +1317319: 6, +1317320: 234, +1317321: 0, +1317322: 69, +1317323: 0, +454147: 160, +454148: 76, +454149: 0, +1500565: 12, +1318735: 0, +1318736: 56, +1318737: 255, +1318738: 87, +1318739: 247, +1318740: 66, +1318741: 41, +1318742: 9, +1318743: 165, +1318744: 0, +1318745: 90, +1318746: 79, +1318747: 181, +1318748: 54, +1318749: 16, +1318750: 38, +1318751: 206, +1318752: 29, +1318753: 0, +1318754: 0, +1318755: 0, +1318756: 0, +1318757: 0, +1318758: 0, +1318759: 0, +1318760: 0, +1318761: 0, +1318762: 0, +1318763: 0, +1318764: 0, +1318765: 0, +1318766: 0, +1500568: 101, +1640071: 0, +1640072: 56, +1115785: 190, +1115786: 75, +1115787: 185, +1115788: 6, +1115789: 234, +1115790: 0, +1115791: 101, +1115792: 0, +1115793: 58, +1115794: 23, +1115795: 118, +1115796: 2, +1115797: 242, +1115798: 1, +1115799: 77, +1115800: 1, +1115801: 0, +1115802: 0, +1115803: 0, +1115804: 0, +1115805: 0, +1115806: 0, +1640095: 134, +1115808: 0, +1115809: 0, +1115810: 0, +1640099: 22, +1115812: 0, +1640101: 19, +1640102: 1, +1377991: 0, +1377992: 56, +1377993: 159, +1377994: 103, +1377995: 153, +1377996: 82, +1377997: 46, +1377998: 37, +1377999: 170, +1640144: 12, +1378001: 252, +1378002: 94, +1640147: 93, +1378004: 70, +1378005: 178, +1378006: 53, +1378007: 112, +1378008: 45, +1640153: 187, +1378010: 91, +1378011: 248, +1378012: 61, +1378013: 14, +1378014: 45, +1640159: 134, +1640160: 20, +1640161: 59, +1378018: 94, +1640163: 22, +1640164: 2, +1640165: 19, +1640166: 1, +1640167: 0, +1640168: 56, +1640169: 253, +1640170: 2, +1640171: 62, +1640172: 1, +1640173: 108, +1640174: 0, +1640175: 102, +1640176: 0, +1640177: 30, +1640178: 2, +1640179: 95, +1640180: 0, +1640181: 89, +1640182: 0, +1640183: 115, +1640184: 0, +1640185: 187, +1640186: 94, +1378043: 179, +1640188: 61, +1640189: 46, +1640190: 41, +1640191: 134, +1640192: 20, +1640193: 59, +1640194: 3, +1640195: 22, +1640196: 2, +1640197: 19, +1640198: 1, +1640199: 0, +1640200: 56, +1640201: 255, +1640202: 107, +1640203: 214, +1640204: 78, +1640205: 164, +1640206: 20, +1640207: 32, +1640208: 4, +1640209: 123, +1640210: 91, +1640211: 82, +1640212: 62, +1640213: 205, +1640214: 49, +1640215: 73, +1640216: 33, +1640217: 187, +1640218: 94, +1640219: 179, +1640220: 61, +1640221: 46, +1640222: 41, +1640223: 134, +1640224: 20, +1640225: 59, +1640226: 3, +1640227: 22, +1640228: 2, +1640229: 19, +1640230: 1, +1640231: 0, +1640232: 56, +1640233: 190, +1640234: 75, +1321726: 0, +1321727: 56, +1321728: 255, +1321729: 87, +1321730: 247, +1640235: 185, +1321732: 41, +1321733: 9, +1321734: 165, +1321735: 0, +1321736: 90, +1640236: 6, +1321738: 181, +1321739: 54, +1321740: 16, +1321741: 38, +1321742: 206, +1640237: 234, +1321744: 223, +1321745: 1, +1321746: 31, +1321747: 0, +1321748: 24, +1321749: 0, +1321750: 10, +1321751: 0, +1321752: 185, +1321753: 6, +1321754: 234, +1640239: 101, +1321756: 69, +1321757: 0, +1640240: 0, +1640241: 58, +1640242: 23, +1640243: 118, +1640244: 2, +1640245: 242, +1640246: 1, +1640247: 77, +1640248: 1, +1640249: 187, +1640250: 94, +1640251: 179, +1640252: 61, +1640253: 46, +1640254: 41, +1640255: 134, +1640256: 20, +1640257: 59, +1640258: 3, +1640259: 22, +1640260: 2, +1640261: 19, +1640262: 1, +1152620: 0, +1152621: 224, +1152622: 127, +1152623: 160, +1152624: 125, +1152625: 224, +1152627: 160, +1152628: 48, +1152629: 224, +1152630: 59, +1152631: 128, +1152632: 38, +1152633: 128, +1152634: 21, +454365: 23, +1324638: 0, +1324639: 56, +1324640: 255, +1324641: 87, +1324642: 247, +1324643: 66, +1324644: 140, +1324645: 21, +1324646: 165, +1324647: 0, +1324648: 90, +1324649: 79, +1324650: 181, +1324651: 54, +1324652: 16, +1324653: 38, +1324654: 206, +1324655: 29, +1324656: 223, +1324657: 28, +1324658: 224, +1324659: 79, +1324660: 32, +1324661: 59, +1324662: 32, +1324663: 42, +1324664: 151, +1324665: 16, +1324666: 223, +1324667: 107, +1324668: 46, +1324669: 4, +890832: 121, +890833: 103, +890835: 46, +890836: 123, +1194109: 0, +1194110: 56, +1194111: 255, +1194112: 127, +1194113: 255, +1194114: 13, +1194115: 191, +1194116: 8, +1194117: 149, +1194118: 8, +1194119: 108, +1194120: 8, +1194121: 71, +1194122: 4, +1194123: 126, +1194124: 107, +1194125: 30, +1194126: 87, +1194127: 88, +1194128: 58, +1194129: 113, +1194130: 33, +1194131: 203, +1194132: 12, +1194133: 159, +1194134: 3, +1194135: 58, +1194136: 2, +1194137: 118, +1194138: 1, +1194139: 0, +1194140: 0, +1194141: 0, +1194142: 0, +1194143: 255, +1194144: 127, +1194145: 255, +1194146: 13, +1194147: 191, +1194148: 8, +1194149: 149, +1194150: 8, +1194151: 108, +1194152: 8, +1194153: 71, +1194154: 4, +1194155: 126, +1194156: 107, +1194157: 30, +1194158: 87, +1194159: 88, +1194160: 58, +1194161: 113, +1194162: 33, +1194163: 203, +1194164: 12, +1194165: 159, +1194166: 3, +1194167: 58, +1194168: 2, +1194169: 118, +1194170: 1, +1194171: 0, +1194172: 0, +1194187: 123, +1194188: 74, +1194189: 144, +1194190: 28, +1194191: 105, +1194192: 20, +1194193: 36, +1194194: 20, +1194195: 8, +1194196: 0, +1194197: 191, +1194198: 36, +1194199: 149, +1194200: 36, +1194201: 108, +1194202: 28, +1194203: 69, +1194204: 16, +1194237: 0, +1194238: 56, +1194239: 255, +1194240: 127, +1194241: 126, +1194242: 107, +1194243: 30, +1194244: 87, +1194245: 88, +1194246: 58, +1194247: 113, +1194248: 33, +1194249: 203, +1194250: 12, +1194251: 126, +1194252: 107, +1194253: 30, +1194254: 87, +1194255: 88, +1194256: 58, +1194257: 113, +1194258: 33, +1194259: 203, +1194260: 12, +1194261: 159, +1194262: 3, +1194263: 58, +1194264: 2, +1194265: 118, +1194266: 1, +1194267: 0, +1194268: 0, +890884: 31, +890886: 5, +890891: 33, +890892: 159, +890893: 87, +890894: 210, +890895: 74, +890897: 58, +890899: 0, +890900: 20, +890902: 170, +890903: 48, +890906: 4, +1327427: 0, +1327428: 56, +1327429: 255, +1327430: 87, +1327431: 247, +1327432: 66, +1327433: 41, +1327434: 9, +1327435: 165, +1327436: 0, +1327437: 90, +1327438: 79, +1327439: 181, +1327440: 54, +1327441: 16, +1327442: 38, +1327443: 206, +1327444: 29, +1327445: 255, +1327446: 2, +1327447: 191, +1327448: 1, +1327449: 15, +1327450: 0, +1327451: 8, +1327452: 0, +1327453: 255, +1327454: 3, +1327455: 55, +1327456: 2, +1327457: 209, +1327458: 0, +1291699: 0, +1291700: 56, +1291701: 157, +1291702: 85, +1291703: 22, +1291704: 24, +1291705: 13, +1291706: 16, +1291707: 29, +1291708: 51, +1291709: 183, +1291710: 42, +1291711: 145, +1291712: 42, +1291713: 12, +1291714: 30, +1291715: 170, +1291716: 25, +1291717: 72, +1160646: 36, +1291719: 229, +1291720: 12, +1291721: 132, +1160650: 20, +1160651: 71, +1291724: 0, +1160653: 224, +1160654: 3, +1160655: 160, +1160656: 2, +1291729: 0, +1160658: 1, +541665: 0, +541666: 56, +541667: 255, +541668: 127, +541669: 255, +541670: 25, +541671: 85, +541672: 29, +541673: 173, +541674: 16, +541675: 255, +541676: 83, +541677: 158, +541678: 3, +541679: 95, +541680: 41, +541681: 223, +541682: 24, +541683: 0, +541684: 0, +541685: 0, +541686: 0, +541687: 0, +541688: 0, +541689: 0, +541690: 0, +541691: 0, +541692: 0, +541693: 0, +541694: 0, +541695: 74, +541696: 16, +541697: 0, +541698: 56, +541699: 255, +541700: 127, +541701: 192, +541702: 126, +541703: 224, +541704: 109, +541705: 224, +541706: 84, +541707: 110, +541708: 127, +541709: 10, +541710: 115, +541711: 101, +541712: 102, +541713: 34, +541714: 94, +541715: 160, +541716: 127, +541717: 96, +541718: 123, +541719: 32, +541720: 119, +541721: 160, +541722: 114, +541723: 96, +541724: 110, +541725: 145, +541726: 127, +541727: 192, +541728: 32, +541729: 0, +541730: 56, +541731: 255, +541732: 127, +541733: 31, +541734: 124, +541735: 22, +541736: 88, +541737: 12, +541738: 48, +541739: 223, +541740: 126, +541741: 223, +541742: 125, +541743: 223, +541744: 124, +541745: 26, +541746: 104, +541747: 0, +541748: 0, +541749: 0, +541750: 0, +541751: 0, +541752: 0, +541753: 0, +541754: 0, +541755: 0, +541756: 0, +541757: 0, +541758: 0, +541759: 9, +541760: 36, +541761: 0, +541762: 56, +541763: 255, +541764: 127, +541765: 224, +541766: 59, +541767: 128, +541768: 38, +541769: 128, +541770: 21, +541771: 250, +541772: 107, +541773: 242, +541774: 75, +541775: 234, +541776: 43, +541777: 224, +541778: 3, +541779: 0, +541780: 0, +541781: 0, +541782: 0, +541783: 0, +541784: 0, +541785: 0, +541786: 0, +541787: 0, +541788: 0, +541789: 0, +541790: 0, +541791: 32, +541792: 9, +541793: 0, +541794: 56, +541795: 255, +541796: 127, +541797: 255, +541798: 3, +541799: 22, +541800: 2, +541801: 238, +541802: 0, +541803: 255, +541804: 107, +541805: 255, +541806: 75, +541807: 255, +541808: 43, +541809: 123, +541810: 3, +541811: 0, +541812: 0, +541813: 0, +541814: 0, +541815: 0, +541816: 0, +541817: 0, +541818: 0, +541819: 0, +541820: 0, +541821: 0, +541822: 0, +541823: 173, +541824: 0, +1328819: 0, +1328820: 56, +1328821: 255, +1328822: 87, +1328823: 247, +1328824: 66, +1328825: 41, +1328826: 9, +1328827: 165, +1328828: 0, +1328829: 90, +1328830: 79, +1328831: 181, +1328832: 54, +1328833: 16, +1328834: 38, +1328835: 206, +1328836: 29, +1328837: 31, +1328838: 0, +1328839: 24, +1328840: 0, +1328841: 15, +1328842: 0, +1328843: 8, +1328844: 0, +1328845: 10, +1328846: 0, +1328847: 255, +1328848: 3, +1328849: 181, +1328850: 2, +1500638: 66, +454551: 2, +454552: 8, +454555: 0, +454556: 0, +454557: 0, +454558: 0, +454559: 0, +454560: 0, +454565: 19, +454566: 60, +454567: 11, +454568: 24, +454570: 20, +454571: 3, +454572: 12, +454573: 1, +454574: 4, +454577: 0, +454579: 0, +454580: 0, +454591: 4, +454592: 16, +454593: 2, +454594: 8, +454595: 0, +454596: 0, +1270464: 176, +1270465: 0, +1270466: 54, +1270467: 2, +1270468: 83, +454597: 0, +1270469: 1, +1270470: 175, +1270471: 0, +1270472: 20, +1270473: 2, +454598: 0, +1270474: 49, +1270475: 1, +1270476: 142, +1270477: 0, +1270478: 210, +454599: 0, +1270479: 1, +1270480: 15, +1270481: 1, +1270482: 140, +1270483: 0, +454600: 0, +1270484: 144, +1270485: 1, +1270486: 237, +1270487: 0, +1270488: 107, +1270489: 0, +1270490: 110, +1270491: 1, +1270492: 204, +1270493: 0, +1270494: 105, +1270495: 0, +454605: 21, +454606: 68, +454607: 13, +454608: 32, +454609: 7, +454610: 28, +454611: 5, +454612: 20, +454613: 3, +454614: 12, +454615: 1, +454616: 4, +454617: 0, +1270569: 4, +454618: 0, +1270749: 20, +1333281: 0, +1333282: 56, +1333283: 87, +1333284: 63, +1333285: 77, +1333286: 46, +1333287: 226, +1333288: 0, +1333289: 96, +1333290: 0, +1333291: 176, +1333292: 58, +1333293: 11, +1333294: 34, +1333295: 102, +1333296: 17, +1333297: 36, +1333298: 9, +1333299: 90, +1333300: 67, +1333301: 148, +1333302: 54, +1333303: 173, +1333304: 21, +1333305: 8, +1333306: 5, +1333307: 255, +1333308: 3, +1333309: 55, +1333310: 2, +1333311: 209, +1333312: 0, +454670: 0, +454671: 17, +454672: 0, +1270871: 65, +1270872: 71, +1270873: 20, +1270874: 3, +1270875: 4, +1270876: 21, +1270877: 78, +1270878: 112, +1270879: 53, +1270880: 203, +1270881: 36, +1270882: 104, +1270883: 24, +1270884: 95, +1270885: 94, +1270886: 63, +1270887: 24, +1270888: 20, +1270889: 16, +1270890: 122, +1270891: 78, +1270892: 115, +1270893: 61, +1270894: 73, +1270895: 20, +1270896: 5, +1270897: 4, +1270898: 214, +1270899: 69, +1270900: 81, +1270901: 49, +1270902: 173, +1334215: 0, +1334216: 56, +1334217: 255, +1334218: 2, +1334219: 191, +1334220: 1, +1334221: 15, +1334222: 0, +1334223: 8, +1334224: 0, +1334225: 191, +1334226: 1, +1334227: 27, +1334228: 1, +1334229: 186, +1334230: 0, +1334231: 17, +1334232: 0, +1334233: 92, +1334234: 90, +1334235: 180, +1334236: 65, +1334237: 13, +1334238: 41, +1334239: 101, +1334240: 16, +1334241: 255, +1334242: 3, +1334243: 55, +1334244: 2, +1334245: 209, +1334246: 0, +1270971: 5, +1153049: 8, +1140109: 92, +1140115: 101, +1335856: 0, +1335857: 56, +1335858: 255, +1335859: 127, +1335860: 224, +1335861: 86, +1335862: 128, +1335863: 49, +1335864: 192, +1335865: 24, +1335866: 192, +1335867: 107, +1335868: 192, +1335869: 94, +1335870: 32, +1335871: 74, +1335872: 160, +1335873: 53, +1335874: 255, +1335875: 127, +1335876: 156, +1335877: 3, +1335878: 55, +1335879: 2, +1335880: 209, +1335881: 0, +1335882: 255, +1335883: 3, +1335884: 55, +1335885: 2, +1335886: 209, +1335887: 0, +1336711: 0, +1336712: 56, +1336713: 255, +1336714: 127, +1336715: 224, +1336716: 59, +1336717: 128, +1336718: 38, +1336719: 128, +1336720: 21, +1336721: 250, +1336722: 107, +1336723: 242, +1336724: 75, +1336725: 234, +1336726: 43, +1336727: 224, +1336728: 3, +1336729: 0, +1336730: 0, +1336731: 0, +1336732: 0, +1336733: 0, +1336734: 0, +1336735: 0, +1336736: 0, +1336737: 0, +1336738: 0, +1336739: 0, +1336740: 0, +1336741: 0, +1336742: 0, +1337260: 0, +1337261: 56, +1337262: 178, +1337263: 114, +1337264: 199, +1337265: 113, +1337266: 97, +1337267: 36, +1337268: 64, +1337269: 24, +1337270: 142, +1337271: 122, +1337272: 11, +1337273: 102, +1337274: 3, +1337275: 77, +1337276: 164, +1337277: 48, +1337278: 164, +1337279: 48, +1337280: 97, +1337281: 36, +1337282: 64, +1337283: 24, +1337284: 0, +1337285: 8, +1337286: 0, +1337287: 0, +1337288: 0, +1337289: 0, +1337290: 0, +1337291: 0, +1337754: 0, +1337755: 56, +1337756: 87, +1337757: 75, +1337758: 77, +1337759: 58, +1337760: 226, +1337761: 12, +1337762: 64, +1337763: 0, +1337764: 176, +1337765: 70, +1337766: 11, +1337767: 46, +1337768: 102, +1337769: 29, +1337770: 36, +1337771: 21, +1337772: 255, +1337773: 72, +1337774: 182, +1337775: 56, +1337776: 110, +1337777: 36, +1337778: 72, +1337779: 20, +1337780: 255, +1337781: 3, +1337782: 55, +1337783: 2, +1337784: 209, +1337785: 0, +1337786: 0, +1337787: 56, +1337788: 255, +1337789: 87, +1337790: 247, +1337791: 66, +1337792: 140, +1337793: 21, +1337794: 165, +1337795: 0, +1337796: 90, +1337797: 79, +1337798: 181, +1337799: 54, +1337800: 16, +1337801: 38, +1337802: 206, +1337803: 29, +1337804: 224, +1337805: 3, +1337806: 1, +1337807: 3, +1337808: 161, +1337809: 1, +1337810: 193, +1337811: 0, +1337812: 250, +1337813: 107, +1337814: 176, +1337815: 66, +1337816: 69, +1337817: 21, +1337818: 0, +1337819: 56, +1337820: 223, +1337821: 90, +1337822: 117, +1337823: 57, +1337824: 10, +1337825: 12, +1337826: 6, +1337827: 0, +1337828: 26, +1337829: 78, +1337830: 51, +1337831: 45, +1337832: 142, +1337833: 28, +1337834: 43, +1337835: 16, +1337836: 17, +1337837: 111, +1337838: 42, +1337839: 82, +1337840: 36, +1337841: 49, +1337842: 36, +1337843: 24, +1337844: 255, +1337845: 2, +1337846: 191, +1337847: 1, +1337848: 15, +1337849: 0, +1127019: 40, +1127021: 24, +1127022: 16, +1127023: 98, +1127024: 107, +1127025: 73, +1127026: 198, +1127027: 56, +1127028: 99, +1127029: 44, +1127030: 90, +1127031: 47, +1127032: 148, +1127033: 34, +1127034: 173, +1127035: 1, +1127036: 8, +1127037: 1, +1127038: 255, +1127039: 127, +1127040: 181, +1127041: 86, +1127042: 74, +1127043: 41, +1141135: 65, +1127100: 0, +1127101: 56, +1127102: 255, +1127103: 2, +1127104: 191, +1127105: 1, +1127106: 15, +1127107: 0, +1127108: 8, +1127109: 0, +1127110: 191, +1127111: 1, +1127112: 27, +1127113: 1, +1127114: 21, +1127115: 0, +1127117: 0, +1127118: 31, +1127119: 36, +1127120: 23, +1127121: 28, +1127122: 47, +1127123: 20, +1127124: 71, +1127125: 12, +1127126: 224, +1127127: 3, +1127128: 160, +1127129: 2, +1127130: 64, +1127131: 1, +1142227: 181, +1348722: 0, +1348723: 56, +1348724: 159, +1348725: 38, +1348726: 89, +1348727: 1, +1348728: 76, +1348729: 0, +1348730: 4, +1348731: 0, +1348732: 57, +1348733: 87, +1348734: 115, +1348735: 66, +1348736: 173, +1348737: 45, +1348738: 198, +1348739: 20, +1348740: 127, +1348741: 54, +1348742: 249, +1348743: 41, +1348744: 115, +1348745: 33, +1348746: 12, +1348747: 21, +1348748: 134, +1348749: 12, +1348750: 255, +1348751: 127, +1348752: 0, +1348753: 0, +1348754: 0, +1348755: 56, +1348756: 0, +1348757: 0, +1348758: 0, +1348759: 0, +1348760: 0, +1348761: 0, +1348762: 36, +1348763: 0, +1348764: 173, +1348765: 41, +1348766: 74, +1348767: 33, +1348768: 231, +1348769: 20, +1348770: 99, +1348771: 12, +1348772: 0, +1348773: 0, +1348774: 0, +1348775: 0, +1348776: 0, +1348777: 0, +1348778: 0, +1348779: 0, +1348780: 0, +1348781: 0, +1348782: 173, +1348783: 41, +1348784: 0, +1348785: 0, +1348786: 0, +1348787: 56, +1348788: 224, +1348789: 127, +1348790: 32, +1348791: 107, +1348792: 64, +1348793: 86, +1348794: 255, +1348795: 3, +1348796: 191, +1348797: 2, +1348798: 95, +1348799: 1, +1348800: 31, +1348801: 0, +1348802: 249, +1348803: 41, +1348804: 115, +1348805: 33, +1348806: 12, +1348807: 21, +1348808: 24, +1348809: 99, +1348810: 49, +1348811: 70, +1348812: 74, +1348813: 41, +1348814: 255, +1348815: 127, +1348816: 99, +1348817: 12, +1348818: 0, +1348819: 56, +1348820: 184, +1348821: 87, +1348822: 17, +1348823: 11, +1348824: 70, +1348825: 22, +1348826: 227, +1348827: 0, +1348828: 255, +1348829: 114, +1348830: 223, +1348831: 44, +1348832: 185, +1348833: 36, +1348834: 175, +1348835: 28, +1348836: 169, +1348837: 24, +1348838: 159, +1348839: 79, +1348840: 216, +1348841: 62, +1348842: 18, +1348843: 46, +1348844: 205, +1348845: 8, +1348846: 255, +1348847: 127, +1348848: 0, +1348849: 0, +1348850: 0, +1348851: 56, +1348852: 24, +1348853: 99, +1348854: 24, +1348855: 99, +1348856: 2, +1348857: 8, +1348858: 148, +1348859: 82, +1348860: 206, +1348861: 57, +1348862: 8, +1348863: 33, +1348864: 132, +1348865: 16, +1348866: 25, +1348867: 0, +1348868: 18, +1348869: 0, +1348870: 0, +1348871: 92, +1348872: 0, +1348873: 64, +1348874: 132, +1348875: 16, +1348876: 127, +1348877: 25, +1348878: 255, +1348879: 127, +1348880: 0, +1348881: 0, +1348882: 0, +1348883: 56, +1348884: 148, +1348885: 82, +1348886: 206, +1348887: 57, +1348888: 8, +1348889: 33, +1348890: 139, +1348891: 127, +1348892: 10, +1348893: 111, +1348894: 136, +1348895: 94, +1348896: 7, +1348897: 78, +1348898: 134, +1348899: 57, +1348900: 5, +1348901: 41, +1348902: 131, +1348903: 24, +1348904: 2, +1348905: 8, +1348906: 132, +1348907: 16, +1348908: 24, +1348909: 99, +1348910: 255, +1348911: 127, +1348912: 0, +1348913: 0, +1218223: 25, +1218224: 3, +1218225: 84, +1218226: 2, +1218227: 143, +1218228: 1, +1218229: 202, +1218230: 0, +1218231: 186, +1218232: 2, +1218233: 244, +1218234: 1, +1218235: 79, +1218236: 1, +1218237: 170, +1218238: 0, +1218239: 59, +1218240: 2, +1218241: 181, +1218242: 1, +1218243: 47, +1218244: 1, +1218245: 137, +1218246: 0, +1218247: 220, +1218248: 1, +1218249: 85, +1218250: 1, +1218251: 239, +1218252: 0, +1218253: 105, +1218254: 0, +1218255: 92, +1218256: 1, +1218257: 22, +1218258: 1, +1218259: 176, +1218260: 0, +1218261: 105, +1218262: 0, +1218263: 253, +1218264: 0, +1218265: 182, +1218266: 0, +1218267: 112, +1218268: 0, +1218269: 73, +1218270: 0, +1218271: 126, +1218272: 0, +1218273: 119, +1218274: 0, +1218275: 80, +1218276: 0, +1218277: 40, +1218278: 0, +1218279: 31, +1218280: 0, +1218281: 23, +1218282: 0, +1218283: 16, +1218284: 0, +1218285: 8, +1218286: 0, +1502684: 57, +1502685: 87, +1677266: 20, +1502686: 115, +1677315: 107, +1677317: 158, +1677328: 24, +1677330: 24, +1677333: 47, +1677336: 71, +1221111: 0, +1221112: 56, +1221113: 87, +1221114: 63, +1221115: 77, +1221116: 46, +1221117: 226, +1221118: 0, +1221119: 96, +1221120: 0, +1221121: 176, +1221122: 58, +1221123: 11, +1221124: 34, +1221125: 102, +1221126: 17, +1221127: 36, +1221128: 9, +1221129: 25, +1221130: 3, +1221131: 84, +1221132: 2, +1221133: 143, +1221134: 1, +1221135: 202, +1221136: 0, +1221137: 27, +1221138: 88, +1221139: 146, +1221140: 24, +1221141: 69, +1221142: 1, +1221143: 0, +1221144: 56, +1221145: 87, +1221146: 63, +1221147: 77, +1221148: 46, +1221149: 226, +1221150: 0, +1221151: 96, +1221152: 0, +1221153: 176, +1221154: 58, +1221155: 11, +1221156: 34, +1221157: 102, +1221158: 17, +1221159: 36, +1221160: 9, +1221161: 25, +1221162: 3, +1221163: 84, +1221164: 2, +1221165: 143, +1221166: 1, +1221167: 202, +1221168: 0, +1221169: 27, +1221170: 88, +1221171: 146, +1221172: 24, +1221173: 69, +1221174: 1, +1221175: 0, +1221176: 56, +1221177: 90, +1221178: 107, +1221179: 82, +1221180: 86, +1221181: 231, +1221182: 40, +1221183: 99, +1221184: 24, +1221185: 181, +1221186: 98, +1221187: 16, +1221188: 74, +1221189: 107, +1221190: 57, +1221191: 41, +1221192: 49, +1221193: 255, +1221194: 67, +1221195: 19, +1221196: 1, +1221197: 15, +1221198: 0, +1221199: 92, +1221200: 23, +1221201: 153, +1221202: 2, +1221203: 214, +1221204: 1, +1221205: 224, +1221206: 3, +1221207: 0, +1221208: 56, +1221209: 156, +1221210: 75, +1221211: 148, +1221212: 54, +1221213: 41, +1221214: 9, +1221215: 66, +1221216: 0, +1221217: 247, +1221218: 66, +1221219: 82, +1221220: 42, +1221221: 173, +1221222: 25, +1221223: 107, +1221224: 17, +1221225: 32, +1221226: 20, +1221227: 32, +1221228: 20, +1221229: 32, +1221230: 20, +1221231: 32, +1221232: 20, +1221233: 32, +1221234: 20, +1221235: 32, +1221236: 20, +1221237: 32, +1221238: 20, +1221239: 0, +1221240: 56, +1221241: 87, +1221242: 63, +1221243: 77, +1221244: 46, +1221245: 226, +1221246: 0, +1221247: 96, +1221248: 0, +1221249: 176, +1221250: 58, +1221251: 11, +1221252: 34, +1221253: 102, +1221254: 17, +1221255: 36, +1221256: 9, +1221257: 25, +1221258: 3, +1221259: 84, +1221260: 2, +1221261: 143, +1221262: 1, +1221263: 202, +1221264: 0, +1221265: 27, +1221266: 88, +1221267: 146, +1221268: 24, +1221269: 69, +1221270: 1, +1364098: 22, +1364099: 74, +1364100: 145, +1364101: 57, +1364102: 44, +1364103: 45, +1364104: 167, +1364105: 28, +1364106: 229, +1364107: 32, +1364108: 164, +1364109: 24, +1364110: 131, +1364111: 16, +1364112: 65, +1364113: 8, +1364114: 0, +1364115: 0, +1364116: 0, +1364117: 0, +1364118: 223, +1364119: 2, +1364120: 0, +1364121: 0, +1364122: 2, +1364123: 8, +1364124: 148, +1364125: 82, +1364126: 206, +1364127: 57, +1364128: 8, +1364129: 33, +1364130: 132, +1364131: 16, +1364132: 25, +1364133: 0, +1364134: 18, +1364135: 0, +1364136: 0, +1364137: 92, +1364138: 0, +1364139: 64, +1364140: 132, +1364141: 16, +1364142: 127, +1364143: 25, +1364144: 255, +1364145: 127, +1364146: 185, +1364147: 78, +1364148: 86, +1364149: 70, +1364150: 18, +1364151: 62, +1364152: 207, +1364153: 49, +1364154: 238, +1364155: 53, +1364156: 205, +1364157: 45, +1364158: 172, +1364159: 41, +1364160: 139, +1364161: 37, +1364162: 74, +1364163: 29, +1364164: 74, +1364165: 29, +1364166: 63, +1364167: 31, +1364168: 74, +1364169: 29, +1364170: 76, +1364171: 37, +1364172: 239, +1364173: 61, +1364174: 107, +1364175: 45, +1364176: 198, +1364177: 24, +1364178: 99, +1364179: 12, +1364180: 19, +1364181: 0, +1364182: 14, +1364183: 0, +1364184: 0, +1364185: 68, +1364186: 0, +1364187: 48, +1364188: 99, +1364189: 12, +1364190: 23, +1364191: 21, +1364192: 247, +1364193: 94, +1364194: 92, +1364195: 87, +1364196: 58, +1364197: 79, +1364198: 25, +1364199: 75, +1364200: 215, +1364201: 70, +1364202: 246, +1364203: 70, +1364204: 214, +1364205: 70, +1364206: 214, +1364207: 66, +1364208: 181, +1364209: 62, +1364210: 181, +1364211: 62, +1364212: 181, +1364213: 62, +1364214: 159, +1364215: 63, +1364216: 181, +1364217: 62, +1364218: 181, +1364219: 62, +1364220: 74, +1364221: 41, +1364222: 231, +1364223: 28, +1364224: 132, +1364225: 16, +1364226: 66, +1364227: 8, +1364228: 13, +1364229: 0, +1364230: 9, +1364231: 0, +1364232: 0, +1364233: 48, +1364234: 0, +1364235: 32, +1364236: 66, +1364237: 8, +1364238: 208, +1364239: 12, +1364240: 16, +1364241: 66, +1364242: 255, +1364243: 91, +1364244: 255, +1364245: 91, +1364246: 255, +1364247: 91, +1364248: 255, +1364249: 91, +1364250: 255, +1364251: 91, +1364252: 255, +1364253: 91, +1364254: 255, +1364255: 91, +1364256: 255, +1364257: 91, +1364258: 255, +1364259: 91, +1364260: 255, +1364261: 91, +1364262: 255, +1364263: 91, +1364264: 255, +1364265: 91, +1364266: 255, +1364267: 91, +1364268: 165, +1364269: 20, +1364270: 132, +1364271: 16, +1364272: 66, +1364273: 8, +1364274: 33, +1364275: 4, +1364276: 6, +1364277: 0, +1364278: 5, +1364279: 0, +1364280: 0, +1364281: 24, +1364282: 0, +1364283: 16, +1364284: 33, +1364285: 4, +1364286: 104, +1364287: 8, +1364288: 8, +1364289: 33, +1364580: 223, +1364581: 46, +1364582: 155, +1364583: 1, +1364584: 142, +1364585: 0, +1364586: 6, +1364587: 0, +1364588: 159, +1364589: 38, +1364590: 89, +1364591: 1, +1364592: 76, +1364593: 0, +1364594: 4, +1364595: 0, +1364596: 93, +1364597: 30, +1364598: 23, +1364599: 1, +1364600: 10, +1364601: 0, +1364602: 2, +1364603: 0, +1364604: 27, +1364605: 22, +1364606: 213, +1364607: 0, +1364608: 8, +1364609: 0, +1364610: 0, +1364611: 0, +1364612: 27, +1364613: 22, +1364614: 213, +1364615: 0, +1364616: 8, +1364617: 0, +1364618: 0, +1364619: 0, +1364620: 93, +1364621: 30, +1364622: 23, +1364623: 1, +1364624: 10, +1364625: 0, +1364626: 2, +1364627: 0, +1364628: 159, +1364629: 38, +1364630: 89, +1364631: 1, +1364632: 76, +1364633: 0, +1364634: 4, +1364635: 0, +1364636: 223, +1364637: 46, +1364638: 155, +1364639: 1, +1364640: 142, +1364641: 0, +1364642: 6, +1364643: 0, +1364644: 159, +1364645: 38, +1364646: 89, +1364647: 1, +1364648: 76, +1364649: 0, +1364650: 4, +1364651: 0, +1364652: 159, +1364653: 34, +1364654: 121, +1364655: 1, +1364656: 142, +1364657: 0, +1364658: 103, +1364659: 0, +1364660: 159, +1364661: 26, +1364662: 186, +1364663: 1, +1364664: 241, +1364665: 0, +1364666: 203, +1364667: 0, +1364668: 191, +1364669: 22, +1364670: 251, +1364671: 1, +1364672: 83, +1364673: 1, +1364674: 46, +1364675: 1, +1364676: 191, +1364677: 22, +1364678: 251, +1364679: 1, +1364680: 83, +1364681: 1, +1364682: 46, +1364683: 1, +1364684: 159, +1364685: 26, +1364686: 186, +1364687: 1, +1364688: 241, +1364689: 0, +1364690: 203, +1364691: 0, +1364692: 159, +1364693: 34, +1364694: 121, +1364695: 1, +1364696: 142, +1364697: 0, +1364698: 103, +1364699: 0, +1364700: 159, +1364701: 38, +1364702: 89, +1364703: 1, +1364704: 76, +1364705: 0, +1364706: 4, +1364707: 0, +449387: 0, +449388: 56, +449389: 8, +449390: 1, +449391: 189, +449392: 3, +449393: 5, +449394: 20, +449395: 224, +449396: 59, +449397: 168, +449398: 33, +449399: 159, +449400: 87, +449401: 210, +449402: 74, +449403: 78, +449404: 58, +449405: 187, +449406: 0, +449407: 181, +449408: 2, +449409: 107, +449410: 1, +449411: 82, +449412: 2, +449413: 4, +449414: 17, +449415: 116, +449416: 0, +449417: 13, +449418: 0, +449423: 0, +449424: 0, +449425: 174, +449426: 82, +449427: 189, +449428: 95, +449429: 229, +449430: 101, +449431: 224, +449432: 127, +449433: 136, +449434: 115, +449435: 255, +449436: 127, +449437: 242, +449438: 127, +449439: 238, +449440: 127, +449441: 155, +449442: 86, +449443: 85, +449444: 95, +449445: 11, +449446: 94, +449447: 246, +449448: 83, +449449: 228, +449450: 98, +449451: 84, +449452: 82, +449453: 237, +449454: 81, +449466: 0, +449467: 56, +449468: 8, +449469: 1, +449470: 189, +449471: 3, +449472: 5, +449473: 20, +449474: 224, +449475: 59, +449476: 168, +449477: 33, +449478: 159, +449479: 87, +449480: 210, +449481: 74, +449482: 78, +449483: 58, +449484: 187, +449485: 0, +449486: 181, +449487: 2, +449488: 107, +449489: 1, +449490: 82, +449491: 2, +449492: 4, +449493: 17, +449494: 116, +449495: 0, +449496: 13, +449497: 0, +449502: 0, +449503: 0, +449504: 174, +449505: 82, +449506: 189, +449507: 95, +449508: 229, +449509: 101, +449510: 224, +449511: 127, +449512: 136, +449513: 115, +449514: 255, +449515: 127, +449516: 242, +449517: 127, +449518: 238, +449519: 127, +449520: 155, +449521: 86, +449522: 85, +449523: 95, +449524: 11, +449525: 94, +449526: 246, +449527: 83, +449528: 228, +449529: 98, +449530: 84, +449531: 82, +449532: 237, +449533: 81, +449545: 0, +449546: 56, +449547: 8, +449548: 1, +449549: 189, +449550: 3, +449551: 5, +449552: 20, +449553: 224, +449554: 59, +449555: 168, +449556: 33, +449557: 159, +449558: 87, +449559: 210, +449560: 74, +449561: 78, +449562: 58, +449563: 187, +449564: 0, +449565: 181, +449566: 2, +449567: 107, +449568: 1, +449569: 82, +449570: 2, +449571: 4, +449572: 17, +449573: 116, +449574: 0, +449575: 13, +449576: 0, +449581: 3, +449582: 32, +449583: 110, +449584: 81, +449585: 189, +449586: 75, +449587: 165, +449588: 100, +449589: 224, +449590: 127, +449591: 72, +449592: 114, +449593: 255, +449594: 127, +449595: 114, +449596: 127, +449597: 238, +449598: 126, +449599: 91, +449600: 85, +449601: 181, +449602: 74, +449603: 107, +449604: 73, +449605: 182, +449606: 82, +449607: 164, +449608: 97, +449609: 20, +449610: 81, +449611: 173, +449612: 80, +449624: 0, +449625: 56, +449626: 8, +449627: 1, +449628: 189, +449629: 3, +449630: 5, +449631: 20, +449632: 224, +449633: 59, +449634: 168, +449635: 33, +449636: 159, +449637: 87, +449638: 210, +449639: 74, +449640: 78, +449641: 58, +449642: 187, +449643: 0, +449644: 181, +449645: 2, +449646: 107, +449647: 1, +449648: 82, +449649: 2, +449650: 4, +449651: 17, +449652: 116, +449653: 0, +449654: 13, +449655: 0, +449660: 3, +449661: 32, +449662: 206, +449663: 40, +449664: 189, +449665: 55, +449666: 5, +449667: 60, +449668: 224, +449669: 99, +449670: 168, +449671: 73, +449672: 159, +449673: 127, +449674: 210, +449675: 114, +449676: 78, +449677: 98, +449678: 187, +449679: 40, +449680: 181, +449681: 54, +449682: 107, +449683: 53, +449684: 22, +449685: 42, +449686: 4, +449687: 57, +449688: 116, +449689: 40, +449690: 13, +449691: 40, +449700: 0, +449701: 56, +449702: 8, +449703: 1, +449704: 189, +449705: 3, +449706: 5, +449707: 20, +449708: 224, +449709: 59, +449710: 168, +449711: 33, +449712: 159, +449713: 87, +449714: 210, +449715: 74, +449716: 78, +449717: 58, +449718: 187, +449719: 0, +449720: 181, +449721: 2, +449722: 107, +449723: 1, +449724: 82, +449725: 2, +449726: 4, +449727: 17, +449728: 116, +449729: 0, +449730: 13, +449731: 0, +449745: 0, +449746: 0, +449747: 8, +449748: 1, +449749: 255, +449750: 2, +449751: 5, +449752: 20, +449753: 224, +449754: 59, +449755: 168, +449756: 33, +449757: 159, +449758: 87, +449759: 210, +449760: 74, +449761: 78, +449762: 58, +449763: 187, +449764: 0, +449765: 190, +449766: 1, +449767: 142, +449768: 0, +449769: 82, +449770: 2, +449771: 4, +449772: 17, +449773: 116, +449774: 0, +449775: 13, +449776: 0, +449781: 0, +449782: 0, +449783: 168, +449784: 121, +449785: 159, +449786: 123, +449787: 229, +449788: 101, +449789: 224, +449790: 127, +449791: 136, +449792: 115, +449793: 255, +449794: 127, +449795: 242, +449796: 127, +449797: 238, +449798: 127, +449799: 155, +449800: 86, +449801: 94, +449802: 118, +449803: 46, +449804: 117, +449805: 77, +449806: 82, +449807: 228, +449808: 98, +449809: 84, +449810: 82, +449811: 237, +449812: 81, +449824: 0, +449825: 0, +449826: 8, +449827: 1, +449828: 255, +449829: 2, +449830: 5, +449831: 20, +449832: 224, +449833: 59, +449834: 168, +449835: 33, +449836: 159, +449837: 87, +449838: 210, +449839: 74, +449840: 78, +449841: 58, +449842: 187, +449843: 0, +449844: 190, +449845: 1, +449846: 142, +449847: 0, +449848: 82, +449849: 2, +449850: 4, +449851: 17, +449852: 116, +449853: 0, +449854: 13, +449855: 0, +449860: 0, +449861: 0, +449862: 168, +449863: 121, +449864: 159, +449865: 123, +449866: 229, +449867: 101, +449868: 224, +449869: 127, +449870: 136, +449871: 115, +449872: 255, +449873: 127, +449874: 242, +449875: 127, +449876: 238, +449877: 127, +449878: 155, +449879: 86, +449880: 94, +449881: 118, +449882: 46, +449883: 117, +449884: 77, +449885: 82, +449886: 228, +449887: 98, +449888: 84, +449889: 82, +449890: 237, +449891: 81, +449903: 0, +449904: 0, +449905: 8, +449906: 1, +449907: 255, +449908: 2, +449909: 5, +449910: 20, +449911: 224, +449912: 59, +449913: 168, +449914: 33, +449915: 159, +449916: 87, +449917: 210, +449918: 74, +449919: 78, +449920: 58, +449921: 187, +449922: 0, +449923: 190, +449924: 1, +449925: 142, +449926: 0, +449927: 82, +449928: 2, +449929: 4, +449930: 17, +449931: 116, +449932: 0, +449933: 13, +449934: 0, +449939: 3, +449940: 32, +449941: 8, +449942: 81, +449943: 255, +449944: 82, +449945: 165, +449946: 100, +449947: 224, +449948: 127, +449949: 72, +449950: 114, +449951: 255, +449952: 127, +449953: 114, +449954: 127, +449955: 238, +449956: 126, +449957: 91, +449958: 85, +449959: 190, +449960: 81, +449961: 142, +449962: 80, +449963: 77, +449964: 62, +449965: 164, +449966: 97, +449967: 20, +449968: 81, +449969: 173, +449970: 80, +449982: 0, +449983: 0, +449984: 8, +449985: 1, +449986: 255, +449987: 2, +449988: 5, +449989: 20, +449990: 224, +449991: 59, +449992: 168, +449993: 33, +449994: 159, +449995: 87, +449996: 210, +449997: 74, +449998: 78, +449999: 58, +450000: 187, +450001: 0, +450002: 190, +450003: 1, +450004: 142, +450005: 0, +450006: 82, +450007: 2, +450008: 4, +450009: 17, +450010: 116, +450011: 0, +450012: 13, +450013: 0, +450018: 3, +450019: 32, +450020: 8, +450021: 41, +450022: 255, +450023: 42, +450024: 5, +450025: 60, +450026: 224, +450027: 99, +450028: 168, +450029: 73, +450030: 159, +450031: 127, +450032: 210, +450033: 114, +450034: 78, +450035: 98, +450036: 187, +450037: 40, +450038: 190, +450039: 41, +450040: 142, +450041: 40, +450042: 77, +450043: 42, +450044: 4, +450045: 57, +450046: 116, +450047: 40, +450048: 13, +450049: 40, +450058: 0, +450059: 0, +450060: 8, +450061: 1, +450062: 255, +450063: 2, +450064: 5, +450065: 20, +450066: 224, +450067: 59, +450068: 168, +450069: 33, +450070: 159, +450071: 87, +450072: 210, +450073: 74, +450074: 78, +450075: 58, +450076: 187, +450077: 0, +450078: 190, +450079: 1, +450080: 142, +450081: 0, +450082: 82, +450083: 2, +450084: 4, +450085: 17, +450086: 116, +450087: 0, +450088: 13, +450089: 0, +450103: 0, +450104: 56, +450105: 8, +450106: 1, +450107: 31, +450108: 66, +450109: 5, +450110: 20, +450111: 224, +450112: 59, +450113: 168, +450114: 33, +450115: 159, +450116: 87, +450117: 210, +450118: 74, +450119: 78, +450120: 58, +450121: 187, +450122: 0, +450123: 20, +450124: 89, +450125: 170, +450126: 48, +450127: 116, +450128: 2, +450129: 4, +450130: 17, +450131: 116, +450132: 0, +450133: 13, +450134: 0, +450139: 0, +450140: 0, +450141: 174, +450142: 82, +450143: 255, +450144: 127, +450145: 229, +450146: 101, +450147: 224, +450148: 127, +450149: 136, +450150: 115, +450151: 255, +450152: 127, +450153: 242, +450154: 127, +450155: 238, +450156: 127, +450157: 155, +450158: 86, +450159: 244, +450160: 126, +450161: 138, +450162: 126, +450163: 246, +450164: 83, +450165: 228, +450166: 98, +450167: 84, +450168: 82, +450169: 237, +450170: 81, +450182: 0, +450183: 56, +450184: 8, +450185: 1, +450186: 31, +450187: 66, +450188: 5, +450189: 20, +450190: 224, +450191: 59, +450192: 168, +450193: 33, +450194: 159, +450195: 87, +450196: 210, +450197: 74, +450198: 78, +450199: 58, +450200: 187, +450201: 0, +450202: 20, +450203: 89, +450204: 170, +450205: 48, +450206: 116, +450207: 2, +450208: 4, +450209: 17, +450210: 116, +450211: 0, +450212: 13, +450213: 0, +450218: 0, +450219: 0, +450220: 174, +450221: 82, +450222: 255, +450223: 127, +450224: 229, +450225: 101, +450226: 224, +450227: 127, +450228: 136, +450229: 115, +450230: 255, +450231: 127, +450232: 242, +450233: 127, +450234: 238, +450235: 127, +450236: 155, +450237: 86, +450238: 244, +450239: 126, +450240: 138, +450241: 126, +450242: 246, +450243: 83, +450244: 228, +450245: 98, +450246: 84, +450247: 82, +450248: 237, +450249: 81, +450261: 0, +450262: 56, +450263: 8, +450264: 1, +450265: 31, +450266: 66, +450267: 5, +450268: 20, +450269: 224, +450270: 59, +450271: 168, +450272: 33, +450273: 159, +450274: 87, +450275: 210, +450276: 74, +450277: 78, +450278: 58, +450279: 187, +450280: 0, +450281: 20, +450282: 89, +450283: 170, +450284: 48, +450285: 116, +450286: 2, +450287: 4, +450288: 17, +450289: 116, +450290: 0, +450291: 13, +450292: 0, +450297: 3, +450298: 32, +450299: 110, +450300: 81, +450301: 191, +450302: 126, +450303: 165, +450304: 100, +450305: 224, +450306: 127, +450307: 72, +450308: 114, +450309: 255, +450310: 127, +450311: 114, +450312: 127, +450313: 238, +450314: 126, +450315: 91, +450316: 85, +450317: 180, +450318: 125, +450319: 74, +450320: 125, +450321: 182, +450322: 82, +450323: 164, +450324: 97, +450325: 20, +450326: 81, +450327: 173, +450328: 80, +450340: 0, +450341: 56, +450342: 8, +450343: 1, +450344: 31, +450345: 66, +450346: 5, +450347: 20, +450348: 224, +450349: 59, +450350: 168, +450351: 33, +450352: 159, +450353: 87, +450354: 210, +450355: 74, +450356: 78, +450357: 58, +450358: 187, +450359: 0, +450360: 20, +450361: 89, +450362: 170, +450363: 48, +450364: 116, +450365: 2, +450366: 4, +450367: 17, +450368: 116, +450369: 0, +450370: 13, +450371: 0, +450376: 3, +450377: 32, +450378: 206, +450379: 40, +450380: 31, +450381: 110, +450382: 5, +450383: 60, +450384: 224, +450385: 99, +450386: 168, +450387: 73, +450388: 159, +450389: 127, +450390: 210, +450391: 114, +450392: 78, +450393: 98, +450394: 187, +450395: 40, +450396: 20, +450397: 125, +450398: 170, +450399: 88, +450400: 22, +450401: 42, +450402: 4, +450403: 57, +450404: 116, +450405: 40, +450406: 13, +450407: 40, +450416: 0, +450417: 56, +450418: 8, +450419: 1, +450420: 31, +450421: 66, +450422: 5, +450423: 20, +450424: 224, +450425: 59, +450426: 168, +450427: 33, +450428: 159, +450429: 87, +450430: 210, +450431: 74, +450432: 78, +450433: 58, +450434: 187, +450435: 0, +450436: 20, +450437: 89, +450438: 170, +450439: 48, +450440: 116, +450441: 2, +450442: 4, +450443: 17, +450444: 116, +450445: 0, +450446: 13, +450447: 0, +1246881: 65, +1246882: 8, +1246884: 110, +451138: 255, +451139: 87, +451140: 255, +451141: 43, +451142: 60, +451143: 31, +451144: 120, +451145: 2, +451146: 176, +451147: 1, +451148: 11, +451149: 1, +451150: 135, +451151: 0, +451158: 222, +451159: 91, +451160: 222, +451161: 51, +451162: 26, +451163: 39, +451164: 87, +451165: 14, +451166: 175, +451167: 9, +451168: 10, +451169: 5, +451170: 134, +451171: 4, +451178: 221, +451179: 99, +451180: 156, +451181: 63, +451182: 249, +451183: 46, +451184: 85, +451185: 22, +451186: 142, +451187: 17, +451188: 10, +451189: 13, +451190: 134, +451191: 4, +451198: 188, +451199: 103, +451200: 123, +451201: 71, +451202: 215, +451203: 54, +451204: 52, +451205: 34, +451206: 141, +451207: 25, +451208: 9, +451209: 17, +451210: 133, +451211: 8, +451218: 155, +451219: 111, +451220: 89, +451221: 83, +451222: 214, +451223: 66, +451224: 50, +451225: 42, +451226: 141, +451227: 29, +451228: 233, +451229: 20, +451230: 133, +451231: 8, +451238: 122, +451239: 115, +451240: 56, +451241: 91, +451242: 180, +451243: 74, +451244: 17, +451245: 54, +451246: 140, +451247: 37, +451248: 232, +451249: 24, +451250: 132, +451251: 12, +451258: 121, +451259: 123, +451260: 246, +451261: 102, +451262: 147, +451263: 82, +451264: 15, +451265: 62, +451266: 107, +451267: 45, +451268: 232, +451269: 32, +451270: 132, +451271: 12, +451278: 88, +451279: 127, +451280: 213, +451281: 110, +451282: 113, +451283: 90, +451284: 238, +451285: 73, +451286: 106, +451287: 53, +451288: 231, +451289: 36, +451290: 131, +451291: 16, +1237849: 0, +1237850: 0, +1237851: 87, +1237852: 63, +1237853: 77, +1237854: 46, +1237855: 226, +1237856: 0, +1237857: 96, +1237858: 0, +1237859: 176, +1237860: 58, +1237861: 11, +1237862: 34, +1237863: 102, +1237864: 17, +1237865: 36, +1237866: 9, +1237867: 255, +1237868: 87, +1237869: 181, +1237870: 58, +1237871: 206, +1237872: 29, +1237873: 231, +1237874: 0, +1237875: 255, +1237876: 3, +1237877: 22, +1237878: 2, +1237879: 176, +1237880: 0, +1237881: 0, +1237882: 0, +1237883: 87, +1237884: 63, +1237885: 77, +1237886: 46, +1237887: 226, +1237888: 0, +1237889: 96, +1237890: 0, +1237891: 176, +1237892: 58, +1237893: 11, +1237894: 34, +1237895: 102, +1237896: 17, +1237897: 36, +1237898: 9, +1237899: 255, +1237900: 87, +1237901: 181, +1237902: 58, +1237903: 206, +1237904: 29, +1237905: 231, +1237906: 0, +1237907: 255, +1237908: 3, +1237909: 22, +1237910: 2, +1237911: 176, +1237912: 0, +1237913: 0, +1237914: 56, +1237915: 146, +1237916: 42, +1237917: 204, +1237918: 33, +1237919: 196, +1237920: 0, +1237921: 98, +1237922: 0, +1237923: 14, +1237924: 38, +1237925: 170, +1237926: 21, +1237927: 39, +1237928: 13, +1237929: 229, +1237930: 4, +1237931: 90, +1237932: 71, +1237933: 82, +1237934: 46, +1237935: 140, +1237936: 25, +1237937: 198, +1237938: 0, +1237939: 63, +1237940: 3, +1237941: 182, +1237942: 1, +1237943: 143, +1237944: 0, +1237945: 0, +1237946: 56, +1237947: 239, +1237948: 21, +1237949: 107, +1237950: 21, +1237951: 165, +1237952: 0, +1237953: 99, +1237954: 0, +1237955: 172, +1237956: 21, +1237957: 73, +1237958: 13, +1237959: 7, +1237960: 9, +1237961: 198, +1237962: 4, +1237963: 214, +1237964: 54, +1237965: 208, +1237966: 33, +1237967: 75, +1237968: 17, +1237969: 166, +1237970: 0, +1237971: 95, +1237972: 2, +1237973: 55, +1237974: 1, +1237975: 141, +1237976: 0, +1500232: 0, +1500233: 0, +1500234: 127, +1500235: 4, +1500236: 121, +1500237: 4, +1500238: 51, +1500239: 0, +1500240: 45, +1500241: 0, +1500242: 59, +1500243: 58, +1500244: 183, +1500245: 45, +1500246: 51, +1500247: 29, +1500248: 142, +1500249: 12, +1500250: 191, +1500251: 37, +1500252: 91, +1500253: 29, +1500254: 247, +1500255: 20, +1500256: 178, +1500257: 12, +1500258: 110, +1500259: 8, +1500260: 191, +1500261: 86, +451686: 16, +1500263: 0, +1500264: 80, +1500265: 4, +1500266: 77, +1500267: 4, +451692: 5, +1500269: 0, +1500270: 39, +1500271: 0, +1500272: 46, +1500273: 29, +1500274: 236, +1500275: 24, +451700: 210, +1500277: 16, +1500278: 71, +451703: 58, +451704: 187, +1500281: 20, +1500282: 174, +1500283: 16, +1500284: 140, +451709: 1, +451710: 82, +1500287: 8, +451712: 4, +1500289: 4, +1500290: 112, +1500291: 45, +1500292: 5, +1500293: 0, +1500294: 31, +1500295: 5, +451720: 4, +1500297: 4, +1500298: 211, +1500299: 0, +1500300: 173, +451725: 3, +1500302: 187, +1500303: 58, +451728: 225, +451729: 59, +451730: 202, +451731: 37, +451732: 159, +451733: 87, +451734: 211, +451735: 74, +451736: 79, +451737: 58, +451738: 188, +451739: 0, +451740: 182, +451741: 2, +451742: 108, +451743: 1, +451744: 83, +451745: 2, +451746: 5, +451747: 17, +451748: 117, +451749: 0, +451750: 14, +451751: 0, +1500328: 106, +1500329: 0, +451754: 4, +451755: 0, +451756: 9, +451757: 1, +451758: 189, +451759: 3, +1500336: 234, +451761: 20, +451762: 225, +451763: 59, +451764: 202, +451765: 37, +451766: 159, +451767: 87, +451768: 211, +451769: 74, +451770: 79, +451771: 58, +451772: 188, +451773: 0, +451774: 182, +451775: 2, +451776: 108, +451777: 1, +451778: 83, +451779: 2, +451780: 5, +451781: 17, +451782: 117, +451783: 0, +451784: 14, +451785: 0, +1500362: 123, +1500363: 59, +451788: 5, +451789: 0, +451790: 10, +451791: 1, +451792: 190, +451793: 3, +451794: 7, +451795: 20, +451796: 226, +451797: 59, +451798: 204, +451799: 37, +451800: 159, +451801: 87, +451802: 212, +451803: 74, +451804: 80, +451805: 58, +451806: 188, +451807: 0, +451808: 183, +451809: 2, +451810: 109, +451811: 1, +451812: 84, +451813: 2, +451814: 6, +451815: 17, +451816: 118, +451817: 0, +451818: 15, +451819: 0, +1500396: 74, +1500397: 17, +451822: 6, +451823: 0, +451824: 10, +451825: 1, +451826: 190, +451827: 3, +451828: 7, +451829: 20, +451830: 226, +451831: 59, +451832: 204, +451833: 37, +451834: 159, +451835: 87, +451836: 212, +451837: 74, +451838: 80, +451839: 58, +451840: 188, +451841: 0, +451842: 183, +451843: 2, +451844: 109, +451845: 1, +451846: 84, +451847: 2, +451848: 6, +451849: 17, +451850: 118, +451851: 0, +451852: 15, +451853: 0, +1500430: 245, +1500431: 38, +451856: 7, +451857: 0, +451858: 11, +451859: 1, +451860: 190, +451861: 3, +451862: 8, +451863: 20, +451864: 227, +451865: 59, +451866: 238, +451867: 41, +451868: 159, +451869: 87, +451870: 213, +451871: 74, +451872: 81, +451873: 58, +451874: 189, +451875: 0, +451876: 184, +451877: 2, +451878: 110, +451879: 1, +1500456: 69, +451881: 2, +451882: 7, +451883: 17, +451884: 119, +451885: 0, +451886: 16, +451887: 0, +1500464: 39, +1500465: 13, +451890: 8, +451891: 0, +451892: 11, +1500469: 4, +451894: 190, +451895: 3, +451896: 8, +1500473: 0, +451898: 227, +1500475: 13, +451900: 238, +451901: 41, +451902: 159, +1500479: 9, +451904: 213, +451905: 74, +451906: 81, +451907: 58, +1500484: 173, +451909: 0, +1500486: 41, +451911: 2, +451912: 110, +451913: 1, +451914: 85, +451915: 2, +451916: 7, +451917: 17, +451918: 119, +451919: 0, +451920: 16, +451921: 0, +1500498: 100, +1500499: 17, +451924: 8, +1500501: 95, +451926: 13, +451927: 1, +451928: 190, +451929: 3, +451930: 10, +451931: 20, +451932: 229, +451933: 59, +451934: 15, +1500511: 4, +451936: 159, +451937: 87, +451938: 215, +451939: 74, +1500516: 37, +451941: 58, +451942: 189, +451943: 0, +451944: 186, +451945: 2, +451946: 112, +451947: 1, +451948: 87, +451949: 2, +451950: 9, +451951: 17, +451952: 121, +451953: 0, +451954: 18, +451955: 0, +1500532: 128, +1500533: 4, +451958: 8, +451959: 0, +451960: 13, +451961: 1, +451962: 190, +451963: 3, +451964: 10, +451965: 20, +451966: 229, +451967: 59, +451968: 15, +451969: 46, +451970: 159, +451971: 87, +451972: 215, +451973: 74, +451974: 83, +451975: 58, +451976: 189, +451977: 0, +451978: 186, +451979: 2, +451980: 112, +451981: 1, +451982: 87, +451983: 2, +451984: 9, +1500561: 107, +451986: 121, +451987: 0, +451988: 18, +451989: 0, +1500566: 136, +1500567: 12, +451992: 8, +451993: 0, +451994: 11, +1500571: 12, +451996: 190, +451997: 3, +451998: 8, +451999: 20, +1500576: 229, +452001: 59, +452002: 238, +452003: 41, +1500580: 43, +452005: 87, +452006: 213, +452007: 74, +452008: 81, +452009: 58, +452010: 189, +452011: 0, +452012: 184, +452013: 2, +452014: 110, +452015: 1, +452016: 85, +452017: 2, +452018: 7, +452019: 17, +452020: 119, +452021: 0, +452022: 16, +452023: 0, +1500600: 35, +1500601: 40, +452026: 7, +452027: 0, +452028: 11, +452029: 1, +1500606: 41, +452031: 3, +452032: 8, +452033: 20, +452034: 227, +452035: 59, +452036: 238, +452037: 41, +452038: 159, +452039: 87, +452040: 213, +452041: 74, +452042: 81, +452043: 58, +452044: 189, +452045: 0, +452046: 184, +452047: 2, +452048: 110, +452049: 1, +452050: 85, +452051: 2, +452052: 7, +452053: 17, +452054: 119, +452055: 0, +452056: 16, +452057: 0, +1500634: 231, +1500635: 44, +1500636: 165, +452061: 0, +452062: 10, +452063: 1, +452064: 190, +452065: 3, +452066: 7, +452067: 20, +452068: 226, +452069: 59, +452070: 204, +452071: 37, +452072: 159, +452073: 87, +452074: 212, +452075: 74, +452076: 80, +452077: 58, +452078: 188, +452079: 0, +452080: 183, +452081: 2, +452082: 109, +452083: 1, +452084: 84, +452085: 2, +452086: 6, +452087: 17, +452088: 118, +452089: 0, +452090: 15, +452091: 0, +1500668: 137, +1500669: 56, +452094: 5, +452095: 0, +452096: 10, +452097: 1, +452098: 190, +452099: 3, +452100: 7, +452101: 20, +452102: 226, +452103: 59, +452104: 204, +452105: 37, +452106: 159, +452107: 87, +452108: 212, +452109: 74, +452110: 80, +452111: 58, +452112: 188, +452113: 0, +452114: 183, +452115: 2, +1500692: 43, +1500693: 49, +452118: 84, +1500695: 44, +452120: 6, +452121: 17, +452122: 118, +452123: 0, +452124: 15, +452125: 0, +1500702: 171, +1500703: 36, +452128: 4, +452129: 0, +452130: 9, +452131: 1, +452132: 189, +1500709: 24, +452134: 6, +452135: 20, +452136: 225, +452137: 59, +1500714: 127, +452139: 37, +452140: 159, +1500717: 44, +452142: 211, +452143: 74, +452144: 79, +452145: 58, +452146: 188, +452147: 0, +452148: 182, +452149: 2, +1500726: 51, +1500727: 73, +1500728: 142, +1500729: 56, +1500730: 191, +1500731: 77, +1500732: 91, +452157: 0, +1500734: 247, +1500735: 64, +1500736: 178, +1500737: 56, +452162: 4, +452163: 0, +452164: 9, +452165: 1, +452166: 189, +452167: 3, +452168: 6, +452169: 20, +452170: 225, +452171: 59, +452172: 202, +452173: 37, +452174: 159, +452175: 87, +452176: 211, +452177: 74, +452178: 79, +452179: 58, +452180: 188, +452181: 0, +452182: 182, +452183: 2, +452184: 108, +452185: 1, +452186: 83, +452187: 2, +452188: 5, +452189: 17, +452190: 117, +452191: 0, +452192: 14, +452193: 0, +1500770: 112, +1500771: 65, +1500772: 5, +452197: 0, +452198: 9, +452199: 1, +452200: 189, +452201: 3, +1500778: 51, +1500779: 20, +452204: 225, +452205: 59, +452206: 202, +452207: 37, +452208: 159, +1500785: 65, +452210: 211, +452211: 74, +452212: 79, +452213: 58, +452214: 188, +452215: 0, +452216: 182, +452217: 2, +452218: 108, +452219: 1, +452220: 83, +452221: 2, +452222: 5, +452223: 17, +452224: 117, +452225: 0, +452226: 14, +452227: 0, +1500804: 80, +1500805: 12, +1500806: 77, +1500807: 12, +1500808: 42, +1500809: 12, +1500810: 39, +1500811: 12, +1500812: 46, +1500813: 41, +1500814: 236, +1500815: 32, +1500816: 170, +1500817: 24, +452242: 16, +1500819: 16, +452244: 8, +452245: 1, +452246: 255, +1500823: 24, +1500824: 140, +452249: 20, +452250: 224, +452251: 59, +452252: 168, +452253: 33, +452254: 159, +452255: 87, +452256: 210, +452257: 74, +452258: 78, +452259: 58, +452260: 187, +452261: 0, +452262: 190, +452263: 1, +452264: 142, +452265: 0, +452266: 82, +452267: 2, +452268: 4, +452269: 17, +452270: 116, +1500847: 1, +452272: 13, +452273: 0, +1500850: 4, +1500851: 0, +452276: 4, +452277: 0, +452278: 9, +452279: 1, +452280: 255, +452281: 2, +452282: 6, +452283: 20, +452284: 225, +452285: 59, +452286: 202, +1500863: 41, +1500864: 115, +1500865: 33, +1500866: 12, +1500867: 21, +452292: 79, +452293: 58, +452294: 188, +452295: 0, +452296: 190, +452297: 1, +452298: 143, +452299: 0, +452300: 83, +452301: 2, +452302: 5, +452303: 17, +452304: 117, +1500881: 0, +1500882: 249, +452307: 0, +1500884: 51, +1500885: 62, +452310: 4, +452311: 0, +452312: 9, +452313: 1, +452314: 255, +452315: 2, +452316: 6, +452317: 20, +1500894: 83, +452319: 59, +452320: 202, +452321: 37, +452322: 159, +452323: 87, +452324: 211, +452325: 74, +452326: 79, +452327: 58, +452328: 188, +452329: 0, +452330: 190, +1500907: 1, +452332: 143, +1500909: 0, +452334: 83, +452335: 2, +452336: 5, +452337: 17, +452338: 117, +452339: 0, +452340: 14, +452341: 0, +1500918: 139, +1500919: 16, +452344: 5, +452345: 0, +452346: 10, +452347: 1, +452348: 255, +452349: 2, +1500926: 207, +1500927: 16, +1500928: 107, +1500929: 8, +452354: 204, +452355: 37, +452356: 159, +452357: 87, +452358: 212, +452359: 74, +1500936: 219, +452361: 58, +452362: 188, +452363: 0, +452364: 190, +1500941: 0, +452366: 144, +452367: 0, +452368: 84, +452369: 2, +1500946: 244, +452371: 17, +1500948: 112, +1500949: 12, +1500950: 127, +452375: 0, +1500952: 59, +1500953: 25, +1500954: 215, +1500955: 16, +452380: 10, +452381: 1, +452382: 255, +452383: 2, +452384: 7, +452385: 20, +452386: 226, +452387: 59, +452388: 204, +452389: 37, +452390: 159, +452391: 87, +452392: 212, +452393: 74, +452394: 80, +452395: 58, +452396: 188, +452397: 0, +452398: 190, +452399: 1, +452400: 144, +452401: 0, +452402: 84, +452403: 2, +452404: 6, +452405: 17, +452406: 118, +452407: 0, +452408: 15, +452409: 0, +1500986: 147, +1500987: 12, +452412: 7, +452413: 0, +452414: 11, +1500991: 74, +452416: 255, +452417: 2, +452418: 8, +452419: 20, +452420: 227, +452421: 59, +452422: 238, +452423: 41, +452424: 159, +452425: 87, +452426: 213, +452427: 74, +452428: 81, +452429: 58, +1501006: 0, +452431: 0, +452432: 190, +452433: 1, +452434: 145, +452435: 0, +452436: 85, +452437: 2, +452438: 7, +452439: 17, +452440: 119, +452441: 0, +452442: 16, +452443: 0, +1501020: 0, +1501021: 0, +452446: 8, +452447: 0, +452448: 11, +452449: 1, +452450: 255, +452451: 2, +452452: 8, +452453: 20, +452454: 227, +452455: 59, +452456: 238, +452457: 41, +452458: 159, +1501035: 0, +452460: 213, +452461: 74, +452462: 81, +1501039: 0, +452464: 189, +452465: 0, +1501042: 111, +452467: 1, +452468: 145, +452469: 0, +452470: 85, +452471: 2, +452472: 7, +452473: 17, +452474: 119, +452475: 0, +452476: 16, +452477: 0, +1501054: 4, +1501055: 0, +452480: 8, +452481: 0, +452482: 13, +452483: 1, +452484: 255, +452485: 2, +452486: 10, +452487: 20, +452488: 229, +452489: 59, +452490: 15, +452491: 46, +452492: 159, +452493: 87, +452494: 215, +452495: 74, +452496: 83, +452497: 58, +452498: 189, +452499: 0, +452500: 191, +452501: 1, +452502: 147, +1501079: 8, +1501080: 8, +452505: 2, +1501082: 8, +1501083: 0, +452508: 121, +1501085: 0, +452510: 18, +452511: 0, +1501088: 8, +1501089: 0, +452514: 8, +452515: 0, +452516: 13, +452517: 1, +452518: 255, +452519: 2, +452520: 10, +452521: 20, +452522: 229, +452523: 59, +452524: 15, +452525: 46, +1501102: 245, +1501103: 20, +452528: 215, +452529: 74, +452530: 83, +452531: 58, +452532: 189, +1501109: 4, +452534: 191, +452535: 1, +452536: 147, +452537: 0, +452538: 87, +452539: 2, +452540: 9, +452541: 17, +452542: 121, +452543: 0, +452544: 18, +452545: 0, +1501122: 14, +1501123: 0, +452548: 8, +452549: 0, +452550: 11, +452551: 1, +452552: 255, +452553: 2, +452554: 8, +452555: 20, +452556: 227, +452557: 59, +452558: 238, +452559: 41, +452560: 159, +452561: 87, +452562: 213, +452563: 74, +452564: 81, +452565: 58, +452566: 189, +452567: 0, +452568: 190, +452569: 1, +452570: 145, +452571: 0, +1501148: 14, +452573: 2, +1501150: 245, +452575: 17, +452576: 119, +452577: 0, +452578: 16, +452579: 0, +452582: 7, +452583: 0, +452584: 11, +452585: 1, +452586: 255, +452587: 2, +452588: 8, +452589: 20, +452590: 227, +452591: 59, +452592: 238, +452593: 41, +452594: 159, +1501171: 87, +1501172: 17, +1501173: 11, +452598: 81, +452599: 58, +452600: 189, +1501177: 0, +452602: 190, +452603: 1, +452604: 145, +452605: 0, +452606: 85, +452607: 2, +452608: 7, +452609: 17, +452610: 119, +1501187: 127, +452612: 16, +452613: 0, +1501190: 18, +1501191: 11, +452616: 6, +452617: 0, +452618: 10, +1501195: 0, +452620: 255, +452621: 2, +1501198: 183, +1501199: 58, +452624: 226, +452625: 59, +452626: 204, +452627: 37, +452628: 159, +452629: 87, +452630: 212, +452631: 74, +452632: 80, +452633: 58, +452634: 188, +452635: 0, +452636: 190, +452637: 1, +452638: 144, +1501215: 71, +1501216: 150, +1501217: 58, +452642: 6, +452643: 17, +1501220: 172, +452645: 0, +452646: 15, +1501223: 115, +1501224: 185, +1501225: 63, +1501226: 243, +1501227: 10, +1501228: 9, +1501229: 14, +1501230: 229, +452655: 2, +452656: 7, +452657: 20, +452658: 226, +452659: 59, +452660: 204, +452661: 37, +1501238: 172, +452663: 87, +452664: 212, +452665: 74, +452666: 80, +452667: 58, +452668: 188, +452669: 0, +452670: 190, +452671: 1, +452672: 144, +452673: 0, +452674: 84, +452675: 2, +1501252: 84, +1501253: 50, +1501254: 175, +452679: 0, +452680: 15, +1501257: 4, +1501258: 222, +1501259: 103, +452684: 4, +452685: 0, +452686: 9, +452687: 1, +452688: 255, +452689: 2, +452690: 6, +452691: 20, +452692: 225, +452693: 59, +452694: 202, +1501271: 46, +452696: 159, +1501273: 33, +452698: 211, +452699: 74, +452700: 79, +452701: 58, +1501278: 154, +1501279: 35, +452704: 190, +1501281: 6, +452706: 143, +1501283: 9, +452708: 83, +452709: 2, +1501286: 151, +1501287: 58, +1501288: 18, +1501289: 46, +452714: 14, +452715: 0, +1501292: 138, +1501293: 4, +452718: 4, +1501295: 91, +452720: 9, +452721: 1, +1501298: 213, +452723: 2, +1501300: 204, +1501301: 5, +452726: 225, +1501303: 0, +452728: 202, +1501305: 54, +452730: 159, +452731: 87, +1501308: 109, +452733: 74, +452734: 79, +452735: 58, +452736: 188, +452737: 0, +452738: 190, +452739: 1, +452740: 143, +452741: 0, +452742: 83, +452743: 2, +452744: 5, +452745: 17, +452746: 117, +452747: 0, +452748: 14, +452749: 0, +452752: 16, +452753: 0, +1501330: 255, +452755: 1, +452756: 255, +452757: 2, +452758: 5, +452759: 20, +1501336: 175, +1501337: 28, +452762: 168, +1501339: 24, +452764: 159, +452765: 87, +452766: 210, +1501343: 44, +452768: 78, +1501345: 40, +452770: 187, +1501347: 32, +1501348: 168, +1501349: 24, +1501350: 155, +1501351: 106, +452776: 82, +1501353: 48, +1501354: 150, +1501355: 40, +1501356: 141, +452781: 0, +1501358: 136, +1501359: 20, +1501360: 89, +1501361: 102, +1501362: 185, +1501363: 48, +1501364: 148, +1501365: 44, +1501366: 140, +1501367: 36, +1501368: 135, +1501369: 20, +1501370: 54, +1501371: 102, +1501372: 150, +1501373: 52, +1501374: 146, +1501375: 44, +1501376: 139, +1501377: 40, +1501378: 102, +1501379: 16, +1501380: 244, +1501381: 97, +1501382: 148, +1501383: 52, +1501384: 144, +1501385: 48, +1501386: 138, +1501387: 44, +1501388: 101, +1501389: 16, +1501390: 210, +1501391: 93, +1501392: 114, +1501393: 56, +1501394: 111, +1501395: 48, +1501396: 105, +1501397: 44, +1501398: 69, +1501399: 12, +1501400: 144, +1501401: 89, +1501402: 112, +1501403: 56, +1501404: 109, +1501405: 52, +1501406: 104, +1501407: 48, +1501408: 68, +1501409: 12, +452834: 9, +452835: 1, +452836: 31, +452837: 66, +452838: 6, +452839: 20, +452840: 225, +452841: 59, +452842: 202, +452843: 37, +452844: 159, +452845: 87, +452846: 211, +452847: 74, +1501424: 22, +1501425: 23, +1501426: 82, +1501427: 2, +1501428: 138, +1501429: 1, +1501430: 198, +1501431: 0, +1501432: 77, +1501433: 73, +1501434: 77, +1501435: 48, +1501436: 75, +1501437: 44, +1501438: 70, +1501439: 40, +1501440: 35, +1501441: 8, +1501442: 18, +1501443: 46, +1501444: 142, +1501445: 33, +1501446: 43, +1501447: 25, +1501448: 104, +1501449: 0, +1501450: 57, +1501451: 75, +1501452: 146, +1501453: 18, +1501454: 239, +1501455: 1, +1501456: 72, +1501457: 1, +1501458: 165, +1501459: 0, +1501460: 11, +1501461: 61, +1501462: 75, +1501463: 40, +1501464: 73, +1501465: 36, +1501466: 69, +1501467: 32, +1501468: 34, +1501469: 8, +1501470: 175, +1501471: 37, +1501472: 76, +1501473: 29, +1501474: 233, +1501475: 20, +1501476: 71, +1501477: 0, +1501478: 181, +1501479: 62, +1501480: 14, +1501481: 14, +1501482: 140, +1501483: 1, +1501484: 6, +1501485: 1, +1501486: 132, +1501487: 0, +1501488: 201, +1501489: 48, +1501490: 41, +1501491: 32, +1501492: 39, +1501493: 28, +1501494: 36, +1501495: 24, +1501496: 34, +1501497: 4, +1501498: 76, +1501499: 29, +1501500: 9, +1501501: 21, +1501502: 199, +1501503: 16, +1501504: 69, +1501505: 0, +1501506: 49, +1501507: 50, +1501508: 139, +1501509: 9, +1501510: 41, +1501511: 1, +1501512: 197, +1501513: 0, +1501514: 99, +1501515: 0, +1501516: 166, +1501517: 36, +1501518: 38, +1501519: 24, +1501520: 37, +1501521: 20, +1501522: 35, +1501523: 20, +1501524: 1, +1501525: 4, +1501526: 9, +1501527: 21, +1501528: 199, +1501529: 16, +1501530: 133, +1501531: 12, +1501532: 36, +1501533: 0, +1501534: 140, +1501535: 37, +1501536: 7, +1501537: 5, +1501538: 198, +1501539: 0, +1501540: 131, +1501541: 0, +1501542: 66, +1501543: 0, +1501544: 100, +1501545: 24, +1501546: 4, +1501547: 16, +1501548: 3, +1501549: 12, +1501550: 2, +1501551: 12, +1501552: 1, +1501553: 0, +1501554: 166, +1501555: 12, +1501556: 132, +1501557: 8, +1501558: 99, +1501559: 8, +1501560: 34, +1501561: 0, +1501562: 8, +1501563: 25, +1501564: 131, +1501565: 0, +1501566: 99, +1501567: 0, +1501568: 65, +1501569: 0, +1501570: 33, +1501571: 0, +1501572: 34, +1501573: 12, +1501574: 2, +1501575: 8, +1501576: 1, +1501577: 4, +1501578: 1, +1501579: 4, +1501580: 0, +1501581: 0, +1501582: 67, +1501583: 4, +1501584: 66, +1501585: 4, +1501586: 33, +1501587: 4, +1501588: 1, +1501589: 0, +1501590: 132, +1501591: 12, +1501592: 0, +1501593: 0, +1501594: 0, +1501595: 0, +1501596: 0, +1501597: 0, +1501598: 0, +1501599: 0, +1501600: 0, +1501601: 0, +1501602: 0, +1501603: 0, +1501604: 0, +1501605: 0, +1501606: 0, +1501607: 0, +1501608: 0, +1501609: 0, +1501610: 0, +1501611: 0, +1501612: 0, +1501613: 0, +1501614: 0, +1501615: 0, +1501616: 0, +1501617: 0, +1501618: 0, +1501619: 0, +453044: 229, +453045: 59, +453046: 15, +453047: 46, +453048: 159, +453049: 87, +453050: 215, +453051: 74, +453052: 83, +453053: 58, +453054: 189, +453055: 0, +453056: 25, +453057: 89, +453058: 175, +453059: 48, +453060: 87, +453061: 2, +453062: 9, +453063: 17, +453064: 121, +453065: 0, +453066: 18, +453067: 0, +453070: 8, +453071: 0, +453072: 13, +453073: 1, +453074: 31, +453075: 66, +453076: 10, +453077: 20, +453078: 229, +453079: 59, +453080: 15, +453081: 46, +453082: 159, +453083: 87, +453084: 215, +453085: 74, +453086: 83, +453087: 58, +453088: 189, +453089: 0, +453090: 25, +453091: 89, +453092: 175, +453093: 48, +453094: 87, +453095: 2, +453096: 9, +453097: 17, +453098: 121, +453099: 0, +453100: 18, +453101: 0, +453104: 8, +453105: 0, +453106: 11, +453107: 1, +453108: 31, +453109: 66, +453110: 8, +453111: 20, +453112: 227, +453113: 59, +453114: 238, +453115: 41, +453116: 159, +453117: 87, +453118: 213, +453119: 74, +453120: 81, +453121: 58, +453122: 189, +453123: 0, +453124: 23, +453125: 89, +453126: 173, +453127: 48, +1501704: 0, +1501705: 0, +1501706: 159, +1501707: 21, +1501708: 219, +1501709: 0, +1501710: 51, +1501711: 0, +1501712: 14, +1501713: 0, +1501714: 251, +1501715: 49, +1501716: 119, +1501717: 37, +1501718: 244, +1501719: 24, +1501720: 112, +1501721: 12, +1501722: 127, +1501723: 29, +1501724: 59, +1501725: 25, +1501726: 215, +1501727: 16, +1501728: 147, +1501729: 12, +1501730: 80, +1501731: 4, +1501732: 95, +1501733: 74, +1501734: 14, +1501735: 0, +1501736: 14, +1501737: 0, +1501738: 14, +1501739: 0, +1501740: 16, +1501741: 0, +1501742: 245, +1501743: 20, +1501744: 179, +1501745: 16, +1501746: 114, +1501747: 8, +1501748: 48, +1501749: 4, +1501750: 14, +1501751: 0, +1501752: 14, +1501753: 0, +1501754: 14, +1501755: 0, +1501756: 14, +1501757: 0, +1501758: 14, +1501759: 0, +1501760: 245, +1501761: 20, +1501762: 125, +1501763: 17, +1501764: 185, +1501765: 0, +1501766: 49, +1501767: 0, +1501768: 13, +1501769: 0, +1501770: 217, +1501771: 45, +1501772: 85, +1501773: 33, +1501774: 210, +1501775: 20, +1501776: 79, +1501777: 8, +1501778: 93, +1501779: 25, +1501780: 25, +1501781: 21, +1501782: 181, +1501783: 12, +1501784: 113, +1501785: 8, +1501786: 47, +1501787: 4, +1501788: 29, +1501789: 66, +1501790: 13, +1501791: 0, +1501792: 13, +1501793: 0, +1501794: 13, +1501795: 0, +1501796: 15, +1501797: 0, +1501798: 211, +1501799: 16, +1501800: 145, +1501801: 12, +1501802: 80, +1501803: 4, +1501804: 47, +1501805: 4, +1501806: 13, +1501807: 0, +1501808: 13, +1501809: 0, +1501810: 13, +1501811: 0, +1501812: 13, +1501813: 0, +1501814: 13, +1501815: 0, +1501816: 211, +1501817: 16, +1501818: 90, +1501819: 17, +1501820: 183, +1501821: 0, +1501822: 16, +1501823: 0, +1501824: 12, +1501825: 0, +1501826: 183, +1501827: 41, +1501828: 52, +1501829: 29, +1501830: 209, +1501831: 20, +1501832: 77, +1501833: 8, +1501834: 58, +1501835: 25, +1501836: 247, +1501837: 20, +1501838: 180, +1501839: 12, +1501840: 112, +1501841: 8, +1501842: 45, +1501843: 0, +1501844: 250, +1501845: 61, +1501846: 12, +1501847: 0, +1501848: 12, +1501849: 0, +1501850: 12, +1501851: 0, +1501852: 13, +1501853: 0, +1501854: 210, +1501855: 16, +1501856: 144, +1501857: 12, +1501858: 79, +1501859: 4, +1501860: 13, +1501861: 0, +1501862: 12, +1501863: 0, +1501864: 12, +1501865: 0, +1501866: 12, +1501867: 0, +1501868: 12, +1501869: 0, +1501870: 12, +1501871: 0, +1501872: 210, +1501873: 16, +1501874: 56, +1501875: 17, +1501876: 149, +1501877: 0, +1501878: 15, +1501879: 0, +1501880: 11, +1501881: 0, +1501882: 149, +1501883: 37, +1501884: 18, +1501885: 29, +1501886: 176, +1501887: 16, +1501888: 76, +1501889: 8, +1501890: 24, +1501891: 21, +1501892: 245, +1501893: 16, +1501894: 146, +1501895: 12, +1501896: 111, +1501897: 8, +1501898: 44, +1501899: 0, +1501900: 216, +1501901: 57, +1501902: 11, +1501903: 0, +1501904: 11, +1501905: 0, +1501906: 11, +1501907: 0, +1501908: 12, +1501909: 0, +1501910: 176, +1501911: 16, +1501912: 143, +1501913: 12, +1501914: 78, +1501915: 4, +1501916: 12, +1501917: 0, +1501918: 11, +1501919: 0, +1501920: 11, +1501921: 0, +1501922: 11, +1501923: 0, +1501924: 11, +1501925: 0, +1501926: 11, +1501927: 0, +1501928: 176, +1501929: 16, +1501930: 22, +1501931: 13, +1501932: 147, +1501933: 0, +1501934: 14, +1501935: 0, +1501936: 10, +1501937: 0, +1501938: 115, +1501939: 33, +1501940: 16, +1501941: 25, +1501942: 174, +1501943: 16, +1501944: 75, +1501945: 8, +1501946: 22, +1501947: 21, +1501948: 211, +1501949: 16, +1501950: 144, +1501951: 12, +1501952: 110, +1501953: 8, +1501954: 43, +1501955: 0, +1501956: 182, +1501957: 53, +1501958: 10, +1501959: 0, +1501960: 10, +1501961: 0, +1501962: 10, +1501963: 0, +1501964: 11, +1501965: 0, +1501966: 175, +1501967: 12, +1501968: 110, +1501969: 12, +1501970: 77, +1501971: 4, +1501972: 11, +1501973: 0, +1501974: 10, +1501975: 0, +1501976: 10, +1501977: 0, +1501978: 10, +1501979: 0, +1501980: 10, +1501981: 0, +1501982: 10, +1501983: 0, +1501984: 175, +1501985: 12, +1501986: 20, +1501987: 13, +1501988: 146, +1501989: 0, +1501990: 12, +1501991: 0, +1501992: 9, +1501993: 0, +1501994: 82, +1501995: 33, +1501996: 239, +1501997: 24, +1501998: 141, +1501999: 16, +1502000: 74, +1502001: 8, +1502002: 244, +1502003: 16, +1502004: 210, +1502005: 16, +1502006: 143, +1502007: 8, +1502008: 76, +1502009: 8, +1502010: 42, +1502011: 0, +1502012: 148, +1502013: 49, +1502014: 9, +1502015: 0, +1502016: 9, +1502017: 0, +1502018: 9, +1502019: 0, +1502020: 10, +1502021: 0, +1502022: 142, +1502023: 12, +1502024: 108, +1502025: 8, +1502026: 76, +1502027: 4, +1502028: 10, +1502029: 0, +1502030: 9, +1502031: 0, +1502032: 9, +1502033: 0, +1502034: 9, +1502035: 0, +1502036: 9, +1502037: 0, +1502038: 9, +1502039: 0, +1502040: 142, +1502041: 12, +1502042: 242, +1502043: 12, +1502044: 112, +1502045: 0, +1502046: 11, +1502047: 0, +1502048: 8, +1502049: 0, +1502050: 48, +1502051: 29, +1502052: 205, +1502053: 20, +1502054: 140, +1502055: 12, +1502056: 41, +1502057: 4, +1502058: 210, +1502059: 16, +1502060: 176, +1502061: 12, +1502062: 109, +1502063: 8, +1502064: 75, +1502065: 4, +1502066: 41, +1502067: 0, +1502068: 82, +1502069: 41, +1502070: 8, +1502071: 0, +1502072: 8, +1502073: 0, +1502074: 8, +1502075: 0, +1502076: 9, +1502077: 0, +1502078: 140, +1502079: 12, +1502080: 107, +1502081: 8, +1502082: 42, +1502083: 4, +1502084: 9, +1502085: 0, +1502086: 8, +1502087: 0, +1502088: 8, +1502089: 0, +1502090: 8, +1502091: 0, +1502092: 8, +1502093: 0, +1502094: 8, +1502095: 0, +1502096: 140, +1502097: 12, +1502098: 208, +1502099: 8, +1502100: 110, +1502101: 0, +1502102: 10, +1502103: 0, +1502104: 7, +1502105: 0, +1502106: 14, +1502107: 25, +1502108: 172, +1502109: 16, +1502110: 106, +1502111: 12, +1502112: 40, +1502113: 4, +1502114: 176, +1502115: 12, +1502116: 142, +1502117: 12, +1502118: 108, +1502119: 8, +1502120: 74, +1502121: 4, +1502122: 40, +1502123: 0, +1502124: 48, +1502125: 37, +1502126: 7, +1502127: 0, +1502128: 7, +1502129: 0, +1502130: 7, +1502131: 0, +1502132: 8, +1502133: 0, +1502134: 107, +1502135: 8, +1502136: 74, +1502137: 8, +1502138: 41, +1502139: 4, +1502140: 8, +1502141: 0, +1502142: 7, +1502143: 0, +1502144: 7, +1502145: 0, +1502146: 7, +1502147: 0, +1502148: 7, +1502149: 0, +1502150: 7, +1502151: 0, +1502152: 107, +1502153: 8, +1502154: 174, +1502155: 8, +1502156: 76, +1502157: 0, +1502158: 8, +1502159: 0, +1502160: 6, +1502161: 0, +1502162: 236, +1502163: 20, +1502164: 170, +1502165: 16, +1502166: 105, +1502167: 8, +1502168: 39, +1502169: 4, +1502170: 174, +1502171: 12, +1502172: 140, +1502173: 8, +1502174: 74, +1502175: 4, +1502176: 40, +1502177: 4, +1502178: 39, +1502179: 0, +1502180: 14, +1502181: 33, +1502182: 6, +1502183: 0, +1502184: 6, +1502185: 0, +1502186: 6, +1502187: 0, +1502188: 7, +1502189: 0, +1502190: 105, +1502191: 8, +1502192: 72, +1502193: 4, +1502194: 40, +1502195: 4, +1502196: 7, +1502197: 0, +1502198: 6, +1502199: 0, +1502200: 6, +1502201: 0, +1502202: 6, +1502203: 0, +1502204: 6, +1502205: 0, +1502206: 6, +1502207: 0, +1502208: 105, +1502209: 8, +1502210: 140, +1502211: 8, +1502212: 74, +1502213: 0, +1502214: 7, +1502215: 0, +1502216: 5, +1502217: 0, +1502218: 202, +1502219: 16, +1502220: 137, +1502221: 12, +1502222: 72, +1502223: 8, +1502224: 38, +1502225: 4, +1502226: 140, +1502227: 8, +1502228: 106, +1502229: 8, +1502230: 73, +1502231: 4, +1502232: 39, +1502233: 4, +1502234: 6, +1502235: 0, +1502236: 236, +1502237: 28, +1502238: 5, +1502239: 0, +1502240: 5, +1502241: 0, +1502242: 5, +1502243: 0, +1502244: 6, +1502245: 0, +1502246: 72, +1502247: 8, +1502248: 71, +1502249: 4, +1502250: 39, +1502251: 0, +1502252: 6, +1502253: 0, +1502254: 5, +1502255: 0, +1502256: 5, +1502257: 0, +1502258: 5, +1502259: 0, +1502260: 5, +1502261: 0, +1502262: 5, +1502263: 0, +1502264: 72, +1502265: 8, +1502266: 138, +1502267: 4, +1502268: 73, +1502269: 0, +1502270: 6, +1502271: 0, +1502272: 4, +1502273: 0, +1502274: 169, +1502275: 16, +1502276: 103, +1502277: 12, +1502278: 70, +1502279: 8, +1502280: 37, +1502281: 4, +1502282: 106, +1502283: 8, +1502284: 105, +1502285: 8, +1502286: 71, +1502287: 4, +1502288: 38, +1502289: 4, +1502290: 5, +1502291: 0, +1502292: 202, +1502293: 24, +1502294: 4, +1502295: 0, +1502296: 4, +1502297: 0, +1502298: 4, +1502299: 0, +1502300: 5, +1502301: 0, +1502302: 71, +1502303: 4, +1502304: 38, +1502305: 4, +1502306: 38, +1502307: 0, +1502308: 5, +1502309: 0, +1502310: 4, +1502311: 0, +1502312: 4, +1502313: 0, +1502314: 4, +1502315: 0, +1502316: 4, +1502317: 0, +1502318: 4, +1502319: 0, +1502320: 71, +1502321: 4, +1502322: 104, +1502323: 4, +1502324: 39, +1502325: 0, +1502326: 5, +1502327: 0, +1502328: 3, +1502329: 0, +1502330: 135, +1502331: 12, +1502332: 102, +1502333: 8, +1502334: 37, +1502335: 4, +1502336: 4, +1502337: 0, +1502338: 104, +1502339: 4, +1502340: 71, +1502341: 4, +1502342: 38, +1502343: 4, +1502344: 37, +1502345: 0, +1502346: 4, +1502347: 0, +1502348: 136, +1502349: 16, +1502350: 3, +1502351: 0, +1502352: 3, +1502353: 0, +1502354: 3, +1502355: 0, +1502356: 4, +1502357: 0, +1502358: 37, +1502359: 4, +1502360: 37, +1502361: 4, +1502362: 4, +1502363: 0, +1502364: 4, +1502365: 0, +1502366: 3, +1502367: 0, +1502368: 3, +1502369: 0, +1502370: 3, +1502371: 0, +1502372: 3, +1502373: 0, +1502374: 3, +1502375: 0, +1502376: 37, +1502377: 4, +1502378: 70, +1502379: 4, +1502380: 37, +1502381: 0, +1502382: 3, +1502383: 0, +1502384: 2, +1502385: 0, +1502386: 101, +1502387: 8, +1502388: 68, +1502389: 4, +1502390: 36, +1502391: 4, +1502392: 3, +1502393: 0, +1502394: 70, +1502395: 4, +1502396: 37, +1502397: 4, +1502398: 36, +1502399: 0, +1502400: 3, +1502401: 0, +1502402: 3, +1502403: 0, +1502404: 102, +1502405: 12, +1502406: 2, +1502407: 0, +1502408: 2, +1502409: 0, +1502410: 2, +1502411: 0, +1502412: 3, +1502413: 0, +1502414: 36, +1502415: 4, +1502416: 35, +1502417: 0, +1502418: 3, +1502419: 0, +1502420: 3, +1502421: 0, +1502422: 2, +1502423: 0, +1502424: 2, +1502425: 0, +1502426: 2, +1502427: 0, +1502428: 2, +1502429: 0, +1502430: 2, +1502431: 0, +1502432: 36, +1502433: 4, +1502434: 36, +1502435: 0, +1502436: 3, +1502437: 0, +1502438: 2, +1502439: 0, +1502440: 1, +1502441: 0, +1502442: 67, +1502443: 4, +1502444: 35, +1502445: 4, +1502446: 34, +1502447: 0, +1502448: 2, +1502449: 0, +1502450: 36, +1502451: 4, +1502452: 35, +1502453: 0, +1502454: 3, +1502455: 0, +1502456: 2, +1502457: 0, +1502458: 2, +1502459: 0, +1502460: 68, +1502461: 8, +1502462: 1, +1502463: 0, +1502464: 1, +1502465: 0, +1502466: 1, +1502467: 0, +1502468: 2, +1502469: 0, +1502470: 34, +1502471: 0, +1502472: 2, +1502473: 0, +1502474: 2, +1502475: 0, +1502476: 2, +1502477: 0, +1502478: 1, +1502479: 0, +1502480: 1, +1502481: 0, +1502482: 1, +1502483: 0, +1502484: 1, +1502485: 0, +1502486: 1, +1502487: 0, +1502488: 34, +1502489: 0, +1502490: 2, +1502491: 0, +1502492: 1, +1502493: 0, +1502494: 1, +1502495: 0, +1502496: 1, +1502497: 0, +1502498: 33, +1502499: 0, +1502500: 1, +1502501: 0, +1502502: 1, +1502503: 0, +1502504: 1, +1502505: 0, +1502506: 2, +1502507: 0, +1502508: 1, +1502509: 0, +1502510: 1, +1502511: 0, +1502512: 1, +1502513: 0, +1502514: 1, +1502515: 0, +1502516: 34, +1502517: 4, +1502518: 1, +1502519: 0, +1502520: 1, +1502521: 0, +1502522: 1, +1502523: 0, +1502524: 1, +1502525: 0, +1502526: 1, +1502527: 0, +1502528: 1, +1502529: 0, +1502530: 1, +1502531: 0, +1502532: 1, +1502533: 0, +1502534: 1, +1502535: 0, +1502536: 1, +1502537: 0, +1502538: 1, +1502539: 0, +1502540: 1, +1502541: 0, +1502542: 1, +1502543: 0, +1502544: 1, +1502545: 0, +1502546: 0, +1502547: 0, +1502548: 0, +1502549: 0, +1502550: 0, +1502551: 0, +1502552: 0, +1502553: 0, +1502554: 0, +1502555: 0, +1502556: 0, +1502557: 0, +1502558: 0, +1502559: 0, +1502560: 0, +1502561: 0, +1502562: 0, +1502563: 0, +1502564: 0, +1502565: 0, +1502566: 0, +1502567: 0, +1502568: 0, +1502569: 0, +1502570: 0, +1502571: 0, +1502572: 0, +1502573: 0, +1502574: 0, +1502575: 0, +1502576: 0, +1502577: 0, +1502578: 0, +1502579: 0, +1502580: 0, +1502581: 0, +1502582: 0, +1502583: 0, +1502584: 0, +1502585: 0, +1502586: 0, +1502587: 0, +1502588: 0, +1502589: 0, +1502590: 0, +1502591: 0, +1502592: 0, +1502593: 0, +1502594: 0, +1502595: 0, +1502596: 0, +1502597: 0, +1502598: 0, +1502599: 0, +1502600: 0, +1502601: 0, +454047: 34, +454048: 93, +454049: 99, +454050: 68, +454051: 64, +454052: 24, +454057: 1, +454058: 89, +454059: 66, +1502636: 0, +1502637: 0, +1502638: 159, +1502639: 38, +1502640: 89, +1502641: 1, +1502642: 76, +1502643: 0, +1502644: 4, +1502645: 0, +1502646: 57, +1502647: 87, +1502648: 115, +1502649: 66, +1502650: 173, +1502651: 45, +1502652: 198, +1502653: 20, +1502654: 127, +1502655: 54, +1502656: 249, +1502657: 41, +1502658: 115, +1502659: 33, +1502660: 12, +1502661: 21, +1502662: 134, +1502663: 12, +1502664: 36, +1502665: 0, +1502666: 173, +1502667: 41, +1502668: 74, +1502669: 33, +1502670: 231, +1502671: 20, +1502672: 99, +1502673: 12, +1502674: 173, +1502675: 41, +1502676: 190, +1502677: 42, +1502678: 120, +1502679: 9, +1502680: 108, +1502681: 4, +1502682: 36, +1502683: 0, +454108: 68, +454109: 0, +454110: 52, +1502687: 66, +1502688: 173, +1502689: 45, +1502690: 198, +1502691: 20, +1502692: 158, +1502693: 58, +1502694: 24, +1502695: 46, +1502696: 147, +1502697: 37, +1502698: 44, +1502699: 25, +1502700: 166, +1502701: 12, +1502702: 36, +1502703: 0, +1502704: 140, +1502705: 37, +1502706: 41, +1502707: 29, +1502708: 231, +1502709: 20, +1502710: 99, +1502711: 12, +1502712: 140, +1502713: 37, +1502714: 189, +1502715: 50, +1502716: 151, +1502717: 17, +1502718: 172, +1502719: 8, +1502720: 36, +1502721: 4, +1502722: 57, +1502723: 87, +1502724: 115, +1502725: 66, +1502726: 173, +1502727: 45, +1502728: 198, +1502729: 20, +1502730: 189, +1502731: 62, +1502732: 56, +1502733: 50, +1502734: 178, +1502735: 37, +1502736: 76, +1502737: 25, +1502738: 199, +1502739: 16, +1502740: 35, +1502741: 0, +1502742: 107, +1502743: 37, +1502744: 41, +1502745: 29, +1502746: 198, +1502747: 16, +1502748: 99, +1502749: 12, +1502750: 107, +1502751: 37, +1502752: 220, +1502753: 54, +1502754: 182, +1502755: 25, +1502756: 204, +1502757: 12, +1502758: 68, +1502759: 4, +1502760: 57, +1502761: 87, +1502762: 115, +1502763: 66, +1502764: 173, +1502765: 45, +1502766: 198, +1502767: 20, +1502768: 220, +1502769: 66, +1502770: 87, +1502771: 54, +1502772: 210, +1502773: 41, +1502774: 108, +1502775: 29, +1502776: 231, +1502777: 16, +1502778: 35, +1502779: 0, +1502780: 74, +1502781: 33, +1502782: 8, +1502783: 25, +1502784: 198, +1502785: 16, +1502786: 99, +1502787: 12, +1502788: 74, +1502789: 33, +1502790: 251, +1502791: 62, +1502792: 245, +1502793: 33, +1502794: 11, +1502795: 21, +1502796: 101, +1502797: 8, +1502798: 57, +1502799: 87, +1502800: 115, +1502801: 66, +1502802: 173, +1502803: 45, +1502804: 198, +1502805: 20, +1502806: 219, +1502807: 66, +1502808: 86, +1502809: 54, +1502810: 210, +1502811: 45, +1502812: 109, +1502813: 33, +1502814: 232, +1502815: 20, +1502816: 35, +1502817: 0, +1502818: 74, +1502819: 29, +1502820: 231, +1502821: 24, +1502822: 165, +1502823: 16, +1502824: 66, +1502825: 8, +1502826: 74, +1502827: 29, +1502828: 26, +1502829: 67, +1502830: 20, +1502831: 42, +1502832: 43, +1502833: 25, +1502834: 133, +1502835: 8, +1502836: 57, +1502837: 87, +1502838: 115, +1502839: 66, +1502840: 173, +1502841: 45, +1502842: 198, +1502843: 20, +1502844: 250, +1502845: 70, +1502846: 117, +1502847: 58, +1502848: 17, +1502849: 50, +1502850: 141, +1502851: 37, +1502852: 8, +1502853: 21, +1502854: 35, +1502855: 0, +1502856: 41, +1502857: 25, +1502858: 198, +1502859: 20, +1502860: 165, +1502861: 16, +1502862: 66, +1502863: 8, +1502864: 41, +1502865: 25, +1502866: 25, +1502867: 75, +1502868: 51, +1502869: 50, +1502870: 107, +1502871: 29, +1502872: 133, +1502873: 12, +1502874: 57, +1502875: 87, +1502876: 115, +1502877: 66, +1502878: 173, +1502879: 45, +1502880: 198, +1502881: 20, +1502882: 25, +1502883: 75, +1502884: 149, +1502885: 62, +1502886: 17, +1502887: 50, +1502888: 173, +1502889: 37, +1502890: 41, +1502891: 25, +1502892: 34, +1502893: 0, +1502894: 8, +1502895: 25, +1502896: 198, +1502897: 20, +1502898: 132, +1502899: 12, +1502900: 66, +1502901: 8, +1502902: 8, +1502903: 25, +1502904: 56, +1502905: 79, +1502906: 82, +1502907: 58, +1502908: 139, +1502909: 33, +1502910: 165, +1502911: 12, +1502912: 57, +1502913: 87, +1502914: 115, +1502915: 66, +1502916: 173, +1502917: 45, +1502918: 198, +1502919: 20, +1502920: 56, +1502921: 79, +1502922: 180, +1502923: 66, +1502924: 49, +1502925: 54, +1502926: 205, +1502927: 41, +1502928: 73, +1502929: 25, +1502930: 34, +1502931: 0, +1502932: 231, +1502933: 20, +1502934: 165, +1502935: 16, +1502936: 132, +1502937: 12, +1502938: 66, +1502939: 8, +1502940: 231, +1502941: 20, +454366: 76, +454367: 15, +454368: 40, +454369: 9, +454370: 36, +454371: 7, +454372: 28, +454373: 5, +454374: 20, +454375: 3, +454376: 12, +454377: 2, +454378: 8, +454379: 1, +454380: 4, +454385: 22, +454386: 72, +454387: 14, +454388: 36, +454389: 8, +454390: 32, +454391: 6, +454392: 24, +454393: 4, +454394: 16, +454395: 2, +454396: 8, +454397: 1, +454398: 4, +454399: 0, +454400: 0, +454405: 21, +454406: 68, +454407: 13, +454408: 32, +454409: 7, +454410: 28, +454411: 5, +454412: 20, +454413: 3, +454414: 12, +454415: 1, +454416: 4, +454417: 0, +454418: 0, +454419: 0, +454420: 0, +454425: 20, +454426: 64, +454427: 12, +454428: 28, +454429: 6, +454430: 24, +454431: 4, +454432: 16, +454433: 2, +454434: 8, +454435: 0, +454436: 0, +454437: 0, +454438: 0, +454439: 0, +454440: 0, +454445: 19, +454446: 60, +454447: 11, +454448: 24, +454449: 5, +454450: 20, +454451: 3, +454452: 12, +454453: 1, +454454: 4, +454455: 0, +454456: 0, +454457: 0, +454458: 0, +454459: 0, +454460: 0, +454465: 18, +454466: 56, +454467: 10, +454468: 20, +454469: 4, +454470: 16, +454471: 2, +454472: 8, +454473: 0, +454474: 0, +454475: 0, +454476: 0, +454477: 0, +454478: 0, +454479: 0, +454480: 0, +454485: 17, +454486: 52, +454487: 9, +454488: 16, +454489: 3, +454490: 12, +454491: 1, +454492: 4, +454493: 0, +454494: 0, +454495: 0, +454496: 0, +454497: 0, +454498: 0, +454499: 0, +454500: 0, +454505: 16, +454506: 48, +454507: 8, +454508: 12, +454509: 2, +454510: 8, +454511: 0, +454512: 0, +454513: 0, +454514: 0, +454515: 0, +454516: 0, +454517: 0, +454518: 0, +454519: 0, +454520: 0, +454525: 17, +454526: 52, +454527: 9, +454528: 16, +454529: 3, +454530: 12, +454531: 1, +454532: 4, +454533: 0, +454534: 0, +454535: 0, +454536: 0, +454537: 0, +454538: 0, +454539: 0, +454540: 0, +454545: 18, +454546: 56, +454547: 10, +454548: 20, +454549: 4, +454550: 16, +1503127: 0, +1503128: 0, +1503129: 117, +1503130: 44, +1503131: 111, +1503132: 44, +1503133: 41, +1503134: 40, +1503135: 35, +1503136: 40, +1503137: 49, +1503138: 98, +1503139: 173, +1503140: 85, +1503141: 41, +1503142: 73, +1503143: 132, +1503144: 56, +1503145: 181, +1503146: 77, +1503147: 81, +1503148: 69, +1503149: 237, +1503150: 64, +1503151: 168, +1503152: 56, +1503153: 100, +1503154: 48, +1503155: 181, +1503156: 126, +1503157: 0, +1503158: 40, +1503159: 34, +1503160: 20, +1503161: 41, +1503162: 49, +1503163: 231, +1503164: 44, +1503165: 165, +1503166: 36, +1503167: 66, +1503168: 28, +1503169: 107, +1503170: 65, +1503171: 213, +1503172: 48, +1503173: 175, +1503174: 44, +1503175: 105, +1503176: 40, +1503177: 67, +1503178: 36, +1503179: 82, +1503180: 98, +1503181: 206, +1503182: 81, +1503183: 74, +1503184: 69, +1503185: 132, +1503186: 52, +1503187: 245, +1503188: 77, +1503189: 145, +1503190: 69, +1503191: 14, +1503192: 65, +1503193: 201, +1503194: 52, +1503195: 133, +1503196: 44, +1503197: 214, +1503198: 126, +1503199: 0, +1503200: 36, +1503201: 34, +1503202: 16, +1503203: 41, +1503204: 45, +1503205: 231, +1503206: 40, +1503207: 165, +1503208: 32, +1503209: 66, +1503210: 24, +1503211: 74, +1503212: 57, +1503213: 54, +1503214: 53, +1503215: 240, +1503216: 48, +1503217: 138, +1503218: 36, +1503219: 68, +1503220: 32, +1503221: 115, +1503222: 94, +1503223: 239, +1503224: 81, +1503225: 74, +1503226: 65, +1503227: 165, +1503228: 44, +1503229: 22, +1503230: 78, +1503231: 178, +1503232: 69, +1503233: 78, +1503234: 61, +1503235: 9, +1503236: 53, +1503237: 165, +1503238: 40, +1503239: 24, +1503240: 127, +1503241: 0, +1503242: 28, +1503243: 34, +1503244: 16, +1503245: 8, +1503246: 41, +1503247: 198, +1503248: 36, +1503249: 165, +1503250: 28, +1503251: 66, +1503252: 24, +1503253: 74, +1503254: 53, +1503255: 150, +1503256: 57, +1503257: 48, +1503258: 49, +1503259: 202, +1503260: 36, +1503261: 100, +1503262: 28, +1503263: 148, +1503264: 94, +1503265: 16, +1503266: 78, +1503267: 107, +1503268: 61, +1503269: 165, +1503270: 40, +1503271: 86, +1503272: 78, +1503273: 242, +1503274: 69, +1503275: 111, +1503276: 61, +1503277: 42, +1503278: 49, +1503279: 198, +1503280: 36, +1503281: 57, +1503282: 127, +1503283: 0, +1503284: 24, +1503285: 34, +1503286: 12, +1503287: 8, +1503288: 37, +1503289: 198, +1503290: 32, +1503291: 165, +1503292: 24, +1503293: 66, +1503294: 20, +1503295: 41, +1503296: 45, +1503297: 23, +1503298: 66, +1503299: 145, +1503300: 53, +1503301: 234, +1503302: 36, +1503303: 100, +1503304: 24, +1503305: 214, +1503306: 90, +1503307: 16, +1503308: 74, +1503309: 107, +1503310: 57, +1503311: 165, +1503312: 36, +1503313: 151, +1503314: 78, +1503315: 19, +1503316: 66, +1503317: 175, +1503318: 57, +1503319: 75, +1503320: 49, +1503321: 231, +1503322: 36, +1503323: 123, +1503324: 127, +1503325: 0, +1503326: 16, +1503327: 34, +1503328: 8, +1503329: 8, +454754: 84, +1503331: 198, +1503332: 28, +1503333: 132, +454758: 11, +1503335: 66, +1503336: 16, +1503337: 41, +1503338: 41, +1503339: 119, +1503340: 70, +1503341: 209, +1503342: 53, +1503343: 42, +1503344: 37, +1503345: 132, +1503346: 20, +1503347: 247, +1503348: 90, +1503349: 49, +1503350: 70, +1503351: 140, +1503352: 53, +1503353: 165, +1503354: 32, +1503355: 215, +1503356: 78, +1503357: 83, +1503358: 66, +1503359: 208, +1503360: 57, +1503361: 108, +1503362: 45, +1503363: 8, +1503364: 33, +1503365: 156, +1503366: 127, +1503367: 0, +1503368: 12, +1503369: 34, +1503370: 4, +1503371: 8, +1503372: 29, +1503373: 198, +1503374: 24, +1503375: 132, +1503376: 20, +1503377: 66, +1503378: 12, +1503379: 8, +1503380: 33, +1503381: 216, +1503382: 74, +1503383: 18, +1503384: 58, +1503385: 75, +1503386: 33, +1503387: 133, +1503388: 16, +1503389: 24, +1503390: 87, +1503391: 82, +1503392: 70, +1503393: 140, +1503394: 49, +1503395: 198, +1503396: 24, +1503397: 248, +1503398: 78, +1503399: 116, +1503400: 66, +1503401: 16, +1503402: 54, +1503403: 172, +1503404: 45, +1503405: 40, +1503406: 29, +1503407: 222, +1503408: 127, +1503409: 0, +1503410: 4, +1503411: 34, +1503412: 4, +1503413: 231, +1503414: 24, +1503415: 165, +1503416: 20, +1503417: 132, +1503418: 16, +1503419: 66, +1503420: 12, +1503421: 8, +1503422: 29, +1503423: 56, +1503424: 79, +1503425: 82, +1503426: 58, +1503427: 139, +1503428: 33, +1503429: 165, +1503430: 12, +1503431: 57, +1503432: 87, +1503433: 115, +1503434: 66, +1503435: 173, +1503436: 45, +1503437: 198, +1503438: 20, +1503439: 56, +1503440: 79, +1503441: 180, +1503442: 66, +1503443: 49, +1503444: 54, +1503445: 205, +1503446: 41, +1503447: 73, +1503448: 25, +1503449: 255, +1503450: 127, +1503451: 0, +1503452: 0, +1503453: 34, +1503454: 0, +1503455: 231, +1503456: 20, +1503457: 165, +1503458: 16, +1503459: 132, +1503460: 12, +1503461: 66, +1503462: 8, +1503463: 231, +1503464: 20, +1503511: 0, +1503512: 0, +1503513: 159, +1503514: 21, +1503515: 219, +1503516: 0, +1503517: 51, +1503518: 0, +1503519: 14, +1503520: 0, +1503521: 251, +1503522: 49, +1503523: 119, +1503524: 37, +1503525: 244, +1503526: 24, +1503527: 112, +1503528: 12, +1503529: 127, +1503530: 29, +1503531: 59, +1503532: 25, +1503533: 215, +1503534: 16, +1503535: 147, +1503536: 12, +1503537: 80, +1503538: 4, +1503539: 95, +1503540: 74, +1503541: 12, +1503542: 0, +1503543: 222, +1503544: 29, +1503545: 26, +1503546: 9, +1503547: 114, +1503548: 4, +1503549: 45, +1503550: 0, +1503551: 27, +1503552: 54, +1503553: 150, +1503554: 41, +1503555: 19, +1503556: 29, +1503557: 111, +1503558: 12, +1503559: 190, +1503560: 37, +1503561: 122, +1503562: 29, +1503563: 22, +1503564: 21, +1503565: 178, +1503566: 16, +1503567: 111, +1503568: 8, +2175588: 27, +1503570: 82, +1503571: 10, +1503572: 0, +1503573: 29, +1503574: 38, +1503575: 56, +1503576: 17, +1503577: 145, +1503578: 8, +1503579: 43, +1503580: 4, +1503581: 90, +1503582: 62, +1503583: 182, +1503584: 45, +1503585: 50, +1503586: 29, +1503587: 141, +1503588: 16, +1503589: 253, +1503590: 41, +1503591: 153, +1503592: 37, +1503593: 53, +1503594: 29, +1503595: 241, +1503596: 20, +1503597: 142, +1503598: 8, +2175589: 0, +1503600: 90, +1503601: 9, +1503602: 0, +1503603: 92, +1503604: 46, +1503605: 119, +1503606: 25, +1503607: 208, +1503608: 12, +1503609: 74, +1503610: 4, +1503611: 122, +1503612: 66, +1503613: 213, +1503614: 49, +1503615: 81, +1503616: 33, +1503617: 140, +1503618: 16, +1503619: 60, +1503620: 50, +1503621: 216, +1503622: 41, +1503623: 116, +1503624: 33, +1503625: 16, +1503626: 25, +1503627: 173, +1503628: 12, +2175590: 0, +1503630: 99, +1503631: 7, +1503632: 0, +1503633: 123, +1503634: 54, +1503635: 182, +1503636: 33, +1503637: 238, +1503638: 20, +1503639: 105, +1503640: 8, +1503641: 186, +1503642: 70, +1503643: 21, +1503644: 54, +1503645: 80, +1503646: 37, +1503647: 170, +1503648: 16, +1503649: 123, +1503650: 58, +1503651: 23, +1503652: 50, +1503653: 148, +1503654: 37, +1503655: 80, +1503656: 29, +1503657: 236, +1503658: 16, +2175591: 223, +1503660: 103, +1503661: 5, +1503662: 0, +1503663: 186, +1503664: 62, +1503665: 245, +1503666: 41, +1503667: 45, +1503668: 25, +1503669: 136, +1503670: 8, +1503671: 218, +1503672: 74, +1503673: 52, +1503674: 58, +1503675: 111, +1503676: 41, +1503677: 169, +1503678: 16, +1503679: 186, +1503680: 66, +1503681: 86, +1503682: 54, +1503683: 211, +1503684: 41, +1503685: 111, +1503686: 33, +1503687: 11, +1503688: 21, +2175592: 2, +1503690: 111, +1503691: 3, +1503692: 0, +1503693: 249, +1503694: 70, +1503695: 19, +1503696: 50, +1503697: 76, +1503698: 29, +1503699: 134, +1503700: 12, +1503701: 25, +1503702: 83, +1503703: 84, +1503704: 62, +1503705: 142, +1503706: 41, +1503707: 199, +1503708: 20, +1503709: 249, +1503710: 70, +1503711: 117, +1503712: 62, +1503713: 242, +1503714: 49, +1503715: 174, +1503716: 37, +1503717: 42, +1503718: 21, +2175593: 215, +1503720: 119, +1503721: 2, +1503722: 0, +1503723: 56, +1503724: 79, +1503725: 82, +1503726: 58, +1503727: 139, +1503728: 33, +1503729: 165, +1503730: 12, +1503731: 57, +1503732: 87, +1503733: 115, +1503734: 66, +1503735: 173, +1503736: 45, +1503737: 198, +1503738: 20, +1503739: 56, +1503740: 79, +1503741: 180, +1503742: 66, +1503743: 49, +1503744: 54, +1503745: 205, +1503746: 41, +1503747: 73, +1503748: 25, +2175594: 1, +1503750: 127, +1503751: 0, +1503752: 0, +2175596: 0, +1503873: 0, +1503874: 0, +1503875: 178, +1503876: 114, +1503877: 199, +1503878: 113, +1503879: 99, +1503880: 68, +1503881: 22, +1503882: 74, +1503883: 145, +1503884: 57, +1503885: 44, +1503886: 45, +1503887: 167, +1503888: 28, +1503889: 229, +1503890: 32, +1503891: 164, +1503892: 24, +1503893: 131, +1503894: 16, +1503895: 65, +1503896: 8, +1503897: 0, +1503898: 0, +1503899: 0, +1503900: 0, +1503901: 223, +1503902: 2, +1503903: 24, +1503904: 99, +1503905: 24, +1503906: 99, +1503907: 2, +1503908: 8, +1503909: 148, +1503910: 82, +1503911: 206, +1503912: 57, +1503913: 8, +1503914: 33, +1503915: 132, +1503916: 16, +1503917: 25, +1503918: 0, +1503919: 18, +1503920: 0, +1503921: 0, +1503922: 92, +1503923: 0, +1503924: 64, +1503925: 132, +1503926: 16, +1503927: 127, +1503928: 25, +1503929: 255, +1503930: 127, +1503931: 79, +1503932: 98, +1503933: 134, +1503934: 97, +1503935: 66, +1503936: 56, +1503937: 178, +1503938: 61, +1503939: 78, +1503940: 49, +1503941: 234, +1503942: 36, +1503943: 134, +1503944: 24, +1503945: 196, +1503946: 24, +1503947: 131, +1503948: 20, +1503949: 98, +1503950: 12, +1503951: 32, +1503952: 4, +1503953: 0, +1503954: 0, +1503955: 0, +1503956: 0, +1503957: 90, +1503958: 2, +1503959: 148, +1503960: 82, +1503961: 148, +1503962: 82, +1503963: 1, +1503964: 4, +1503965: 49, +1503966: 70, +1503967: 140, +1503968: 49, +1503969: 198, +1503970: 24, +1503971: 99, +1503972: 12, +1503973: 21, +1503974: 0, +1503975: 15, +1503976: 0, +1503977: 0, +1503978: 76, +1503979: 0, +1503980: 52, +1503981: 99, +1503982: 12, +1503983: 58, +1503984: 21, +1503985: 90, +1503986: 107, +1503987: 236, +1503988: 81, +1503989: 69, +1503990: 81, +1503991: 66, +1503992: 48, +1503993: 111, +1503994: 49, +1503995: 12, +1503996: 41, +1503997: 200, +1503998: 28, +1503999: 101, +1504000: 20, +1504001: 163, +1504002: 20, +1504003: 98, +1504004: 16, +1504005: 66, +1504006: 8, +1504007: 32, +1504008: 4, +1504009: 0, +1504010: 0, +1504011: 0, +1504012: 0, +1504013: 246, +1504014: 1, +1504015: 49, +1504016: 70, +1504017: 49, +1504018: 70, +1504019: 1, +1504020: 4, +1504021: 206, +1504022: 57, +1504023: 74, +1504024: 41, +1504025: 165, +1504026: 20, +1504027: 66, +1504028: 8, +1504029: 17, +1504030: 0, +1504031: 12, +1504032: 0, +1504033: 0, +1504034: 64, +1504035: 0, +1504036: 44, +1504037: 66, +1504038: 8, +1504039: 246, +1504040: 16, +1504041: 214, +1504042: 90, +1504043: 138, +1504044: 65, +1504045: 4, +1504046: 65, +1504047: 33, +1504048: 36, +1504049: 44, +1504050: 41, +1504051: 201, +1504052: 32, +1504053: 166, +1504054: 24, +1504055: 68, +1504056: 16, +1504057: 130, +1504058: 16, +1504059: 66, +1504060: 12, +1504061: 65, +1504062: 8, +1504063: 32, +1504064: 4, +1504065: 0, +1504066: 0, +1504067: 0, +1504068: 0, +1504069: 145, +1504070: 1, +1504071: 173, +1504072: 53, +1504073: 173, +1504074: 53, +1504075: 1, +1504076: 4, +1504077: 107, +1504078: 45, +1504079: 8, +1504080: 33, +1504081: 132, +1504082: 16, +1504083: 66, +1504084: 8, +1504085: 14, +1504086: 0, +1504087: 10, +1504088: 0, +1504089: 0, +1504090: 52, +1504091: 0, +1504092: 36, +1504093: 66, +1504094: 8, +1504095: 209, +1504096: 12, +1504097: 49, +1504098: 70, +1504099: 39, +1504100: 49, +1504101: 195, +1504102: 48, +1504103: 33, +1504104: 28, +1504105: 201, +1504106: 28, +1504107: 167, +1504108: 24, +1504109: 101, +1504110: 16, +1504111: 67, +1504112: 12, +1504113: 98, +1504114: 12, +1504115: 65, +1504116: 8, +1504117: 33, +1504118: 4, +1504119: 0, +1504120: 0, +1504121: 0, +1504122: 0, +1504123: 0, +1504124: 0, +1504125: 45, +1504126: 1, +1504127: 74, +1504128: 41, +1504129: 74, +1504130: 41, +1504131: 0, +1504132: 0, +1504133: 8, +1504134: 33, +1504135: 198, +1504136: 24, +1504137: 99, +1504138: 12, +1504139: 33, +1504140: 4, +1504141: 10, +1504142: 0, +1504143: 7, +1504144: 0, +1504145: 0, +1504146: 36, +1504147: 0, +1504148: 24, +1504149: 33, +1504150: 4, +1504151: 141, +1504152: 8, +1504153: 173, +1504154: 53, +1504155: 197, +1504156: 32, +1504157: 130, +1504158: 32, +1504159: 0, +1504160: 16, +1504161: 134, +1504162: 20, +1504163: 100, +1504164: 16, +1504165: 67, +1504166: 12, +1504167: 34, +1504168: 8, +1504169: 65, +1504170: 8, +1504171: 33, +1504172: 4, +1504173: 32, +1504174: 4, +1504175: 0, +1504176: 0, +1504177: 0, +1504178: 0, +1504179: 0, +1504180: 0, +1504181: 200, +1504182: 0, +1504183: 198, +1504184: 24, +1504185: 198, +1504186: 24, +1504187: 0, +1504188: 0, +1504189: 165, +1504190: 20, +1504191: 132, +1504192: 16, +1504193: 66, +1504194: 8, +1504195: 33, +1504196: 4, +1504197: 7, +1504198: 0, +1504199: 5, +1504200: 0, +1504201: 0, +1504202: 24, +1504203: 0, +1504204: 16, +1504205: 33, +1504206: 4, +1504207: 104, +1504208: 4, +1504209: 8, +1504210: 33, +1504211: 98, +1504212: 16, +1504213: 65, +1504214: 16, +1504215: 0, +1504216: 8, +1504217: 67, +1504218: 8, +1504219: 34, +1504220: 8, +1504221: 33, +1504222: 4, +1504223: 1, +1504224: 4, +1504225: 32, +1504226: 4, +1504227: 0, +1504228: 0, +1504229: 0, +1504230: 0, +1504231: 0, +1504232: 0, +1504233: 0, +1504234: 0, +1504235: 0, +1504236: 0, +1504237: 100, +1504238: 0, +1504239: 99, +1504240: 12, +1504241: 99, +1504242: 12, +1504243: 0, +1504244: 0, +1504245: 66, +1504246: 8, +1504247: 66, +1504248: 8, +1504249: 33, +1504250: 4, +1504251: 0, +1504252: 0, +1504253: 3, +1504254: 0, +1504255: 2, +1504256: 0, +1504257: 0, +1504258: 12, +1504259: 0, +1504260: 8, +1504261: 0, +1504262: 0, +1504263: 36, +1504264: 0, +1504265: 132, +1504266: 16, +892443: 99, +892449: 56, +455919: 217, +455920: 62, +455921: 87, +455922: 46, +455923: 53, +455924: 42, +455925: 243, +455926: 37, +455927: 210, +455928: 37, +455929: 176, +455930: 29, +455931: 110, +455932: 25, +455933: 46, +455934: 17, +455939: 87, +455940: 46, +455941: 53, +455942: 42, +455943: 243, +455944: 37, +455945: 217, +455946: 62, +455947: 176, +455948: 29, +455949: 110, +455950: 25, +455951: 46, +455952: 17, +455953: 210, +455954: 37, +455959: 53, +455960: 42, +455961: 243, +455962: 37, +455963: 217, +455964: 62, +455965: 87, +455966: 46, +455967: 110, +455968: 25, +455969: 46, +455970: 17, +455971: 210, +455972: 37, +455973: 176, +455974: 29, +455979: 243, +455980: 37, +455981: 217, +455982: 62, +455983: 87, +455984: 46, +455985: 53, +455986: 42, +455987: 46, +455988: 17, +455989: 210, +455990: 37, +455991: 176, +455992: 29, +455993: 110, +455994: 25, +456007: 210, +456008: 37, +456009: 176, +456010: 29, +456011: 110, +456012: 25, +456013: 46, +456014: 17, +456019: 176, +456020: 29, +456021: 110, +456022: 25, +456023: 46, +456024: 17, +456025: 210, +456026: 37, +456031: 110, +456032: 25, +456033: 46, +456034: 17, +456035: 210, +456036: 37, +456037: 176, +456038: 29, +456043: 46, +456044: 17, +456045: 210, +456046: 37, +456047: 176, +456048: 29, +456049: 110, +456050: 25, +456063: 0, +456064: 4, +456065: 34, +456066: 12, +456067: 100, +456068: 24, +456069: 134, +456070: 32, +456071: 201, +456072: 44, +456073: 101, +456074: 28, +456075: 67, +456076: 16, +456077: 33, +456078: 8, +456083: 34, +456084: 12, +456085: 100, +456086: 24, +456087: 134, +456088: 32, +456089: 201, +456090: 44, +456091: 101, +456092: 28, +456093: 67, +456094: 16, +456095: 33, +456096: 8, +456097: 0, +456098: 4, +456103: 100, +456104: 24, +456105: 134, +456106: 32, +456107: 201, +456108: 44, +456109: 101, +456110: 28, +456111: 67, +456112: 16, +456113: 33, +456114: 8, +456115: 0, +456116: 4, +456117: 34, +456118: 12, +456123: 134, +456124: 32, +456125: 201, +456126: 44, +456127: 101, +456128: 28, +456129: 67, +456130: 16, +456131: 33, +456132: 8, +456133: 0, +456134: 4, +456135: 34, +456136: 12, +456137: 100, +456138: 24, +456143: 201, +456144: 44, +456145: 101, +456146: 28, +456147: 67, +456148: 16, +456149: 33, +456150: 8, +456151: 0, +456152: 4, +456153: 34, +456154: 12, +456155: 100, +456156: 24, +456157: 134, +456158: 32, +456163: 101, +456164: 28, +456165: 67, +456166: 16, +456167: 33, +456168: 8, +456169: 0, +456170: 4, +456171: 34, +456172: 12, +456173: 100, +456174: 24, +456175: 134, +456176: 32, +456177: 201, +456178: 44, +456183: 67, +456184: 16, +456185: 33, +456186: 8, +456187: 0, +456188: 4, +456189: 34, +456190: 12, +456191: 100, +456192: 24, +456193: 134, +456194: 32, +456195: 201, +456196: 44, +456197: 101, +456198: 28, +456203: 33, +456204: 8, +456205: 0, +456206: 4, +456207: 34, +456208: 12, +456209: 100, +456210: 24, +456211: 134, +456212: 32, +456213: 201, +456214: 44, +456215: 101, +456216: 28, +456217: 67, +456218: 16, +892461: 87, +456256: 25, +456257: 0, +456258: 18, +456259: 0, +456260: 0, +456261: 92, +456262: 0, +456263: 64, +456264: 132, +456265: 16, +456266: 127, +456267: 25, +456268: 255, +456269: 127, +456278: 22, +456279: 0, +456280: 15, +456281: 0, +456282: 0, +456283: 80, +456284: 0, +456285: 52, +456286: 132, +456287: 16, +456288: 28, +456289: 13, +456290: 156, +456291: 115, +456300: 19, +456301: 0, +456302: 12, +456303: 0, +456304: 0, +456305: 68, +456306: 0, +456307: 40, +456308: 132, +456309: 16, +456310: 185, +456311: 0, +456312: 57, +456313: 103, +456322: 16, +456323: 0, +456324: 9, +456325: 0, +456326: 0, +456327: 56, +456328: 0, +456329: 28, +456330: 132, +456331: 16, +456332: 86, +456333: 0, +456334: 214, +456335: 90, +456344: 13, +456345: 0, +456346: 6, +456347: 0, +456348: 0, +456349: 44, +456350: 0, +456351: 16, +456352: 132, +456353: 16, +456354: 19, +456355: 0, +456356: 115, +456357: 78, +456366: 10, +456367: 0, +456368: 3, +456369: 0, +456370: 0, +456371: 32, +456372: 0, +456373: 4, +456374: 132, +456375: 16, +456376: 16, +456377: 0, +456378: 16, +456379: 66, +456388: 10, +456389: 0, +456390: 3, +456391: 0, +456392: 0, +456393: 32, +456394: 0, +456395: 4, +456396: 132, +456397: 16, +456398: 16, +456399: 0, +456400: 16, +1373905: 255, +1373906: 114, +1373907: 223, +1373908: 44, +1373909: 185, +1373910: 36, +1373911: 175, +1373912: 28, +1373913: 95, +1373914: 94, +1373915: 63, +1373916: 24, +1373917: 20, +1373918: 16, +1373919: 10, +1373920: 8, +1373921: 186, +1373922: 73, +1373923: 26, +1373924: 4, +1373925: 15, +1373926: 0, +1373927: 5, +1373928: 0, +1373929: 21, +1373930: 53, +1373931: 21, +1373932: 0, +1373933: 10, +1373934: 0, +1373935: 5, +456432: 16, +456433: 0, +1373938: 53, +456435: 0, +1373940: 0, +1373941: 10, +1373942: 0, +1373943: 5, +1373944: 0, +1373945: 186, +1373946: 73, +1373947: 26, +1373948: 4, +1373949: 15, +1373950: 0, +1373951: 5, +1373952: 0, +1373953: 95, +1373954: 94, +1373955: 63, +1373956: 24, +1373957: 20, +1373958: 16, +1373959: 10, +1373960: 8, +1373961: 255, +1373962: 114, +1373963: 223, +1373964: 44, +1373965: 185, +1373966: 36, +1373967: 175, +1373968: 28, +1373969: 144, +1373970: 89, +1373971: 112, +1373972: 56, +1373973: 109, +1373974: 52, +1373975: 104, +1373976: 48, +1373977: 77, +1373978: 73, +1373979: 109, +1373980: 48, +1373981: 74, +1373982: 40, +1373983: 70, +1373984: 36, +1373985: 11, +1373986: 61, +1373987: 75, +1373988: 36, +1373989: 72, +1373990: 32, +1373991: 68, +1373992: 24, +1373993: 200, +1373994: 44, +1373995: 72, +1373996: 28, +1373997: 37, +1373998: 20, +1373999: 34, +1374000: 12, +1374001: 200, +1374002: 44, +1374003: 72, +1374004: 28, +1374005: 37, +1374006: 20, +1374007: 34, +1374008: 12, +1374009: 11, +1374010: 61, +1374011: 75, +1374012: 36, +1374013: 72, +1374014: 32, +1374015: 68, +1374016: 24, +1374017: 77, +1374018: 73, +1374019: 109, +1374020: 48, +1374021: 74, +1374022: 40, +1374023: 70, +1374024: 36, +1374025: 144, +1374026: 89, +1374027: 112, +1374028: 56, +1374029: 109, +1374030: 52, +1374031: 104, +1374032: 48, +456623: 148, +456624: 82, +456625: 16, +456626: 66, +456627: 140, +456628: 49, +456629: 8, +456630: 33, +456631: 132, +456632: 16, +456633: 255, +456634: 127, +456639: 117, +456640: 78, +456641: 241, +456642: 61, +456643: 109, +456644: 45, +456645: 232, +456646: 28, +456647: 100, +456648: 12, +456649: 191, +456650: 119, +456655: 85, +456656: 74, +456657: 209, +456658: 57, +456659: 109, +456660: 45, +456661: 232, +456662: 28, +456663: 100, +456664: 12, +456665: 159, +456666: 115, +456671: 54, +456672: 70, +456673: 178, +456674: 53, +456675: 77, +456676: 41, +456677: 201, +456678: 24, +456679: 68, +456680: 8, +456681: 95, +456682: 107, +456687: 246, +456688: 61, +456689: 146, +456690: 49, +456691: 45, +456692: 37, +456693: 169, +456694: 20, +456695: 68, +456696: 8, +456697: 63, +456698: 103, +456703: 215, +456704: 57, +456705: 114, +456706: 45, +456707: 14, +456708: 33, +456709: 137, +456710: 16, +456711: 36, +456712: 4, +456713: 255, +456714: 94, +456719: 183, +456720: 53, +456721: 82, +456722: 41, +456723: 238, +456724: 28, +456725: 137, +456726: 16, +456727: 36, +456728: 4, +456729: 223, +456730: 90, +456735: 152, +456736: 49, +456737: 51, +456738: 37, +456739: 206, +456740: 24, +456741: 105, +456742: 12, +456743: 4, +456744: 0, +456745: 159, +456746: 82, +456751: 183, +456752: 53, +456753: 82, +456754: 41, +456755: 238, +456756: 28, +456757: 137, +456758: 16, +456759: 36, +456760: 4, +456761: 223, +456762: 90, +456767: 215, +456768: 57, +456769: 114, +456770: 45, +456771: 14, +456772: 33, +456773: 137, +456774: 16, +456775: 36, +456776: 4, +456777: 255, +456778: 94, +456783: 246, +456784: 61, +456785: 146, +456786: 49, +456787: 45, +456788: 37, +456789: 169, +456790: 20, +456791: 68, +456792: 8, +456793: 63, +456794: 103, +456799: 54, +456800: 70, +456801: 178, +456802: 53, +456803: 77, +456804: 41, +456805: 201, +456806: 24, +456807: 68, +456808: 8, +456809: 95, +456810: 107, +456815: 85, +456816: 74, +456817: 209, +456818: 57, +456819: 109, +456820: 45, +456821: 232, +456822: 28, +456823: 100, +456824: 12, +456825: 159, +456826: 115, +456831: 117, +456832: 78, +456833: 241, +456834: 61, +456835: 109, +456836: 45, +456837: 232, +456838: 28, +456839: 100, +456840: 12, +456841: 191, +456842: 119, +456855: 26, +456856: 8, +456857: 18, +456858: 8, +456859: 43, +456860: 4, +456861: 35, +456862: 4, +456867: 55, +456868: 12, +456869: 48, +456870: 12, +456871: 42, +456872: 4, +456873: 35, +456874: 4, +456879: 84, +456880: 16, +456881: 46, +456882: 12, +456883: 73, +456884: 8, +456885: 34, +456886: 4, +456891: 113, +456892: 20, +456893: 76, +456894: 16, +456895: 72, +456896: 8, +456897: 34, +456898: 4, +456903: 142, +456904: 20, +456905: 106, +456906: 16, +456907: 102, +456908: 12, +456909: 66, +456910: 8, +456915: 171, +456916: 24, +456917: 136, +456918: 20, +456919: 101, +456920: 12, +456921: 66, +456922: 8, +456927: 200, +456928: 28, +456929: 134, +456930: 20, +456931: 132, +456932: 16, +456933: 65, +1374438: 0, +1374439: 56, +1374440: 184, +1374441: 87, +1374442: 17, +1374443: 11, +1374444: 70, +1374445: 22, +1374446: 227, +1374447: 0, +1374448: 255, +1374449: 114, +1374450: 223, +1374451: 44, +1374452: 185, +1374453: 36, +1374454: 175, +1374455: 28, +1374456: 169, +1374457: 24, +1374458: 159, +1374459: 79, +1374460: 216, +1374461: 62, +456958: 8, +1374463: 46, +1374464: 205, +1374465: 8, +1374466: 255, +1374467: 127, +1374468: 0, +1374469: 0, +456966: 20, +456967: 101, +456968: 12, +456969: 66, +456970: 8, +456975: 142, +456976: 20, +456977: 106, +456978: 16, +456979: 102, +456980: 12, +456981: 66, +456982: 8, +456987: 113, +456988: 20, +456989: 76, +456990: 16, +456991: 72, +456992: 8, +456993: 34, +456994: 4, +456999: 84, +457000: 16, +457001: 46, +457002: 12, +457003: 73, +457004: 8, +457005: 34, +457006: 4, +457011: 55, +457012: 12, +457013: 48, +457014: 12, +457015: 42, +457016: 4, +457017: 35, +457018: 4, +457039: 148, +457040: 82, +457041: 206, +457042: 57, +457043: 8, +457044: 33, +457045: 132, +457046: 16, +457047: 25, +457048: 0, +457049: 18, +457050: 0, +457059: 117, +457060: 78, +457061: 175, +457062: 53, +457063: 232, +457064: 28, +457065: 100, +457066: 12, +457067: 13, +457068: 8, +457069: 9, +457070: 8, +457079: 85, +457080: 74, +457081: 143, +457082: 49, +457083: 233, +457084: 28, +457085: 100, +457086: 12, +457087: 0, +457088: 16, +457089: 0, +457090: 16, +457099: 54, +457100: 70, +457101: 112, +457102: 45, +457103: 201, +457104: 24, +457105: 68, +457106: 8, +457107: 13, +457108: 8, +457109: 9, +457110: 8, +457119: 246, +457120: 61, +457121: 112, +457122: 45, +457123: 202, +457124: 24, +457125: 68, +457126: 8, +457127: 25, +457128: 0, +457129: 18, +457130: 0, +457139: 215, +457140: 57, +457141: 81, +457142: 41, +457143: 170, +457144: 20, +457145: 36, +457146: 4, +457147: 13, +457148: 8, +457149: 9, +457150: 8, +457159: 183, +457160: 53, +457161: 49, +457162: 37, +457163: 171, +457164: 20, +457165: 36, +457166: 4, +457167: 0, +457168: 16, +457169: 0, +457170: 16, +457179: 152, +457180: 49, +457181: 18, +457182: 33, +457183: 139, +457184: 16, +457185: 4, +457186: 0, +457187: 13, +457188: 8, +457189: 9, +457190: 8, +457199: 183, +457200: 53, +457201: 49, +457202: 37, +457203: 171, +457204: 20, +457205: 36, +457206: 4, +457207: 0, +457208: 16, +457209: 0, +457210: 16, +457219: 215, +457220: 57, +457221: 81, +457222: 41, +457223: 170, +457224: 20, +457225: 36, +457226: 4, +457227: 13, +457228: 8, +457229: 9, +457230: 8, +457239: 246, +457240: 61, +457241: 112, +457242: 45, +457243: 202, +457244: 24, +457245: 68, +457246: 8, +457247: 25, +457248: 0, +457249: 18, +457250: 0, +457259: 54, +457260: 70, +457261: 112, +457262: 45, +457263: 201, +457264: 24, +457265: 68, +457266: 8, +457267: 13, +457268: 8, +457269: 9, +457270: 8, +457279: 85, +457280: 74, +457281: 143, +457282: 49, +457283: 233, +457284: 28, +457285: 100, +457286: 12, +457287: 0, +457288: 16, +457289: 0, +457290: 16, +457299: 117, +457300: 78, +457301: 175, +457302: 53, +457303: 232, +457304: 28, +457305: 100, +457306: 12, +457307: 13, +457308: 8, +457309: 9, +457310: 8, +892503: 1, +892509: 21, +457987: 137, +457988: 29, +457989: 6, +457990: 13, +457991: 163, +457992: 12, +457993: 14, +457994: 45, +457995: 9, +457996: 45, +457997: 197, +457998: 40, +457999: 129, +458000: 12, +458005: 138, +458006: 29, +458007: 7, +458008: 13, +458009: 164, +458010: 12, +458011: 14, +458012: 45, +458013: 9, +458014: 45, +458015: 197, +458016: 40, +458017: 33, +458018: 4, +458023: 138, +458024: 29, +458025: 40, +458026: 13, +458027: 196, +458028: 12, +458029: 15, +458030: 45, +458031: 10, +458032: 45, +458033: 198, +458034: 40, +458035: 35, +458036: 4, +458041: 139, +458042: 29, +458043: 41, +458044: 13, +458045: 197, +458046: 12, +458047: 15, +458048: 45, +458049: 10, +458050: 45, +458051: 198, +458052: 40, +458053: 36, +458054: 4, +458059: 171, +458060: 29, +458061: 73, +458062: 17, +458063: 197, +458064: 16, +458065: 15, +458066: 45, +458067: 11, +458068: 45, +458069: 199, +458070: 40, +458071: 69, +458072: 8, +458077: 172, +458078: 29, +458079: 74, +458080: 17, +458081: 198, +458082: 16, +458083: 15, +458084: 45, +458085: 11, +458086: 45, +458087: 199, +458088: 40, +458089: 70, +458090: 8, +458095: 172, +458096: 29, +458097: 107, +458098: 17, +458099: 230, +458100: 16, +458101: 16, +458102: 45, +458103: 12, +458104: 45, +458105: 200, +458106: 40, +458107: 72, +458108: 8, +458113: 173, +458114: 29, +458115: 108, +2175692: 97, +458117: 231, +458118: 16, +458119: 16, +458120: 45, +458121: 12, +458122: 45, +458123: 200, +458124: 40, +458125: 21, +458126: 0, +458131: 172, +458132: 29, +458133: 107, +458134: 17, +458135: 230, +458136: 16, +458137: 16, +458138: 45, +458139: 12, +458140: 45, +458141: 200, +458142: 40, +458143: 72, +458144: 8, +458149: 172, +458150: 29, +458151: 74, +458152: 17, +458153: 198, +458154: 16, +458155: 15, +458156: 45, +458157: 11, +458158: 45, +458159: 199, +458160: 40, +458161: 70, +458162: 8, +458167: 171, +458168: 29, +458169: 73, +458170: 17, +458171: 197, +458172: 16, +458173: 15, +458174: 45, +458175: 11, +458176: 45, +458177: 199, +458178: 40, +458179: 69, +458180: 8, +458185: 139, +458186: 29, +458187: 41, +458188: 13, +458189: 197, +458190: 12, +458191: 15, +458192: 45, +458193: 10, +458194: 45, +458195: 198, +458196: 40, +458197: 36, +458198: 4, +458203: 138, +458204: 29, +458205: 40, +458206: 13, +458207: 196, +458208: 12, +458209: 15, +458210: 45, +458211: 10, +458212: 45, +458213: 198, +458214: 40, +458215: 35, +458216: 4, +458221: 138, +458222: 29, +458223: 7, +458224: 13, +458225: 164, +458226: 12, +458227: 14, +458228: 45, +458229: 9, +458230: 45, +458231: 197, +458232: 40, +458233: 33, +458234: 4, +852394: 29, +852395: 124, +852396: 20, +852397: 88, +852398: 10, +852399: 48, +1321731: 66, +1321737: 79, +1321743: 29, +1321755: 0, +1115783: 0, +1115784: 56, +1640073: 85, +1640074: 87, +1640075: 79, +1640076: 74, +1640077: 228, +1640078: 28, +1640079: 96, +1640080: 12, +1640081: 178, +1640082: 86, +1640083: 13, +1640084: 62, +1640085: 104, +1640086: 45, +1640087: 38, +1640088: 37, +1640089: 187, +1640090: 94, +1640091: 179, +1640092: 61, +1640093: 46, +1640094: 41, +1115807: 0, +1640096: 20, +1640097: 59, +1640098: 3, +1246883: 247, +1640100: 2, +1246885: 0, +1246886: 0, +1640103: 0, +1640104: 56, +1640105: 87, +1640106: 63, +1640107: 77, +1640108: 46, +1640109: 226, +1640110: 0, +1640111: 96, +1640112: 0, +1640113: 176, +1640114: 58, +1640115: 11, +1640116: 34, +1640117: 102, +1640118: 17, +1640119: 36, +1640120: 9, +1640121: 187, +1640122: 94, +1640123: 179, +1640124: 61, +1640125: 46, +1640126: 41, +1640127: 134, +1640128: 20, +1640129: 59, +1640130: 3, +1640131: 22, +1640132: 2, +1640133: 19, +1640134: 1, +1640135: 0, +1640136: 56, +1640137: 191, +1640138: 78, +1640139: 158, +1640140: 77, +1640141: 9, +1640142: 16, +1640143: 4, +1378000: 20, +1640145: 222, +1640146: 73, +1378003: 87, +1640148: 85, +1640149: 176, +1640150: 48, +1640151: 77, +1640152: 28, +1378009: 127, +1640154: 94, +1640155: 179, +1640156: 61, +1640157: 46, +1640158: 41, +1378015: 95, +1378016: 95, +1378017: 26, +1640162: 3, +1378019: 53, +1378020: 93, +1378021: 99, +1378022: 12, +1378023: 0, +1378024: 56, +1378025: 186, +1378026: 74, +1378027: 178, +1378028: 53, +1378029: 71, +1378030: 8, +1378031: 3, +1378032: 0, +1378033: 21, +1378034: 66, +1378035: 112, +1378036: 41, +1378037: 203, +1378038: 24, +1378039: 137, +1378040: 16, +1378041: 58, +1378042: 70, +1640187: 179, +1378044: 40, +1378045: 9, +1378046: 24, +1378047: 127, +1378048: 111, +1378049: 253, +1378050: 81, +1378051: 19, +1378052: 65, +1378053: 99, +1378054: 12, +1378055: 0, +1378056: 56, +1378057: 186, +1378058: 86, +1378059: 178, +1378060: 65, +1378061: 71, +1378062: 20, +1378063: 3, +1378064: 4, +1378065: 21, +1378066: 78, +1378067: 112, +1378068: 53, +1378069: 203, +1378070: 36, +1378071: 104, +1378072: 24, +1378073: 127, +1378074: 111, +1378075: 248, +1378076: 81, +1378077: 14, +1378078: 65, +1378079: 31, +1378080: 3, +1378081: 218, +1378082: 1, +1378083: 245, +1378084: 0, +1378085: 99, +1378086: 12, +1378087: 0, +1378088: 56, +1378089: 21, +1378090: 66, +1378091: 13, +1378092: 45, +1378093: 2, +1378094: 0, +1378095: 0, +1378096: 0, +1378097: 112, +1378098: 57, +1378099: 203, +1378100: 32, +1378101: 38, +1378102: 12, +1378103: 3, +1378104: 4, +1378105: 58, +1378106: 70, +1378107: 179, +1378108: 40, +1378109: 9, +1378110: 24, +1378111: 127, +1378112: 111, +1378113: 253, +1378114: 81, +1378115: 19, +1378116: 65, +1378117: 99, +1378118: 12, +1378119: 0, +1378120: 56, +1378121: 181, +1378122: 106, +1378123: 176, +1378124: 73, +1378125: 69, +1378126: 28, +1378127: 1, +1378128: 12, +1378129: 19, +1378130: 86, +1378131: 109, +1378132: 65, +1378133: 201, +1378134: 44, +1378135: 102, +1378136: 32, +1378137: 20, +1378138: 87, +1378139: 204, +1378140: 49, +1378141: 227, +1378142: 20, +1378143: 48, +1378144: 86, +1378145: 105, +1378146: 53, +1378147: 131, +1378148: 24, +1378149: 102, +1378150: 12, +1378151: 0, +1378152: 56, +1378153: 16, +1378154: 86, +1378155: 11, +1378156: 53, +1378157: 0, +1378158: 8, +1378159: 0, +1378160: 0, +1378161: 110, +1378162: 65, +1378163: 200, +1378164: 44, +1378165: 35, +1378166: 24, +1378167: 1, +1378168: 12, +1378169: 49, +1378170: 106, +1378171: 170, +1378172: 76, +1378173: 6, +1378174: 36, +1378175: 123, +1378176: 127, +1378177: 244, +1378178: 117, +1378179: 16, +1378180: 77, +1378181: 99, +1378182: 12, +1378183: 0, +1378184: 56, +1378185: 190, +1378186: 75, +1378187: 185, +1378188: 6, +1378189: 168, +1378190: 0, +1378191: 0, +1378192: 0, +1378193: 58, +1378194: 23, +1378195: 118, +1378196: 2, +1378197: 242, +1378198: 1, +1378199: 77, +1378200: 1, +1378201: 224, +1378202: 115, +1378203: 32, +1378204: 79, +1378205: 32, +1378206: 42, +1378207: 224, +1378208: 127, +1378209: 160, +1378210: 90, +1378211: 32, +1378212: 89, +1378213: 67, +1378214: 0, +1378215: 0, +1378216: 56, +1378217: 25, +1378218: 55, +1378219: 20, +1378220: 2, +1378221: 3, +1378222: 0, +1378223: 0, +1378224: 0, +1378225: 149, +1378226: 2, +1378227: 209, +1378228: 1, +1378229: 77, +1378230: 1, +1378231: 168, +1378232: 0, +1378233: 64, +1378234: 75, +1378235: 224, +1378236: 37, +1378237: 224, +1378238: 0, +1378239: 64, +1378240: 107, +1378241: 0, +1378242: 70, +1378243: 128, +1378244: 68, +1378245: 0, +1378246: 0, +1117024: 0, +1117025: 56, +1117026: 156, +1117027: 75, +1117028: 148, +1117029: 54, +1117030: 231, +1117031: 8, +1117032: 132, +1117033: 8, +1117034: 247, +1117035: 66, +1117036: 82, +1117037: 42, +1117038: 173, +1117039: 25, +1117040: 41, +1117041: 17, +1117042: 255, +1117043: 127, +1117044: 59, +1117045: 3, +1117046: 22, +1117047: 2, +1117048: 19, +1117049: 1, +1117050: 255, +1117051: 127, +1117052: 255, +1117053: 3, +1117054: 13, +1117055: 0, +1503689: 127, +1120653: 0, +1120654: 56, +1120655: 156, +1120656: 75, +1120657: 16, +1120658: 38, +1120659: 198, +1120660: 12, +1120661: 99, +1120662: 12, +1120663: 247, +1120664: 66, +1120665: 82, +1120666: 42, +1120667: 173, +1120668: 25, +1120669: 41, +1120670: 13, +1120671: 23, +1120672: 86, +1120673: 114, +1120674: 61, +1120675: 72, +1120676: 28, +1120677: 5, +1120678: 12, +1120679: 59, +1120680: 3, +1120681: 22, +1120682: 2, +1120683: 19, +1120684: 1, +1121898: 0, +1121899: 56, +1121900: 87, +1121901: 63, +1121902: 77, +1121903: 46, +1121904: 226, +1121905: 0, +1121906: 96, +1121907: 0, +1121908: 176, +1121909: 58, +1121910: 11, +1121911: 34, +1121912: 102, +1121913: 17, +1121914: 36, +1121915: 9, +1121916: 255, +1121917: 87, +1121918: 247, +1121919: 66, +1121920: 16, +1121921: 38, +1121922: 140, +1121923: 21, +1121924: 127, +1121925: 1, +1121926: 22, +1121927: 0, +1121928: 10, +1121929: 48, +2174963: 115, +2174964: 78, +2174965: 173, +2174966: 53, +2174967: 231, +2174968: 28, +2174969: 99, +2174970: 12, +2174971: 24, +2174972: 99, +2174973: 4, +2174974: 8, +2174975: 255, +2174976: 127, +2174977: 35, +2174978: 0, +2174979: 69, +2174980: 24, +2174981: 99, +2174982: 7, +2174983: 82, +2174984: 25, +2174985: 171, +2174986: 24, +2174987: 103, +2174988: 20, +2174989: 68, +2174990: 16, +2174991: 75, +2174992: 24, +2174993: 99, +2174994: 195, +1123211: 0, +1123212: 56, +1123213: 156, +1123214: 75, +1123215: 16, +1123216: 38, +1123217: 198, +1123218: 12, +1123219: 99, +1123220: 12, +1123221: 247, +1123222: 66, +1123223: 82, +1123224: 42, +1123225: 173, +1123226: 25, +1123227: 41, +1123228: 13, +1123229: 89, +1123230: 94, +1123231: 114, +1123232: 61, +1123233: 238, +1123234: 44, +1123235: 71, +1123236: 20, +1123237: 59, +1123238: 3, +1123239: 22, +1123240: 2, +1123241: 19, +1123242: 1, +1123742: 0, +1123743: 56, +1123744: 149, +1123745: 2, +1123746: 144, +1123747: 1, +1123748: 3, +1123749: 4, +1123750: 1, +1123751: 0, +1123752: 17, +1123753: 2, +1123754: 77, +1123755: 1, +1123756: 201, +1123757: 0, +1123758: 36, +1123759: 0, +1123760: 224, +1123761: 59, +1123762: 128, +1123763: 38, +1123764: 128, +1123765: 21, +1123766: 198, +1123767: 36, +1123768: 66, +1123769: 20, +1123770: 0, +1123771: 0, +1123772: 0, +1123773: 0, +1148551: 0, +1148552: 56, +1148553: 250, +1148554: 114, +1148555: 176, +1148556: 85, +1148557: 69, +1148558: 40, +1148559: 1, +1148560: 24, +1148561: 16, +1148562: 98, +1148563: 107, +1148564: 73, +1148565: 198, +1148566: 56, +1148567: 99, +1148568: 44, +1148569: 31, +1148570: 36, +1148571: 23, +1148572: 28, +1148573: 47, +1148574: 20, +1148575: 71, +1148576: 12, +1148577: 224, +1148578: 59, +1148579: 128, +1148580: 38, +1148581: 128, +1148582: 21, +456464: 185, +456465: 0, +456466: 57, +456467: 103, +2175500: 33, +2175501: 0, +2175502: 173, +2175503: 85, +2175504: 231, +2175505: 56, +2175506: 33, +2175507: 32, +2175508: 0, +2175509: 16, +2175510: 33, +1126935: 2, +2175512: 231, +1126937: 1, +2175514: 82, +2175515: 74, +2175516: 35, +2175517: 0, +2175518: 4, +2175519: 163, +2175520: 1, +2175521: 3, +2175522: 1, +1126947: 29, +1126948: 37, +1126949: 9, +2175526: 1, +2175527: 8, +2175528: 66, +2175529: 34, +2175530: 0, +2175531: 13, +456476: 22, +456477: 0, +456478: 15, +456479: 0, +2174332: 27, +2174333: 0, +2174334: 0, +2174335: 223, +2174336: 2, +2174337: 215, +2174338: 1, +2174339: 172, +2174340: 0, +2174341: 245, +2174342: 93, +2174343: 14, +2174344: 61, +2174345: 170, +2174346: 40, +2174347: 35, +2174348: 20, +2174349: 177, +2174350: 11, +2174351: 251, +2174352: 72, +2174353: 255, +2174354: 127, +2174355: 0, +2174356: 0, +2174357: 255, +2174358: 127, +2174359: 229, +2174360: 68, +2174361: 195, +2174362: 8, +2174363: 21, +2174364: 99, +2174365: 68, +2174366: 177, +2174367: 11, +2174368: 169, +2174369: 30, +2174370: 69, +2174371: 1, +2174372: 187, +2174373: 94, +2174374: 179, +2174375: 61, +2174376: 46, +2174377: 41, +2174378: 134, +2174379: 20, +2174380: 24, +2174381: 99, +2174382: 231, +2174383: 28, +2174384: 132, +2174385: 16, +2174386: 195, +2174387: 32, +2174388: 2, +2174389: 223, +2174390: 2, +2174391: 31, +2174392: 34, +2174393: 0, +2174394: 29, +2174395: 99, +2174396: 68, +2174397: 188, +2174398: 114, +2174399: 251, +2174400: 72, +2174401: 22, +2174402: 24, +2174403: 132, +2174404: 20, +2174405: 99, +2174406: 16, +2174407: 33, +2174408: 8, +2174409: 0, +2174410: 4, +2174411: 239, +2174412: 1, +2174413: 173, +2174414: 1, +2174415: 107, +2174416: 1, +2174417: 41, +2174418: 1, +2174419: 231, +2174420: 4, +2174421: 165, +2174422: 4, +2174423: 99, +2174424: 4, +2174425: 195, +2174426: 32, +2174427: 13, +2174428: 178, +2174429: 114, +2174430: 199, +2174431: 113, +2174432: 3, +2174433: 77, +2174434: 149, +2174435: 82, +2174436: 240, +2174437: 61, +2174438: 108, +2174439: 45, +2174440: 9, +2174441: 33, +2174442: 77, +2174443: 24, +2174444: 99, +2174445: 195, +2174446: 32, +2174447: 224, +2174448: 45, +2174449: 137, +2174450: 29, +2174451: 6, +2174452: 13, +2174453: 163, +2174454: 12, +2174455: 14, +2174456: 45, +2174457: 9, +2174458: 45, +2174459: 197, +2174460: 40, +2174461: 129, +2174462: 12, +2174463: 206, +2174464: 85, +2174465: 104, +2174466: 61, +2174467: 162, +2174468: 36, +2174469: 0, +2174470: 20, +2174471: 12, +2174472: 50, +2174473: 8, +2174474: 33, +2174475: 255, +2174476: 127, +2174477: 32, +2174478: 4, +2174479: 99, +2174480: 68, +2174481: 148, +2174482: 82, +2174483: 206, +2174484: 57, +2174485: 8, +2174486: 33, +2174487: 232, +2174488: 28, +2174489: 199, +2174490: 24, +2174491: 166, +2174492: 20, +2174493: 133, +2174494: 16, +2174495: 67, +2174496: 67, +2174497: 8, +2174498: 13, +2174499: 34, +2174500: 4, +2174501: 33, +2174502: 4, +2174503: 79, +2174504: 45, +2174505: 15, +2174506: 41, +2174507: 255, +2174508: 127, +2174509: 66, +2174510: 0, +2174511: 99, +2174512: 68, +2174513: 69, +2174514: 24, +2174515: 99, +2174516: 15, +2174517: 96, +2174518: 45, +2174519: 32, +2174520: 37, +2174521: 192, +2174522: 24, +2174523: 128, +2174524: 16, +2174525: 12, +2174526: 38, +2174527: 169, +2174528: 25, +2174529: 7, +2174530: 9, +2174531: 130, +2174532: 0, +2174533: 195, +2174534: 70, +2174535: 197, +2174536: 192, +2174537: 69, +2174538: 24, +2174539: 99, +2174540: 7, +2174541: 148, +2174542: 62, +2174543: 206, +2174544: 37, +2174545: 8, +2174546: 13, +2174547: 198, +2174548: 4, +2174549: 199, +2174550: 96, +2174551: 67, +2174552: 24, +2174553: 99, +2174554: 195, +2174555: 32, +2174556: 255, +2174557: 27, +2174558: 99, +2174559: 68, +2174560: 223, +2174561: 2, +2174562: 215, +2174563: 1, +2174564: 172, +2174565: 0, +2174566: 245, +2174567: 93, +2174568: 14, +2174569: 61, +2174570: 170, +2174571: 40, +2174572: 35, +2174573: 20, +2174574: 177, +2174575: 11, +2174576: 251, +2174577: 72, +2174578: 255, +2174579: 127, +2174580: 0, +2174581: 0, +2174582: 255, +2174583: 127, +2174584: 229, +2174585: 68, +2174586: 195, +2174587: 8, +2174588: 21, +2174589: 99, +2174590: 68, +2174591: 177, +2174592: 11, +2174593: 169, +2174594: 30, +2174595: 69, +2174596: 1, +2174597: 187, +2174598: 94, +2174599: 179, +2174600: 61, +2174601: 46, +2174602: 41, +2174603: 134, +2174604: 20, +2174605: 24, +2174606: 99, +2174607: 231, +2174608: 28, +2174609: 132, +2174610: 16, +2174611: 195, +2174612: 32, +2174613: 2, +2174614: 223, +2174615: 2, +2174616: 31, +2174617: 34, +2174618: 0, +2174619: 29, +2174620: 99, +2174621: 68, +2174622: 188, +2174623: 114, +2174624: 251, +2174625: 72, +2174626: 22, +2174627: 24, +2174628: 14, +2174629: 0, +2174630: 6, +2174631: 0, +2174632: 2, +2174633: 0, +2174634: 1, +2174635: 0, +2174636: 239, +2174637: 1, +2174638: 173, +2174639: 1, +2174640: 107, +2174641: 1, +2174642: 41, +2174643: 1, +2174644: 231, +2174645: 4, +2174646: 165, +2174647: 4, +2174648: 99, +2174649: 4, +2174650: 195, +2174651: 32, +2174652: 13, +2174653: 178, +2174654: 114, +2174655: 199, +2174656: 113, +2174657: 3, +2174658: 77, +2174659: 149, +2174660: 82, +2174661: 240, +2174662: 61, +2174663: 108, +2174664: 45, +2174665: 9, +2174666: 33, +2174667: 77, +2174668: 24, +2174669: 99, +2174670: 195, +2174671: 32, +2174672: 10, +2174673: 142, +2174674: 29, +2174675: 11, +2174676: 13, +2174677: 168, +2174678: 12, +2174679: 22, +2174680: 0, +2174681: 15, +2174682: 0, +2174683: 7, +2174684: 194, +2174685: 66, +2174686: 224, +2174687: 36, +2174688: 206, +2174689: 85, +2174690: 104, +2174691: 61, +2174692: 162, +2174693: 36, +2174694: 0, +2174695: 20, +2174696: 17, +2174697: 50, +2174698: 8, +2174699: 33, +2174700: 255, +2174701: 127, +2174702: 32, +2174703: 4, +2174704: 99, +2174705: 68, +2174706: 148, +2174707: 82, +2174708: 206, +2174709: 57, +2174710: 8, +2174711: 33, +2174712: 127, +2174713: 8, +2174714: 123, +2174715: 8, +2174716: 86, +2174717: 4, +2174718: 82, +2174719: 4, +2174720: 45, +2174721: 4, +2174722: 41, +2174723: 4, +2174724: 4, +2174725: 34, +2174726: 0, +2174727: 9, +2174728: 79, +2174729: 45, +2174730: 15, +2174731: 41, +2174732: 255, +2174733: 127, +2174734: 64, +2174735: 0, +2174736: 99, +2174737: 68, +2174738: 69, +2174739: 24, +2174740: 99, +2174741: 19, +2174742: 107, +2174743: 45, +2174744: 43, +2174745: 37, +2174746: 203, +2174747: 24, +2174748: 139, +2174749: 16, +2174750: 17, +2174751: 38, +2174752: 174, +2174753: 25, +2174754: 12, +2174755: 9, +2174756: 135, +2174757: 0, +2174758: 109, +2174759: 61, +2174760: 167, +2174761: 36, +2174762: 197, +2174763: 192, +2174764: 69, +2174765: 24, +2174766: 99, +2174767: 10, +2174768: 153, +2174769: 62, +2174770: 211, +2174771: 37, +2174772: 13, +2174773: 13, +2174774: 198, +2174775: 4, +2174776: 211, +2174777: 85, +2174778: 109, +2174779: 196, +2174780: 96, +2174781: 67, +2174782: 24, +2174783: 99, +2174784: 195, +2174785: 32, +2174786: 255, +2174787: 29, +2174788: 0, +2174789: 0, +2174790: 223, +2174791: 2, +2174792: 215, +2174793: 1, +2174794: 172, +2174795: 0, +2174796: 55, +2174797: 95, +2174798: 16, +2174799: 62, +2174800: 108, +2174801: 41, +2174802: 165, +2174803: 20, +2174804: 177, +2174805: 11, +2174806: 251, +2174807: 72, +2174808: 255, +2174809: 127, +2174810: 0, +2174811: 0, +2174812: 255, +2174813: 127, +2174814: 229, +2174815: 68, +2174816: 255, +2174817: 127, +2174818: 35, +2174819: 0, +2174820: 19, +2174821: 177, +2174822: 11, +2174823: 169, +2174824: 30, +2174825: 69, +2174826: 1, +2174827: 187, +2174828: 94, +2174829: 179, +2174830: 61, +2174831: 46, +2174832: 41, +2174833: 134, +2174834: 20, +2174835: 24, +2174836: 99, +2174837: 231, +2174838: 28, +2174839: 132, +2174840: 16, +2174841: 195, +2174842: 32, +2174843: 2, +2174844: 223, +2174845: 2, +2174846: 31, +2174847: 36, +2174848: 0, +2174849: 5, +2174850: 188, +2174851: 114, +2174852: 251, +2174853: 72, +2174854: 22, +2174855: 24, +2174856: 75, +2174857: 24, +2174858: 99, +2174859: 9, +2174860: 134, +2174861: 44, +2174862: 130, +2174863: 28, +2174864: 72, +2174865: 68, +2174866: 35, +2174867: 44, +2174868: 255, +2174869: 127, +2174870: 35, +2174871: 0, +2174872: 5, +2174873: 178, +2174874: 114, +2174875: 199, +2174876: 113, +2174877: 3, +2174878: 77, +2174879: 83, +2174880: 24, +2174881: 99, +2174882: 1, +2174883: 255, +2174884: 127, +2174885: 35, +2174886: 0, +2174887: 197, +2174888: 88, +2174889: 224, +2174890: 85, +2174891: 111, +2174892: 45, +2174893: 200, +2174894: 40, +2174895: 132, +2174896: 36, +2174897: 97, +2174898: 28, +2174899: 147, +2174900: 92, +2174901: 142, +2174902: 72, +2174903: 6, +2174904: 44, +2174905: 72, +2174906: 68, +2174907: 4, +2174908: 20, +2174909: 2, +2174910: 16, +2174911: 187, +2174912: 94, +2174913: 0, +2174914: 8, +2174915: 0, +2174916: 0, +2174917: 115, +2174918: 90, +2174919: 173, +2174920: 65, +2174921: 231, +2174922: 40, +2174923: 78, +2174924: 37, +2174925: 167, +2174926: 36, +2174927: 99, +2174928: 32, +2174929: 64, +2174930: 24, +2174931: 25, +2174932: 0, +2174933: 18, +2174934: 0, +2174935: 96, +2174936: 52, +2174937: 64, +2174938: 24, +2174939: 68, +2174940: 8, +2174941: 127, +2174942: 25, +2174943: 156, +2174944: 127, +2174945: 0, +2174946: 4, +2174947: 0, +2174948: 0, +2174949: 15, +2174950: 52, +2174951: 11, +2174952: 36, +2174953: 8, +2174954: 24, +2174955: 115, +2174956: 17, +2174957: 204, +2174958: 16, +2174959: 136, +2174960: 12, +2174961: 101, +2174962: 8, +1126387: 0, +1126388: 56, +1126389: 250, +1126390: 114, +1126391: 176, +1126392: 85, +1126393: 69, +1126394: 40, +1126395: 1, +1126396: 24, +1126397: 16, +1126398: 98, +1126399: 107, +1126400: 73, +1126401: 198, +1126402: 56, +1126403: 99, +1126404: 44, +1126405: 239, +1126406: 47, +1126407: 13, +1126408: 3, +1126409: 9, +1126410: 2, +1126411: 69, +1126412: 1, +1126413: 63, +1126414: 24, +1126415: 20, +1126416: 16, +1126417: 10, +1126418: 8, +2174995: 32, +2174996: 255, +2174997: 29, +2174998: 0, +2174999: 0, +2175000: 223, +2175001: 2, +2175002: 215, +2175003: 1, +2175004: 172, +2175005: 0, +2175006: 55, +2175007: 95, +2175008: 16, +2175009: 62, +2175010: 108, +2175011: 41, +2175012: 165, +2175013: 20, +2175014: 177, +2175015: 11, +2175016: 251, +2175017: 72, +2175018: 255, +2175019: 127, +2175020: 0, +2175021: 0, +2175022: 255, +2175023: 127, +2175024: 229, +2175025: 68, +2175026: 255, +2175027: 127, +2175028: 35, +2175029: 0, +2175030: 19, +2175031: 177, +2175032: 11, +2175033: 169, +2175034: 30, +2175035: 69, +2175036: 1, +2175037: 187, +2175038: 94, +2175039: 179, +2175040: 61, +2175041: 46, +2175042: 41, +2175043: 134, +2175044: 20, +2175045: 24, +2175046: 99, +2175047: 231, +2175048: 28, +2175049: 132, +2175050: 16, +2175051: 195, +2175052: 32, +2175053: 2, +2175054: 223, +2175055: 2, +2175056: 31, +2175057: 36, +2175058: 0, +2175059: 5, +2175060: 188, +2175061: 114, +2175062: 251, +2175063: 72, +2175064: 22, +2175065: 24, +2175066: 75, +2175067: 24, +2175068: 99, +2175069: 9, +2175070: 134, +2175071: 44, +2175072: 130, +2175073: 28, +2175074: 72, +2175075: 68, +2175076: 35, +2175077: 44, +2175078: 255, +2175079: 127, +2175080: 35, +2175081: 0, +2175082: 5, +2175083: 178, +2175084: 114, +2175085: 199, +2175086: 113, +2175087: 3, +2175088: 77, +2175089: 83, +2175090: 24, +2175091: 99, +2175092: 1, +2175093: 255, +2175094: 127, +2175095: 35, +2175096: 0, +2175097: 197, +2175098: 88, +2175099: 224, +2175100: 85, +2175101: 111, +2175102: 45, +2175103: 200, +2175104: 40, +2175105: 132, +2175106: 36, +2175107: 97, +2175108: 28, +2175109: 147, +2175110: 92, +2175111: 142, +2175112: 72, +2175113: 6, +2175114: 44, +2175115: 72, +2175116: 68, +2175117: 4, +2175118: 20, +2175119: 2, +2175120: 16, +2175121: 187, +2175122: 94, +2175123: 0, +2175124: 8, +2175125: 0, +2175126: 0, +2175127: 12, +2175128: 33, +2175129: 168, +2175130: 20, +2175131: 2, +2175132: 0, +2175133: 235, +2175134: 24, +2175135: 68, +2175136: 24, +2175137: 33, +2175138: 20, +2175139: 0, +2175140: 12, +2175141: 25, +2175142: 0, +2175143: 18, +2175144: 0, +2175145: 72, +2175146: 68, +2175147: 6, +2175148: 44, +2175149: 68, +2175150: 8, +2175151: 127, +2175152: 25, +2175153: 244, +2175154: 61, +2175155: 0, +2175156: 4, +2175157: 0, +2175158: 0, +2175159: 15, +2175160: 52, +2175161: 11, +2175162: 36, +2175163: 8, +2175164: 24, +2175165: 115, +2175166: 17, +2175167: 204, +2175168: 16, +2175169: 136, +2175170: 12, +2175171: 101, +2175172: 8, +2175173: 115, +2175174: 78, +2175175: 173, +2175176: 53, +2175177: 231, +2175178: 28, +2175179: 99, +2175180: 12, +2175181: 24, +2175182: 99, +2175183: 4, +2175184: 8, +2175185: 255, +2175186: 127, +2175187: 35, +2175188: 0, +2175189: 69, +2175190: 24, +2175191: 99, +2175192: 7, +2175193: 82, +2175194: 25, +2175195: 171, +2175196: 24, +2175197: 103, +2175198: 20, +2175199: 68, +2175200: 16, +2175201: 75, +2175202: 24, +2175203: 99, +2175204: 195, +2175205: 32, +2175206: 255, +2175207: 27, +2175208: 0, +2175209: 0, +2175210: 223, +2175211: 2, +2175212: 215, +2175213: 1, +2175214: 172, +2175215: 0, +2175216: 187, +2175217: 94, +2175218: 179, +2175219: 61, +2175220: 46, +2175221: 41, +2175222: 134, +2175223: 20, +2175224: 177, +2175225: 11, +2175226: 251, +2175227: 72, +2175228: 255, +2175229: 127, +2175230: 0, +2175231: 0, +2175232: 255, +2175233: 127, +2175234: 229, +2175235: 68, +2175236: 195, +2175237: 8, +2175238: 7, +2175239: 3, +2175240: 32, +2175241: 177, +2175242: 11, +2175243: 169, +2175244: 30, +2175245: 69, +2175246: 1, +2175247: 199, +2175248: 32, +2175249: 5, +2175250: 24, +2175251: 99, +2175252: 231, +2175253: 28, +2175254: 132, +2175255: 16, +2175256: 195, +2175257: 32, +2175258: 2, +2175259: 223, +2175260: 2, +2175261: 31, +2175262: 34, +2175263: 0, +2175264: 7, +2175265: 3, +2175266: 32, +2175267: 188, +2175268: 114, +2175269: 251, +2175270: 72, +2175271: 22, +2175272: 24, +2175273: 83, +2175274: 24, +2175275: 99, +2175276: 1, +2175277: 255, +2175278: 127, +2175279: 35, +2175280: 0, +2175281: 5, +2175282: 178, +2175283: 114, +2175284: 199, +2175285: 113, +2175286: 3, +2175287: 77, +2175288: 83, +2175289: 24, +2175290: 99, +2175291: 1, +2175292: 255, +2175293: 127, +2175294: 35, +2175295: 0, +2175296: 27, +2175297: 244, +2175298: 62, +2175299: 46, +2175300: 38, +2175301: 104, +2175302: 13, +2175303: 148, +2175304: 62, +2175305: 206, +2175306: 37, +2175307: 8, +2175308: 13, +2175309: 132, +2175310: 20, +2175311: 148, +2175312: 110, +2175313: 206, +2175314: 85, +2175315: 8, +2175316: 61, +2175317: 132, +2175318: 44, +2175319: 169, +2175320: 30, +2175321: 177, +2175322: 11, +2175323: 255, +2175324: 127, +2175325: 35, +2175326: 0, +2175327: 27, +2175328: 70, +2175329: 18, +2175330: 69, +2175331: 1, +2175332: 192, +2175333: 0, +2175334: 82, +2175335: 54, +2175336: 140, +2175337: 29, +2175338: 198, +2175339: 4, +2175340: 132, +2175341: 4, +2175342: 49, +2175343: 98, +2175344: 107, +2175345: 73, +2175346: 165, +2175347: 48, +2175348: 33, +2175349: 32, +2175350: 21, +2175351: 0, +2175352: 91, +2175353: 2, +2175354: 255, +2175355: 127, +2175356: 35, +2175357: 0, +2175358: 89, +2175359: 24, +2175360: 99, +2175361: 1, +2175362: 255, +2175363: 127, +2175364: 35, +2175365: 0, +2175366: 29, +2175367: 123, +2175368: 71, +2175369: 82, +2175370: 46, +2175371: 198, +2175372: 0, +2175373: 99, +2175374: 0, +2175375: 181, +2175376: 58, +2175377: 16, +2175378: 34, +2175379: 107, +2175380: 17, +2175381: 8, +2175382: 5, +2175383: 255, +2175384: 127, +2175385: 181, +2175386: 54, +2175387: 173, +2175388: 25, +2175389: 41, +2175390: 9, +2175391: 29, +2175392: 56, +2175393: 20, +2175394: 24, +2175395: 10, +2175396: 0, +2175397: 255, +2175398: 27, +2175399: 0, +2175400: 0, +2175401: 223, +2175402: 2, +2175403: 215, +2175404: 1, +2175405: 172, +2175406: 0, +2175407: 187, +2175408: 94, +2175409: 179, +2175410: 61, +2175411: 46, +2175412: 41, +2175413: 134, +2175414: 20, +2175415: 177, +2175416: 11, +2175417: 251, +2175418: 72, +2175419: 255, +2175420: 127, +2175421: 0, +2175422: 0, +2175423: 255, +2175424: 127, +2175425: 229, +2175426: 68, +2175427: 195, +2175428: 8, +2175429: 7, +2175430: 3, +2175431: 32, +2175432: 177, +2175433: 11, +2175434: 169, +2175435: 30, +2175436: 69, +2175437: 1, +2175438: 199, +2175439: 32, +2175440: 5, +2175441: 24, +2175442: 99, +2175443: 231, +2175444: 28, +2175445: 132, +2175446: 16, +2175447: 195, +2175448: 32, +2175449: 2, +2175450: 223, +2175451: 2, +2175452: 31, +2175453: 34, +2175454: 0, +2175455: 7, +2175456: 3, +2175457: 32, +2175458: 188, +2175459: 114, +2175460: 251, +2175461: 72, +2175462: 22, +2175463: 24, +2175464: 83, +2175465: 24, +2175466: 99, +2175467: 1, +2175468: 255, +2175469: 127, +2175470: 35, +2175471: 0, +2175472: 5, +2175473: 178, +2175474: 114, +2175475: 199, +2175476: 113, +2175477: 3, +2175478: 77, +2175479: 83, +2175480: 24, +2175481: 99, +2175482: 1, +2175483: 255, +2175484: 127, +2175485: 35, +2175486: 0, +2175487: 27, +2175488: 170, +2175489: 21, +2175490: 5, +2175491: 1, +2175492: 129, +2175493: 0, +2175494: 74, +2175495: 21, +2175496: 231, +2175497: 12, +2175498: 99, +2175499: 0, +1126924: 0, +1126925: 56, +1126926: 159, +1126927: 3, +1126928: 191, +1126929: 1, +1126930: 15, +1126931: 0, +1126932: 5, +1126933: 0, +1126934: 31, +2175511: 32, +1126936: 91, +2175513: 56, +1126938: 186, +1126939: 0, +1126940: 17, +1126941: 0, +1126942: 114, +1126943: 79, +1126944: 173, +1126945: 54, +1126946: 200, +2175523: 32, +2175524: 195, +2175525: 32, +1126950: 31, +1126951: 126, +1126952: 21, +1126953: 84, +1126954: 10, +1126955: 40, +2175532: 74, +2175533: 69, +2175534: 132, +2175535: 44, +2175536: 0, +2175537: 20, +2175538: 0, +2175539: 0, +2175540: 18, +2175541: 0, +2175542: 248, +2175543: 1, +2175544: 82, +2175545: 74, +2175546: 35, +2175547: 0, +2175548: 89, +2175549: 24, +2175550: 99, +2175551: 1, +2175552: 255, +2175553: 127, +2175554: 35, +2175555: 0, +2175556: 29, +2175557: 123, +2175558: 71, +2175559: 82, +2175560: 46, +2175561: 198, +2175562: 0, +2175563: 99, +2175564: 0, +2175565: 181, +2175566: 58, +2175567: 16, +2175568: 34, +2175569: 107, +2175570: 17, +2175571: 8, +2175572: 5, +2175573: 255, +2175574: 127, +2175575: 181, +2175576: 54, +2175577: 173, +2175578: 25, +2175579: 41, +2175580: 9, +2175581: 29, +2175582: 56, +2175583: 20, +2175584: 24, +2175585: 10, +2175586: 0, +2175587: 255, +1127012: 0, +1127013: 56, +1127014: 250, +1127015: 114, +1127016: 176, +1127017: 85, +1127018: 69, +2175595: 172, +1127020: 1, +2175597: 115, +2175598: 90, +2175599: 173, +2175600: 65, +2175601: 8, +2175602: 45, +2175603: 99, +2175604: 24, +2175605: 177, +2175606: 11, +2175607: 251, +2175608: 72, +2175609: 255, +2175610: 127, +2175611: 0, +2175612: 0, +2175613: 255, +2175614: 127, +2175615: 229, +2175616: 68, +2175617: 195, +2175618: 8, +2175619: 21, +2175620: 3, +2175621: 32, +2175622: 177, +2175623: 11, +2175624: 169, +2175625: 30, +2175626: 69, +2175627: 1, +2175628: 187, +2175629: 94, +2175630: 179, +2175631: 61, +2175632: 46, +2175633: 41, +2175634: 134, +2175635: 20, +2175636: 24, +2175637: 99, +2175638: 82, +2175639: 74, +2175640: 140, +2175641: 49, +2175642: 195, +2175643: 32, +2175644: 2, +2175645: 223, +2175646: 2, +2175647: 31, +2175648: 34, +2175649: 0, +2175650: 29, +2175651: 3, +2175652: 32, +2175653: 188, +2175654: 114, +2175655: 251, +2175656: 72, +2175657: 22, +2175658: 24, +2175659: 144, +2175660: 22, +2175661: 20, +2175662: 14, +2175663: 229, +2175664: 0, +2175665: 65, +2175666: 0, +2175667: 198, +2175668: 48, +2175669: 98, +2175670: 36, +2175671: 33, +2175672: 20, +2175673: 0, +2175674: 8, +2175675: 255, +2175676: 126, +2175677: 64, +2175678: 24, +2175679: 255, +2175680: 127, +2175681: 35, +2175682: 0, +2175683: 21, +2175684: 178, +2175685: 114, +2175686: 199, +2175687: 113, +2175688: 3, +2175689: 77, +2175690: 164, +2175691: 48, +1127116: 17, +2175693: 36, +2175694: 64, +2175695: 24, +2175696: 0, +2175697: 8, +2175698: 82, +2175699: 94, +2175700: 107, +2175701: 65, +2175702: 228, +2175703: 48, +2175704: 96, +2175705: 28, +2175706: 67, +2175707: 24, +2175708: 99, +2175709: 1, +2175710: 156, +2175711: 127, +2175712: 35, +2175713: 0, +2175714: 67, +2175715: 24, +2175716: 99, +2175717: 23, +2175718: 224, +2175719: 32, +2175720: 167, +2175721: 29, +2175722: 33, +2175723: 45, +2175724: 160, +2175725: 40, +2175726: 32, +2175727: 24, +2175728: 169, +2175729: 38, +2175730: 233, +2175731: 37, +2175732: 66, +2175733: 21, +2175734: 32, +2175735: 4, +2175736: 169, +2175737: 38, +2175738: 130, +2175739: 0, +2175740: 113, +2175741: 39, +2175742: 35, +2175743: 0, +2175744: 27, +2175745: 191, +2175746: 2, +2175747: 127, +2175748: 1, +2175749: 21, +2175750: 0, +2175751: 149, +2175752: 79, +2175753: 174, +2175754: 66, +2175755: 6, +2175756: 50, +2175757: 35, +2175758: 37, +2175759: 42, +2175760: 21, +2175761: 199, +2175762: 20, +2175763: 99, +2175764: 20, +2175765: 2, +2175766: 4, +2175767: 161, +2175768: 12, +2175769: 64, +2175770: 24, +2175771: 181, +2175772: 126, +2175773: 35, +2175774: 0, +2175775: 25, +2175776: 186, +2175777: 94, +2175778: 54, +2175779: 78, +2175780: 178, +2175781: 61, +2175782: 46, +2175783: 45, +2175784: 203, +2175785: 32, +2175786: 72, +2175787: 16, +2175788: 2, +2175789: 8, +2175790: 119, +2175791: 26, +2175792: 209, +2175793: 33, +2175794: 199, +2175795: 36, +2175796: 66, +2175797: 0, +2175798: 28, +2175799: 107, +2175800: 24, +2175801: 99, +2175802: 194, +2175803: 32, +2175804: 24, +2175805: 12, +2175806: 0, +2175807: 0, +2175808: 34, +2175809: 93, +2175810: 99, +2175811: 68, +2175812: 64, +2175813: 24, +2175814: 192, +2175815: 36, +2175816: 160, +2175817: 28, +2175818: 128, +2175819: 20, +2175820: 64, +2175821: 16, +2175822: 223, +2175823: 22, +2175824: 215, +2175825: 21, +2175826: 238, +2175827: 20, +2175828: 134, +2175829: 20, +2175830: 67, +2175831: 24, +2175832: 99, +2175833: 3, +2175834: 223, +2175835: 22, +2175836: 0, +2175837: 8, +2175838: 255, +2175839: 27, +2175840: 0, +2175841: 0, +2175842: 223, +2175843: 2, +2175844: 215, +2175845: 1, +2175846: 172, +2175847: 0, +2175848: 115, +2175849: 90, +2175850: 173, +2175851: 65, +2175852: 8, +2175853: 45, +2175854: 99, +2175855: 24, +2175856: 177, +2175857: 11, +2175858: 251, +2175859: 72, +2175860: 255, +2175861: 127, +2175862: 0, +2175863: 0, +2175864: 255, +2175865: 127, +2175866: 229, +2175867: 68, +2175868: 195, +2175869: 8, +2175870: 21, +2175871: 3, +2175872: 32, +2175873: 177, +2175874: 11, +2175875: 169, +2175876: 30, +2175877: 69, +2175878: 1, +2175879: 187, +2175880: 94, +2175881: 179, +2175882: 61, +2175883: 46, +2175884: 41, +2175885: 134, +2175886: 20, +2175887: 24, +2175888: 99, +2175889: 82, +2175890: 74, +2175891: 140, +2175892: 49, +2175893: 195, +2175894: 32, +2175895: 2, +2175896: 223, +2175897: 2, +2175898: 31, +2175899: 34, +2175900: 0, +2175901: 15, +2175902: 3, +2175903: 32, +2175904: 188, +2175905: 114, +2175906: 251, +2175907: 72, +2175908: 22, +2175909: 24, +2175910: 10, +2175911: 29, +2175912: 199, +2175913: 24, +2175914: 69, +2175915: 12, +2175916: 3, +2175917: 4, +2175918: 75, +2175919: 24, +2175920: 99, +2175921: 1, +2175922: 255, +2175923: 127, +2175924: 35, +2175925: 0, +2175926: 13, +2175927: 178, +2175928: 114, +2175929: 199, +2175930: 113, +2175931: 3, +2175932: 77, +2175933: 197, +2175934: 52, +2175935: 130, +2175936: 40, +2175937: 64, +2175938: 32, +2175939: 0, +2175940: 8, +2175941: 71, +2175942: 24, +2175943: 99, +2175944: 5, +2175945: 52, +2175946: 127, +2175947: 0, +2175948: 125, +2175949: 247, +2175950: 91, +2175951: 35, +2175952: 0, +2175953: 27, +2175954: 191, +2175955: 42, +2175956: 127, +2175957: 41, +2175958: 21, +2175959: 40, +2175960: 63, +2175961: 54, +2175962: 124, +2175963: 45, +2175964: 23, +2175965: 33, +2175966: 211, +2175967: 28, +2175968: 176, +2175969: 20, +2175970: 108, +2175971: 16, +2175972: 72, +2175973: 12, +2175974: 4, +2175975: 8, +2175976: 82, +2175977: 74, +2175978: 231, +2175979: 28, +2175980: 255, +2175981: 127, +2175982: 35, +2175983: 0, +2175984: 67, +2175985: 24, +2175986: 99, +2175987: 18, +2175988: 84, +2175989: 42, +2175990: 175, +2175991: 25, +2175992: 41, +2175993: 21, +2175994: 132, +2175995: 8, +2175996: 33, +2175997: 8, +2175998: 176, +2175999: 22, +2176000: 192, +2176001: 29, +2176002: 224, +2176003: 20, +2176004: 96, +2176005: 20, +2176006: 231, +2176007: 194, +2176008: 138, +2176009: 1, +2176010: 255, +2176011: 127, +2176012: 35, +2176013: 0, +2176014: 69, +2176015: 24, +2176016: 99, +2176017: 17, +2176018: 23, +2176019: 76, +2176020: 15, +2176021: 40, +2176022: 9, +2176023: 36, +2176024: 7, +2176025: 28, +2176026: 5, +2176027: 20, +2176028: 3, +2176029: 12, +2176030: 2, +2176031: 8, +2176032: 1, +2176033: 4, +2176034: 59, +2176035: 93, +2176036: 195, +2176037: 32, +2176038: 35, +2176039: 0, +2176040: 22, +2176041: 191, +2176042: 2, +2176043: 127, +2176044: 1, +2176045: 21, +2176046: 0, +2176047: 73, +2176048: 55, +2176049: 192, +2176050: 62, +2176051: 128, +2176052: 29, +2176053: 192, +2176054: 4, +2176055: 0, +2176056: 1, +2176057: 192, +2176058: 0, +2176059: 128, +2176060: 0, +2176061: 64, +2176062: 0, +2176063: 52, +2176064: 68, +2176065: 127, +2176066: 255, +2176067: 1, +2176068: 0, +2176069: 0, +2176070: 255, +2176071: 27, +2176072: 0, +2176073: 0, +2176074: 223, +2176075: 2, +2176076: 215, +2176077: 1, +2176078: 172, +2176079: 0, +2176080: 115, +2176081: 90, +2176082: 173, +2176083: 65, +2176084: 8, +2176085: 45, +2176086: 99, +2176087: 24, +2176088: 177, +2176089: 11, +2176090: 251, +2176091: 72, +2176092: 255, +2176093: 127, +2176094: 0, +2176095: 0, +2176096: 255, +2176097: 127, +2176098: 229, +2176099: 68, +2176100: 195, +2176101: 8, +2176102: 21, +2176103: 3, +2176104: 32, +2176105: 177, +2176106: 11, +2176107: 169, +2176108: 30, +2176109: 69, +2176110: 1, +2176111: 187, +2176112: 94, +2176113: 179, +2176114: 61, +2176115: 46, +2176116: 41, +2176117: 134, +2176118: 20, +2176119: 24, +2176120: 99, +2176121: 82, +2176122: 74, +2176123: 140, +2176124: 49, +2176125: 195, +2176126: 32, +2176127: 2, +2176128: 223, +2176129: 2, +2176130: 31, +2176131: 34, +2176132: 0, +2176133: 15, +2176134: 3, +2176135: 32, +2176136: 188, +2176137: 114, +2176138: 251, +2176139: 72, +2176140: 22, +2176141: 24, +2176142: 10, +2176143: 29, +2176144: 199, +2176145: 24, +2176146: 69, +2176147: 12, +2176148: 3, +2176149: 4, +2176150: 75, +2176151: 24, +2176152: 99, +2176153: 1, +2176154: 255, +2176155: 127, +2176156: 35, +2176157: 0, +2176158: 5, +2176159: 178, +2176160: 114, +2176161: 199, +2176162: 113, +2176163: 3, +2176164: 77, +2176165: 83, +2176166: 24, +2176167: 99, +2176168: 1, +2176169: 255, +2176170: 127, +2176171: 35, +2176172: 0, +2176173: 27, +2176174: 191, +2176175: 2, +2176176: 127, +2176177: 1, +2176178: 21, +2176179: 0, +2176180: 140, +2176181: 69, +2176182: 107, +2176183: 61, +2176184: 41, +2176185: 53, +2176186: 8, +2176187: 45, +2176188: 198, +2176189: 36, +2176190: 165, +2176191: 24, +2176192: 99, +2176193: 16, +2176194: 66, +2176195: 8, +2176196: 19, +2176197: 127, +2176198: 0, +2176199: 125, +2176200: 255, +2176201: 127, +2176202: 35, +2176203: 0, +2176204: 67, +2176205: 24, +2176206: 99, +2176207: 19, +2176208: 250, +2176209: 107, +2176210: 179, +2176211: 110, +2176212: 11, +2176213: 78, +2176214: 6, +2176215: 49, +2176216: 65, +2176217: 12, +2176218: 176, +2176219: 22, +2176220: 192, +2176221: 29, +2176222: 224, +2176223: 20, +2176224: 96, +2176225: 20, +2176226: 72, +2176227: 69, +2176228: 195, +2176229: 64, +2176230: 35, +2176231: 0, +2176232: 69, +2176233: 24, +2176234: 99, +2176235: 17, +2176236: 244, +2176237: 65, +2176238: 145, +2176239: 53, +2176240: 79, +2176241: 45, +2176242: 13, +2176243: 37, +2176244: 203, +2176245: 28, +2176246: 137, +2176247: 20, +2176248: 68, +2176249: 12, +2176250: 33, +2176251: 4, +2176252: 153, +2176253: 86, +2176254: 195, +2176255: 32, +2176256: 35, +2176257: 0, +2176258: 197, +2176259: 96, +2176260: 199, +2176261: 64, +2176262: 73, +2176263: 24, +2176264: 99, +2176265: 67, +2176266: 255, +2176267: 127, +2176268: 1, +2176269: 0, +2176270: 0, +2176271: 255, +2176272: 29, +2176273: 0, +2176274: 0, +2176275: 223, +2176276: 2, +2176277: 215, +2176278: 1, +2176279: 172, +2176280: 0, +2176281: 115, +2176282: 90, +2176283: 173, +2176284: 65, +2176285: 8, +2176286: 45, +2176287: 99, +2176288: 24, +2176289: 177, +2176290: 11, +2176291: 251, +2176292: 72, +2176293: 255, +2176294: 127, +2176295: 0, +2176296: 0, +2176297: 255, +2176298: 127, +2176299: 229, +2176300: 68, +2176301: 255, +2176302: 127, +2176303: 34, +2176304: 0, +2176305: 20, +2176306: 56, +2176307: 177, +2176308: 11, +2176309: 169, +2176310: 30, +2176311: 69, +2176312: 1, +2176313: 187, +2176314: 94, +2176315: 179, +2176316: 61, +2176317: 46, +2176318: 41, +2176319: 134, +2176320: 20, +2176321: 24, +2176322: 99, +2176323: 82, +2176324: 74, +2176325: 140, +2176326: 49, +2176327: 195, +2176328: 32, +2176329: 2, +2176330: 223, +2176331: 2, +2176332: 31, +2176333: 35, +2176334: 0, +2176335: 28, +2176336: 56, +2176337: 188, +2176338: 114, +2176339: 251, +2176340: 72, +2176341: 22, +2176342: 24, +2176343: 144, +2176344: 22, +2176345: 20, +2176346: 14, +2176347: 229, +2176348: 0, +2176349: 65, +2176350: 0, +2176351: 198, +2176352: 48, +2176353: 98, +2176354: 36, +2176355: 33, +2176356: 20, +2176357: 0, +2176358: 8, +2176359: 255, +2176360: 126, +2176361: 24, +2176362: 99, +2176363: 255, +2176364: 127, +2176365: 34, +2176366: 0, +2176367: 14, +2176368: 56, +2176369: 178, +2176370: 114, +2176371: 199, +2176372: 113, +2176373: 3, +2176374: 77, +2176375: 73, +2176376: 61, +2176377: 196, +2176378: 48, +2176379: 64, +2176380: 36, +2176381: 0, +2176382: 16, +2176383: 75, +2176384: 24, +2176385: 99, +2176386: 1, +2176387: 255, +2176388: 127, +2176389: 34, +2176390: 0, +2176391: 0, +2176392: 56, +2176393: 69, +2176394: 24, +2176395: 99, +2176396: 17, +2176397: 46, +2176398: 31, +2176399: 130, +2176400: 21, +2176401: 128, +2176402: 12, +2176403: 0, +2176404: 4, +2176405: 154, +2176406: 103, +2176407: 76, +2176408: 42, +2176409: 163, +2176410: 17, +2176411: 32, +2176412: 4, +2176413: 255, +2176414: 3, +2176415: 195, +2176416: 32, +2176417: 34, +2176418: 0, +2176419: 28, +2176420: 56, +2176421: 191, +2176422: 2, +2176423: 127, +2176424: 1, +2176425: 21, +2176426: 0, +2176427: 73, +2176428: 55, +2176429: 192, +2176430: 62, +2176431: 128, +2176432: 29, +2176433: 192, +2176434: 4, +2176435: 0, +2176436: 1, +2176437: 192, +2176438: 0, +2176439: 128, +2176440: 0, +2176441: 64, +2176442: 0, +2176443: 19, +2176444: 127, +2176445: 0, +2176446: 125, +2176447: 255, +2176448: 127, +2176449: 34, +2176450: 0, +2176451: 0, +2176452: 56, +2176453: 62, +2176454: 0, +2176455: 24, +2176456: 56, +2176457: 157, +2176458: 85, +2176459: 22, +2176460: 24, +2176461: 13, +2176462: 16, +2176463: 159, +2176464: 75, +2176465: 55, +2176466: 63, +2176467: 208, +2176468: 54, +2176469: 105, +2176470: 46, +2176471: 8, +2176472: 38, +2176473: 166, +2176474: 29, +2176475: 37, +2176476: 17, +2176477: 197, +2176478: 8, +2176479: 3, +2176480: 0, +2176481: 197, +2176482: 96, +2176483: 255, +2176484: 27, +2176485: 0, +2176486: 0, +2176487: 223, +2176488: 2, +2176489: 215, +2176490: 1, +2176491: 172, +2176492: 0, +2176493: 187, +2176494: 94, +2176495: 179, +2176496: 61, +2176497: 46, +2176498: 41, +2176499: 134, +2176500: 20, +2176501: 177, +2176502: 11, +2176503: 251, +2176504: 72, +2176505: 255, +2176506: 127, +2176507: 0, +2176508: 0, +2176509: 255, +2176510: 127, +2176511: 229, +2176512: 68, +2176513: 195, +2176514: 8, +2176515: 7, +2176516: 3, +2176517: 32, +2176518: 177, +2176519: 11, +2176520: 169, +2176521: 30, +2176522: 69, +2176523: 1, +2176524: 199, +2176525: 32, +2176526: 5, +2176527: 223, +2176528: 14, +2176529: 63, +2176530: 14, +2176531: 127, +2176532: 13, +2176533: 195, +2176534: 32, +2176535: 2, +2176536: 223, +2176537: 2, +2176538: 31, +2176539: 34, +2176540: 0, +2176541: 7, +2176542: 3, +2176543: 32, +2176544: 188, +2176545: 114, +2176546: 251, +2176547: 72, +2176548: 22, +2176549: 24, +2176550: 205, +2176551: 32, +2176552: 7, +2176553: 159, +2176554: 12, +2176555: 151, +2176556: 54, +2176557: 63, +2176558: 14, +2176559: 255, +2176560: 127, +2176561: 35, +2176562: 0, +2176563: 5, +2176564: 178, +2176565: 114, +2176566: 199, +2176567: 113, +2176568: 3, +2176569: 77, +2176570: 199, +2176571: 24, +2176572: 13, +2176573: 247, +2176574: 94, +2176575: 82, +2176576: 74, +2176577: 107, +2176578: 45, +2176579: 132, +2176580: 16, +2176581: 63, +2176582: 14, +2176583: 82, +2176584: 74, +2176585: 255, +2176586: 127, +2176587: 35, +2176588: 0, +2176589: 197, +2176590: 24, +2176591: 21, +2176592: 236, +2176593: 83, +2176594: 9, +2176595: 67, +2176596: 197, +2176597: 37, +2176598: 194, +2176599: 16, +2176600: 153, +2176601: 12, +2176602: 114, +2176603: 12, +2176604: 73, +2176605: 12, +2176606: 65, +2176607: 12, +2176608: 9, +2176609: 67, +2176610: 119, +2176611: 12, +2176612: 255, +2176613: 127, +2176614: 35, +2176615: 0, +2176616: 197, +2176617: 32, +2176618: 21, +2176619: 58, +2176620: 58, +2176621: 179, +2176622: 45, +2176623: 44, +2176624: 33, +2176625: 132, +2176626: 16, +2176627: 85, +2176628: 85, +2176629: 207, +2176630: 56, +2176631: 105, +2176632: 28, +2176633: 3, +2176634: 4, +2176635: 179, +2176636: 45, +2176637: 207, +2176638: 56, +2176639: 255, +2176640: 127, +2176641: 35, +2176642: 0, +2176643: 16, +2176644: 150, +2176645: 5, +2176646: 214, +2176647: 4, +2176648: 86, +2176649: 4, +2176650: 204, +2176651: 8, +2176652: 168, +2176653: 8, +2176654: 133, +2176655: 8, +2176656: 130, +2176657: 8, +2176658: 39, +2176659: 16, +2176660: 5, +2176661: 67, +2176662: 12, +2176663: 3, +2176664: 194, +2176665: 14, +2176666: 3, +2176667: 5, +2176668: 12, +2176669: 255, +2176670: 127, +2176671: 35, +2176672: 0, +2176673: 197, +2176674: 32, +2176675: 12, +2176676: 12, +2176677: 20, +2176678: 8, +2176679: 12, +2176680: 4, +2176681: 4, +2176682: 0, +2176683: 0, +2176684: 14, +2176685: 8, +2176686: 9, +2176687: 4, +2176688: 4, +2176689: 34, +2176690: 0, +2176691: 3, +2176692: 8, +2176693: 12, +2176694: 9, +2176695: 4, +2176696: 195, +2176697: 32, +2176698: 255, +2176699: 27, +2176700: 0, +2176701: 0, +2176702: 223, +2176703: 2, +2176704: 215, +2176705: 1, +2176706: 172, +2176707: 0, +2176708: 53, +2176709: 107, +2176710: 14, +2176711: 74, +2176712: 106, +2176713: 53, +2176714: 163, +2176715: 32, +2176716: 177, +2176717: 11, +2176718: 251, +2176719: 72, +2176720: 255, +2176721: 127, +2176722: 0, +2176723: 0, +2176724: 255, +2176725: 127, +2176726: 229, +2176727: 68, +2176728: 195, +2176729: 8, +2176730: 0, +2176731: 3, +2176732: 194, +2176733: 18, +2176734: 17, +2176735: 169, +2176736: 30, +2176737: 69, +2176738: 1, +2176739: 187, +2176740: 94, +2176741: 179, +2176742: 61, +2176743: 46, +2176744: 41, +2176745: 134, +2176746: 20, +2176747: 223, +2176748: 14, +2176749: 63, +2176750: 14, +2176751: 127, +2176752: 13, +2176753: 195, +2176754: 32, +2176755: 2, +2176756: 223, +2176757: 2, +2176758: 31, +2176759: 34, +2176760: 0, +2176761: 7, +2176762: 3, +2176763: 32, +2176764: 188, +2176765: 114, +2176766: 251, +2176767: 72, +2176768: 22, +2176769: 24, +2176770: 205, +2176771: 32, +2176772: 7, +2176773: 159, +2176774: 12, +2176775: 151, +2176776: 54, +2176777: 63, +2176778: 14, +2176779: 255, +2176780: 127, +2176781: 35, +2176782: 0, +2176783: 5, +2176784: 178, +2176785: 114, +2176786: 199, +2176787: 113, +2176788: 3, +2176789: 77, +2176790: 199, +2176791: 24, +2176792: 13, +2176793: 247, +2176794: 94, +2176795: 82, +2176796: 74, +2176797: 107, +2176798: 45, +2176799: 132, +2176800: 16, +2176801: 63, +2176802: 14, +2176803: 82, +2176804: 74, +2176805: 255, +2176806: 127, +2176807: 35, +2176808: 0, +2176809: 197, +2176810: 24, +2176811: 21, +2176812: 236, +2176813: 83, +2176814: 9, +2176815: 67, +2176816: 197, +2176817: 37, +2176818: 194, +2176819: 16, +2176820: 121, +2176821: 78, +2176822: 212, +2176823: 57, +2176824: 237, +2176825: 28, +2176826: 65, +2176827: 12, +2176828: 9, +2176829: 67, +2176830: 119, +2176831: 12, +2176832: 255, +2176833: 127, +2176834: 35, +2176835: 0, +2176836: 197, +2176837: 32, +2176838: 21, +2176839: 58, +2176840: 58, +2176841: 179, +2176842: 45, +2176843: 44, +2176844: 33, +2176845: 99, +2176846: 12, +2176847: 85, +2176848: 85, +2176849: 207, +2176850: 56, +2176851: 105, +2176852: 28, +2176853: 3, +2176854: 4, +2176855: 179, +2176856: 45, +2176857: 207, +2176858: 56, +2176859: 255, +2176860: 127, +2176861: 35, +2176862: 0, +2176863: 16, +2176864: 150, +2176865: 5, +2176866: 214, +2176867: 4, +2176868: 86, +2176869: 4, +2176870: 206, +2176871: 32, +2176872: 137, +2176873: 28, +2176874: 101, +2176875: 20, +2176876: 32, +2176877: 20, +2176878: 39, +2176879: 16, +2176880: 5, +2176881: 67, +2176882: 12, +2176883: 3, +2176884: 6, +2176885: 8, +2176886: 168, +2176887: 8, +2176888: 5, +2176889: 12, +2176890: 255, +2176891: 127, +2176892: 35, +2176893: 0, +2176894: 197, +2176895: 32, +2176896: 19, +2176897: 136, +2176898: 20, +2176899: 70, +2176900: 12, +2176901: 35, +2176902: 4, +2176903: 0, +2176904: 0, +2176905: 14, +2176906: 33, +2176907: 201, +2176908: 24, +2176909: 100, +2176910: 12, +2176911: 0, +2176912: 0, +2176913: 8, +2176914: 12, +2176915: 9, +2176916: 4, +2176917: 195, +2176918: 32, +2176919: 255, +2176920: 27, +2176921: 0, +2176922: 0, +2176923: 223, +2176924: 2, +2176925: 215, +2176926: 1, +2176927: 172, +2176928: 0, +2176929: 115, +2176930: 90, +2176931: 173, +2176932: 65, +2176933: 8, +2176934: 45, +2176935: 99, +2176936: 24, +2176937: 177, +2176938: 11, +2176939: 251, +2176940: 72, +2176941: 255, +2176942: 127, +2176943: 0, +2176944: 0, +2176945: 255, +2176946: 127, +2176947: 229, +2176948: 68, +2176949: 195, +2176950: 8, +2176951: 27, +2176952: 3, +2176953: 32, +2176954: 177, +2176955: 11, +2176956: 169, +2176957: 30, +2176958: 69, +2176959: 1, +2176960: 187, +2176961: 94, +2176962: 179, +2176963: 61, +2176964: 46, +2176965: 41, +2176966: 134, +2176967: 20, +2176968: 24, +2176969: 99, +2176970: 82, +2176971: 74, +2176972: 140, +2176973: 49, +2176974: 0, +2176975: 0, +2176976: 24, +2176977: 99, +2176978: 223, +2176979: 2, +2176980: 197, +2176981: 32, +2176982: 5, +2176983: 188, +2176984: 114, +2176985: 157, +2176986: 85, +2176987: 22, +2176988: 24, +2176989: 81, +2176990: 24, +2176991: 99, +2176992: 1, +2176993: 0, +2176994: 0, +2176995: 67, +2176996: 24, +2176997: 99, +2176998: 7, +2176999: 0, +2177000: 0, +2177001: 178, +2177002: 114, +2177003: 199, +2177004: 113, +2177005: 99, +2177006: 68, +2177007: 81, +2177008: 24, +2177009: 99, +2177010: 1, +2177011: 0, +2177012: 0, +2177013: 67, +2177014: 24, +2177015: 99, +2177016: 1, +2177017: 0, +2177018: 0, +2177019: 93, +2177020: 24, +2177021: 99, +2177022: 1, +2177023: 0, +2177024: 0, +2177025: 93, +2177026: 24, +2177027: 99, +2177028: 195, +2177029: 170, +2177030: 25, +2177031: 255, +2177032: 45, +2177033: 191, +2177034: 40, +2177035: 144, +2177036: 28, +2177037: 105, +2177038: 20, +2177039: 36, +2177040: 20, +2177041: 123, +2177042: 74, +2177043: 27, +2177044: 54, +2177045: 85, +2177046: 25, +2177047: 110, +2177048: 0, +2177049: 8, +2177050: 0, +2177051: 159, +2177052: 3, +2177053: 58, +2177054: 2, +2177055: 118, +2177056: 1, +2177057: 35, +2177058: 0, +2177059: 194, +2177060: 32, +2177061: 18, +2177062: 13, +2177063: 191, +2177064: 8, +2177065: 149, +2177066: 8, +2177067: 108, +2177068: 8, +2177069: 71, +2177070: 4, +2177071: 126, +2177072: 107, +2177073: 30, +2177074: 87, +2177075: 88, +2177076: 58, +2177077: 113, +2177078: 33, +2177079: 203, +2177080: 12, +2177081: 199, +2177082: 32, +2177083: 255, +2177084: 27, +2177085: 0, +2177086: 0, +2177087: 223, +2177088: 2, +2177089: 215, +2177090: 1, +2177091: 172, +2177092: 0, +2177093: 25, +2177094: 106, +2177095: 51, +2177096: 81, +2177097: 171, +2177098: 52, +2177099: 102, +2177100: 32, +2177101: 177, +2177102: 11, +2177103: 251, +2177104: 72, +2177105: 255, +2177106: 127, +2177107: 0, +2177108: 0, +2177109: 255, +2177110: 127, +2177111: 229, +2177112: 68, +2177113: 195, +2177114: 8, +2177115: 0, +2177116: 3, +2177117: 194, +2177118: 18, +2177119: 17, +2177120: 169, +2177121: 30, +2177122: 69, +2177123: 1, +2177124: 187, +2177125: 94, +2177126: 179, +2177127: 61, +2177128: 46, +2177129: 41, +2177130: 134, +2177131: 20, +2177132: 16, +2177133: 66, +2177134: 0, +2177135: 4, +2177136: 129, +2177137: 20, +2177138: 195, +2177139: 32, +2177140: 2, +2177141: 223, +2177142: 2, +2177143: 31, +2177144: 34, +2177145: 0, +2177146: 224, +2177147: 61, +2177148: 3, +2177149: 32, +2177150: 188, +2177151: 114, +2177152: 251, +2177153: 72, +2177154: 22, +2177155: 24, +2177156: 157, +2177157: 42, +2177158: 214, +2177159: 25, +2177160: 16, +2177161: 13, +2177162: 72, +2177163: 0, +2177164: 133, +2177165: 33, +2177166: 2, +2177167: 21, +2177168: 193, +2177169: 16, +2177170: 96, +2177171: 4, +2177172: 72, +2177173: 66, +2177174: 181, +2177175: 26, +2177176: 255, +2177177: 127, +2177178: 33, +2177179: 8, +2177180: 0, +2177181: 0, +2177182: 178, +2177183: 114, +2177184: 199, +2177185: 113, +2177186: 3, +2177187: 77, +2177188: 170, +2177189: 33, +2177190: 71, +2177191: 21, +2177192: 229, +2177193: 16, +2177194: 130, +2177195: 4, +2177196: 201, +2177197: 16, +2177198: 135, +2177199: 12, +2177200: 36, +2177201: 4, +2177202: 1, +2177203: 0, +2177204: 110, +2177205: 33, +2177206: 128, +2177207: 8, +2177208: 185, +2177209: 54, +2177210: 35, +2177211: 0, +2177212: 28, +2177213: 102, +2177214: 69, +2177215: 227, +2177216: 40, +2177217: 96, +2177218: 28, +2177219: 176, +2177220: 54, +2177221: 11, +2177222: 38, +2177223: 104, +2177224: 25, +2177225: 226, +2177226: 12, +2177227: 202, +2177228: 90, +2177229: 72, +2177230: 62, +2177231: 99, +2177232: 33, +2177233: 163, +2177234: 4, +2177235: 124, +2177236: 81, +2177237: 23, +2177238: 127, +2177239: 255, +2177240: 127, +2177241: 32, +2177242: 34, +2177243: 0, +2177244: 27, +2177245: 236, +2177246: 41, +2177247: 104, +2177248: 29, +2177249: 6, +2177250: 21, +2177251: 236, +2177252: 82, +2177253: 71, +2177254: 62, +2177255: 68, +2177256: 33, +2177257: 193, +2177258: 16, +2177259: 135, +2177260: 0, +2177261: 233, +2177262: 12, +2177263: 76, +2177264: 17, +2177265: 242, +2177266: 25, +2177267: 2, +2177268: 0, +2177269: 36, +2177270: 0, +2177271: 255, +2177272: 127, +2177273: 35, +2177274: 0, +2177275: 5, +2177276: 49, +2177277: 49, +2177278: 238, +2177279: 40, +2177280: 167, +2177281: 24, +2177282: 197, +2177283: 62, +2177284: 15, +2177285: 128, +2177286: 4, +2177287: 10, +2177288: 13, +2177289: 168, +2177290: 8, +2177291: 102, +2177292: 4, +2177293: 68, +2177294: 0, +2177295: 236, +2177296: 8, +2177297: 215, +2177298: 22, +2177299: 16, +2177300: 66, +2177301: 35, +2177302: 0, +2177303: 69, +2177304: 16, +2177305: 66, +2177306: 199, +2177307: 96, +2177308: 15, +2177309: 87, +2177310: 119, +2177311: 114, +2177312: 110, +2177313: 238, +2177314: 89, +2177315: 106, +2177316: 69, +2177317: 40, +2177318: 53, +2177319: 5, +2177320: 37, +2177321: 156, +2177322: 127, +2177323: 0, +2177324: 0, +2177325: 255, +2177326: 27, +2177327: 0, +2177328: 0, +2177329: 223, +2177330: 2, +2177331: 215, +2177332: 1, +2177333: 172, +2177334: 0, +2177335: 250, +2177336: 105, +2177337: 52, +2177338: 73, +2177339: 173, +2177340: 44, +2177341: 103, +2177342: 20, +2177343: 177, +2177344: 11, +2177345: 251, +2177346: 72, +2177347: 255, +2177348: 127, +2177349: 0, +2177350: 0, +2177351: 255, +2177352: 127, +2177353: 229, +2177354: 68, +2177355: 195, +2177356: 8, +2177357: 21, +2177358: 3, +2177359: 32, +2177360: 177, +2177361: 11, +2177362: 169, +2177363: 30, +2177364: 69, +2177365: 1, +2177366: 187, +2177367: 94, +2177368: 179, +2177369: 61, +2177370: 46, +2177371: 41, +2177372: 134, +2177373: 20, +2177374: 16, +2177375: 66, +2177376: 0, +2177377: 4, +2177378: 35, +2177379: 20, +2177380: 195, +2177381: 32, +2177382: 2, +2177383: 223, +2177384: 2, +2177385: 31, +2177386: 34, +2177387: 0, +2177388: 23, +2177389: 3, +2177390: 32, +2177391: 188, +2177392: 114, +2177393: 251, +2177394: 72, +2177395: 22, +2177396: 24, +2177397: 217, +2177398: 62, +2177399: 87, +2177400: 46, +2177401: 53, +2177402: 42, +2177403: 243, +2177404: 37, +2177405: 210, +2177406: 37, +2177407: 176, +2177408: 29, +2177409: 110, +2177410: 25, +2177411: 46, +2177412: 17, +2177413: 195, +2177414: 80, +2177415: 1, +2177416: 255, +2177417: 127, +2177418: 35, +2177419: 0, +2177420: 27, +2177421: 178, +2177422: 114, +2177423: 199, +2177424: 113, +2177425: 3, +2177426: 77, +2177427: 32, +2177428: 0, +2177429: 97, +2177430: 4, +2177431: 162, +2177432: 8, +2177433: 227, +2177434: 12, +2177435: 36, +2177436: 17, +2177437: 101, +2177438: 21, +2177439: 166, +2177440: 25, +2177441: 9, +2177442: 38, +2177443: 102, +2177444: 4, +2177445: 36, +2177446: 0, +2177447: 255, +2177448: 127, +2177449: 35, +2177450: 0, +2177451: 224, +2177452: 59, +2177453: 106, +2177454: 61, +2177455: 40, +2177456: 53, +2177457: 197, +2177458: 40, +2177459: 209, +2177460: 29, +2177461: 110, +2177462: 21, +2177463: 11, +2177464: 17, +2177465: 135, +2177466: 4, +2177467: 100, +2177468: 37, +2177469: 3, +2177470: 29, +2177471: 194, +2177472: 20, +2177473: 129, +2177474: 12, +2177475: 255, +2177476: 127, +2177477: 36, +2177478: 0, +2177479: 40, +2177480: 54, +2177481: 96, +2177482: 8, +2177483: 0, +2177484: 0, +2177485: 191, +2177486: 99, +2177487: 93, +2177488: 83, +2177489: 251, +2177490: 66, +2177491: 239, +2177492: 69, +2177493: 107, +2177494: 53, +2177495: 231, +2177496: 36, +2177497: 132, +2177498: 24, +2177499: 135, +2177500: 0, +2177501: 233, +2177502: 12, +2177503: 76, +2177504: 17, +2177505: 242, +2177506: 25, +2177507: 1, +2177508: 0, +2177509: 38, +2177510: 0, +2177511: 90, +2177512: 115, +2177513: 35, +2177514: 0, +2177515: 224, +2177516: 61, +2177517: 36, +2177518: 29, +2177519: 161, +2177520: 16, +2177521: 96, +2177522: 8, +2177523: 2, +2177524: 0, +2177525: 35, +2177526: 0, +2177527: 69, +2177528: 4, +2177529: 102, +2177530: 4, +2177531: 103, +2177532: 8, +2177533: 136, +2177534: 8, +2177535: 170, +2177536: 12, +2177537: 203, +2177538: 12, +2177539: 16, +2177540: 66, +2177541: 255, +2177542: 127, +2177543: 35, +2177544: 0, +2177545: 64, +2177546: 4, +2177547: 0, +2177548: 0, +2177549: 21, +2177550: 38, +2177551: 113, +2177552: 25, +2177553: 235, +2177554: 12, +2177555: 154, +2177556: 101, +2177557: 51, +2177558: 77, +2177559: 238, +2177560: 60, +2177561: 137, +2177562: 44, +2177563: 91, +2177564: 123, +2177565: 182, +2177566: 102, +2177567: 50, +2177568: 86, +2177569: 207, +2177570: 73, +2177571: 45, +2177572: 61, +2177573: 201, +2177574: 40, +2177575: 101, +2177576: 28, +2177577: 1, +2177578: 8, +2177579: 255, +2177580: 29, +2177581: 0, +2177582: 0, +2177583: 223, +2177584: 2, +2177585: 215, +2177586: 1, +2177587: 172, +2177588: 0, +2177589: 244, +2177590: 106, +2177591: 111, +2177592: 90, +2177593: 170, +2177594: 61, +2177595: 229, +2177596: 32, +2177597: 177, +2177598: 11, +2177599: 251, +2177600: 72, +2177601: 255, +2177602: 127, +2177603: 0, +2177604: 0, +2177605: 255, +2177606: 127, +2177607: 229, +2177608: 68, +2177609: 255, +2177610: 127, +2177611: 34, +2177612: 0, +2177613: 20, +2177614: 56, +2177615: 177, +2177616: 11, +2177617: 169, +2177618: 30, +2177619: 69, +2177620: 1, +2177621: 187, +2177622: 94, +2177623: 179, +2177624: 61, +2177625: 46, +2177626: 41, +2177627: 134, +2177628: 20, +2177629: 24, +2177630: 99, +2177631: 82, +2177632: 74, +2177633: 140, +2177634: 49, +2177635: 195, +2177636: 32, +2177637: 2, +2177638: 223, +2177639: 2, +2177640: 31, +2177641: 35, +2177642: 0, +2177643: 6, +2177644: 56, +2177645: 188, +2177646: 114, +2177647: 251, +2177648: 72, +2177649: 22, +2177650: 24, +2177651: 83, +2177652: 128, +2177653: 21, +2177654: 1, +2177655: 255, +2177656: 127, +2177657: 34, +2177658: 0, +2177659: 6, +2177660: 56, +2177661: 178, +2177662: 114, +2177663: 199, +2177664: 113, +2177665: 3, +2177666: 77, +2177667: 83, +2177668: 128, +2177669: 21, +2177670: 1, +2177671: 255, +2177672: 127, +2177673: 34, +1129098: 2, +2177675: 8, +2177676: 56, +2177677: 255, +2177678: 127, +2177679: 247, +2177680: 94, +2177681: 16, +2177682: 66, +2177683: 8, +2177684: 33, +2177685: 199, +2177686: 8, +2177687: 77, +2177688: 128, +2177689: 21, +2177690: 31, +2177691: 0, +2177692: 56, +2177693: 87, +2177694: 63, +2177695: 77, +2177696: 46, +2177697: 226, +2177698: 0, +2177699: 96, +2177700: 0, +2177701: 176, +2177702: 58, +2177703: 11, +2177704: 34, +2177705: 102, +2177706: 17, +2177707: 36, +2177708: 9, +2177709: 25, +2177710: 3, +2177711: 84, +2177712: 2, +2177713: 143, +2177714: 1, +2177715: 202, +2177716: 0, +2177717: 27, +2177718: 88, +2177719: 146, +2177720: 24, +2177721: 69, +2177722: 1, +2177723: 194, +2177724: 64, +2177725: 14, +2177726: 87, +2177727: 247, +2177728: 66, +2177729: 140, +2177730: 21, +2177731: 165, +2177732: 0, +2177733: 90, +2177734: 79, +2177735: 181, +2177736: 54, +2177737: 16, +2177738: 38, +2177739: 206, +2177740: 29, +2177741: 205, +2177742: 48, +2177743: 21, +2177744: 0, +2177745: 56, +2177746: 156, +2177747: 75, +2177748: 148, +2177749: 54, +2177750: 41, +2177751: 9, +2177752: 66, +2177753: 0, +2177754: 247, +2177755: 66, +2177756: 82, +2177757: 42, +2177758: 173, +2177759: 25, +2177760: 107, +2177761: 17, +2177762: 231, +2177763: 0, +2177764: 132, +2177765: 0, +2177766: 71, +2177767: 128, +2177768: 21, +2177769: 1, +2177770: 224, +2177771: 127, +2177772: 255, +2177773: 29, +2177774: 0, +2177775: 0, +2177776: 223, +2177777: 2, +2177778: 215, +2177779: 1, +2177780: 172, +2177781: 0, +2177782: 115, +2177783: 90, +2177784: 173, +2177785: 65, +2177786: 8, +2177787: 45, +2177788: 99, +2177789: 24, +2177790: 177, +2177791: 11, +2177792: 251, +2177793: 72, +2177794: 255, +2177795: 127, +2177796: 0, +2177797: 0, +2177798: 255, +2177799: 127, +2177800: 229, +2177801: 68, +2177802: 255, +2177803: 127, +2177804: 35, +2177805: 0, +2177806: 19, +2177807: 177, +2177808: 11, +2177809: 169, +2177810: 30, +2177811: 69, +2177812: 1, +2177813: 187, +2177814: 94, +2177815: 179, +2177816: 61, +2177817: 46, +2177818: 41, +2177819: 134, +2177820: 20, +2177821: 24, +2177822: 99, +2177823: 82, +2177824: 74, +2177825: 140, +2177826: 49, +2177827: 195, +2177828: 32, +2177829: 2, +2177830: 223, +2177831: 2, +2177832: 31, +2177833: 36, +2177834: 0, +2177835: 21, +2177836: 188, +2177837: 114, +2177838: 251, +2177839: 72, +2177840: 22, +2177841: 24, +2177842: 84, +2177843: 47, +2177844: 114, +2177845: 2, +2177846: 110, +2177847: 1, +2177848: 71, +2177849: 0, +2177850: 138, +2177851: 24, +2177852: 39, +2177853: 12, +2177854: 36, +2177855: 4, +2177856: 2, +2177857: 4, +2177858: 67, +2177859: 69, +2177860: 1, +2177861: 1, +2177862: 255, +2177863: 127, +2177864: 35, +2177865: 0, +2177866: 21, +2177867: 178, +2177868: 114, +2177869: 199, +2177870: 113, +2177871: 3, +2177872: 77, +2177873: 30, +2177874: 103, +2177875: 22, +2177876: 74, +2177877: 145, +2177878: 53, +2177879: 233, +2177880: 32, +2177881: 237, +2177882: 65, +2177883: 39, +2177884: 45, +2177885: 193, +2177886: 24, +2177887: 96, +2177888: 16, +2177889: 35, +2177890: 0, +2177891: 1, +2177892: 223, +2177893: 2, +2177894: 35, +2177895: 0, +2177896: 15, +2177897: 249, +2177898: 70, +2177899: 18, +2177900: 54, +2177901: 233, +2177902: 16, +2177903: 165, +2177904: 0, +2177905: 117, +2177906: 66, +2177907: 208, +2177908: 41, +2177909: 43, +2177910: 25, +2177911: 200, +2177912: 12, +2177913: 199, +2177914: 74, +2177915: 7, +2177916: 188, +2177917: 114, +2177918: 157, +2177919: 85, +2177920: 22, +2177921: 24, +2177922: 0, +2177923: 0, +2177924: 67, +2177925: 24, +2177926: 99, +2177927: 23, +2177928: 2, +2177929: 8, +2177930: 148, +2177931: 82, +2177932: 206, +2177933: 57, +2177934: 8, +2177935: 33, +2177936: 132, +2177937: 16, +2177938: 25, +2177939: 0, +2177940: 18, +2177941: 0, +2177942: 0, +2177943: 92, +2177944: 0, +2177945: 64, +2177946: 132, +2177947: 16, +2177948: 127, +2177949: 25, +2177950: 255, +2177951: 127, +2177952: 35, +2177953: 0, +2177954: 15, +2177955: 156, +2177956: 75, +2177957: 148, +2177958: 54, +2177959: 41, +2177960: 9, +2177961: 66, +2177962: 0, +2177963: 247, +2177964: 66, +2177965: 82, +2177966: 42, +2177967: 173, +2177968: 25, +2177969: 107, +2177970: 17, +2177971: 75, +2177972: 69, +2177973: 1, +2177974: 3, +2177975: 2, +2177976: 4, +2177977: 0, +2177978: 0, +2177979: 67, +2177980: 24, +2177981: 99, +2177982: 217, +2177983: 64, +2177984: 255, +2177985: 27, +2177986: 0, +2177987: 0, +2177988: 223, +2177989: 2, +2177990: 215, +2177991: 1, +2177992: 172, +2177993: 0, +2177994: 115, +2177995: 90, +2177996: 173, +2177997: 65, +2177998: 8, +2177999: 45, +2178000: 99, +2178001: 24, +2178002: 177, +2178003: 11, +2178004: 251, +2178005: 72, +2178006: 255, +2178007: 127, +2178008: 0, +2178009: 0, +2178010: 255, +2178011: 127, +2178012: 229, +2178013: 68, +2178014: 195, +2178015: 8, +2178016: 21, +2178017: 3, +2178018: 32, +2178019: 177, +2178020: 11, +2178021: 169, +2178022: 30, +2178023: 69, +2178024: 1, +2178025: 187, +2178026: 94, +2178027: 179, +2178028: 61, +2178029: 46, +2178030: 41, +2178031: 134, +2178032: 20, +2178033: 24, +2178034: 99, +2178035: 63, +2178036: 14, +2178037: 127, +2178038: 13, +2178039: 195, +2178040: 32, +2178041: 2, +2178042: 223, +2178043: 2, +2178044: 31, +2178045: 34, +2178046: 0, +2178047: 23, +2178048: 3, +2178049: 32, +2178050: 188, +2178051: 114, +2178052: 251, +2178053: 72, +2178054: 22, +2178055: 24, +2178056: 84, +2178057: 47, +2178058: 114, +2178059: 2, +2178060: 110, +2178061: 1, +2178062: 71, +2178063: 0, +2178064: 138, +2178065: 24, +2178066: 39, +2178067: 12, +2178068: 36, +2178069: 4, +2178070: 2, +2178071: 4, +2178072: 67, +2178073: 69, +2178074: 1, +2178075: 1, +2178076: 255, +2178077: 127, +2178078: 35, +2178079: 0, +2178080: 21, +2178081: 178, +2178082: 114, +2178083: 199, +2178084: 113, +2178085: 3, +2178086: 77, +2178087: 22, +2178088: 74, +2178089: 145, +2178090: 57, +2178091: 44, +2178092: 45, +2178093: 167, +2178094: 28, +2178095: 229, +2178096: 32, +2178097: 164, +2178098: 24, +2178099: 131, +2178100: 16, +2178101: 65, +2178102: 8, +2178103: 35, +2178104: 0, +2178105: 1, +2178106: 223, +2178107: 2, +2178108: 35, +2178109: 0, +2178110: 27, +2178111: 191, +2178112: 4, +2178113: 150, +2178114: 4, +2178115: 77, +2178116: 0, +2178117: 36, +2178118: 0, +2178119: 57, +2178120: 87, +2178121: 115, +2178122: 66, +2178123: 173, +2178124: 45, +2178125: 198, +2178126: 20, +2178127: 127, +2178128: 54, +2178129: 249, +2178130: 41, +2178131: 115, +2178132: 33, +2178133: 12, +2178134: 21, +2178135: 134, +2178136: 12, +2178137: 255, +2178138: 127, +2178139: 35, +2178140: 0, +2178141: 67, +2178142: 24, +2178143: 99, +2178144: 23, +2178145: 2, +2178146: 8, +2178147: 148, +2178148: 82, +2178149: 206, +2178150: 57, +2178151: 8, +2178152: 33, +2178153: 132, +2178154: 16, +2178155: 25, +2178156: 0, +2178157: 18, +2178158: 0, +2178159: 0, +2178160: 92, +2178161: 0, +2178162: 64, +2178163: 132, +2178164: 16, +2178165: 127, +2178166: 25, +2178167: 255, +2178168: 127, +2178169: 35, +2178170: 0, +2178171: 197, +2178172: 26, +2178173: 21, +2178174: 139, +2178175: 127, +2178176: 10, +2178177: 111, +2178178: 136, +2178179: 94, +2178180: 7, +2178181: 78, +2178182: 134, +2178183: 57, +2178184: 5, +2178185: 41, +2178186: 131, +2178187: 24, +2178188: 2, +2178189: 8, +2178190: 132, +2178191: 16, +2178192: 24, +2178193: 99, +2178194: 255, +2178195: 127, +2178196: 35, +2178197: 0, +2178198: 67, +2178199: 24, +2178200: 99, +2178201: 217, +2178202: 64, +2178203: 255, +2178204: 27, +2178205: 0, +2178206: 0, +2178207: 223, +2178208: 2, +2178209: 215, +2178210: 1, +2178211: 172, +2178212: 0, +2178213: 115, +2178214: 90, +2178215: 173, +2178216: 65, +2178217: 8, +2178218: 45, +2178219: 99, +2178220: 24, +2178221: 177, +2178222: 11, +2178223: 251, +2178224: 72, +2178225: 255, +2178226: 127, +2178227: 0, +2178228: 0, +2178229: 255, +2178230: 127, +2178231: 229, +2178232: 68, +2178233: 195, +2178234: 8, +2178235: 21, +2178236: 3, +2178237: 32, +2178238: 177, +2178239: 11, +2178240: 169, +2178241: 30, +2178242: 69, +2178243: 1, +2178244: 187, +2178245: 94, +2178246: 179, +2178247: 61, +2178248: 46, +2178249: 41, +2178250: 134, +2178251: 20, +2178252: 24, +2178253: 99, +2178254: 227, +2178255: 40, +2178256: 96, +2178257: 28, +2178258: 195, +2178259: 32, +2178260: 2, +2178261: 223, +2178262: 2, +2178263: 31, +2178264: 34, +2178265: 0, +2178266: 29, +2178267: 3, +2178268: 32, +2178269: 188, +2178270: 114, +2178271: 251, +2178272: 72, +2178273: 22, +2178274: 24, +2178275: 255, +2178276: 107, +2178277: 243, +2178278: 66, +2178279: 12, +2178280: 22, +2178281: 4, +2178282: 9, +2178283: 146, +2178284: 42, +2178285: 171, +2178286: 9, +2178287: 38, +2178288: 1, +2178289: 33, +2178290: 0, +2178291: 131, +2178292: 0, +2178293: 16, +2178294: 26, +2178295: 255, +2178296: 127, +2178297: 35, +2178298: 0, +2178299: 19, +2178300: 178, +2178301: 114, +2178302: 199, +2178303: 113, +2178304: 3, +2178305: 77, +2178306: 73, +2178307: 12, +2178308: 40, +2178309: 20, +2178310: 39, +2178311: 16, +2178312: 6, +2178313: 12, +2178314: 5, +2178315: 16, +2178316: 4, +2178317: 12, +2178318: 3, +2178319: 12, +2178320: 69, +2178321: 130, +2178322: 21, +2178323: 1, +2178324: 255, +2178325: 127, +2178326: 35, +2178327: 0, +2178328: 21, +2178329: 88, +2178330: 82, +2178331: 244, +2178332: 69, +2178333: 177, +2178334: 57, +2178335: 78, +2178336: 49, +2178337: 11, +2178338: 37, +2178339: 200, +2178340: 24, +2178341: 134, +2178342: 20, +2178343: 57, +2178344: 79, +2178345: 115, +2178346: 54, +2178347: 173, +2178348: 29, +2178349: 41, +2178350: 13, +2178351: 67, +2178352: 130, +2178353: 21, +2178354: 1, +2178355: 255, +2178356: 127, +2178357: 35, +2178358: 0, +2178359: 13, +2178360: 182, +2178361: 11, +2178362: 174, +2178363: 30, +2178364: 74, +2178365: 1, +2178366: 88, +2178367: 82, +2178368: 80, +2178369: 49, +2178370: 203, +2178371: 28, +2178372: 35, +2178373: 8, +2178374: 199, +2178375: 136, +2178376: 67, +2178377: 130, +2178378: 21, +2178379: 1, +2178380: 255, +2178381: 127, +2178382: 35, +2178383: 0, +2178384: 21, +2178385: 255, +2178386: 87, +2178387: 255, +2178388: 43, +2178389: 60, +2178390: 31, +2178391: 120, +2178392: 2, +2178393: 176, +2178394: 1, +2178395: 11, +2178396: 1, +2178397: 135, +2178398: 0, +2178399: 68, +2178400: 0, +2178401: 224, +2178402: 127, +2178403: 64, +2178404: 74, +2178405: 32, +2178406: 37, +2178407: 69, +2178408: 255, +2178409: 127, +2178410: 35, +2178411: 0, +2178412: 207, +2178413: 32, +2178414: 69, +2178415: 255, +2178416: 127, +2178417: 7, +2178418: 224, +2178419: 3, +2178420: 64, +2178421: 2, +2178422: 32, +2178423: 1, +2178424: 0, +2178425: 0, +2178426: 255, +2178427: 27, +2178428: 0, +2178429: 0, +2178430: 223, +2178431: 2, +2178432: 215, +2178433: 1, +2178434: 172, +2178435: 0, +2178436: 187, +2178437: 94, +2178438: 179, +2178439: 61, +2178440: 46, +2178441: 41, +2178442: 134, +2178443: 20, +2178444: 177, +2178445: 11, +2178446: 251, +2178447: 72, +2178448: 255, +2178449: 127, +2178450: 0, +2178451: 0, +2178452: 255, +2178453: 127, +2178454: 229, +2178455: 68, +2178456: 195, +2178457: 8, +2178458: 7, +2178459: 3, +2178460: 32, +2178461: 177, +2178462: 11, +2178463: 169, +2178464: 30, +2178465: 69, +2178466: 1, +2178467: 199, +2178468: 32, +2178469: 5, +2178470: 24, +2178471: 99, +2178472: 227, +2178473: 40, +2178474: 96, +2178475: 28, +2178476: 195, +2178477: 32, +2178478: 2, +2178479: 223, +2178480: 2, +2178481: 31, +2178482: 34, +2178483: 0, +2178484: 29, +2178485: 3, +2178486: 32, +2178487: 213, +2178488: 85, +2178489: 182, +2178490: 56, +2178491: 15, +2178492: 0, +2178493: 255, +2178494: 107, +2178495: 243, +2178496: 66, +2178497: 12, +2178498: 22, +2178499: 4, +2178500: 9, +2178501: 146, +2178502: 42, +2178503: 171, +2178504: 9, +2178505: 38, +2178506: 1, +2178507: 33, +2178508: 0, +2178509: 131, +2178510: 0, +2178511: 16, +2178512: 26, +2178513: 255, +2178514: 127, +2178515: 35, +2178516: 0, +2178517: 6, +2178518: 203, +2178519: 85, +2178520: 224, +2178521: 84, +2178522: 0, +2178523: 40, +2178524: 4, +2178525: 68, +2178526: 0, +2178527: 3, +2178528: 0, +2178529: 2, +2178530: 67, +2178531: 0, +2178532: 1, +2178533: 34, +2178534: 0, +2178535: 69, +2178536: 130, +2178537: 21, +2178538: 1, +2178539: 255, +2178540: 127, +2178541: 35, +2178542: 0, +2178543: 21, +2178544: 138, +2178545: 24, +2178546: 105, +2178547: 20, +2178548: 103, +2178549: 16, +2178550: 70, +2178551: 16, +2178552: 37, +2178553: 12, +2178554: 35, +2178555: 8, +2178556: 2, +2178557: 4, +2178558: 48, +2178559: 46, +2178560: 140, +2178561: 29, +2178562: 7, +2178563: 17, +2178564: 99, +2178565: 0, +2178566: 67, +2178567: 130, +2178568: 21, +2178569: 1, +2178570: 255, +2178571: 127, +2178572: 35, +2178573: 0, +2178574: 13, +2178575: 41, +2178576: 2, +2178577: 135, +2178578: 1, +2178579: 197, +2178580: 0, +2178581: 46, +2178582: 45, +2178583: 202, +2178584: 32, +2178585: 102, +2178586: 16, +2178587: 2, +2178588: 4, +2178589: 199, +2178590: 136, +2178591: 67, +2178592: 130, +2178593: 21, +2178594: 1, +2178595: 181, +2178596: 86, +2178597: 35, +2178598: 0, +2178599: 27, +1130024: 19, +1130025: 0, +2178602: 255, +2178603: 43, +1130028: 11, +1130029: 0, +2178606: 120, +1130031: 127, +1130032: 156, +2178609: 1, +2178610: 11, +1130035: 2, +2178612: 135, +2178613: 0, +2178614: 68, +2178615: 0, +2178616: 31, +2178617: 0, +2178618: 18, +2178619: 0, +2178620: 9, +2178621: 0, +2178622: 255, +2178623: 3, +2178624: 82, +2178625: 2, +2178626: 41, +2178627: 1, +2178628: 35, +2178629: 0, +2178630: 207, +2178631: 32, +2178632: 13, +2178633: 224, +2178634: 127, +2178635: 64, +2178636: 74, +2178637: 32, +2178638: 37, +2178639: 224, +2178640: 3, +2178641: 64, +2178642: 2, +2178643: 32, +2178644: 1, +2178645: 0, +2178646: 0, +2178647: 255, +2178648: 27, +2178649: 0, +2178650: 0, +2178651: 223, +2178652: 2, +2178653: 215, +2178654: 1, +2178655: 172, +2178656: 0, +2178657: 115, +2178658: 90, +2178659: 173, +2178660: 65, +2178661: 8, +2178662: 45, +2178663: 99, +2178664: 24, +2178665: 177, +2178666: 11, +2178667: 251, +2178668: 72, +2178669: 255, +2178670: 127, +2178671: 0, +2178672: 0, +2178673: 255, +2178674: 127, +2178675: 229, +2178676: 68, +2178677: 195, +2178678: 8, +2178679: 21, +2178680: 3, +2178681: 32, +2178682: 177, +2178683: 11, +2178684: 169, +2178685: 30, +2178686: 69, +2178687: 1, +2178688: 187, +2178689: 94, +2178690: 179, +2178691: 61, +2178692: 46, +2178693: 41, +2178694: 134, +2178695: 20, +2178696: 24, +2178697: 99, +2178698: 214, +2178699: 90, +2178700: 82, +2178701: 74, +2178702: 195, +2178703: 32, +2178704: 2, +2178705: 223, +2178706: 2, +2178707: 31, +2178708: 34, +2178709: 0, +2178710: 29, +2178711: 3, +2178712: 32, +2178713: 188, +2178714: 114, +2178715: 251, +2178716: 72, +2178717: 22, +2178718: 24, +2178719: 255, +2178720: 107, +2178721: 243, +2178722: 66, +2178723: 12, +2178724: 22, +2178725: 4, +2178726: 9, +2178727: 146, +2178728: 42, +2178729: 171, +2178730: 9, +2178731: 38, +2178732: 1, +2178733: 33, +2178734: 0, +2178735: 131, +2178736: 0, +2178737: 16, +2178738: 26, +2178739: 255, +2178740: 127, +2178741: 35, +2178742: 0, +2178743: 19, +2178744: 178, +2178745: 114, +2178746: 199, +2178747: 113, +2178748: 3, +2178749: 77, +2178750: 66, +2178751: 60, +2178752: 66, +2178753: 52, +2178754: 33, +2178755: 40, +2178756: 33, +2178757: 32, +2178758: 33, +2178759: 24, +2178760: 0, +2178761: 12, +2178762: 0, +2178763: 4, +2178764: 69, +2178765: 130, +2178766: 21, +2178767: 1, +2178768: 255, +2178769: 127, +2178770: 35, +2178771: 0, +2178772: 21, +2178773: 199, +2178774: 113, +2178775: 134, +2178776: 97, +2178777: 69, +2178778: 81, +2178779: 4, +2178780: 65, +2178781: 163, +2178782: 44, +2178783: 98, +2178784: 28, +2178785: 33, +2178786: 12, +2178787: 86, +2178788: 123, +2178789: 176, +2178790: 110, +2178791: 224, +2178792: 117, +2178793: 35, +2178794: 85, +2178795: 67, +2178796: 130, +2178797: 21, +2178798: 1, +2178799: 255, +2178800: 127, +2178801: 35, +2178802: 0, +2178803: 13, +2178804: 182, +2178805: 11, +2178806: 174, +2178807: 30, +2178808: 74, +2178809: 1, +2178810: 145, +2178811: 110, +2178812: 199, +2178813: 113, +2178814: 66, +2178815: 40, +2178816: 33, +2178817: 20, +2178818: 199, +2178819: 32, +2178820: 67, +2178821: 130, +2178822: 21, +2178823: 1, +2178824: 255, +2178825: 127, +2178826: 35, +2178827: 0, +2178828: 27, +2178829: 255, +2178830: 87, +2178831: 255, +2178832: 43, +2178833: 60, +2178834: 31, +2178835: 120, +2178836: 2, +2178837: 176, +2178838: 1, +2178839: 11, +2178840: 1, +2178841: 135, +2178842: 0, +2178843: 68, +2178844: 0, +2178845: 31, +2178846: 0, +2178847: 18, +2178848: 0, +2178849: 9, +2178850: 0, +2178851: 255, +2178852: 3, +2178853: 82, +2178854: 2, +2178855: 41, +2178856: 1, +2178857: 35, +2178858: 0, +2178859: 207, +2178860: 32, +2178861: 13, +2178862: 224, +2178863: 127, +2178864: 64, +2178865: 74, +2178866: 32, +2178867: 37, +2178868: 224, +2178869: 3, +2178870: 64, +2178871: 2, +2178872: 32, +2178873: 1, +2178874: 0, +2178875: 0, +2178876: 255, +2178877: 27, +2178878: 0, +2178879: 0, +2178880: 223, +2178881: 2, +2178882: 215, +2178883: 1, +2178884: 172, +2178885: 0, +2178886: 115, +2178887: 90, +2178888: 173, +2178889: 65, +2178890: 8, +2178891: 45, +2178892: 99, +2178893: 24, +2178894: 177, +2178895: 11, +2178896: 251, +2178897: 72, +2178898: 255, +2178899: 127, +2178900: 0, +2178901: 0, +2178902: 255, +2178903: 127, +2178904: 229, +2178905: 68, +2178906: 195, +2178907: 8, +2178908: 21, +2178909: 3, +2178910: 32, +2178911: 177, +2178912: 11, +2178913: 169, +2178914: 30, +2178915: 69, +2178916: 1, +2178917: 187, +2178918: 94, +2178919: 179, +2178920: 61, +2178921: 46, +2178922: 41, +2178923: 134, +2178924: 20, +2178925: 24, +2178926: 99, +2178927: 214, +2178928: 90, +2178929: 82, +2178930: 74, +2178931: 195, +2178932: 32, +2178933: 2, +2178934: 223, +2178935: 2, +2178936: 31, +2178937: 34, +2178938: 0, +2178939: 29, +2178940: 3, +2178941: 32, +2178942: 188, +2178943: 114, +2178944: 251, +2178945: 72, +2178946: 22, +2178947: 24, +2178948: 255, +2178949: 107, +2178950: 243, +2178951: 66, +2178952: 12, +2178953: 22, +2178954: 4, +2178955: 9, +2178956: 146, +2178957: 42, +2178958: 171, +2178959: 9, +2178960: 38, +2178961: 1, +2178962: 33, +2178963: 0, +2178964: 131, +2178965: 0, +2178966: 16, +2178967: 26, +2178968: 255, +2178969: 127, +2178970: 35, +2178971: 0, +2178972: 19, +2178973: 178, +2178974: 114, +2178975: 199, +2178976: 113, +2178977: 3, +2178978: 77, +2178979: 226, +2178980: 12, +2178981: 194, +2178982: 12, +2178983: 161, +2178984: 8, +2178985: 129, +2178986: 8, +2178987: 65, +2178988: 4, +2178989: 32, +2178990: 4, +2178991: 0, +2178992: 0, +2178993: 69, +2178994: 130, +2178995: 21, +2178996: 1, +2178997: 255, +2178998: 127, +2178999: 35, +2179000: 0, +2179001: 21, +2179002: 56, +2179003: 55, +2179004: 180, +2179005: 46, +2179006: 48, +2179007: 38, +2179008: 205, +2179009: 29, +2179010: 73, +2179011: 17, +2179012: 197, +2179013: 8, +2179014: 65, +2179015: 0, +2179016: 221, +2179017: 75, +2179018: 52, +2179019: 51, +2179020: 106, +2179021: 22, +2179022: 99, +2179023: 1, +2179024: 67, +2179025: 130, +2179026: 21, +2179027: 1, +2179028: 255, +2179029: 127, +2179030: 35, +2179031: 0, +2179032: 12, +2179033: 182, +2179034: 11, +2179035: 174, +2179036: 30, +2179037: 74, +2179038: 1, +2179039: 101, +2179040: 35, +2179041: 160, +2179042: 21, +2179043: 224, +2179044: 4, +2179045: 32, +2179046: 200, +2179047: 32, +2179048: 67, +2179049: 130, +2179050: 21, +2179051: 1, +2179052: 255, +2179053: 127, +2179054: 35, +2179055: 0, +2179056: 27, +2179057: 255, +2179058: 87, +2179059: 255, +2179060: 43, +2179061: 60, +2179062: 31, +2179063: 120, +2179064: 2, +2179065: 176, +2179066: 1, +2179067: 11, +2179068: 1, +2179069: 135, +2179070: 0, +2179071: 68, +2179072: 0, +2179073: 31, +2179074: 0, +2179075: 18, +2179076: 0, +2179077: 9, +2179078: 0, +2179079: 255, +2179080: 3, +2179081: 82, +2179082: 2, +2179083: 41, +2179084: 1, +2179085: 35, +2179086: 0, +2179087: 207, +2179088: 32, +2179089: 13, +2179090: 224, +2179091: 127, +2179092: 64, +2179093: 74, +2179094: 32, +2179095: 37, +2179096: 224, +2179097: 3, +2179098: 64, +2179099: 2, +2179100: 32, +2179101: 1, +2179102: 0, +2179103: 0, +2179104: 255, +2179105: 27, +2179106: 0, +2179107: 0, +2179108: 223, +2179109: 2, +2179110: 215, +2179111: 1, +2179112: 172, +2179113: 0, +2179114: 115, +2179115: 90, +2179116: 173, +2179117: 65, +2179118: 8, +2179119: 45, +2179120: 99, +2179121: 24, +2179122: 177, +2179123: 11, +2179124: 251, +2179125: 72, +2179126: 255, +2179127: 127, +2179128: 0, +2179129: 0, +2179130: 255, +2179131: 127, +2179132: 229, +2179133: 68, +2179134: 195, +2179135: 8, +2179136: 21, +2179137: 3, +2179138: 32, +2179139: 177, +2179140: 11, +2179141: 169, +2179142: 30, +2179143: 69, +2179144: 1, +2179145: 187, +2179146: 94, +2179147: 179, +2179148: 61, +2179149: 46, +2179150: 41, +2179151: 134, +2179152: 20, +2179153: 24, +2179154: 99, +2179155: 214, +2179156: 90, +2179157: 82, +2179158: 74, +2179159: 195, +2179160: 32, +2179161: 2, +2179162: 223, +2179163: 2, +2179164: 31, +2179165: 34, +2179166: 0, +2179167: 29, +2179168: 3, +2179169: 32, +2179170: 188, +2179171: 114, +2179172: 251, +2179173: 72, +2179174: 22, +2179175: 24, +2179176: 255, +2179177: 107, +2179178: 243, +2179179: 66, +2179180: 12, +2179181: 22, +2179182: 4, +2179183: 9, +2179184: 146, +2179185: 42, +2179186: 171, +2179187: 9, +2179188: 38, +2179189: 1, +2179190: 33, +2179191: 0, +2179192: 131, +2179193: 0, +2179194: 16, +2179195: 26, +2179196: 255, +2179197: 127, +2179198: 35, +2179199: 0, +2179200: 16, +2179201: 178, +2179202: 114, +2179203: 199, +2179204: 113, +2179205: 3, +2179206: 77, +2179207: 135, +2179208: 0, +2179209: 102, +2179210: 0, +2179211: 69, +2179212: 0, +2179213: 36, +2179214: 0, +2179215: 34, +2179216: 0, +2179217: 1, +2179218: 34, +2179219: 0, +2179220: 69, +2179221: 130, +2179222: 21, +2179223: 1, +2179224: 255, +2179225: 127, +2179226: 35, +2179227: 0, +2179228: 8, +2179229: 212, +2179230: 1, +2179231: 144, +2179232: 1, +2179233: 77, +2179234: 1, +2179235: 233, +2179236: 0, +2179237: 137, +2179238: 194, +2179239: 30, +2179240: 9, +2179241: 1, +2179242: 0, +2179243: 152, +2179244: 31, +2179245: 174, +2179246: 30, +2179247: 168, +2179248: 1, +2179249: 161, +2179250: 0, +2179251: 67, +2179252: 130, +2179253: 21, +2179254: 1, +2179255: 255, +2179256: 127, +2179257: 35, +2179258: 0, +2179259: 12, +2179260: 186, +2179261: 55, +2179262: 174, +2179263: 30, +2179264: 74, +2179265: 1, +2179266: 82, +2179267: 41, +2179268: 174, +2179269: 16, +2179270: 72, +2179271: 4, +2179272: 34, +2179273: 200, +2179274: 32, +2179275: 67, +2179276: 130, +2179277: 21, +2179278: 1, +2179279: 255, +2179280: 127, +1130705: 0, +1130706: 56, +1130707: 127, +1130708: 78, +1130709: 117, +1130710: 57, +1130711: 10, +1130712: 12, +1130713: 6, +1130714: 0, +1130715: 216, +1130716: 69, +1130717: 51, +1130718: 45, +1130719: 142, +1130720: 28, +1130721: 43, +1130722: 16, +1130723: 71, +1130724: 83, +1130725: 36, +2179302: 18, +1130727: 98, +1130728: 89, +1130729: 160, +1130730: 92, +1130731: 189, +1130732: 27, +1130733: 253, +1130734: 13, +1130735: 157, +1130736: 0, +2179313: 0, +2179314: 207, +2179315: 32, +2179316: 13, +2179317: 224, +2179318: 127, +2179319: 64, +2179320: 74, +2179321: 32, +2179322: 37, +2179323: 224, +2179324: 3, +2179325: 64, +2179326: 2, +2179327: 32, +2179328: 1, +2179329: 0, +2179330: 0, +2179331: 255, +2179332: 29, +2179333: 0, +2179334: 0, +2179335: 223, +2179336: 2, +2179337: 215, +2179338: 1, +2179339: 172, +2179340: 0, +2179341: 78, +2179342: 94, +2179343: 70, +2179344: 61, +2179345: 193, +2179346: 40, +2179347: 32, +2179348: 20, +2179349: 177, +2179350: 11, +2179351: 251, +2179352: 72, +2179353: 255, +2179354: 127, +2179355: 0, +2179356: 0, +2179357: 255, +2179358: 127, +2179359: 229, +2179360: 68, +2179361: 255, +2179362: 127, +2179363: 35, +2179364: 0, +2179365: 5, +2179366: 177, +2179367: 11, +2179368: 169, +2179369: 30, +2179370: 69, +2179371: 1, +2179372: 199, +2179373: 32, +2179374: 5, +2179375: 24, +2179376: 99, +2179377: 82, +2179378: 74, +2179379: 140, +2179380: 49, +2179381: 195, +2179382: 32, +2179383: 2, +2179384: 223, +2179385: 2, +2179386: 31, +2179387: 36, +2179388: 0, +2179389: 224, +2179390: 61, +2179391: 32, +2179392: 126, +2179393: 96, +2179394: 101, +2179395: 96, +2179396: 32, +2179397: 0, +2179398: 16, +2179399: 64, +2179400: 121, +2179401: 0, +2179402: 93, +2179403: 160, +2179404: 76, +2179405: 160, +2179406: 60, +2179407: 255, +2179408: 127, +2179409: 19, +2179410: 1, +2179411: 15, +2179412: 0, +2179413: 92, +2179414: 23, +2179415: 153, +2179416: 2, +2179417: 214, +2179418: 1, +2179419: 32, +2179420: 12, +2179421: 0, +2179422: 0, +2179423: 245, +2179424: 107, +2179425: 225, +2179426: 6, +2179427: 65, +2179428: 6, +2179429: 161, +2179430: 5, +2179431: 95, +2179432: 94, +2179433: 63, +2179434: 24, +2179435: 20, +2179436: 16, +2179437: 10, +2179438: 8, +2179439: 4, +2179440: 4, +2179441: 159, +2179442: 79, +2179443: 216, +2179444: 62, +2179445: 18, +2179446: 46, +2179447: 112, +2179448: 111, +2179449: 255, +2179450: 127, +2179451: 224, +2179452: 94, +2179453: 34, +2179454: 0, +2179455: 14, +2179456: 124, +2179457: 0, +2179458: 88, +2179459: 0, +2179460: 36, +2179461: 0, +2179462: 16, +2179463: 0, +2179464: 108, +2179465: 0, +2179466: 72, +2179467: 0, +2179468: 64, +2179469: 0, +2179470: 56, +2179471: 47, +2179472: 0, +2179473: 26, +2179474: 96, +2179475: 53, +2179476: 192, +2179477: 36, +2179478: 0, +2179479: 8, +2179480: 0, +2179481: 4, +2179482: 32, +2179483: 49, +2179484: 128, +2179485: 24, +2179486: 64, +2179487: 20, +2179488: 0, +2179489: 28, +2179490: 255, +2179491: 67, +2179492: 24, +2179493: 1, +2179494: 20, +2179495: 0, +2179496: 255, +2179497: 22, +2179498: 62, +2179499: 2, +2179500: 123, +2179501: 196, +2179502: 96, +2179503: 7, +2179504: 242, +2179505: 114, +2179506: 77, +2179507: 106, +2179508: 36, +2179509: 69, +2179510: 0, +2179511: 20, +2179512: 194, +2179513: 96, +2179514: 17, +2179515: 44, +2179516: 20, +2179517: 36, +2179518: 10, +2179519: 28, +2179520: 94, +2179521: 107, +2179522: 120, +2179523: 78, +2179524: 145, +2179525: 41, +2179526: 153, +2179527: 106, +2179528: 116, +2179529: 65, +2179530: 15, +2179531: 64, +2179532: 64, +2179533: 34, +2179534: 0, +2179535: 6, +2179536: 32, +2179537: 126, +2179538: 31, +2179539: 64, +2179540: 0, +2179541: 93, +2179542: 10, +2179543: 195, +2179544: 214, +2179545: 194, +2179546: 98, +2179547: 1, +2179548: 64, +2179549: 121, +2179550: 41, +2179551: 0, +2179552: 195, +2179553: 224, +2179554: 255, +2179555: 29, +2179556: 0, +2179557: 0, +2179558: 223, +2179559: 2, +2179560: 215, +2179561: 1, +2179562: 172, +2179563: 0, +2179564: 85, +2179565: 87, +2179566: 13, +2179567: 62, +2179568: 38, +2179569: 37, +2179570: 96, +2179571: 12, +2179572: 177, +2179573: 11, +2179574: 251, +2179575: 72, +2179576: 255, +2179577: 127, +2179578: 0, +2179579: 0, +2179580: 255, +2179581: 127, +2179582: 229, +2179583: 68, +2179584: 255, +2179585: 127, +2179586: 35, +2179587: 0, +2179588: 5, +2179589: 177, +2179590: 11, +2179591: 169, +2179592: 30, +2179593: 69, +2179594: 1, +2179595: 199, +2179596: 32, +2179597: 5, +2179598: 24, +2179599: 99, +2179600: 82, +2179601: 74, +2179602: 140, +2179603: 49, +2179604: 195, +2179605: 32, +2179606: 2, +2179607: 223, +2179608: 2, +2179609: 31, +2179610: 36, +2179611: 0, +2179612: 27, +2179613: 85, +2179614: 87, +2179615: 79, +2179616: 74, +2179617: 228, +2179618: 28, +2179619: 96, +2179620: 12, +2179621: 178, +2179622: 86, +2179623: 13, +2179624: 62, +2179625: 104, +2179626: 45, +2179627: 38, +2179628: 37, +2179629: 255, +2179630: 71, +2179631: 19, +2179632: 1, +2179633: 15, +2179634: 0, +2179635: 92, +2179636: 23, +2179637: 153, +2179638: 2, +2179639: 214, +2179640: 1, +2179641: 35, +2179642: 0, +2179643: 224, +2179644: 46, +2179645: 245, +2179646: 107, +2179647: 225, +2179648: 6, +2179649: 65, +2179650: 6, +2179651: 161, +2179652: 5, +2179653: 95, +2179654: 94, +2179655: 63, +2179656: 24, +2179657: 20, +2179658: 16, +2179659: 10, +2179660: 8, +2179661: 4, +2179662: 4, +2179663: 159, +2179664: 79, +2179665: 216, +2179666: 62, +2179667: 18, +2179668: 46, +2179669: 112, +2179670: 111, +2179671: 255, +2179672: 127, +2179673: 224, +2179674: 94, +2179675: 0, +2179676: 0, +2179677: 145, +2179678: 38, +2179679: 102, +2179680: 17, +2179681: 128, +2179682: 0, +2179683: 32, +2179684: 0, +2179685: 201, +2179686: 29, +2179687: 102, +2179688: 13, +2179689: 3, +2179690: 5, +2179691: 193, +2179692: 48, +2179693: 0, +2179694: 224, +2179695: 39, +2179696: 112, +2179697: 53, +2179698: 203, +2179699: 36, +2179700: 2, +2179701: 4, +2179702: 1, +2179703: 4, +2179704: 46, +2179705: 49, +2179706: 137, +2179707: 24, +2179708: 38, +2179709: 16, +2179710: 4, +2179711: 12, +2179712: 255, +2179713: 67, +2179714: 24, +2179715: 1, +2179716: 20, +2179717: 0, +2179718: 255, +2179719: 22, +2179720: 62, +2179721: 2, +2179722: 123, +2179723: 1, +2179724: 32, +2179725: 12, +2179726: 0, +2179727: 0, +2179728: 151, +2179729: 95, +2179730: 242, +2179731: 86, +2179732: 201, +2179733: 49, +2179734: 160, +2179735: 0, +2179736: 199, +2179737: 96, +2179738: 5, +2179739: 94, +2179740: 107, +2179741: 120, +2179742: 78, +2179743: 145, +2179744: 41, +2179745: 197, +2179746: 128, +2179747: 35, +2179748: 0, +2179749: 15, +2179750: 59, +2179751: 3, +2179752: 31, +2179753: 0, +2179754: 116, +2179755: 1, +2179756: 10, +2179757: 8, +2179758: 255, +2179759: 127, +2179760: 102, +2179761: 13, +2179762: 226, +2179763: 0, +2179764: 153, +2179765: 2, +2179766: 41, +2179767: 0, +2179768: 195, +2179769: 224, +2179770: 255, +1132026: 0, +1132027: 56, +1132028: 250, +1132029: 114, +1132030: 176, +1132031: 85, +1132032: 69, +1132033: 40, +1132034: 1, +1132035: 24, +1132036: 16, +1132037: 98, +1132038: 107, +1132039: 73, +1132040: 198, +1132041: 56, +1132042: 99, +1132043: 44, +1132044: 127, +1132045: 125, +1132046: 213, +1132047: 84, +1132048: 77, +1132049: 56, +1132050: 7, +1132051: 32, +1132052: 31, +1132053: 2, +1132054: 20, +1132055: 16, +1132056: 10, +1132057: 8, +452331: 1, +1133099: 0, +1133100: 56, +1133101: 87, +1133102: 63, +1133103: 77, +1133104: 46, +1133105: 226, +1133106: 0, +1133107: 96, +1133108: 0, +1133109: 176, +1133110: 58, +1133111: 11, +1133112: 34, +1133113: 102, +1133114: 17, +1133115: 36, +1133116: 9, +1133117: 185, +1133118: 33, +1133119: 51, +1133120: 21, +1133121: 206, +1133122: 12, +1133123: 72, +1133124: 4, +1133125: 224, +1133126: 3, +1133127: 160, +1133128: 2, +1133129: 64, +1133130: 1, +1115811: 0, +1115813: 0, +1115814: 0, +1398597: 0, +1398598: 56, +1398599: 255, +1398600: 87, +1398601: 255, +1398602: 43, +1398603: 60, +1398604: 31, +1398605: 120, +1398606: 2, +1398607: 176, +1398608: 1, +1398609: 11, +1398610: 1, +1398611: 135, +1398612: 0, +1398613: 68, +1398614: 0, +1398615: 31, +1398616: 0, +1398617: 18, +1398618: 0, +1398619: 9, +1398620: 0, +1398621: 255, +1398622: 127, +1398623: 255, +1398624: 127, +1398625: 255, +1398626: 127, +1398627: 0, +1398628: 0, +1398629: 0, +1398630: 56, +1398631: 255, +1398632: 87, +1398633: 255, +1398634: 43, +1398635: 60, +1398636: 31, +1398637: 120, +1398638: 2, +1398639: 176, +1398640: 1, +1398641: 11, +1398642: 1, +1398643: 135, +1398644: 0, +1398645: 68, +1398646: 0, +1398647: 255, +1398648: 127, +1398649: 255, +1398650: 127, +1398651: 255, +1398652: 127, +1398653: 255, +1398654: 3, +1398655: 82, +1398656: 2, +1398657: 41, +1398658: 1, +1398659: 0, +1398660: 0, +1398661: 0, +1398662: 56, +1398663: 249, +1398664: 39, +1398665: 117, +1398666: 35, +1398667: 210, +1398668: 26, +1398669: 78, +1398670: 22, +1398671: 171, +1398672: 17, +1398673: 39, +1398674: 13, +1398675: 132, +1398676: 4, +1398677: 0, +1398678: 0, +1398679: 95, +1398680: 127, +1398681: 31, +1398682: 124, +1398683: 22, +1398684: 88, +1398685: 12, +1398686: 48, +1398687: 148, +1398688: 82, +1398689: 206, +1398690: 57, +1398691: 8, +1398692: 33, +1136652: 0, +1136653: 56, +1136654: 156, +1136655: 75, +1136656: 16, +1136657: 38, +1136658: 198, +1136659: 12, +1136660: 99, +1136661: 12, +1136662: 247, +1136663: 66, +1136664: 82, +1136665: 42, +1136666: 173, +1136667: 25, +1136668: 41, +1136669: 13, +1136670: 89, +1136671: 94, +1136672: 114, +1136673: 61, +1136674: 238, +1136675: 44, +1136676: 71, +1136677: 20, +1136678: 59, +1136679: 3, +1136680: 22, +1136681: 2, +1136682: 19, +1136683: 1, +1399057: 0, +1399058: 56, +1399059: 85, +1399060: 87, +1399061: 79, +1399062: 74, +1399063: 228, +1399064: 28, +1399065: 96, +1399066: 12, +1399067: 178, +1399068: 86, +1399069: 13, +1399070: 62, +1399071: 104, +1399072: 45, +1399073: 38, +1399074: 37, +1399075: 255, +1399076: 3, +1399077: 247, +1399078: 2, +1399079: 16, +1399080: 2, +1399081: 8, +1399082: 1, +1399083: 31, +1399084: 0, +1399085: 24, +1399086: 0, +1399087: 14, +1399088: 0, +1500262: 10, +451687: 0, +451688: 8, +451689: 1, +451690: 189, +451691: 3, +1500268: 42, +451693: 20, +451694: 224, +451695: 59, +451696: 168, +451697: 33, +451698: 159, +451699: 87, +1500276: 170, +451701: 74, +451702: 78, +1500279: 8, +1500280: 240, +451705: 0, +451706: 181, +451707: 2, +451708: 107, +1500285: 12, +1500286: 105, +451711: 2, +1500288: 71, +451713: 17, +451714: 116, +451715: 0, +451716: 13, +451717: 0, +1500296: 249, +451721: 0, +451722: 9, +451723: 1, +451724: 189, +1500301: 0, +451726: 6, +451727: 20, +1500304: 55, +1500305: 46, +1500306: 179, +1500307: 29, +1500308: 46, +1500309: 13, +1500310: 63, +1500311: 38, +1500312: 251, +1500313: 29, +1500314: 151, +1500315: 21, +1500316: 82, +1500317: 13, +1500318: 238, +1500319: 8, +1500320: 63, +1500321: 87, +1500322: 170, +1500323: 0, +1500324: 144, +1500325: 4, +1500326: 141, +1500327: 4, +1500330: 103, +1500331: 0, +1500332: 110, +1246855: 0, +1500333: 29, +1500334: 44, +1500335: 25, +451760: 6, +1500337: 16, +1246856: 56, +1500338: 167, +1500339: 8, +1500340: 48, +1500341: 21, +1500342: 14, +1246857: 239, +1500343: 17, +1500344: 204, +1500345: 12, +1500346: 169, +1500347: 8, +1246858: 73, +1500348: 135, +1500349: 4, +1500350: 176, +1500351: 45, +1500352: 101, +1246859: 206, +1500353: 0, +1500354: 223, +1500355: 5, +1500356: 185, +1500357: 5, +1246860: 69, +1500358: 147, +1500359: 1, +1500360: 109, +1500361: 1, +1246861: 140, +1500364: 247, +1500365: 46, +1500366: 115, +1500367: 30, +1246862: 61, +1500368: 206, +1500369: 13, +1500370: 255, +1500371: 38, +1500372: 155, +1246863: 107, +1500373: 30, +1500374: 87, +1500375: 22, +1500376: 18, +1500377: 14, +1246864: 57, +1500378: 174, +1500379: 9, +1500380: 255, +1500381: 87, +1500382: 74, +1246865: 41, +1500383: 1, +1500384: 240, +1500385: 4, +1500386: 237, +1500387: 4, +1246866: 53, +1500388: 202, +1500389: 0, +1500390: 199, +1500391: 0, +1500392: 206, +1246867: 8, +1500393: 29, +1500394: 140, +1500395: 25, +1246868: 45, +1500398: 231, +1500399: 8, +1500400: 144, +1500401: 21, +1500402: 78, +1246869: 198, +1500403: 17, +1500404: 44, +1500405: 13, +1500406: 9, +1500407: 9, +1246870: 40, +1500408: 231, +1500409: 4, +1500410: 16, +1500411: 46, +1500412: 165, +1246871: 165, +1500413: 0, +1500414: 213, +1500415: 5, +1500416: 175, +1500417: 5, +1246872: 32, +1500418: 137, +1500419: 1, +1500420: 99, +1500421: 1, +1129051: 0, +1500422: 113, +1246873: 99, +1129052: 56, +1500423: 59, +1129053: 57, +1500424: 237, +1129054: 87, +1500425: 46, +1129055: 115, +1500426: 105, +1129056: 66, +1500427: 30, +1246874: 28, +1129057: 173, +1500428: 196, +1129058: 45, +1500429: 13, +1129059: 198, +1129060: 20, +1129061: 218, +1500432: 145, +1246875: 6, +1129062: 25, +1500433: 30, +1129063: 116, +1500434: 77, +1129064: 17, +1500435: 22, +1129065: 15, +1500436: 8, +1129066: 13, +1500437: 14, +1246876: 37, +1129067: 170, +1500438: 164, +1129068: 8, +1500439: 9, +1129069: 222, +1500440: 245, +1129070: 15, +1500441: 87, +1129071: 223, +1500442: 64, +1246877: 196, +1129072: 2, +1500443: 1, +1129073: 159, +1500444: 235, +1129074: 1, +1500445: 4, +1129075: 95, +1500446: 232, +1129076: 0, +1500447: 4, +1246878: 28, +1129077: 55, +1500448: 197, +1129078: 0, +1500449: 0, +1129079: 223, +1500450: 194, +1129080: 111, +1500451: 0, +1129081: 6, +1500452: 201, +1246879: 131, +1129082: 0, +1500453: 29, +1129083: 0, +1500454: 135, +1129084: 56, +1500455: 25, +1129085: 255, +451880: 85, +1129086: 47, +1500457: 17, +1246880: 16, +1500458: 226, +1129088: 26, +1500459: 8, +1129089: 74, +1500460: 139, +1129090: 1, +1500461: 21, +1129091: 99, +1500462: 73, +1129092: 0, +1500463: 17, +1129093: 90, +1129094: 39, +1129095: 181, +1500466: 4, +1129096: 14, +1500467: 9, +1129097: 16, +1500468: 226, +2177674: 0, +451893: 1, +1129099: 206, +1500470: 11, +1129100: 1, +1500471: 46, +1129101: 224, +1500472: 160, +1129102: 3, +451897: 20, +1129103: 224, +1500474: 149, +1129104: 2, +451899: 59, +1129105: 0, +1500476: 111, +1129106: 2, +1500477: 13, +1129107: 0, +1500478: 73, +1129108: 1, +451903: 87, +1129109: 0, +1500480: 35, +1129110: 127, +1500481: 9, +1129111: 224, +1500482: 49, +1129112: 109, +1500483: 67, +1129113: 224, +451908: 189, +1129114: 84, +1500485: 54, +451910: 184, +1500487: 38, +1500488: 132, +1500489: 21, +1500490: 181, +1500491: 46, +1500492: 81, +1500493: 38, +1500494: 13, +1500495: 30, +1500496: 200, +1500497: 21, +1500500: 181, +451925: 0, +1500502: 0, +1500503: 9, +1500504: 203, +1500505: 8, +1500506: 200, +1500507: 8, +1500508: 165, +1500509: 4, +1500510: 162, +451935: 46, +1500512: 169, +1500513: 33, +1500514: 103, +1150989: 114, +1500515: 29, +451940: 83, +1500517: 21, +1500518: 194, +1500519: 12, +1500520: 107, +1500521: 25, +1500522: 41, +1500523: 21, +1500524: 7, +1500525: 17, +1500526: 228, +1500527: 12, +1500528: 194, +1500529: 8, +1500530: 235, +1500531: 49, +1500534: 21, +1500535: 25, +1500536: 15, +1500537: 25, +1500538: 201, +1500539: 24, +1500540: 195, +1500541: 24, +1500542: 209, +1500543: 82, +1500544: 77, +1500545: 66, +1500546: 201, +1500547: 53, +1500548: 36, +1500549: 37, +1500550: 85, +1500551: 58, +1500552: 241, +1500553: 49, +1500554: 141, +1500555: 45, +1500556: 72, +1500557: 37, +1500558: 4, +1500559: 33, +1500560: 85, +451985: 17, +1500562: 160, +1500563: 24, +1500564: 139, +1139067: 0, +1139068: 56, +1139069: 156, +1139070: 75, +1139071: 148, +1139072: 54, +1139073: 41, +1139074: 9, +1139075: 66, +1139076: 0, +1139077: 24, +1139078: 59, +1139079: 82, +1139080: 42, +1139081: 173, +1139082: 25, +1139083: 107, +1139084: 17, +1139085: 90, +1139086: 127, +1139087: 192, +1139088: 126, +1139089: 224, +1139090: 109, +1139091: 224, +1139092: 84, +1139093: 29, +1139094: 0, +1139095: 20, +1139096: 0, +1139097: 10, +1139098: 0, +1500570: 98, +451995: 1, +1500572: 105, +1500573: 41, +1500574: 39, +1500575: 33, +452000: 227, +1500577: 28, +1500578: 162, +1500579: 20, +452004: 159, +1500581: 29, +1500582: 9, +1500583: 25, +1500584: 199, +1500585: 24, +1500586: 164, +1500587: 20, +1500588: 130, +1500589: 16, +1500590: 171, +1500591: 53, +1500592: 96, +1500593: 12, +1500594: 117, +1500595: 44, +1500596: 111, +1500597: 44, +1500598: 41, +1500599: 40, +1500602: 49, +1500603: 98, +1500604: 173, +1500605: 85, +452030: 190, +1500607: 73, +1500608: 132, +1500609: 56, +1500610: 181, +1500611: 77, +1500612: 81, +1500613: 69, +1500614: 237, +1270442: 31, +1270443: 3, +1270444: 218, +1270445: 1, +1270446: 245, +1270447: 0, +1270448: 221, +1270449: 2, +1270450: 184, +1270451: 1, +1270452: 211, +1270453: 0, +1270454: 154, +1270455: 2, +1270456: 150, +1270457: 1, +1270458: 210, +1270459: 0, +1270460: 120, +1270461: 2, +1270462: 116, +1270463: 1, +1139392: 0, +1139393: 56, +1139394: 29, +1139395: 2, +1139396: 21, +1139397: 0, +1139398: 8, +1139399: 0, +1139400: 3, +1139401: 0, +1139402: 189, +1139403: 0, +1139404: 19, +1139405: 0, +1139406: 14, +1139407: 0, +1139408: 11, +1139409: 0, +1139410: 190, +1139411: 23, +1139412: 159, +1139413: 26, +1139414: 83, +1139415: 12, +1139416: 75, +1139417: 8, +1139418: 192, +1139419: 126, +1139420: 224, +1139421: 109, +1139422: 224, +1139423: 84, +1270496: 44, +1270497: 1, +1270498: 170, +1270499: 0, +1270500: 72, +1270501: 0, +1270502: 10, +1270503: 1, +1270504: 136, +1270505: 0, +1270506: 71, +1270507: 0, +1270508: 200, +1270509: 0, +1270510: 103, +1270511: 0, +1270512: 37, +1270513: 0, +1270514: 134, +1270515: 0, +1270516: 69, +1270517: 0, +1270518: 36, +1270519: 0, +1270520: 100, +1270521: 0, +1270522: 35, +1270523: 0, +1270524: 34, +1270525: 0, +1270526: 34, +1270527: 0, +1270528: 33, +1270529: 0, +1270530: 1, +1270531: 0, +1270532: 0, +1270533: 0, +1270534: 0, +1270535: 0, +1270536: 0, +1270537: 0, +1500631: 20, +1500632: 41, +1500633: 49, +1270554: 0, +1270555: 0, +1270556: 0, +1270557: 0, +1270558: 0, +1270559: 0, +1270560: 33, +1270561: 4, +1270562: 1, +1270563: 4, +1270564: 0, +1270565: 0, +1270566: 0, +1270567: 0, +1270568: 33, +452060: 6, +1270570: 1, +1270571: 0, +1270572: 0, +1270573: 0, +1270574: 0, +1270575: 0, +1270576: 34, +1270577: 4, +1270578: 2, +1270579: 0, +1270580: 1, +1270581: 0, +1270582: 67, +1270583: 8, +1270584: 34, +1270585: 8, +1270586: 1, +1270587: 0, +1270588: 0, +1270589: 0, +1270590: 66, +1270591: 8, +1270592: 34, +1270593: 4, +1270594: 1, +1270595: 4, +1270596: 1, +1270597: 0, +1270598: 68, +1270599: 12, +1270600: 4, +1270601: 0, +1270602: 2, +1270603: 0, +1270604: 133, +1270605: 16, +1270606: 67, +1270607: 12, +1270608: 1, +1270609: 4, +1270610: 0, +1270611: 0, +1270612: 100, +1270613: 12, +1270614: 67, +1270615: 8, +1270616: 34, +1270617: 4, +1270618: 1, +1270619: 4, +1270620: 102, +1270621: 16, +1270622: 6, +1270623: 4, +1270624: 4, +1270625: 0, +1270626: 167, +1270627: 20, +1270628: 100, +1270629: 16, +1270630: 1, +1270631: 4, +1270632: 0, +1270633: 0, +1270634: 133, +1270635: 20, +1270636: 100, +1270637: 12, +1270638: 35, +1270639: 8, +1270640: 2, +1270641: 4, +1270642: 136, +1270643: 24, +1270644: 8, +1270645: 4, +1270646: 5, +1270647: 4, +1270648: 232, +1270649: 28, +1270650: 134, +1270651: 20, +1270652: 2, +1270653: 4, +1270654: 1, +1270655: 0, +1270656: 167, +1270657: 24, +1270658: 101, +1270659: 16, +1270660: 67, +1270661: 12, +1270662: 34, +1270663: 8, +1270664: 202, +1270665: 28, +1270666: 10, +1270667: 8, +1270668: 6, +1270669: 4, +1270670: 10, +1270671: 33, +1270672: 167, +1270673: 24, +1270674: 2, +1270675: 8, +1270676: 1, +1270677: 0, +1270678: 200, +1270679: 28, +1270680: 134, +1270681: 20, +1270682: 68, +1270683: 12, +1270684: 35, +1270685: 8, +1270686: 236, +1270687: 36, +1270688: 12, +1270689: 8, +1270690: 8, +1270691: 4, +1270692: 44, +1270693: 37, +1270694: 200, +1270695: 28, +1270696: 35, +1270697: 8, +1270698: 1, +1270699: 0, +1270700: 233, +1270701: 32, +1270702: 167, +1270703: 24, +1270704: 69, +1270705: 16, +1270706: 35, +1270707: 8, +1270708: 14, +1270709: 41, +1270710: 14, +1270711: 8, +1270712: 9, +1270713: 4, +1270714: 109, +1270715: 45, +1270716: 233, +1270717: 32, +1270718: 35, +1270719: 8, +1270720: 1, +1270721: 0, +1270722: 11, +1270723: 41, +1270724: 168, +1270725: 28, +1270726: 101, +1270727: 16, +1270728: 36, +1270729: 12, +1270730: 48, +1270731: 49, +1270732: 16, +1270733: 12, +1270734: 10, +1270735: 8, +1270736: 143, +1270737: 49, +1270738: 234, +1270739: 36, +1270740: 36, +1270741: 12, +1270742: 1, +1270743: 0, +1270744: 44, +1270745: 45, +1270746: 201, +1270747: 28, +1270748: 102, +1500666: 46, +1270750: 36, +1270751: 12, +1270752: 82, +1270753: 53, +1270754: 18, +1270755: 12, +1270756: 12, +1270757: 8, +1270758: 209, +1270759: 57, +1270760: 12, +1270761: 41, +1270762: 36, +1270763: 12, +1270764: 2, +1270765: 0, +1270766: 78, +1270767: 49, +1270768: 234, +1270769: 32, +1270770: 135, +1270771: 24, +1270772: 69, +1270773: 16, +1270774: 148, +1270775: 61, +1270776: 20, +1270777: 16, +1270778: 13, +1270779: 8, +1270780: 243, +1270781: 61, +1270782: 45, +1270783: 45, +1270784: 37, +1270785: 12, +1270786: 2, +1270787: 0, +1270788: 111, +1270789: 57, +1270790: 11, +1270791: 37, +1270792: 136, +1270793: 24, +1270794: 69, +1270795: 16, +1270796: 182, +1270797: 65, +1270798: 22, +1270799: 16, +1270800: 14, +1270801: 12, +1270802: 20, +1270803: 66, +1270804: 78, +1270805: 49, +1270806: 37, +1270807: 16, +1270808: 2, +1270809: 0, +1270810: 144, +1270811: 61, +1270812: 12, +1270813: 41, +1270814: 136, +1270815: 28, +1270816: 70, +1270817: 16, +1270818: 216, +1270819: 73, +1270820: 24, +1270821: 16, +1270822: 16, +1270823: 12, +1270824: 86, +1270825: 74, +1270826: 111, +1270827: 53, +1270828: 38, +1270829: 16, +1270830: 2, +1270831: 0, +1270832: 178, +1270833: 65, +1270834: 45, +1270835: 45, +1270836: 169, +1270837: 28, +1270838: 71, +1270839: 20, +1270840: 250, +1270841: 81, +1270842: 26, +1270843: 20, +1270844: 17, +1270845: 12, +1270846: 120, +1270847: 78, +1270848: 144, +1270849: 61, +1270850: 38, +1270851: 16, +1270852: 2, +1270853: 4, +1270854: 243, +1270855: 69, +1270856: 79, +1270857: 49, +1270858: 170, +1270859: 32, +1270860: 71, +1270861: 20, +1270862: 29, +1270863: 86, +1270864: 61, +1270865: 20, +1270866: 18, +1270867: 12, +1270868: 186, +1270869: 86, +1270870: 178, +1139799: 0, +1139800: 56, +1139801: 31, +1139802: 47, +1139803: 50, +1139804: 9, +1139805: 106, +1139806: 0, +1139807: 3, +1139808: 0, +1139809: 28, +1139810: 34, +1139811: 182, +1139812: 25, +1139813: 81, +1139814: 21, +1139815: 236, +1139816: 16, +1139817: 190, +1139818: 23, +1139819: 159, +1139820: 24, +1139821: 83, +1139822: 12, +1139823: 75, +1139824: 8, +1139825: 224, +1139826: 59, +1139827: 128, +1139828: 38, +1139829: 128, +1139830: 21, +1270903: 32, +1270904: 74, +1270905: 24, +1270906: 31, +1270907: 86, +1270908: 63, +1270909: 24, +1270910: 21, +1270911: 16, +1270912: 191, +1270913: 2, +1270914: 154, +1270915: 1, +1270916: 214, +1270917: 0, +1270918: 90, +1270919: 74, +1270920: 115, +1270921: 57, +1270922: 42, +1270923: 20, +1270924: 7, +1270925: 4, +1270926: 182, +1270927: 69, +1270928: 50, +1270929: 49, +1270930: 174, +1270931: 32, +1270932: 75, +1270933: 24, +1270934: 255, +1270935: 81, +1270936: 63, +1270937: 24, +1270938: 21, +1270939: 16, +1270940: 159, +1270941: 2, +1270942: 154, +1270943: 1, +1270944: 214, +1270945: 0, +1270946: 59, +1270947: 74, +1270948: 84, +1270949: 57, +1270950: 43, +1270951: 20, +1270952: 8, +1270953: 8, +1270954: 183, +1270955: 65, +1270956: 51, +1270957: 45, +1270958: 175, +1270959: 32, +1270960: 76, +1270961: 24, +1270962: 223, +1270963: 77, +1270964: 63, +1270965: 24, +1270966: 22, +1270967: 16, +1270968: 127, +1270969: 6, +1270970: 123, +1129087: 247, +1270972: 183, +1270973: 4, +1500704: 137, +1500705: 32, +1500706: 103, +1500707: 28, +1500708: 69, +452133: 3, +1500710: 109, +1500711: 65, +1500712: 3, +1500713: 20, +452138: 202, +1500715: 44, +1500716: 121, +452141: 87, +1500718: 51, +1500719: 40, +1500720: 45, +1500721: 40, +1500722: 59, +1500723: 98, +1500724: 183, +1500725: 85, +452150: 108, +452151: 1, +452152: 83, +452153: 2, +452154: 5, +452155: 17, +452156: 117, +1500733: 69, +452158: 14, +1140091: 0, +1140092: 56, +1140093: 255, +1140094: 2, +1140095: 191, +1140096: 1, +1140097: 15, +1140098: 0, +1140099: 8, +1140100: 0, +1140101: 191, +1140102: 1, +1140103: 27, +1140104: 1, +1140105: 186, +1140106: 0, +1140107: 17, +1140108: 0, +1500738: 110, +1140110: 90, +1140111: 180, +1140112: 65, +1140113: 13, +1140114: 41, +1500739: 48, +1140116: 16, +1140117: 255, +1140118: 3, +1140119: 55, +1140120: 2, +1140121: 209, +1140122: 0, +1500741: 126, +1500742: 10, +1500743: 40, +1500744: 80, +1500745: 24, +1500746: 77, +1500747: 24, +1500748: 42, +1500749: 20, +1500750: 39, +1500751: 20, +1500752: 46, +1500753: 49, +1500754: 236, +1500755: 44, +1500756: 170, +1500757: 36, +1500758: 71, +1500759: 28, +1500760: 240, +1500761: 40, +1500762: 174, +1500763: 36, +1500764: 140, +1500765: 32, +1500766: 105, +1500767: 28, +1500768: 71, +1500769: 24, +452196: 3, +1500773: 20, +1500774: 127, +1500775: 24, +1500776: 121, +1500777: 24, +452202: 6, +452203: 20, +1500780: 45, +1500781: 20, +1500782: 59, +1500783: 78, +1500784: 183, +452209: 87, +1500786: 51, +1500787: 49, +1500788: 142, +1500789: 32, +1500790: 191, +1500791: 57, +1500792: 91, +1500793: 49, +1500794: 247, +1500795: 40, +1500796: 178, +1500797: 32, +1500798: 110, +1500799: 28, +1500800: 191, +1500801: 106, +1500802: 10, +1500803: 20, +1500818: 71, +452243: 0, +1500820: 240, +1500821: 28, +1500822: 174, +452247: 2, +452248: 5, +1500825: 20, +1500826: 105, +1500827: 16, +1500828: 71, +1500829: 16, +1500830: 112, +1500831: 53, +1500832: 5, +1500833: 12, +1500844: 159, +1500845: 38, +1500846: 89, +452271: 0, +1500848: 76, +1500849: 0, +1500852: 57, +1500853: 87, +1500854: 115, +1500855: 66, +1500856: 173, +1500857: 45, +1500858: 198, +1500859: 20, +1500860: 127, +1500861: 54, +1500862: 249, +452287: 37, +452288: 159, +452289: 87, +452290: 211, +452291: 74, +1500868: 134, +1500869: 12, +1500870: 255, +1500871: 127, +1500872: 0, +1500873: 0, +1500874: 95, +1500875: 34, +1500876: 57, +1500877: 1, +1500878: 45, +1500879: 0, +1500880: 5, +452305: 0, +452306: 14, +1500883: 78, +1500886: 142, +1500887: 41, +1500888: 167, +1500889: 16, +1500890: 63, +1500891: 50, +1500892: 217, +1500893: 37, +452318: 225, +1500895: 29, +1500896: 237, +1500897: 16, +1500898: 103, +1500899: 8, +1500900: 191, +1500901: 119, +1500902: 2, +1500903: 0, +1500904: 31, +1500905: 30, +1141112: 0, +1141113: 56, +1141114: 31, +1141115: 77, +1141116: 182, +1141117: 56, +1141118: 110, +1141119: 36, +1141120: 72, +1141121: 20, +1141122: 255, +1141123: 71, +1141124: 250, +1141125: 46, +1141126: 22, +1141127: 22, +1141128: 50, +1141129: 1, +1141130: 57, +1141131: 111, +1141132: 115, +1141133: 90, +1141134: 173, +452333: 0, +1141136: 8, +1141137: 45, +1141138: 99, +1141139: 24, +1141140: 255, +1141141: 127, +1141142: 65, +1141143: 0, +1500911: 0, +1500912: 154, +1500913: 66, +1500914: 245, +1500915: 49, +1500916: 80, +1500917: 33, +1500920: 255, +1500921: 41, +1500922: 154, +1500923: 33, +1500924: 21, +1500925: 25, +452350: 7, +452351: 20, +452352: 226, +452353: 59, +1500930: 31, +1500931: 99, +1500932: 6, +1500933: 0, +1500934: 159, +1500935: 21, +452360: 80, +1500937: 0, +1500938: 51, +1500939: 0, +1500940: 14, +452365: 1, +1500942: 251, +1500943: 49, +1500944: 119, +1500945: 37, +452370: 6, +1500947: 24, +452372: 118, +452373: 0, +452374: 15, +1500951: 29, +452378: 6, +452379: 0, +1500956: 147, +1500957: 12, +1500958: 80, +1500959: 4, +1500960: 95, +1500961: 74, +1500962: 12, +1500963: 0, +1500964: 159, +1500965: 21, +1500966: 219, +1500967: 0, +1500968: 51, +1500969: 0, +1500970: 14, +1500971: 0, +1500972: 251, +1500973: 49, +1500974: 119, +1500975: 37, +1500976: 244, +1500977: 24, +1500978: 112, +1500979: 12, +1500980: 127, +1500981: 29, +1500982: 59, +1500983: 25, +1500984: 215, +1500985: 16, +1500988: 80, +1500989: 4, +1500990: 95, +452415: 1, +1500992: 12, +1500993: 0, +1501004: 0, +1501005: 0, +452430: 189, +1501007: 0, +1501008: 0, +1501009: 0, +1501010: 36, +1501011: 0, +1501012: 173, +1501013: 41, +1501014: 74, +1501015: 33, +1501016: 231, +1501017: 20, +1501018: 99, +1501019: 12, +1501022: 0, +1501023: 0, +1501024: 0, +1501025: 0, +1501026: 0, +1501027: 0, +1501028: 0, +1501029: 0, +1501030: 173, +1501031: 41, +1501032: 0, +1501033: 0, +1501034: 4, +452459: 87, +1501036: 4, +1501037: 0, +1501038: 4, +452463: 58, +1501040: 7, +1501041: 0, +452466: 190, +1501043: 33, +1501044: 12, +1501045: 29, +1501046: 202, +1501047: 16, +1501048: 70, +1501049: 8, +1501050: 4, +1501051: 0, +1501052: 4, +1501053: 0, +1501056: 4, +1501057: 0, +1501058: 4, +1501059: 0, +1501060: 111, +1501061: 33, +1501062: 4, +1501063: 0, +1501064: 8, +1501065: 0, +1501066: 8, +1501067: 0, +1501068: 8, +1501069: 0, +1501070: 11, +1501071: 0, +1501072: 49, +1501073: 29, +1501074: 239, +1501075: 20, +1501076: 173, +1501077: 12, +1501078: 74, +452503: 0, +452504: 87, +1501081: 0, +452506: 9, +452507: 17, +1501084: 8, +452509: 0, +1501086: 8, +1501087: 0, +1142215: 0, +1142216: 56, +1142217: 90, +1142218: 127, +1142219: 224, +1142220: 59, +1142221: 128, +1142222: 38, +1142223: 32, +1142224: 9, +1142225: 90, +1142226: 79, +1501091: 29, +1142228: 54, +1142229: 16, +1142230: 38, +1142231: 206, +1142232: 29, +1142233: 148, +1142234: 82, +1142235: 206, +1142236: 57, +1142237: 8, +1142238: 33, +1142239: 132, +1142240: 16, +1142241: 59, +1142242: 3, +1142243: 22, +1142244: 2, +1142245: 19, +1142246: 1, +1501095: 0, +1501096: 14, +1501097: 0, +1501098: 14, +1501099: 0, +1501100: 16, +1501101: 0, +452526: 159, +452527: 87, +1501104: 179, +1501105: 16, +1501106: 114, +1501107: 8, +1501108: 48, +452533: 0, +1501110: 14, +1501111: 0, +1501112: 14, +1501113: 0, +1501114: 14, +1501115: 0, +1501116: 14, +1501117: 0, +1501118: 14, +1501119: 0, +1501120: 245, +1501121: 20, +1501124: 14, +1501125: 0, +1501126: 14, +1501127: 0, +1501128: 14, +1501129: 0, +1501130: 16, +1501131: 0, +1501132: 245, +1501133: 20, +1501134: 179, +1501135: 16, +1501136: 114, +1501137: 8, +1501138: 48, +1501139: 4, +1501140: 14, +1501141: 0, +1501142: 14, +1501143: 0, +1501144: 14, +1501145: 0, +1501146: 14, +1501147: 0, +452572: 85, +1501149: 0, +452574: 7, +1501151: 20, +1501152: 14, +1501153: 0, +1501170: 184, +452595: 87, +452596: 213, +452597: 74, +1501174: 70, +1501175: 22, +1501176: 227, +452601: 0, +1501178: 159, +1501179: 79, +1501180: 216, +1501181: 62, +1501182: 18, +1501183: 46, +1501184: 205, +1501185: 8, +1501186: 255, +452611: 0, +1501188: 184, +1501189: 79, +1501192: 39, +1501193: 18, +1501194: 228, +452619: 1, +1501196: 126, +1501197: 75, +452622: 7, +452623: 20, +1501200: 241, +1501201: 41, +1501202: 205, +1501203: 8, +1501204: 255, +1501205: 123, +1501206: 185, +1501207: 71, +1501208: 242, +1501209: 10, +1501210: 40, +1501211: 18, +1501212: 228, +1501213: 0, +1501214: 60, +452639: 0, +452640: 84, +452641: 2, +1501218: 241, +1501219: 41, +452644: 118, +1501221: 8, +1501222: 255, +452647: 0, +452650: 5, +452651: 0, +452652: 10, +452653: 1, +452654: 255, +1501231: 0, +1501232: 27, +1501233: 67, +1501234: 117, +1501235: 54, +1501236: 208, +1501237: 37, +452662: 159, +1501239: 8, +1501240: 255, +1501241: 111, +1501242: 153, +1501243: 51, +1501244: 243, +1501245: 6, +1501246: 9, +1501247: 14, +1501248: 229, +1501249: 0, +1501250: 250, +1501251: 66, +452676: 6, +452677: 17, +452678: 118, +1501255: 37, +1501256: 171, +452681: 0, +1501260: 153, +1501261: 43, +1501262: 244, +1501263: 6, +1501264: 234, +1501265: 9, +1501266: 230, +1501267: 0, +1501268: 217, +1501269: 62, +1501270: 51, +452695: 37, +1501272: 142, +452697: 87, +1501274: 171, +1501275: 4, +1501276: 222, +1501277: 99, +452702: 188, +452703: 0, +1501280: 212, +452705: 1, +1501282: 235, +452707: 0, +1501284: 230, +1501285: 0, +452710: 5, +452711: 17, +452712: 117, +452713: 0, +1501290: 142, +1501291: 33, +1501294: 222, +452719: 0, +1501296: 154, +1501297: 27, +452722: 255, +1501299: 6, +452724: 6, +452725: 20, +1501302: 231, +452727: 59, +1501304: 118, +452729: 37, +1501306: 241, +1501307: 41, +452732: 211, +1501309: 29, +1501310: 138, +1501311: 4, +1501312: 222, +1501313: 87, +452754: 8, +1501331: 114, +1501332: 223, +1501333: 44, +1501334: 185, +1501335: 36, +452760: 224, +452761: 59, +1501338: 169, +452763: 33, +1501340: 189, +1501341: 110, +1501342: 221, +452767: 74, +1501344: 183, +452769: 58, +1501346: 174, +452771: 0, +452772: 190, +452773: 1, +452774: 142, +452775: 0, +1501352: 187, +452777: 2, +452778: 4, +452779: 17, +452780: 116, +1501357: 32, +452782: 13, +452783: 0, +452798: 16, +452799: 0, +452800: 8, +452801: 1, +452802: 31, +452803: 66, +452804: 5, +452805: 20, +452806: 224, +1130012: 0, +452807: 59, +1130013: 56, +452808: 168, +1130014: 29, +452809: 33, +1130015: 2, +452810: 159, +1130016: 21, +452811: 87, +1130017: 0, +452812: 210, +1130018: 8, +452813: 74, +1130019: 0, +452814: 78, +1130020: 3, +452815: 58, +1130021: 0, +452816: 187, +1130022: 189, +452817: 0, +1130023: 0, +452818: 20, +2178600: 255, +452819: 89, +2178601: 87, +452820: 170, +1130026: 14, +452821: 48, +1130027: 0, +452822: 116, +2178604: 60, +452823: 2, +2178605: 31, +452824: 4, +1130030: 255, +452825: 17, +2178607: 2, +452826: 116, +2178608: 176, +452827: 0, +1130033: 3, +452828: 13, +1130034: 55, +452829: 0, +2178611: 1, +1130036: 209, +1130037: 0, +452832: 4, +1130038: 160, +452833: 0, +1130039: 3, +1130040: 192, +1130041: 2, +1130042: 224, +1130043: 5, +452848: 79, +452849: 58, +452850: 188, +452851: 0, +452852: 21, +452853: 89, +452854: 171, +452855: 48, +452856: 83, +452857: 2, +452858: 5, +452859: 17, +452860: 117, +452861: 0, +452862: 14, +452863: 0, +452866: 4, +452867: 0, +452868: 9, +452869: 1, +452870: 31, +452871: 66, +452872: 6, +452873: 20, +452874: 225, +452875: 59, +452876: 202, +452877: 37, +452878: 159, +452879: 87, +452880: 211, +452881: 74, +452882: 79, +452883: 58, +452884: 188, +452885: 0, +452886: 21, +452887: 89, +452888: 171, +452889: 48, +452890: 83, +452891: 2, +452892: 5, +452893: 17, +452894: 117, +452895: 0, +452896: 14, +452897: 0, +452900: 5, +452901: 0, +452902: 10, +452903: 1, +452904: 31, +452905: 66, +452906: 7, +452907: 20, +452908: 226, +452909: 59, +452910: 204, +452911: 37, +452912: 159, +452913: 87, +452914: 212, +452915: 74, +452916: 80, +452917: 58, +452918: 188, +452919: 0, +452920: 22, +452921: 89, +452922: 172, +452923: 48, +452924: 84, +452925: 2, +452926: 6, +452927: 17, +452928: 118, +452929: 0, +452930: 15, +452931: 0, +452934: 6, +452935: 0, +452936: 10, +452937: 1, +452938: 31, +452939: 66, +452940: 7, +452941: 20, +452942: 226, +452943: 59, +452944: 204, +452945: 37, +452946: 159, +452947: 87, +452948: 212, +452949: 74, +452950: 80, +452951: 58, +452952: 188, +452953: 0, +452954: 22, +452955: 89, +452956: 172, +452957: 48, +452958: 84, +452959: 2, +452960: 6, +452961: 17, +452962: 118, +452963: 0, +452964: 15, +452965: 0, +452968: 7, +452969: 0, +452970: 11, +452971: 1, +452972: 31, +452973: 66, +452974: 8, +452975: 20, +452976: 227, +452977: 59, +452978: 238, +452979: 41, +452980: 159, +452981: 87, +452982: 213, +452983: 74, +452984: 81, +452985: 58, +452986: 189, +452987: 0, +452988: 23, +452989: 89, +452990: 173, +452991: 48, +452992: 85, +452993: 2, +452994: 7, +452995: 17, +452996: 119, +452997: 0, +452998: 16, +452999: 0, +453002: 8, +453003: 0, +453004: 11, +453005: 1, +453006: 31, +453007: 66, +453008: 8, +453009: 20, +453010: 227, +453011: 59, +453012: 238, +453013: 41, +453014: 159, +453015: 87, +453016: 213, +453017: 74, +453018: 81, +453019: 58, +453020: 189, +453021: 0, +453022: 23, +453023: 89, +453024: 173, +453025: 48, +453026: 85, +453027: 2, +453028: 7, +453029: 17, +453030: 119, +453031: 0, +453032: 16, +453033: 0, +453036: 8, +453037: 0, +453038: 13, +453039: 1, +453040: 31, +453041: 66, +456480: 0, +453042: 10, +453043: 20, +452116: 109, +453128: 85, +453129: 2, +453130: 7, +453131: 17, +453132: 119, +453133: 0, +453134: 16, +453135: 0, +453138: 7, +453139: 0, +453140: 11, +453141: 1, +453142: 31, +453143: 66, +453144: 8, +453145: 20, +453146: 227, +453147: 59, +453148: 238, +453149: 41, +453150: 159, +453151: 87, +453152: 213, +453153: 74, +453154: 81, +453155: 58, +453156: 189, +453157: 0, +453158: 23, +453159: 89, +453160: 173, +453161: 48, +453162: 85, +453163: 2, +453164: 7, +453165: 17, +453166: 119, +456481: 80, +453167: 0, +453168: 16, +453169: 0, +453172: 6, +453173: 0, +453174: 10, +453175: 1, +453176: 31, +453177: 66, +453178: 7, +453179: 20, +453180: 226, +453181: 59, +453182: 204, +453183: 37, +453184: 159, +1277319: 95, +1277320: 0, +1277321: 55, +1277322: 0, +1277323: 63, +1277324: 0, +1277325: 25, +1277326: 0, +1277327: 63, +1277328: 0, +1277329: 27, +1277330: 0, +1277331: 31, +1277332: 0, +1277333: 29, +1277334: 0, +1277335: 31, +1277336: 0, +1277337: 31, +1277338: 0, +1277339: 31, +1277340: 0, +1277341: 29, +1277342: 0, +1277343: 63, +1277344: 0, +1277345: 27, +1277346: 0, +1277347: 63, +1277348: 0, +1277349: 25, +1277350: 0, +453191: 0, +453192: 22, +453193: 89, +453194: 172, +453195: 48, +453196: 84, +453197: 2, +453198: 6, +453199: 17, +453200: 118, +453201: 0, +453202: 15, +453203: 0, +453206: 5, +453207: 0, +453208: 10, +453209: 1, +453210: 31, +453211: 66, +453212: 7, +453213: 20, +453214: 226, +453215: 59, +453216: 204, +453217: 37, +453218: 159, +453219: 87, +453220: 212, +453221: 74, +453222: 80, +453223: 58, +453224: 188, +453225: 0, +453226: 22, +453227: 89, +453228: 172, +453229: 48, +453230: 84, +453231: 2, +453232: 6, +453233: 17, +453234: 118, +453235: 0, +453236: 15, +453237: 0, +453240: 4, +453241: 0, +453242: 9, +453243: 1, +453244: 31, +453245: 66, +453246: 6, +453247: 20, +453248: 225, +453249: 59, +453250: 202, +453251: 37, +453252: 159, +453253: 87, +453254: 211, +453255: 74, +453256: 79, +453257: 58, +453258: 188, +453259: 0, +453260: 21, +453261: 89, +453262: 171, +453263: 48, +453264: 83, +453265: 2, +453266: 5, +453267: 17, +453268: 117, +453269: 0, +453270: 14, +453271: 0, +453274: 4, +453275: 0, +453276: 9, +453277: 1, +453278: 31, +453279: 66, +453280: 6, +453281: 20, +453282: 225, +453283: 59, +453284: 202, +453285: 37, +453286: 159, +453287: 87, +453288: 211, +453289: 74, +453290: 79, +453291: 58, +453292: 188, +453293: 0, +453294: 21, +453295: 89, +453296: 171, +453297: 48, +453298: 83, +453299: 2, +453300: 5, +453301: 17, +453302: 117, +453303: 0, +453304: 14, +453305: 0, +453308: 16, +453309: 0, +453310: 8, +453311: 1, +453312: 31, +453313: 66, +453314: 5, +453315: 20, +453316: 224, +453317: 59, +453318: 168, +453319: 33, +453320: 159, +453321: 87, +453322: 210, +453323: 74, +453324: 78, +453325: 58, +453326: 187, +453327: 0, +453328: 20, +453329: 89, +453330: 170, +453331: 48, +453332: 116, +453333: 2, +453334: 4, +453335: 17, +453336: 116, +453337: 0, +453338: 13, +453339: 0, +453352: 169, +453353: 30, +453354: 177, +453355: 11, +453360: 103, +453361: 22, +453362: 78, +453363: 3, +453368: 37, +453369: 14, +453370: 235, +453371: 2, +453376: 227, +453377: 5, +453378: 136, +453379: 2, +453384: 161, +453385: 1, +453386: 37, +453387: 2, +453392: 227, +453393: 5, +453394: 136, +453395: 2, +453400: 37, +453401: 14, +453402: 235, +453403: 2, +453408: 103, +453409: 22, +453410: 78, +453411: 3, +230300: 0, +230301: 56, +230302: 88, +230303: 127, +230304: 213, +230305: 110, +230306: 113, +230307: 90, +230308: 238, +230309: 73, +230310: 106, +230311: 53, +230312: 231, +230313: 36, +230314: 131, +230315: 16, +2179281: 35, +2179282: 0, +2179283: 27, +2179284: 255, +2179285: 87, +2179286: 255, +2179287: 43, +2179288: 60, +2179289: 31, +2179290: 120, +2179291: 2, +2179292: 176, +2179293: 1, +2179294: 11, +2179295: 1, +2179296: 135, +2179297: 0, +2179298: 68, +2179299: 0, +2179300: 31, +2179301: 0, +1130726: 86, +2179303: 0, +2179304: 9, +2179305: 0, +2179306: 255, +2179307: 3, +2179308: 82, +2179309: 2, +2179310: 41, +2179311: 1, +2179312: 35, +1152603: 0, +890460: 116, +1152605: 223, +1152606: 62, +890463: 0, +1152608: 0, +1152609: 15, +890466: 14, +1152611: 5, +1152612: 0, +890469: 67, +1152614: 1, +1152615: 24, +1152616: 1, +1152617: 147, +1152618: 0, +890475: 34, +1279623: 0, +1279624: 56, +1279625: 157, +1279626: 85, +1279627: 22, +1279628: 24, +1279629: 13, +1279630: 16, +1279631: 159, +1279632: 75, +1279633: 55, +1279634: 63, +1279635: 208, +1279636: 54, +1279637: 105, +1279638: 46, +1279639: 8, +1279640: 38, +1279641: 166, +1279642: 29, +1279643: 37, +1279644: 17, +1279645: 197, +1279646: 8, +1279647: 3, +1279648: 0, +1279649: 24, +1279650: 99, +1279651: 255, +1279652: 127, +1279653: 0, +1279654: 0, +1279655: 0, +1279656: 56, +1279657: 157, +1279658: 85, +1279659: 22, +1279660: 24, +1279661: 13, +1279662: 16, +1279663: 159, +1279664: 75, +1279665: 55, +1279666: 63, +1279667: 208, +1279668: 54, +1279669: 105, +1279670: 46, +1279671: 8, +1279672: 38, +1279673: 166, +1279674: 29, +1279675: 37, +1279676: 17, +1279677: 197, +1279678: 8, +1279679: 3, +1279680: 0, +1279681: 24, +1279682: 99, +1279683: 255, +1279684: 127, +1279685: 0, +1279686: 0, +1279687: 0, +1279688: 4, +1279689: 107, +1279690: 45, +1279691: 107, +1279692: 45, +1279693: 107, +1279694: 45, +1279695: 134, +1279696: 25, +1279697: 194, +1279698: 20, +1279699: 64, +1279700: 8, +1279701: 0, +1279702: 4, +1279703: 237, +1279704: 49, +1279705: 198, +1279706: 24, +1279707: 195, +1279708: 16, +1279709: 32, +1279710: 4, +1279711: 82, +1279712: 2, +1279713: 107, +1279714: 45, +1279715: 82, +1279716: 74, +1279717: 0, +1279718: 0, +1673099: 0, +1673100: 56, +1673101: 29, +1673102: 2, +1673103: 21, +1673104: 0, +1673105: 8, +1673106: 0, +1673107: 3, +1673108: 0, +1673109: 189, +1673110: 0, +1673111: 19, +1673112: 0, +1673113: 14, +1673114: 0, +1673115: 11, +1673116: 0, +1673117: 90, +1673118: 127, +1673119: 192, +1673120: 126, +1673121: 224, +1673122: 109, +1673123: 224, +1673124: 84, +1673125: 224, +1673126: 3, +1673127: 160, +1673128: 2, +1673129: 64, +1673130: 1, +1149168: 0, +1149169: 56, +1149170: 31, +1149171: 77, +1149172: 182, +1149173: 56, +1149174: 110, +1149175: 36, +1149176: 72, +1149177: 20, +1149178: 255, +1149179: 71, +1149180: 250, +1149181: 46, +1149182: 22, +1149183: 22, +1149184: 50, +1149185: 1, +1149186: 57, +1149187: 111, +1149188: 115, +1149189: 90, +1149190: 173, +1149191: 65, +1149192: 8, +1149193: 45, +1149194: 99, +1149195: 24, +1149196: 255, +1149197: 127, +1149198: 65, +1149199: 0, +1673725: 0, +1673726: 56, +1673727: 149, +1673728: 63, +1673729: 139, +1673730: 46, +1673731: 32, +1673732: 1, +1673733: 96, +1673734: 0, +1673735: 238, +1673736: 58, +1673737: 73, +1673738: 34, +1673739: 164, +1673740: 17, +1673741: 98, +1673742: 9, +1673743: 187, +1673744: 57, +1673745: 245, +1673746: 48, +1673747: 110, +1673748: 44, +1673749: 39, +1673750: 40, +1673751: 147, +1673752: 127, +1673753: 206, +1673754: 110, +1673755: 41, +1673756: 98, +1673921: 0, +1673922: 56, +1673923: 29, +1673924: 2, +1673925: 21, +1673926: 0, +1673927: 8, +1673928: 0, +1673929: 3, +1673930: 0, +1673931: 95, +1673932: 1, +1673933: 118, +1673934: 0, +1673935: 80, +1673936: 0, +1673937: 11, +1673938: 0, +1673939: 255, +1673940: 127, +1673941: 224, +1673942: 86, +1673943: 128, +1673944: 49, +1673945: 192, +1673946: 24, +1673947: 255, +1673948: 67, +1673949: 220, +1673950: 66, +1673951: 118, +1673952: 65, +1149967: 0, +1149968: 56, +1149969: 255, +1149970: 2, +1149971: 191, +1149972: 1, +1149973: 15, +1149974: 0, +1149975: 8, +1149976: 0, +1149977: 191, +1149978: 1, +1149979: 27, +1149980: 1, +1149981: 186, +1149982: 0, +1149983: 17, +1149984: 0, +1149985: 90, +1149986: 127, +1149987: 69, +1149988: 93, +1149989: 195, +1149990: 64, +1149991: 98, +1149992: 36, +1149993: 233, +1149994: 83, +1149995: 198, +1149996: 58, +1149997: 97, +1149998: 25, +1677184: 22, +1677185: 234, +1677186: 17, +1677187: 6, +1677188: 9, +1677189: 189, +1677190: 54, +1677191: 249, +1677192: 41, +1677193: 21, +1677194: 29, +890763: 33, +1677196: 16, +1677197: 223, +1677198: 17, +1677199: 123, +890768: 78, +1677201: 22, +1677202: 17, +1677203: 179, +1677204: 16, +890773: 89, +1677206: 16, +1677207: 222, +1677208: 103, +1677209: 8, +890778: 4, +1677211: 0, +1677212: 0, +1677213: 242, +890782: 13, +890783: 0, +1677216: 18, +1677217: 236, +1677218: 17, +1677219: 8, +890788: 191, +1677221: 125, +1677222: 50, +1677223: 185, +1677224: 37, +1677225: 214, +1677226: 24, +1677227: 83, +1677228: 20, +1677229: 191, +1677230: 17, +1677231: 92, +1677232: 17, +1677233: 247, +1677234: 16, +1677235: 147, +1677236: 16, +1677237: 46, +1677238: 16, +890807: 69, +890808: 185, +1677241: 8, +1677242: 4, +1677243: 0, +1677244: 0, +890813: 21, +1677246: 35, +1677247: 241, +1677248: 18, +1677249: 14, +890818: 57, +1677251: 41, +1677252: 9, +1677253: 61, +1677254: 46, +1677255: 122, +456487: 13, +1677256: 37, +1677257: 183, +1677258: 24, +1677259: 52, +890828: 223, +1677261: 159, +1677262: 21, +1677263: 60, +1674972: 0, +1674973: 56, +1674974: 255, +1674975: 87, +1674976: 247, +1674977: 66, +1674978: 140, +1674979: 21, +1674980: 165, +1674981: 0, +1674982: 90, +1674983: 79, +1674984: 181, +1674985: 54, +1674986: 16, +1674987: 38, +1674988: 206, +1674989: 29, +1674990: 224, +1674991: 63, +1674992: 224, +1674993: 46, +1674994: 0, +1674995: 34, +1674996: 0, +1674997: 17, +1674998: 91, +1674999: 126, +1675000: 47, +1675001: 85, +1675002: 135, +1675003: 40, +1677269: 46, +890838: 21, +1677271: 222, +1677272: 87, +1677273: 9, +1677274: 4, +890843: 62, +1677276: 0, +1677277: 246, +1677278: 31, +1677279: 243, +1677280: 14, +1677281: 47, +1677282: 14, +1677283: 74, +1677284: 5, +890853: 127, +1677286: 45, +1677287: 90, +890856: 240, +890857: 123, +1677290: 24, +1677291: 54, +1677292: 20, +890861: 127, +890862: 255, +890863: 127, +890864: 254, +890865: 123, +1677298: 20, +1677299: 116, +1677300: 20, +1677301: 47, +1677302: 20, +1677303: 255, +1677304: 79, +1677305: 10, +1677306: 0, +1677307: 0, +890876: 127, +1677309: 248, +1677310: 31, +1677311: 21, +1677312: 15, +1677313: 81, +1150986: 0, +1150987: 56, +1150988: 250, +1677314: 14, +1150990: 176, +1150991: 85, +1150992: 69, +1150993: 40, +1150994: 1, +1150995: 24, +1150996: 16, +1150997: 98, +1150998: 107, +1150999: 73, +1151000: 198, +1151001: 56, +1151002: 99, +1151003: 44, +1151004: 31, +1151005: 36, +1151006: 23, +1151007: 28, +1151008: 47, +1151009: 20, +1151010: 71, +1151011: 12, +1151012: 255, +1151013: 3, +1151014: 55, +1151015: 2, +1151016: 209, +1151017: 0, +1677319: 27, +1677320: 33, +1677321: 121, +1677322: 24, +1677323: 23, +1677324: 20, +1677325: 63, +1677326: 25, +1677327: 252, +1153040: 74, +1677329: 183, +1153042: 17, +1677331: 116, +1677332: 24, +1153045: 82, +1677334: 24, +1677335: 255, +1153048: 62, +1677337: 11, +1677338: 0, +1153051: 198, +1153052: 37, +890909: 0, +1153054: 3, +890911: 0, +1153056: 2, +890913: 0, +890914: 8, +890915: 1, +890916: 189, +890917: 3, +890918: 5, +890919: 20, +890920: 224, +890921: 59, +890922: 168, +454060: 64, +454061: 32, +454062: 20, +454067: 224, +454068: 84, +454069: 33, +454070: 60, +454071: 0, +454072: 16, +454077: 192, +454078: 80, +454079: 33, +454080: 60, +454081: 0, +454082: 16, +454087: 160, +454088: 76, +454089: 0, +454090: 56, +454091: 0, +454092: 12, +454097: 128, +454098: 72, +454099: 0, +454100: 56, +454101: 0, +454102: 12, +1676057: 0, +1676058: 0, +1676059: 233, +1676060: 39, +1676061: 102, +1676062: 26, +1676063: 133, +1676064: 21, +1676065: 163, +1676066: 12, +1676067: 156, +1676068: 63, +1676069: 151, +1676070: 46, +1676071: 114, +1676072: 29, +1676073: 142, +1676074: 16, +1676075: 95, +1676076: 10, +1676077: 219, +1676078: 9, +1676079: 86, +1676080: 9, +1676081: 210, +1676082: 8, +1676083: 44, +1676084: 8, +1676085: 189, +1676086: 127, +1676087: 5, +1676088: 12, +454111: 0, +454112: 8, +454117: 64, +454118: 64, +454119: 0, +454120: 52, +454121: 0, +454122: 8, +454127: 96, +454128: 68, +454129: 0, +454130: 52, +454131: 0, +454132: 8, +454137: 128, +454138: 72, +454139: 0, +454140: 56, +454141: 0, +454142: 12, +889856: 0, +889857: 56, +889858: 8, +889859: 1, +889860: 189, +889861: 3, +889862: 5, +889863: 20, +889864: 224, +889865: 59, +889866: 168, +889867: 33, +889868: 159, +889869: 87, +889870: 210, +889871: 74, +889872: 78, +889873: 58, +889874: 187, +889875: 0, +889876: 181, +889877: 2, +889878: 107, +889879: 1, +889880: 82, +889881: 2, +889882: 4, +889883: 17, +889884: 116, +889885: 0, +889886: 13, +889887: 0, +454150: 56, +454151: 0, +454152: 12, +454157: 192, +454158: 80, +454159: 33, +454160: 60, +454161: 0, +454162: 16, +454167: 224, +454168: 84, +454169: 33, +454170: 60, +454171: 0, +454172: 16, +454177: 1, +454178: 89, +454179: 66, +454180: 64, +454181: 32, +454182: 20, +890144: 0, +890145: 0, +890146: 8, +890147: 1, +890148: 255, +890149: 2, +890150: 5, +890151: 20, +890152: 224, +890153: 59, +890154: 168, +890155: 33, +890156: 159, +890157: 87, +890158: 210, +890159: 74, +890160: 78, +890161: 58, +890162: 187, +890163: 0, +890164: 190, +890165: 1, +890166: 142, +890167: 0, +890168: 82, +890169: 2, +890170: 4, +890171: 17, +890172: 116, +890173: 0, +890174: 13, +890175: 0, +890176: 224, +890177: 20, +890178: 206, +890179: 0, +890180: 31, +890181: 66, +890182: 5, +890183: 20, +890184: 224, +890185: 59, +890186: 168, +890187: 33, +890188: 159, +890189: 87, +890190: 210, +890191: 74, +890192: 78, +890193: 58, +890194: 187, +890195: 0, +890196: 20, +890197: 89, +890198: 170, +890199: 48, +890200: 22, +890201: 2, +890202: 4, +890203: 17, +890204: 116, +890205: 0, +890206: 13, +890207: 0, +890208: 3, +890209: 32, +890210: 206, +890211: 40, +890212: 31, +890213: 110, +890214: 5, +890215: 60, +890216: 224, +890217: 99, +890218: 168, +890219: 73, +890220: 159, +890221: 127, +890222: 210, +890223: 114, +890224: 78, +890225: 98, +890226: 187, +890227: 40, +890228: 20, +890229: 125, +890230: 170, +890231: 88, +890232: 22, +890233: 42, +890234: 4, +890235: 57, +890236: 116, +890237: 40, +890238: 13, +890239: 40, +890240: 3, +890241: 32, +890242: 110, +890243: 81, +890244: 191, +890245: 126, +890246: 165, +890247: 100, +890248: 224, +890249: 127, +890250: 72, +890251: 114, +890252: 255, +890253: 127, +890254: 114, +890255: 127, +890256: 238, +890257: 126, +890258: 91, +890259: 85, +890260: 180, +890261: 125, +890262: 74, +890263: 125, +890264: 182, +890265: 82, +890266: 164, +890267: 97, +890268: 20, +890269: 81, +890270: 173, +890271: 80, +890272: 0, +890273: 0, +890274: 174, +890275: 82, +890276: 255, +890277: 127, +890278: 229, +890279: 101, +890280: 224, +890281: 127, +890282: 136, +890283: 115, +890284: 255, +890285: 127, +890286: 242, +890287: 127, +890288: 238, +890289: 127, +890290: 155, +890291: 86, +890292: 244, +890293: 126, +890294: 138, +890295: 126, +890296: 246, +890297: 83, +890298: 228, +890299: 98, +890300: 84, +890301: 82, +890302: 237, +890303: 81, +890304: 0, +890305: 56, +890306: 206, +890307: 0, +890308: 31, +890309: 66, +890310: 5, +890311: 20, +890312: 224, +890313: 59, +890314: 168, +890315: 33, +890316: 159, +890317: 87, +890318: 210, +890319: 74, +890320: 78, +890321: 58, +890322: 187, +890323: 0, +890324: 20, +890325: 89, +890326: 170, +890327: 48, +890328: 22, +890329: 2, +890330: 4, +890331: 17, +890332: 116, +890333: 0, +890334: 13, +890335: 0, +890336: 0, +890337: 56, +890338: 24, +890339: 22, +890340: 95, +890341: 87, +890342: 79, +890343: 41, +890344: 234, +890345: 79, +890346: 242, +890347: 54, +890348: 255, +890349: 107, +890350: 252, +890351: 95, +890352: 152, +890353: 71, +890354: 255, +890355: 21, +890356: 94, +890357: 110, +890358: 244, +890359: 69, +890360: 95, +890361: 23, +890362: 78, +890363: 38, +890364: 190, +890365: 21, +890366: 87, +890367: 21, +890368: 0, +890369: 56, +890370: 222, +890371: 2, +890372: 255, +890373: 67, +890374: 21, +890375: 22, +890376: 240, +890377: 59, +890378: 184, +890379: 35, +890380: 255, +890381: 87, +890382: 255, +890383: 75, +890384: 254, +890385: 59, +890386: 191, +890387: 2, +890388: 31, +890389: 91, +890390: 186, +890391: 50, +890392: 255, +890393: 3, +890394: 20, +890395: 19, +890396: 127, +890397: 2, +890398: 29, +890399: 2, +890400: 0, +890401: 56, +890402: 255, +890403: 43, +890404: 255, +890405: 107, +890406: 95, +890407: 63, +890408: 250, +890409: 99, +890410: 255, +890411: 75, +890412: 255, +890413: 127, +890414: 255, +890415: 115, +890416: 255, +890417: 99, +890418: 255, +890419: 43, +890420: 255, +890421: 127, +890422: 255, +890423: 91, +890424: 255, +890425: 43, +890426: 254, +890427: 59, +890428: 191, +890429: 43, +890430: 95, +890431: 43, +890432: 0, +890433: 56, +890434: 206, +890435: 0, +890436: 31, +890437: 66, +890438: 5, +890439: 20, +890440: 224, +890441: 59, +890442: 168, +890443: 33, +890444: 159, +890445: 87, +890446: 210, +890447: 74, +890448: 78, +890449: 58, +890450: 187, +890451: 0, +890452: 20, +890453: 89, +890454: 170, +890455: 48, +890456: 22, +890457: 2, +890458: 4, +890459: 17, +1152604: 56, +890461: 0, +890462: 13, +1152607: 24, +890464: 0, +890465: 56, +1152610: 0, +890467: 2, +890468: 95, +1152613: 221, +890470: 69, +890471: 21, +890472: 224, +890473: 67, +890474: 232, +1152619: 47, +890476: 255, +890477: 87, +890478: 242, +890479: 75, +890480: 142, +890481: 59, +1152626: 72, +890483: 1, +890484: 84, +890485: 90, +890486: 234, +890487: 49, +890488: 86, +890489: 3, +890490: 68, +890491: 18, +890492: 180, +890493: 1, +890494: 77, +890495: 1, +890496: 0, +890497: 56, +890498: 78, +890499: 3, +890500: 255, +890501: 67, +890502: 133, +890503: 22, +890504: 224, +890505: 79, +890506: 232, +890507: 35, +890508: 255, +890509: 87, +890510: 242, +890511: 75, +890512: 238, +890513: 59, +890514: 59, +890515: 3, +890516: 148, +890517: 91, +890518: 42, +890519: 51, +890520: 246, +890521: 3, +890522: 132, +890523: 19, +890524: 244, +890525: 2, +890526: 141, +890527: 2, +890528: 0, +890529: 56, +890530: 238, +890531: 43, +890532: 255, +890533: 107, +890534: 197, +890535: 63, +890536: 224, +890537: 99, +890538: 232, +890539: 75, +890540: 255, +890541: 127, +890542: 242, +890543: 115, +890544: 238, +890545: 99, +890546: 251, +890547: 43, +890548: 244, +890549: 127, +890550: 234, +890551: 91, +890552: 246, +890553: 43, +890554: 228, +890555: 59, +890556: 244, +890557: 43, +890558: 237, +890559: 43, +1677083: 0, +1677084: 0, +1677085: 233, +1677086: 39, +1677087: 102, +1677088: 26, +1677089: 133, +1677090: 21, +1677091: 163, +1677092: 12, +1677093: 156, +1677094: 63, +1677095: 151, +1677096: 46, +1677097: 114, +1677098: 29, +1677099: 142, +1677100: 16, +1677101: 95, +1677102: 10, +1677103: 219, +1677104: 9, +1677105: 86, +1677106: 9, +1677107: 210, +1677108: 8, +1677109: 44, +1677110: 8, +1677111: 189, +1677112: 127, +1677113: 5, +1677114: 12, +1677115: 3, +1677116: 32, +1677117: 235, +1677118: 39, +1677119: 136, +1677120: 26, +1677121: 167, +1677122: 21, +1677123: 196, +1677124: 12, +1677125: 92, +1677126: 59, +1677127: 88, +1677128: 46, +1677129: 83, +1677130: 29, +1677131: 111, +1677132: 16, +1677133: 63, +1677134: 14, +1677135: 187, +1677136: 13, +1677137: 54, +1677138: 13, +1677139: 210, +1677140: 12, +1677141: 44, +1677142: 12, +1677143: 189, +1677144: 119, +1677145: 6, +1677146: 12, +1677147: 3, +1677148: 32, +1677149: 237, +1677150: 35, +1677151: 138, +1677152: 22, +1677153: 200, +1677154: 17, +1677155: 229, +1677156: 8, +1677157: 253, +1677158: 58, +1677159: 56, +1677160: 42, +1677161: 52, +1677162: 29, +1677163: 113, +1677164: 16, +1677165: 255, +1677166: 13, +1677167: 155, +1677168: 13, +1677169: 54, +1677170: 13, +1677171: 179, +1677172: 12, +1677173: 45, +1677174: 12, +1677175: 222, +1677176: 111, +1677177: 7, +1677178: 8, +1677179: 0, +1677180: 0, +1677181: 239, +1677182: 35, +1677183: 172, +890752: 0, +890753: 0, +890754: 206, +890755: 0, +890756: 31, +890757: 66, +890758: 5, +890759: 20, +890760: 224, +890761: 59, +890762: 168, +1677195: 82, +890764: 159, +890765: 87, +890766: 210, +890767: 74, +1677200: 17, +890769: 58, +890770: 187, +890771: 0, +890772: 20, +1677205: 45, +890774: 170, +890775: 48, +890776: 22, +890777: 2, +1677210: 8, +890779: 17, +890780: 116, +890781: 0, +1677214: 35, +1677215: 207, +890784: 0, +890785: 0, +890786: 214, +890787: 33, +1677220: 9, +890789: 86, +890790: 170, +890791: 40, +890792: 229, +890793: 79, +890794: 77, +890795: 54, +890796: 191, +890797: 99, +890798: 54, +890799: 91, +890800: 212, +890801: 82, +890802: 92, +890803: 21, +890804: 184, +890805: 101, +890806: 79, +1677239: 222, +1677240: 95, +890809: 22, +890810: 169, +890811: 37, +890812: 24, +1677245: 244, +890814: 178, +890815: 20, +890816: 0, +890817: 0, +1677250: 18, +890819: 46, +890820: 95, +890821: 107, +890822: 112, +890823: 65, +890824: 235, +890825: 103, +890826: 19, +890827: 79, +1677260: 20, +890829: 115, +890830: 155, +890831: 111, +1677264: 21, +1677265: 215, +890834: 30, +1677267: 147, +1677268: 20, +890837: 114, +1677270: 20, +890839: 94, +890840: 92, +890841: 47, +890842: 111, +1677275: 0, +890844: 219, +890845: 45, +890846: 120, +890847: 45, +890848: 0, +890849: 0, +890850: 222, +890851: 66, +890852: 255, +1677285: 222, +890854: 21, +890855: 86, +1677288: 33, +1677289: 152, +890858: 184, +890859: 99, +890860: 255, +1677293: 95, +1677294: 21, +1677295: 28, +1677296: 21, +1677297: 215, +890866: 191, +890867: 66, +890868: 31, +890869: 127, +890870: 186, +890871: 114, +890872: 255, +890873: 67, +890874: 20, +890875: 83, +1677308: 0, +890877: 66, +890878: 29, +890879: 66, +890880: 0, +890881: 56, +890882: 8, +890883: 1, +1677316: 5, +890885: 66, +1677318: 41, +890887: 20, +890888: 224, +890889: 59, +890890: 168, +1153035: 0, +1153036: 56, +1153037: 245, +1153038: 87, +1153039: 239, +890896: 78, +1153041: 130, +890898: 187, +1153043: 192, +1153044: 0, +890901: 89, +1153046: 87, +1153047: 173, +890904: 116, +890905: 2, +1153050: 46, +890907: 17, +890908: 116, +1153053: 253, +890910: 13, +1153055: 213, +890912: 0, +1153057: 15, +1153058: 2, +1153059: 73, +1153060: 1, +1153061: 223, +1153062: 62, +1153063: 24, +1153064: 0, +1153065: 15, +1153066: 0, +890923: 33, +890924: 159, +890925: 87, +890926: 210, +890927: 74, +890928: 78, +890929: 58, +890930: 187, +890931: 0, +890932: 181, +890933: 2, +890934: 107, +890935: 1, +890936: 82, +890937: 2, +890938: 4, +890939: 17, +890940: 116, +890941: 0, +890942: 13, +890943: 0, +890944: 99, +890945: 12, +890946: 74, +890947: 13, +890948: 189, +890949: 15, +890950: 104, +890951: 32, +890952: 227, +890953: 67, +890954: 234, +890955: 41, +890956: 159, +890957: 91, +890958: 243, +890959: 78, +890960: 112, +890961: 66, +890962: 27, +890963: 13, +890964: 214, +890965: 14, +890966: 173, +890967: 13, +890968: 115, +890969: 14, +890970: 71, +890971: 29, +890972: 213, +890973: 12, +890974: 111, +890975: 12, +890976: 231, +890977: 28, +890978: 173, +890979: 29, +890980: 189, +890981: 31, +890982: 235, +890983: 44, +890984: 231, +890985: 75, +890986: 45, +890987: 54, +890988: 159, +890989: 95, +890990: 21, +890991: 87, +890992: 178, +890993: 74, +890994: 124, +890995: 29, +890996: 247, +890997: 30, +890998: 16, +890999: 30, +891000: 181, +891001: 30, +891002: 170, +891003: 41, +891004: 86, +891005: 29, +891006: 241, +891007: 28, +891008: 107, +891009: 45, +891010: 16, +891011: 46, +891012: 189, +891013: 47, +891014: 110, +891015: 57, +891016: 235, +891017: 83, +891018: 112, +891019: 66, +891020: 191, +891021: 99, +891022: 54, +891023: 91, +891024: 212, +891025: 82, +891026: 220, +891027: 45, +891028: 24, +891029: 47, +891030: 82, +891031: 46, +891032: 214, +891033: 46, +891034: 14, +891035: 58, +891036: 184, +891037: 45, +891038: 115, +891039: 45, +891040: 239, +891041: 61, +891042: 115, +891043: 62, +891044: 222, +891045: 63, +891046: 242, +891047: 73, +891048: 239, +891049: 91, +891050: 211, +891051: 78, +891052: 191, +891053: 107, +891054: 88, +891055: 99, +891056: 22, +891057: 91, +891058: 93, +891059: 62, +891060: 90, +891061: 63, +891062: 181, +891063: 62, +891064: 24, +891065: 63, +891066: 113, +891067: 70, +891068: 57, +891069: 62, +891070: 246, +891071: 61, +891072: 115, +891073: 78, +891074: 214, +891075: 78, +891076: 222, +891077: 79, +891078: 117, +891079: 86, +891080: 243, +891081: 99, +891082: 22, +891083: 91, +891084: 191, +891085: 111, +891086: 122, +891087: 107, +891088: 88, +891089: 99, +891090: 189, +891091: 78, +891092: 123, +891093: 79, +891094: 247, +891095: 78, +891096: 90, +891097: 79, +891098: 212, +891099: 82, +891100: 154, +891101: 78, +891102: 120, +891103: 78, +891104: 247, +891105: 94, +891106: 57, +891107: 95, +891108: 222, +891109: 95, +891110: 248, +891111: 98, +891112: 247, +891113: 107, +891114: 89, +891115: 103, +891116: 223, +891117: 115, +891118: 155, +891119: 111, +891120: 122, +891121: 107, +891122: 30, +891123: 95, +891124: 156, +891125: 95, +891126: 90, +891127: 95, +891128: 123, +891129: 95, +891130: 56, +891131: 99, +891132: 28, +891133: 95, +891134: 250, +891135: 94, +891136: 123, +891137: 111, +891138: 156, +891139: 111, +891140: 222, +891141: 111, +891142: 123, +891143: 111, +891144: 251, +891145: 115, +891146: 156, +891147: 115, +891148: 223, +891149: 119, +891150: 189, +891151: 119, +891152: 188, +891153: 115, +891154: 126, +891155: 111, +891156: 189, +891157: 111, +891158: 156, +891159: 111, +891160: 189, +891161: 111, +891162: 155, +891163: 111, +891164: 125, +891165: 111, +891166: 124, +891167: 111, +891168: 0, +891169: 0, +891170: 8, +891171: 1, +891172: 255, +891173: 2, +891174: 5, +891175: 20, +891176: 224, +891177: 59, +891178: 168, +891179: 33, +891180: 159, +891181: 87, +891182: 210, +891183: 74, +891184: 78, +891185: 58, +891186: 187, +891187: 0, +891188: 190, +891189: 1, +891190: 142, +891191: 0, +891192: 82, +891193: 2, +891194: 4, +891195: 17, +891196: 116, +891197: 0, +891198: 13, +891199: 0, +891200: 99, +891201: 12, +891202: 74, +891203: 13, +891204: 31, +891205: 15, +891206: 104, +891207: 32, +891208: 227, +891209: 67, +891210: 234, +891211: 41, +891212: 159, +891213: 91, +891214: 243, +891215: 78, +891216: 112, +891217: 66, +891218: 27, +891219: 13, +891220: 254, +891221: 13, +891222: 240, +891223: 12, +891224: 115, +891225: 14, +891226: 71, +891227: 29, +891228: 213, +891229: 12, +891230: 111, +891231: 12, +891232: 231, +891233: 28, +891234: 173, +891235: 29, +891236: 63, +891237: 31, +891238: 235, +891239: 44, +891240: 231, +891241: 75, +891242: 45, +891243: 54, +891244: 159, +891245: 95, +891246: 21, +891247: 87, +891248: 178, +891249: 74, +891250: 124, +891251: 29, +891252: 62, +891253: 30, +891254: 82, +891255: 29, +891256: 181, +891257: 30, +891258: 170, +891259: 41, +891260: 86, +891261: 29, +891262: 241, +891263: 28, +891264: 107, +891265: 45, +891266: 16, +891267: 46, +891268: 95, +891269: 47, +891270: 110, +891271: 57, +891272: 235, +891273: 83, +891274: 112, +891275: 66, +891276: 191, +891277: 99, +891278: 54, +891279: 91, +891280: 212, +891281: 82, +891282: 220, +891283: 45, +891284: 126, +891285: 46, +891286: 212, +891287: 45, +891288: 214, +891289: 46, +891290: 14, +891291: 58, +891292: 184, +891293: 45, +891294: 115, +891295: 45, +891296: 239, +891297: 61, +891298: 115, +891299: 62, +891300: 127, +891301: 63, +891302: 242, +891303: 73, +891304: 239, +891305: 91, +891306: 211, +891307: 78, +891308: 191, +891309: 107, +891310: 88, +891311: 99, +891312: 22, +891313: 91, +891314: 93, +891315: 62, +891316: 222, +891317: 62, +891318: 54, +891319: 62, +891320: 24, +891321: 63, +891322: 113, +891323: 70, +891324: 57, +891325: 62, +891326: 246, +891327: 61, +891328: 115, +891329: 78, +891330: 214, +891331: 78, +891332: 159, +891333: 79, +891334: 117, +891335: 86, +891336: 243, +891337: 99, +891338: 22, +891339: 91, +891340: 191, +891341: 111, +891342: 122, +891343: 107, +891344: 88, +891345: 99, +891346: 189, +891347: 78, +891348: 30, +891349: 79, +891350: 152, +891351: 78, +891352: 90, +891353: 79, +891354: 212, +891355: 82, +891356: 154, +891357: 78, +891358: 120, +891359: 78, +891360: 247, +891361: 94, +891362: 57, +891363: 95, +891364: 191, +891365: 95, +891366: 248, +891367: 98, +891368: 247, +891369: 107, +891370: 89, +891371: 103, +891372: 223, +891373: 115, +891374: 155, +891375: 111, +891376: 122, +891377: 107, +891378: 30, +891379: 95, +891380: 94, +891381: 95, +891382: 26, +891383: 95, +891384: 123, +891385: 95, +891386: 56, +891387: 99, +891388: 28, +891389: 95, +891390: 250, +891391: 94, +891392: 123, +891393: 111, +891394: 156, +891395: 111, +891396: 223, +891397: 111, +891398: 123, +891399: 111, +891400: 251, +891401: 115, +891402: 156, +891403: 115, +891404: 223, +891405: 119, +891406: 189, +891407: 119, +891408: 188, +891409: 115, +891410: 126, +891411: 111, +891412: 158, +891413: 111, +891414: 124, +891415: 111, +891416: 189, +891417: 111, +891418: 155, +891419: 111, +891420: 125, +891421: 111, +891422: 124, +891423: 111, +891424: 224, +891425: 20, +891426: 206, +891427: 0, +891428: 31, +891429: 66, +891430: 5, +891431: 20, +891432: 224, +891433: 59, +891434: 168, +891435: 33, +891436: 159, +891437: 87, +891438: 210, +891439: 74, +891440: 78, +891441: 58, +891442: 187, +891443: 0, +891444: 20, +891445: 89, +891446: 170, +891447: 48, +891448: 22, +891449: 2, +891450: 4, +891451: 17, +891452: 116, +891453: 0, +891454: 13, +891455: 0, +891456: 67, +891457: 33, +891458: 48, +891459: 13, +891460: 63, +891461: 70, +891462: 104, +891463: 32, +891464: 227, +891465: 67, +891466: 234, +891467: 41, +891468: 159, +891469: 91, +891470: 243, +891471: 78, +891472: 112, +891473: 66, +891474: 27, +891475: 13, +891476: 85, +891477: 93, +891478: 12, +891479: 57, +891480: 55, +891481: 14, +891482: 71, +891483: 29, +891484: 213, +891485: 12, +891486: 111, +891487: 12, +891488: 167, +891489: 45, +891490: 146, +891491: 29, +891492: 127, +891493: 78, +891494: 235, +891495: 44, +891496: 231, +891497: 75, +891498: 45, +891499: 54, +891500: 159, +891501: 95, +891502: 21, +891503: 87, +891504: 178, +891505: 74, +891506: 124, +891507: 29, +891508: 182, +891509: 97, +891510: 111, +891511: 65, +891512: 120, +891513: 30, +891514: 170, +891515: 41, +891516: 86, +891517: 29, +891518: 241, +891519: 28, +891520: 11, +891521: 58, +891522: 244, +891523: 45, +891524: 191, +891525: 86, +891526: 110, +891527: 57, +891528: 235, +891529: 83, +891530: 112, +891531: 66, +891532: 191, +891533: 99, +891534: 54, +891535: 91, +891536: 212, +891537: 82, +891538: 220, +891539: 45, +891540: 24, +891541: 102, +891542: 209, +891543: 77, +891544: 185, +891545: 46, +891546: 14, +891547: 58, +891548: 184, +891549: 45, +891550: 115, +891551: 45, +891552: 111, +891553: 74, +891554: 86, +891555: 62, +891556: 255, +891557: 94, +891558: 242, +891559: 73, +891560: 239, +891561: 91, +891562: 211, +891563: 78, +891564: 191, +891565: 107, +891566: 88, +891567: 99, +891568: 22, +891569: 91, +891570: 93, +891571: 62, +891572: 121, +891573: 106, +891574: 84, +891575: 86, +891576: 250, +891577: 62, +891578: 113, +891579: 70, +891580: 57, +891581: 62, +891582: 246, +891583: 61, +891584: 211, +891585: 86, +891586: 184, +891587: 78, +891588: 63, +891589: 103, +891590: 117, +891591: 86, +891592: 243, +891593: 99, +891594: 22, +891595: 91, +891596: 191, +891597: 111, +891598: 122, +891599: 107, +891600: 88, +891601: 99, +891602: 189, +891603: 78, +891604: 218, +891605: 110, +891606: 183, +891607: 94, +891608: 59, +891609: 79, +891610: 212, +891611: 82, +891612: 154, +891613: 78, +891614: 120, +891615: 78, +891616: 55, +891617: 99, +891618: 26, +891619: 95, +891620: 127, +891621: 111, +891622: 248, +891623: 98, +891624: 247, +891625: 107, +891626: 89, +891627: 103, +891628: 223, +891629: 115, +891630: 155, +891631: 111, +891632: 122, +891633: 107, +891634: 30, +891635: 95, +891636: 60, +891637: 115, +891638: 25, +891639: 107, +891640: 124, +891641: 95, +891642: 56, +891643: 99, +891644: 28, +891645: 95, +891646: 250, +891647: 94, +891648: 155, +891649: 111, +891650: 124, +891651: 111, +891652: 191, +891653: 119, +891654: 123, +891655: 111, +891656: 251, +891657: 115, +891658: 156, +891659: 115, +891660: 223, +891661: 119, +891662: 189, +891663: 119, +891664: 188, +891665: 115, +891666: 126, +891667: 111, +891668: 157, +891669: 119, +891670: 124, +891671: 115, +891672: 189, +891673: 111, +891674: 155, +891675: 111, +891676: 125, +891677: 111, +891678: 124, +891679: 111, +891680: 0, +891681: 0, +891682: 8, +891683: 1, +891684: 189, +891685: 3, +891686: 5, +891687: 20, +891688: 224, +891689: 59, +891690: 168, +891691: 33, +891692: 159, +891693: 87, +891694: 210, +891695: 74, +891696: 78, +891697: 58, +891698: 187, +891699: 0, +891700: 181, +891701: 2, +891702: 107, +891703: 1, +891704: 82, +891705: 2, +891706: 4, +891707: 17, +891708: 116, +891709: 0, +891710: 13, +891711: 0, +891712: 3, +891713: 32, +891714: 206, +891715: 40, +891716: 189, +891717: 55, +891718: 5, +891719: 60, +891720: 224, +891721: 99, +891722: 168, +891723: 73, +891724: 159, +891725: 127, +891726: 210, +891727: 114, +891728: 78, +891729: 98, +891730: 187, +891731: 40, +891732: 181, +891733: 54, +891734: 107, +891735: 53, +891736: 22, +891737: 42, +891738: 4, +891739: 57, +891740: 116, +891741: 40, +891742: 13, +891743: 40, +891744: 3, +891745: 32, +891746: 110, +891747: 81, +891748: 189, +891749: 75, +891750: 165, +891751: 100, +891752: 224, +891753: 127, +891754: 72, +891755: 114, +891756: 255, +891757: 127, +891758: 114, +891759: 127, +891760: 238, +891761: 126, +891762: 91, +891763: 85, +891764: 181, +891765: 74, +891766: 107, +891767: 73, +891768: 182, +891769: 82, +891770: 164, +1153915: 0, +1153916: 56, +1153917: 87, +1153918: 63, +1153919: 77, +891776: 0, +1153921: 226, +1153922: 0, +1153923: 96, +1153924: 0, +1153925: 176, +1153926: 58, +1153927: 11, +1153928: 34, +1153929: 102, +1153930: 17, +1153931: 36, +1153932: 9, +1153933: 90, +1153934: 127, +1153935: 192, +1153936: 126, +1153937: 224, +1153938: 109, +1153939: 224, +1153940: 84, +1153941: 255, +1153942: 3, +1153943: 55, +1153944: 2, +1153945: 209, +1153946: 0, +1153947: 0, +1153948: 56, +1153949: 90, +891806: 237, +1153951: 224, +1153952: 59, +1153953: 128, +1153954: 38, +1153955: 32, +1153956: 9, +1153957: 90, +1153958: 79, +1153959: 181, +1153960: 54, +1153961: 16, +1153962: 38, +1153963: 206, +1153964: 29, +1153965: 148, +1153966: 82, +1153967: 206, +1153968: 57, +1153969: 8, +1153970: 33, +1153971: 132, +1153972: 16, +1153973: 59, +1153974: 3, +1153975: 22, +1153976: 2, +1153977: 19, +1153978: 1, +891835: 17, +891836: 116, +891837: 0, +891838: 13, +891839: 0, +891840: 0, +891841: 0, +891842: 173, +891843: 29, +891844: 189, +891845: 31, +891846: 235, +891847: 44, +891848: 231, +891849: 75, +891850: 45, +891851: 54, +891852: 159, +891853: 95, +891854: 21, +891855: 87, +891856: 178, +891857: 74, +891858: 124, +891859: 29, +891860: 247, +891861: 30, +891862: 16, +891863: 30, +891864: 181, +891865: 30, +891866: 170, +891867: 41, +891868: 86, +891869: 29, +891870: 241, +891871: 28, +891872: 0, +891873: 0, +891874: 115, +891875: 62, +891876: 222, +891877: 63, +891878: 242, +891879: 73, +891880: 239, +891881: 91, +891882: 211, +891883: 78, +891884: 191, +891885: 107, +891886: 88, +891887: 99, +891888: 22, +891889: 91, +891890: 93, +891891: 62, +891892: 90, +891893: 63, +891894: 181, +891895: 62, +891896: 24, +891897: 63, +891898: 113, +891899: 70, +891900: 57, +891901: 62, +891902: 246, +891903: 61, +891904: 0, +891905: 0, +891906: 57, +891907: 95, +891908: 222, +891909: 95, +891910: 248, +891911: 98, +891912: 247, +891913: 107, +891914: 89, +891915: 103, +891916: 223, +891917: 115, +891918: 155, +891919: 111, +891920: 122, +891921: 107, +891922: 30, +891923: 95, +891924: 156, +891925: 95, +891926: 90, +891927: 95, +891928: 123, +891929: 95, +891930: 56, +891931: 99, +891932: 28, +891933: 95, +891934: 250, +891935: 94, +891936: 0, +891937: 56, +891938: 8, +891939: 1, +891940: 189, +891941: 3, +891942: 5, +891943: 20, +891944: 224, +891945: 59, +891946: 168, +891947: 33, +891948: 159, +891949: 87, +891950: 210, +891951: 74, +891952: 78, +891953: 58, +891954: 187, +891955: 0, +891956: 181, +891957: 2, +891958: 107, +891959: 1, +891960: 82, +891961: 2, +891962: 4, +891963: 17, +891964: 116, +891965: 0, +891966: 13, +891967: 0, +891968: 0, +891969: 56, +891970: 173, +891971: 1, +891972: 255, +891973: 3, +891974: 79, +891975: 41, +891976: 234, +891977: 79, +891978: 242, +891979: 54, +891980: 255, +891981: 107, +891982: 252, +891983: 95, +891984: 152, +891985: 71, +891986: 124, +891987: 1, +891988: 24, +891989: 3, +891990: 16, +891991: 2, +891992: 181, +891993: 2, +891994: 78, +891995: 38, +891996: 190, +891997: 21, +891998: 87, +891999: 21, +892000: 0, +892001: 56, +892002: 115, +892003: 2, +892004: 255, +892005: 23, +892006: 21, +892007: 22, +892008: 240, +892009: 59, +892010: 184, +892011: 35, +892012: 255, +892013: 87, +892014: 255, +892015: 75, +892016: 254, +892017: 59, +892018: 93, +892019: 2, +892020: 123, +892021: 3, +892022: 181, +892023: 2, +892024: 24, +892025: 3, +892026: 20, +892027: 19, +892028: 127, +892029: 2, +892030: 29, +892031: 2, +892032: 0, +892033: 56, +892034: 57, +892035: 3, +892036: 255, +892037: 35, +892038: 95, +892039: 63, +892040: 250, +892041: 99, +892042: 255, +892043: 75, +892044: 255, +892045: 127, +892046: 255, +892047: 115, +892048: 255, +892049: 99, +892050: 30, +892051: 3, +892052: 222, +892053: 3, +892054: 90, +892055: 3, +892056: 123, +892057: 3, +892058: 254, +892059: 59, +892060: 191, +892061: 43, +892062: 95, +892063: 43, +892064: 0, +892065: 56, +892066: 8, +892067: 1, +892068: 189, +892069: 3, +892070: 5, +892071: 20, +892072: 224, +892073: 59, +892074: 168, +892075: 33, +892076: 159, +892077: 87, +892078: 210, +892079: 74, +892080: 78, +892081: 58, +892082: 187, +892083: 0, +892084: 181, +892085: 2, +892086: 107, +892087: 1, +892088: 82, +892089: 2, +892090: 4, +892091: 17, +892092: 116, +892093: 0, +892094: 13, +892095: 0, +892096: 0, +892097: 56, +892098: 168, +892099: 1, +892100: 248, +892101: 3, +892102: 69, +892103: 21, +892104: 224, +892105: 67, +892106: 232, +892107: 34, +892108: 255, +892109: 87, +892110: 242, +892111: 75, +892112: 142, +892113: 59, +892114: 251, +892115: 1, +892116: 85, +892117: 3, +892118: 11, +892119: 2, +892120: 86, +892121: 3, +892122: 68, +892123: 18, +892124: 180, +892125: 1, +892126: 77, +892127: 1, +892128: 0, +892129: 56, +892130: 72, +892131: 2, +892132: 243, +892133: 3, +892134: 133, +892135: 22, +892136: 224, +892137: 79, +892138: 232, +892139: 35, +892140: 255, +892141: 87, +892142: 242, +892143: 75, +892144: 238, +892145: 59, +892146: 59, +892147: 3, +892148: 245, +892149: 3, +892150: 171, +892151: 2, +892152: 246, +892153: 3, +892154: 132, +892155: 19, +892156: 244, +892157: 2, +892158: 141, +892159: 2, +892160: 0, +892161: 56, +892162: 232, +892163: 2, +892164: 238, +892165: 3, +892166: 197, +892167: 63, +892168: 224, +892169: 99, +892170: 232, +892171: 75, +892172: 255, +892173: 127, +892174: 242, +892175: 115, +892176: 238, +892177: 99, +892178: 251, +892179: 43, +892180: 240, +892181: 3, +892182: 75, +892183: 3, +892184: 246, +892185: 43, +892186: 228, +892187: 59, +892188: 244, +892189: 43, +892190: 237, +892191: 43, +892192: 0, +892193: 0, +892194: 8, +892195: 1, +892196: 255, +892197: 2, +892198: 5, +892199: 20, +892200: 224, +892201: 59, +892202: 168, +892203: 33, +892204: 159, +892205: 87, +892206: 210, +892207: 74, +892208: 78, +892209: 58, +892210: 187, +892211: 0, +892212: 190, +892213: 1, +892214: 142, +892215: 0, +892216: 82, +892217: 2, +892218: 4, +892219: 17, +892220: 116, +892221: 0, +892222: 13, +892223: 0, +892224: 3, +892225: 32, +892226: 8, +892227: 41, +892228: 255, +892229: 42, +892230: 5, +892231: 60, +892232: 224, +892233: 99, +892234: 168, +892235: 73, +892236: 159, +892237: 127, +892238: 210, +892239: 114, +892240: 78, +892241: 98, +892242: 187, +892243: 40, +892244: 190, +892245: 41, +892246: 142, +892247: 40, +892248: 77, +892249: 42, +892250: 4, +892251: 57, +892252: 116, +892253: 40, +892254: 13, +892255: 40, +892256: 3, +892257: 32, +892258: 8, +892259: 81, +892260: 255, +892261: 82, +892262: 165, +892263: 100, +892264: 224, +892265: 127, +892266: 72, +892267: 114, +892268: 255, +892269: 127, +892270: 114, +892271: 127, +892272: 238, +892273: 126, +892274: 91, +892275: 85, +892276: 190, +892277: 81, +892278: 142, +892279: 80, +892280: 77, +892281: 62, +892282: 164, +892283: 97, +892284: 20, +892285: 81, +892286: 173, +892287: 80, +892288: 0, +892289: 0, +892290: 168, +892291: 121, +892292: 159, +892293: 123, +892294: 229, +892295: 101, +892296: 224, +892297: 127, +892298: 136, +892299: 115, +892300: 255, +892301: 127, +892302: 242, +892303: 127, +892304: 238, +892305: 127, +892306: 155, +892307: 86, +892308: 94, +892309: 118, +892310: 46, +454553: 0, +892312: 77, +892313: 82, +892314: 228, +892315: 98, +892316: 84, +454554: 0, +892318: 237, +892319: 81, +892320: 0, +892321: 0, +892322: 8, +892323: 1, +892324: 255, +892325: 2, +892326: 5, +892327: 20, +892328: 224, +892329: 59, +892330: 168, +892331: 33, +892332: 159, +892333: 87, +892334: 210, +892335: 74, +892336: 78, +892337: 58, +892338: 187, +892339: 0, +892340: 190, +892341: 1, +892342: 142, +892343: 0, +892344: 82, +892345: 2, +892346: 4, +892347: 17, +892348: 116, +892349: 0, +892350: 13, +892351: 0, +892352: 0, +892353: 0, +892354: 173, +892355: 29, +892356: 63, +892357: 31, +892358: 235, +892359: 44, +892360: 231, +892361: 75, +892362: 45, +892363: 54, +892364: 159, +892365: 95, +892366: 21, +892367: 87, +892368: 178, +892369: 74, +892370: 124, +892371: 29, +892372: 62, +892373: 30, +892374: 82, +892375: 29, +892376: 181, +892377: 30, +892378: 170, +892379: 41, +892380: 86, +892381: 29, +892382: 241, +892383: 28, +892384: 0, +892385: 0, +892386: 115, +892387: 62, +892388: 127, +892389: 63, +892390: 242, +892391: 73, +892392: 239, +892393: 91, +892394: 211, +892395: 78, +892396: 191, +892397: 107, +892398: 88, +892399: 99, +892400: 22, +892401: 91, +892402: 93, +892403: 62, +892404: 222, +892405: 62, +892406: 54, +454569: 5, +892408: 24, +892409: 63, +892410: 113, +892411: 70, +892412: 57, +892413: 62, +892414: 246, +892415: 61, +892416: 0, +892417: 0, +892418: 57, +892419: 95, +892420: 191, +892421: 95, +892422: 248, +892423: 98, +892424: 247, +892425: 107, +892426: 89, +892427: 103, +892428: 223, +892429: 115, +892430: 155, +892431: 111, +892432: 122, +892433: 107, +892434: 30, +892435: 95, +892436: 94, +892437: 95, +892438: 26, +892439: 95, +892440: 123, +892441: 95, +892442: 56, +454575: 0, +892444: 28, +892445: 95, +892446: 250, +892447: 94, +892448: 0, +454576: 0, +892450: 8, +892451: 1, +892452: 255, +892453: 2, +892454: 5, +892455: 20, +892456: 224, +892457: 59, +892458: 168, +892459: 33, +892460: 159, +454578: 0, +892462: 210, +892463: 74, +892464: 78, +892465: 58, +892466: 187, +892467: 0, +892468: 190, +892469: 1, +892470: 142, +892471: 0, +892472: 82, +892473: 2, +892474: 4, +892475: 17, +892476: 116, +892477: 0, +892478: 13, +892479: 0, +892480: 0, +892481: 56, +892482: 173, +892483: 1, +892484: 159, +892485: 3, +892486: 79, +892487: 41, +892488: 234, +892489: 79, +892490: 242, +892491: 54, +892492: 255, +892493: 107, +892494: 252, +892495: 95, +892496: 152, +892497: 71, +892498: 255, +892499: 21, +892500: 94, +892501: 2, +892502: 51, +454585: 20, +892504: 247, +892505: 2, +892506: 78, +892507: 38, +892508: 190, +454586: 64, +892510: 87, +892511: 21, +892512: 0, +892513: 56, +892514: 82, +454587: 12, +892516: 255, +892517: 3, +892518: 21, +892519: 22, +892520: 240, +454588: 28, +892522: 184, +892523: 35, +892524: 255, +892525: 87, +892526: 255, +454589: 6, +892528: 254, +892529: 59, +892530: 191, +892531: 2, +892532: 254, +454590: 24, +892534: 216, +892535: 1, +892536: 156, +892537: 3, +892538: 20, +892539: 19, +892540: 127, +892541: 2, +892542: 29, +892543: 2, +892544: 0, +892545: 56, +892546: 247, +892547: 2, +892548: 255, +892549: 23, +892550: 95, +892551: 63, +892552: 250, +892553: 99, +892554: 255, +892555: 75, +892556: 255, +892557: 127, +892558: 255, +892559: 115, +892560: 255, +892561: 99, +892562: 255, +892563: 43, +892564: 158, +892565: 3, +892566: 125, +892567: 2, +892568: 255, +892569: 3, +892570: 254, +892571: 59, +892572: 191, +892573: 43, +892574: 95, +892575: 43, +892576: 0, +892577: 56, +892578: 8, +892579: 1, +892580: 255, +892581: 2, +892582: 5, +892583: 20, +892584: 224, +892585: 59, +892586: 168, +892587: 33, +892588: 159, +892589: 87, +892590: 210, +892591: 74, +892592: 78, +892593: 58, +892594: 187, +892595: 0, +892596: 190, +892597: 1, +892598: 142, +892599: 0, +892600: 82, +892601: 2, +892602: 4, +892603: 17, +892604: 116, +892605: 0, +892606: 13, +892607: 0, +892608: 0, +892609: 56, +892610: 168, +892611: 1, +892612: 159, +892613: 3, +892614: 69, +892615: 21, +892616: 224, +892617: 67, +892618: 232, +892619: 34, +892620: 255, +892621: 87, +892622: 242, +892623: 75, +892624: 142, +892625: 59, +892626: 251, +892627: 1, +892628: 94, +892629: 2, +892630: 46, +892631: 1, +892632: 242, +892633: 2, +892634: 68, +892635: 18, +892636: 180, +892637: 1, +892638: 77, +892639: 1, +892640: 0, +892641: 56, +892642: 72, +892643: 2, +892644: 154, +892645: 3, +892646: 133, +892647: 22, +892648: 224, +892649: 79, +892650: 232, +892651: 35, +892652: 255, +892653: 87, +892654: 242, +892655: 75, +892656: 238, +892657: 59, +892658: 59, +892659: 3, +892660: 254, +892661: 2, +892662: 206, +892663: 1, +892664: 146, +892665: 3, +892666: 132, +892667: 19, +892668: 244, +892669: 2, +892670: 141, +892671: 2, +892672: 0, +892673: 56, +892674: 232, +892675: 2, +892676: 148, +892677: 3, +892678: 197, +892679: 63, +892680: 224, +892681: 99, +892682: 232, +892683: 75, +892684: 255, +892685: 127, +892686: 242, +892687: 115, +892688: 238, +892689: 99, +892690: 251, +892691: 43, +892692: 158, +892693: 3, +892694: 110, +892695: 2, +892696: 242, +892697: 3, +892698: 228, +892699: 59, +892700: 244, +892701: 43, +892702: 237, +892703: 43, +1500569: 12, +892705: 20, +892706: 206, +454619: 0, +892708: 31, +892709: 66, +892710: 5, +892711: 20, +892712: 224, +454620: 0, +892714: 168, +892715: 33, +892716: 159, +892717: 87, +892718: 210, +892719: 74, +892720: 78, +892721: 58, +892722: 187, +892723: 0, +892724: 20, +892725: 89, +892726: 170, +892727: 48, +892728: 22, +892729: 2, +892730: 4, +892731: 17, +892732: 116, +892733: 0, +892734: 13, +892735: 0, +892736: 3, +892737: 32, +892738: 206, +892739: 40, +892740: 31, +892741: 110, +892742: 5, +454625: 22, +892744: 224, +892745: 99, +892746: 168, +892747: 73, +892748: 159, +454626: 72, +892750: 210, +892751: 114, +892752: 78, +892753: 98, +892754: 187, +454627: 14, +892756: 20, +892757: 125, +892758: 170, +892759: 88, +892760: 22, +454628: 36, +892762: 4, +892763: 57, +892764: 116, +892765: 40, +892766: 13, +454629: 8, +892768: 3, +892769: 32, +892770: 110, +892771: 81, +892772: 191, +454630: 32, +892774: 165, +892775: 100, +892776: 224, +892777: 127, +892778: 72, +454631: 6, +892780: 255, +892781: 127, +892782: 114, +892783: 127, +892784: 238, +454632: 24, +892786: 91, +892787: 85, +892788: 180, +892789: 125, +892790: 74, +454633: 4, +892792: 182, +892793: 82, +892794: 164, +892795: 97, +892796: 20, +454634: 16, +892798: 173, +892799: 80, +892800: 0, +892801: 0, +892802: 174, +454635: 2, +892804: 255, +892805: 127, +892806: 229, +892807: 101, +892808: 224, +454636: 8, +892810: 136, +892811: 115, +892812: 255, +892813: 127, +892814: 242, +454637: 1, +892816: 238, +892817: 127, +892818: 155, +892819: 86, +892820: 244, +454638: 4, +892822: 138, +892823: 126, +892824: 246, +892825: 83, +892826: 228, +454639: 0, +892828: 84, +892829: 82, +892830: 237, +892831: 81, +892832: 0, +454640: 0, +892834: 206, +1640238: 0, +892836: 31, +892837: 66, +892838: 5, +892839: 20, +892840: 224, +892841: 59, +892842: 168, +892843: 33, +892844: 159, +892845: 87, +892846: 210, +892847: 74, +892848: 78, +892849: 58, +892850: 187, +892851: 0, +892852: 20, +892853: 89, +892854: 170, +892855: 48, +892856: 22, +892857: 2, +892858: 4, +892859: 17, +892860: 116, +892861: 0, +892862: 13, +892863: 0, +892864: 0, +892865: 0, +892866: 146, +892867: 29, +892868: 127, +892869: 78, +892870: 235, +892871: 44, +892872: 231, +892873: 75, +892874: 45, +892875: 54, +892876: 159, +892877: 95, +892878: 21, +892879: 87, +892880: 178, +892881: 74, +892882: 124, +892883: 29, +892884: 182, +892885: 97, +892886: 111, +892887: 65, +892888: 120, +892889: 30, +892890: 170, +892891: 41, +892892: 86, +892893: 29, +892894: 241, +892895: 28, +892896: 0, +892897: 0, +892898: 86, +892899: 62, +892900: 255, +892901: 94, +892902: 242, +892903: 73, +892904: 239, +892905: 91, +892906: 211, +892907: 78, +892908: 191, +892909: 107, +892910: 88, +454653: 191, +892912: 22, +892913: 91, +892914: 93, +892915: 62, +892916: 121, +454654: 2, +892918: 84, +892919: 86, +892920: 250, +892921: 62, +892922: 113, +454655: 127, +892924: 57, +892925: 62, +892926: 246, +892927: 61, +892928: 0, +454656: 1, +892930: 26, +892931: 95, +892932: 127, +892933: 111, +892934: 248, +454657: 21, +892936: 247, +892937: 107, +892938: 89, +892939: 103, +892940: 223, +454658: 0, +892942: 155, +892943: 111, +892944: 122, +892945: 107, +892946: 30, +892947: 95, +892948: 60, +892949: 115, +892950: 25, +892951: 107, +892952: 124, +892953: 95, +892954: 56, +892955: 99, +892956: 28, +892957: 95, +892958: 250, +892959: 94, +892960: 0, +892961: 56, +892962: 206, +892963: 0, +892964: 31, +892965: 66, +892966: 5, +892967: 20, +892968: 224, +892969: 59, +892970: 168, +892971: 33, +892972: 159, +892973: 87, +892974: 210, +892975: 74, +892976: 78, +892977: 58, +892978: 187, +892979: 0, +892980: 20, +892981: 89, +892982: 170, +892983: 48, +892984: 22, +892985: 2, +892986: 4, +892987: 17, +892988: 116, +892989: 0, +892990: 13, +892991: 0, +892992: 0, +892993: 56, +892994: 24, +454667: 59, +892996: 95, +892997: 87, +892998: 79, +892999: 41, +893000: 234, +454668: 2, +893002: 242, +893003: 54, +893004: 255, +893005: 107, +893006: 252, +454669: 251, +893008: 152, +1155153: 0, +1155154: 56, +1155155: 87, +1155156: 63, +1155157: 77, +1155158: 46, +1155159: 226, +1155160: 0, +1155161: 96, +1155162: 0, +1155163: 176, +1155164: 58, +1155165: 11, +1155166: 34, +1155167: 102, +1155168: 17, +1155169: 36, +1155170: 9, +1155171: 90, +1155172: 67, +1155173: 148, +1155174: 54, +1155175: 173, +1155176: 21, +1155177: 8, +1155178: 5, +1155179: 255, +1155180: 3, +1155181: 55, +1155182: 2, +1155183: 209, +1155184: 0, +893041: 59, +893042: 191, +893043: 2, +893044: 31, +893045: 91, +893046: 186, +893047: 50, +893048: 255, +893049: 3, +893050: 20, +893051: 19, +893052: 127, +893053: 2, +893054: 29, +893055: 2, +893056: 0, +893057: 56, +893058: 255, +893059: 43, +893060: 255, +893061: 107, +893062: 95, +893063: 63, +893064: 250, +893065: 99, +893066: 255, +893067: 75, +893068: 255, +893069: 127, +893070: 255, +893071: 115, +893072: 255, +893073: 99, +893074: 255, +893075: 43, +893076: 255, +893077: 127, +893078: 255, +454681: 216, +893080: 255, +893081: 43, +893082: 254, +893083: 59, +893084: 191, +454682: 1, +893086: 95, +893087: 43, +893088: 0, +893089: 56, +893090: 206, +454683: 152, +893092: 31, +893093: 66, +893094: 5, +893095: 20, +893096: 224, +454684: 0, +893098: 168, +893099: 33, +893100: 159, +893101: 87, +893102: 210, +454685: 14, +893104: 78, +893105: 58, +893106: 187, +893107: 0, +893108: 20, +454686: 0, +893110: 170, +893111: 48, +893112: 22, +893113: 2, +893114: 4, +893115: 17, +893116: 116, +893117: 0, +893118: 13, +893119: 0, +893120: 0, +893121: 56, +893122: 14, +893123: 2, +893124: 95, +893125: 67, +893126: 69, +893127: 21, +893128: 224, +893129: 67, +893130: 232, +893131: 34, +893132: 255, +893133: 87, +893134: 242, +893135: 75, +893136: 142, +893137: 59, +893138: 251, +893139: 1, +893140: 84, +893141: 90, +893142: 234, +893143: 49, +893144: 86, +893145: 3, +893146: 68, +893147: 18, +893148: 180, +893149: 1, +893150: 77, +893151: 1, +893152: 0, +893153: 56, +893154: 78, +893155: 3, +893156: 255, +893157: 67, +893158: 133, +893159: 22, +893160: 224, +893161: 79, +893162: 232, +454695: 84, +893164: 255, +893165: 87, +893166: 242, +893167: 75, +893168: 238, +454696: 1, +893170: 59, +893171: 3, +893172: 148, +893173: 91, +893174: 42, +454697: 85, +893176: 246, +893177: 3, +893178: 132, +893179: 19, +893180: 244, +454698: 0, +893182: 141, +893183: 2, +893184: 0, +893185: 56, +893186: 238, +454699: 11, +893188: 255, +893189: 107, +893190: 197, +893191: 63, +893192: 224, +454700: 0, +893194: 232, +893195: 75, +893196: 255, +893197: 127, +893198: 242, +893199: 115, +893200: 238, +893201: 99, +893202: 251, +893203: 43, +893204: 244, +893205: 127, +893206: 234, +893207: 91, +893208: 246, +893209: 43, +893210: 228, +893211: 59, +893212: 244, +893213: 43, +893214: 237, +893215: 43, +454709: 208, +454710: 0, +454711: 16, +454712: 0, +454713: 7, +454714: 0, +454723: 170, +454724: 0, +454725: 11, +454726: 0, +454727: 4, +454728: 0, +454740: 208, +454741: 0, +454742: 16, +454743: 0, +454744: 7, +454745: 0, +1503330: 33, +454755: 1, +454756: 85, +454757: 0, +1503334: 24, +454759: 0, +454768: 216, +454769: 1, +454770: 152, +454771: 0, +454772: 14, +454773: 0, +454782: 59, +454783: 2, +454784: 251, +454785: 0, +454786: 17, +454787: 0, +1500615: 64, +1500616: 168, +1500617: 56, +1500618: 100, +891771: 97, +891772: 20, +891773: 81, +891774: 173, +891775: 80, +1500619: 48, +1153920: 46, +891777: 0, +891778: 174, +891779: 82, +891780: 189, +1500620: 181, +891781: 95, +891782: 229, +891783: 101, +891784: 224, +891785: 127, +1500621: 126, +891786: 136, +891787: 115, +891788: 255, +891789: 127, +891790: 242, +1500622: 0, +891791: 127, +891792: 238, +891793: 127, +891794: 155, +891795: 86, +1500623: 40, +891796: 85, +891797: 95, +891798: 11, +891799: 94, +891800: 246, +1500624: 75, +891801: 83, +891802: 228, +891803: 98, +891804: 84, +891805: 82, +1500625: 24, +1153950: 127, +891807: 81, +891808: 0, +891809: 0, +891810: 8, +1500626: 72, +891811: 1, +891812: 189, +891813: 3, +891814: 5, +891815: 20, +1500627: 24, +891816: 224, +891817: 59, +891818: 168, +891819: 33, +891820: 159, +1500628: 37, +891821: 87, +891822: 210, +891823: 74, +891824: 78, +891825: 58, +1500629: 20, +891826: 187, +891827: 0, +891828: 181, +891829: 2, +891830: 107, +1500630: 34, +891831: 1, +891832: 82, +891833: 2, +891834: 4, +1500637: 36, +1156901: 0, +1156902: 56, +1156903: 183, +1156904: 63, +1156905: 173, +1156906: 46, +1156907: 66, +1156908: 1, +1156909: 96, +1156910: 0, +1156911: 16, +1156912: 59, +1156913: 107, +1156914: 34, +1156915: 198, +1156916: 17, +1156917: 132, +1156918: 9, +1156919: 255, +1156920: 114, +1156921: 223, +1156922: 44, +1156923: 185, +1156924: 36, +1156925: 175, +1156926: 28, +1156927: 157, +1156928: 42, +1156929: 214, +1156930: 25, +1156931: 16, +1156932: 13, +1500639: 28, +1500640: 235, +1500641: 40, +1500642: 169, +1500643: 36, +1503569: 159, +1500644: 135, +1500645: 32, +1500646: 100, +1500647: 28, +1500648: 66, +1500649: 24, +1503599: 223, +1500650: 107, +1500651: 65, +1500652: 0, +1500653: 20, +1500654: 154, +1500655: 44, +1503629: 31, +1500656: 116, +1500657: 44, +1500658: 78, +1500659: 40, +1500660: 40, +1500661: 40, +1503659: 63, +1500662: 54, +1500663: 98, +1500664: 178, +1157704: 0, +1157705: 56, +1157706: 255, +1157707: 87, +1157708: 247, +1157709: 66, +1157710: 74, +1157711: 13, +1157712: 99, +1157713: 0, +1157714: 90, +1157715: 79, +1157716: 181, +1157717: 54, +1157718: 16, +1157719: 38, +1157720: 206, +1157721: 29, +1157722: 218, +1157723: 78, +1157724: 53, +1157725: 58, +1157726: 144, +1157727: 41, +1157728: 235, +1157729: 20, +1157730: 224, +1500665: 85, +1157732: 160, +1157733: 50, +1157734: 64, +1157735: 25, +1500667: 73, +1288880: 246, +1288881: 13, +1288882: 146, +1288883: 13, +1288884: 236, +1288885: 12, +1288886: 169, +1288887: 12, +453185: 87, +1500670: 186, +453186: 212, +1500671: 77, +453187: 74, +1500672: 86, +453188: 80, +1500673: 69, +1503719: 191, +453189: 58, +1500674: 18, +453190: 188, +1500675: 65, +1500676: 205, +1500677: 56, +1500678: 105, +1500679: 48, +1503749: 255, +1500680: 186, +1500681: 126, +1500682: 5, +1500683: 40, +1500684: 77, +1500685: 24, +1500686: 74, +1500687: 24, +1500688: 39, +1500689: 20, +1500690: 36, +1500691: 20, +1500694: 233, +1500696: 167, +1500697: 36, +1500698: 69, +1500699: 28, +1500700: 237, +1500701: 40, +1159045: 0, +1159046: 56, +1159047: 250, +1159048: 114, +1159049: 176, +1159050: 85, +1159051: 69, +1159052: 40, +1159053: 1, +1159054: 24, +1159055: 16, +1159056: 98, +1159057: 107, +1159058: 73, +1159059: 198, +1159060: 56, +1159061: 99, +1159062: 44, +1159063: 31, +1159064: 36, +1159065: 23, +1159066: 28, +1159067: 47, +1159068: 20, +1159069: 71, +1159070: 12, +1159071: 224, +1159072: 3, +1159073: 160, +1159074: 2, +1159075: 64, +1159076: 1, +1159301: 0, +1159302: 56, +1159303: 255, +1159304: 87, +1159305: 247, +1159306: 66, +1159307: 74, +1159308: 13, +1159309: 99, +1159310: 0, +1159311: 90, +1159312: 79, +1159313: 181, +1159314: 54, +1159315: 16, +1159316: 38, +1159317: 206, +1159318: 29, +1159319: 218, +1159320: 78, +1159321: 53, +1159322: 58, +1159323: 144, +1159324: 41, +1159325: 235, +1159326: 20, +1159327: 224, +1159328: 95, +1159329: 160, +1159330: 50, +1159331: 64, +1159332: 25, +1159333: 0, +1159334: 56, +1159335: 255, +1159336: 127, +1159337: 224, +1159338: 86, +1159339: 128, +1159340: 49, +1159341: 192, +1159342: 24, +1159343: 192, +1159344: 107, +1159345: 192, +1159346: 94, +1159347: 32, +1159348: 74, +1159349: 160, +1159350: 53, +1159351: 255, +1159352: 127, +1159353: 156, +1159354: 3, +1159355: 55, +1159356: 2, +1159357: 209, +1159358: 0, +1159359: 255, +1159360: 3, +1159361: 55, +1159362: 2, +1159363: 209, +1159364: 0, +892311: 117, +892317: 82, +1159703: 0, +1159704: 56, +1159705: 250, +1159706: 114, +1159707: 176, +1159708: 85, +1159709: 69, +1159710: 40, +1159711: 1, +1159712: 24, +1159713: 16, +1159714: 98, +1159715: 107, +1159716: 73, +1159717: 198, +1159718: 56, +1159719: 99, +1159720: 44, +1159721: 31, +1159722: 36, +1159723: 23, +1159724: 28, +1159725: 47, +1159726: 20, +1159727: 71, +1159728: 12, +1159729: 224, +1159730: 3, +1159731: 160, +1159732: 2, +1159733: 64, +1159734: 1, +1500740: 191, +1160097: 0, +1160098: 56, +1160099: 250, +1160100: 114, +1160101: 176, +1160102: 85, +1160103: 69, +1160104: 40, +1160105: 1, +1160106: 24, +1160107: 16, +1160108: 98, +1160109: 107, +1160110: 73, +1160111: 198, +1160112: 56, +1160113: 99, +1160114: 44, +1160115: 121, +1160116: 29, +1160117: 51, +1160118: 25, +1160119: 14, +1160120: 21, +1160121: 200, +1160122: 16, +1160123: 224, +1160124: 59, +1160125: 128, +1160126: 38, +1160127: 128, +1160128: 21, +892407: 62, +1291251: 0, +1291252: 0, +1291253: 157, +1291254: 85, +1291255: 22, +1291256: 24, +1291257: 13, +1291258: 16, +1291259: 122, +1291260: 18, +1291261: 23, +1291262: 14, +1291263: 211, +1291264: 13, +1291265: 112, +1291266: 9, +1291267: 45, +1291268: 9, +1291269: 202, +1291270: 4, +1291271: 134, +1291272: 4, +1291273: 35, +1291274: 0, +1291275: 67, +1291276: 4, +1291277: 82, +1291278: 62, +1291279: 213, +1291280: 74, +1291281: 0, +1291282: 0, +1291283: 0, +1291284: 56, +1291285: 157, +1291286: 85, +1291287: 22, +1291288: 24, +1291289: 13, +1291290: 16, +1291291: 155, +1291292: 26, +1291293: 55, +1291294: 22, +1291295: 243, +1291296: 17, +1291297: 143, +1291298: 13, +1291299: 76, +1291300: 13, +1291301: 233, +1291302: 8, +1291303: 166, +1291304: 4, +1291305: 67, +1291306: 0, +1291307: 67, +1291308: 4, +1291309: 115, +1291310: 66, +1291311: 246, +1291312: 82, +1291313: 0, +1291314: 0, +1291315: 0, +1291316: 56, +1291317: 157, +1291318: 85, +1291319: 22, +1291320: 24, +1291321: 13, +1291322: 16, +1291323: 219, +1291324: 34, +1291325: 119, +1291326: 26, +1291327: 18, +1291328: 26, +1291329: 174, +1291330: 21, +1291331: 108, +1291332: 17, +1291333: 9, +1291334: 13, +1291335: 166, +1291336: 8, +1291337: 68, +1291338: 4, +1291339: 35, +1291340: 4, +1291341: 148, +1291342: 74, +1291343: 56, +1291344: 91, +1291345: 0, +1291346: 0, +1291347: 0, +1291348: 56, +1291349: 157, +1291350: 85, +1291351: 22, +1291352: 24, +1291353: 13, +1291354: 16, +1291355: 252, +1291356: 42, +1291357: 151, +1291358: 34, +1291359: 50, +1291360: 30, +1291361: 205, +1291362: 25, +1291363: 139, +1291364: 21, +1291365: 40, +1291366: 17, +1291367: 198, +1291368: 8, +1291369: 100, +1291370: 4, +1291371: 35, +1291372: 4, +1291373: 181, +1291374: 78, +1291375: 89, +1291376: 99, +1291377: 0, +1291378: 0, +1291379: 0, +1291380: 56, +1291381: 157, +1291382: 85, +1291383: 22, +1291384: 24, +1291385: 13, +1291386: 16, +1291387: 29, +1291388: 51, +1291389: 183, +1291390: 42, +1291391: 145, +1291392: 42, +1291393: 12, +1291394: 30, +1291395: 170, +1291396: 25, +1291397: 72, +1291398: 17, +1291399: 229, +1291400: 12, +1291401: 132, +1291402: 4, +1291403: 35, +1291404: 0, +1291405: 181, +1291406: 82, +1291407: 123, +1291408: 103, +1291409: 0, +1291410: 0, +1291411: 0, +1291412: 56, +1291413: 157, +1291414: 85, +1291415: 22, +1291416: 24, +1291417: 13, +1291418: 16, +1291419: 62, +1291420: 59, +1291421: 215, +1291422: 50, +1291423: 176, +1291424: 50, +1291425: 43, +1291426: 34, +1291427: 201, +1291428: 29, +1291429: 103, +1291430: 21, +1291431: 5, +1291432: 13, +1291433: 164, +1291434: 4, +1291435: 35, +1291436: 0, +1291437: 214, +1291438: 86, +1291439: 156, +1291440: 111, +1291441: 0, +1291442: 0, +1291443: 0, +1291444: 56, +1291445: 157, +1291446: 85, +1291447: 22, +1291448: 24, +1291449: 13, +1291450: 16, +1291451: 126, +1291452: 67, +1291453: 23, +1291454: 55, +1291455: 176, +1291456: 50, +1291457: 74, +1291458: 42, +1291459: 233, +1291460: 33, +1291461: 135, +1291462: 25, +1291463: 5, +1291464: 17, +1291465: 165, +1291466: 8, +1291467: 3, +1291468: 0, +1291469: 247, +1291470: 94, +1291471: 222, +1291472: 119, +1291473: 0, +1291474: 0, +1291475: 0, +1291476: 56, +1291477: 157, +1291478: 85, +1291479: 22, +1291480: 24, +1291481: 13, +1291482: 16, +1291483: 159, +1291484: 75, +1291485: 55, +1291486: 63, +1291487: 208, +1291488: 54, +1291489: 105, +1291490: 46, +1291491: 8, +1291492: 38, +1291493: 166, +1291494: 29, +1291495: 37, +1291496: 17, +1291497: 197, +1291498: 8, +1291499: 3, +1291500: 0, +1291501: 24, +1291502: 99, +1291503: 255, +1291504: 127, +1291505: 0, +1291506: 0, +1291507: 0, +1291508: 56, +1291509: 7, +1291510: 8, +1291511: 4, +1291512: 4, +1291513: 0, +1291514: 0, +1291515: 122, +1291516: 18, +1291517: 23, +1291518: 14, +1291519: 211, +1291520: 13, +1291521: 112, +1291522: 9, +1291523: 45, +1291524: 9, +1291525: 202, +1291526: 4, +1291527: 134, +1291528: 4, +1291529: 35, +1291530: 0, +1291531: 67, +1291532: 4, +1291533: 82, +1291534: 62, +1291535: 213, +1291536: 74, +1291537: 0, +1291538: 0, +1291571: 0, +1291572: 0, +1291573: 157, +1291574: 85, +1291575: 22, +1291576: 24, +1291577: 13, +1291578: 16, +1291579: 122, +1291580: 18, +1291581: 23, +1291582: 14, +1291583: 211, +1291584: 13, +1291585: 112, +1291586: 9, +1291587: 45, +1291588: 9, +1291589: 202, +1291590: 4, +1291591: 134, +1291592: 4, +1291593: 35, +1291594: 0, +1291595: 67, +1291596: 4, +1291597: 82, +1291598: 62, +1291599: 213, +1291600: 74, +1291601: 0, +1291602: 0, +1291603: 0, +1291604: 56, +1291605: 157, +1291606: 85, +1291607: 22, +1291608: 24, +1291609: 13, +1291610: 16, +1291611: 155, +1291612: 26, +1291613: 55, +1291614: 22, +1291615: 243, +1291616: 17, +1291617: 143, +1291618: 13, +1291619: 76, +1291620: 13, +1291621: 233, +1291622: 8, +1291623: 166, +1291624: 4, +1291625: 67, +1291626: 0, +1291627: 67, +1291628: 4, +1291629: 115, +1291630: 66, +1291631: 246, +1291632: 82, +1291633: 0, +1291634: 0, +1291635: 0, +1291636: 56, +1291637: 157, +1291638: 85, +1291639: 22, +1291640: 24, +1291641: 13, +1291642: 16, +1291643: 219, +1291644: 34, +1291645: 119, +1291646: 26, +1291647: 18, +1291648: 26, +1291649: 174, +1291650: 21, +1291651: 108, +1291652: 17, +1291653: 9, +1291654: 13, +1291655: 166, +1291656: 8, +1291657: 68, +1291658: 4, +1291659: 35, +1291660: 4, +1291661: 148, +1291662: 74, +1291663: 56, +1291664: 91, +1291665: 0, +1291666: 0, +1291667: 0, +1291668: 56, +1291669: 157, +1291670: 85, +1291671: 22, +1291672: 24, +1291673: 13, +1291674: 16, +1291675: 252, +1291676: 42, +1291677: 151, +1291678: 34, +1291679: 50, +1291680: 30, +1291681: 205, +1291682: 25, +1291683: 139, +1291684: 21, +1291685: 40, +1291686: 17, +1291687: 198, +1291688: 8, +1291689: 100, +1291690: 4, +1291691: 35, +1291692: 4, +1291693: 181, +1291694: 78, +1291695: 89, +1291696: 99, +1291697: 0, +1291698: 0, +1160627: 0, +1160628: 56, +1160629: 255, +1160630: 127, +1160631: 224, +1160632: 86, +1160633: 128, +1160634: 49, +1160635: 192, +1160636: 24, +1160637: 192, +1160638: 107, +1160639: 192, +1160640: 94, +1160641: 32, +1160642: 74, +1160643: 160, +1160644: 53, +1160645: 31, +1291718: 17, +1160647: 23, +1160648: 28, +1160649: 47, +1291722: 4, +1291723: 35, +1160652: 12, +1291725: 181, +1291726: 82, +1291727: 123, +1291728: 103, +1160657: 64, +1291730: 0, +1291731: 0, +1291732: 56, +1291733: 157, +1291734: 85, +1291735: 22, +1291736: 24, +1291737: 13, +1291738: 16, +1291739: 62, +1291740: 59, +1291741: 215, +1291742: 50, +1291743: 176, +1291744: 50, +1291745: 43, +1291746: 34, +1291747: 201, +1291748: 29, +1291749: 103, +1291750: 21, +1291751: 5, +1291752: 13, +1291753: 164, +1291754: 4, +1291755: 35, +1291756: 0, +1291757: 214, +1291758: 86, +1291759: 156, +1291760: 111, +1291761: 0, +1291762: 0, +1291763: 0, +1291764: 56, +1291765: 157, +1291766: 85, +1291767: 22, +1291768: 24, +1291769: 13, +1291770: 16, +1291771: 126, +1291772: 67, +1291773: 23, +1291774: 55, +1291775: 176, +1291776: 50, +1291777: 74, +1291778: 42, +1291779: 233, +1291780: 33, +1291781: 135, +1291782: 25, +1291783: 5, +1291784: 17, +1291785: 165, +1291786: 8, +1291787: 3, +1291788: 0, +1291789: 247, +1291790: 94, +1291791: 222, +1291792: 119, +1291793: 0, +1291794: 0, +1291795: 0, +1291796: 56, +1291797: 157, +1291798: 85, +1291799: 22, +1291800: 24, +1291801: 13, +1291802: 16, +1291803: 159, +1291804: 75, +1291805: 55, +1291806: 63, +1291807: 208, +1291808: 54, +1291809: 105, +1291810: 46, +1291811: 8, +1291812: 38, +1291813: 166, +1291814: 29, +1291815: 37, +1291816: 17, +1291817: 197, +1291818: 8, +1291819: 3, +1291820: 0, +1291821: 24, +1291822: 99, +1291823: 255, +1291824: 127, +1291825: 0, +1291826: 0, +892515: 2, +892521: 59, +892527: 75, +892533: 2, +892704: 224, +892707: 0, +892713: 59, +892743: 60, +892749: 127, +892755: 40, +892761: 42, +892767: 40, +892773: 126, +892779: 114, +892785: 126, +892791: 125, +892797: 81, +892803: 82, +892809: 127, +892815: 127, +892821: 126, +892827: 98, +892833: 0, +892835: 0, +892911: 99, +892917: 106, +892923: 70, +892929: 0, +892935: 98, +892941: 115, +892995: 22, +893001: 79, +893007: 95, +893009: 71, +893010: 255, +893011: 21, +893012: 94, +893013: 110, +893014: 244, +893015: 69, +893016: 95, +893017: 23, +893018: 78, +893019: 38, +893020: 190, +893021: 21, +893022: 87, +893023: 21, +893024: 0, +893025: 56, +893026: 222, +893027: 2, +893028: 255, +893029: 67, +893030: 21, +893031: 22, +893032: 240, +893033: 59, +893034: 184, +893035: 35, +893036: 255, +893037: 87, +893038: 255, +893039: 75, +893040: 254, +893079: 91, +893085: 43, +893091: 0, +893097: 59, +893103: 74, +893109: 89, +893163: 35, +893169: 59, +893175: 51, +893181: 2, +893187: 43, +893193: 99, +1164862: 0, +1164863: 56, +1164864: 87, +1164865: 63, +1164866: 77, +1164867: 46, +1164868: 226, +1164869: 0, +1164870: 96, +1164871: 0, +1164872: 176, +1164873: 58, +1164874: 11, +1164875: 34, +1164876: 102, +1164877: 17, +1164878: 36, +1164879: 9, +1164880: 255, +1164881: 127, +1164882: 156, +1164883: 3, +1164884: 55, +1164885: 2, +1164886: 209, +1164887: 0, +1164888: 191, +1164889: 97, +1164890: 182, +1164891: 56, +1164892: 72, +1164893: 20, +902791: 145, +902792: 127, +1500906: 26, +1500908: 47, +1500910: 9, +1165478: 0, +1165479: 56, +1165480: 255, +1165481: 87, +1165482: 247, +1165483: 66, +1165484: 140, +1165485: 21, +1165486: 165, +1165487: 0, +1165488: 90, +1165489: 79, +1165490: 181, +1165491: 54, +1165492: 16, +1165493: 38, +1165494: 206, +1165495: 29, +1165496: 255, +1165497: 119, +1165498: 181, +1165499: 98, +1165500: 74, +1165501: 49, +1165502: 99, +1165503: 16, +1165504: 31, +1165505: 77, +1165506: 182, +1165507: 56, +1165508: 110, +1165509: 36, +456401: 66, +456410: 13, +456411: 0, +456412: 6, +456413: 0, +456414: 0, +456415: 44, +456416: 0, +456417: 16, +456418: 132, +456419: 16, +456420: 19, +456421: 0, +456422: 115, +456423: 78, +1373936: 0, +1373937: 21, +456434: 9, +1373939: 21, +456436: 0, +456437: 56, +456438: 0, +456439: 28, +456440: 132, +456441: 16, +456442: 86, +456443: 0, +456444: 214, +456445: 90, +1296897: 0, +1296898: 56, +1296899: 255, +1296900: 127, +1296901: 192, +1296902: 126, +1296903: 224, +1296904: 109, +1296905: 224, +1296906: 84, +1296907: 0, +1296908: 0, +1296909: 0, +1296910: 0, +1296911: 0, +1296912: 0, +1296913: 0, +1296914: 0, +1296915: 0, +1296916: 0, +1296917: 0, +1296918: 0, +1296919: 0, +1296920: 0, +1296921: 0, +1296922: 0, +1296923: 0, +1296924: 0, +1296925: 0, +1296926: 0, +1296927: 0, +1296928: 0, +456454: 19, +456455: 0, +456456: 12, +456457: 0, +456458: 0, +456459: 68, +456460: 0, +456461: 40, +456462: 132, +456463: 16, +1296993: 0, +1296994: 0, +1296995: 223, +1296996: 2, +1296997: 215, +1296998: 1, +1296999: 172, +1297000: 0, +1297001: 187, +1297002: 94, +1297003: 179, +1297004: 61, +1297005: 46, +1297006: 41, +1297007: 134, +1297008: 20, +1297009: 177, +1297010: 11, +1297011: 251, +1297012: 72, +1297013: 255, +1297014: 127, +1297015: 0, +1297016: 0, +1297017: 255, +1297018: 127, +1297019: 229, +1297020: 68, +1297021: 255, +1297022: 127, +1297023: 0, +1297024: 0, +1297025: 3, +1297026: 32, +1297027: 177, +1297028: 11, +1297029: 169, +1297030: 30, +1297031: 69, +1297032: 1, +1297033: 187, +1297034: 94, +1297035: 179, +1297036: 61, +1297037: 46, +1297038: 41, +1297039: 134, +1297040: 20, +1297041: 24, +1297042: 99, +1297043: 231, +1297044: 28, +1297045: 132, +1297046: 16, +1297047: 0, +1297048: 0, +1297049: 255, +1297050: 127, +1297051: 223, +1297052: 2, +1297053: 31, +1297054: 0, +1297055: 0, +1297056: 0, +1297057: 3, +1297058: 32, +1297059: 188, +1297060: 114, +1297061: 251, +1297062: 72, +1297063: 22, +1297064: 24, +1297065: 24, +1297066: 99, +1297067: 24, +1297068: 99, +1297069: 24, +1297070: 99, +1297071: 24, +1297072: 99, +1297073: 24, +1297074: 99, +1297075: 24, +1297076: 99, +1297077: 24, +1297078: 99, +1297079: 24, +1297080: 99, +1297081: 24, +1297082: 99, +1297083: 24, +1297084: 99, +1297085: 255, +1297086: 127, +1297087: 0, +1297088: 0, +1297089: 0, +1297090: 0, +1297091: 178, +1297092: 114, +1297093: 199, +1297094: 113, +1297095: 3, +1297096: 77, +1297097: 24, +1297098: 99, +1297099: 24, +1297100: 99, +1297101: 24, +1297102: 99, +1297103: 24, +1297104: 99, +1297105: 24, +1297106: 99, +1297107: 24, +1297108: 99, +1297109: 24, +1297110: 99, +1297111: 24, +1297112: 99, +1297113: 24, +1297114: 99, +1297115: 24, +1297116: 99, +1297117: 255, +1297118: 127, +1297119: 0, +1297120: 0, +1297121: 0, +1297122: 0, +1297123: 244, +1297124: 62, +1297125: 46, +1297126: 38, +1297127: 104, +1297128: 13, +1297129: 148, +1297130: 62, +1297131: 206, +1297132: 37, +1297133: 8, +1297134: 13, +1297135: 132, +1297136: 20, +1297137: 148, +1297138: 110, +1297139: 206, +1297140: 85, +1297141: 8, +1297142: 61, +1297143: 132, +1297144: 44, +1297145: 169, +1297146: 30, +1297147: 177, +1297148: 11, +1297149: 255, +1297150: 127, +1297151: 0, +1297152: 0, +1297153: 0, +1297154: 0, +1297155: 70, +1297156: 18, +1297157: 69, +1297158: 1, +1297159: 192, +1297160: 0, +1297161: 82, +1297162: 54, +1297163: 140, +1297164: 29, +1297165: 198, +1297166: 4, +1297167: 132, +1297168: 4, +1297169: 49, +1297170: 98, +1297171: 107, +1297172: 73, +1297173: 165, +1297174: 48, +1297175: 33, +1297176: 32, +1297177: 21, +1297178: 0, +1297179: 91, +1297180: 2, +1297181: 255, +1297182: 127, +1297183: 0, +1297184: 0, +1297185: 0, +1297186: 0, +1297187: 24, +1297188: 99, +1297189: 24, +1297190: 99, +1297191: 24, +1297192: 99, +1297193: 24, +1297194: 99, +1297195: 24, +1297196: 99, +1297197: 24, +1297198: 99, +1297199: 24, +1297200: 99, +1297201: 24, +1297202: 99, +1297203: 24, +1297204: 99, +1297205: 24, +1297206: 99, +1297207: 24, +1297208: 99, +1297209: 24, +1297210: 99, +1297211: 24, +1297212: 99, +1297213: 255, +1297214: 127, +1297215: 0, +1297216: 0, +1297217: 14, +1297218: 0, +1297219: 220, +1297220: 37, +1297221: 56, +1297222: 21, +1297223: 113, +1297224: 0, +1297225: 48, +1297226: 0, +1297227: 121, +1297228: 29, +1297229: 23, +1297230: 17, +1297231: 180, +1297232: 8, +1297233: 146, +1297234: 0, +1297235: 31, +1297236: 66, +1297237: 121, +1297238: 29, +1297239: 245, +1297240: 12, +1297241: 147, +1297242: 4, +1297243: 29, +1297244: 28, +1297245: 25, +1297246: 12, +1297247: 19, +1297248: 0, +1297249: 12, +1297250: 0, +1297251: 28, +1297252: 42, +1297253: 87, +1297254: 25, +1297255: 112, +1297256: 0, +1297257: 46, +1297258: 0, +1297259: 153, +1297260: 33, +1297261: 54, +1297262: 17, +1297263: 211, +1297264: 8, +1297265: 145, +1297266: 0, +1297267: 95, +1297268: 74, +1297269: 153, +1297270: 29, +1297271: 244, +1297272: 12, +1297273: 177, +1297274: 4, +1297275: 29, +1297276: 32, +1297277: 24, +1297278: 12, +1297279: 18, +1297280: 0, +1297281: 10, +1297282: 0, +1297283: 92, +1297284: 46, +1297285: 150, +1297286: 29, +1297287: 142, +1297288: 0, +1297289: 76, +1297290: 0, +1297291: 216, +1297292: 37, +1297293: 85, +1297294: 21, +1297295: 241, +1297296: 8, +1297297: 175, +1297298: 0, +1297299: 159, +1297300: 82, +1297301: 216, +1297302: 33, +1297303: 19, +1297304: 17, +1297305: 208, +1297306: 4, +1297307: 29, +1297308: 36, +1297309: 23, +1297310: 16, +1297311: 17, +1297312: 0, +1297313: 8, +1297314: 0, +1297315: 124, +1297316: 50, +1297317: 181, +1297318: 33, +1297319: 140, +1297320: 0, +1297321: 74, +1297322: 0, +1297323: 247, +1297324: 41, +1297325: 116, +1297326: 21, +1297327: 16, +1297328: 13, +1297329: 174, +1297330: 0, +1297331: 223, +1297332: 90, +1297333: 247, +1297334: 37, +1297335: 49, +1297336: 17, +1297337: 206, +1297338: 4, +1297339: 29, +1297340: 40, +1297341: 22, +1297342: 16, +1297343: 15, +1297344: 0, +1297345: 6, +1297346: 0, +1297347: 187, +1297348: 54, +1297349: 212, +1297350: 33, +1297351: 139, +1297352: 0, +1297353: 72, +1297354: 0, +1297355: 23, +1297356: 46, +1297357: 147, +1297358: 25, +1297359: 15, +1297360: 13, +1297361: 204, +1297362: 0, +1297363: 31, +1297364: 99, +1297365: 23, +1297366: 42, +1297367: 80, +1297368: 17, +1297369: 237, +1297370: 4, +1297371: 29, +1297372: 44, +1297373: 22, +1297374: 16, +1297375: 14, +1297376: 0, +1297377: 4, +1297378: 0, +1297379: 251, +1297380: 58, +1297381: 243, +1297382: 37, +1297383: 169, +1297384: 0, +1297385: 70, +1297386: 0, +1297387: 86, +1297388: 50, +1297389: 178, +1297390: 29, +1297391: 45, +1297392: 13, +1297393: 235, +1297394: 0, +1297395: 95, +1297396: 107, +1297397: 86, +1297398: 46, +1297399: 111, +1297400: 21, +1297401: 235, +1297402: 4, +1297403: 29, +1297404: 48, +1297405: 21, +1297406: 20, +1297407: 12, +1297408: 0, +1297409: 2, +1297410: 0, +1297411: 59, +1297412: 63, +1297413: 18, +1297414: 42, +1297415: 167, +1297416: 0, +1297417: 68, +1297418: 0, +1297419: 117, +1297420: 54, +1297421: 241, +1297422: 29, +1297423: 76, +1297424: 13, +1297425: 233, +1297426: 4, +1297427: 191, +1297428: 119, +1297429: 117, +1297430: 50, +1297431: 142, +1297432: 21, +1297433: 10, +1297434: 5, +1297435: 29, +1297436: 52, +1297437: 20, +1297438: 20, +1297439: 11, +1297440: 0, +1297441: 0, +1297442: 0, +1297443: 123, +1297444: 71, +1297445: 82, +1297446: 46, +1297447: 198, +1297448: 0, +1297449: 99, +1297450: 0, +1297451: 181, +1297452: 58, +1297453: 16, +1297454: 34, +1297455: 107, +1297456: 17, +1297457: 8, +1297458: 5, +1297459: 255, +1297460: 127, +1297461: 181, +1297462: 54, +1297463: 173, +1297464: 25, +1297465: 41, +1297466: 9, +1297467: 29, +1297468: 56, +1297469: 20, +1297470: 24, +1297471: 10, +1297472: 0, +456934: 8, +456939: 229, +456940: 32, +456941: 164, +456942: 24, +456943: 131, +456944: 16, +456945: 65, +456946: 8, +456951: 200, +456952: 28, +456953: 134, +456954: 20, +456955: 132, +456956: 16, +456957: 65, +1374462: 18, +456963: 171, +456964: 24, +456965: 136, +1501090: 49, +1501092: 8, +1501093: 0, +1501094: 14, +1171362: 0, +1171363: 56, +1171364: 90, +1171365: 127, +1171366: 224, +1171367: 59, +1171368: 128, +1171369: 38, +1171370: 32, +1171371: 9, +1171372: 90, +1171373: 79, +1171374: 181, +1171375: 54, +1171376: 16, +1171377: 38, +1171378: 206, +1171379: 29, +1171380: 148, +1171381: 82, +1171382: 206, +1171383: 57, +1171384: 8, +1171385: 33, +1171386: 132, +1171387: 16, +1171388: 59, +1171389: 3, +1171390: 22, +1171391: 2, +1171392: 19, +1171393: 1, +1172028: 0, +1172029: 56, +1172030: 90, +1172031: 63, +1172032: 80, +1172033: 46, +1172034: 229, +1172035: 0, +1172036: 99, +1172037: 0, +1172038: 179, +1172039: 58, +1172040: 14, +1172041: 34, +1172042: 105, +1172043: 17, +1172044: 39, +1172045: 9, +1172046: 255, +1172047: 3, +1172048: 247, +1172049: 2, +1172050: 16, +1172051: 2, +1172052: 8, +1172053: 1, +1172054: 255, +1172055: 127, +1172056: 153, +1172057: 2, +1172058: 112, +1172059: 1, +1697061: 0, +1697062: 56, +1697063: 151, +1697064: 95, +1697065: 242, +1697066: 86, +1697067: 135, +1697068: 41, +1697069: 160, +1697070: 0, +1697071: 85, +1697072: 99, +1697073: 176, +1697074: 74, +1697075: 11, +1697076: 58, +1697077: 201, +1697078: 49, +1697079: 255, +1697080: 87, +1697081: 247, +1697082: 66, +1697083: 16, +1697084: 38, +1697085: 140, +1697086: 21, +1697087: 255, +1697088: 3, +1697089: 121, +1697090: 2, +1697091: 41, +1697092: 0, +1172860: 0, +1172861: 56, +1172862: 255, +1172863: 2, +1172864: 191, +1172865: 1, +1172866: 15, +1172867: 0, +1172868: 8, +1172869: 0, +1172870: 191, +1172871: 1, +1172872: 27, +1172873: 1, +1172874: 186, +1172875: 0, +1172876: 17, +1172877: 0, +1172878: 255, +1172879: 127, +1172880: 156, +1172881: 3, +1172882: 55, +1172883: 2, +1172884: 209, +1172885: 0, +1172886: 224, +1172887: 59, +1172888: 128, +1172889: 38, +1172890: 128, +1172891: 21, +1172912: 0, +1172913: 56, +1172914: 250, +1172915: 114, +1172916: 176, +1172917: 85, +1172918: 69, +1172919: 40, +1172920: 1, +1172921: 24, +1172922: 16, +1172923: 98, +1172924: 107, +1172925: 73, +1172926: 198, +1172927: 56, +1172928: 99, +1172929: 44, +1172930: 224, +1172931: 127, +1172932: 160, +1172933: 125, +1172934: 224, +1172935: 72, +1172936: 160, +1172937: 48, +1172938: 255, +1172939: 3, +1172940: 55, +1172941: 2, +1172942: 209, +1172943: 0, +1172944: 0, +1172945: 56, +1172946: 255, +1172947: 127, +1172948: 211, +1172949: 78, +1172950: 38, +1172951: 25, +1172952: 129, +1172953: 4, +1172954: 87, +1172955: 95, +1172956: 145, +1172957: 70, +1172958: 13, +1172959: 54, +1172960: 203, +1172961: 45, +1172962: 186, +1172963: 107, +1172964: 178, +1172965: 86, +1172966: 71, +1172967: 41, +1172968: 195, +1172969: 24, +1172970: 255, +1172971: 127, +1172972: 211, +1172973: 78, +1172974: 38, +1172975: 25, +452117: 1, +456482: 0, +456483: 52, +452119: 2, +456484: 132, +1304574: 0, +1304575: 56, +1304576: 151, +1304577: 95, +1304578: 242, +1304579: 86, +1304580: 135, +1304581: 41, +1304582: 160, +1304583: 0, +1304584: 85, +1304585: 99, +1304586: 176, +1304587: 74, +1304588: 11, +1304589: 58, +1304590: 201, +1304591: 49, +1304592: 255, +1304593: 87, +1304594: 247, +1304595: 66, +1304596: 16, +1304597: 38, +1304598: 140, +1304599: 21, +1304600: 255, +1304601: 3, +1304602: 121, +1304603: 2, +1304604: 41, +1304605: 0, +456485: 16, +456486: 28, +1698116: 0, +1698117: 56, +1698118: 87, +1698119: 63, +1698120: 77, +1698121: 46, +1698122: 226, +1698123: 0, +1698124: 96, +1698125: 0, +1698126: 176, +1698127: 58, +1698128: 11, +1698129: 34, +1698130: 102, +1698131: 17, +1698132: 36, +1698133: 9, +1698134: 255, +1698135: 87, +1698136: 247, +1698137: 66, +1698138: 16, +1698139: 38, +1698140: 140, +1698141: 21, +1698142: 31, +1698143: 124, +1698144: 24, +1698145: 96, +1698146: 10, +1698147: 48, +1173935: 0, +1173936: 56, +1173937: 245, +1173938: 107, +1173939: 225, +1173940: 6, +1173941: 65, +1173942: 6, +1173943: 161, +1173944: 5, +1173945: 95, +1173946: 94, +1173947: 63, +1173948: 24, +1173949: 20, +1173950: 16, +1173951: 10, +1173952: 8, +1173953: 4, +1173954: 4, +1173955: 159, +1173956: 79, +1173957: 216, +1173958: 62, +1173959: 18, +1173960: 46, +1173961: 112, +1173962: 111, +1173963: 255, +1173964: 127, +1173965: 224, +1173966: 94, +456488: 156, +456489: 115, +3141200: 255, +3141201: 255, +3141202: 255, +3141203: 255, +3141204: 255, +3141205: 255, +3141206: 255, +3141207: 255, +3141208: 255, +3141209: 255, +3141210: 255, +3141211: 255, +3141212: 255, +3141213: 255, +3141214: 255, +3141215: 255, +3141216: 255, +3141217: 255, +3141218: 255, +3141219: 255, +3141220: 255, +3141221: 255, +3141222: 255, +3141223: 255, +3141224: 255, +3141225: 255, +3141226: 255, +3141227: 255, +3141228: 255, +3141229: 255, +3141230: 255, +3141231: 255, +3141232: 255, +3141233: 255, +3141234: 255, +3141235: 255, +3141236: 255, +3141237: 255, +3141238: 255, +3141239: 255, +3141240: 255, +3141241: 255, +3141242: 255, +3141243: 255, +3141244: 255, +3141245: 255, +3141246: 255, +3141247: 255, +3141248: 255, +3141249: 255, +3141250: 255, +3141251: 255, +3141252: 255, +3141253: 255, +3141254: 255, +3141255: 255, +3141256: 255, +3141257: 255, +3141258: 255, +3141259: 255, +3141260: 255, +3141261: 255, +3141262: 255, +3141263: 255, +3141264: 255, +3141265: 255, +3141266: 255, +3141267: 255, +3141268: 255, +3141269: 255, +3141270: 255, +3141271: 255, +3141272: 255, +3141273: 255, +3141274: 255, +3141275: 255, +3141276: 255, +3141277: 255, +3141278: 255, +3141279: 255, +3141280: 255, +3141281: 255, +3141282: 255, +3141283: 255, +3141284: 255, +3141285: 255, +3141286: 255, +3141287: 255, +3141288: 255, +3141289: 255, +3141290: 255, +3141291: 255, +3141292: 255, +3141293: 255, +3141294: 255, +3141295: 255, +3141296: 255, +3141297: 255, +3141298: 255, +3141299: 255, +3141300: 255, +3141301: 255, +3141302: 255, +3141303: 255, +3141304: 255, +3141305: 255, +3141306: 255, +3141307: 255, +3141308: 255, +3141309: 255, +3141310: 255, +3141311: 255, +3141312: 255, +3141313: 255, +3141314: 255, +3141315: 255, +3141316: 255, +3141317: 255, +3141318: 255, +3141319: 255, +3141320: 255, +3141321: 255, +3141322: 255, +3141323: 255, +3141324: 255, +3141325: 255, +3141326: 255, +3141327: 255, +3141328: 255, +3141329: 255, +3141330: 255, +3141331: 255, +3141332: 255, +3141333: 255, +3141334: 255, +3141335: 255, +3141336: 255, +3141337: 255, +3141338: 255, +3141339: 255, +3141340: 255, +3141341: 255, +3141342: 255, +3141343: 255, +3141344: 255, +3141345: 255, +3141346: 255, +3141347: 255, +3141348: 255, +3141349: 255, +3141350: 255, +3141351: 255, +3141352: 255, +3141353: 255, +3141354: 255, +3141355: 255, +3141356: 255, +3141357: 255, +3141358: 255, +3141359: 255, +3141360: 255, +3141361: 255, +3141362: 255, +3141363: 255, +3141364: 255, +3141365: 255, +3141366: 255, +3141367: 255, +3141368: 255, +3141369: 255, +3141370: 255, +3141371: 255, +3141372: 255, +3141373: 255, +3141374: 255, +3141375: 255, +3141376: 255, +3141377: 255, +3141378: 255, +3141379: 255, +3141380: 255, +3141381: 255, +3141382: 255, +3141383: 255, +3141384: 255, +3141385: 255, +3141386: 255, +3141387: 255, +3141388: 255, +3141389: 255, +3141390: 255, +3141391: 255, +3141392: 255, +3141393: 255, +3141394: 255, +3141395: 255, +3141396: 255, +3141397: 255, +3141398: 255, +3141399: 255, +3141400: 255, +3141401: 255, +3141402: 255, +3141403: 255, +3141404: 255, +3141405: 255, +3141406: 255, +3141407: 255, +3141408: 255, +458116: 17, +1307173: 0, +1307174: 56, +1307175: 87, +1307176: 63, +1307177: 77, +1307178: 46, +1307179: 226, +1307180: 0, +1307181: 96, +1307182: 0, +1307183: 176, +1307184: 58, +1307185: 11, +1307186: 34, +1307187: 102, +1307188: 17, +1307189: 36, +1307190: 9, +1307191: 255, +1307192: 87, +1307193: 247, +1307194: 66, +1307195: 16, +1307196: 38, +1307197: 140, +1307198: 21, +1307199: 31, +1307200: 124, +1307201: 24, +1307202: 96, +1307203: 10, +1307204: 48, +3143248: 255, +3143249: 255, +3143250: 255, +3143251: 255, +3143252: 255, +3143253: 255, +3143254: 255, +3143255: 255, +3143256: 255, +3143257: 255, +3143258: 255, +3143259: 255, +3143260: 255, +3143261: 255, +3143262: 255, +3143263: 255, +3143264: 255, +3143265: 255, +3143266: 255, +3143267: 255, +3143268: 255, +3143269: 255, +3143270: 255, +3143271: 255, +3143272: 255, +3143273: 255, +3143274: 255, +3143275: 255, +3143276: 255, +3143277: 255, +3143278: 255, +3143279: 255, +3143280: 255, +3143281: 255, +3143282: 255, +3143283: 255, +3143284: 255, +3143285: 255, +3143286: 255, +3143287: 255, +3143288: 255, +3143289: 255, +3143290: 255, +3143291: 255, +3143292: 255, +3143293: 255, +3143294: 255, +3143295: 255, +3143296: 255, +3143297: 255, +3143298: 255, +3143299: 255, +3143300: 255, +3143301: 255, +3143302: 255, +3143303: 255, +3143304: 255, +3143305: 255, +3143306: 255, +3143307: 255, +3143308: 255, +3143309: 255, +3143310: 255, +3143311: 255, +3143312: 255, +3143313: 255, +3143314: 255, +3143315: 255, +3143316: 255, +3143317: 255, +3143318: 255, +3143319: 255, +3143320: 255, +3143321: 255, +3143322: 255, +3143323: 255, +3143324: 255, +3143325: 255, +3143326: 255, +3143327: 255, +3143328: 255, +3143329: 255, +3143330: 255, +3143331: 255, +3143332: 255, +3143333: 255, +3143334: 255, +3143335: 255, +3143336: 255, +3143337: 255, +3143338: 255, +3143339: 255, +3143340: 255, +3143341: 255, +3143342: 255, +3143343: 255, +3143344: 255, +3143345: 255, +3143346: 255, +3143347: 255, +3143348: 255, +3143349: 255, +3143350: 255, +3143351: 255, +3143352: 255, +3143353: 255, +3143354: 255, +3143355: 255, +3143356: 255, +3143357: 255, +3143358: 255, +3143359: 255, +3143360: 255, +3143361: 255, +3143362: 255, +3143363: 255, +3143364: 255, +3143365: 255, +3143366: 255, +3143367: 255, +3143368: 255, +3143369: 255, +3143370: 255, +3143371: 255, +3143372: 255, +3143373: 255, +3143374: 255, +3143375: 255, +3143376: 255, +3143377: 255, +3143378: 255, +3143379: 255, +3143380: 255, +3143381: 255, +3143382: 255, +3143383: 255, +3143384: 255, +3143385: 255, +3143386: 255, +3143387: 255, +3143388: 255, +3143389: 255, +3143390: 255, +3143391: 255, +3143392: 255, +3143393: 255, +3143394: 255, +3143395: 255, +3143396: 255, +3143397: 255, +3143398: 255, +3143399: 255, +3143400: 255, +3143401: 255, +3143402: 255, +3143403: 255, +3143404: 255, +3143405: 255, +3143406: 255, +3143407: 255, +3143408: 255, +3143409: 255, +3143410: 255, +3143411: 255, +3143412: 255, +3143413: 255, +3143414: 255, +3143415: 255, +3143416: 255, +3143417: 255, +3143418: 255, +3143419: 255, +3143420: 255, +3143421: 255, +3143422: 255, +3143423: 255, +3143424: 255, +3143425: 255, +3143426: 255, +3143427: 255, +3143428: 255, +3143429: 255, +3143430: 255, +3143431: 255, +3143432: 255, +3143433: 255, +3143434: 255, +3143435: 255, +3143436: 255, +3143437: 255, +3143438: 255, +3143439: 255, +3143440: 255, +3143441: 255, +3143442: 255, +3143443: 255, +3143760: 255, +3143761: 255, +3143762: 255, +3143763: 255, +3143764: 255, +3143765: 255, +3143766: 255, +3143767: 255, +3143768: 255, +3143769: 255, +3143770: 255, +3143771: 255, +3143772: 255, +3143773: 255, +3143774: 255, +3143775: 255, +3143776: 255, +3143777: 255, +3143778: 255, +3143779: 255, +3143780: 255, +3143781: 255, +3143782: 255, +3143783: 255, +3143784: 255, +3143785: 255, +3143786: 255, +3143787: 255, +3143788: 255, +3143789: 255, +3143790: 255, +3143791: 255, +3143792: 255, +3143793: 255, +3143794: 255, +3143795: 255, +3143796: 255, +3143797: 255, +3143798: 255, +3143799: 255, +3143800: 255, +3143801: 255, +3143802: 255, +3143803: 255, +3143804: 255, +3143805: 255, +3143806: 255, +3143807: 255, +3143808: 255, +3143809: 255, +3143810: 255, +3143811: 255, +3143812: 255, +3143813: 255, +3143814: 255, +3143815: 255, +3143816: 255, +3143817: 255, +3143818: 255, +3143819: 255, +3143820: 255, +3143821: 255, +3143822: 255, +3143823: 255, +3143824: 255, +3143825: 255, +3143826: 255, +3143827: 255, +3143828: 255, +3143829: 255, +3143830: 255, +3143831: 255, +3143832: 255, +3143833: 255, +3143834: 255, +3143835: 255, +3143836: 255, +3143837: 255, +3143838: 255, +3143839: 255, +3143840: 255, +3143841: 255, +3143842: 255, +3143843: 255, +3143844: 255, +3143845: 255, +3143846: 255, +3143847: 255, +3143848: 255, +3143849: 255, +3143850: 255, +3143851: 255, +3143852: 255, +3143853: 255, +3143854: 255, +3143855: 255, +3143856: 255, +3143857: 255, +3143858: 255, +3143859: 255, +3143860: 255, +3143861: 255, +3143862: 255, +3143863: 255, +3143864: 255, +3143865: 255, +3143866: 255, +3143867: 255, +3143868: 255, +3143869: 255, +3143870: 255, +3143871: 255, +3143872: 255, +3143873: 255, +3143874: 255, +3143875: 255, +3143876: 255, +3143877: 255, +3143878: 255, +3143879: 255, +3143880: 255, +3143881: 255, +3143882: 255, +3143883: 255, +3143884: 255, +3143885: 255, +3143886: 255, +3143887: 255, +3143888: 255, +3143889: 255, +3143890: 255, +3143891: 255, +3143892: 255, +3143893: 255, +3143894: 255, +3143895: 255, +3143896: 255, +3143897: 255, +3143898: 255, +3143899: 255, +3143900: 255, +3143901: 255, +3143902: 255, +3143903: 255, +3143904: 255, +3143905: 255, +3143906: 255, +3143907: 255, +3143908: 255, +3143909: 255, +3143910: 255, +3143911: 255, +3143912: 255, +3143913: 255, +3143914: 255, +3143915: 255, +3143916: 255, +3143917: 255, +3143918: 255, +3143919: 255, +3143920: 255, +3143921: 255, +3143922: 255, +3143923: 255, +3143924: 255, +3143925: 255, +3143926: 255, +3143927: 255, +3143928: 255, +3143929: 255, +3143930: 255, +3143931: 255, +3143932: 255, +3143933: 255, +3143934: 255, +3143935: 255, +3143936: 255, +3143937: 255, +3143938: 255, +3143939: 255, +3143940: 255, +3143941: 255, +3143942: 255, +3143943: 255, +3143944: 255, +3143945: 255, +3143946: 255, +3143947: 255, +3143948: 255, +3143949: 255, +3143950: 255, +3143951: 255, +3143952: 255, +3143953: 255, +3143954: 255, +3143955: 255, +3143956: 255, +1157731: 95, +452159: 0, +852400: 224, +852401: 59, +852402: 128, +852403: 38, +852404: 128, +852405: 21, +1237977: 1, +1237978: 255, +1237979: 0, +1237980: 254, +1237981: 1, +1237982: 255, +1237983: 1, +1237984: 254, +1237985: 0, +1237986: 255, +1237987: 1, +1237988: 254, +1237989: 0, +1237990: 255, +1237991: 1, +1237992: 254, +1237993: 0, +1237994: 255, +1237995: 0, +1237996: 254, +1237997: 1, +1237998: 255, +1237999: 0, +1238000: 254, +1238001: 0, +1238002: 254, +1238003: 0, +1238004: 255, +1238005: 0, +1238006: 254, +1238007: 0, +1238008: 255, +1238009: 0, +1238010: 254, +1238011: 255, +1238012: 254, +1238013: 0, +1238014: 255, +1238015: 0, +1238016: 254, +1238017: 255, +1238018: 254, +1238019: 0, +1238020: 254, +1238021: 255, +1238022: 255, +1238023: 255, +1238024: 254, +1238025: 255, +1238026: 254, +1238027: 255, +1238028: 255, +1238029: 255, +1238030: 254, +1238031: 255, +1238032: 254, +1238033: 255, +1238034: 255, +1238035: 255, +1238036: 254, +1238037: 255, +1238038: 255, +1238039: 255, +1238040: 255, +1238041: 255, +1238042: 254, +1238043: 254, +1238044: 255, +1238045: 255, +1238046: 255, +1238047: 255, +1238048: 255, +1238049: 254, +1238050: 255, +1238051: 255, +1238052: 0, +1238053: 255, +1238054: 255, +1238055: 254, +1238056: 255, +1238057: 255, +1238058: 0, +1238059: 255, +1238060: 0, +1238061: 254, +1238062: 0, +1238063: 255, +1238064: 0, +1238065: 255, +1238066: 1, +1238067: 255, +1238068: 0, +1238069: 254, +1238070: 1, +1238071: 255, +1238072: 0, +1238073: 255, +1238074: 1, +1238075: 255, +1238076: 1, +1238077: 255, +1238078: 1, +1238079: 254, +1238080: 1, +1238081: 255, +1238082: 2, +1238083: 255, +1238084: 1, +1238085: 255, +1238086: 2, +1238087: 255, +1238088: 1, +1238089: 255, +1238090: 2, +1238091: 255, +1238092: 1, +1238093: 255, +1238094: 2, +1238095: 0, +1238096: 2, +1238097: 255, +1238098: 1, +1238099: 255, +1238100: 2, +1238101: 255, +1238102: 2, +1238103: 0, +1238104: 2, +1238105: 255, +1238106: 2, +1238107: 255, +1238108: 2, +1238109: 0, +1238110: 1, +1238111: 255, +1238112: 2, +1238113: 0, +1238114: 2, +1238115: 0, +1238116: 2, +1238117: 255, +1238118: 1, +1238119: 0, +1238120: 2, +1238121: 0, +1238122: 2, +1238123: 0, +1238124: 1, +1238125: 0, +1238126: 2, +1238127: 0, +1238128: 2, +1238129: 0, +1238130: 1, +1238131: 0, +1238132: 2, +1238133: 0, +1238134: 1, +1238135: 0, +1238136: 2, +1238137: 0, +1238138: 2, +1238139: 0, +1238140: 1, +1238141: 0, +1238142: 2, +1238143: 1, +1238144: 1, +1238145: 0, +1238146: 2, +1238147: 0, +1238148: 1, +1238149: 1, +1238150: 2, +1238151: 0, +1238152: 2, +1238153: 1, +1238154: 1, +1238155: 0, +1238156: 2, +1238157: 1, +1238158: 1, +1238159: 0, +1238160: 2, +1238161: 1, +1238162: 1, +1238163: 0, +1238164: 2, +1238165: 1, +1238166: 1, +1238167: 0, +1238168: 2, +1238169: 1, +1238170: 1, +1238171: 1, +1238172: 2, +1238173: 0, +1238174: 2, +1238175: 1, +1238176: 1, +1238177: 1, +1238178: 2, +1238179: 1, +1238180: 1, +1238181: 0, +1238182: 2, +1238183: 1, +1238184: 1, +1238185: 1, +1238186: 2, +1238187: 1, +1238188: 1, +1238189: 1, +1238190: 2, +1238191: 1, +1238192: 1, +1238193: 1, +1238194: 1, +1238195: 1, +1238196: 2, +1238197: 1, +1238198: 1, +1238199: 1, +1238200: 1, +1238201: 1, +1238202: 1, +1238203: 1, +1238204: 1, +1238205: 1, +1238206: 1, +1238207: 1, +1238208: 1, +1238209: 2, +1238210: 1, +1238211: 1, +1238212: 1, +1238213: 1, +1238214: 1, +1238215: 1, +1238216: 1, +1238217: 2, +1238218: 0, +1238219: 1, +1238220: 1, +1238221: 2, +1238222: 1, +1238223: 1, +1238224: 0, +1238225: 2, +1238226: 1, +1238227: 1, +1238228: 0, +1238229: 2, +1238230: 0, +1238231: 1, +1238232: 1, +1238233: 2, +1238234: 0, +1238235: 1, +1238236: 0, +1238237: 2, +1238238: 0, +1238239: 2, +1238240: 0, +1238241: 1, +1238242: 0, +1238243: 2, +1238244: 0, +1238245: 2, +1238246: 0, +1238247: 2, +1238248: 0, +1238249: 1, +1238250: 0, +1238251: 2, +1238252: 0, +1238253: 2, +1238254: 0, +1238255: 1, +1238256: 0, +1238257: 2, +1238258: 0, +1238259: 2, +1238260: 0, +1238261: 1, +1238262: 255, +1238263: 2, +1238264: 0 +} diff --git a/worlds/sm/variaRandomizer/randomizer.py b/worlds/sm/variaRandomizer/randomizer.py new file mode 100644 index 00000000..563ed6bf --- /dev/null +++ b/worlds/sm/variaRandomizer/randomizer.py @@ -0,0 +1,851 @@ +#!/usr/bin/python3 + +from Utils import output_path +import argparse, os.path, json, sys, shutil, random, copy, requests + +from rando.RandoSettings import RandoSettings, GraphSettings +from rando.RandoExec import RandoExec +from graph.graph_utils import vanillaTransitions, vanillaBossesTransitions, GraphUtils, getAccessPoint +from utils.parameters import Knows, Controller, easy, medium, hard, harder, hardcore, mania, infinity, text2diff, diff2text, appDir +from rom.rom_patches import RomPatches +from rom.rompatcher import RomPatcher +from utils.utils import PresetLoader, loadRandoPreset, getDefaultMultiValues, getPresetDir +from utils.version import displayedVersion +from logic.smbool import SMBool +from utils.doorsmanager import DoorsManager +from logic.logic import Logic + +import utils.log + +# we need to know the logic before doing anything else +def getLogic(): + # check if --logic is there + logic = 'vanilla' + for i, param in enumerate(sys.argv): + if param == '--logic' and i+1 < len(sys.argv): + logic = sys.argv[i+1] + return logic +Logic.factory(getLogic()) +defaultMultiValues = getDefaultMultiValues() +speeds = defaultMultiValues['progressionSpeed'] +energyQties = defaultMultiValues['energyQty'] +progDiffs = defaultMultiValues['progressionDifficulty'] +morphPlacements = defaultMultiValues['morphPlacement'] +majorsSplits = defaultMultiValues['majorsSplit'] +gravityBehaviours = defaultMultiValues['gravityBehaviour'] + +def randomMulti(args, param, defaultMultiValues): + value = args[param] + + isRandom = False + if value == "random": + isRandom = True + if args[param+"List"] != None: + # use provided list + choices = args[param+"List"].split(',') + value = random.choice(choices) + else: + # use default list + value = random.choice(defaultMultiValues) + + return (isRandom, value) + +def dumpErrorMsg(outFileName, msg): + print("DIAG: " + msg) + if outFileName is not None: + with open(outFileName, 'w') as jsonFile: + json.dump({"errorMsg": msg}, jsonFile) + +def dumpErrorMsgs(outFileName, msgs): + dumpErrorMsg(outFileName, joinErrorMsgs(msgs)) + +def joinErrorMsgs(msgs): + return '\n'.join(msgs) + +def restricted_float(x): + x = float(x) + if x < 0.0 or x > 9.0: + raise argparse.ArgumentTypeError("%r not in range [1.0, 9.0]"%(x,)) + return x + +def to_pascal_case_with_space(snake_str): + return snake_str.replace("_", " ").title() + +class VariaRandomizer: + + parser = argparse.ArgumentParser(description="Random Metroid Randomizer") + parser.add_argument('--patchOnly', + help="only apply patches, do not perform any randomization", action='store_true', + dest='patchOnly', default=False) + parser.add_argument('--param', '-p', help="the input parameters", + default=None, dest='paramsFileName') + parser.add_argument('--dir', + help="output directory for ROM and dot files", + dest='directory', nargs='?', default='.') + parser.add_argument('--dot', + help="generate dot file with area graph", + action='store_true',dest='dot', default=False) + parser.add_argument('--area', help="area mode", + dest='area', nargs='?', const=True, default=False) + parser.add_argument('--areaLayoutBase', + help="use simple layout patch for area mode", action='store_true', + dest='areaLayoutBase', default=False) + parser.add_argument('--lightArea', help="keep number of transitions between vanilla areas", action='store_true', + dest='lightArea', default=False) + parser.add_argument('--escapeRando', + help="Randomize the escape sequence", + dest='escapeRando', nargs='?', const=True, default=False) + parser.add_argument('--noRemoveEscapeEnemies', + help="Do not remove enemies during escape sequence", action='store_true', + dest='noRemoveEscapeEnemies', default=False) + parser.add_argument('--bosses', help="randomize bosses", + dest='bosses', nargs='?', const=True, default=False) + parser.add_argument('--minimizer', help="minimizer mode: area and boss mixed together. arg is number of non boss locations", + dest='minimizerN', nargs='?', const=35, default=None, + choices=[str(i) for i in range(30,101)]+["random"]) + parser.add_argument('--minimizerTourian', + help="Tourian speedup in minimizer mode", + dest='minimizerTourian', nargs='?', const=True, default=False) + parser.add_argument('--startLocation', help="Name of the Access Point to start from", + dest='startLocation', nargs='?', default="Landing Site", + choices=['random'] + GraphUtils.getStartAccessPointNames()) + parser.add_argument('--startLocationList', help="list to choose from when random", + dest='startLocationList', nargs='?', default=None) + parser.add_argument('--debug', '-d', help="activate debug logging", dest='debug', + action='store_true') + parser.add_argument('--maxDifficulty', '-t', + help="the maximum difficulty generated seed will be for given parameters", + dest='maxDifficulty', nargs='?', default=None, + choices=['easy', 'medium', 'hard', 'harder', 'hardcore', 'mania', 'random']) + parser.add_argument('--minDifficulty', + help="the minimum difficulty generated seed will be for given parameters (speedrun prog speed required)", + dest='minDifficulty', nargs='?', default=None, + choices=['easy', 'medium', 'hard', 'harder', 'hardcore', 'mania']) + parser.add_argument('--seed', '-s', help="randomization seed to use", dest='seed', + nargs='?', default=0, type=int) + parser.add_argument('--rom', '-r', + help="the vanilla ROM", + dest='rom', nargs='?', default=None) + parser.add_argument('--output', + help="to choose the name of the generated json (for the webservice)", + dest='output', nargs='?', default=None) + parser.add_argument('--preset', + help="the name of the preset (for the webservice)", + dest='preset', nargs='?', default=None) + parser.add_argument('--patch', '-c', + help="optional patches to add", + dest='patches', nargs='?', default=[], action='append', + choices=['itemsounds.ips', 'elevators_doors_speed.ips', 'random_music.ips', + 'spinjumprestart.ips', 'rando_speed.ips', 'No_Music', 'AimAnyButton.ips', + 'max_ammo_display.ips', 'supermetroid_msu1.ips', 'Infinite_Space_Jump', + 'refill_before_save.ips', 'remove_elevators_doors_speed.ips', + 'remove_itemsounds.ips', 'vanilla_music.ips']) + parser.add_argument('--missileQty', '-m', + help="quantity of missiles", + dest='missileQty', nargs='?', default=3, + type=restricted_float) + parser.add_argument('--superQty', '-q', + help="quantity of super missiles", + dest='superQty', nargs='?', default=2, + type=restricted_float) + parser.add_argument('--powerBombQty', '-w', + help="quantity of power bombs", + dest='powerBombQty', nargs='?', default=1, + type=restricted_float) + parser.add_argument('--minorQty', '-n', + help="quantity of minors", + dest='minorQty', nargs='?', default=100, + choices=[str(i) for i in range(0,101)]) + parser.add_argument('--energyQty', '-g', + help="quantity of ETanks/Reserve Tanks", + dest='energyQty', nargs='?', default='vanilla', + choices=energyQties + ['random']) + parser.add_argument('--energyQtyList', help="list to choose from when random", + dest='energyQtyList', nargs='?', default=None) + parser.add_argument('--strictMinors', + help="minors quantities values will be strictly followed instead of being probabilities", + dest='strictMinors', nargs='?', const=True, default=False) + parser.add_argument('--majorsSplit', + help="how to split majors/minors: Full, FullWithHUD, Major, Chozo, Scavenger", + dest='majorsSplit', nargs='?', choices=majorsSplits + ['random'], default='Full') + parser.add_argument('--majorsSplitList', help="list to choose from when random", + dest='majorsSplitList', nargs='?', default=None) + parser.add_argument('--scavNumLocs', + help="For Scavenger split, number of major locations in the mandatory route", + dest='scavNumLocs', nargs='?', default=10, + choices=["0"]+[str(i) for i in range(4,17)]) + parser.add_argument('--scavRandomized', + help="For Scavenger split, decide whether mandatory major locs will have non-vanilla items", + dest='scavRandomized', nargs='?', const=True, default=False) + parser.add_argument('--scavEscape', + help="For Scavenger split, decide whether escape sequence shall be triggered as soon as the hunt is over", + dest='scavEscape', nargs='?', const=True, default=False) + parser.add_argument('--suitsRestriction', + help="no suits in early game", + dest='suitsRestriction', nargs='?', const=True, default=False) + parser.add_argument('--morphPlacement', + help="morph placement", + dest='morphPlacement', nargs='?', default='early', + choices=morphPlacements + ['random']) + parser.add_argument('--morphPlacementList', help="list to choose from when random", + dest='morphPlacementList', nargs='?', default=None) + parser.add_argument('--hideItems', help="Like in dessy's rando hide half of the items", + dest="hideItems", nargs='?', const=True, default=False) + parser.add_argument('--progressionSpeed', '-i', + help="progression speed, from " + str(speeds) + ". 'random' picks a random speed from these. Pick a random speed from a subset using comma-separated values, like 'slow,medium,fast'.", + dest='progressionSpeed', nargs='?', default='medium', choices=speeds+['random']) + parser.add_argument('--progressionSpeedList', help="list to choose from when random", + dest='progressionSpeedList', nargs='?', default=None) + parser.add_argument('--progressionDifficulty', + help="", + dest='progressionDifficulty', nargs='?', default='normal', + choices=progDiffs + ['random']) + parser.add_argument('--progressionDifficultyList', help="list to choose from when random", + dest='progressionDifficultyList', nargs='?', default=None) + parser.add_argument('--superFun', + help="randomly remove major items from the pool for maximum enjoyment", + dest='superFun', nargs='?', default=[], action='append', + choices=['Movement', 'Combat', 'Suits', 'MovementRandom', 'CombatRandom', 'SuitsRandom']) + parser.add_argument('--animals', + help="randomly change the save the animals room", + dest='animals', action='store_true', default=False) + parser.add_argument('--nolayout', + help="do not include total randomizer layout patches", + dest='noLayout', action='store_true', default=False) + parser.add_argument('--gravityBehaviour', + help="varia/gravity suits behaviour", + dest='gravityBehaviour', nargs='?', default='Balanced', choices=gravityBehaviours+['random']) + parser.add_argument('--gravityBehaviourList', help="list to choose from when random", + dest='gravityBehaviourList', nargs='?', default=None) + parser.add_argument('--nerfedCharge', + help="apply nerfed charge patch", + dest='nerfedCharge', action='store_true', default=False) + parser.add_argument('--novariatweaks', + help="do not include VARIA randomizer tweaks", + dest='noVariaTweaks', action='store_true', default=False) + parser.add_argument('--controls', + help="specify controls, comma-separated, in that order: Shoot,Jump,Dash,ItemSelect,ItemCancel,AngleUp,AngleDown. Possible values: A,B,X,Y,L,R,Select,None", + dest='controls') + parser.add_argument('--moonwalk', + help="Enables moonwalk by default", + dest='moonWalk', action='store_true', default=False) + parser.add_argument('--runtime', + help="Maximum runtime limit in seconds. If 0 or negative, no runtime limit. Default is 30.", + dest='runtimeLimit_s', nargs='?', default=30, type=int) + parser.add_argument('--race', help="Race mode magic number, between 1 and 65535", dest='raceMagic', + type=int) + parser.add_argument('--vcr', help="Generate VCR output file", dest='vcr', action='store_true') + parser.add_argument('--palette', help="Randomize the palettes", dest='palette', action='store_true') + parser.add_argument('--individual_suit_shift', help="palette param", action='store_true', + dest='individual_suit_shift', default=False) + parser.add_argument('--individual_tileset_shift', help="palette param", action='store_true', + dest='individual_tileset_shift', default=False) + parser.add_argument('--no_match_ship_and_power', help="palette param", action='store_false', + dest='match_ship_and_power', default=True) + parser.add_argument('--seperate_enemy_palette_groups', help="palette param", action='store_true', + dest='seperate_enemy_palette_groups', default=False) + parser.add_argument('--no_match_room_shift_with_boss', help="palette param", action='store_false', + dest='match_room_shift_with_boss', default=True) + parser.add_argument('--no_shift_tileset_palette', help="palette param", action='store_false', + dest='shift_tileset_palette', default=True) + parser.add_argument('--no_shift_boss_palettes', help="palette param", action='store_false', + dest='shift_boss_palettes', default=True) + parser.add_argument('--no_shift_suit_palettes', help="palette param", action='store_false', + dest='shift_suit_palettes', default=True) + parser.add_argument('--no_shift_enemy_palettes', help="palette param", action='store_false', + dest='shift_enemy_palettes', default=True) + parser.add_argument('--no_shift_beam_palettes', help="palette param", action='store_false', + dest='shift_beam_palettes', default=True) + parser.add_argument('--no_shift_ship_palette', help="palette param", action='store_false', + dest='shift_ship_palette', default=True) + parser.add_argument('--min_degree', help="min hue shift", dest='min_degree', nargs='?', default=-180, type=int) + parser.add_argument('--max_degree', help="max hue shift", dest='max_degree', nargs='?', default=180, type=int) + parser.add_argument('--no_global_shift', help="", action='store_false', dest='global_shift', default=True) + parser.add_argument('--invert', help="invert color range", dest='invert', action='store_true', default=False) + parser.add_argument('--no_blue_door_palette', help="palette param", action='store_true', + dest='no_blue_door_palette', default=False) + parser.add_argument('--ext_stats', help="dump extended stats SQL", nargs='?', default=None, dest='extStatsFilename') + parser.add_argument('--randoPreset', help="rando preset file", dest="randoPreset", nargs='?', default=None) + parser.add_argument('--fakeRandoPreset', help="for prog speed stats", dest="fakeRandoPreset", nargs='?', default=None) + parser.add_argument('--plandoRando', help="json string with already placed items/locs", dest="plandoRando", + nargs='?', default=None) + parser.add_argument('--sprite', help='use a custom sprite for Samus', dest='sprite', default=None) + parser.add_argument('--no_spin_attack', help='when using a custom sprite, use the same animation for screw attack with or without Space Jump', dest='noSpinAttack', action='store_true', default=False) + parser.add_argument('--customItemNames', help='add custom item names for some of them, related to the custom sprite', + dest='customItemNames', action='store_true', default=False) + parser.add_argument('--ship', help='use a custom sprite for Samus ship', dest='ship', default=None) + parser.add_argument('--seedIps', help='ips generated from previous seed', dest='seedIps', default=None) + parser.add_argument('--jm,', help="display data used by jm for its stats", dest='jm', action='store_true', default=False) + parser.add_argument('--doorsColorsRando', help='randomize color of colored doors', dest='doorsColorsRando', + nargs='?', const=True, default=False) + parser.add_argument('--allowGreyDoors', help='add grey color in doors colors pool', dest='allowGreyDoors', + nargs='?', const=True, default=False) + parser.add_argument('--logic', help='logic to use', dest='logic', nargs='?', default="varia", choices=["varia", "rotation"]) + parser.add_argument('--hud', help='Enable VARIA hud', dest='hud', + nargs='?', const=True, default=False) + + def __init__(self, world, rom, player): + # parse args + self.args = copy.deepcopy(VariaRandomizer.parser.parse_args(["--logic", "varia"])) #dummy custom args to skip parsing _sys.argv while still get default values + self.player = player + args = self.args + args.rom = rom + # args.startLocation = to_pascal_case_with_space(world.startLocation[player].current_key) + + if args.output is None and args.rom is None: + print("Need --output or --rom parameter") + sys.exit(-1) + elif args.output is not None and args.rom is not None: + print("Can't have both --output and --rom parameters") + sys.exit(-1) + + if args.plandoRando != None and args.output == None: + print("plandoRando param requires output param") + sys.exit(-1) + + utils.log.init(args.debug) + logger = utils.log.get('Rando') + + Logic.factory(args.logic) + + # service to force an argument value and notify it + argDict = vars(args) + self.forcedArgs = {} + self.optErrMsgs = [ ] + optErrMsgs = self.optErrMsgs + def forceArg(arg, value, msg, altValue=None, webArg=None, webValue=None): + okValues = [value] + if altValue is not None: + okValues.append(altValue) + + if argDict[arg] not in okValues: + argDict[arg] = value + self.forcedArgs[webArg if webArg != None else arg] = webValue if webValue != None else value + # print(msg) + # optErrMsgs.append(msg) + + preset = loadRandoPreset(world, self.player, args) + # use the skill preset from the rando preset + if preset is not None and preset != 'custom' and preset != 'varia_custom' and args.paramsFileName is None: + args.paramsFileName = '{}/{}/{}.json'.format(appDir, getPresetDir(preset), preset) + + # if diff preset given, load it + if args.paramsFileName is not None: + PresetLoader.factory(args.paramsFileName).load(self.player) + preset = os.path.splitext(os.path.basename(args.paramsFileName))[0] + + if args.preset is not None: + preset = args.preset + else: + if preset == 'custom': + PresetLoader.factory(world.custom_preset[player].value).load(self.player) + elif preset == 'varia_custom': + url = 'https://randommetroidsolver.pythonanywhere.com/presetWebService' + preset_name = next(iter(world.varia_custom_preset[player].value)) + payload = '{{"preset": "{}"}}'.format(preset_name) + headers = {'content-type': 'application/json', 'Accept-Charset': 'UTF-8'} + response = requests.post(url, data=payload, headers=headers) + if response.ok: + PresetLoader.factory(json.loads(response.text)).load(self.player) + else: + print("Got error {} {} {} from trying to fetch varia custom preset named {}".format(response.status_code, response.reason, response.text, preset_name)) + sys.exit(-1) + else: + preset = 'default' + PresetLoader.factory('{}/{}/{}.json'.format(appDir, getPresetDir('casual'), 'casual')).load(self.player) + + + + logger.debug("preset: {}".format(preset)) + + # if no seed given, choose one + if args.seed == 0: + self.seed = random.randrange(sys.maxsize) + else: + self.seed = args.seed + logger.debug("seed: {}".format(self.seed)) + + seed4rand = self.seed + if args.raceMagic is not None: + if args.raceMagic <= 0 or args.raceMagic >= 0x10000: + print("Invalid magic") + sys.exit(-1) + seed4rand = self.seed ^ args.raceMagic + # random.seed(seed4rand) + # if no max diff, set it very high + if args.maxDifficulty: + if args.maxDifficulty == 'random': + diffs = ['easy', 'medium', 'hard', 'harder', 'hardcore', 'mania'] + self.maxDifficulty = text2diff[random.choice(diffs)] + else: + self.maxDifficulty = text2diff[args.maxDifficulty] + else: + self.maxDifficulty = infinity + # same as solver, increase max difficulty + threshold = self.maxDifficulty + epsilon = 0.001 + if self.maxDifficulty <= easy: + threshold = medium - epsilon + elif self.maxDifficulty <= medium: + threshold = hard - epsilon + elif self.maxDifficulty <= hard: + threshold = harder - epsilon + elif self.maxDifficulty <= harder: + threshold = hardcore - epsilon + elif self.maxDifficulty <= hardcore: + threshold = mania - epsilon + maxDifficulty = threshold + logger.debug("maxDifficulty: {}".format(self.maxDifficulty)) + + # handle random parameters with dynamic pool of values + (_, progSpeed) = randomMulti(args.__dict__, "progressionSpeed", speeds) + (_, progDiff) = randomMulti(args.__dict__, "progressionDifficulty", progDiffs) + (majorsSplitRandom, args.majorsSplit) = randomMulti(args.__dict__, "majorsSplit", majorsSplits) + (_, self.gravityBehaviour) = randomMulti(args.__dict__, "gravityBehaviour", gravityBehaviours) + if args.minDifficulty: + minDifficulty = text2diff[args.minDifficulty] + if progSpeed != "speedrun": + optErrMsgs.append("Minimum difficulty setting ignored, as prog speed is not speedrun") + else: + minDifficulty = 0 + + if args.area == True and args.bosses == True and args.minimizerN is not None: + forceArg('majorsSplit', 'Full', "'Majors Split' forced to Full", altValue='FullWithHUD') + if args.minimizerN == "random": + self.minimizerN = random.randint(30, 60) + logger.debug("minimizerN: {}".format(self.minimizerN)) + else: + self.minimizerN = int(args.minimizerN) + else: + self.minimizerN = None + areaRandom = False + if args.area == 'random': + areaRandom = True + args.area = bool(random.getrandbits(1)) + logger.debug("area: {}".format(args.area)) + + doorsColorsRandom = False + if args.doorsColorsRando == 'random': + doorsColorsRandom = True + args.doorsColorsRando = bool(random.getrandbits(1)) + logger.debug("doorsColorsRando: {}".format(args.doorsColorsRando)) + + bossesRandom = False + if args.bosses == 'random': + bossesRandom = True + args.bosses = bool(random.getrandbits(1)) + logger.debug("bosses: {}".format(args.bosses)) + + if args.escapeRando == 'random': + args.escapeRando = bool(random.getrandbits(1)) + logger.debug("escapeRando: {}".format(args.escapeRando)) + + if args.suitsRestriction != False and self.minimizerN is not None: + forceArg('suitsRestriction', False, "'Suits restriction' forced to off", webValue='off') + + if args.suitsRestriction == 'random': + if args.morphPlacement == 'late' and args.area == True: + forceArg('suitsRestriction', False, "'Suits restriction' forced to off", webValue='off') + else: + args.suitsRestriction = bool(random.getrandbits(1)) + logger.debug("suitsRestriction: {}".format(args.suitsRestriction)) + + if args.hideItems == 'random': + args.hideItems = bool(random.getrandbits(1)) + + if args.morphPlacement == 'random': + if args.morphPlacementList != None: + morphPlacements = args.morphPlacementList.split(',') + args.morphPlacement = random.choice(morphPlacements) + # Scavenger Hunt constraints + if args.majorsSplit == 'Scavenger': + forceArg('progressionSpeed', 'speedrun', "'Progression speed' forced to speedrun") + progSpeed = "speedrun" + forceArg('hud', True, "'VARIA HUD' forced to on", webValue='on') + if not GraphUtils.isStandardStart(args.startLocation): + forceArg('startLocation', "Landing Site", "Start Location forced to Landing Site because of Scavenger mode") + if args.morphPlacement == 'late': + forceArg('morphPlacement', 'normal', "'Morph Placement' forced to normal instead of late") + if args.scavEscape == True: + forceArg('escapeRando', True, "'Escape randomization' forced to on", webValue='on') + forceArg('noRemoveEscapeEnemies', True, "Enemies enabled during escape sequence", webArg='removeEscapeEnemies', webValue='off') + # random fill makes certain options unavailable + if (progSpeed == 'speedrun' or progSpeed == 'basic') and args.majorsSplit != 'Scavenger': + forceArg('progressionDifficulty', 'normal', "'Progression difficulty' forced to normal") + progDiff = args.progressionDifficulty + logger.debug("progressionDifficulty: {}".format(progDiff)) + + if args.strictMinors == 'random': + args.strictMinors = bool(random.getrandbits(1)) + + # in plando rando we know that the start ap is ok + if not GraphUtils.isStandardStart(args.startLocation) and args.plandoRando is None: + if args.majorsSplit in ['Major', "Chozo"]: + forceArg('hud', True, "'VARIA HUD' forced to on", webValue='on') + forceArg('noVariaTweaks', False, "'VARIA tweaks' forced to on", webValue='on') + forceArg('noLayout', False, "'Anti-softlock layout patches' forced to on", webValue='on') + forceArg('suitsRestriction', False, "'Suits restriction' forced to off", webValue='off') + forceArg('areaLayoutBase', False, "'Additional layout patches for easier navigation' forced to on", webValue='on') + possibleStartAPs, reasons = GraphUtils.getPossibleStartAPs(args.area, self.maxDifficulty, args.morphPlacement, self.player) + if args.startLocation == 'random': + if args.startLocationList != None: + # to be able to give the list in jm we had to replace ' ' with '_', do the opposite operation + startLocationList = args.startLocationList.replace('_', ' ') + startLocationList = startLocationList.split(',') + # intersection between user whishes and reality + possibleStartAPs = sorted(list(set(possibleStartAPs).intersection(set(startLocationList)))) + if len(possibleStartAPs) == 0: + optErrMsgs += ["%s : %s" % (apName, cause) for apName, cause in reasons.items() if apName in startLocationList] + optErrMsgs.append('Invalid start locations list with your settings.') + dumpErrorMsgs(args.output, optErrMsgs) + sys.exit(-1) + args.startLocation = random.choice(possibleStartAPs) + elif args.startLocation not in possibleStartAPs: + optErrMsgs.append('Invalid start location: {}. {}'.format(args.startLocation, reasons[args.startLocation])) + optErrMsgs.append('Possible start locations with these settings: {}'.format(possibleStartAPs)) + dumpErrorMsgs(args.output, optErrMsgs) + sys.exit(-1) + ap = getAccessPoint(args.startLocation) + if 'forcedEarlyMorph' in ap.Start and ap.Start['forcedEarlyMorph'] == True: + forceArg('morphPlacement', 'early', "'Morph Placement' forced to early for custom start location") + else: + if progSpeed == 'speedrun': + if args.morphPlacement == 'late': + forceArg('morphPlacement', 'normal', "'Morph Placement' forced to normal instead of late") + elif (not GraphUtils.isStandardStart(args.startLocation)) and args.morphPlacement != 'normal': + forceArg('morphPlacement', 'normal', "'Morph Placement' forced to normal for custom start location") + if args.majorsSplit == 'Chozo' and args.morphPlacement == "late": + forceArg('morphPlacement', 'normal', "'Morph Placement' forced to normal for Chozo") + #if args.patchOnly == False: + # print("SEED: " + str(self.seed)) + + # fill restrictions dict + restrictions = { 'Suits' : args.suitsRestriction, 'Morph' : args.morphPlacement, "doors": "normal" if not args.doorsColorsRando else "late" } + restrictions['MajorMinor'] = 'Full' if args.majorsSplit == 'FullWithHUD' else args.majorsSplit + if restrictions["MajorMinor"] == "Scavenger": + scavNumLocs = int(args.scavNumLocs) + if scavNumLocs == 0: + scavNumLocs = random.randint(4,16) + restrictions["ScavengerParams"] = {'numLocs':scavNumLocs, 'vanillaItems':not args.scavRandomized, 'escape': args.scavEscape} + seedCode = 'X' + if majorsSplitRandom == False: + if restrictions['MajorMinor'] == 'Full': + seedCode = 'FX' + elif restrictions['MajorMinor'] == 'Chozo': + seedCode = 'ZX' + elif restrictions['MajorMinor'] == 'Major': + seedCode = 'MX' + elif restrictions['MajorMinor'] == 'Scavenger': + seedCode = 'SX' + if args.bosses == True and bossesRandom == False: + seedCode = 'B'+seedCode + if args.doorsColorsRando == True and doorsColorsRandom == False: + seedCode = 'D'+seedCode + if args.area == True and areaRandom == False: + seedCode = 'A'+seedCode + + # output ROM name + #if args.patchOnly == False: + # self.fileName = 'VARIA_Randomizer_' + seedCode + str(self.seed) + '_' + preset + # if args.progressionSpeed != "random": + # self.fileName += "_" + args.progressionSpeed + #else: + # self.fileName = 'VARIA' # TODO : find better way to name the file (argument?) + self.fileName = output_path + seedName = self.fileName + if args.directory != '.': + self.fileName = args.directory + '/' + self.fileName + if args.noLayout == True: + RomPatches.ActivePatches[self.player] = RomPatches.TotalBase.copy() + else: + RomPatches.ActivePatches[self.player] = RomPatches.Total.copy() + RomPatches.ActivePatches[self.player].remove(RomPatches.BlueBrinstarBlueDoor) + RomPatches.ActivePatches[self.player] += GraphUtils.getGraphPatches(args.startLocation) + if self.gravityBehaviour != "Balanced": + RomPatches.ActivePatches[self.player].remove(RomPatches.NoGravityEnvProtection) + if self.gravityBehaviour == "Progressive": + RomPatches.ActivePatches[self.player].append(RomPatches.ProgressiveSuits) + if args.nerfedCharge == True: + RomPatches.ActivePatches[self.player].append(RomPatches.NerfedCharge) + if args.noVariaTweaks == False: + RomPatches.ActivePatches[self.player] += RomPatches.VariaTweaks + if self.minimizerN is not None: + RomPatches.ActivePatches[self.player].append(RomPatches.NoGadoras) + if args.minimizerTourian == True: + RomPatches.ActivePatches[self.player] += RomPatches.MinimizerTourian + missileQty = float(args.missileQty) + superQty = float(args.superQty) + powerBombQty = float(args.powerBombQty) + minorQty = int(args.minorQty) + self.energyQty = args.energyQty + if missileQty < 1: + missileQty = random.randint(1, 9) + if superQty < 1: + superQty = random.randint(1, 9) + if powerBombQty < 1: + powerBombQty = random.randint(1, 9) + if minorQty < 1: + minorQty = random.randint(25, 100) + if self.energyQty == 'random': + if args.energyQtyList != None: + # with jm can't have a list with space in it + energyQtyList = args.energyQtyList.replace('_', ' ') + energyQties = energyQtyList.split(',') + self.energyQty = random.choice(energyQties) + if self.energyQty == 'ultra sparse': + # add nerfed rainbow beam patch + RomPatches.ActivePatches[self.player].append(RomPatches.NerfedRainbowBeam) + qty = {'energy': self.energyQty, + 'minors': minorQty, + 'ammo': { 'Missile': missileQty, + 'Super': superQty, + 'PowerBomb': powerBombQty }, + 'strictMinors' : args.strictMinors } + logger.debug("quantities: {}".format(qty)) + + if len(args.superFun) > 0: + superFun = [] + for fun in args.superFun: + if fun.find('Random') != -1: + if bool(random.getrandbits(1)) == True: + superFun.append(fun[0:fun.find('Random')]) + else: + superFun.append(fun) + args.superFun = superFun + logger.debug("superFun: {}".format(args.superFun)) + + ctrlButton = ["A", "B", "X", "Y", "L", "R", "Select"] + ctrl = Controller.ControllerDict[self.player] + self.ctrlDict = { getattr(ctrl, button) : button for button in ctrlButton } + args.moonWalk = ctrl.Moonwalk + + plandoSettings = None + if args.plandoRando is not None: + forceArg('progressionSpeed', 'speedrun', "'Progression Speed' forced to speedrun") + progSpeed = 'speedrun' + forceArg('majorsSplit', 'Full', "'Majors Split' forced to Full") + forceArg('morphPlacement', 'normal', "'Morph Placement' forced to normal") + forceArg('progressionDifficulty', 'normal', "'Progression difficulty' forced to normal") + progDiff = 'normal' + args.plandoRando = json.loads(args.plandoRando) + RomPatches.ActivePatches[self.player] = args.plandoRando["patches"] + DoorsManager.unserialize(args.plandoRando["doors"]) + plandoSettings = {"locsItems": args.plandoRando['locsItems'], "forbiddenItems": args.plandoRando['forbiddenItems']} + randoSettings = RandoSettings(self.maxDifficulty, progSpeed, progDiff, qty, + restrictions, args.superFun, args.runtimeLimit_s, + plandoSettings, minDifficulty) + + # print some parameters for jm's stats + if args.jm == True: + print("startLocation:{}".format(args.startLocation)) + print("progressionSpeed:{}".format(progSpeed)) + print("majorsSplit:{}".format(args.majorsSplit)) + print("morphPlacement:{}".format(args.morphPlacement)) + + dotFile = None + if args.area == True: + if args.dot == True: + dotFile = args.directory + '/' + seedName + '.dot' + RomPatches.ActivePatches[self.player] += RomPatches.AreaBaseSet + if args.areaLayoutBase == False: + RomPatches.ActivePatches[self.player] += RomPatches.AreaComfortSet + if args.doorsColorsRando == True: + RomPatches.ActivePatches[self.player].append(RomPatches.RedDoorsMissileOnly) + graphSettings = GraphSettings(args.startLocation, args.area, args.lightArea, args.bosses, + args.escapeRando, self.minimizerN, dotFile, args.doorsColorsRando, args.allowGreyDoors, + args.plandoRando["transitions"] if args.plandoRando != None else None) + + if args.plandoRando is None: + DoorsManager.setDoorsColor(self.player) + + self.escapeAttr = None + if args.patchOnly == False: + try: + self.randoExec = RandoExec(seedName, args.vcr, randoSettings, graphSettings, self.player) + self.container = self.randoExec.randomize() + # if we couldn't find an area layout then the escape graph is not created either + # and getDoorConnections will crash if random escape is activated. + stuck = False + if not stuck or args.vcr == True: + self.doors = GraphUtils.getDoorConnections(self.randoExec.areaGraph, + args.area, args.bosses, + args.escapeRando) + escapeAttr = self.randoExec.areaGraph.EscapeAttributes if args.escapeRando else None + if escapeAttr is not None: + escapeAttr['patches'] = [] + if args.noRemoveEscapeEnemies == True: + escapeAttr['patches'].append("Escape_Rando_Enable_Enemies") + if args.scavEscape == True: + escapeAttr['patches'].append('Escape_Scavenger') + except Exception as e: + import traceback + traceback.print_exc(file=sys.stdout) + dumpErrorMsg(args.output, "Error: {}".format(e)) + sys.exit(-1) + else: + stuck = False + itemLocs = [] + progItemLocs = None + if stuck == True: + dumpErrorMsg(args.output, self.randoExec.errorMsg) + print("Can't generate " + self.fileName + " with the given parameters: {}".format(self.randoExec.errorMsg)) + # in vcr mode we still want the seed to be generated to analyze it + if args.vcr == False: + sys.exit(-1) + #if args.patchOnly == False: + # randoExec.postProcessItemLocs(itemLocs, args.hideItems) + + def PatchRom(self, outputFilename, customPatchApply = None): + args = self.args + optErrMsgs = self.optErrMsgs + + # choose on animal patch + if args.animals == True: + animalsPatches = ['animal_enemies.ips', 'animals.ips', 'draygonimals.ips', 'escapimals.ips', + 'gameend.ips', 'grey_door_animals.ips', 'low_timer.ips', 'metalimals.ips', + 'phantoonimals.ips', 'ridleyimals.ips'] + if args.escapeRando == False: + args.patches.append(random.choice(animalsPatches)) + args.patches.append("Escape_Animals_Change_Event") + else: + optErrMsgs.append("Ignored animals surprise because of escape randomization") + # # transform itemLocs in our usual dict(location, item), exclude minors, we'll get them with the solver + # locsItems = {} + # for itemLoc in itemLocs: + # locName = itemLoc.Location.Name + # itemType = itemLoc.Item.Type + # if itemType in ['Missile', 'Super', 'PowerBomb']: + # continue + # locsItems[locName] = itemType + # if args.debug == True: + # for loc in sorted(locsItems.keys()): + # print('{:>50}: {:>16} '.format(loc, locsItems[loc])) + + # if args.plandoRando != None: + # with open(args.output, 'w') as jsonFile: + # json.dump({"itemLocs": [il.json() for il in itemLocs], "errorMsg": randoExec.errorMsg}, jsonFile) + # sys.exit(0) + + # # generate extended stats + # if args.extStatsFilename != None: + # with open(args.extStatsFilename, 'a') as extStatsFile: + # skillPreset = os.path.splitext(os.path.basename(args.paramsFileName))[0] + # if args.fakeRandoPreset is not None: + # randoPreset = args.fakeRandoPreset + # else: + # randoPreset = os.path.splitext(os.path.basename(args.randoPreset))[0] + # db.DB.dumpExtStatsItems(skillPreset, randoPreset, locsItems, extStatsFile) + + try: + # args.rom is not None: generate local rom named filename.sfc with args.rom as source + # args.output is not None: generate local json named args.output + if args.rom is not None: + # patch local rom + romFileName = args.rom + shutil.copyfile(romFileName, outputFilename) + romPatcher = RomPatcher(outputFilename, args.raceMagic, False, self.player) + else: + romPatcher = RomPatcher(magic=args.raceMagic) + + if args.hud == True or args.majorsSplit == "FullWithHUD": + args.patches.append("varia_hud.ips") + if args.patchOnly == False: + romPatcher.applyIPSPatches(args.startLocation, args.patches, + args.noLayout, self.gravityBehaviour, + args.area, args.bosses, args.areaLayoutBase, + args.noVariaTweaks, args.nerfedCharge, self.energyQty == 'ultra sparse', + self.escapeAttr, self.minimizerN, args.minimizerTourian, + args.doorsColorsRando) + else: + # from customizer permalink, apply previously generated seed ips first + if args.seedIps != None: + romPatcher.applyIPSPatch(args.seedIps) + + romPatcher.addIPSPatches(args.patches) + # don't color randomize custom ships + args.shift_ship_palette = False + + if customPatchApply != None: + customPatchApply(romPatcher) + + # we have to write ips to ROM before doing our direct modifications which will rewrite some parts (like in credits), + # but in web mode we only want to generate a global ips at the end +# if args.rom != None: +# romPatcher.commitIPS() + if args.patchOnly == False: +# romPatcher.writeItemsLocs(itemLocs) +# romPatcher.writeSplitLocs(args.majorsSplit, itemLocs, progItemLocs) + romPatcher.writeItemsNumber() + romPatcher.writeSeed(self.seed) # lol if race mode +# romPatcher.writeSpoiler(itemLocs, progItemLocs) +# romPatcher.writeRandoSettings(self.randoExec.randoSettings, itemLocs) + romPatcher.writeDoorConnections(self.doors) + romPatcher.writeVersion(displayedVersion) + if self.ctrlDict is not None: + romPatcher.writeControls(self.ctrlDict) + if args.moonWalk == True: + romPatcher.enableMoonWalk() + if args.patchOnly == False: + romPatcher.writeMagic() + romPatcher.writeMajorsSplit(args.majorsSplit) + # if args.palette == True: + # paletteSettings = { + # "global_shift": None, + # "individual_suit_shift": None, + # "individual_tileset_shift": None, + # "match_ship_and_power": None, + # "seperate_enemy_palette_groups": None, + # "match_room_shift_with_boss": None, + # "shift_tileset_palette": None, + # "shift_boss_palettes": None, + # "shift_suit_palettes": None, + # "shift_enemy_palettes": None, + # "shift_beam_palettes": None, + # "shift_ship_palette": None, + # "min_degree": None, + # "max_degree": None, + # "invert": None, + # "no_blue_door_palette": None + # } + # for param in paletteSettings: + # paletteSettings[param] = getattr(args, param) + # PaletteRando(romPatcher, paletteSettings, args.sprite).randomize() + + # web mode, generate only one ips at the end + if args.rom == None: + romPatcher.commitIPS() + romPatcher.end() + if args.patchOnly == False: + if len(optErrMsgs) > 0: +# optErrMsgs.append(randoExec.errorMsg) + msg = joinErrorMsgs(optErrMsgs) + else: +# msg = randoExec.errorMsg + msg = '' + else: + msg = '' + if args.rom is None: # web mode + data = romPatcher.romFile.data + self.fileName = '{}.sfc'.format(self.fileName) + data["fileName"] = self.fileName + # error msg in json to be displayed by the web site + data["errorMsg"] = msg + # replaced parameters to update stats in database + if len(self.forcedArgs) > 0: + data["forcedArgs"] = self.forcedArgs + with open(outputFilename, 'w') as jsonFile: + json.dump(data, jsonFile) + else: # CLI mode + if msg != "": + print(msg) + except Exception as e: + import traceback + traceback.print_exc(file=sys.stdout) + msg = "Error patching {}: ({}: {})".format(outputFilename, type(e).__name__, e) + dumpErrorMsg(args.output, msg) + sys.exit(-1) + +# if stuck == True: +# print("Rom generated for debug purpose: {}".format(self.fileName)) +# else: + # print("Rom generated: {}".format(self.fileName)) diff --git a/worlds/sm/variaRandomizer/rom/__init__.py b/worlds/sm/variaRandomizer/rom/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/worlds/sm/variaRandomizer/rom/ips.py b/worlds/sm/variaRandomizer/rom/ips.py new file mode 100644 index 00000000..dcce44a2 --- /dev/null +++ b/worlds/sm/variaRandomizer/rom/ips.py @@ -0,0 +1,244 @@ +import itertools + +from utils.utils import range_union + +# adapted from ips-util for python 3.2 (https://pypi.org/project/ips-util/) +class IPS_Patch(object): + def __init__(self, patchDict=None): + self.records = [] + self.truncate_length = None + self.max_size = 0 + if patchDict is not None: + for addr, data in patchDict.items(): + byteData = bytearray(data) + self.add_record(addr, byteData) + + def toDict(self): + ret = {} + for record in self.records: + if 'rle_count' in record: + ret[record['address']] = [int.from_bytes(record['data'],'little')]*record['rle_count'] + else: + ret[record['address']] = [int(b) for b in record['data']] + return ret + + @staticmethod + def load(filename): + loaded_patch = IPS_Patch() + with open(filename, 'rb') as file: + header = file.read(5) + if header != b'PATCH': + raise Exception('Not a valid IPS patch file!') + while True: + address_bytes = file.read(3) + if address_bytes == b'EOF': + break + address = int.from_bytes(address_bytes, byteorder='big') + length = int.from_bytes(file.read(2), byteorder='big') + rle_count = 0 + if length == 0: + rle_count = int.from_bytes(file.read(2), byteorder='big') + length = 1 + data = file.read(length) + if rle_count > 0: + loaded_patch.add_rle_record(address, data, rle_count) + else: + loaded_patch.add_record(address, data) + + truncate_bytes = file.read(3) + if len(truncate_bytes) == 3: + loaded_patch.set_truncate_length(int.from_bytes(truncate_bytes, byteorder='big')) + return loaded_patch + + @staticmethod + def create(original_data, patched_data): + # The heuristics for optimizing a patch were chosen with reference to + # the source code of Flips: https://github.com/Alcaro/Flips + + patch = IPS_Patch() + + run_in_progress = False + current_run_start = 0 + current_run_data = bytearray() + + runs = [] + + if len(original_data) > len(patched_data): + patch.set_truncate_length(len(patched_data)) + original_data = original_data[:len(patched_data)] + elif len(original_data) < len(patched_data): + original_data += bytes([0] * (len(patched_data) - len(original_data))) + + if original_data[-1] == 0 and patched_data[-1] == 0: + patch.add_record(len(patched_data) - 1, bytes([0])) + + for index, (original, patched) in enumerate(zip(original_data, patched_data)): + if not run_in_progress: + if original != patched: + run_in_progress = True + current_run_start = index + current_run_data = bytearray([patched]) + else: + if original == patched: + runs.append((current_run_start, current_run_data)) + run_in_progress = False + else: + current_run_data.append(patched) + if run_in_progress: + runs.append((current_run_start, current_run_data)) + + for start, data in runs: + if start == int.from_bytes(b'EOF', byteorder='big'): + start -= 1 + data = bytes([patched_data[start - 1]]) + data + + grouped_byte_data = list([ + {'val': key, 'count': sum(1 for _ in group), 'is_last': False} + for key,group in itertools.groupby(data) + ]) + + grouped_byte_data[-1]['is_last'] = True + + record_in_progress = bytearray() + pos = start + + for group in grouped_byte_data: + if len(record_in_progress) > 0: + # We don't want to interrupt a record in progress with a new header unless + # this group is longer than two complete headers. + if group['count'] > 13: + patch.add_record(pos, record_in_progress) + pos += len(record_in_progress) + record_in_progress = bytearray() + + patch.add_rle_record(pos, bytes([group['val']]), group['count']) + pos += group['count'] + else: + record_in_progress += bytes([group['val']] * group['count']) + elif (group['count'] > 3 and group['is_last']) or group['count'] > 8: + # We benefit from making this an RLE record if the length is at least 8, + # or the length is at least 3 and we know it to be the last part of this diff. + + # Make sure not to overflow the maximum length. Split it up if necessary. + remaining_length = group['count'] + while remaining_length > 0xffff: + patch.add_rle_record(pos, bytes([group['val']]), 0xffff) + remaining_length -= 0xffff + pos += 0xffff + + patch.add_rle_record(pos, bytes([group['val']]), remaining_length) + pos += remaining_length + else: + # Just begin a new standard record. + record_in_progress += bytes([group['val']] * group['count']) + + if len(record_in_progress) > 0xffff: + patch.add_record(pos, record_in_progress[:0xffff]) + record_in_progress = record_in_progress[0xffff:] + pos += 0xffff + + # Finalize any record still in progress. + if len(record_in_progress) > 0: + patch.add_record(pos, record_in_progress) + + return patch + + def add_record(self, address, data): + if address == int.from_bytes(b'EOF', byteorder='big'): + raise RuntimeError('Start address {0:x} is invalid in the IPS format. Please shift your starting address back by one byte to avoid it.'.format(address)) + if address > 0xffffff: + raise RuntimeError('Start address {0:x} is too large for the IPS format. Addresses must fit into 3 bytes.'.format(address)) + if len(data) > 0xffff: + raise RuntimeError('Record with length {0} is too large for the IPS format. Records must be less than 65536 bytes.'.format(len(data))) + if len(data) == 0: # ignore empty records + return + record = {'address': address, 'data': data, 'size':len(data)} + self.appendRecord(record) + + def add_rle_record(self, address, data, count): + if address == int.from_bytes(b'EOF', byteorder='big'): + raise RuntimeError('Start address {0:x} is invalid in the IPS format. Please shift your starting address back by one byte to avoid it.'.format(address)) + if address > 0xffffff: + raise RuntimeError('Start address {0:x} is too large for the IPS format. Addresses must fit into 3 bytes.'.format(address)) + if count > 0xffff: + raise RuntimeError('RLE record with length {0} is too large for the IPS format. RLE records must be less than 65536 bytes.'.format(count)) + if len(data) != 1: + raise RuntimeError('Data for RLE record must be exactly one byte! Received {0}.'.format(data)) + record = {'address': address, 'data': data, 'rle_count': count, 'size': count} + self.appendRecord(record) + + def appendRecord(self, record): + sz = record['address'] + record['size'] + if sz > self.max_size: + self.max_size = sz + self.records.append(record) + + def set_truncate_length(self, truncate_length): + self.truncate_length = truncate_length + + def encode(self): + encoded_bytes = bytearray() + + encoded_bytes += 'PATCH'.encode('ascii') + + for record in self.records: + encoded_bytes += record['address'].to_bytes(3, byteorder='big') + if 'rle_count' in record: + encoded_bytes += (0).to_bytes(2, byteorder='big') + encoded_bytes += record['rle_count'].to_bytes(2, byteorder='big') + else: + encoded_bytes += len(record['data']).to_bytes(2, byteorder='big') + encoded_bytes += record['data'] + + encoded_bytes += 'EOF'.encode('ascii') + + if self.truncate_length is not None: + encoded_bytes += self.truncate_length.to_bytes(3, byteorder='big') + + return encoded_bytes + + # save patch into IPS file + def save(self, path): + with open(path, 'wb') as ipsFile: + ipsFile.write(self.encode()) + + # applies patch on an existing bytearray + def apply(self, in_data): + out_data = bytearray(in_data) + + for record in self.records: + if record['address'] >= len(out_data): + out_data += bytes([0] * (record['address'] - len(out_data) + 1)) + + if 'rle_count' in record: + out_data[record['address'] : record['address'] + record['rle_count']] = b''.join([record['data']] * record['rle_count']) + else: + out_data[record['address'] : record['address'] + len(record['data'])] = record['data'] + + if self.truncate_length is not None: + out_data = out_data[:self.truncate_length] + + return out_data + + # applies patch on an opened file + def applyFile(self, handle): + for record in self.records: + handle.seek(record['address']) + if 'rle_count' in record: + handle.write(bytearray(b'').join([record['data']]) * record['rle_count']) + else: + handle.write(record['data']) + + # appends an IPS_Patch on top of this one + def append(self, patch): + if patch.truncate_length is not None and (self.truncate_length is None or patch.truncate_length > self.truncate_length): + self.set_truncate_length(patch.truncate_length) + for record in patch.records: + if record['size'] > 0: # ignore empty records + self.appendRecord(record) + + # gets address ranges written to by this patch + def getRanges(self): + def getRange(record): + return range(record['address'], record['address']+record['size']) + return range_union([getRange(record) for record in self.records]) diff --git a/worlds/sm/variaRandomizer/rom/rom.py b/worlds/sm/variaRandomizer/rom/rom.py new file mode 100644 index 00000000..52b1fd04 --- /dev/null +++ b/worlds/sm/variaRandomizer/rom/rom.py @@ -0,0 +1,64 @@ +import base64 + +from rom.ips import IPS_Patch + +def pc_to_snes(pcaddress): + snesaddress=(((pcaddress<<1)&0x7F0000)|(pcaddress&0x7FFF)|0x8000)|0x800000 + return snesaddress + +def snes_to_pc(B): + B_1 = B >> 16 + B_2 = B & 0xFFFF + # return 0 if invalid LoROM address + if B_1 < 0x80 or B_1 > 0xFFFFFF or B_2 < 0x8000: + return 0 + A_1 = (B_1 - 0x80) >> 1 + # if B_1 is even, remove most significant bit + A_2 = B_2 & 0x7FFF if (B_1 & 1) == 0 else B_2 + + return (A_1 << 16) | A_2 + +class ROM(object): + def readWord(self, address=None): + return self.readBytes(2, address) + + def readByte(self, address=None): + return self.readBytes(1, address) + + def readBytes(self, size, address=None): + if address != None: + self.seek(address) + return int.from_bytes(self.read(size), byteorder='little') + + def writeWord(self, word, address=None): + self.writeBytes(word, 2, address) + + def writeByte(self, byte, address=None): + self.writeBytes(byte, 1, address) + + def writeBytes(self, value, size, address=None): + if address != None: + self.seek(address) + self.write(value.to_bytes(size, byteorder='little')) + +class RealROM(ROM): + def __init__(self, name): + self.romFile = open(name, "rb+") + self.address = 0 + + def seek(self, address): + self.address = address + self.romFile.seek(address) + + def write(self, bytes): + self.romFile.write(bytes) + + def read(self, byteCount): + return self.romFile.read(byteCount) + + def close(self): + self.romFile.close() + + def ipsPatch(self, ipsPatches): + for ips in ipsPatches: + ips.applyFile(self) diff --git a/worlds/sm/variaRandomizer/rom/rom_patches.py b/worlds/sm/variaRandomizer/rom/rom_patches.py new file mode 100644 index 00000000..9fe68c9f --- /dev/null +++ b/worlds/sm/variaRandomizer/rom/rom_patches.py @@ -0,0 +1,130 @@ +from logic.smbool import SMBool + +# layout patches added by randomizers +class RomPatches: + #### Patches definitions + + ### Layout + # blue door to access the room with etank+missile + BlueBrinstarBlueDoor = 10 + # missile in the first room is a major item and accessible and ceiling is a minor + BlueBrinstarMissile = 11 + # shot block instead of bomb blocks for spazer access + SpazerShotBlock = 20 + # climb back up red tower from bottom no matter what + RedTowerLeftPassage = 21 + # exit red tower top to crateria + RedTowerBlueDoors = 22 + # shot block in crumble blocks at early supers + EarlySupersShotBlock = 23 + # brinstar reserve area door blue + BrinReserveBlueDoors = 24 + # red tower top PB door to hellway + HellwayBlueDoor = 25 + # etecoon supers blue door + EtecoonSupersBlueDoor = 26 + # shot block to exit hi jump area + HiJumpShotBlock = 30 + # access main upper norfair without anything + CathedralEntranceWallJump = 31 + # graph blue doors + HiJumpAreaBlueDoor = 32 + SpeedAreaBlueDoors = 33 + # LN start + LowerNorfairPBRoomHeatDisable = 34 + FirefleasRemoveFune = 35 + # moat bottom block + MoatShotBlock = 41 + #graph+forgotten hiway anti softlock + SpongeBathBlueDoor = 42 + # forgotten hiway anti softlock + EastOceanPlatforms = 43 + # maridia + MaridiaTubeOpened = 51 + MamaTurtleBlueDoor = 52 + # ws start + WsEtankBlueDoor = 53 + ## Area rando patches + # remove crumble block for reverse lower norfair door access + SingleChamberNoCrumble = 101 + # remove green gates for reverse maridia access + AreaRandoGatesBase = 102 + # remove crab green gate in maridia and blue gate in green brinstar + AreaRandoGatesOther = 103 + # disable Green Hill Yellow, Noob Bridge Green, Coude Yellow, and Kronic Boost yellow doors + AreaRandoBlueDoors = 104 + # crateria key hunter yellow, green pirates shaft red + AreaRandoMoreBlueDoors = 105 + # croc green+grey doors + CrocBlueDoors = 106 + # maridia crab shaft AP door + CrabShaftBlueDoor = 107 + # wrap door from sand halls left to under botwoon + MaridiaSandWarp = 108 + ## Minimizer Patches + NoGadoras = 200 + TourianSpeedup = 201 + OpenZebetites = 202 + + ### Other + # Gravity no longer protects from environmental damage (heat, spikes...) + NoGravityEnvProtection = 1000 + # Wrecked Ship etank accessible when Phantoon is alive + WsEtankPhantoonAlive = 1001 + # Lower Norfair chozo (vanilla access to GT/Screw Area) : disable space jump check + LNChozoSJCheckDisabled = 1002 + # Progressive suits patch, mutually exclusive with NoGravityEnvProtection + ProgressiveSuits = 1003 + # Nerfed charge beam available from the start + NerfedCharge = 1004 + # Nerfed rainbow beam for ultra sparse energy qty + NerfedRainbowBeam = 1005 + # Red doors open with one missile, and don't react to supers: part of door color rando + RedDoorsMissileOnly = 1006 + + ### Hacks + # rotation hack + RotationHack = 10000 + + #### Patch sets + # total randomizer + TotalBase = [ BlueBrinstarBlueDoor, RedTowerBlueDoors, NoGravityEnvProtection ] + # tournament and full + TotalLayout = [ MoatShotBlock, EarlySupersShotBlock, + SpazerShotBlock, RedTowerLeftPassage, + HiJumpShotBlock, CathedralEntranceWallJump ] + + Total = TotalBase + TotalLayout + + # casual + TotalCasual = [ BlueBrinstarMissile ] + Total + + # area rando patch set + AreaBaseSet = [ SingleChamberNoCrumble, AreaRandoGatesBase, + AreaRandoBlueDoors, AreaRandoMoreBlueDoors, + CrocBlueDoors, CrabShaftBlueDoor, MaridiaSandWarp ] + AreaComfortSet = [ AreaRandoGatesOther, SpongeBathBlueDoor, EastOceanPlatforms ] + AreaSet = AreaBaseSet + AreaComfortSet + + # VARIA specific patch set + VariaTweaks = [ WsEtankPhantoonAlive, LNChozoSJCheckDisabled ] + + # Tourian speedup in minimizer mode + MinimizerTourian = [ TourianSpeedup, OpenZebetites ] + + # dessyreqt randomizer + Dessy = [] + + ### Active patches + ActivePatches = {} + + @staticmethod + def has(player, patch): + return SMBool(patch in RomPatches.ActivePatches[player]) + + @staticmethod + def setDefaultPatches(startLocation): + # called by the isolver in seedless mode. + # activate only layout patch (the most common one), red tower blue doors and the startLocation's patches. + from graph.graph_utils import GraphUtils + RomPatches.ActivePatches[0] = [RomPatches.RedTowerBlueDoors] + RomPatches.TotalLayout + GraphUtils.getGraphPatches(startLocation) diff --git a/worlds/sm/variaRandomizer/rom/rompatcher.py b/worlds/sm/variaRandomizer/rom/rompatcher.py new file mode 100644 index 00000000..471f982a --- /dev/null +++ b/worlds/sm/variaRandomizer/rom/rompatcher.py @@ -0,0 +1,1093 @@ +import os, random, re + +from rando.Items import ItemManager +from rom.ips import IPS_Patch +from utils.doorsmanager import DoorsManager +from graph.graph_utils import GraphUtils, getAccessPoint, locIdsByAreaAddresses +from logic.logic import Logic +from rom.rom import RealROM, snes_to_pc +from patches.patchaccess import PatchAccess +from utils.parameters import appDir +import utils.log + +def getWord(w): + return (w & 0x00FF, (w & 0xFF00) >> 8) + +class RomPatcher: + # possible patches. see patches asm source if applicable and available for more information + IPSPatches = { + # applied on all seeds + 'Standard': [ + # faster MB cutscene transitions + 'Mother_Brain_Cutscene_Edits', + # "Balanced" suit mode + 'Removes_Gravity_Suit_heat_protection', + # door ASM to skip G4 cutscene when all 4 bosses are dead + 'g4_skip.ips', + # basepatch is generated from https://github.com/lordlou/SMBasepatch + 'basepatch.ips' + ], + # VARIA tweaks + 'VariaTweaks' : ['WS_Etank', 'LN_Chozo_SpaceJump_Check_Disable', 'ln_chozo_platform.ips', 'bomb_torizo.ips'], + # anti-softlock/game opening layout patches + 'Layout': ['dachora.ips', 'early_super_bridge.ips', 'high_jump.ips', 'moat.ips', 'spospo_save.ips', + 'nova_boost_platform.ips', 'red_tower.ips', 'spazer.ips', + 'brinstar_map_room.ips', 'kraid_save.ips', 'mission_impossible.ips'], + # comfort patches + 'Optional': ['rando_speed.ips', 'Infinite_Space_Jump', 'refill_before_save.ips', + 'spinjumprestart.ips', 'elevators_doors_speed.ips', 'No_Music', 'random_music.ips', + # animals + 'animal_enemies.ips', 'animals.ips', 'draygonimals.ips', + 'escapimals.ips', 'gameend.ips', 'grey_door_animals.ips', + 'low_timer.ips', 'metalimals.ips', 'phantoonimals.ips', 'ridleyimals.ips', + 'Escape_Animals_Change_Event', # ...end animals + # vanilla behaviour restore + 'remove_elevators_doors_speed.ips', + 'varia_hud.ips'], + # base patchset+optional layout for area rando + 'Area': ['area_rando_layout.ips', 'door_transition.ips', 'area_rando_doors.ips', + 'Sponge_Bath_Blinking_Door', 'east_ocean.ips', 'area_rando_warp_door.ips', + 'crab_shaft.ips', 'Save_Crab_Shaft', 'Save_Main_Street', 'no_demo.ips'], + # patches for boss rando + 'Bosses': ['door_transition.ips', 'no_demo.ips'], + # patches for escape rando + 'Escape' : ['rando_escape.ips', 'rando_escape_ws_fix.ips', 'door_transition.ips'], + # patches for minimizer with fast Tourian + 'MinimizerTourian': ['minimizer_tourian.ips', 'open_zebetites.ips'], + # patches for door color rando + 'DoorsColors': ['beam_doors_plms.ips', 'beam_doors_gfx.ips', 'red_doors.ips'] + } + + def __init__(self, romFileName=None, magic=None, plando=False, player=0): + self.log = utils.log.get('RomPatcher') + self.romFileName = romFileName + self.race = None + self.romFile = RealROM(romFileName) + #if magic is not None: + # from rom.race_mode import RaceModePatcher + # self.race = RaceModePatcher(self, magic, plando) + # IPS_Patch objects list + self.ipsPatches = [] + # loc name to alternate address. we still write to original + # address to help the RomReader. + self.altLocsAddresses = {} + # specific fixes for area rando connections + self.roomConnectionSpecific = { + # fix scrolling sky when transitioning to west ocean + 0x93fe: self.patchWestOcean + } + self.doorConnectionSpecific = { + # get out of kraid room: reload CRE + 0x91ce: self.forceRoomCRE, + # get out of croc room: reload CRE + 0x93ea: self.forceRoomCRE + } + self.patchAccess = PatchAccess() + self.player = player + + def end(self): + self.romFile.close() + + def writeItemCode(self, item, visibility, address): + itemCode = ItemManager.getItemTypeCode(item, visibility) + if self.race is None: + self.romFile.writeWord(itemCode, address) + else: + self.race.writeItemCode(itemCode, address) + + def getLocAddresses(self, loc): + ret = [loc.Address] + if loc.Name in self.altLocsAddresses: + ret.append(self.altLocsAddresses[loc.Name]) + return ret + + def writeItem(self, itemLoc): + loc = itemLoc.Location + if loc.isBoss(): + raise ValueError('Cannot write Boss location') + #print('write ' + itemLoc.Item.Type + ' at ' + loc.Name) + for addr in self.getLocAddresses(loc): + self.writeItemCode(itemLoc.Item, loc.Visibility, addr) + + def writeItemsLocs(self, itemLocs): + self.nItems = 0 + for itemLoc in itemLocs: + loc = itemLoc.Location + item = itemLoc.Item + if loc.isBoss(): + continue + self.writeItem(itemLoc) + if item.Category != 'Nothing': + self.nItems += 1 + if loc.Name == 'Morphing Ball': + self.patchMorphBallEye(item) + + def writeSplitLocs(self, split, itemLocs, progItemLocs): + majChozoCheck = lambda itemLoc: itemLoc.Item.Class == split and itemLoc.Location.isClass(split) + fullCheck = lambda itemLoc: itemLoc.Location.Id is not None + splitChecks = { + 'Full': fullCheck, + 'Scavenger': fullCheck, + 'Major': majChozoCheck, + 'Chozo': majChozoCheck, + 'FullWithHUD': lambda itemLoc: itemLoc.Item.Category not in ['Energy', 'Ammo', 'Boss'] + } + itemLocCheck = lambda itemLoc: itemLoc.Item.Category != "Nothing" and splitChecks[split](itemLoc) + for area,addr in locIdsByAreaAddresses.items(): + locs = [il.Location for il in itemLocs if itemLocCheck(il) and il.Location.GraphArea == area] + self.log.debug("writeSplitLocs. area="+area) + self.log.debug(str([loc.Name for loc in locs])) + self.romFile.seek(addr) + for loc in locs: + self.romFile.writeByte(loc.Id) + self.romFile.writeByte(0xff) + if split == "Scavenger": + # write required major item order + self.romFile.seek(snes_to_pc(0xA1F5D8)) + for itemLoc in progItemLocs: + self.romFile.writeWord((itemLoc.Location.Id << 8) | itemLoc.Location.HUD) + # bogus loc ID | "HUNT OVER" index + self.romFile.writeWord(0xff10) + + # trigger morph eye enemy on whatever item we put there, + # not just morph ball + def patchMorphBallEye(self, item): +# print('Eye item = ' + item.Type) + isAmmo = item.Category == 'Ammo' + # category to check + if ItemManager.isBeam(item): + cat = 0xA8 # collected beams + elif item.Type == 'ETank': + cat = 0xC4 # max health + elif item.Type == 'Reserve': + cat = 0xD4 # max reserves + elif item.Type == 'Missile': + cat = 0xC8 # max missiles + elif item.Type == 'Super': + cat = 0xCC # max supers + elif item.Type == 'PowerBomb': + cat = 0xD0 # max PBs + else: + cat = 0xA4 # collected items + # comparison/branch instruction + # the branch is taken if we did NOT collect item yet + if item.Category == 'Energy' or isAmmo: + comp = 0xC9 # CMP (immediate) + branch = 0x30 # BMI + else: + comp = 0x89 # BIT (immediate) + branch = 0xF0 # BEQ + # what to compare to + if item.Type == 'ETank': + operand = 0x65 # < 100 + elif item.Type == 'Reserve' or isAmmo: + operand = 0x1 # < 1 + elif ItemManager.isBeam(item): + operand = item.BeamBits + else: + operand = item.ItemBits + self.patchMorphBallCheck(0x1410E6, cat, comp, operand, branch) # eye main AI + self.patchMorphBallCheck(0x1468B2, cat, comp, operand, branch) # head main AI + + def patchMorphBallCheck(self, offset, cat, comp, operand, branch): + # actually patch enemy AI + self.romFile.writeByte(cat, offset) + self.romFile.writeByte(comp, offset+2) + self.romFile.writeWord(operand) + self.romFile.writeByte(branch) + + def writeItemsNumber(self): + # write total number of actual items for item percentage patch (patch the patch) + for addr in [0x5E64E, 0x5E6AB]: + self.romFile.writeByte(self.nItems, addr) + + def addIPSPatches(self, patches): + for patchName in patches: + self.applyIPSPatch(patchName) + + def writePlmTable(self, plms, area, bosses, startLocation): + # called when saving a plando + try: + if bosses == True or area == True: + plms.append('WS_Save_Blinking_Door') + + doors = self.getStartDoors(plms, area, None) + self.writeDoorsColor(doors, self.player) + self.applyStartAP(startLocation, plms, doors) + + self.applyPLMs(plms) + except Exception as e: + raise Exception("Error patching {}. ({})".format(self.romFileName, e)) + + def applyIPSPatches(self, startLocation="Landing Site", + optionalPatches=[], noLayout=False, suitsMode="Balanced", + area=False, bosses=False, areaLayoutBase=False, + noVariaTweaks=False, nerfedCharge=False, nerfedRainbowBeam=False, + escapeAttr=None, minimizerN=None, minimizerTourian=True, + doorsColorsRando=False): + try: + # apply standard patches + stdPatches = [] + plms = [] + # apply race mode first because it fills the rom with a bunch of crap + if self.race is not None: + stdPatches.append('race_mode.ips') + stdPatches += RomPatcher.IPSPatches['Standard'][:] + if self.race is not None: + stdPatches.append('race_mode_credits.ips') + if suitsMode != "Balanced": + stdPatches.remove('Removes_Gravity_Suit_heat_protection') + if suitsMode == "Progressive": + stdPatches.append('progressive_suits.ips') + if nerfedCharge == True: + stdPatches.append('nerfed_charge.ips') + if nerfedRainbowBeam == True: + stdPatches.append('nerfed_rainbow_beam.ips') + if bosses == True or area == True: + stdPatches += ["WS_Main_Open_Grey", "WS_Save_Active"] + plms.append('WS_Save_Blinking_Door') + if bosses == True: + stdPatches.append("Phantoon_Eye_Door") + if area == True or doorsColorsRando == True: + stdPatches.append("Enable_Backup_Saves") + if 'varia_hud.ips' in optionalPatches: + # varia hud has its own variant of g4_skip for scavenger mode, + # it can also make demos glitch out + stdPatches.remove("g4_skip.ips") + self.applyIPSPatch("no_demo.ips") + for patchName in stdPatches: + self.applyIPSPatch(patchName) + + if noLayout == False: + # apply layout patches + for patchName in RomPatcher.IPSPatches['Layout']: + self.applyIPSPatch(patchName) + if noVariaTweaks == False: + # VARIA tweaks + for patchName in RomPatcher.IPSPatches['VariaTweaks']: + self.applyIPSPatch(patchName) + + # apply optional patches + for patchName in optionalPatches: + if patchName in RomPatcher.IPSPatches['Optional']: + self.applyIPSPatch(patchName) + + # random escape + if escapeAttr is not None: + for patchName in RomPatcher.IPSPatches['Escape']: + self.applyIPSPatch(patchName) + # animals and timer + self.applyEscapeAttributes(escapeAttr, plms) + + # apply area patches + if area == True: + for patchName in RomPatcher.IPSPatches['Area']: + if areaLayoutBase == True and patchName in ['area_rando_layout.ips', 'Sponge_Bath_Blinking_Door', 'east_ocean.ips']: + continue + self.applyIPSPatch(patchName) + if areaLayoutBase == True: + self.applyIPSPatch('area_rando_layout_base.ips') + + + else: + self.applyIPSPatch('area_ids_alt.ips') + if bosses == True: + for patchName in RomPatcher.IPSPatches['Bosses']: + self.applyIPSPatch(patchName) + if minimizerN is not None: + self.applyIPSPatch('minimizer_bosses.ips') + if minimizerTourian == True: + for patchName in RomPatcher.IPSPatches['MinimizerTourian']: + self.applyIPSPatch(patchName) + doors = self.getStartDoors(plms, area, minimizerN) + if doorsColorsRando == True: + for patchName in RomPatcher.IPSPatches['DoorsColors']: + self.applyIPSPatch(patchName) + self.writeDoorsColor(doors, self.player) + self.applyStartAP(startLocation, plms, doors) + self.applyPLMs(plms) + except Exception as e: + raise Exception("Error patching {}. ({})".format(self.romFileName, e)) + + def applyIPSPatch(self, patchName, patchDict=None, ipsDir=None): + if patchDict is None: + patchDict = self.patchAccess.getDictPatches() + # print("Apply patch {}".format(patchName)) + if patchName in patchDict: + patch = IPS_Patch(patchDict[patchName]) + else: + # look for ips file + if ipsDir is None: + patch = IPS_Patch.load(self.patchAccess.getPatchPath(patchName)) + else: + patch = IPS_Patch.load(os.path.join(appDir, ipsDir, patchName)) + self.ipsPatches.append(patch) + + def applyIPSPatchDict(self, patchDict): + for patchName in patchDict.keys(): + # print("Apply patch {}".format(patchName)) + patch = IPS_Patch(patchDict[patchName]) + self.ipsPatches.append(patch) + + def getStartDoors(self, plms, area, minimizerN): + doors = [0x10] # red brin elevator + def addBlinking(name): + key = 'Blinking[{}]'.format(name) + if key in self.patchAccess.getDictPatches(): + self.applyIPSPatch(key) + if key in self.patchAccess.getAdditionalPLMs(): + plms.append(key) + if area == True: + plms += ['Maridia Sand Hall Seal', "Save_Main_Street", "Save_Crab_Shaft"] + for accessPoint in Logic.accessPoints: + if accessPoint.Internal == True or accessPoint.Boss == True: + continue + addBlinking(accessPoint.Name) + addBlinking("West Sand Hall Left") + addBlinking("Below Botwoon Energy Tank Right") + if minimizerN is not None: + # add blinking doors inside and outside boss rooms + for accessPoint in Logic.accessPoints: + if accessPoint.Boss == True: + addBlinking(accessPoint.Name) + return doors + + def applyStartAP(self, apName, plms, doors): + ap = getAccessPoint(apName) + # if start loc is not Ceres or Landing Site, or the ceiling loc picked up before morph loc, + # Zebes will be awake and morph loc item will disappear. + # this PLM ensures the item will be here whenever zebes awakes + plms.append('Morph_Zebes_Awake') + (w0, w1) = getWord(ap.Start['spawn']) + if 'doors' in ap.Start: + doors += ap.Start['doors'] + doors.append(0x0) + addr = 0x10F200 + patch = [w0, w1] + doors + assert (addr + len(patch)) < 0x10F210, "Stopped before new_game overwrite" + patchDict = { + 'StartAP': { + addr: patch + }, + } + self.applyIPSPatch('StartAP', patchDict) + # handle custom saves + if 'save' in ap.Start: + self.applyIPSPatch(ap.Start['save']) + plms.append(ap.Start['save']) + # handle optional rom patches + if 'rom_patches' in ap.Start: + for patch in ap.Start['rom_patches']: + self.applyIPSPatch(patch) + + def applyEscapeAttributes(self, escapeAttr, plms): + # timer + escapeTimer = escapeAttr['Timer'] + if escapeTimer is not None: + minute = int(escapeTimer / 60) + second = escapeTimer % 60 + minute = int(minute / 10) * 16 + minute % 10 + second = int(second / 10) * 16 + second % 10 + patchDict = {'Escape_Timer': {0x1E21:[second, minute]}} + self.applyIPSPatch('Escape_Timer', patchDict) + # animals door to open + if escapeAttr['Animals'] is not None: + escapeOpenPatches = { + 'Green Brinstar Main Shaft Top Left':'Escape_Animals_Open_Brinstar', + 'Business Center Mid Left':"Escape_Animals_Open_Norfair", + 'Crab Hole Bottom Right':"Escape_Animals_Open_Maridia", + } + if escapeAttr['Animals'] in escapeOpenPatches: + plms.append("WS_Map_Grey_Door") + self.applyIPSPatch(escapeOpenPatches[escapeAttr['Animals']]) + else: + plms.append("WS_Map_Grey_Door_Openable") + else: + plms.append("WS_Map_Grey_Door") + # optional patches (enemies, scavenger) + for patch in escapeAttr['patches']: + self.applyIPSPatch(patch) + + # adds ad-hoc "IPS patches" for additional PLM tables + def applyPLMs(self, plms): + # compose a dict (room, state, door) => PLM array + # 'PLMs' being a 6 byte arrays + plmDict = {} + # we might need to update locations addresses on the fly + plmLocs = {} # room key above => loc name + additionalPLMs = self.patchAccess.getAdditionalPLMs() + for p in plms: + plm = additionalPLMs[p] + room = plm['room'] + state = 0 + if 'state' in plm: + state = plm['state'] + door = 0 + if 'door' in plm: + door = plm['door'] + k = (room, state, door) + if k not in plmDict: + plmDict[k] = [] + plmDict[k] += plm['plm_bytes_list'] + if 'locations' in plm: + locList = plm['locations'] + for locName, locIndex in locList: + plmLocs[(k, locIndex)] = locName + # make two patches out of this dict + plmTblAddr = 0x7E9A0 # moves downwards + plmPatchData = [] + roomTblAddr = 0x7EC00 # moves upwards + roomPatchData = [] + plmTblOffset = plmTblAddr + def appendPlmBytes(bytez): + nonlocal plmPatchData, plmTblOffset + plmPatchData += bytez + plmTblOffset += len(bytez) + def addRoomPatchData(bytez): + nonlocal roomPatchData, roomTblAddr + roomPatchData = bytez + roomPatchData + roomTblAddr -= len(bytez) + for roomKey, plmList in plmDict.items(): + entryAddr = plmTblOffset + roomData = [] + for i in range(len(plmList)): + plmBytes = plmList[i] + assert len(plmBytes) == 6, "Invalid PLM entry for roomKey " + str(roomKey) + ": PLM list len is " + str(len(plmBytes)) + if (roomKey, i) in plmLocs: + self.altLocsAddresses[plmLocs[(roomKey, i)]] = plmTblOffset + appendPlmBytes(plmBytes) + appendPlmBytes([0x0, 0x0]) # list terminator + def appendRoomWord(w, data): + (w0, w1) = getWord(w) + data += [w0, w1] + for i in range(3): + appendRoomWord(roomKey[i], roomData) + appendRoomWord(entryAddr, roomData) + addRoomPatchData(roomData) + # write room table terminator + addRoomPatchData([0x0] * 8) + assert plmTblOffset < roomTblAddr, "Spawn PLM table overlap" + patchDict = { + "PLM_Spawn_Tables" : { + plmTblAddr: plmPatchData, + roomTblAddr: roomPatchData + } + } + self.applyIPSPatch("PLM_Spawn_Tables", patchDict) + + def commitIPS(self): + self.romFile.ipsPatch(self.ipsPatches) + + def writeSeed(self, seed): + random.seed(seed) + seedInfo = random.randint(0, 0xFFFF) + seedInfo2 = random.randint(0, 0xFFFF) + self.romFile.writeWord(seedInfo, 0x2FFF00) + self.romFile.writeWord(seedInfo2) + + def writeMagic(self): + if self.race is not None: + self.race.writeMagic() + + def writeMajorsSplit(self, majorsSplit): + address = 0x17B6C + splits = { + 'Chozo': 'Z', + 'Major': 'M', + 'FullWithHUD': 'H', + 'Scavenger': 'S' + } + char = splits.get(majorsSplit, 'F') + self.romFile.writeByte(ord(char), address) + + def getItemQty(self, itemLocs, itemType): + return len([il for il in itemLocs if il.Accessible and il.Item.Type == itemType]) + + def getMinorsDistribution(self, itemLocs): + dist = {} + minQty = 100 + minors = ['Missile', 'Super', 'PowerBomb'] + for m in minors: + # in vcr mode if the seed has stuck we may not have these items, return at least 1 + q = float(max(self.getItemQty(itemLocs, m), 1)) + dist[m] = {'Quantity' : q } + if q < minQty: + minQty = q + for m in minors: + dist[m]['Proportion'] = dist[m]['Quantity']/minQty + + return dist + + def getAmmoPct(self, minorsDist): + q = 0 + for m,v in minorsDist.items(): + q += v['Quantity'] + return 100*q/66 + + def writeRandoSettings(self, settings, itemLocs): + dist = self.getMinorsDistribution(itemLocs) + totalAmmo = sum(d['Quantity'] for ammo,d in dist.items()) + totalItemLocs = sum(1 for il in itemLocs if il.Accessible and not il.Location.isBoss()) + totalNothing = sum(1 for il in itemLocs if il.Accessible and il.Item.Category == 'Nothing') + totalEnergy = self.getItemQty(itemLocs, 'ETank')+self.getItemQty(itemLocs, 'Reserve') + totalMajors = max(totalItemLocs - totalEnergy - totalAmmo - totalNothing, 0) + address = 0x2736C0 + value = "{:>2}".format(totalItemLocs) + line = " ITEM LOCATIONS %s " % value + self.writeCreditsStringBig(address, line, top=True) + address += 0x40 + + line = " item locations ............ %s " % value + self.writeCreditsStringBig(address, line, top=False) + address += 0x40 + + maj = "{:>2}".format(int(totalMajors)) + htanks = "{:>2}".format(int(totalEnergy)) + ammo = "{:>2}".format(int(totalAmmo)) + blank = "{:>2}".format(int(totalNothing)) + line = " MAJ %s EN %s AMMO %s BLANK %s " % (maj, htanks, ammo, blank) + self.writeCreditsStringBig(address, line, top=True) + address += 0x40 + line = " maj %s en %s ammo %s blank %s " % (maj, htanks, ammo, blank) + self.writeCreditsStringBig(address, line, top=False) + address += 0x40 + + pbs = "{:>2}".format(int(dist['PowerBomb']['Quantity'])) + miss = "{:>2}".format(int(dist['Missile']['Quantity'])) + supers = "{:>2}".format(int(dist['Super']['Quantity'])) + line = " AMMO PACKS MI %s SUP %s PB %s " % (miss, supers, pbs) + self.writeCreditsStringBig(address, line, top=True) + address += 0x40 + + line = " ammo packs mi %s sup %s pb %s " % (miss, supers, pbs) + self.writeCreditsStringBig(address, line, top=False) + address += 0x40 + + etanks = "{:>2}".format(int(self.getItemQty(itemLocs, 'ETank'))) + reserves = "{:>2}".format(int(self.getItemQty(itemLocs, 'Reserve'))) + line = " HEALTH TANKS E %s R %s " % (etanks, reserves) + self.writeCreditsStringBig(address, line, top=True) + address += 0x40 + + line = " health tanks ...... e %s r %s " % (etanks, reserves) + self.writeCreditsStringBig(address, line, top=False) + address += 0x80 + + value = " "+settings.progSpeed.upper() + line = " PROGRESSION SPEED ....%s " % value.rjust(8, '.') + self.writeCreditsString(address, 0x04, line) + address += 0x40 + + line = " PROGRESSION DIFFICULTY %s " % settings.progDiff.upper() + self.writeCreditsString(address, 0x04, line) + address += 0x80 # skip item distrib title + + param = (' SUITS RESTRICTION ........%s', 'Suits') + line = param[0] % ('. ON' if settings.restrictions[param[1]] == True else ' OFF') + self.writeCreditsString(address, 0x04, line) + address += 0x40 + + value = " "+settings.restrictions['Morph'].upper() + line = " MORPH PLACEMENT .....%s" % value.rjust(9, '.') + self.writeCreditsString(address, 0x04, line) + address += 0x40 + + for superFun in [(' SUPER FUN COMBAT .........%s', 'Combat'), + (' SUPER FUN MOVEMENT .......%s', 'Movement'), + (' SUPER FUN SUITS ..........%s', 'Suits')]: + line = superFun[0] % ('. ON' if superFun[1] in settings.superFun else ' OFF') + self.writeCreditsString(address, 0x04, line) + address += 0x40 + + value = "%.1f %.1f %.1f" % (dist['Missile']['Proportion'], dist['Super']['Proportion'], dist['PowerBomb']['Proportion']) + line = " AMMO DISTRIBUTION %s " % value + self.writeCreditsStringBig(address, line, top=True) + address += 0x40 + + line = " ammo distribution %s " % value + self.writeCreditsStringBig(address, line, top=False) + address += 0x40 + + # write ammo/energy pct + address = 0x273C40 + (ammoPct, energyPct) = (int(self.getAmmoPct(dist)), int(100*totalEnergy/18)) + line = " AVAILABLE AMMO {:>3}% ENERGY {:>3}%".format(ammoPct, energyPct) + self.writeCreditsStringBig(address, line, top=True) + address += 0x40 + line = " available ammo {:>3}% energy {:>3}%".format(ammoPct, energyPct) + self.writeCreditsStringBig(address, line, top=False) + + def writeSpoiler(self, itemLocs, progItemLocs=None): + # keep only majors + fItemLocs = [il for il in itemLocs if il.Item.Category not in ['Ammo', 'Nothing', 'Energy', 'Boss']] + # add location of the first instance of each minor + for t in ['Missile', 'Super', 'PowerBomb']: + itLoc = None + if progItemLocs is not None: + itLoc = next((il for il in progItemLocs if il.Item.Type == t), None) + if itLoc is None: + itLoc = next((il for il in itemLocs if il.Item.Type == t), None) + if itLoc is not None: # in vcr mode if the seed has stucked we may not have these minors + fItemLocs.append(itLoc) + regex = re.compile(r"[^A-Z0-9\.,'!: ]+") + + itemLocs = {} + for iL in fItemLocs: + itemLocs[iL.Item.Name] = iL.Location.Name + + def prepareString(s, isItem=True): + s = s.upper() + # remove chars not displayable + s = regex.sub('', s) + # remove space before and after + s = s.strip() + # limit to 30 chars, add one space before + # pad to 32 chars + if isItem is True: + s = " " + s[0:30] + s = s.ljust(32) + else: + s = " " + s[0:30] + " " + s = " " + s.rjust(31, '.') + + return s + + isRace = self.race is not None + startCreditAddress = 0x2f5240 + address = startCreditAddress + if isRace: + addr = address - 0x40 + data = [0x007f, 0x007f, 0x007f, 0x007f, 0x007f, 0x007f, 0x007f, 0x007f, 0x007f, 0x1008, 0x1013, 0x1004, 0x100c, 0x007f, 0x100b, 0x100e, 0x1002, 0x1000, 0x1013, 0x1008, 0x100e, 0x100d, 0x1012, 0x007f, 0x007f, 0x007f, 0x007f, 0x007f, 0x007f, 0x007f, 0x007f, 0x007f] + for i in range(0x20): + w = data[i] + self.romFile.seek(addr) + self.race.writeWordMagic(w) + addr += 0x2 + # standard item order + items = ["Missile", "Super Missile", "Power Bomb", + "Charge Beam", "Ice Beam", "Wave Beam", "Spazer", "Plasma Beam", + "Varia Suit", "Gravity Suit", + "Morph Ball", "Bomb", "Spring Ball", "Screw Attack", + "Hi-Jump Boots", "Space Jump", "Speed Booster", + "Grappling Beam", "X-Ray Scope"] + displayNames = {} + if progItemLocs is not None: + # reorder it with progression indices + prog = ord('A') + idx = 0 + progNames = [il.Item.Name for il in progItemLocs if il.Item.Category != 'Boss'] + for i in range(len(progNames)): + item = progNames[i] + if item in items and item not in displayNames: + items.remove(item) + items.insert(idx, item) + displayNames[item] = chr(prog + i) + ": " + item + idx += 1 + for item in items: + # super fun removes items + if item not in itemLocs: + continue + display = item + if item in displayNames: + display = displayNames[item] + itemName = prepareString(display) + locationName = prepareString(itemLocs[item], isItem=False) + + self.writeCreditsString(address, 0x04, itemName, isRace) + self.writeCreditsString((address + 0x40), 0x18, locationName, isRace) + + address += 0x80 + + # we need 19 items displayed, if we've removed majors, add some blank text + while address < startCreditAddress + len(items)*0x80: + self.writeCreditsString(address, 0x04, prepareString(""), isRace) + self.writeCreditsString((address + 0x40), 0x18, prepareString(""), isRace) + + address += 0x80 + + self.patchBytes(address, [0, 0, 0, 0], isRace) + + def writeCreditsString(self, address, color, string, isRace=False): + array = [self.convertCreditsChar(color, char) for char in string] + self.patchBytes(address, array, isRace) + + def writeCreditsStringBig(self, address, string, top=True): + array = [self.convertCreditsCharBig(char, top) for char in string] + self.patchBytes(address, array) + + def convertCreditsChar(self, color, byte): + if byte == ' ': + ib = 0x7f + elif byte == '!': + ib = 0x1F + elif byte == ':': + ib = 0x1E + elif byte == '\\': + ib = 0x1D + elif byte == '_': + ib = 0x1C + elif byte == ',': + ib = 0x1B + elif byte == '.': + ib = 0x1A + else: + ib = ord(byte) - 0x41 + + if ib == 0x7F: + return 0x007F + else: + return (color << 8) + ib + + def convertCreditsCharBig(self, byte, top=True): + # from: https://jathys.zophar.net/supermetroid/kejardon/TextFormat.txt + # 2-tile high characters: + # A-P = $XX20-$XX2F(TOP) and $XX30-$XX3F(BOTTOM) + # Q-Z = $XX40-$XX49(TOP) and $XX50-$XX59(BOTTOM) + # ' = $XX4A, $XX7F + # " = $XX4B, $XX7F + # . = $XX7F, $XX5A + # 0-9 = $XX60-$XX69(TOP) and $XX70-$XX79(BOTTOM) + # % = $XX6A, $XX7A + + if byte == ' ': + ib = 0x7F + elif byte == "'": + if top == True: + ib = 0x4A + else: + ib = 0x7F + elif byte == '"': + if top == True: + ib = 0x4B + else: + ib = 0x7F + elif byte == '.': + if top == True: + ib = 0x7F + else: + ib = 0x5A + elif byte == '%': + if top == True: + ib = 0x6A + else: + ib = 0x7A + + byte = ord(byte) + if byte >= ord('A') and byte <= ord('P'): + ib = byte - 0x21 + elif byte >= ord('Q') and byte <= ord('Z'): + ib = byte - 0x11 + elif byte >= ord('a') and byte <= ord('p'): + ib = byte - 0x31 + elif byte >= ord('q') and byte <= ord('z'): + ib = byte - 0x21 + elif byte >= ord('0') and byte <= ord('9'): + if top == True: + ib = byte + 0x30 + else: + ib = byte + 0x40 + + return ib + + def patchBytes(self, address, array, isRace=False): + self.romFile.seek(address) + for w in array: + if not isRace: + self.romFile.writeWord(w) + else: + self.race.writeWordMagic(w) + + # write area randomizer transitions to ROM + # doorConnections : a list of connections. each connection is a dictionary describing + # - where to write in the ROM : + # DoorPtr : door pointer to write to + # - what to write in the ROM : + # RoomPtr, direction, bitflag, cap, screen, distanceToSpawn : door properties + # * if SamusX and SamusY are defined in the dict, custom ASM has to be written + # to reposition samus, and call doorAsmPtr if non-zero. The written Door ASM + # property shall point to this custom ASM. + # * if not, just write doorAsmPtr as the door property directly. + def writeDoorConnections(self, doorConnections): + asmAddress = 0x7F800 + for conn in doorConnections: + # write door ASM for transition doors (code and pointers) +# print('Writing door connection ' + conn['ID']) + doorPtr = conn['DoorPtr'] + roomPtr = conn['RoomPtr'] + if doorPtr in self.doorConnectionSpecific: + self.doorConnectionSpecific[doorPtr](roomPtr) + if roomPtr in self.roomConnectionSpecific: + self.roomConnectionSpecific[roomPtr](doorPtr) + self.romFile.seek(0x10000 + doorPtr) + + # write room ptr + self.romFile.writeWord(roomPtr & 0xFFFF) + + # write bitflag (if area switch we have to set bit 0x40, and remove it if same area) + self.romFile.writeByte(conn['bitFlag']) + + # write direction + self.romFile.writeByte(conn['direction']) + + # write door cap x + self.romFile.writeByte(conn['cap'][0]) + + # write door cap y + self.romFile.writeByte(conn['cap'][1]) + + # write screen x + self.romFile.writeByte(conn['screen'][0]) + + # write screen y + self.romFile.writeByte(conn['screen'][1]) + + # write distance to spawn + self.romFile.writeWord(conn['distanceToSpawn'] & 0xFFFF) + + # write door asm + asmPatch = [] + # call original door asm ptr if needed + if conn['doorAsmPtr'] != 0x0000: + # endian convert + (D0, D1) = (conn['doorAsmPtr'] & 0x00FF, (conn['doorAsmPtr'] & 0xFF00) >> 8) + asmPatch += [ 0x20, D0, D1 ] # JSR $doorAsmPtr + # special ASM hook point for VARIA needs when taking the door (used for animals) + if 'exitAsmPtr' in conn: + # endian convert + (D0, D1) = (conn['exitAsmPtr'] & 0x00FF, (conn['exitAsmPtr'] & 0xFF00) >> 8) + asmPatch += [ 0x20, D0, D1 ] # JSR $exitAsmPtr + # incompatible transition + if 'SamusX' in conn: + # endian convert + (X0, X1) = (conn['SamusX'] & 0x00FF, (conn['SamusX'] & 0xFF00) >> 8) + (Y0, Y1) = (conn['SamusY'] & 0x00FF, (conn['SamusY'] & 0xFF00) >> 8) + # force samus position + # see door_transition.asm. assemble it to print routines SNES addresses. + asmPatch += [ 0x20, 0x00, 0xF6 ] # JSR incompatible_doors + asmPatch += [ 0xA9, X0, X1 ] # LDA #$SamusX ; fixed Samus X position + asmPatch += [ 0x8D, 0xF6, 0x0A ] # STA $0AF6 ; update Samus X position in memory + asmPatch += [ 0xA9, Y0, Y1 ] # LDA #$SamusY ; fixed Samus Y position + asmPatch += [ 0x8D, 0xFA, 0x0A ] # STA $0AFA ; update Samus Y position in memory + else: + # still give I-frames + asmPatch += [ 0x20, 0x40, 0xF6 ] # JSR giveiframes + # return + asmPatch += [ 0x60 ] # RTS + self.romFile.writeWord(asmAddress & 0xFFFF) + + self.romFile.seek(asmAddress) + for byte in asmPatch: + self.romFile.writeByte(byte) + # print("asmAddress=%x" % asmAddress) + # print("asmPatch=" + str(["%02x" % b for b in asmPatch])) + + asmAddress += len(asmPatch) + # update room state header with song changes + # TODO just do an IPS patch for this as it is completely static + # this would get rid of both 'song' and 'songs' fields + # as well as this code + if 'song' in conn: + for addr in conn["songs"]: + self.romFile.seek(0x70000 + addr) + self.romFile.writeByte(conn['song']) + self.romFile.writeByte(0x5) + + # change BG table to avoid scrolling sky bug when transitioning to west ocean + def patchWestOcean(self, doorPtr): + self.romFile.writeWord(doorPtr, 0x7B7BB) + + # forces CRE graphics refresh when exiting kraid's or croc room + def forceRoomCRE(self, roomPtr, creFlag=0x2): + # Room ptr in bank 8F + CRE flag offset + offset = 0x70000 + roomPtr + 0x8 + self.romFile.writeByte(creFlag, offset) + + buttons = { + "Select" : [0x00, 0x20], + "A" : [0x80, 0x00], + "B" : [0x00, 0x80], + "X" : [0x40, 0x00], + "Y" : [0x00, 0x40], + "L" : [0x20, 0x00], + "R" : [0x10, 0x00], + "None" : [0x00, 0x00] + } + + controls = { + "Shoot" : [0xb331, 0x1722d], + "Jump" : [0xb325, 0x17233], + "Dash" : [0xb32b, 0x17239], + "Item Select" : [0xb33d, 0x17245], + "Item Cancel" : [0xb337, 0x1723f], + "Angle Up" : [0xb343, 0x1724b], + "Angle Down" : [0xb349, 0x17251] + } + + # write custom contols to ROM. + # controlsDict : possible keys are "Shot", "Jump", "Dash", "ItemSelect", "ItemCancel", "AngleUp", "AngleDown" + # possible values are "A", "B", "X", "Y", "L", "R", "Select", "None" + def writeControls(self, controlsDict): + for ctrl, button in controlsDict.items(): + if ctrl not in RomPatcher.controls: + raise ValueError("Invalid control name : " + str(ctrl)) + if button not in RomPatcher.buttons: + raise ValueError("Invalid button name : " + str(button)) + for addr in RomPatcher.controls[ctrl]: + self.romFile.writeByte(RomPatcher.buttons[button][0], addr) + self.romFile.writeByte(RomPatcher.buttons[button][1]) + + def writePlandoAddresses(self, locations): + self.romFile.seek(0x2F6000) + for loc in locations: + self.romFile.writeWord(loc.Address & 0xFFFF) + + # fill remaining addresses with 0xFFFF + maxLocsNumber = 128 + for i in range(0, maxLocsNumber-len(locations)): + self.romFile.writeWord(0xFFFF) + + def writePlandoTransitions(self, transitions, doorsPtrs, maxTransitions): + self.romFile.seek(0x2F6100) + + for (src, dest) in transitions: + self.romFile.writeWord(doorsPtrs[src]) + self.romFile.writeWord(doorsPtrs[dest]) + + # fill remaining addresses with 0xFFFF + for i in range(0, maxTransitions-len(transitions)): + self.romFile.writeWord(0xFFFF) + self.romFile.writeWord(0xFFFF) + + def enableMoonWalk(self): + # replace STZ with STA since A is non-zero at this point + self.romFile.writeByte(0x8D, 0xB35D) + + def setOamTile(self, nth, middle, newTile, y=0xFC): + # an oam entry is made of five bytes: (s000000 xxxxxxxxx) (yyyyyyyy) (YXpp000t tttttttt) + + # after and before the middle of the screen is not handle the same + if nth >= middle: + x = (nth - middle) * 0x08 + else: + x = 0x200 - (0x08 * (middle - nth)) + + self.romFile.writeWord(x) + self.romFile.writeByte(y) + self.romFile.writeWord(0x3100+newTile) + + def writeVersion(self, version, addRotation=False): + # max 32 chars + + # new oamlist address in free space at the end of bank 8C + self.romFile.writeWord(0xF3E9, 0x5a0e3) + self.romFile.writeWord(0xF3E9, 0x5a0e9) + + # string length + versionLength = len(version) + if addRotation: + rotationLength = len('rotation') + length = versionLength + rotationLength + else: + length = versionLength + self.romFile.writeWord(length, 0x0673e9) + versionMiddle = int(versionLength / 2) + versionLength % 2 + + # oams + for (i, char) in enumerate(version): + self.setOamTile(i, versionMiddle, char2tile[char]) + + if addRotation: + rotationMiddle = int(rotationLength / 2) + rotationLength % 2 + for (i, char) in enumerate('rotation'): + self.setOamTile(i, rotationMiddle, char2tile[char], y=0x8e) + + def writeDoorsColor(self, doors, player): + DoorsManager.writeDoorsColor(self.romFile, doors, player) + +# tile number in tileset +char2tile = { + '-': 207, + 'a': 208, + '.': 243, + '0': 244 +} +for i in range(1, ord('z')-ord('a')+1): + char2tile[chr(ord('a')+i)] = char2tile['a']+i +for i in range(1, ord('9')-ord('0')+1): + char2tile[chr(ord('0')+i)] = char2tile['0']+i + +class MessageBox(object): + def __init__(self, rom): + self.rom = rom + + # in message boxes the char a is at offset 0xe0 in the tileset + self.char2tile = {'1': 0x00, '2': 0x01, '3': 0x02, '4': 0x03, '5': 0x04, '6': 0x05, '7': 0x06, '8': 0x07, '9': 0x08, '0': 0x09, + ' ': 0x4e, '-': 0xcf, 'a': 0xe0, '.': 0xfa, ',': 0xfb, '`': 0xfc, "'": 0xfd, '?': 0xfe, '!': 0xff} + for i in range(1, ord('z')-ord('a')+1): + self.char2tile[chr(ord('a')+i)] = self.char2tile['a']+i + + # add 0x0c/0x06 to offsets as there's 12/6 bytes before the strings, string length is either 0x13/0x1a + self.offsets = { + 'ETank': (0x2877f+0x0c, 0x13), + 'Missile': (0x287bf+0x06, 0x1a), + 'Super': (0x288bf+0x06, 0x1a), + 'PowerBomb': (0x289bf+0x06, 0x1a), + 'Grapple': (0x28abf+0x06, 0x1a), + 'XRayScope': (0x28bbf+0x06, 0x1a), + 'Varia': (0x28cbf+0x0c, 0x13), + 'SpringBall': (0x28cff+0x0c, 0x13), + 'Morph': (0x28d3f+0x0c, 0x13), + 'ScrewAttack': (0x28d7f+0x0c, 0x13), + 'HiJump': (0x28dbf+0x0c, 0x13), + 'SpaceJump': (0x28dff+0x0c, 0x13), + 'SpeedBooster': (0x28e3f+0x06, 0x1a), + 'Charge': (0x28f3f+0x0c, 0x13), + 'Ice': (0x28f7f+0x0c, 0x13), + 'Wave': (0x28fbf+0x0c, 0x13), + 'Spazer': (0x28fff+0x0c, 0x13), + 'Plasma': (0x2903f+0x0c, 0x13), + 'Bomb': (0x2907f+0x06, 0x1a), + 'Reserve': (0x294ff+0x0c, 0x13), + 'Gravity': (0x2953f+0x0c, 0x13) + } + + def updateMessage(self, box, message, vFlip=False, hFlip=False): + (address, oldLength) = self.offsets[box] + newLength = len(message) + assert newLength <= oldLength, "string '{}' is too long, max {}".format(message, oldLength) + padding = oldLength - newLength + paddingLeft = int(padding / 2) + paddingRight = int(padding / 2) + paddingRight += padding % 2 + + attr = self.getAttr(vFlip, hFlip) + + # write spaces for padding left + for i in range(paddingLeft): + self.writeChar(address, ' ') + address += 0x02 + # write message + for char in message: + self.writeChar(address, char) + address += 0x01 + self.updateAttr(attr, address) + address += 0x01 + # write spaces for padding right + for i in range(paddingRight): + self.writeChar(address, ' ') + address += 0x02 + + def writeChar(self, address, char): + self.rom.writeByte(self.char2tile[char], address) + + def getAttr(self, vFlip, hFlip): + # vanilla is 0x28: + byte = 0x28 + if vFlip: + byte |= 0b10000000 + if hFlip: + byte |= 0b01000000 + return byte + + def updateAttr(self, byte, address): + self.rom.writeByte(byte, address) diff --git a/worlds/sm/variaRandomizer/solver.py b/worlds/sm/variaRandomizer/solver.py new file mode 100644 index 00000000..88738146 --- /dev/null +++ b/worlds/sm/variaRandomizer/solver.py @@ -0,0 +1,224 @@ +#!/usr/bin/python3 +import sys, argparse + +from solver.interactiveSolver import InteractiveSolver +from solver.standardSolver import StandardSolver +from solver.conf import Conf +import utils.log + +def interactiveSolver(args): + # to init, requires interactive/romFileName/presetFileName/output parameters in standard/plando mode + # to init, requires interactive/presetFileName/output parameters in seedless mode + # to iterate, requires interactive/state/[loc]/[item]/action/output parameters in item scope + # to iterate, requires interactive/state/[startPoint]/[endPoint]/action/output parameters in area scope + if args.action == 'init': + # init + if args.mode != 'seedless' and args.romFileName == None: + print("Missing romFileName parameter for {} mode".format(args.mode)) + sys.exit(1) + + if args.presetFileName == None or args.output == None: + print("Missing preset or output parameter") + sys.exit(1) + + solver = InteractiveSolver(args.output, args.logic) + solver.initialize(args.mode, args.romFileName, args.presetFileName, magic=args.raceMagic, fill=args.fill, startLocation=args.startLocation) + else: + # iterate + params = {} + if args.scope == 'common': + if args.action == "save": + params["lock"] = args.lock + params["escapeTimer"] = args.escapeTimer + elif args.action == "randomize": + params["minorQty"] = args.minorQty + params["energyQty"] = args.energyQty + params["forbiddenItems"] = args.forbiddenItems.split(',') if args.forbiddenItems is not None else [] + elif args.scope == 'item': + if args.state == None or args.action == None or args.output == None: + print("Missing state/action/output parameter") + sys.exit(1) + if args.action in ["add", "replace"]: + if args.mode not in ['seedless', 'race', 'debug'] and args.loc == None: + print("Missing loc parameter when using action add for item") + sys.exit(1) + if args.mode == 'plando': + if args.item == None: + print("Missing item parameter when using action add in plando/suitless mode") + sys.exit(1) + params = {'loc': args.loc, 'item': args.item, 'hide': args.hide} + elif args.action == "remove": + if args.loc != None: + params = {'loc': args.loc} + elif args.item != None: + params = {'item': args.item} + else: + params = {'count': args.count} + elif args.action == "toggle": + params = {'item': args.item} + elif args.scope == 'area': + if args.state == None or args.action == None or args.output == None: + print("Missing state/action/output parameter") + sys.exit(1) + if args.action == "add": + if args.startPoint == None or args.endPoint == None: + print("Missing start or end point parameter when using action add for item") + sys.exit(1) + params = {'startPoint': args.startPoint, 'endPoint': args.endPoint} + if args.action == "remove" and args.startPoint != None: + params = {'startPoint': args.startPoint} + elif args.scope == 'door': + if args.state == None or args.action == None or args.output == None: + print("Missing state/action/output parameter") + sys.exit(1) + if args.action == "replace": + if args.doorName is None or args.newColor is None: + print("Missing doorName or newColor parameter when using action replace for door") + sys.exit(1) + params = {'doorName': args.doorName, 'newColor': args.newColor} + elif args.action == "toggle": + if args.doorName is None: + print("Missing doorName parameter when using action toggle for door") + sys.exit(1) + params = {'doorName': args.doorName} + elif args.scope == 'dump': + if args.action == "import": + if args.dump is None: + print("Missing dump parameter when import a dump") + params = {'dump': args.dump} + params["debug"] = args.mode == 'debug' + + solver = InteractiveSolver(args.output, args.logic) + solver.iterate(args.state, args.scope, args.action, params) + +def standardSolver(args): + if args.romFileName is None: + print("Parameter --romFileName mandatory when not in interactive mode") + sys.exit(1) + + if args.difficultyTarget is None: + difficultyTarget = Conf.difficultyTarget + else: + difficultyTarget = args.difficultyTarget + + if args.pickupStrategy is None: + pickupStrategy = Conf.itemsPickup + else: + pickupStrategy = args.pickupStrategy + + # itemsForbidden is like that: [['Varia'], ['Reserve'], ['Gravity']], fix it + args.itemsForbidden = [item[0] for item in args.itemsForbidden] + + solver = StandardSolver(args.romFileName, args.presetFileName, difficultyTarget, + pickupStrategy, args.itemsForbidden, type=args.type, + firstItemsLog=args.firstItemsLog, extStatsFilename=args.extStatsFilename, + extStatsStep=args.extStatsStep, + displayGeneratedPath=args.displayGeneratedPath, + outputFileName=args.output, magic=args.raceMagic, + checkDuplicateMajor=args.checkDuplicateMajor, vcr=args.vcr, + runtimeLimit_s=args.runtimeLimit_s) + + solver.solveRom() + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Random Metroid Solver") + parser.add_argument('--romFileName', '-r', help="the input rom", nargs='?', + default=None, dest="romFileName") + parser.add_argument('--preset', '-p', help="the preset file", nargs='?', + default=None, dest='presetFileName') + parser.add_argument('--difficultyTarget', '-t', + help="the difficulty target that the solver will aim for", + dest='difficultyTarget', nargs='?', default=None, type=int) + parser.add_argument('--pickupStrategy', '-s', help="Pickup strategy for the Solver", + dest='pickupStrategy', nargs='?', default=None, + choices=['all', 'any']) + parser.add_argument('--itemsForbidden', '-f', help="Item not picked up during solving", + dest='itemsForbidden', nargs='+', default=[], action='append') + + parser.add_argument('--type', '-y', help="web or console", dest='type', nargs='?', + default='console', choices=['web', 'console']) + parser.add_argument('--checkDuplicateMajor', dest="checkDuplicateMajor", action='store_true', + help="print a warning if the same major is collected more than once") + parser.add_argument('--debug', '-d', help="activate debug logging", dest='debug', action='store_true') + parser.add_argument('--firstItemsLog', '-1', + help="path to file where for each item type the first time it was found and where will be written (spoilers!)", + nargs='?', default=None, type=str, dest='firstItemsLog') + parser.add_argument('--ext_stats', help="Generate extended stats", + nargs='?', default=None, dest='extStatsFilename') + parser.add_argument('--ext_stats_step', help="what extended stats to generate", + nargs='?', default=None, dest='extStatsStep', type=int) + parser.add_argument('--displayGeneratedPath', '-g', help="display the generated path (spoilers!)", + dest='displayGeneratedPath', action='store_true') + parser.add_argument('--race', help="Race mode magic number", dest='raceMagic', type=int) + parser.add_argument('--vcr', help="Generate VCR output file", dest='vcr', action='store_true') + # standard/interactive, web site + parser.add_argument('--output', '-o', help="When called from the website, contains the result of the solver", + dest='output', nargs='?', default=None) + # interactive, web site + parser.add_argument('--interactive', '-i', help="Activate interactive mode for the solver", + dest='interactive', action='store_true') + parser.add_argument('--state', help="JSON file of the Solver state (used in interactive mode)", + dest="state", nargs='?', default=None) + parser.add_argument('--loc', help="Name of the location to action on (used in interactive mode)", + dest="loc", nargs='?', default=None) + parser.add_argument('--action', help="Pickup item at location, remove last pickedup location, clear all (used in interactive mode)", + dest="action", nargs="?", default=None, choices=['init', 'add', 'remove', 'clear', 'get', 'save', 'replace', 'randomize', 'toggle', 'import']) + parser.add_argument('--item', help="Name of the item to place in plando mode (used in interactive mode)", + dest="item", nargs='?', default=None) + parser.add_argument('--hide', help="Hide the item to place in plando mode (used in interactive mode)", + dest="hide", action='store_true') + parser.add_argument('--startPoint', help="The start AP to connect (used in interactive mode)", + dest="startPoint", nargs='?', default=None) + parser.add_argument('--endPoint', help="The destination AP to connect (used in interactive mode)", + dest="endPoint", nargs='?', default=None) + + parser.add_argument('--mode', help="Solver mode: standard/seedless/plando (used in interactive mode)", + dest="mode", nargs="?", default=None, choices=['standard', 'seedless', 'plando', 'race', 'debug']) + parser.add_argument('--scope', help="Scope for the action: common/area/item (used in interactive mode)", + dest="scope", nargs="?", default=None, choices=['common', 'area', 'item', 'door', 'dump']) + parser.add_argument('--count', help="Number of item rollback (used in interactive mode)", + dest="count", type=int) + parser.add_argument('--lock', help="lock the plando seed (used in interactive mode)", + dest="lock", action='store_true') + parser.add_argument('--escapeTimer', help="escape timer like 03:00", dest="escapeTimer", default=None) + parser.add_argument('--fill', help="in plando load all the source seed locations/transitions as a base (used in interactive mode)", + dest="fill", action='store_true') + parser.add_argument('--startLocation', help="in plando/seedless: the start location", dest="startLocation", default="Landing Site") + parser.add_argument('--minorQty', help="rando plando (used in interactive mode)", + dest="minorQty", nargs="?", default=None, choices=[str(i) for i in range(0,101)]) + parser.add_argument('--energyQty', help="rando plando (used in interactive mode)", + dest="energyQty", nargs="?", default=None, choices=["sparse", "medium", "vanilla"]) + parser.add_argument('--forbiddenItems', help="rando plando (used in interactive mode)", + dest="forbiddenItems", nargs="?", default=None) + parser.add_argument('--doorName', help="door to replace (used in interactive mode)", + dest="doorName", nargs="?", default=None) + parser.add_argument('--newColor', help="new color for door (used in interactive mode)", + dest="newColor", nargs="?", default=None) + parser.add_argument('--logic', help='logic to use (used in interactive mode)', dest='logic', nargs='?', default="vanilla", choices=["vanilla", "rotation"]) + parser.add_argument('--runtime', + help="Maximum runtime limit in seconds. If 0 or negative, no runtime limit.", + dest='runtimeLimit_s', nargs='?', default=0, type=int) + parser.add_argument('--dump', help="dump file with autotracker state (used in interactive mode)", + dest="dump", nargs="?", default=None) + + args = parser.parse_args() + + if args.presetFileName is None: + args.presetFileName = 'worlds/sm/variaRandomizer/standard_presets/regular.json' + + if args.raceMagic != None: + if args.raceMagic <= 0 or args.raceMagic >= 0x10000: + print("Invalid magic") + sys.exit(-1) + + if args.count != None: + if args.count < 1 or args.count > 0x80: + print("Invalid count") + sys.exit(-1) + + utils.log.init(args.debug) + + if args.interactive == True: + interactiveSolver(args) + else: + standardSolver(args) diff --git a/worlds/sm/variaRandomizer/standard_presets/SMRAT2021.json b/worlds/sm/variaRandomizer/standard_presets/SMRAT2021.json new file mode 100644 index 00000000..6402d000 --- /dev/null +++ b/worlds/sm/variaRandomizer/standard_presets/SMRAT2021.json @@ -0,0 +1,344 @@ +{ + "score": 10, + "Controller": { + "A": "Jump", + "B": "Dash", + "Moonwalk": false, + "L": "Angle Down", + "R": "Angle Up", + "Y": "Item Cancel", + "X": "Shoot", + "Select": "Item Select" + }, + "password": "6f0aa3c5e171edbf1868831f3f1ee4f75efec3cc5a39d5e1d9e73b7977faed2e", + "Knows": { + "AccessSpringBallWithHiJump": [ + false, + 0 + ], + "CrouchJump": [ + false, + 0 + ], + "Mockball": [ + false, + 0 + ], + "MicrowavePhantoon": [ + false, + 0 + ], + "GreenGateGlitch": [ + false, + 0 + ], + "NovaBoost": [ + false, + 0 + ], + "ScrewAttackExit": [ + false, + 0 + ], + "AlcatrazEscape": [ + false, + 0 + ], + "RedTowerClimb": [ + false, + 0 + ], + "TediousMountEverest": [ + false, + 0 + ], + "AccessSpringBallWithGravJump": [ + false, + 0 + ], + "MockballWs": [ + false, + 0 + ], + "RonPopeilScrew": [ + false, + 0 + ], + "MidAirMorph": [ + false, + 0 + ], + "GravLessLevel1": [ + false, + 0 + ], + "CeilingDBoost": [ + false, + 0 + ], + "XrayIce": [ + false, + 0 + ], + "BotwoonToDraygonWithIce": [ + false, + 0 + ], + "GravLessLevel3": [ + false, + 0 + ], + "GetAroundWallJump": [ + false, + 0 + ], + "OldMBWithSpeed": [ + false, + 0 + ], + "SpringBallJumpFromWall": [ + false, + 0 + ], + "ContinuousWallJump": [ + false, + 0 + ], + "LavaDive": [ + false, + 0 + ], + "HiJumpLessGauntletAccess": [ + false, + 0 + ], + "PuyoClipXRay": [ + false, + 0 + ], + "ShortCharge": [ + false, + 0 + ], + "PreciousRoomXRayExit": [ + false, + 0 + ], + "LowGauntlet": [ + false, + 0 + ], + "MaridiaWallJumps": [ + false, + 0 + ], + "IceZebSkip": [ + false, + 0 + ], + "WallJumpCathedralExit": [ + false, + 0 + ], + "SpongeBathSpeed": [ + false, + 0 + ], + "MicrowaveDraygon": [ + false, + 0 + ], + "Moondance": [ + false, + 0 + ], + "HiJumpMamaTurtle": [ + false, + 0 + ], + "IceEscape": [ + false, + 0 + ], + "SpongeBathHiJump": [ + false, + 0 + ], + "DraygonRoomGrappleExit": [ + false, + 0 + ], + "InfiniteBombJump": [ + true, + 1 + ], + "SpongeBathBombJump": [ + false, + 0 + ], + "DoubleSpringBallJump": [ + false, + 0 + ], + "SuitlessPuyoClip": [ + false, + 0 + ], + "XrayDboost": [ + false, + 0 + ], + "PuyoClip": [ + false, + 0 + ], + "ReverseGateGlitch": [ + false, + 0 + ], + "AccessSpringBallWithXRayClimb": [ + false, + 0 + ], + "DraygonSparkKill": [ + false, + 0 + ], + "AccessSpringBallWithBombJumps": [ + false, + 0 + ], + "AccessSpringBallWithSpringBallBombJumps": [ + false, + 0 + ], + "ReverseGateGlitchHiJumpLess": [ + false, + 0 + ], + "DraygonRoomCrystalFlash": [ + false, + 0 + ], + "GravityJump": [ + false, + 0 + ], + "KillPlasmaPiratesWithCharge": [ + false, + 0 + ], + "DoubleChamberWallJump": [ + false, + 0 + ], + "LavaDiveNoHiJump": [ + false, + 0 + ], + "ShineSpark": [ + false, + 0 + ], + "AccessSpringBallWithSpringBallJump": [ + false, + 0 + ], + "NorfairReserveDBoost": [ + false, + 0 + ], + "SnailClip": [ + false, + 0 + ], + "KillPlasmaPiratesWithSpark": [ + false, + 0 + ], + "SpringBallJump": [ + false, + 0 + ], + "WallJump": [ + false, + 0 + ], + "SimpleShortCharge": [ + false, + 0 + ], + "DiagonalBombJump": [ + false, + 0 + ], + "SpeedZebSkip": [ + false, + 0 + ], + "CrocPBsDBoost": [ + false, + 0 + ], + "GravLessLevel2": [ + false, + 0 + ], + "CrocPBsIce": [ + false, + 0 + ], + "ScrewAttackExitWithoutScrew": [ + false, + 0 + ], + "MochtroidClip": [ + false, + 0 + ], + "HiJumpGauntletAccess": [ + false, + 0 + ], + "EarlyKraid": [ + false, + 0 + ], + "BillyMays": [ + false, + 0 + ], + "IceMissileFromCroc": [ + false, + 0 + ], + "BubbleMountainWallJump": [ + false, + 0 + ], + "DraygonGrappleKill": [ + false, + 0 + ], + "WorstRoomIceCharge": [ + false, + 0 + ], + "FrogSpeedwayWithoutSpeed": [ + false, + 0 + ] + }, + "Settings": { + "Kraid": "He's annoying", + "MainUpperNorfair": "No thanks", + "Phantoon": "A lot of trouble", + "Ice": "No thanks", + "Ridley": "I'm scared!", + "MotherBrain": "It can get ugly", + "LowerNorfair": "Default", + "Gauntlet": "Aarghh", + "X-Ray": "Aarghh", + "Draygon": "A lot of trouble" + } +} \ No newline at end of file diff --git a/worlds/sm/variaRandomizer/standard_presets/Season_Races.json b/worlds/sm/variaRandomizer/standard_presets/Season_Races.json new file mode 100644 index 00000000..66566891 --- /dev/null +++ b/worlds/sm/variaRandomizer/standard_presets/Season_Races.json @@ -0,0 +1,336 @@ +{ + "score": 219, + "Controller": { + "A": "Jump", + "B": "Dash", + "Moonwalk": false, + "L": "Angle Down", + "R": "Angle Up", + "Y": "Item Cancel", + "X": "Shoot", + "Select": "Item Select" + }, + "password": "57919619252ffcbe02b54faa32bb52a2c1d8bd2b077e192ed95cb737d4d3abe0", + "Knows": { + "CrouchJump": [ + false, + 0 + ], + "Mockball": [ + true, + 1 + ], + "MicrowavePhantoon": [ + true, + 5 + ], + "GreenGateGlitch": [ + true, + 5 + ], + "NovaBoost": [ + false, + 0 + ], + "ScrewAttackExit": [ + true, + 10 + ], + "AlcatrazEscape": [ + true, + 5 + ], + "RedTowerClimb": [ + true, + 10 + ], + "TediousMountEverest": [ + false, + 0 + ], + "AccessSpringBallWithGravJump": [ + false, + 0 + ], + "MockballWs": [ + true, + 25 + ], + "RonPopeilScrew": [ + false, + 0 + ], + "MidAirMorph": [ + false, + 0 + ], + "GravLessLevel1": [ + true, + 25 + ], + "CeilingDBoost": [ + true, + 1 + ], + "XrayIce": [ + true, + 10 + ], + "BotwoonToDraygonWithIce": [ + false, + 0 + ], + "GravLessLevel3": [ + false, + 0 + ], + "GetAroundWallJump": [ + true, + 10 + ], + "OldMBWithSpeed": [ + false, + 0 + ], + "SpringBallJumpFromWall": [ + true, + 25 + ], + "ContinuousWallJump": [ + true, + 25 + ], + "LavaDive": [ + true, + 10 + ], + "HiJumpLessGauntletAccess": [ + true, + 5 + ], + "PuyoClipXRay": [ + false, + 0 + ], + "ShortCharge": [ + false, + 0 + ], + "PreciousRoomXRayExit": [ + false, + 0 + ], + "LowGauntlet": [ + false, + 0 + ], + "IceZebSkip": [ + true, + 5 + ], + "WallJumpCathedralExit": [ + true, + 5 + ], + "SpongeBathSpeed": [ + true, + 1 + ], + "MicrowaveDraygon": [ + true, + 1 + ], + "Moondance": [ + false, + 0 + ], + "HiJumpMamaTurtle": [ + false, + 0 + ], + "IceEscape": [ + false, + 0 + ], + "SpongeBathHiJump": [ + true, + 1 + ], + "DraygonRoomGrappleExit": [ + false, + 0 + ], + "InfiniteBombJump": [ + true, + 5 + ], + "SpongeBathBombJump": [ + true, + 25 + ], + "SuitlessPuyoClip": [ + false, + 0 + ], + "XrayDboost": [ + false, + 0 + ], + "PuyoClip": [ + true, + 25 + ], + "ReverseGateGlitch": [ + true, + 5 + ], + "AccessSpringBallWithXRayClimb": [ + false, + 0 + ], + "DraygonSparkKill": [ + true, + 10 + ], + "AccessSpringBallWithBombJumps": [ + false, + 0 + ], + "AccessSpringBallWithSpringBallBombJumps": [ + true, + 25 + ], + "ReverseGateGlitchHiJumpLess": [ + true, + 25 + ], + "DraygonRoomCrystalFlash": [ + false, + 0 + ], + "GravityJump": [ + true, + 5 + ], + "KillPlasmaPiratesWithCharge": [ + true, + 5 + ], + "DoubleSpringBallJump": [ + false, + 0 + ], + "LavaDiveNoHiJump": [ + false, + 0 + ], + "ShineSpark": [ + false, + 0 + ], + "AccessSpringBallWithSpringBallJump": [ + false, + 0 + ], + "NorfairReserveDBoost": [ + true, + 5 + ], + "SnailClip": [ + false, + 0 + ], + "KillPlasmaPiratesWithSpark": [ + true, + 25 + ], + "SpringBallJump": [ + true, + 10 + ], + "WallJump": [ + false, + 0 + ], + "SimpleShortCharge": [ + true, + 1 + ], + "DiagonalBombJump": [ + true, + 25 + ], + "SpeedZebSkip": [ + true, + 25 + ], + "CrocPBsDBoost": [ + false, + 0 + ], + "GravLessLevel2": [ + false, + 0 + ], + "CrocPBsIce": [ + true, + 5 + ], + "ScrewAttackExitWithoutScrew": [ + false, + 0 + ], + "MochtroidClip": [ + true, + 5 + ], + "HiJumpGauntletAccess": [ + true, + 5 + ], + "EarlyKraid": [ + true, + 1 + ], + "BillyMays": [ + true, + 1 + ], + "IceMissileFromCroc": [ + false, + 0 + ], + "BubbleMountainWallJump": [ + true, + 5 + ], + "DraygonGrappleKill": [ + true, + 5 + ], + "WorstRoomIceCharge": [ + false, + 0 + ], + "FrogSpeedwayWithoutSpeed": [ + false, + 0 + ], + "FirefleasWalljump": [ + true, + 10 + ] + }, + "Settings": { + "Kraid": "Quick Kill", + "MainUpperNorfair": "Bring the heat", + "Phantoon": "Used to it", + "Ice": "Bring the heat", + "Ridley": "Used to it", + "MotherBrain": "Is this really the last boss?", + "LowerNorfair": "Default", + "Gauntlet": "I don't like acid", + "X-Ray": "Default", + "Draygon": "Used to it" + } +} \ No newline at end of file diff --git a/worlds/sm/variaRandomizer/standard_presets/casual.json b/worlds/sm/variaRandomizer/standard_presets/casual.json new file mode 100644 index 00000000..aa7af410 --- /dev/null +++ b/worlds/sm/variaRandomizer/standard_presets/casual.json @@ -0,0 +1,368 @@ +{ + "Knows": { + "WallJump": [ + false, + 0 + ], + "ShineSpark": [ + false, + 0 + ], + "MidAirMorph": [ + false, + 0 + ], + "CrouchJump": [ + false, + 0 + ], + "Mockball": [ + true, + 5 + ], + "SimpleShortCharge": [ + true, + 1 + ], + "InfiniteBombJump": [ + true, + 10 + ], + "GreenGateGlitch": [ + true, + 10 + ], + "ShortCharge": [ + false, + 0 + ], + "GravityJump": [ + true, + 10 + ], + "SpringBallJump": [ + false, + 0 + ], + "SpringBallJumpFromWall": [ + false, + 0 + ], + "GetAroundWallJump": [ + true, + 25 + ], + "DraygonGrappleKill": [ + true, + 5 + ], + "DraygonSparkKill": [ + false, + 0 + ], + "MicrowaveDraygon": [ + false, + 0 + ], + "MicrowavePhantoon": [ + false, + 0 + ], + "IceZebSkip": [ + false, + 0 + ], + "SpeedZebSkip": [ + false, + 0 + ], + "HiJumpMamaTurtle": [ + false, + 0 + ], + "MaridiaWallJumps": [ + true, + 10 + ], + "GravLessLevel1": [ + false, + 0 + ], + "GravLessLevel2": [ + false, + 0 + ], + "GravLessLevel3": [ + false, + 0 + ], + "CeilingDBoost": [ + true, + 5 + ], + "BillyMays": [ + true, + 1 + ], + "AlcatrazEscape": [ + false, + 0 + ], + "ReverseGateGlitch": [ + false, + 0 + ], + "ReverseGateGlitchHiJumpLess": [ + false, + 0 + ], + "EarlyKraid": [ + true, + 1 + ], + "XrayDboost": [ + false, + 0 + ], + "XrayIce": [ + false, + 0 + ], + "RedTowerClimb": [ + true, + 50 + ], + "RonPopeilScrew": [ + false, + 0 + ], + "OldMBWithSpeed": [ + false, + 0 + ], + "Moondance": [ + false, + 0 + ], + "HiJumpLessGauntletAccess": [ + false, + 0 + ], + "HiJumpGauntletAccess": [ + false, + 0 + ], + "LowGauntlet": [ + false, + 0 + ], + "IceEscape": [ + false, + 0 + ], + "WallJumpCathedralExit": [ + false, + 0 + ], + "BubbleMountainWallJump": [ + true, + 5 + ], + "NovaBoost": [ + false, + 0 + ], + "NorfairReserveDBoost": [ + false, + 0 + ], + "CrocPBsDBoost": [ + false, + 0 + ], + "CrocPBsIce": [ + false, + 0 + ], + "IceMissileFromCroc": [ + false, + 0 + ], + "FrogSpeedwayWithoutSpeed": [ + false, + 0 + ], + "LavaDive": [ + false, + 0 + ], + "LavaDiveNoHiJump": [ + false, + 0 + ], + "WorstRoomIceCharge": [ + false, + 0 + ], + "WorstRoomWallJump": [ + false, + 0 + ], + "ScrewAttackExit": [ + false, + 0 + ], + "ScrewAttackExitWithoutScrew": [ + false, + 0 + ], + "FirefleasWalljump": [ + false, + 0 + ], + "DodgeLowerNorfairEnemies": [ + false, + 0 + ], + "ContinuousWallJump": [ + false, + 0 + ], + "DiagonalBombJump": [ + false, + 0 + ], + "MockballWs": [ + false, + 0 + ], + "SpongeBathBombJump": [ + false, + 0 + ], + "SpongeBathHiJump": [ + true, + 1 + ], + "SpongeBathSpeed": [ + true, + 5 + ], + "TediousMountEverest": [ + false, + 0 + ], + "DoubleSpringBallJump": [ + false, + 0 + ], + "BotwoonToDraygonWithIce": [ + false, + 0 + ], + "WestSandHoleSuitlessWallJumps": [ + false, + 0 + ], + "DraygonRoomGrappleExit": [ + false, + 0 + ], + "DraygonRoomCrystalFlash": [ + false, + 0 + ], + "PreciousRoomXRayExit": [ + false, + 0 + ], + "MochtroidClip": [ + false, + 0 + ], + "PuyoClip": [ + false, + 0 + ], + "PuyoClipXRay": [ + false, + 0 + ], + "SnailClip": [ + false, + 0 + ], + "SuitlessPuyoClip": [ + false, + 0 + ], + "CrystalFlashClip": [ + false, + 0 + ], + "SuitlessCrystalFlashClip": [ + false, + 0 + ], + "KillPlasmaPiratesWithSpark": [ + false, + 0 + ], + "KillPlasmaPiratesWithCharge": [ + true, + 10 + ], + "AccessSpringBallWithHiJump": [ + true, + 1 + ], + "AccessSpringBallWithSpringBallBombJumps": [ + false, + 0 + ], + "AccessSpringBallWithBombJumps": [ + false, + 0 + ], + "AccessSpringBallWithSpringBallJump": [ + false, + 0 + ], + "AccessSpringBallWithXRayClimb": [ + false, + 0 + ], + "AccessSpringBallWithGravJump": [ + false, + 0 + ], + "AccessSpringBallWithFlatley": [ + false, + 0 + ] + }, + "Settings": { + "Ice": "No thanks", + "MainUpperNorfair": "No thanks", + "LowerNorfair": "Default", + "Kraid": "Default", + "Phantoon": "A lot of trouble", + "Draygon": "A lot of trouble", + "Ridley": "Default", + "MotherBrain": "Default", + "X-Ray": "Aarghh", + "Gauntlet": "Aarghh" + }, + "Controller": { + "A": "Jump", + "B": "Dash", + "X": "Shoot", + "Y": "Item Cancel", + "L": "Angle Down", + "R": "Angle Up", + "Select": "Item Select", + "Moonwalk": false + }, + "password": "48a71434ba135c1f37d6952639b21ce9658861d4a0f0133c79694a9228487697", + "score": 88 +} \ No newline at end of file diff --git a/worlds/sm/variaRandomizer/standard_presets/expert.json b/worlds/sm/variaRandomizer/standard_presets/expert.json new file mode 100644 index 00000000..f5fc5242 --- /dev/null +++ b/worlds/sm/variaRandomizer/standard_presets/expert.json @@ -0,0 +1,388 @@ +{ + "Knows": { + "WallJump": [ + false, + 0 + ], + "ShineSpark": [ + false, + 0 + ], + "MidAirMorph": [ + false, + 0 + ], + "CrouchJump": [ + false, + 0 + ], + "Mockball": [ + true, + 1 + ], + "SimpleShortCharge": [ + true, + 1 + ], + "InfiniteBombJump": [ + true, + 1 + ], + "GreenGateGlitch": [ + true, + 1 + ], + "ShortCharge": [ + true, + 10 + ], + "GravityJump": [ + true, + 1 + ], + "SpringBallJump": [ + true, + 5 + ], + "SpringBallJumpFromWall": [ + true, + 5 + ], + "GetAroundWallJump": [ + true, + 1 + ], + "DraygonGrappleKill": [ + true, + 1 + ], + "DraygonSparkKill": [ + true, + 1 + ], + "MicrowaveDraygon": [ + true, + 1 + ], + "MicrowavePhantoon": [ + true, + 1 + ], + "LowAmmoCroc": [ + true, + 25 + ], + "LowStuffBotwoon": [ + true, + 25 + ], + "LowStuffGT": [ + true, + 25 + ], + "IceZebSkip": [ + true, + 1 + ], + "SpeedZebSkip": [ + true, + 5 + ], + "HiJumpMamaTurtle": [ + true, + 1 + ], + "MaridiaWallJumps": [ + true, + 1 + ], + "MtEverestGravJump": [ + true, + 5 + ], + "GravLessLevel1": [ + true, + 5 + ], + "GravLessLevel2": [ + true, + 10 + ], + "GravLessLevel3": [ + true, + 50 + ], + "CeilingDBoost": [ + true, + 1 + ], + "BillyMays": [ + true, + 1 + ], + "AlcatrazEscape": [ + true, + 1 + ], + "ReverseGateGlitch": [ + true, + 1 + ], + "ReverseGateGlitchHiJumpLess": [ + true, + 5 + ], + "EarlyKraid": [ + true, + 1 + ], + "XrayDboost": [ + true, + 5 + ], + "XrayIce": [ + true, + 10 + ], + "RedTowerClimb": [ + true, + 5 + ], + "RonPopeilScrew": [ + false, + 0 + ], + "OldMBWithSpeed": [ + false, + 0 + ], + "Moondance": [ + false, + 0 + ], + "HiJumpLessGauntletAccess": [ + true, + 1 + ], + "HiJumpGauntletAccess": [ + true, + 1 + ], + "LowGauntlet": [ + false, + 0 + ], + "IceEscape": [ + true, + 10 + ], + "WallJumpCathedralExit": [ + true, + 1 + ], + "BubbleMountainWallJump": [ + true, + 5 + ], + "NovaBoost": [ + true, + 50 + ], + "NorfairReserveDBoost": [ + true, + 5 + ], + "CrocPBsDBoost": [ + true, + 10 + ], + "CrocPBsIce": [ + true, + 5 + ], + "IceMissileFromCroc": [ + true, + 10 + ], + "FrogSpeedwayWithoutSpeed": [ + true, + 1 + ], + "LavaDive": [ + true, + 5 + ], + "LavaDiveNoHiJump": [ + true, + 100 + ], + "WorstRoomIceCharge": [ + true, + 10 + ], + "WorstRoomWallJump": [ + false, + 0 + ], + "ScrewAttackExit": [ + true, + 5 + ], + "ScrewAttackExitWithoutScrew": [ + true, + 5 + ], + "FirefleasWalljump": [ + true, + 5 + ], + "DodgeLowerNorfairEnemies": [ + true, + 100 + ], + "ContinuousWallJump": [ + true, + 5 + ], + "DiagonalBombJump": [ + true, + 5 + ], + "MockballWs": [ + true, + 5 + ], + "SpongeBathBombJump": [ + true, + 10 + ], + "SpongeBathHiJump": [ + true, + 1 + ], + "SpongeBathSpeed": [ + true, + 1 + ], + "TediousMountEverest": [ + false, + 0 + ], + "DoubleSpringBallJump": [ + false, + 0 + ], + "BotwoonToDraygonWithIce": [ + false, + 0 + ], + "WestSandHoleSuitlessWallJumps": [ + true, + 100 + ], + "DraygonRoomGrappleExit": [ + true, + 25 + ], + "DraygonRoomCrystalFlash": [ + true, + 25 + ], + "PreciousRoomXRayExit": [ + true, + 25 + ], + "PreciousRoomGravJumpExit": [ + false, + 0 + ], + "MochtroidClip": [ + true, + 1 + ], + "PuyoClip": [ + true, + 10 + ], + "PuyoClipXRay": [ + true, + 5 + ], + "SnailClip": [ + true, + 5 + ], + "SuitlessPuyoClip": [ + false, + 0 + ], + "CrystalFlashClip": [ + true, + 50 + ], + "SuitlessCrystalFlashClip": [ + false, + 0 + ], + "KillPlasmaPiratesWithSpark": [ + true, + 5 + ], + "KillPlasmaPiratesWithCharge": [ + true, + 1 + ], + "AccessSpringBallWithHiJump": [ + true, + 1 + ], + "AccessSpringBallWithSpringBallBombJumps": [ + true, + 5 + ], + "AccessSpringBallWithBombJumps": [ + true, + 10 + ], + "AccessSpringBallWithSpringBallJump": [ + true, + 10 + ], + "AccessSpringBallWithXRayClimb": [ + true, + 25 + ], + "AccessSpringBallWithGravJump": [ + true, + 25 + ], + "AccessSpringBallWithFlatley": [ + true, + 25 + ] + }, + "Settings": { + "Ice": "Bring the heat", + "MainUpperNorfair": "Bring the heat", + "LowerNorfair": "Bring the heat", + "Kraid": "Quick Kill", + "Phantoon": "No problemo", + "Draygon": "No problemo", + "Ridley": "Piece of cake", + "MotherBrain": "Nice cutscene bro", + "X-Ray": "I don't mind spikes", + "Gauntlet": "Default" + }, + "Controller": { + "A": "Jump", + "B": "Dash", + "X": "Shoot", + "Y": "Item Cancel", + "L": "Angle Down", + "R": "Angle Up", + "Select": "Item Select", + "Moonwalk": false + }, + "password": "365453a4dc558a8dc1e368684b360dc452792dea8614fa66e48deb52bb5edab1", + "score": 394 +} \ No newline at end of file diff --git a/worlds/sm/variaRandomizer/standard_presets/master.json b/worlds/sm/variaRandomizer/standard_presets/master.json new file mode 100644 index 00000000..fb702cc3 --- /dev/null +++ b/worlds/sm/variaRandomizer/standard_presets/master.json @@ -0,0 +1,388 @@ +{ + "Knows": { + "WallJump": [ + false, + 0 + ], + "ShineSpark": [ + false, + 0 + ], + "MidAirMorph": [ + false, + 0 + ], + "CrouchJump": [ + false, + 0 + ], + "Mockball": [ + true, + 1 + ], + "SimpleShortCharge": [ + true, + 1 + ], + "InfiniteBombJump": [ + true, + 1 + ], + "GreenGateGlitch": [ + true, + 1 + ], + "ShortCharge": [ + true, + 5 + ], + "GravityJump": [ + true, + 1 + ], + "SpringBallJump": [ + true, + 5 + ], + "SpringBallJumpFromWall": [ + true, + 5 + ], + "GetAroundWallJump": [ + true, + 1 + ], + "DraygonGrappleKill": [ + true, + 1 + ], + "DraygonSparkKill": [ + true, + 1 + ], + "MicrowaveDraygon": [ + true, + 1 + ], + "MicrowavePhantoon": [ + true, + 1 + ], + "LowAmmoCroc": [ + true, + 10 + ], + "LowStuffBotwoon": [ + true, + 10 + ], + "LowStuffGT": [ + true, + 10 + ], + "IceZebSkip": [ + true, + 1 + ], + "SpeedZebSkip": [ + true, + 5 + ], + "HiJumpMamaTurtle": [ + true, + 1 + ], + "MaridiaWallJumps": [ + true, + 1 + ], + "MtEverestGravJump": [ + true, + 5 + ], + "GravLessLevel1": [ + true, + 1 + ], + "GravLessLevel2": [ + true, + 5 + ], + "GravLessLevel3": [ + true, + 10 + ], + "CeilingDBoost": [ + true, + 1 + ], + "BillyMays": [ + true, + 1 + ], + "AlcatrazEscape": [ + true, + 1 + ], + "ReverseGateGlitch": [ + true, + 1 + ], + "ReverseGateGlitchHiJumpLess": [ + true, + 1 + ], + "EarlyKraid": [ + true, + 1 + ], + "XrayDboost": [ + true, + 5 + ], + "XrayIce": [ + true, + 10 + ], + "RedTowerClimb": [ + true, + 5 + ], + "RonPopeilScrew": [ + true, + 5 + ], + "OldMBWithSpeed": [ + true, + 25 + ], + "Moondance": [ + true, + 25 + ], + "HiJumpLessGauntletAccess": [ + true, + 1 + ], + "HiJumpGauntletAccess": [ + true, + 1 + ], + "LowGauntlet": [ + true, + 10 + ], + "IceEscape": [ + true, + 5 + ], + "WallJumpCathedralExit": [ + true, + 1 + ], + "BubbleMountainWallJump": [ + true, + 5 + ], + "NovaBoost": [ + true, + 10 + ], + "NorfairReserveDBoost": [ + true, + 1 + ], + "CrocPBsDBoost": [ + true, + 5 + ], + "CrocPBsIce": [ + true, + 1 + ], + "IceMissileFromCroc": [ + true, + 5 + ], + "FrogSpeedwayWithoutSpeed": [ + true, + 5 + ], + "LavaDive": [ + true, + 5 + ], + "LavaDiveNoHiJump": [ + true, + 25 + ], + "WorstRoomIceCharge": [ + true, + 10 + ], + "WorstRoomWallJump": [ + true, + 50 + ], + "ScrewAttackExit": [ + true, + 1 + ], + "ScrewAttackExitWithoutScrew": [ + true, + 1 + ], + "FirefleasWalljump": [ + true, + 1 + ], + "DodgeLowerNorfairEnemies": [ + true, + 10 + ], + "ContinuousWallJump": [ + true, + 1 + ], + "DiagonalBombJump": [ + true, + 1 + ], + "MockballWs": [ + true, + 1 + ], + "SpongeBathBombJump": [ + true, + 5 + ], + "SpongeBathHiJump": [ + true, + 1 + ], + "SpongeBathSpeed": [ + true, + 1 + ], + "TediousMountEverest": [ + true, + 25 + ], + "DoubleSpringBallJump": [ + true, + 25 + ], + "BotwoonToDraygonWithIce": [ + true, + 25 + ], + "WestSandHoleSuitlessWallJumps": [ + true, + 25 + ], + "DraygonRoomGrappleExit": [ + true, + 10 + ], + "DraygonRoomCrystalFlash": [ + true, + 10 + ], + "PreciousRoomXRayExit": [ + true, + 10 + ], + "PreciousRoomGravJumpExit": [ + true, + 25 + ], + "MochtroidClip": [ + true, + 1 + ], + "PuyoClip": [ + true, + 5 + ], + "PuyoClipXRay": [ + true, + 1 + ], + "SnailClip": [ + true, + 1 + ], + "SuitlessPuyoClip": [ + true, + 100 + ], + "CrystalFlashClip": [ + true, + 10 + ], + "SuitlessCrystalFlashClip": [ + true, + 50 + ], + "KillPlasmaPiratesWithSpark": [ + true, + 5 + ], + "KillPlasmaPiratesWithCharge": [ + true, + 1 + ], + "AccessSpringBallWithHiJump": [ + true, + 1 + ], + "AccessSpringBallWithSpringBallBombJumps": [ + true, + 1 + ], + "AccessSpringBallWithBombJumps": [ + true, + 5 + ], + "AccessSpringBallWithSpringBallJump": [ + true, + 5 + ], + "AccessSpringBallWithXRayClimb": [ + true, + 10 + ], + "AccessSpringBallWithGravJump": [ + true, + 10 + ], + "AccessSpringBallWithFlatley": [ + true, + 10 + ] + }, + "Settings": { + "Ice": "I run RBO", + "MainUpperNorfair": "I run RBO", + "LowerNorfair": "I run RBO", + "Kraid": "Quick Kill", + "Phantoon": "No problemo", + "Draygon": "No problemo", + "Ridley": "Piece of cake", + "MotherBrain": "Nice cutscene bro", + "X-Ray": "D-Boost master", + "Gauntlet": "Default" + }, + "Controller": { + "A": "Jump", + "B": "Dash", + "X": "Shoot", + "Y": "Item Cancel", + "L": "Angle Down", + "R": "Angle Up", + "Select": "Item Select", + "Moonwalk": false + }, + "password": "888765cfc382409bf63df1fe40a127666942e54b5712678daef44a17a0e158d4", + "score": 480 +} \ No newline at end of file diff --git a/worlds/sm/variaRandomizer/standard_presets/newbie.json b/worlds/sm/variaRandomizer/standard_presets/newbie.json new file mode 100644 index 00000000..5e515f46 --- /dev/null +++ b/worlds/sm/variaRandomizer/standard_presets/newbie.json @@ -0,0 +1,392 @@ +{ + "Knows": { + "WallJump": [ + false, + 0 + ], + "ShineSpark": [ + false, + 0 + ], + "MidAirMorph": [ + false, + 0 + ], + "CrouchJump": [ + false, + 0 + ], + "Mockball": [ + false, + 0 + ], + "SimpleShortCharge": [ + false, + 0 + ], + "InfiniteBombJump": [ + false, + 0 + ], + "GreenGateGlitch": [ + false, + 0 + ], + "ShortCharge": [ + false, + 0 + ], + "GravityJump": [ + false, + 0 + ], + "SpringBallJump": [ + false, + 0 + ], + "SpringBallJumpFromWall": [ + false, + 0 + ], + "GetAroundWallJump": [ + false, + 0 + ], + "DraygonGrappleKill": [ + false, + 0 + ], + "DraygonSparkKill": [ + false, + 0 + ], + "MicrowaveDraygon": [ + false, + 0 + ], + "MicrowavePhantoon": [ + false, + 0 + ], + "LowAmmoCroc": [ + false, + 0 + ], + "LowStuffBotwoon": [ + false, + 0 + ], + "LowStuffGT": [ + false, + 0 + ], + "IceZebSkip": [ + false, + 0 + ], + "SpeedZebSkip": [ + false, + 0 + ], + "HiJumpMamaTurtle": [ + false, + 0 + ], + "MaridiaWallJumps": [ + false, + 0 + ], + "MtEverestGravJump": [ + false, + 0 + ], + "GravLessLevel1": [ + false, + 0 + ], + "GravLessLevel2": [ + false, + 0 + ], + "GravLessLevel3": [ + false, + 0 + ], + "CeilingDBoost": [ + false, + 0 + ], + "BillyMays": [ + false, + 0 + ], + "AlcatrazEscape": [ + false, + 0 + ], + "ReverseGateGlitch": [ + false, + 0 + ], + "ReverseGateGlitchHiJumpLess": [ + false, + 0 + ], + "EarlyKraid": [ + false, + 0 + ], + "XrayDboost": [ + false, + 0 + ], + "XrayIce": [ + false, + 0 + ], + "RedTowerClimb": [ + false, + 0 + ], + "RonPopeilScrew": [ + false, + 0 + ], + "OldMBWithSpeed": [ + false, + 0 + ], + "Moondance": [ + false, + 0 + ], + "HiJumpLessGauntletAccess": [ + false, + 0 + ], + "HiJumpGauntletAccess": [ + false, + 0 + ], + "LowGauntlet": [ + false, + 0 + ], + "IceEscape": [ + false, + 0 + ], + "WallJumpCathedralExit": [ + false, + 0 + ], + "BubbleMountainWallJump": [ + false, + 0 + ], + "DoubleChamberWallJump": [ + false, + 0 + ], + "NovaBoost": [ + false, + 0 + ], + "NorfairReserveDBoost": [ + false, + 0 + ], + "CrocPBsDBoost": [ + false, + 0 + ], + "CrocPBsIce": [ + false, + 0 + ], + "IceMissileFromCroc": [ + false, + 0 + ], + "FrogSpeedwayWithoutSpeed": [ + false, + 0 + ], + "LavaDive": [ + false, + 0 + ], + "LavaDiveNoHiJump": [ + false, + 0 + ], + "WorstRoomIceCharge": [ + false, + 0 + ], + "WorstRoomWallJump": [ + false, + 0 + ], + "ScrewAttackExit": [ + false, + 0 + ], + "ScrewAttackExitWithoutScrew": [ + false, + 0 + ], + "FirefleasWalljump": [ + false, + 0 + ], + "DodgeLowerNorfairEnemies": [ + false, + 0 + ], + "ContinuousWallJump": [ + false, + 0 + ], + "DiagonalBombJump": [ + false, + 0 + ], + "MockballWs": [ + false, + 0 + ], + "SpongeBathBombJump": [ + false, + 0 + ], + "SpongeBathHiJump": [ + false, + 0 + ], + "SpongeBathSpeed": [ + false, + 0 + ], + "TediousMountEverest": [ + false, + 0 + ], + "DoubleSpringBallJump": [ + false, + 0 + ], + "BotwoonToDraygonWithIce": [ + false, + 0 + ], + "WestSandHoleSuitlessWallJumps": [ + false, + 0 + ], + "DraygonRoomGrappleExit": [ + false, + 0 + ], + "DraygonRoomCrystalFlash": [ + false, + 0 + ], + "PreciousRoomXRayExit": [ + false, + 0 + ], + "PreciousRoomGravJumpExit": [ + false, + 0 + ], + "MochtroidClip": [ + false, + 0 + ], + "PuyoClip": [ + false, + 0 + ], + "PuyoClipXRay": [ + false, + 0 + ], + "SnailClip": [ + false, + 0 + ], + "SuitlessPuyoClip": [ + false, + 0 + ], + "CrystalFlashClip": [ + false, + 0 + ], + "SuitlessCrystalFlashClip": [ + false, + 0 + ], + "KillPlasmaPiratesWithSpark": [ + false, + 0 + ], + "KillPlasmaPiratesWithCharge": [ + false, + 0 + ], + "AccessSpringBallWithHiJump": [ + false, + 0 + ], + "AccessSpringBallWithSpringBallBombJumps": [ + false, + 0 + ], + "AccessSpringBallWithBombJumps": [ + false, + 0 + ], + "AccessSpringBallWithSpringBallJump": [ + false, + 0 + ], + "AccessSpringBallWithXRayClimb": [ + false, + 0 + ], + "AccessSpringBallWithGravJump": [ + false, + 0 + ], + "AccessSpringBallWithFlatley": [ + false, + 0 + ] + }, + "Settings": { + "Ice": "No thanks", + "MainUpperNorfair": "No thanks", + "LowerNorfair": "Default", + "Kraid": "He's annoying", + "Phantoon": "A lot of trouble", + "Draygon": "A lot of trouble", + "Ridley": "I'm scared!", + "MotherBrain": "It can get ugly", + "X-Ray": "Aarghh", + "Gauntlet": "Aarghh" + }, + "Controller": { + "A": "Jump", + "B": "Dash", + "X": "Shoot", + "Y": "Item Cancel", + "L": "Angle Down", + "R": "Angle Up", + "Select": "Item Select", + "Moonwalk": false + }, + "password": "732071161c41f9d3c0342e85d579a5bf27ddabc0529a5344749c9b084ca52bf0", + "score": 5 +} \ No newline at end of file diff --git a/worlds/sm/variaRandomizer/standard_presets/regular.json b/worlds/sm/variaRandomizer/standard_presets/regular.json new file mode 100644 index 00000000..9b7c791d --- /dev/null +++ b/worlds/sm/variaRandomizer/standard_presets/regular.json @@ -0,0 +1,340 @@ +{ + "Knows": { + "WallJump": [ + false, + 0 + ], + "ShineSpark": [ + false, + 0 + ], + "MidAirMorph": [ + false, + 0 + ], + "CrouchJump": [ + false, + 0 + ], + "Mockball": [ + true, + 1 + ], + "SimpleShortCharge": [ + true, + 1 + ], + "InfiniteBombJump": [ + true, + 5 + ], + "GreenGateGlitch": [ + true, + 5 + ], + "ShortCharge": [ + false, + 0 + ], + "GravityJump": [ + true, + 10 + ], + "SpringBallJump": [ + true, + 10 + ], + "SpringBallJumpFromWall": [ + false, + 0 + ], + "GetAroundWallJump": [ + true, + 10 + ], + "DraygonGrappleKill": [ + true, + 5 + ], + "DraygonSparkKill": [ + false, + 0 + ], + "MicrowaveDraygon": [ + true, + 1 + ], + "MicrowavePhantoon": [ + true, + 5 + ], + "IceZebSkip": [ + false, + 0 + ], + "SpeedZebSkip": [ + false, + 0 + ], + "HiJumpMamaTurtle": [ + false, + 0 + ], + "GravLessLevel1": [ + true, + 50 + ], + "GravLessLevel2": [ + false, + 0 + ], + "GravLessLevel3": [ + false, + 0 + ], + "CeilingDBoost": [ + true, + 1 + ], + "BillyMays": [ + true, + 1 + ], + "AlcatrazEscape": [ + true, + 25 + ], + "ReverseGateGlitch": [ + true, + 5 + ], + "ReverseGateGlitchHiJumpLess": [ + false, + 0 + ], + "EarlyKraid": [ + true, + 1 + ], + "XrayDboost": [ + false, + 0 + ], + "XrayIce": [ + true, + 10 + ], + "RedTowerClimb": [ + true, + 25 + ], + "RonPopeilScrew": [ + false, + 0 + ], + "OldMBWithSpeed": [ + false, + 0 + ], + "Moondance": [ + false, + 0 + ], + "HiJumpLessGauntletAccess": [ + true, + 50 + ], + "HiJumpGauntletAccess": [ + true, + 25 + ], + "LowGauntlet": [ + false, + 0 + ], + "IceEscape": [ + false, + 0 + ], + "WallJumpCathedralExit": [ + true, + 5 + ], + "BubbleMountainWallJump": [ + true, + 5 + ], + "NovaBoost": [ + false, + 0 + ], + "NorfairReserveDBoost": [ + false, + 0 + ], + "CrocPBsDBoost": [ + false, + 0 + ], + "CrocPBsIce": [ + false, + 0 + ], + "IceMissileFromCroc": [ + false, + 0 + ], + "FrogSpeedwayWithoutSpeed": [ + false, + 0 + ], + "LavaDive": [ + true, + 50 + ], + "LavaDiveNoHiJump": [ + false, + 0 + ], + "WorstRoomIceCharge": [ + false, + 0 + ], + "ScrewAttackExit": [ + false, + 0 + ], + "ScrewAttackExitWithoutScrew": [ + false, + 0 + ], + "FirefleasWalljump": [ + true, + 25 + ], + "ContinuousWallJump": [ + false, + 0 + ], + "DiagonalBombJump": [ + false, + 0 + ], + "MockballWs": [ + false, + 0 + ], + "SpongeBathBombJump": [ + false, + 0 + ], + "SpongeBathHiJump": [ + true, + 1 + ], + "SpongeBathSpeed": [ + true, + 5 + ], + "TediousMountEverest": [ + false, + 0 + ], + "DoubleSpringBallJump": [ + false, + 0 + ], + "BotwoonToDraygonWithIce": [ + false, + 0 + ], + "DraygonRoomGrappleExit": [ + false, + 0 + ], + "DraygonRoomCrystalFlash": [ + false, + 0 + ], + "PreciousRoomXRayExit": [ + false, + 0 + ], + "MochtroidClip": [ + true, + 5 + ], + "PuyoClip": [ + false, + 0 + ], + "PuyoClipXRay": [ + false, + 0 + ], + "SnailClip": [ + false, + 0 + ], + "SuitlessPuyoClip": [ + false, + 0 + ], + "KillPlasmaPiratesWithSpark": [ + false, + 0 + ], + "KillPlasmaPiratesWithCharge": [ + true, + 5 + ], + "AccessSpringBallWithHiJump": [ + true, + 1 + ], + "AccessSpringBallWithSpringBallBombJumps": [ + true, + 10 + ], + "AccessSpringBallWithBombJumps": [ + false, + 0 + ], + "AccessSpringBallWithSpringBallJump": [ + false, + 0 + ], + "AccessSpringBallWithXRayClimb": [ + false, + 0 + ], + "AccessSpringBallWithGravJump": [ + false, + 0 + ] + }, + "Settings": { + "Ice": "Gimme energy", + "MainUpperNorfair": "Gimme energy", + "LowerNorfair": "Default", + "Kraid": "Default", + "Phantoon": "Default", + "Draygon": "Default", + "Ridley": "Default", + "MotherBrain": "Default", + "X-Ray": "I don't like spikes", + "Gauntlet": "I don't like acid" + }, + "Controller": { + "A": "Jump", + "B": "Dash", + "X": "Shoot", + "Y": "Item Cancel", + "L": "Angle Down", + "R": "Angle Up", + "Select": "Item Select", + "Moonwalk": false + }, + "password": "90f2524685d745fb06d03bd94ddf3eca1d9d5a835b452ad52b2097becee01b7f", + "score": 152 +} \ No newline at end of file diff --git a/worlds/sm/variaRandomizer/standard_presets/samus.json b/worlds/sm/variaRandomizer/standard_presets/samus.json new file mode 100644 index 00000000..6b6c82c8 --- /dev/null +++ b/worlds/sm/variaRandomizer/standard_presets/samus.json @@ -0,0 +1,384 @@ +{ + "score": 437, + "Controller": { + "A": "Jump", + "B": "Dash", + "Moonwalk": false, + "L": "Angle Down", + "R": "Angle Up", + "Y": "Item Cancel", + "X": "Shoot", + "Select": "Item Select" + }, + "password": "488791bad255bb16b3dc666edfe234889a0cbb952608a45142ccc5c28f580fc6", + "Knows": { + "CrouchJump": [ + false, + 0 + ], + "BubbleMountainWallJump": [ + true, + 1 + ], + "Mockball": [ + true, + 1 + ], + "MicrowavePhantoon": [ + true, + 1 + ], + "GreenGateGlitch": [ + true, + 1 + ], + "NovaBoost": [ + true, + 1 + ], + "ScrewAttackExit": [ + true, + 1 + ], + "WorstRoomIceCharge": [ + true, + 1 + ], + "RedTowerClimb": [ + true, + 1 + ], + "TediousMountEverest": [ + true, + 1 + ], + "MockballWs": [ + true, + 1 + ], + "RonPopeilScrew": [ + true, + 1 + ], + "MidAirMorph": [ + false, + 0 + ], + "GravLessLevel1": [ + true, + 1 + ], + "CeilingDBoost": [ + true, + 1 + ], + "AlcatrazEscape": [ + true, + 1 + ], + "GravLessLevel3": [ + true, + 1 + ], + "GetAroundWallJump": [ + true, + 1 + ], + "SpringBallJumpFromWall": [ + true, + 1 + ], + "ContinuousWallJump": [ + true, + 1 + ], + "LavaDive": [ + true, + 1 + ], + "HiJumpLessGauntletAccess": [ + true, + 1 + ], + "PuyoClipXRay": [ + true, + 1 + ], + "ShortCharge": [ + true, + 1 + ], + "PreciousRoomXRayExit": [ + true, + 1 + ], + "LowGauntlet": [ + true, + 1 + ], + "WallJumpCathedralExit": [ + true, + 1 + ], + "DoubleSpringBallJump": [ + true, + 1 + ], + "SpongeBathSpeed": [ + true, + 1 + ], + "MicrowaveDraygon": [ + true, + 1 + ], + "Moondance": [ + true, + 1 + ], + "HiJumpMamaTurtle": [ + true, + 1 + ], + "SpeedZebSkip": [ + true, + 1 + ], + "SpongeBathHiJump": [ + true, + 1 + ], + "DraygonRoomGrappleExit": [ + true, + 1 + ], + "SpongeBathBombJump": [ + true, + 1 + ], + "SuitlessPuyoClip": [ + true, + 1 + ], + "XrayDboost": [ + true, + 1 + ], + "XrayIce": [ + true, + 1 + ], + "PuyoClip": [ + true, + 1 + ], + "ReverseGateGlitch": [ + true, + 1 + ], + "AccessSpringBallWithXRayClimb": [ + true, + 1 + ], + "AccessSpringBallWithGravJump": [ + true, + 1 + ], + "BotwoonToDraygonWithIce": [ + true, + 1 + ], + "DraygonSparkKill": [ + true, + 1 + ], + "AccessSpringBallWithBombJumps": [ + true, + 1 + ], + "AccessSpringBallWithSpringBallBombJumps": [ + true, + 1 + ], + "ReverseGateGlitchHiJumpLess": [ + true, + 1 + ], + "DraygonRoomCrystalFlash": [ + true, + 1 + ], + "GravityJump": [ + true, + 1 + ], + "KillPlasmaPiratesWithCharge": [ + true, + 1 + ], + "GravLessLevel2": [ + true, + 1 + ], + "LavaDiveNoHiJump": [ + true, + 1 + ], + "ShineSpark": [ + false, + 0 + ], + "AccessSpringBallWithSpringBallJump": [ + true, + 1 + ], + "NorfairReserveDBoost": [ + true, + 1 + ], + "SnailClip": [ + true, + 1 + ], + "KillPlasmaPiratesWithSpark": [ + true, + 1 + ], + "SpringBallJump": [ + true, + 1 + ], + "WallJump": [ + false, + 0 + ], + "SimpleShortCharge": [ + true, + 1 + ], + "DiagonalBombJump": [ + true, + 1 + ], + "IceZebSkip": [ + true, + 1 + ], + "CrocPBsDBoost": [ + true, + 1 + ], + "CrocPBsIce": [ + true, + 1 + ], + "MochtroidClip": [ + true, + 1 + ], + "HiJumpGauntletAccess": [ + true, + 1 + ], + "EarlyKraid": [ + true, + 1 + ], + "IceMissileFromCroc": [ + true, + 1 + ], + "InfiniteBombJump": [ + true, + 1 + ], + "DraygonGrappleKill": [ + true, + 1 + ], + "FrogSpeedwayWithoutSpeed": [ + true, + 1 + ], + "IceEscape": [ + true, + 1 + ], + "ScrewAttackExitWithoutScrew": [ + true, + 1 + ], + "OldMBWithSpeed": [ + true, + 1 + ], + "FirefleasWalljump": [ + true, + 1 + ], + "CrystalFlashClip": [ + true, + 1 + ], + "SuitlessCrystalFlashClip": [ + true, + 1 + ], + "DodgeLowerNorfairEnemies": [ + true, + 1 + ], + "WorstRoomWallJump": [ + true, + 1 + ], + "AccessSpringBallWithFlatley": [ + true, + 1 + ], + "MaridiaWallJumps": [ + true, + 1 + ], + "WestSandHoleSuitlessWallJumps": [ + true, + 1 + ], + "PreciousRoomGravJumpExit": [ + true, + 1 + ], + "LowAmmoCroc": [ + true, + 1 + ], + "LowStuffBotwoon": [ + true, + 1 + ], + "LowStuffGT": [ + true, + 1 + ], + "MtEverestGravJump": [ + true, + 1 + ], + "DoubleChamberWallJump": [ + true, + 1 + ] + }, + "Settings": { + "Kraid": "Quick Kill", + "MainUpperNorfair": "I run RBO", + "Phantoon": "No problemo", + "Ice": "I run RBO", + "Ridley": "Piece of cake", + "MotherBrain": "Nice cutscene bro", + "LowerNorfair": "I run RBO", + "Gauntlet": "Default", + "X-Ray": "D-Boost master", + "Draygon": "No problemo" + } +} diff --git a/worlds/sm/variaRandomizer/standard_presets/solution.json b/worlds/sm/variaRandomizer/standard_presets/solution.json new file mode 100644 index 00000000..96dbd4d3 --- /dev/null +++ b/worlds/sm/variaRandomizer/standard_presets/solution.json @@ -0,0 +1,376 @@ +{ + "score": 205, + "Controller": { + "A": "Jump", + "B": "Dash", + "Moonwalk": false, + "L": "Angle Down", + "R": "Angle Up", + "Y": "Item Cancel", + "X": "Shoot", + "Select": "Item Select" + }, + "password": "94aeb7486a094e800e3cd8437bec904d60a55dacf0e316e5f8da3dc6a1e3ac05", + "Knows": { + "CrouchJump": [ + false, + 0 + ], + "Mockball": [ + true, + 1 + ], + "MicrowavePhantoon": [ + true, + 10 + ], + "GreenGateGlitch": [ + true, + 5 + ], + "NovaBoost": [ + true, + 100 + ], + "ScrewAttackExit": [ + true, + 10 + ], + "WorstRoomIceCharge": [ + true, + 100 + ], + "RedTowerClimb": [ + true, + 10 + ], + "TediousMountEverest": [ + true, + 100 + ], + "MockballWs": [ + true, + 50 + ], + "RonPopeilScrew": [ + true, + 100 + ], + "MidAirMorph": [ + false, + 0 + ], + "GravLessLevel1": [ + true, + 25 + ], + "CeilingDBoost": [ + true, + 1 + ], + "AlcatrazEscape": [ + true, + 10 + ], + "GravLessLevel3": [ + true, + 100 + ], + "BotwoonToDraygonWithIce": [ + true, + 100 + ], + "GetAroundWallJump": [ + true, + 10 + ], + "SpringBallJumpFromWall": [ + true, + 25 + ], + "ContinuousWallJump": [ + true, + 100 + ], + "LavaDive": [ + true, + 25 + ], + "HiJumpLessGauntletAccess": [ + true, + 25 + ], + "PuyoClipXRay": [ + true, + 100 + ], + "ShortCharge": [ + true, + 50 + ], + "PreciousRoomXRayExit": [ + true, + 100 + ], + "LowGauntlet": [ + true, + 100 + ], + "WallJumpCathedralExit": [ + true, + 5 + ], + "DoubleSpringBallJump": [ + true, + 50 + ], + "SpongeBathSpeed": [ + true, + 1 + ], + "MicrowaveDraygon": [ + true, + 5 + ], + "Moondance": [ + true, + 100 + ], + "HiJumpMamaTurtle": [ + true, + 100 + ], + "SpeedZebSkip": [ + true, + 50 + ], + "SpongeBathHiJump": [ + true, + 1 + ], + "DraygonRoomGrappleExit": [ + true, + 100 + ], + "SpongeBathBombJump": [ + true, + 100 + ], + "SuitlessPuyoClip": [ + true, + 100 + ], + "XrayDboost": [ + true, + 50 + ], + "PuyoClip": [ + true, + 100 + ], + "ReverseGateGlitch": [ + true, + 5 + ], + "AccessSpringBallWithXRayClimb": [ + true, + 100 + ], + "AccessSpringBallWithGravJump": [ + true, + 100 + ], + "DraygonSparkKill": [ + true, + 25 + ], + "AccessSpringBallWithBombJumps": [ + true, + 50 + ], + "AccessSpringBallWithSpringBallBombJumps": [ + true, + 10 + ], + "ReverseGateGlitchHiJumpLess": [ + true, + 50 + ], + "DraygonRoomCrystalFlash": [ + true, + 100 + ], + "GravityJump": [ + true, + 10 + ], + "KillPlasmaPiratesWithCharge": [ + true, + 5 + ], + "GravLessLevel2": [ + true, + 100 + ], + "LavaDiveNoHiJump": [ + true, + 100 + ], + "ShineSpark": [ + false, + 0 + ], + "AccessSpringBallWithSpringBallJump": [ + true, + 50 + ], + "NorfairReserveDBoost": [ + true, + 5 + ], + "SnailClip": [ + true, + 50 + ], + "KillPlasmaPiratesWithSpark": [ + true, + 50 + ], + "SpringBallJump": [ + true, + 10 + ], + "WallJump": [ + false, + 0 + ], + "SimpleShortCharge": [ + true, + 1 + ], + "DiagonalBombJump": [ + true, + 100 + ], + "IceZebSkip": [ + true, + 10 + ], + "CrocPBsDBoost": [ + true, + 5 + ], + "CrocPBsIce": [ + true, + 5 + ], + "MochtroidClip": [ + true, + 10 + ], + "HiJumpGauntletAccess": [ + true, + 25 + ], + "EarlyKraid": [ + true, + 1 + ], + "IceMissileFromCroc": [ + true, + 10 + ], + "InfiniteBombJump": [ + true, + 5 + ], + "DraygonGrappleKill": [ + true, + 5 + ], + "FrogSpeedwayWithoutSpeed": [ + true, + 5 + ], + "IceEscape": [ + true, + 50 + ], + "ScrewAttackExitWithoutScrew": [ + true, + 25 + ], + "OldMBWithSpeed": [ + true, + 100 + ], + "FirefleasWalljump": [ + true, + 25 + ], + "CrystalFlashClip": [ + true, + 100 + ], + "SuitlessCrystalFlashClip": [ + true, + 100 + ], + "DodgeLowerNorfairEnemies": [ + true, + 100 + ], + "WorstRoomWallJump": [ + true, + 100 + ], + "AccessSpringBallWithFlatley": [ + true, + 100 + ], + "MaridiaWallJumps": [ + true, + 5 + ], + "WestSandHoleSuitlessWallJumps": [ + true, + 100 + ], + "PreciousRoomGravJumpExit": [ + true, + 100 + ], + "LowAmmoCroc": [ + true, + 100 + ], + "LowStuffBotwoon": [ + true, + 100 + ], + "LowStuffGT": [ + true, + 100 + ], + "MtEverestGravJump": [ + true, + 50 + ], + "DoubleChamberWallJump": [ + true, + 1 + ] + }, + "Settings": { + "Kraid": "Default", + "MainUpperNorfair": "No thanks", + "Phantoon": "Default", + "Ice": "No thanks", + "Ridley": "Default", + "MotherBrain": "Default", + "LowerNorfair": "Default", + "Gauntlet": "Default", + "X-Ray": "Aarghh", + "Draygon": "Default" + } +} diff --git a/worlds/sm/variaRandomizer/standard_presets/veteran.json b/worlds/sm/variaRandomizer/standard_presets/veteran.json new file mode 100644 index 00000000..cabf1816 --- /dev/null +++ b/worlds/sm/variaRandomizer/standard_presets/veteran.json @@ -0,0 +1,388 @@ +{ + "Knows": { + "WallJump": [ + false, + 0 + ], + "ShineSpark": [ + false, + 0 + ], + "MidAirMorph": [ + false, + 0 + ], + "CrouchJump": [ + false, + 0 + ], + "Mockball": [ + true, + 1 + ], + "SimpleShortCharge": [ + true, + 1 + ], + "InfiniteBombJump": [ + true, + 1 + ], + "GreenGateGlitch": [ + true, + 5 + ], + "ShortCharge": [ + false, + 0 + ], + "GravityJump": [ + true, + 5 + ], + "SpringBallJump": [ + true, + 5 + ], + "SpringBallJumpFromWall": [ + true, + 10 + ], + "GetAroundWallJump": [ + true, + 1 + ], + "DraygonGrappleKill": [ + true, + 1 + ], + "DraygonSparkKill": [ + true, + 5 + ], + "MicrowaveDraygon": [ + true, + 1 + ], + "MicrowavePhantoon": [ + true, + 5 + ], + "LowAmmoCroc": [ + false, + 0 + ], + "LowStuffBotwoon": [ + true, + 25 + ], + "LowStuffGT": [ + false, + 0 + ], + "IceZebSkip": [ + true, + 5 + ], + "SpeedZebSkip": [ + false, + 0 + ], + "HiJumpMamaTurtle": [ + true, + 5 + ], + "MaridiaWallJumps": [ + true, + 1 + ], + "MtEverestGravJump": [ + true, + 10 + ], + "GravLessLevel1": [ + true, + 10 + ], + "GravLessLevel2": [ + true, + 100 + ], + "GravLessLevel3": [ + false, + 0 + ], + "CeilingDBoost": [ + true, + 1 + ], + "BillyMays": [ + true, + 1 + ], + "AlcatrazEscape": [ + true, + 1 + ], + "ReverseGateGlitch": [ + true, + 1 + ], + "ReverseGateGlitchHiJumpLess": [ + true, + 10 + ], + "EarlyKraid": [ + true, + 1 + ], + "XrayDboost": [ + true, + 25 + ], + "XrayIce": [ + true, + 10 + ], + "RedTowerClimb": [ + true, + 1 + ], + "RonPopeilScrew": [ + false, + 0 + ], + "OldMBWithSpeed": [ + false, + 0 + ], + "Moondance": [ + false, + 0 + ], + "HiJumpLessGauntletAccess": [ + true, + 5 + ], + "HiJumpGauntletAccess": [ + true, + 1 + ], + "LowGauntlet": [ + false, + 0 + ], + "IceEscape": [ + false, + 0 + ], + "WallJumpCathedralExit": [ + true, + 1 + ], + "BubbleMountainWallJump": [ + true, + 5 + ], + "NovaBoost": [ + false, + 0 + ], + "NorfairReserveDBoost": [ + true, + 50 + ], + "CrocPBsDBoost": [ + false, + 0 + ], + "CrocPBsIce": [ + true, + 10 + ], + "IceMissileFromCroc": [ + false, + 0 + ], + "FrogSpeedwayWithoutSpeed": [ + false, + 0 + ], + "LavaDive": [ + true, + 5 + ], + "LavaDiveNoHiJump": [ + false, + 0 + ], + "WorstRoomIceCharge": [ + true, + 25 + ], + "WorstRoomWallJump": [ + false, + 0 + ], + "ScrewAttackExit": [ + true, + 5 + ], + "ScrewAttackExitWithoutScrew": [ + false, + 0 + ], + "FirefleasWalljump": [ + true, + 10 + ], + "DodgeLowerNorfairEnemies": [ + false, + 0 + ], + "ContinuousWallJump": [ + true, + 50 + ], + "DiagonalBombJump": [ + true, + 50 + ], + "MockballWs": [ + true, + 10 + ], + "SpongeBathBombJump": [ + true, + 50 + ], + "SpongeBathHiJump": [ + true, + 1 + ], + "SpongeBathSpeed": [ + true, + 1 + ], + "TediousMountEverest": [ + false, + 0 + ], + "DoubleSpringBallJump": [ + false, + 0 + ], + "BotwoonToDraygonWithIce": [ + false, + 0 + ], + "WestSandHoleSuitlessWallJumps": [ + false, + 0 + ], + "DraygonRoomGrappleExit": [ + false, + 0 + ], + "DraygonRoomCrystalFlash": [ + false, + 0 + ], + "PreciousRoomXRayExit": [ + false, + 0 + ], + "PreciousRoomGravJumpExit": [ + false, + 0 + ], + "MochtroidClip": [ + true, + 1 + ], + "PuyoClip": [ + false, + 0 + ], + "PuyoClipXRay": [ + true, + 10 + ], + "SnailClip": [ + true, + 10 + ], + "SuitlessPuyoClip": [ + false, + 0 + ], + "CrystalFlashClip": [ + false, + 0 + ], + "SuitlessCrystalFlashClip": [ + false, + 0 + ], + "KillPlasmaPiratesWithSpark": [ + true, + 10 + ], + "KillPlasmaPiratesWithCharge": [ + true, + 1 + ], + "AccessSpringBallWithHiJump": [ + true, + 1 + ], + "AccessSpringBallWithSpringBallBombJumps": [ + true, + 5 + ], + "AccessSpringBallWithBombJumps": [ + true, + 100 + ], + "AccessSpringBallWithSpringBallJump": [ + true, + 25 + ], + "AccessSpringBallWithXRayClimb": [ + false, + 0 + ], + "AccessSpringBallWithGravJump": [ + false, + 0 + ], + "AccessSpringBallWithFlatley": [ + false, + 0 + ] + }, + "Settings": { + "Ice": "Default", + "MainUpperNorfair": "Default", + "LowerNorfair": "Default", + "Kraid": "Default", + "Phantoon": "Used to it", + "Draygon": "Used to it", + "Ridley": "Used to it", + "MotherBrain": "Is this really the last boss?", + "X-Ray": "Default", + "Gauntlet": "Default" + }, + "Controller": { + "A": "Jump", + "B": "Dash", + "X": "Shoot", + "Y": "Item Cancel", + "L": "Angle Down", + "R": "Angle Up", + "Select": "Item Select", + "Moonwalk": false + }, + "password": "c27373b41e8a43a0af6f9d7285bac03fe8e3f2300e2e008991b78e9e2658453e", + "score": 272 +} \ No newline at end of file diff --git a/worlds/sm/variaRandomizer/utils/__init__.py b/worlds/sm/variaRandomizer/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/worlds/sm/variaRandomizer/utils/doorsmanager.py b/worlds/sm/variaRandomizer/utils/doorsmanager.py new file mode 100644 index 00000000..49b4540b --- /dev/null +++ b/worlds/sm/variaRandomizer/utils/doorsmanager.py @@ -0,0 +1,397 @@ +import random +import copy +from logic.smbool import SMBool +from rom.rom_patches import RomPatches +import utils.log, logging + +LOG = utils.log.get('DoorsManager') + +colorsList = ['red', 'green', 'yellow', 'wave', 'spazer', 'plasma', 'ice'] +# 1/15 chance to have the door set to grey +colorsListGrey = colorsList * 2 + ['grey'] + +class Facing: + Left = 0 + Right = 1 + Top = 2 + Bottom = 3 + +# door facing left - right - top - bottom +plmRed = [0xc88a, 0xc890, 0xc896, 0xc89c] +plmGreen = [0xc872, 0xc878, 0xc87e, 0xc884] +plmYellow = [0xc85a, 0xc860, 0xc866, 0xc86c] +plmGrey = [0xc842, 0xc848, 0xc84e, 0xc854] +plmWave = [0xf763, 0xf769, 0xf70f, 0xf715] +plmSpazer = [0xf733, 0xf739, 0xf73f, 0xf745] +plmPlasma = [0xf74b, 0xf751, 0xf757, 0xf75d] +plmIce = [0xf71b, 0xf721, 0xf727, 0xf72d] + +colors2plm = { + 'red': plmRed, + 'green': plmGreen, + 'yellow': plmYellow, + 'grey': plmGrey, + 'wave': plmWave, + 'spazer': plmSpazer, + 'plasma': plmPlasma, + 'ice': plmIce +} + +class Door(object): + __slots__ = ('name', 'address', 'vanillaColor', 'color', 'forced', 'facing', 'hidden', 'id', 'canGrey', 'forbiddenColors') + def __init__(self, name, address, vanillaColor, facing, id=None, canGrey=False, forbiddenColors=None): + self.name = name + self.address = address + self.vanillaColor = vanillaColor + self.setColor(vanillaColor) + self.forced = False + self.facing = facing + self.hidden = False + self.canGrey = canGrey + self.id = id + # list of forbidden colors + self.forbiddenColors = forbiddenColors + + def forceBlue(self): + # custom start location, area, patches can force doors to blue + self.setColor('blue') + self.forced = True + + def setColor(self, color): + self.color = color + + def getColor(self): + if self.hidden: + return 'grey' + else: + return self.color + + def isRandom(self): + return self.color != self.vanillaColor and not self.isBlue() + + def isBlue(self): + return self.color == 'blue' + + def canRandomize(self): + return not self.forced and self.id is None + + def filterColorList(self, colorsList): + if self.forbiddenColors is None: + return colorsList + else: + return [color for color in colorsList if color not in self.forbiddenColors] + + def randomize(self, allowGreyDoors): + if self.canRandomize(): + if self.canGrey and allowGreyDoors: + self.setColor(random.choice(self.filterColorList(colorsListGrey))) + else: + self.setColor(random.choice(self.filterColorList(colorsList))) + + def traverse(self, smbm): + if self.hidden or self.color == 'grey': + return SMBool(False) + elif self.color == 'red': + return smbm.canOpenRedDoors() + elif self.color == 'green': + return smbm.canOpenGreenDoors() + elif self.color == 'yellow': + return smbm.canOpenYellowDoors() + elif self.color == 'wave': + return smbm.haveItem('Wave') + elif self.color == 'spazer': + return smbm.haveItem('Spazer') + elif self.color == 'plasma': + return smbm.haveItem('Plasma') + elif self.color == 'ice': + return smbm.haveItem('Ice') + else: + return SMBool(True) + + def __repr__(self): + return "Door({}, {})".format(self.name, self.color) + + def isRefillSave(self): + return self.address is None + + def writeColor(self, rom): + if self.isBlue() or self.isRefillSave(): + return + + rom.writeWord(colors2plm[self.color][self.facing], self.address) + + # also set plm args high byte to never opened, even during escape + if self.color == 'grey': + rom.writeByte(0x90, self.address+5) + + def readColor(self, rom): + if self.forced or self.isRefillSave(): + return + + plm = rom.readWord(self.address) + if plm in plmRed: + self.setColor('red') + elif plm in plmGreen: + self.setColor('green') + elif plm in plmYellow: + self.setColor('yellow') + elif plm in plmGrey: + self.setColor('grey') + elif plm in plmWave: + self.setColor('wave') + elif plm in plmSpazer: + self.setColor('spazer') + elif plm in plmPlasma: + self.setColor('plasma') + elif plm in plmIce: + self.setColor('ice') + else: + raise Exception("Unknown color {} for {}".format(hex(plm), self.name)) + + # for tracker + def canHide(self): + return self.color != 'blue' + + def hide(self): + if self.canHide(): + self.hidden = True + + def reveal(self): + self.hidden = False + + def switch(self): + if self.hidden: + self.reveal() + else: + self.hide() + + # to send/receive state to tracker/plando + def serialize(self): + return (self.color, self.facing, self.hidden) + + def unserialize(self, data): + self.setColor(data[0]) + self.facing = data[1] + self.hidden = data[2] + +class DoorsManager(): + doorsDict = {} + doors = { + # crateria + 'LandingSiteRight': Door('LandingSiteRight', 0x78018, 'green', Facing.Left, canGrey=True), + 'LandingSiteTopRight': Door('LandingSiteTopRight', 0x07801e, 'yellow', Facing.Left), + 'KihunterBottom': Door('KihunterBottom', 0x78228, 'yellow', Facing.Top, canGrey=True), + 'KihunterRight': Door('KihunterRight', 0x78222, 'yellow', Facing.Left, canGrey=True), + 'FlywayRight': Door('FlywayRight', 0x78420, 'red', Facing.Left), + 'GreenPiratesShaftBottomRight': Door('GreenPiratesShaftBottomRight', 0x78470, 'red', Facing.Left, canGrey=True), + 'RedBrinstarElevatorTop': Door('RedBrinstarElevatorTop', 0x78256, 'yellow', Facing.Bottom), + 'ClimbRight': Door('ClimbRight', 0x78304, 'yellow', Facing.Left), + # blue brinstar + 'ConstructionZoneRight': Door('ConstructionZoneRight', 0x78784, 'red', Facing.Left), + # green brinstar + 'GreenHillZoneTopRight': Door('GreenHillZoneTopRight', 0x78670, 'yellow', Facing.Left, canGrey=True), + 'NoobBridgeRight': Door('NoobBridgeRight', 0x787a6, 'green', Facing.Left, canGrey=True), + 'MainShaftRight': Door('MainShaftRight', 0x784be, 'red', Facing.Left), + 'MainShaftBottomRight': Door('MainShaftBottomRight', 0x784c4, 'red', Facing.Left, canGrey=True), + 'EarlySupersRight': Door('EarlySupersRight', 0x78512, 'red', Facing.Left), + 'EtecoonEnergyTankLeft': Door('EtecoonEnergyTankLeft', 0x787c8, 'green', Facing.Right), + # pink brinstar + 'BigPinkTopRight': Door('BigPinkTopRight', 0x78626, 'red', Facing.Left), + 'BigPinkRight': Door('BigPinkRight', 0x7861a, 'yellow', Facing.Left), + 'BigPinkBottomRight': Door('BigPinkBottomRight', 0x78620, 'green', Facing.Left, canGrey=True), + 'BigPinkBottomLeft': Door('BigPinkBottomLeft', 0x7862c, 'red', Facing.Right), + # red brinstar + 'RedTowerLeft': Door('RedTowerLeft', 0x78866, 'yellow', Facing.Right), + 'RedBrinstarFirefleaLeft': Door('RedBrinstarFirefleaLeft', 0x7886e, 'red', Facing.Right), + 'RedTowerElevatorTopLeft': Door('RedTowerElevatorTopLeft', 0x788aa, 'green', Facing.Right), + 'RedTowerElevatorLeft': Door('RedTowerElevatorLeft', 0x788b0, 'yellow', Facing.Right), + 'RedTowerElevatorBottomLeft': Door('RedTowerElevatorBottomLeft', 0x788b6, 'green', Facing.Right), + 'BelowSpazerTopRight': Door('BelowSpazerTopRight', 0x78966, 'green', Facing.Left), + # Wrecked ship + 'WestOceanRight': Door('WestOceanRight', 0x781e2, 'green', Facing.Left, canGrey=True), + 'LeCoudeBottom': Door('LeCoudeBottom', 0x7823e, 'yellow', Facing.Top, canGrey=True), + 'WreckedShipMainShaftBottom': Door('WreckedShipMainShaftBottom', 0x7c277, 'green', Facing.Top), + 'ElectricDeathRoomTopLeft': Door('ElectricDeathRoomTopLeft', 0x7c32f, 'red', Facing.Right), + # Upper Norfair + 'BusinessCenterTopLeft': Door('BusinessCenterTopLeft', 0x78b00, 'green', Facing.Right), + 'BusinessCenterBottomLeft': Door('BusinessCenterBottomLeft', 0x78b0c, 'red', Facing.Right), + 'CathedralEntranceRight': Door('CathedralEntranceRight', 0x78af2, 'red', Facing.Left, canGrey=True), + 'CathedralRight': Door('CathedralRight', 0x78aea, 'green', Facing.Left), + 'BubbleMountainTopRight': Door('BubbleMountainTopRight', 0x78c60, 'green', Facing.Left), + 'BubbleMountainTopLeft': Door('BubbleMountainTopLeft', 0x78c5a, 'green', Facing.Right), + 'SpeedBoosterHallRight': Door('SpeedBoosterHallRight', 0x78c7a, 'red', Facing.Left), + 'SingleChamberRight': Door('SingleChamberRight', 0x78ca8, 'red', Facing.Left), + 'DoubleChamberRight': Door('DoubleChamberRight', 0x78cc2, 'red', Facing.Left), + 'KronicBoostBottomLeft': Door('KronicBoostBottomLeft', 0x78d4e, 'yellow', Facing.Right, canGrey=True), + 'CrocomireSpeedwayBottom': Door('CrocomireSpeedwayBottom', 0x78b96, 'green', Facing.Top, canGrey=True), + # Crocomire + 'PostCrocomireUpperLeft': Door('PostCrocomireUpperLeft', 0x78bf4, 'red', Facing.Right), + 'PostCrocomireShaftRight': Door('PostCrocomireShaftRight', 0x78c0c, 'red', Facing.Left), + # Lower Norfair + 'RedKihunterShaftBottom': Door('RedKihunterShaftBottom', 0x7902e, 'yellow', Facing.Top), + 'WastelandLeft': Door('WastelandLeft', 0x790ba, 'green', Facing.Right, forbiddenColors=['yellow']), + # Maridia + 'MainStreetBottomRight': Door('MainStreetBottomRight', 0x7c431, 'red', Facing.Left), + 'FishTankRight': Door('FishTankRight', 0x7c475, 'red', Facing.Left), + 'CrabShaftRight': Door('CrabShaftRight', 0x7c4fb, 'green', Facing.Left), + 'ColosseumBottomRight': Door('ColosseumBottomRight', 0x7c6fb, 'green', Facing.Left), + 'PlasmaSparkBottom': Door('PlasmaSparkBottom', 0x7c577, 'green', Facing.Top), + 'OasisTop': Door('OasisTop', 0x7c5d3, 'green', Facing.Bottom), + # refill/save + 'GreenBrinstarSaveStation': Door('GreenBrinstarSaveStation', None, 'red', Facing.Right, id=0x1f), + 'MaridiaBottomSaveStation': Door('MaridiaBottomSaveStation', None, 'red', Facing.Left, id=0x8c), + 'MaridiaAqueductSaveStation': Door('MaridiaAqueductSaveStation', None, 'red', Facing.Right, id=0x96), + 'ForgottenHighwaySaveStation': Door('ForgottenHighwaySaveStation', None, 'red', Facing.Left, id=0x92), + 'DraygonSaveRefillStation': Door('DraygonSaveRefillStation', None, 'red', Facing.Left, id=0x98), + 'KraidRefillStation': Door('KraidRefillStation', None, 'green', Facing.Left, id=0x44), + 'RedBrinstarEnergyRefill': Door('RedBrinstarEnergyRefill', None, 'green', Facing.Right, id=0x38), + 'GreenBrinstarMissileRefill': Door('GreenBrinstarMissileRefill', None, 'red', Facing.Right, id=0x23) + } + + # call from logic + def traverse(self, smbm, doorName): + return DoorsManager.doorsDict[smbm.player][doorName].traverse(smbm) + + @staticmethod + def setDoorsColor(player=0): + if player not in DoorsManager.doorsDict.keys(): + DoorsManager.doorsDict[player] = copy.deepcopy(DoorsManager.doors) + currentDoors = DoorsManager.doorsDict[player] + + # depending on loaded patches, force some doors to blue, excluding them from randomization + if RomPatches.has(player, RomPatches.BlueBrinstarBlueDoor): + currentDoors['ConstructionZoneRight'].forceBlue() + if RomPatches.has(player, RomPatches.BrinReserveBlueDoors): + currentDoors['MainShaftRight'].forceBlue() + currentDoors['EarlySupersRight'].forceBlue() + if RomPatches.has(player, RomPatches.EtecoonSupersBlueDoor): + currentDoors['EtecoonEnergyTankLeft'].forceBlue() + #if RomPatches.has(player, RomPatches.SpongeBathBlueDoor): + # currentDoors[''].forceBlue() + if RomPatches.has(player, RomPatches.HiJumpAreaBlueDoor): + currentDoors['BusinessCenterBottomLeft'].forceBlue() + if RomPatches.has(player, RomPatches.SpeedAreaBlueDoors): + currentDoors['BubbleMountainTopRight'].forceBlue() + currentDoors['SpeedBoosterHallRight'].forceBlue() + if RomPatches.has(player, RomPatches.MamaTurtleBlueDoor): + currentDoors['FishTankRight'].forceBlue() + if RomPatches.has(player, RomPatches.HellwayBlueDoor): + currentDoors['RedTowerElevatorLeft'].forceBlue() + if RomPatches.has(player, RomPatches.RedTowerBlueDoors): + currentDoors['RedBrinstarElevatorTop'].forceBlue() + if RomPatches.has(player, RomPatches.AreaRandoBlueDoors): + currentDoors['GreenHillZoneTopRight'].forceBlue() + currentDoors['NoobBridgeRight'].forceBlue() + currentDoors['LeCoudeBottom'].forceBlue() + currentDoors['KronicBoostBottomLeft'].forceBlue() + else: + # no area rando, prevent some doors to be in the grey doors pool + currentDoors['GreenPiratesShaftBottomRight'].canGrey = False + currentDoors['CrocomireSpeedwayBottom'].canGrey = False + currentDoors['KronicBoostBottomLeft'].canGrey = False + if RomPatches.has(player, RomPatches.AreaRandoMoreBlueDoors): + currentDoors['KihunterBottom'].forceBlue() + currentDoors['GreenPiratesShaftBottomRight'].forceBlue() + if RomPatches.has(player, RomPatches.CrocBlueDoors): + currentDoors['CrocomireSpeedwayBottom'].forceBlue() + if RomPatches.has(player, RomPatches.CrabShaftBlueDoor): + currentDoors['CrabShaftRight'].forceBlue() + + @staticmethod + def randomize(allowGreyDoors, player): + for door in DoorsManager.doorsDict[player].values(): + door.randomize(allowGreyDoors) + # set both ends of toilet to the same color to avoid soft locking in area rando + toiletTop = DoorsManager.doorsDict[player]['PlasmaSparkBottom'] + toiletBottom = DoorsManager.doorsDict[player]['OasisTop'] + if toiletTop.color != toiletBottom.color: + toiletBottom.setColor(toiletTop.color) + DoorsManager.debugDoorsColor() + + # call from rom loader + @staticmethod + def loadDoorsColor(rom): + # force to blue some doors depending on patches + DoorsManager.setDoorsColor() + # for each door store it's color + for door in DoorsManager.doors.values(): + door.readColor(rom) + DoorsManager.debugDoorsColor() + + # tell that we have randomized doors + isRandom = DoorsManager.isRandom() + if isRandom: + DoorsManager.setRefillSaveToBlue() + return isRandom + + @staticmethod + def isRandom(player): + return any(door.isRandom() for door in DoorsManager.doorsDict[player].values()) + + @staticmethod + def setRefillSaveToBlue(player): + for door in DoorsManager.doorsDict[player].values(): + if door.id is not None: + door.forceBlue() + + @staticmethod + def debugDoorsColor(): + if LOG.getEffectiveLevel() == logging.DEBUG: + for door in DoorsManager.doors.values(): + LOG.debug("{:>32}: {:>6}".format(door.name, door.color)) + + # call from rom patcher + @staticmethod + def writeDoorsColor(rom, doors, player): + for door in DoorsManager.doorsDict[player].values(): + door.writeColor(rom) + # also set save/refill doors to blue + if door.id is not None: + doors.append(door.id) + + # call from web + @staticmethod + def getAddressesToRead(): + return [door.address for door in DoorsManager.doors.values() if door.address is not None] + [door.address+1 for door in DoorsManager.doors.values() if door.address is not None] + + # for isolver state + @staticmethod + def serialize(): + return {door.name: door.serialize() for door in DoorsManager.doors.values()} + + @staticmethod + def unserialize(state): + for name, data in state.items(): + DoorsManager.doors[name].unserialize(data) + + @staticmethod + def allDoorsRevealed(): + for door in DoorsManager.doors.values(): + if door.hidden: + return False + return True + + # when using the tracker, first set all colored doors to grey until the user clicks on it + @staticmethod + def initTracker(): + for door in DoorsManager.doors.values(): + door.hide() + + # when the user clicks on a door in the tracker + @staticmethod + def switchVisibility(name): + DoorsManager.doors[name].switch() + + # when the user clicks on a door in the race tracker or the plando + @staticmethod + def setColor(name, color): + # in race mode the doors are hidden + DoorsManager.doors[name].reveal() + DoorsManager.doors[name].setColor(color) + + # in autotracker we need the current doors state + @staticmethod + def getDoorsState(): + hiddenDoors = set([door.name for door in DoorsManager.doors.values() if door.hidden]) + revealedDoor = set([door.name for door in DoorsManager.doors.values() if (not door.hidden) and door.canHide()]) + return (hiddenDoors, revealedDoor) diff --git a/worlds/sm/variaRandomizer/utils/log.py b/worlds/sm/variaRandomizer/utils/log.py new file mode 100644 index 00000000..6e481ff8 --- /dev/null +++ b/worlds/sm/variaRandomizer/utils/log.py @@ -0,0 +1,16 @@ +import logging, sys + +# store the debug flag at module level +debug = False + +def init(pdebug): + global debug + debug = pdebug + + if debug == True: + logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) + else: + logging.basicConfig(level=logging.INFO) + +def get(name): + return logging.getLogger(name) diff --git a/worlds/sm/variaRandomizer/utils/parameters.py b/worlds/sm/variaRandomizer/utils/parameters.py new file mode 100644 index 00000000..7adec798 --- /dev/null +++ b/worlds/sm/variaRandomizer/utils/parameters.py @@ -0,0 +1,1163 @@ +from logic.smbool import SMBool +import os +import sys + +# the different difficulties available +easy = 1 +medium = 5 +hard = 10 +harder = 25 +hardcore = 50 +mania = 100 +god = mania*2 +samus = god*2 +impossibru = samus*2 +infinity = sys.maxsize + +diff2text = { + 0: 'baby', + easy: 'easy', + medium: 'medium', + hard: 'hard', + harder: 'very hard', + hardcore: 'hardcore', + mania: 'mania', + god: 'god', + samus: 'samus', + impossibru: 'impossibru', + infinity: 'infinity' +} + +text2diff = { + 'baby': 0, + 'easy': easy, + 'medium': medium, + 'hard': hard, + 'harder': harder, + 'very hard': harder, + 'hardcore': hardcore, + 'mania': mania, + 'god': god, + 'samus': samus, + 'impossibru': impossibru, + 'infinity': infinity +} + +def diff4solver(difficulty): + if difficulty == -1: + return "break" + elif difficulty < medium: + return "easy" + elif difficulty < hard: + return "medium" + elif difficulty < harder: + return "hard" + elif difficulty < hardcore: + return "harder" + elif difficulty < mania: + return "hardcore" + else: + return "mania" + +# allow multiple local repo +appDir = sys.path[0] + +def isKnows(knows): + return knows[0:len('__')] != '__' and knows[0] == knows[0].upper() + +def isBossKnows(knows): + ret = None + if isKnows(knows) and 'boss' in Knows.desc[knows]: + ret = Knows.desc[knows]['boss'] + return ret + +class Knows: + knowsDict = {} + # the different technics to know (cf. http://deanyd.net/sm/index.php?title=Item_Randomizer) + # and the personnal perceived difficulty. + # False means: I can't do this technic or I don't know it. + + # store the descriptions used by the website along side the definition of the knows + desc = {} + + # used across the game + WallJump = SMBool(True, easy, ['WallJump']) + desc['WallJump'] = {'display': 'Wall Jump', + 'title': 'Kick-jump from wall: wall to wall, single wall climb', + 'href': 'https://wiki.supermetroid.run/Walljump', + 'rooms': [], + 'readonly' : True} + + ShineSpark = SMBool(True, easy, ['ShineSpark']) + desc['ShineSpark'] = {'display': 'Shinespark', + 'title': 'With Speed Booster, press down to activate Shinespark. Then launch it in every possible direction, from ground or mid-air', + 'href': 'https://wiki.supermetroid.run/Shinespark', + 'rooms': [], + 'readonly' : True} + + MidAirMorph = SMBool(True, easy, ['MidAirMorph']) + desc['MidAirMorph'] = {'display': 'Mid-air Morph', + 'title': 'Activate Morph Ball while jumping straight up (keep jump pressed) to reach high places in Morph Ball form without bomb jumping', + 'href': None, + 'rooms': [], + 'readonly' : True} + + CrouchJump = SMBool(True, easy, ['CrouchJump']) + desc['CrouchJump'] = {'display': 'Crouch Jump', + 'title': 'Jump higher by crouching before jumping', + 'href': "https://www.youtube.com/watch?v=aTbubkfc7iE", + 'rooms': [], + 'readonly' : True} + + UnequipItem = SMBool(True, easy, ['UnequipItem']) + desc['UnequipItem'] = {'display': 'Unequip an item', + 'title': 'Unequip Gravity suit to have more time to morph after jumping or unequip Hi-Jump Boots to jump to limit jump height', + # TODO::add a video with all the occurences + 'href': None, + 'rooms': ['Main Street', 'Spring Ball Room', 'West Ocean'], + 'readonly' : True} + + Mockball = SMBool(True, easy, ['Mockball']) + desc['Mockball'] = {'display': 'Mockball', + 'title': 'Morph from runing without loosing momentum to get Early Super and Ice Beam', + 'href': 'https://wiki.supermetroid.run/index.php?title=Mockball', + 'rooms': ['Early Supers Room', 'Ice Beam Gate Room']} + + SimpleShortCharge = SMBool(True, easy, ['SimpleShortCharge']) + desc['SimpleShortCharge'] = {'display': 'Simple Short Charge', + 'title': 'Activate SpeedBooster faster than normal by delaying run button input', + 'href': 'https://wiki.supermetroid.run/index.php?title=Quick_charge', + 'rooms': ['Parlor and Alcatraz', 'Waterway Energy Tank Room', + 'Landing Site', 'Crateria Keyhunter Room', 'Blue Brinstar Energy Tank Room', + 'Crateria Super Room', 'Main Street', "Golden Torizo's Room"]} + + InfiniteBombJump = SMBool(True, medium, ['InfiniteBombJump']) + desc['InfiniteBombJump'] = {'display': 'Infinite Bomb-Jump', + 'title': 'To access certain locations without Hi-Jump or Space-Jump', + 'href': 'https://www.youtube.com/watch?v=Qfmcm7hkXP4', + 'rooms': ['Blue Brinstar Energy Tank Room', 'Bubble Mountain', 'Screw Attack Room', 'Mama Turtle Room', 'Plasma Room', "Draygon's Room", 'Landing Site', 'Crocomire Escape', 'Post Crocomire Farming Room', 'Post Crocomire Jump Room', 'Warehouse Entrance', 'The Worst Room In The Game', 'Mt. Everest', 'The Moat', 'Red Brinstar Fireflea Room', 'Crab Hole', 'Cathedral Entrance', 'Red Tower', 'The Precious Room']} + + GreenGateGlitch = SMBool(True, medium, ['GreenGateGlitch']) + desc['GreenGateGlitch'] = {'display': 'Green Gate Glitch', + 'title': 'Open gates from other side to access Screw Attack and Crocomire', + 'href': 'https://wiki.supermetroid.run/index.php?title=Gate_Glitch', + 'rooms': ['Green Hill Zone', 'Grapple Tutorial Room 3', 'Fast Ripper Room', 'Upper Norfair Farming Room', 'Crab Tunnel', 'Double Chamber']} + + ShortCharge = SMBool(False, 0, ['ShortCharge']) + desc['ShortCharge'] = {'display': 'Tight Short Charge', + 'title': 'Activate SpeedBooster really fast (3 taps or stutter 3)', + 'href': 'https://wiki.supermetroid.run/index.php?title=Short_Charge', + 'rooms': ['Red Tower', 'Landing Site', 'Gauntlet Energy Tank Room', 'Plasma Room', + 'Post Crocomire Jump Room', 'Crocomire Escape', 'Wasteland', 'Kraid Room']} + + GravityJump = SMBool(True, hard, ['GravityJump']) + desc['GravityJump'] = {'display': 'Gravity-Jump', + 'title': "Super Hi-Jumps in water/lava using game's bug", + 'href': 'https://wiki.supermetroid.run/index.php?title=14%25#Gravity_Jump', + 'rooms': ["Draygon's Room", 'The Moat', 'Lava Dive Room', + 'Crab Hole', 'Mt. Everest', 'The Precious Room']} + + SpringBallJump = SMBool(True, hard, ['SpringBallJump']) + desc['SpringBallJump'] = {'display': 'SpringBall-Jump', + 'title': 'Do a SpringBall Jump from a jump to Access to Wrecked Ship Etank without anything else, Suitless Maridia navigation', + 'href': 'https://www.youtube.com/watch?v=8ldQUIgBavw&t=49s', + 'rooms': ['Sponge Bath', 'East Ocean', + 'Main Street', 'Crab Shaft', 'Pseudo Plasma Spark Room', + 'Mama Turtle Room', 'The Precious Room', 'Spring Ball Room', 'East Sand Hole', + 'Cathedral Entrance', 'Crocomire Escape', 'Post Crocomire Jump Room', + 'Red Brinstar Fireflea Room', 'Lower Norfair Fireflea Room']} + + SpringBallJumpFromWall = SMBool(True, harder, ['SpringBallJumpFromWall']) + desc['SpringBallJumpFromWall'] = {'display': 'SpringBall-Jump from wall', + 'title': 'Do a SpringBall jump after a Wall jump to exit Screw Attack area, climb Worst Room without Hi-Jump', + 'href': 'https://youtu.be/3bTe2MN7mS4', + 'rooms': ['Screw Attack Room', 'The Worst Room In The Game', 'Bubble Mountain', + 'Mama Turtle Room', 'Plasma Room', 'Cathedral Entrance']} + + GetAroundWallJump = SMBool(True, hard, ['GetAroundWallJump']) + desc['GetAroundWallJump'] = {'display': 'Get around Wall-Jump', + 'title': 'Tricky Wall-Jumps where you have to get around the platform you want to Wall-Jump on using Hi-Jump boots', + 'href': 'https://www.youtube.com/watch?v=2GPx-6ARSIw&t=137s', + 'rooms': ['The Worst Room In The Game', + 'Bubble Mountain', 'Plasma Room']} + + # bosses + DraygonGrappleKill = SMBool(True, medium, ['DraygonGrappleKill']) + desc['DraygonGrappleKill'] = {'display': 'Draygon Grapple Kill', + 'title': 'Instant kill on Draygon with electric grapple', + 'href': 'https://www.youtube.com/watch?v=gcemRrXqCbE', + 'rooms': ["Draygon's Room"], + 'boss': "Draygon"} + + DraygonSparkKill = SMBool(False, mania, ['DraygonSparkKill']) + desc['DraygonSparkKill'] = {'display': 'Draygon Spark Kill', + 'title': 'Kill Draygon using Speed Booster and shinesparks', + 'href': None, # TODO + 'rooms': ["Draygon's Room"], + 'boss': "Draygon"} + + MicrowaveDraygon = SMBool(True, easy, ['MicrowaveDraygon']) + desc['MicrowaveDraygon'] = {'display': 'Microwave Draygon', + 'title': 'Charge/Plasma/X-Ray glitch on Draygon', + 'href': 'https://www.youtube.com/watch?v=tj0VybUH6ZY', + 'rooms': ["Draygon's Room"], + 'boss': "Draygon"} + + MicrowavePhantoon = SMBool(True, medium, ['MicrowavePhantoon']) + desc['MicrowavePhantoon'] = {'display': 'Microwave Phantoon', + 'title': 'Same as Draygon, with a few missiles to start', + 'href': 'https://youtu.be/tox6blvT5Ao', + 'rooms': ["Phantoon's Room"], + 'boss': "Phantoon"} + + # mini-bosses + LowAmmoCroc = SMBool(False, 0, ['LowAmmoCroc']) + desc['LowAmmoCroc'] = {'display': 'Low Ammo Crocomire fight', + 'title': 'Use drops to fight Crocomire with no charge and just 10 missiles, or 5 missiles/5 supers. With the technique disabled and no charge beam, VARIA requires 5000 damage worth of ammo.', + 'href': None, + 'rooms': ["Crocomire's Room"]} + + LowStuffBotwoon = SMBool(False, 0, ['LowStuffBotwoon']) + desc['LowStuffBotwoon'] = {'display': 'Low Ammo/Health Botwoon fight', + 'title': 'Fight Botwoon with no charge and just 3500 damage worth of ammo (Botwoon has 3000 HP). With the technique disabled, VARIA requires 6000 damage worth of ammo (if no charge beam) and 4 tanks of energy (with no suits).', + 'href': None, + 'rooms': ["Botwoon's Room"]} + + LowStuffGT = SMBool(False, 0, ['LowStuffGT']) + desc['LowStuffGT'] = {'display': 'Low Ammo/Health Golden Torizo', + 'title': "Fight GT with either charge beam or 5 supers and nothing else. Otherwise require either 30 supers or Charge+Plasma and 4 tanks of energy (with Varia).", + 'href': None, + 'rooms': ["Golden Torizo's Room"]} + + # End Game + IceZebSkip = SMBool(False, 0, ['IceZebSkip']) + desc['IceZebSkip'] = {'display': 'Ice Zeb Skip', + 'title': 'Skip the Zebetites with Ice beam', + 'href': 'https://youtu.be/udowj-vMzMA', + 'rooms': ['Mother Brain Room']} + + SpeedZebSkip = SMBool(False, 0, ['SpeedZebSkip']) + desc['SpeedZebSkip'] = {'display': 'Speed Zeb Skip', + 'title': 'Skip the Zebetites with a shinespark', + 'href': 'https://www.youtube.com/watch?v=jEAgdWQ9kLQ', + 'rooms': ['Mother Brain Room']} + # maridia WJs + HiJumpMamaTurtle = SMBool(False, 0, ['HiJumpMamaTurtle']) + desc['HiJumpMamaTurtle'] = {'display': 'Mama Turtle E-Tank with High-Jump+Speed', + 'title': 'Access Mama Turtle E-Tank with High-Jump and Speed Booster (and Morph or X-Ray to turn around without moving)', + 'href': 'https://www.youtube.com/watch?v=1DINqLnINc8', + 'rooms': ['Mama Turtle Room']} + + MaridiaWallJumps = SMBool(True, medium, ['MaridiaWallJumps']) + desc['MaridiaWallJumps'] = {'display': 'Various Maridia wall jumps', + 'title': 'Kinda tricky wall jumps to: access items in West Sand Hole without Spring Ball or Bombs, exit Spring Ball area without Hi Jump, exit Crab Hole with Gravity+Hi-Jump', + 'href': 'https://youtu.be/F3xAJem6VlA', + 'rooms': ['West Sand Hole', 'Spring Ball Room', 'Crab Hole']} + + MtEverestGravJump = SMBool(False, 0, ['MtEverestGravJump']) + desc['MtEverestGravJump'] = {'display': 'Mount Everest Gravity Jump', + 'title': 'Access Mount Everest top door by doing a crouch+gravity jump in the last couple frames possible', + 'href': 'https://www.youtube.com/watch?v=sZLEzdTgJbI', + 'rooms': ['Mt. Everest']} + + # underwater grav-less + GravLessLevel1 = SMBool(True, hardcore, ['GravLessLevel1']) + desc['GravLessLevel1'] = {'display': 'Level 1', + 'title': 'Make your way underwater with Hi-Jump and Ice, freezing crabs and fishes. Access Botwoon with grapple.', + 'href': 'https://www.youtube.com/watch?v=c2xoPigezvM', + 'rooms': ['Main Street', 'Mt. Everest', 'Crab Shaft', 'Pseudo Plasma Spark Room', + 'Aqueduct', 'Botwoon Hallway', "Botwoon's Room", 'Botwoon Energy Tank Room', + 'Crab Hole', 'Crab Tunnel', 'East Ocean']} + + GravLessLevel2 = SMBool(False, 0, ['GravLessLevel2']) + desc['GravLessLevel2'] = {'display': 'Level 2', + 'title': "Level 1 + access Draygon's lair and do the Draygon fight (exiting Draygon are separate techniques)", + 'href': None, + 'rooms': ['Halfie Climb Room', 'Colosseum', 'The Precious Room', "Draygon's Room", + 'East Cactus Alley Room', 'West Cactus Alley Room']} + + GravLessLevel3 = SMBool(False, 0, ['GravLessLevel3']) + desc['GravLessLevel3'] = {'display': 'Level 3', + 'title': 'Level 2 and : no problem getting out of sand suitless, traverse mini-draygons area, wall jumps to access items in the left sand pit, access missile location in the right sand pit.', + 'href': 'https://www.youtube.com/watch?v=Fn2z0ByOcj4', + 'rooms': ['West Sand Hole', 'East Sand Hole', + 'West Sand Hall', 'East Sand Hall']} + # Area difficulties + + # Brinstar + CeilingDBoost = SMBool(True, easy, ['CeilingDBoost']) + desc['CeilingDBoost'] = {'display': 'Ceiling Damage Boost', + 'title': 'Hit an enemy at the right time to get the item in Blue Brinstar Ceiling', + 'href': 'https://www.metroid2002.com/3/early_items_blue_brinstar_energy_tank.php', + 'rooms': ['Blue Brinstar Energy Tank Room']} + + BillyMays = SMBool(True, easy, ['BillyMays']) + desc['BillyMays'] = {'display': 'Billy Mays access without Gravity or Space Jump', + 'title': 'Jump through the door to get on the invisible platform', + 'href': 'https://youtu.be/LOyj4CuOWik', + 'rooms': ['Blue Brinstar Boulder Room']} + + AlcatrazEscape = SMBool(True, harder, ['AlcatrazEscape']) + desc['AlcatrazEscape'] = {'display': 'Alcatraz Escape', + 'title': 'Escape from Bomb area using its entrance tunnel', + 'href': 'https://www.youtube.com/watch?v=XSBeLJJafjY', + 'rooms': ['Parlor and Alcatraz']} + + ReverseGateGlitch = SMBool(True, medium, ['ReverseGateGlitch']) + desc['ReverseGateGlitch'] = {'display': 'Reverse Gate Glitch', + 'title': 'Open wave gate in Pink Brinstar from bottom left corner with Hi-Jump', + 'href': 'https://wiki.supermetroid.run/Pink_Brinstar_Hopper_Room', + 'rooms': ['Pink Brinstar Hopper Room']} + + ReverseGateGlitchHiJumpLess = SMBool(False, 0, ['ReverseGateGlitchHiJumpLess']) + desc['ReverseGateGlitchHiJumpLess'] = {'display': 'Reverse Gate Glitch w/o Hi-Jump', + 'title': 'Open wave gate in Pink Brinstar from bottom left corner without Hi-Jump', + 'href': 'https://wiki.supermetroid.run/Pink_Brinstar_Hopper_Room', + 'rooms': ['Pink Brinstar Hopper Room']} + + EarlyKraid = SMBool(True, easy, ['EarlyKraid']) + desc['EarlyKraid'] = {'display': 'Early Kraid', + 'title': 'Access Kraid area by Wall-Jumping', + 'href': 'https://www.youtube.com/watch?v=rHMHqTHHqHs', + 'rooms': ['Warehouse Entrance']} + + XrayDboost = SMBool(False, 0, ['XrayDboost']) + desc['XrayDboost'] = {'display': 'X-Ray Damage Boost', + 'title': 'Get to X-Ray location without Space-Jump, Grapple (no energy requirements), Ice+Hi-Jump or Bombs (see Hard Room settings)', + 'href': 'https://www.twitch.tv/videos/168724062', + 'rooms': ['Red Brinstar Fireflea Room']} + + XrayIce = SMBool(True, hard, ['XrayIce']) + desc['XrayIce'] = {'display': 'X-Ray Ice Climb', + 'title': 'Get to X-Ray location with Ice beam and no Hi-Jump (see Hard Room settings for energy requirements)', + 'href': 'https://www.youtube.com/watch?v=j36noNULtI8', + 'rooms': ['Red Brinstar Fireflea Room']} + + RedTowerClimb = SMBool(True, harder, ['RedTowerClimb']) + desc['RedTowerClimb'] = {'display': 'Red Tower Climb', + 'title': 'Climb Red Tower without Ice or Space-Jump', + 'href': 'https://www.youtube.com/watch?v=g3goe6PZ4o0', + 'rooms': ['Red Tower']} + + RonPopeilScrew = SMBool(False, 0, ['RonPopeilScrew']) + desc['RonPopeilScrew'] = {'display': 'Bomb-less Ron Popeil Missiles', + 'title': 'Access the most behind missile in Green Brinstar Reserve with Morph and Screw Attack', + 'href': 'https://wiki.supermetroid.run/Brinstar_Reserve_Tank_Room', + 'rooms': ['Brinstar Reserve Tank Room']} + + OldMBWithSpeed = SMBool(False, mania, ['OldMBWithSpeed']) + desc['OldMBWithSpeed'] = {'display': 'Old Mother Brain with Speed', + 'title': 'Access Old Mother Brain Missile pack location with just the Speed Booster', + 'href': 'https://www.youtube.com/watch?v=-SO2QykqnZw', + 'rooms': ['Climb', 'Pit Room']} + + Moondance = SMBool(False, mania, ['Moondance']) + desc['Moondance'] = {'display': 'Moondance', + 'title': 'Access Etecoons area using moonfall shenanigans', + 'href': 'http://crocomi.re/92', + 'rooms': ['Green Brinstar Main Shaft']} + # Gauntlet + HiJumpLessGauntletAccess = SMBool(False, 0, ['HiJumpLessGauntletAccess']) + desc['HiJumpLessGauntletAccess'] = {'display': 'Gauntlet Access w/o Hi-Jump', + 'title': 'Access Gauntlet area using really tricky Wall-Jumps', + 'href': 'https://www.youtube.com/watch?v=uVU2X-egOTI&t=25s', + 'rooms': ['Landing Site']} + + HiJumpGauntletAccess = SMBool(True, harder, ['HiJumpGauntletAccess']) + desc['HiJumpGauntletAccess'] = {'display': 'Hi-Jump Gauntlet Access', + 'title': 'Access Gauntlet area using tricky Wall-Jumps', + 'href': 'https://www.youtube.com/watch?v=2a6mf-kB60U', + 'rooms': ['Landing Site']} + + LowGauntlet = SMBool(False, 0, ['LowGauntlet']) + desc['LowGauntlet'] = {'display': 'Gauntlet Minors Access', + 'title': 'Access Gauntlet minors with SpeedBooster, 1 Etank and 1 Power Bomb pack', + 'href': 'https://www.youtube.com/watch?v=JU6BFcjuR4c', + 'rooms': ['Landing Site', 'Gauntlet Entrance', 'Gauntlet Energy Tank Room']} + + # Norfair + IceEscape = SMBool(False, 0, ['IceEscape']) + desc['IceEscape'] = {'display': 'Ice Escape', + 'title': 'Freeze the platforms and exit Ice Beam area without bombs', + 'href': 'https://www.youtube.com/watch?v=lFJPqu8qk54', + 'rooms': ['Ice Beam Acid Room']} + + WallJumpCathedralExit = SMBool(True, easy, ['WallJumpCathedralExit']) + desc['WallJumpCathedralExit'] = {'display': 'Wall Jump to exit Cathedral', + 'title': 'Use a delayed wall jump to exit Cathedral', + 'href': 'https://www.youtube.com/watch?v=CqQik2z6IkE', + 'rooms': ['Cathedral Entrance']} + + BubbleMountainWallJump = SMBool(True, medium, ['BubbleMountainWallJump']) + desc['BubbleMountainWallJump'] = {'display': 'Bubble Mountain wall jump', + 'title': 'Run from the save room and get up Bubble Mountain without Hi-Jump', + 'href': 'https://youtu.be/2RmbFRCMlUg', + 'rooms': ['Bubble Mountain']} + + DoubleChamberWallJump = SMBool(True, easy, ['DoubleChamberWallJump']) + desc['DoubleChamberWallJump'] = {'display': 'Double Chamber wall jump', + 'title': 'Climb up Double Chamber (pre-Wave Beam room) after entering through the bottom door', + 'href': 'https://www.youtube.com/watch?v=MVaaoW8Y_VU', + 'rooms': ['Double Chamber']} + + NovaBoost = SMBool(False, 0, ['NovaBoost']) + desc['NovaBoost'] = {'display': 'Nova Boost', + 'title': 'Use a D-Boost on the Sova to enter Cathedral', + 'href': 'https://www.twitch.tv/videos/144055441', + 'rooms': ['Cathedral Entrance']} + + NorfairReserveDBoost = SMBool(False, 0, ['NorfairReserveDBoost']) + desc['NorfairReserveDBoost'] = {'display': 'Norfair Reserve Damage Boost', + 'title': 'Use a D-Boost to reach Norfair Reserve area', + 'href': 'https://youtu.be/PglBIsdAiFI', + 'rooms': ['Bubble Mountain']} + + CrocPBsDBoost = SMBool(False, 0, ['CrocPBsDBoost']) + desc['CrocPBsDBoost'] = {'display': 'Crocomire Power Bombs Damage Boost', + 'title': 'Use a D-Boost to reach Crocomire Power Bombs', + 'href': 'https://youtu.be/ld8FC_Q9c6Y', + 'rooms': ['Post Crocomire Farming Room']} + + CrocPBsIce = SMBool(False, 0, ['CrocPBsIce']) + desc['CrocPBsIce'] = {'display': 'Crocomire Power Bombs with Ice Beam', + 'title': 'Get the farm bugs up and freeze them to reach Crocomire Power Bombs', + 'href': 'https://www.youtube.com/watch?v=ERer642mil8', + 'rooms': ['Post Crocomire Farming Room']} + + IceMissileFromCroc = SMBool(False, 0, ['IceMissileFromCroc']) + desc['IceMissileFromCroc'] = {'display': 'Missile under Ice beam from Crocomire', + 'title': 'Access missile pack under Ice beam location by reverse sparking Crocomire speedway', + 'href': 'https://clips.twitch.tv/CrackyConfidentMomEagleEye', + 'rooms': ['Crocomire Speedway']} + + FrogSpeedwayWithoutSpeed = SMBool(False, 0, ['FrogSpeedwayWithoutSpeed']) + desc['FrogSpeedwayWithoutSpeed'] = {'display': 'Frog speedway without speed', + 'title': 'Traverse frog speedway from right to left, without Speed Booster, but with Wave and either Spazer or Plasma', + 'href': 'https://puu.sh/CvsCT/7757bb4f62.mp4', + 'rooms': ['Frog Speedway']} + + LavaDive = SMBool(True, harder, ['LavaDive']) + desc['LavaDive'] = {'display': 'Lava Dive', + 'title': 'Enter Lower Norfair with Varia and Hi-Jump', + 'href': 'https://www.youtube.com/watch?v=pdyBy_54dB0', + 'rooms': ['Lava Dive Room']} + + LavaDiveNoHiJump = SMBool(False, 0, ['LavaDiveNoHiJump']) + desc['LavaDiveNoHiJump'] = {'display': 'Hi-Jump less Lava Dive', + 'title': 'Enter Lower Norfair with just the Varia suit', + 'href': 'https://www.youtube.com/watch?v=qmlSDfw8FXQ', + 'rooms': ['Lava Dive Room']} + + WorstRoomIceCharge = SMBool(True, mania, ['WorstRoomIceCharge']) + desc['WorstRoomIceCharge'] = {'display': 'Worst Room Ice and Charge', + 'title': 'Go through Worst Room In The Game JUST by freezing pirates', + 'href': 'https://www.youtube.com/watch?v=AYK7LREbLI8', + 'rooms': ['The Worst Room In The Game']} + + WorstRoomWallJump = SMBool(False, 0, ['WorstRoomWallJump']) + desc['WorstRoomWallJump'] = {'display': 'Worst Room insane wall jump', + 'title': 'Do the frame+pixel perfect wall jump to get out Worst Room without Hi-Jump', + 'href': 'https://clips.twitch.tv/FuriousHeartlessArugulaStrawBeary', + 'rooms': ['The Worst Room In The Game']} + + ScrewAttackExit = SMBool(True, medium, ['ScrewAttackExit']) + desc['ScrewAttackExit'] = {'display': 'Screw Attack Exit', + 'title': 'Gain momentum with Hi-Jump and Speed Booster from Golden Torizo Energy Recharge room, then Wall Jump in Screw Attack room, destroying the ceiling with Screw Attack.', + 'href': 'https://youtu.be/l-L6zzpqim4', + 'rooms': ['Screw Attack Room']} + + ScrewAttackExitWithoutScrew = SMBool(False, 0, ['ScrewAttackExitWithoutScrew']) + desc['ScrewAttackExitWithoutScrew'] = {'display': 'Screw Attack Exit without Screw', + 'title': 'Destroy the ceiling, then jump from inside the door with Hi-Jump and Speed Booster to climb up in Screw Attack room', + 'href': 'https://youtu.be/2Ws0Zokg-SQ', + 'rooms': ['Screw Attack Room']} + + FirefleasWalljump = SMBool(False, 0, ['FirefleasWalljump']) + desc['FirefleasWalljump'] = {'display': 'Firefleas Wall Jump', + 'title': 'Get back up from bottom of firefleas without movement items or Ice Beam', + 'href': 'https://youtu.be/tp4V9aNKp64', + 'rooms': ['Lower Norfair Fireflea Room']} + + DodgeLowerNorfairEnemies = SMBool(False, 0, ['DodgeLowerNorfairEnemies']) + desc['DodgeLowerNorfairEnemies'] = {'display': 'Dodge Lower Norfair Enemies', + 'title': 'Go through hard-hitting enemies in Lower Norfair without taking damage or killing them', + 'href': 'https://www.youtube.com/watch?v=5yuBK0YFulA', + 'rooms': ["Three Musketeers' Room", "Wasteland", 'Red Kihunter Shaft', 'The Worst Room In The Game']} + + # wrecked ship + ContinuousWallJump = SMBool(False, 0, ['ContinuousWallJump']) + desc['ContinuousWallJump'] = {'display': 'Continuous Wall-Jump', + 'title': 'Get over the Moat using CWJ', + 'href': 'https://www.youtube.com/watch?v=4HVhTwwax6g', + 'rooms': ['The Moat']} + + DiagonalBombJump = SMBool(True, mania, ['DiagonalBombJump']) + desc['DiagonalBombJump'] = {'display': 'Diagonal Bomb-Jump', + 'title': 'Get over The Moat using Bomb-Jumps', + 'href': 'https://www.youtube.com/watch?v=9Q8WGKCVb40', + 'rooms': ['The Moat']} + + MockballWs = SMBool(True, hardcore, ['MockballWs']) + desc['MockballWs'] = {'display': 'Mockball Wrecked Ship', + 'title': 'Get over the moat using Mockball and Spring Ball', + 'href': 'https://www.youtube.com/watch?v=WYxtRF--834', + 'rooms': ['The Moat']} + + # wrecked ship etank access ("sponge bath" room) + SpongeBathBombJump = SMBool(True, mania, ['SpongeBathBombJump']) + desc['SpongeBathBombJump'] = {'display': 'SpongeBath Bomb-Jump', + 'title': 'Get through Sponge Bath room with Bomb-Jumps', + 'href': 'https://www.youtube.com/watch?v=8ldQUIgBavw', + 'rooms': ['Sponge Bath']} + + SpongeBathHiJump = SMBool(True, easy, ['SpongeBathHiJump']) + desc['SpongeBathHiJump'] = {'display': 'SpongeBath Hi-Jump', + 'title': 'Get through sponge bath room with Hi-Jump and Wall-Jumps', + 'href': 'https://www.youtube.com/watch?v=8ldQUIgBavw', + 'rooms': ['Sponge Bath']} + + SpongeBathSpeed = SMBool(True, medium, ['SpongeBathSpeed']) + desc['SpongeBathSpeed'] = {'display': 'SpongeBath Speed', + 'title': 'Get through sponge bath room with Speed Booster and Wall-Jumps', + 'href': 'https://www.youtube.com/watch?v=8ldQUIgBavw', + 'rooms': ['Sponge Bath']} + + # Maridia + # Suitless + TediousMountEverest = SMBool(False, 0, ['TediousMountEverest']) + desc['TediousMountEverest'] = {'display': 'Mt. Everest without anything', + 'title': 'Make your way through Mt. Everest with nothing but Ice and Hi-Jump', + 'href': 'https://www.youtube.com/watch?v=chFbX9rRV_k&t=123s', + 'rooms': ['Mt. Everest']} + + DoubleSpringBallJump = SMBool(False, 0, ['DoubleSpringBallJump']) + desc['DoubleSpringBallJump'] = {'display': 'Double SpringBall-Jump', + 'title': 'With Hi-Jump boots do two SpringBall-Jumps in a row', + 'href': 'https://youtu.be/KohE3e8sGLQ', + 'rooms': ['Mt. Everest', "Draygon's Room", 'Halfie Climb Room']} + + BotwoonToDraygonWithIce = SMBool(False, 0, ['BotwoonToDraygonWithIce']) + desc['BotwoonToDraygonWithIce'] = {'display': 'Botwoon to Draygon with Ice', + 'title': 'When past Botwoon, access Draygon using Hi-Jump and Ice only', + 'href': 'https://www.twitch.tv/videos/480882188', + 'rooms': ['Halfie Climb Room', 'Colosseum']} + + WestSandHoleSuitlessWallJumps = SMBool(False, 0, ['WestSandHoleSuitlessWallJumps']) + desc['WestSandHoleSuitlessWallJumps'] = {'display': 'West Sand Hole suitless Wall Jumps', + 'title': 'Access items in West Sand Hole (aka Left Sand Pit) with just Hi-Jump', + 'href': 'https://www.youtube.com/watch?v=Fn2z0ByOcj4', + 'rooms': ['West Sand Hole']} + + # Suitless Draygon + DraygonRoomGrappleExit = SMBool(False, 0, ['DraygonRoomGrappleExit']) + desc['DraygonRoomGrappleExit'] = {'display': 'Exit Draygon room with the Grapple', + 'title': 'Use Grapple to bounce them morph and demorph up to the platform', + 'href': 'https://www.youtube.com/watch?v=i2OGuFpcfiw&t=154s', + 'rooms': ["Draygon's Room"]} + + DraygonRoomCrystalFlash = SMBool(False, 0, ['DraygonRoomCrystalFlash']) + desc['DraygonRoomCrystalFlash'] = {'display': 'Exit Draygon room or precious room with a shine spark', + 'title': 'Doing a Crystal flash and being grabbed by Draygon gives a free shine spark', + 'href': 'https://www.youtube.com/watch?v=hrHHfvGD3wo&t=625s', + 'rooms': ["Draygon's Room"]} + + PreciousRoomXRayExit = SMBool(False, 0, ['PreciousRoomXRayExit']) + desc['PreciousRoomXRayExit'] = {'display': 'Exit the Precious room with an Xray glitch', + 'title': 'Use an XrayScope glitch to climb out of the Precious room', + 'href': 'https://www.youtube.com/watch?v=i2OGuFpcfiw&t=160s', + 'rooms': ['The Precious Room']} + + PreciousRoomGravJumpExit = SMBool(False, 0, ['PreciousRoomGravJumpExit']) + desc['PreciousRoomGravJumpExit'] = {'display': 'Exit the Precious room with a Gravity Jump', + 'title': 'Jump through the exit door into the water in the next room to climb up with a Gravity Jump', + 'href': 'https://www.twitch.tv/jooniejoone/clip/StylishVainPorcupineVoHiYo', + 'rooms': ['The Precious Room', 'Colosseum']} + + # clips + MochtroidClip = SMBool(True, medium, ['MochtroidClip']) + desc['MochtroidClip'] = {'display': 'Mochtroid Clip', + 'title': 'Get to Botwoon with Ice Beam', + 'href': 'https://wiki.supermetroid.run/index.php?title=14%25#Mochtroid_Clip', + 'rooms': ['Botwoon Hallway']} + + PuyoClip = SMBool(False, 0, ['PuyoClip']) + desc['PuyoClip'] = {'display': 'Puyo Clip', + 'title': 'Get to Spring Ball with Gravity Suit and Ice Beam', + 'href': 'https://www.youtube.com/watch?v=e5ZH_9paSLw', + 'rooms': ['Pants Room']} + + PuyoClipXRay = SMBool(False, 0, ['PuyoClipXRay']) + desc['PuyoClipXRay'] = {'display': 'Puyo Clip with X-Ray', + 'title': 'Get to Spring Ball with Gravity Suit, Ice Beam and X-Ray', + 'href': 'https://youtu.be/83JajzNyZtQ', + 'rooms': ['Pants Room']} + + SnailClip = SMBool(False, 0, ['SnailClip']) + desc['SnailClip'] = {'display': 'Snail Clip', + 'title': 'Access Aqueduct Missile and Super Missile without SpeedBooster', + 'href': 'https://www.youtube.com/watch?v=fBQubU6h11U&t=70s', + 'rooms': ['Aqueduct']} + + SuitlessPuyoClip = SMBool(False, 0, ['SuitlessPuyoClip']) + desc['SuitlessPuyoClip'] = {'display': 'Suitless Puyo Clip', + 'title': 'Do the Puyo clip with Hi Jump and without Gravity', + 'href': 'https://snipaclip.com/watch/HomelyImpartialVampireFloof', + 'rooms': ['Pants Room']} + + CrystalFlashClip = SMBool(False, 0, ['CrystalFlashClip']) + desc['CrystalFlashClip'] = {'display': 'Crystal Flash Clip', + 'title': 'Use a Crystal Flash to clip through crumble blocks to get to Botwoon or Shaktool, using Gravity and Bombs', + 'href': 'https://www.youtube.com/watch?v=z2c3u8ICO6A', + 'rooms': ['Botwoon Hallway', 'East Pants Room']} + + SuitlessCrystalFlashClip = SMBool(False, 0, ['SuitlessCrystalFlashClip']) + desc['SuitlessCrystalFlashClip'] = {'display': 'Suitless Crystal Flash Clip', + 'title': 'Use a Crystal Flash to clip through crumble blocks to get to Botwoon or Shaktool', + 'href': 'https://www.youtube.com/watch?v=BUzmHsk0H7k', + 'rooms': ['Botwoon Hallway', 'East Pants Room']} + + # plasma room + KillPlasmaPiratesWithSpark = SMBool(False, 0, ['KillPlasmaPiratesWithSpark']) + desc['KillPlasmaPiratesWithSpark'] = {'display': 'Kill Plasma Pirates with Spark', + 'title': 'Use shinesparks to kill the pirates in Plasma Beam room', + 'href': 'https://youtu.be/nORE1HkP64E', + 'rooms': ['Plasma Room']} + + KillPlasmaPiratesWithCharge = SMBool(True, hard, ['KillPlasmaPiratesWithCharge']) + desc['KillPlasmaPiratesWithCharge'] = {'display': 'Kill Plasma Pirates with Charge', + 'title': 'Use pseudo-screw to kill the pirates in Plasma Beam room', + 'href': 'https://youtu.be/hn6nrjUGmSk', + 'rooms': ['Plasma Room']} + + # spring ball access + AccessSpringBallWithHiJump = SMBool(True, easy, ['AccessSpringBallWithHiJump']) + desc['AccessSpringBallWithHiJump'] = {'display': 'Access Spring Ball location with Hi-Jump', + 'title': 'With Gravity and Hi-Jump, jump to get through the grapple hole', + 'href': 'https://youtu.be/mHiSd3kebHo', + 'rooms': ['Pants Room']} + + AccessSpringBallWithSpringBallBombJumps = SMBool(False, 0, ['AccessSpringBallWithSpringBallBombJumps']) + desc['AccessSpringBallWithSpringBallBombJumps'] = {'display': 'Access Spring Ball location with bomb jumps and spring ball', + 'title': 'With Gravity, bounce on the sand and bomb jump up to get through the grapple hole', + 'href': 'https://youtu.be/VbR6z3aZuWg', + 'rooms': ['Pants Room']} + + AccessSpringBallWithBombJumps = SMBool(False, 0, ['AccessSpringBallWithBombJumps']) + desc['AccessSpringBallWithBombJumps'] = {'display': 'Access Spring Ball location with bomb jumps only', + 'title': 'With Gravity, bomb jump up from the sand to get through the grapple hole', + 'href': 'https://youtu.be/8s_Tng-3oZM', + 'rooms': ['Pants Room']} + + AccessSpringBallWithSpringBallJump = SMBool(False, 0, ['AccessSpringBallWithSpringBallJump']) + desc['AccessSpringBallWithSpringBallJump'] = {'display': 'Access Spring Ball location with a spring ball jump', + 'title': 'With Gravity, use a spring ball jump, either from the sand or a ledge to get through the grapple hole', + 'href': 'https://youtu.be/YrmAqwJxbYs', + 'rooms': ['Pants Room']} + + AccessSpringBallWithXRayClimb = SMBool(False, 0, ['AccessSpringBallWithXRayClimb']) + desc['AccessSpringBallWithXRayClimb'] = {'display': 'Access Spring Ball location with X-Ray climbing', + 'title': 'Use inbounds X-Ray climbing to get past the grapple hole. Can be required suitless if suitless movement level 3 is enabled.', + 'href': 'https://youtu.be/I-f5X5cNypA', + 'rooms': ['Pants Room']} + + AccessSpringBallWithGravJump = SMBool(False, 0, ['AccessSpringBallWithGravJump']) + desc['AccessSpringBallWithGravJump'] = {'display': 'Access Spring Ball location with a Gravity jump', + 'title': 'Do a tricky gravity jump from the sand and get through the grapple hole', + 'href': 'https://www.twitch.tv/videos/480378897', + 'rooms': ['Pants Room']} + + AccessSpringBallWithFlatley = SMBool(False, 0, ['AccessSpringBallWithFlatley']) + desc['AccessSpringBallWithFlatley'] = {'display': 'Access Spring Ball location suitless with a flatley jump and Space Jump', + 'title': 'Do a suitless flatley jump to get through the grapple hole, and get out of the water with Space Jump', + 'href': 'https://www.youtube.com/watch?v=8JHsAGeUdhQ', + 'rooms': ['Pants Room']} + + categories = { + 'Common': [ + {'knows': ['WallJump', 'ShineSpark', 'MidAirMorph', 'CrouchJump', 'UnequipItem'], + 'title': 'Basics'}, + {'knows': ['Mockball', 'SimpleShortCharge', 'InfiniteBombJump', 'GreenGateGlitch', + 'GravityJump', 'GetAroundWallJump', + 'SpringBallJump', 'SpringBallJumpFromWall', 'ShortCharge'], + 'title': 'Used across the game'} + ], + 'Crateria/Brinstar': [ + {'knows': ['AlcatrazEscape', 'HiJumpGauntletAccess', 'HiJumpLessGauntletAccess', 'LowGauntlet', 'OldMBWithSpeed'], + 'title': 'Crateria'}, + {'knows': ['CeilingDBoost', 'BillyMays', 'EarlyKraid', + 'ReverseGateGlitch', 'ReverseGateGlitchHiJumpLess', + 'RedTowerClimb', 'XrayDboost', 'XrayIce', + 'RonPopeilScrew', 'Moondance'], + 'title': 'Brinstar'} + ], + 'Wrecked Ship': [ + {'knows': ['ContinuousWallJump', 'DiagonalBombJump', 'MockballWs'], + 'title': 'Access'}, + {'knows': ['SpongeBathHiJump', 'SpongeBathSpeed', 'SpongeBathBombJump'], + 'title': 'Sponge Bath'} + ], + 'Maridia 1/2': [ + {'knows': ['GravLessLevel1', 'GravLessLevel2', 'GravLessLevel3'], + 'title': 'Underwater movement without Gravity Suit'}, + {'knows': ['MochtroidClip', 'PuyoClip', 'PuyoClipXRay', + 'SnailClip', 'CrystalFlashClip'], + 'title': 'Clips'}, + {'knows': ['KillPlasmaPiratesWithCharge', 'KillPlasmaPiratesWithSpark'], + 'title': 'Plasma Room'}, + {'knows': ['HiJumpMamaTurtle', 'MaridiaWallJumps', 'MtEverestGravJump'], + 'title': 'Jumps'} + ], + 'Maridia 2/2': [ + {'knows': ['AccessSpringBallWithHiJump', 'AccessSpringBallWithSpringBallBombJumps', + 'AccessSpringBallWithBombJumps', 'AccessSpringBallWithSpringBallJump', + 'AccessSpringBallWithGravJump', 'AccessSpringBallWithXRayClimb', 'AccessSpringBallWithFlatley'], + 'title': 'Spring Ball Access'}, + {'knows': ['DraygonRoomGrappleExit', 'DraygonRoomCrystalFlash', 'PreciousRoomXRayExit', 'PreciousRoomGravJumpExit'], + 'title': 'Suitless Draygon Exit'}, + {'knows': ['WestSandHoleSuitlessWallJumps', 'DoubleSpringBallJump', 'TediousMountEverest', + 'BotwoonToDraygonWithIce', 'SuitlessCrystalFlashClip', + 'SuitlessPuyoClip'], + 'title': 'Obscure suitless stuff'} + ], + 'Upper Norfair': [ + {'knows': ['WallJumpCathedralExit', 'IceEscape', 'FrogSpeedwayWithoutSpeed', 'NovaBoost'], + 'title': 'Main Upper Norfair'}, + {'knows': ['BubbleMountainWallJump', 'NorfairReserveDBoost', 'DoubleChamberWallJump'], + 'title': 'Bubble Mountain'}, + {'knows': ['CrocPBsIce', 'CrocPBsDBoost', 'IceMissileFromCroc'], + 'title': 'Crocomire'} + ], + 'Lower Norfair': [ + {'knows': ['LavaDive', 'LavaDiveNoHiJump'], + 'title': 'Access'}, + {'knows': ['ScrewAttackExit', 'ScrewAttackExitWithoutScrew'], + 'title': 'Screw Attack'}, + {'knows': ['WorstRoomIceCharge', 'WorstRoomWallJump'], + 'title': 'Worst Room In The Game'}, + {'knows': [ 'FirefleasWalljump', 'DodgeLowerNorfairEnemies'], + 'title': 'Other'} + ], + 'Bosses/End': [ + {'knows': ['DraygonGrappleKill', 'DraygonSparkKill', 'MicrowaveDraygon', 'MicrowavePhantoon'], + 'title': 'Bosses'}, + {'knows': ['LowAmmoCroc', 'LowStuffBotwoon', 'LowStuffGT'], + 'title': 'Mini-Bosses'}, + {'knows': ['IceZebSkip', 'SpeedZebSkip'], + 'title': 'End Game'} + ] + } + + def knows(self, name, diff): + k = getattr(self, name) + return k.bool and k.difficulty <= diff + +def isSettings(settings): + return settings[0:len('__')] != '__' + +class Settings: + SettingsDict = {} + # boss difficulty tables : + # + # key is boss name. value is a dictionary where you define: + # + # 1. Rate : the rate of time in which you can land shots. For example + # : 0.5 means you can land shots half the time in the boss (30secs in + # a given minute). It represents a combination of the fraction of the + # time you can hit the boss and your general accuracy against the boss. + # If no information is given here, the fight will be + # considered to be 2 minutes, regardless of anything else. + # + # 2. Energy : a dictionary where key is total energy you have divided by 100 + # (the initial 99 energy count as 1). + # value is estimated difficulty *for a 2-minute fight*, with Varia + # suit only. + # If not defined, the one below will be chosen, or the minimum one if + # no below entry is defined. You can give any difficulty number + # instead of the fixed values defined above. + # + # Actual difficulty calculation will also take into account estimated + # fight duration. Difficulty will be multiplied with the ratio against + # 2-minutes value entered here. The ammo margin will also be + # considered if you do not have charge. + # + # If not enough info is provided here, base difficulty will be medium. + + # logic behind the presets : the ones where you find the boss difficult are + # calibrated to give 'hard' in vanilla situations, the default are calibrated + # to give 'medium' in vanilla situations, just above gives 'easy' in vanilla + # situations, and top settings basically discards the boss except for extreme + # situations (very low energy or firepower) + bossesDifficultyPresets = { + 'Kraid' : { + "He's annoying" : { + 'Rate' : 0.0075, + 'Energy' : { + 0.5 : hard, + 1 : medium, + 2 : easy + } + }, + 'Default' : { + 'Rate' : 0.015, + 'Energy' : { + 0.5 : hard, + 1.5 : medium, + 2.5 : easy + } + }, + 'Quick Kill' : { + 'Rate' : 1, + 'Energy' : { + 0.5 : easy + } + } + }, + 'Phantoon' : { + 'A lot of trouble' : { + 'Rate' : 0.01, + 'Energy' : { + 1.5 : mania, + 3 : hardcore, + 4 : harder, + 5 : hard, + 7 : medium, + 10 : easy + } + }, + 'Default' : { + 'Rate' : 0.015, + 'Energy' : { + 0.5 : samus, + 1 : mania, + 2 : hardcore, + 4 : harder, + 5 : hard, + 6 : medium, + 10 : easy + } + }, + 'Used to it' : { + 'Rate' : 0.02, + 'Energy' : { + 0.5 : 150, + 1 : (mania+hardcore)/2, + 2 : harder, + 2.5 : hard, + 4 : medium, + 6 : easy + } + }, + 'No problemo' : { + 'Rate' : 0.02, + 'Energy' : { + 0.5 : harder, + 1 : hard, + 2 : medium, + 3 : easy + } + } + }, + 'Draygon' : { + 'A lot of trouble' : { + 'Rate' : 0.025, + 'Energy' : { + 1 : mania, + 6 : hardcore, + 8 : harder, + 11 : hard, + 14 : medium, + 20 : easy + }, + }, + 'Default' : { + 'Rate' : 0.05, + 'Energy' : { + 0.5 : samus, + 1 : mania, + 6 : hardcore, + 8 : harder, + 11 : hard, + 14 : medium, + 20 : easy + }, + }, + 'Used to it' : { + 'Rate' : 0.06, + 'Energy' : { + 1 : mania, + 4 : hardcore, + 6 : harder, + 8 : hard, + 11 : medium, + 14 : easy + }, + }, + 'No problemo' : { + 'Rate' : 0.08, + 'Energy' : { + 1 : mania, + 4 : hardcore, + 5 : harder, + 6 : hard, + 8 : medium, + 12 : easy + }, + } + }, + 'Ridley' : { + "I'm scared!" : { + 'Rate' : 0.047, + 'Energy' : { + 1 : mania, + 7 : hardcore, + 11 : harder, + 14 : hard, + 20 : medium + }, + }, + 'Default' : { + 'Rate' : 0.12, + 'Energy' : { + 0.5 : samus, + 1 : mania, + 6 : hardcore, + 8 : harder, + 12 : hard, + 20 : medium, + 36 : easy + }, + }, + 'Used to it' : { + 'Rate' : 0.16, + 'Energy' : { + 1 : mania, + 6 : hardcore, + 8 : harder, + 10 : hard, + 14 : medium, + 20 : easy + }, + }, + 'Piece of cake' : { + 'Rate' : 0.3, + 'Energy' : { + 1 : mania, + 3 : hardcore, + 4 : harder, + 6 : hard, + 8 : medium, + 10 : easy + } + } + }, + # 2 = 6 tanks/no suits. 4 = 3 tanks + Varia + 'MotherBrain' : { + "It can get ugly" : { + 'Rate' : 0.18, + 'Energy' : { + 2 : impossibru, + 4 : mania, + 8 : hardcore, + 12 : harder, + 16 : hard, + 24 : medium, + 32 : easy + } + }, + 'Default' : { + 'Rate' : 0.25, + 'Energy' : { + 2 : impossibru, + 4 : mania, + 8 : hardcore, + 12 : harder, + 16 : hard, + 20 : medium, + 24 : easy + } + }, + 'Is this really the last boss?': { + 'Rate' : 0.5, + 'Energy' : { + 2 : impossibru, + 4 : mania, + 6 : hardcore, + 8 : harder, + 12 : hard, + 14 : medium, + 20 : easy + } + }, + 'Nice cutscene bro' : { + 'Rate' : 0.6, + 'Energy' : { + 2 : mania, + 4 : hard, + 8 : medium, + 12 : easy + } + } + } + } + + bossesDifficulty = { + 'Kraid' : bossesDifficultyPresets['Kraid']['Default'], + 'Phantoon' : bossesDifficultyPresets['Phantoon']['Default'], + 'Draygon' : bossesDifficultyPresets['Draygon']['Default'], + 'Ridley' : bossesDifficultyPresets['Ridley']['Default'], + 'MotherBrain' : bossesDifficultyPresets['MotherBrain']['Default'] + } + + # hell run table + # set entry to None to disable + hellRunPresets = { + 'Ice' : { + 'No thanks' : None, + # get comfortable before going in + 'Gimme energy' : [(4, hardcore), (5, harder), (6, hard), (10, medium)], + # balanced setting + 'Default' : [(3, harder), (4, hard), (5, medium)], + # you don't mind doing hell runs at all + 'Bring the heat' : [(2, harder), (3, hard), (4, medium)], + # RBO runner + 'I run RBO' : [(2, medium), (3, easy)], + 'Solution' : [(2, hardcore), (3, harder), (4, hard), (5, medium)], + }, + 'MainUpperNorfair' : { + 'No thanks' : None, + 'Gimme energy' : [(5, mania), (6, hardcore), (8, harder), (10, hard), (14, medium)], + 'Default' : [(4, mania), (5, hardcore), (6, hard), (9, medium)], + 'Bring the heat' : [(3, mania), (4, harder), (5, hard), (7, medium)], + 'I run RBO' : [(3, harder), (4, hard), (5, medium), (6, easy)], + 'Solution' : [(3, samus), (4, mania), (5, hardcore), (6, hard), (9, medium)] + }, + 'LowerNorfair' : { + 'Default' : None, + 'Bring the heat' : [(10, mania), (13, hardcore), (18, harder)], + 'I run RBO' : [(8, mania), (9, hardcore), (11, harder), (14, hard), (18, medium)], + 'Solution' : [(8, impossibru), (18, mania)] + } + } + + hellRuns = { + # Ice Beam hell run + 'Ice' : hellRunPresets['Ice']['Default'], + # rest of upper norfair + 'MainUpperNorfair' : hellRunPresets['MainUpperNorfair']['Default'], + 'LowerNorfair' : hellRunPresets['LowerNorfair']['Default'] + } + + # centralize all the hellruns coeffs + hellRunsTable = { + 'Ice': { + 'Norfair Entrance -> Ice Beam': {'mult': 1.0, 'minE': 2, 'hellRun': 'Ice'}, + 'Norfair Entrance -> Croc via Ice': {'mult': 1.5, 'minE': 2, 'hellRun': 'Ice'}, + 'Croc -> Norfair Entrance': {'mult': 2.0, 'minE': 1, 'hellRun': 'Ice'}, + 'Croc -> Bubble Mountain': {'mult': 2.0, 'minE': 1, 'hellRun': 'Ice'} + }, + 'MainUpperNorfair': { + 'Norfair Entrance -> Bubble': {'mult': 1.0, 'minE': 2, 'hellRun': 'MainUpperNorfair'}, + 'Bubble -> Norfair Entrance': {'mult': 0.75, 'minE': 2, 'hellRun': 'MainUpperNorfair'}, + 'Norfair Entrance -> Cathedral Missiles': {'mult': 0.66, 'minE': 2, 'hellRun': 'MainUpperNorfair'}, + 'Bubble -> Cathedral Missiles': {'mult': 0.66, 'minE': 2, 'hellRun': 'MainUpperNorfair'}, + 'Norfair Entrance -> Croc via Frog': {'mult': 2.0, 'minE': 1, 'hellRun': 'MainUpperNorfair'}, + 'Norfair Entrance -> Croc via Frog w/Wave': {'mult': 4.0, 'minE': 1, 'hellRun': 'MainUpperNorfair'}, + 'Bubble -> Norfair Reserve Missiles': {'mult': 3.0, 'minE': 1, 'hellRun': 'MainUpperNorfair'}, + 'Bubble -> Norfair Reserve': {'mult': 1.0, 'minE': 2, 'hellRun': 'MainUpperNorfair'}, + 'Bubble -> Speed Booster': {'mult': 1.0, 'minE': 3, 'hellRun': 'MainUpperNorfair'}, + 'Bubble -> Speed Booster w/Speed': {'mult': 2.0, 'minE': 2, 'hellRun': 'MainUpperNorfair'}, + 'Bubble -> Wave': {'mult': 0.75, 'minE': 2, 'hellRun': 'MainUpperNorfair'}, + 'Bubble -> Kronic Boost Room': {'mult': 1.25, 'minE': 2, 'hellRun': 'MainUpperNorfair'}, + 'Bubble -> Kronic Boost Room wo/Bomb': {'mult': 0.5, 'minE': 2, 'hellRun': 'MainUpperNorfair'}, + 'Bubble -> Croc': {'mult': 2.0, 'minE': 2, 'hellRun': 'MainUpperNorfair'}, + 'Bubble Top <-> Bubble Bottom': {'mult': 0.357, 'minE': 2, 'hellRun': 'MainUpperNorfair'}, + 'Croc -> Grapple Escape Missiles': {'mult': 1.0, 'minE': 2, 'hellRun': 'MainUpperNorfair'}, + 'Croc -> Ice Missiles': {'mult': 1.0, 'minE': 2, 'hellRun': 'MainUpperNorfair'}, + 'Single Chamber <-> Bubble Mountain': {'mult': 1.25, 'minE': 2, 'hellRun': 'MainUpperNorfair'}, + 'Kronic Boost Room -> Bubble Mountain Top': {'mult': 0.5, 'minE': 2, 'hellRun': 'MainUpperNorfair'}, + 'Kronic Boost Room <-> Croc': {'mult': 1.0, 'minE': 2, 'hellRun': 'MainUpperNorfair'}, + 'Croc -> Norfair Entrance': {'mult': 1.25, 'minE': 2, 'hellRun': 'MainUpperNorfair'} + }, + 'LowerNorfair': { + 'Main': {'mult':1.0, 'minE':8, 'hellRun':'LowerNorfair'}, + 'Entrance -> GT via Chozo': {'mult':0.8, 'minE':8, 'hellRun':'LowerNorfair'} + } + } + + hardRoomsPresets = { + 'X-Ray' : { + 'Aarghh' : [(10, hard), (14, medium)], + "I don't like spikes" : [(8, hard), (10, medium), (14, easy)], + 'Default' : [(6, hard), (8, medium), (10, easy)], + "I don't mind spikes" : [(4, hard), (6, medium), (8, easy)], + 'D-Boost master': [(1, hardcore), (2, harder), (3, hard), (4, medium), (6, easy)], + 'Solution' : [(1, samus), (4, mania), (6, hard), (8, medium), (10, easy)], + }, + 'Gauntlet' : { + 'Aarghh' : [(5, hard), (10, medium)], + "I don't like acid" : [(1, harder), (2, hard), (5, medium), (10, easy)], + 'Default' : [(0, harder), (1, hard), (3, medium), (6, easy)] + } + } + + hardRooms = { + 'X-Ray' : hardRoomsPresets['X-Ray']['Default'], + 'Gauntlet' : hardRoomsPresets['Gauntlet']['Default'] + } + + # various settings used in difficulty computation + algoSettings = { + # Boss Fights + + # number of missiles fired per second during boss battles + # (used along with Rate) + 'missilesPerSecond' : 3, + # number of supers fired per second during boss battles + # (used along with Rate) + 'supersPerSecond' : 1.85, + # number of power bombs fired per second during boss battles + # (used along with Rate) + 'powerBombsPerSecond' : 0.33, + # number of charged shots per second (at most 1) + 'chargedShotsPerSecond' : 0.75, + # firepower grabbed by picking up drops during boss battles + # in missiles per minute (1 super = 3 missiles) + 'missileDropsPerMinute' : 12, + # if no charge beam, amount of ammo margin to consider the boss + # fight as 'normal' (1.5 = 50% more for instance) + # boss fight difficulty will be linearly increased between this value + # and 1 + 'ammoMarginIfNoCharge' : 1.5, + # divide the difficulty by this amount if charge + 'phantoonFlamesAvoidBonusCharge' : 1.2, + # divide the difficulty by this amount if screw + 'phantoonFlamesAvoidBonusScrew' : 1.5, + # multiply the difficulty by this amount if no charge and few missiles + 'phantoonLowMissileMalus' : 1.2, + # multiply the difficulty by this amount if you have to fight with water physics + 'draygonNoGravityMalus' : 2.0, + # if gravity+no morph + 'draygonNoMorphMalus' : 2.0, + # if gravity+screw + 'draygonScrewBonus' : 2.0, + # dmg reduction factor for bosses giving drops. + # Varia (2) is considered "standard" dmg reduction. + # this is to take into account the impact of health drops + # relative to how hard the boss hits + 'dmgReductionDifficultyFactor' : 1.5 + } + +def isButton(button): + return button[0:len('__')] != '__' + +class Controller: + ControllerDict = {} + # controller mapping + A = "Jump" + B = "Dash" + X = "Shoot" + Y = "Item Cancel" + L = "Angle Down" + R = "Angle Up" + Select = "Item Select" + Moonwalk = False diff --git a/worlds/sm/variaRandomizer/utils/utils.py b/worlds/sm/variaRandomizer/utils/utils.py new file mode 100644 index 00000000..d6c71b00 --- /dev/null +++ b/worlds/sm/variaRandomizer/utils/utils.py @@ -0,0 +1,450 @@ +import os, json, sys, re, random + +from utils.parameters import Knows, Settings, Controller, isKnows, isSettings, isButton +from utils.parameters import easy, medium, hard, harder, hardcore, mania, text2diff +from logic.smbool import SMBool + +from Utils import is_frozen + +def isStdPreset(preset): + return preset in ['newbie', 'casual', 'regular', 'veteran', 'expert', 'master', 'samus', 'solution', 'Season_Races', 'SMRAT2021'] + +def getPresetDir(preset): + if isStdPreset(preset): + return 'lib/worlds/sm/variaRandomizer/standard_presets' if is_frozen() else 'worlds/sm/variaRandomizer/standard_presets' + else: + return 'lib/worlds/sm/variaRandomizer/community_presets' if is_frozen() else 'worlds/sm/variaRandomizer/community_presets' + +def removeChars(string, toRemove): + return re.sub('[{}]+'.format(toRemove), '', string) + +def range_union(ranges): + ret = [] + for rg in sorted([[r.start, r.stop] for r in ranges]): + begin, end = rg[0], rg[-1] + if ret and ret[-1][1] > begin: + ret[-1][1] = max(ret[-1][1], end) + else: + ret.append([begin, end]) + return [range(r[0], r[1]) for r in ret] + +# https://github.com/robotools/fontParts/commit/7cb561033929cfb4a723d274672e7257f5e68237 +def normalizeRounding(n): + # Normalizes rounding as Python 2 and Python 3 handing the rounding of halves (0.5, 1.5, etc) differently. + # This normalizes rounding to be the same in both environments. + if round(0.5) != 1 and n % 1 == .5 and not int(n) % 2: + return int((round(n) + (abs(n) / n) * 1)) + else: + return int(round(n)) + +# gauss random in [0, r] range +# the higher the slope, the less probable extreme values are. +def randGaussBounds(r, slope=5): + r = float(r) + n = normalizeRounding(random.gauss(r/2, r/slope)) + if n < 0: + n = 0 + if n > r: + n = int(r) + return n + +# from a relative weight dictionary, gives a normalized range dictionary +# example : +# { 'a' : 10, 'b' : 17, 'c' : 3 } => {'c': 0.1, 'a':0.4333333, 'b':1 } +def getRangeDict(weightDict): + total = float(sum(weightDict.values())) + rangeDict = {} + current = 0.0 + for k in sorted(weightDict, key=weightDict.get): + w = float(weightDict[k]) / total + current += w + rangeDict[k] = current + + return rangeDict + +def chooseFromRange(rangeDict): + r = random.random() + val = None + for v in sorted(rangeDict, key=rangeDict.get): + val = v + if r < rangeDict[v]: + return v + return val + +class PresetLoader(object): + @staticmethod + def factory(params): + # can be a json, a python file or a dict with the parameters + if type(params) == str: + ext = os.path.splitext(params) + if ext[1].lower() == '.json': + return PresetLoaderJson(params) + else: + raise Exception("PresetLoader: wrong parameters file type: {}".format(ext[1])) + elif type(params) is dict: + return PresetLoaderDict(params) + else: + raise Exception("wrong parameters input, is neither a string nor a json file name: {}::{}".format(params, type(params))) + + def __init__(self): + if 'Knows' not in self.params: + if 'knows' in self.params: + self.params['Knows'] = self.params['knows'] + else: + self.params['Knows'] = {} + if 'Settings' not in self.params: + if 'settings' in self.params: + self.params['Settings'] = self.params['settings'] + else: + self.params['Settings'] = {} + if 'Controller' not in self.params: + if 'controller' in self.params: + self.params['Controller'] = self.params['controller'] + else: + self.params['Controller'] = {} + self.params['score'] = self.computeScore() + + def load(self, player): + # update the parameters in the parameters classes: Knows, Settings + Knows.knowsDict[player] = Knows() + Settings.SettingsDict[player] = Settings() + Controller.ControllerDict[player] = Controller() + + # Knows + for param in self.params['Knows']: + if isKnows(param) and hasattr(Knows, param): + setattr(Knows.knowsDict[player], param, SMBool( self.params['Knows'][param][0], + self.params['Knows'][param][1], + ['{}'.format(param)])) + # Settings + ## hard rooms + for hardRoom in ['X-Ray', 'Gauntlet']: + if hardRoom in self.params['Settings']: + Settings.SettingsDict[player].hardRooms[hardRoom] = Settings.hardRoomsPresets[hardRoom][self.params['Settings'][hardRoom]] + + ## bosses + for boss in ['Kraid', 'Phantoon', 'Draygon', 'Ridley', 'MotherBrain']: + if boss in self.params['Settings']: + Settings.SettingsDict[player].bossesDifficulty[boss] = Settings.bossesDifficultyPresets[boss][self.params['Settings'][boss]] + + ## hellruns + for hellRun in ['Ice', 'MainUpperNorfair', 'LowerNorfair']: + if hellRun in self.params['Settings']: + Settings.SettingsDict[player].hellRuns[hellRun] = Settings.hellRunPresets[hellRun][self.params['Settings'][hellRun]] + + # Controller + for button in self.params['Controller']: + if isButton(button): + setattr(Controller.ControllerDict[player], button, self.params['Controller'][button]) + + def dump(self, fileName): + with open(fileName, 'w') as jsonFile: + json.dump(self.params, jsonFile) + + def printToScreen(self): + print("self.params: {}".format(self.params)) + + print("loaded knows: ") + for knows in Knows.__dict__: + if isKnows(knows): + print("{}: {}".format(knows, Knows.__dict__[knows])) + print("loaded settings:") + for setting in Settings.__dict__: + if isSettings(setting): + print("{}: {}".format(setting, Settings.__dict__[setting])) + print("loaded controller:") + for button in Controller.__dict__: + if isButton(button): + print("{}: {}".format(button, Controller.__dict__[button])) + print("loaded score: {}".format(self.params['score'])) + + def computeScore(self): + # the more techniques you know and the smaller the difficulty of the techniques, the higher the score + diff2score = { + easy: 6, + medium: 5, + hard: 4, + harder: 3, + hardcore: 2, + mania: 1 + } + + boss2score = { + "He's annoying": 1, + 'A lot of trouble': 1, + "I'm scared!": 1, + "It can get ugly": 1, + 'Default': 2, + 'Quick Kill': 3, + 'Used to it': 3, + 'Is this really the last boss?': 3, + 'No problemo': 4, + 'Piece of cake': 4, + 'Nice cutscene bro': 4 + } + + hellrun2score = { + 'No thanks': 0, + 'Solution': 0, + 'Gimme energy': 2, + 'Default': 4, + 'Bring the heat': 6, + 'I run RBO': 8 + } + + hellrunLN2score = { + 'Default': 0, + 'Solution': 0, + 'Bring the heat': 6, + 'I run RBO': 12 + } + + xray2score = { + 'Aarghh': 0, + 'Solution': 0, + "I don't like spikes": 1, + 'Default': 2, + "I don't mind spikes": 3, + 'D-Boost master': 4 + } + + gauntlet2score = { + 'Aarghh': 0, + "I don't like acid": 1, + 'Default': 2 + } + + score = 0 + + # knows + for know in Knows.__dict__: + if isKnows(know): + if know in self.params['Knows']: + if self.params['Knows'][know][0] == True: + score += diff2score[self.params['Knows'][know][1]] + else: + # if old preset with not all the knows, use default values for the know + if Knows.__dict__[know].bool == True: + score += diff2score[Knows.__dict__[know].difficulty] + + # hard rooms + hardRoom = 'X-Ray' + if hardRoom in self.params['Settings']: + score += xray2score[self.params['Settings'][hardRoom]] + + hardRoom = 'Gauntlet' + if hardRoom in self.params['Settings']: + score += gauntlet2score[self.params['Settings'][hardRoom]] + + # bosses + for boss in ['Kraid', 'Phantoon', 'Draygon', 'Ridley', 'MotherBrain']: + if boss in self.params['Settings']: + score += boss2score[self.params['Settings'][boss]] + + # hellruns + for hellRun in ['Ice', 'MainUpperNorfair']: + if hellRun in self.params['Settings']: + score += hellrun2score[self.params['Settings'][hellRun]] + + hellRun = 'LowerNorfair' + if hellRun in self.params['Settings']: + score += hellrunLN2score[self.params['Settings'][hellRun]] + + return score + +class PresetLoaderJson(PresetLoader): + # when called from the test suite + def __init__(self, jsonFileName): + with open(jsonFileName) as jsonFile: + self.params = json.load(jsonFile) + super(PresetLoaderJson, self).__init__() + +class PresetLoaderDict(PresetLoader): + # when called from the website + def __init__(self, params): + self.params = params + super(PresetLoaderDict, self).__init__() + +def getDefaultMultiValues(): + from graph.graph_utils import GraphUtils + defaultMultiValues = { + 'startLocation': GraphUtils.getStartAccessPointNames(), + 'majorsSplit': ['Full', 'FullWithHUD', 'Major', 'Chozo', 'Scavenger'], + 'progressionSpeed': ['slowest', 'slow', 'medium', 'fast', 'fastest', 'basic', 'VARIAble', 'speedrun'], + 'progressionDifficulty': ['easier', 'normal', 'harder'], + 'morphPlacement': ['early', 'normal'], #['early', 'late', 'normal'], + 'energyQty': ['ultra sparse', 'sparse', 'medium', 'vanilla'], + 'gravityBehaviour': ['Vanilla', 'Balanced', 'Progressive'] + } + return defaultMultiValues + +def getPresetValues(): + return [ + "newbie", + "casual", + "regular", + "veteran", + "expert", + "master", + "samus", + "Season_Races", + "SMRAT2021", + "solution", + "custom", + "varia_custom" + ] + +# from web to cli +def convertParam(randoParams, param, inverse=False): + value = randoParams.get(param, "off" if inverse == False else "on") + if value == "on": + return True if inverse == False else False + elif value == "off": + return False if inverse == False else True + elif value == "random": + return "random" + raise Exception("invalid value for parameter {}".format(param)) + +def loadRandoPreset(world, player, args): + defaultMultiValues = getDefaultMultiValues() + diffs = ["easy", "medium", "hard", "harder", "hardcore", "mania", "infinity"] + presetValues = getPresetValues() + + args.animals = world.animals[player].value + args.noVariaTweaks = not world.varia_tweaks[player].value + args.maxDifficulty = diffs[world.max_difficulty[player].value] + args.suitsRestriction = world.suits_restriction[player].value + #args.hideItems = world.hide_items[player].value + args.strictMinors = world.strict_minors[player].value + args.noLayout = not world.layout_patches[player].value + args.gravityBehaviour = defaultMultiValues["gravityBehaviour"][world.gravity_behaviour[player].value] + args.nerfedCharge = world.nerfed_charge[player].value + args.area = world.area_randomization[player].value != 0 + if args.area: + args.areaLayoutBase = not world.area_layout[player].value + args.lightArea = world.area_randomization[player].value == 1 + #args.escapeRando + #args.noRemoveEscapeEnemies + args.doorsColorsRando = world.doors_colors_rando[player].value + args.allowGreyDoors = world.allow_grey_doors[player].value + args.bosses = world.boss_randomization[player].value + if world.fun_combat[player].value: + args.superFun.append("Combat") + if world.fun_movement[player].value: + args.superFun.append("Movement") + if world.fun_suits[player].value: + args.superFun.append("Suits") + + ipsPatches = {"spin_jump_restart":"spinjumprestart", "rando_speed":"rando_speed", "elevators_doors_speed":"elevators_doors_speed", "refill_before_save":"refill_before_save"} + for settingName, patchName in ipsPatches.items(): + if hasattr(world, settingName) and getattr(world, settingName)[player].value: + args.patches.append(patchName + '.ips') + + patches = {"no_music":"No_Music", "infinite_space_jump":"Infinite_Space_Jump"} + for settingName, patchName in patches.items(): + if hasattr(world, settingName) and getattr(world, settingName)[player].value: + args.patches.append(patchName) + + args.hud = world.hud[player].value + args.morphPlacement = defaultMultiValues["morphPlacement"][world.morph_placement[player].value] + #args.majorsSplit + #args.scavNumLocs + #args.scavRandomized + #args.scavEscape + args.startLocation = defaultMultiValues["startLocation"][world.start_location[player].value] + #args.progressionDifficulty + #args.progressionSpeed + args.missileQty = world.missile_qty[player].value / float(10) + args.superQty = world.super_qty[player].value / float(10) + args.powerBombQty = world.power_bomb_qty[player].value / float(10) + args.minorQty = world.minor_qty[player].value + args.energyQty = defaultMultiValues["energyQty"][world.energy_qty[player].value] + #args.minimizerN + #args.minimizerTourian + + return presetValues[world.preset[player].value] + +def getRandomizerDefaultParameters(): + defaultParams = {} + defaultMultiValues = getDefaultMultiValues() + + defaultParams['complexity'] = "simple" + defaultParams['preset'] = 'regular' + defaultParams['randoPreset'] = "" + defaultParams['raceMode'] = "off" + defaultParams['majorsSplit'] = "Full" + defaultParams['majorsSplitMultiSelect'] = defaultMultiValues['majorsSplit'] + defaultParams['scavNumLocs'] = "10" + defaultParams['scavRandomized'] = "off" + defaultParams['scavEscape'] = "off" + defaultParams['startLocation'] = "Landing Site" + defaultParams['startLocationMultiSelect'] = defaultMultiValues['startLocation'] + defaultParams['maxDifficulty'] = 'hardcore' + defaultParams['progressionSpeed'] = "medium" + defaultParams['progressionSpeedMultiSelect'] = defaultMultiValues['progressionSpeed'] + defaultParams['progressionDifficulty'] = 'normal' + defaultParams['progressionDifficultyMultiSelect'] = defaultMultiValues['progressionDifficulty'] + defaultParams['morphPlacement'] = "early" + defaultParams['morphPlacementMultiSelect'] = defaultMultiValues['morphPlacement'] + defaultParams['suitsRestriction'] = "on" + defaultParams['hideItems'] = "off" + defaultParams['strictMinors'] = "off" + defaultParams['missileQty'] = "3" + defaultParams['superQty'] = "2" + defaultParams['powerBombQty'] = "1" + defaultParams['minorQty'] = "100" + defaultParams['energyQty'] = "vanilla" + defaultParams['energyQtyMultiSelect'] = defaultMultiValues['energyQty'] + defaultParams['areaRandomization'] = "off" + defaultParams['areaLayout'] = "off" + defaultParams['lightAreaRandomization'] = "off" + defaultParams['doorsColorsRando'] = "off" + defaultParams['allowGreyDoors'] = "off" + defaultParams['escapeRando'] = "off" + defaultParams['removeEscapeEnemies'] = "off" + defaultParams['bossRandomization'] = "off" + defaultParams['minimizer'] = "off" + defaultParams['minimizerQty'] = "45" + defaultParams['minimizerTourian'] = "off" + defaultParams['funCombat'] = "off" + defaultParams['funMovement'] = "off" + defaultParams['funSuits'] = "off" + defaultParams['layoutPatches'] = "on" + defaultParams['variaTweaks'] = "on" + defaultParams['gravityBehaviour'] = "Balanced" + defaultParams['gravityBehaviourMultiSelect'] = defaultMultiValues['gravityBehaviour'] + defaultParams['nerfedCharge'] = "off" + defaultParams['itemsounds'] = "on" + defaultParams['elevators_doors_speed'] = "on" + defaultParams['spinjumprestart'] = "off" + defaultParams['rando_speed'] = "off" + defaultParams['Infinite_Space_Jump'] = "off" + defaultParams['refill_before_save'] = "off" + defaultParams['hud'] = "off" + defaultParams['animals'] = "off" + defaultParams['No_Music'] = "off" + defaultParams['random_music'] = "off" + + return defaultParams + +def fixEnergy(items): + # display number of energy used + energies = [i for i in items if i.find('ETank') != -1] + if len(energies) > 0: + (maxETank, maxReserve, maxEnergy) = (0, 0, 0) + for energy in energies: + nETank = int(energy[0:energy.find('-ETank')]) + if energy.find('-Reserve') != -1: + nReserve = int(energy[energy.find(' - ')+len(' - '):energy.find('-Reserve')]) + else: + nReserve = 0 + nEnergy = nETank + nReserve + if nEnergy > maxEnergy: + maxEnergy = nEnergy + maxETank = nETank + maxReserve = nReserve + items.remove(energy) + items.append('{}-ETank'.format(maxETank)) + if maxReserve > 0: + items.append('{}-Reserve'.format(maxReserve)) + return items diff --git a/worlds/sm/variaRandomizer/utils/vcr.py b/worlds/sm/variaRandomizer/utils/vcr.py new file mode 100644 index 00000000..6a2a4d04 --- /dev/null +++ b/worlds/sm/variaRandomizer/utils/vcr.py @@ -0,0 +1,27 @@ +import json, os.path + +# record solver/rando to play in the VCR tracker +class VCR(object): + def __init__(self, name, type): + self.baseName = os.path.basename(os.path.splitext(name)[0]) + self.outFileName = "{}.{}.vcr".format(self.baseName, type) + self.empty() + + def empty(self): + self.tape = [] + + def addLocation(self, locName, itemName): + self.tape.append({'type': 'location', 'loc': locName, 'item': itemName}) + + def addRollback(self, count): + self.tape.append({'type': 'rollback', 'count': count}) + + def dump(self): + with open(self.outFileName, 'w') as jsonFile: + json.dump(self.tape, jsonFile) + + # in scavenger we have the rando solver then the scav solver, generate vcr for both + def reinit(self, type): + self.dump() + self.outFileName = "{}.{}.vcr".format(self.baseName, type) + self.empty() diff --git a/worlds/sm/variaRandomizer/utils/version.py b/worlds/sm/variaRandomizer/utils/version.py new file mode 100644 index 00000000..686ade28 --- /dev/null +++ b/worlds/sm/variaRandomizer/utils/version.py @@ -0,0 +1,3 @@ +# version displayed on the title screen, must be a max 32 chars [a-z0-9.-] string +# either 'beta' or 'r.yyyy.mm.dd' +displayedVersion = 'beta'