// Latest version should always be at top of file so it is default! state("Froghop", "2.5.0.0") { int room : 0x00D452D4; double gameTimer : 0x00B35220, 0x48, 0x10, 0x17C0, 0x190; double isPaused : 0x00B35220, 0x48, 0x10, 0x17C0, 0xE0; double levelComplete : 0x00B35220, 0x48, 0x10, 0x17C0, 0xC0; double gemCount : 0x00B35220, 0x48, 0x10, 0x2530, 0x00; double gem1 : 0x00B35220, 0x48, 0x10, 0x40, 0x00, 0x90, 0x00; double gem2 : 0x00B35220, 0x48, 0x10, 0x40, 0x00, 0x90, 0x10; double gem3 : 0x00B35220, 0x48, 0x10, 0x40, 0x00, 0x90, 0x20; double gem4 : 0x00B35220, 0x48, 0x10, 0x40, 0x00, 0x90, 0x30; double gem5 : 0x00B35220, 0x48, 0x10, 0x40, 0x00, 0x90, 0x40; } state("Froghop", "2.4.7.0") { int room : 0x00A3FCF4; double gameTimer : 0x0082FC70, 0x48, 0x10, 0x3CB0, 0x1B0; double isPaused : 0x0082FC70, 0x48, 0x10, 0x3CB0, 0xE0; double levelComplete : 0x0082FC70, 0x48, 0x10, 0x3CB0, 0xC0; double gemCount : 0x0082FC70, 0x48, 0x10, 0x2F10, 0x00; double gem1 : 0x0082FC70, 0x48, 0x10, 0xA20, 0x00, 0x90, 0x00; double gem2 : 0x0082FC70, 0x48, 0x10, 0xA20, 0x00, 0x90, 0x10; double gem3 : 0x0082FC70, 0x48, 0x10, 0xA20, 0x00, 0x90, 0x20; double gem4 : 0x0082FC70, 0x48, 0x10, 0xA20, 0x00, 0x90, 0x30; double gem5 : 0x0082FC70, 0x48, 0x10, 0xA20, 0x00, 0x90, 0x40; } state("Froghop", "2.4.6.0") { int room : 0x0089B4C8; double gameTimer : 0x006715A0, 0x1F18, 0x48, 0x10, 0x60, 0xF0; double isPaused : 0x006715A0, 0x1F18, 0x48, 0x10, 0x60, 0x30; double levelComplete : 0x006715A0, 0x1F18, 0x48, 0x10, 0x60, 0x10; double gemCount : 0x0068A9A0, 0x48, 0x10, 0x38F0, 0x00; double gem1 : 0x0068A9A0, 0x48, 0x10, 0x1400, 0x00, 0x90, 0x00; double gem2 : 0x0068A9A0, 0x48, 0x10, 0x1400, 0x00, 0x90, 0x10; double gem3 : 0x0068A9A0, 0x48, 0x10, 0x1400, 0x00, 0x90, 0x20; double gem4 : 0x0068A9A0, 0x48, 0x10, 0x1400, 0x00, 0x90, 0x30; double gem5 : 0x0068A9A0, 0x48, 0x10, 0x1400, 0x00, 0x90, 0x40; } state("Froghop", "2.4.5.0") { int room : 0x0089B4C8; double gameTimer : 0x006715A0, 0x1F18, 0x48, 0x10, 0x40, 0x100; double isPaused : 0x006715A0, 0x1F18, 0x48, 0x10, 0x40, 0x40; double levelComplete : 0x006715A0, 0x1F18, 0x48, 0x10, 0x40, 0x20; double gemCount : 0x0068A9A0, 0x48, 0x10, 0x1DE0, 0x00; double gem1 : 0x0068A9A0, 0x48, 0x10, 0x38F0, 0x00, 0x90, 0x00; double gem2 : 0x0068A9A0, 0x48, 0x10, 0x38F0, 0x00, 0x90, 0x10; double gem3 : 0x0068A9A0, 0x48, 0x10, 0x38F0, 0x00, 0x90, 0x20; double gem4 : 0x0068A9A0, 0x48, 0x10, 0x38F0, 0x00, 0x90, 0x30; double gem5 : 0x0068A9A0, 0x48, 0x10, 0x38F0, 0x00, 0x90, 0x40; } state("Froghop", "2.4.4.0") { int room : 0x0089B4C8; double gameTimer : 0x006715A0, 0x1F18, 0x48, 0x10, 0x40, 0x100; double isPaused : 0x006715A0, 0x1F18, 0x48, 0x10, 0x40, 0x40; double levelComplete : 0x006715A0, 0x1F18, 0x48, 0x10, 0x40, 0x20; double gemCount : 0x0068A9A0, 0x48, 0x10, 0x1DE0, 0x00; double gem1 : 0x0068A9A0, 0x48, 0x10, 0x38F0, 0x00, 0x90, 0x00; double gem2 : 0x0068A9A0, 0x48, 0x10, 0x38F0, 0x00, 0x90, 0x10; double gem3 : 0x0068A9A0, 0x48, 0x10, 0x38F0, 0x00, 0x90, 0x20; double gem4 : 0x0068A9A0, 0x48, 0x10, 0x38F0, 0x00, 0x90, 0x30; double gem5 : 0x0068A9A0, 0x48, 0x10, 0x38F0, 0x00, 0x90, 0x40; } state("Froghop", "2.4.3.0") { int room : 0x0089B4C8; double gameTimer : 0x006715A0, 0x1E70, 0x48, 0x10, 0x40, 0x3D0; double isPaused : 0x006715A0, 0x1E70, 0x48, 0x10, 0x40, 0x40; double levelComplete : 0x006715A0, 0x1E70, 0x48, 0x10, 0x40, 0x10; double gemCount : 0x0068A9A0, 0x48, 0x10, 0x1920, 0x00; double gem1 : 0x0068A9A0, 0x48, 0x10, 0x3430, 0x00, 0x90, 0x00; double gem2 : 0x0068A9A0, 0x48, 0x10, 0x3430, 0x00, 0x90, 0x10; double gem3 : 0x0068A9A0, 0x48, 0x10, 0x3430, 0x00, 0x90, 0x20; double gem4 : 0x0068A9A0, 0x48, 0x10, 0x3430, 0x00, 0x90, 0x30; double gem5 : 0x0068A9A0, 0x48, 0x10, 0x3430, 0x00, 0x90, 0x40; } state("Froghop", "2.4.2.0") { int room : 0x0076A5A8; double gameTimer : 0x007772C0, 0x30, 0x348, 0x1E0; double isPaused : 0x007772C0, 0x30, 0x348, 0x130; double levelComplete : 0x007772C0, 0x30, 0x348, 0x100; double gemCount : 0x007772C0, 0x30, 0x1A40, 0x00; double gem1 : 0x007772C0, 0x30, 0x2E8C, 0x00, 0x64, 0x00; double gem2 : 0x007772C0, 0x30, 0x2E8C, 0x00, 0x64, 0x10; double gem3 : 0x007772C0, 0x30, 0x2E8C, 0x00, 0x64, 0x20; double gem4 : 0x007772C0, 0x30, 0x2E8C, 0x00, 0x64, 0x30; double gem5 : 0x007772C0, 0x30, 0x2E8C, 0x00, 0x64, 0x40; } state("Froghop", "2.4.1.0") { int room : 0x0076A5A8; double gameTimer : 0x007772C0, 0x30, 0x348, 0x200; double isPaused : 0x007772C0, 0x30, 0x348, 0x150; double levelComplete : 0x007772C0, 0x30, 0x348, 0x120; double gemCount : 0x007772C0, 0x30, 0x5F4, 0x00; double gem1 : 0x007772C0, 0x30, 0x1A40, 0x00, 0x64, 0x00; double gem2 : 0x007772C0, 0x30, 0x1A40, 0x00, 0x64, 0x10; double gem3 : 0x007772C0, 0x30, 0x1A40, 0x00, 0x64, 0x20; double gem4 : 0x007772C0, 0x30, 0x1A40, 0x00, 0x64, 0x30; double gem5 : 0x007772C0, 0x30, 0x1A40, 0x00, 0x64, 0x40; } state("Froghop", "2.4.0.0") { int room : 0x0076A5A8; double gameTimer : 0x007772C0, 0x30, 0x348, 0x200; double isPaused : 0x007772C0, 0x30, 0x348, 0x150; double levelComplete : 0x007772C0, 0x30, 0x348, 0x120; double gemCount : 0x007772C0, 0x30, 0x5F4, 0x00; double gem1 : 0x007772C0, 0x30, 0x1A40, 0x00, 0x64, 0x00; double gem2 : 0x007772C0, 0x30, 0x1A40, 0x00, 0x64, 0x10; double gem3 : 0x007772C0, 0x30, 0x1A40, 0x00, 0x64, 0x20; double gem4 : 0x007772C0, 0x30, 0x1A40, 0x00, 0x64, 0x30; double gem5 : 0x007772C0, 0x30, 0x1A40, 0x00, 0x64, 0x40; } state("Froghop", "2.3.0.0") { int room : 0x00769598; double gameTimer : 0x007762B0, 0x30, 0x3C0, 0x810; double isPaused : 0x007762B0, 0x30, 0x3C0, 0x760; double levelComplete : 0x007762B0, 0x30, 0x3C0, 0x730; double gemCount : 0x005590C0, 0x24, 0x10, 0x21F0, 0x00; double gem1 : 0x007762B0, 0x30, 0x63C, 0x00, 0x64, 0x00; double gem2 : 0x007762B0, 0x30, 0x63C, 0x00, 0x64, 0x10; double gem3 : 0x007762B0, 0x30, 0x63C, 0x00, 0x64, 0x20; double gem4 : 0x007762B0, 0x30, 0x63C, 0x00, 0x64, 0x30; double gem5 : 0x007762B0, 0x30, 0x63C, 0x00, 0x64, 0x40; } state("Froghop", "2.2.1.0") { int room : 0x006F9C30; double gameTimer : 0x00707918, 0x30, 0x960, 0x70; double isPaused : 0x00707918, 0x30, 0x1F8, 0x40; double levelComplete : 0x00707918, 0x30, 0x1F8, 0x00; double gemCount : 0x00707918, 0x30, 0x150, 0x90; double gem1 : 0x0070791C, 0x850, 0x64, 0x00; double gem2 : 0x0070791C, 0x850, 0x64, 0x10; double gem3 : 0x0070791C, 0x850, 0x64, 0x20; double gem4 : 0x0070791C, 0x850, 0x64, 0x30; double gem5 : 0x0070791C, 0x850, 0x64, 0x40; } state("Froghop", "2.2.0.0") { int room : 0x00722248; double gameTimer : 0x00511D80, 0x2C, 0x10, 0xDB0, 0x00; double isPaused : 0x00511D80, 0x2C, 0x10, 0x1FE0, 0x00; double levelComplete : 0x00511D80, 0x2C, 0x10, 0x1878, 0x00; double gemCount : 0x00511D80, 0x2C, 0x10, 0x126C, 0x00; double gem1 : 0x00511D80, 0x2C, 0x10, 0x26A0, 0x00, 0x6C, 0x00; double gem2 : 0x00511D80, 0x2C, 0x10, 0x26A0, 0x00, 0x6C, 0x10; double gem3 : 0x00511D80, 0x2C, 0x10, 0x26A0, 0x00, 0x6C, 0x20; double gem4 : 0x00511D80, 0x2C, 0x10, 0x26A0, 0x00, 0x6C, 0x30; double gem5 : 0x00511D80, 0x2C, 0x10, 0x26A0, 0x00, 0x6C, 0x40; } state("Froghop", "2.1.1.0") { int room : 0x017B9150; double gameTimer : 0x017AB240, 0x54C, 0x2C, 0x10, 0x90, 0x3C0; double isPaused : 0x017AB240, 0x54C, 0x2C, 0x10, 0x90, 0x0330; double levelComplete : 0x017AB240, 0x54C, 0x2C, 0x10, 0x90, 0x310; double gemCount : 0x017AB240, 0x54C, 0x34C, 0x2C, 0x10, 0x30, 0x20; double gem1 : 0x017A71F4, 0x30, 0x288, 0x60, 0x6C, 0x00; double gem2 : 0x017A71F4, 0x30, 0x288, 0x60, 0x6C, 0x10; double gem3 : 0x017A71F4, 0x30, 0x288, 0x60, 0x6C, 0x20; double gem4 : 0x017A71F4, 0x30, 0x288, 0x60, 0x6C, 0x30; double gem5 : 0x017A71F4, 0x30, 0x288, 0x60, 0x6C, 0x40; } state("Frog Hop", "2.0.0.0") { int room : 0x006EFE10; double gameTimer : 0x004CE070, 0x00, 0x2C, 0x10, 0x2FC4, 0x00; double isPaused : 0x004E0AB0, 0x428, 0x50, 0x2C, 0x10, 0x00, 0x07E0; double levelComplete : 0x004E0AB0, 0x53C, 0x30, 0x2C, 0x10, 0x00, 0x360; double gemCount : 0x004E0AB0, 0x53C, 0x30, 0x2C, 0x10, 0x00, 0x420; double gem1 : 0x006FCAF4, 0x30, 0x1218, 0x00, 0x6C, 0x00; double gem2 : 0x006FCAF4, 0x30, 0x1218, 0x00, 0x6C, 0x10; double gem3 : 0x006FCAF4, 0x30, 0x1218, 0x00, 0x6C, 0x20; double gem4 : 0x006FCAF4, 0x30, 0x1218, 0x00, 0x6C, 0x30; double gem5 : 0x006FCAF4, 0x30, 0x1218, 0x00, 0x6C, 0x40; } state("Frog Hop", "v1.03") { int room : 0x00617EA0; double gameTimer : 0x0040794C, 0x44, 0x10, 0xA00, 0x00; double isPaused : 0x003F8F44, 0x00, 0x44, 0x10, 0x28, 0x00; double levelComplete : 0x003F8F44, 0x00, 0x44, 0x10, 0x3C4, 0x00; double gemCount : 0x003F8F44, 0x00, 0x44, 0x10, 0xA30, 0x00; double gem1 : 0x003F8F44, 0x00, 0x44, 0x10, 0xA54, 0x00, 0x04, 0x04, 0x00; double gem2 : 0x003F8F44, 0x00, 0x44, 0x10, 0xA54, 0x00, 0x04, 0x04, 0x10; double gem3 : 0x003F8F44, 0x00, 0x44, 0x10, 0xA54, 0x00, 0x04, 0x04, 0x20; double gem4 : 0x003F8F44, 0x00, 0x44, 0x10, 0xA54, 0x00, 0x04, 0x04, 0x30; double gem5 : 0x003F8F44, 0x00, 0x44, 0x10, 0xA54, 0x00, 0x04, 0x04, 0x40; } startup { settings.Add("autostart", true, "Auto-start at World Map"); settings.SetToolTip("autostart", "By default will only start on a new file."); settings.Add("notnew", false, "Even if not a new file", "autostart"); settings.Add("100percent", false, "100%"); settings.SetToolTip("100percent", "Will only split after you complete a level with all gems."); settings.Add("world_1", true, "World 1"); settings.Add("world_2", true, "World 2"); settings.Add("world_3", true, "World 3"); settings.Add("world_4", true, "World 4"); settings.Add("world_5", true, "World 5"); settings.CurrentDefaultParent = "world_1"; settings.Add("level_15", true, "World 1-1"); settings.Add("level_22", true, "World 1-2"); settings.Add("level_26", true, "World 1-3 (Bottom)"); settings.SetToolTip("level_26", "NOTE: Having multiple branches checked does not required you to complete them. It simply will cause a split at the end of the level if it is checked. You don't have to disable anything for Any% runs."); settings.Add("level_32", true, "World 1-3 (Top)"); settings.SetToolTip("level_32", "NOTE: Having multiple branches checked does not required you to complete them. It simply will cause a split at the end of the level if it is checked. You don't have to disable anything for Any% runs."); settings.Add("level_36", true, "World 1-4"); settings.CurrentDefaultParent = "world_2"; settings.Add("level_42", true, "World 2-1"); settings.Add("level_47", true, "World 2-2"); settings.Add("level_52", true, "World 2-3 (Top)"); settings.Add("level_56", true, "World 2-3 (Bottom)"); settings.Add("level_60", true, "World 2-4"); settings.Add("level_63", true, "World 2-5"); settings.CurrentDefaultParent = "world_3"; settings.Add("level_67", true, "World 3-1"); settings.Add("level_70", true, "World 3-2 (Left)"); settings.Add("level_74", true, "World 3-2 (Right)"); settings.Add("level_77", true, "World 3-3"); settings.Add("level_82", true, "World 3-4"); settings.Add("level_87", true, "World 3-5"); settings.CurrentDefaultParent = "world_4"; settings.Add("level_90", true, "World 4-1"); settings.Add("level_96", true, "World 4-2 (Left)"); settings.Add("level_102", true, "World 4-2 (Middle)"); settings.Add("level_107", true, "World 4-2 (Right)"); settings.Add("level_111", true, "World 4-3"); settings.Add("level_118", true, "World 4-4"); settings.CurrentDefaultParent = "world_5"; settings.Add("level_119", true, "World 5-1"); settings.Add("level_126", true, "World 5-2 (Left)"); settings.Add("level_132", true, "World 5-2 (Right)"); settings.Add("level_139", true, "World 5-3"); settings.Add("level_145", true, "World 5-4"); settings.Add("level_160", true, "World 5-5"); settings.CurrentDefaultParent = null; settings.Add("outro", true, "Meet Jumpy"); settings.SetToolTip("outro", "After the final boss and before the credits. This is the scene where Hoppy meets up with Jumpy."); Action DebugOutput = (text) => { print("[Autosplitter] "+text); }; vars.DebugOutput = DebugOutput; // Frog Hop doesn't really track levels; it just has room IDs which are kind of weird... // We'll track the level when it starts so we don't have to check multiple room IDs in split. vars.level = 0; // This is a constant. World map is special because we use it to detect several things. vars.worldMapId = 6; // Might be different in different version? // Keep track of total run based on game time. (Stored in milliseconds) vars.savedTime = 0; } init { int moduleSize = modules.First().ModuleMemorySize; if (moduleSize == 6631424) { version = "v1.03"; } else { // looks like "2.0.0.0" version = modules.First().FileVersionInfo.ProductVersion; } vars.DebugOutput("INITIALIZED with version: " + version); } exit { timer.IsGameTimePaused = true; } update { // Disable if version not detected if (version == "") return false; // This can happen if the run is reset if (timer.CurrentPhase == TimerPhase.NotRunning && vars.savedTime > 0) { vars.savedTime = 0; } // Look for level start. if (vars.level == 0 && settings.ContainsKey("level_" + current.room) && settings["level_" + current.room] == true) { // We could map level to our own ID, but there's no reason to really vars.level = current.room; } else if (current.room == vars.worldMapId) { // Reset whenever we return to the world map so we can find the next level start. vars.level = 0; } } start { if (current.room == vars.worldMapId && settings["autostart"] && (old.room == 177 || settings["notnew"])) { vars.DebugOutput("Timer started"); return true; } } split { if (current.room <= vars.worldMapId) { return false; } // Only trigger once, right when the level is marked complete if (current.levelComplete == 1 && old.levelComplete == 0 && vars.level > 0) { if (settings["level_" + vars.level]) { if (settings["100percent"]) { var allGems = new double[] { current.gem1, current.gem2, current.gem3, current.gem4, current.gem5 }; if (allGems.Sum(x => x > 0 ? 1 : 0) < current.gemCount) return false; } vars.DebugOutput("Split level " + vars.level + " completed."); return true; } } // RoomID 7 = final room player has control; 164 = Stay; 171 = Leave; else if ( settings["outro"] && ( (old.room == 7 && (current.room == 164 || current.room == 171)) || // Normal finish current.room == 170 // Rocket finish ) ) { return true; } } isLoading { return current.room <= vars.worldMapId || current.room >= 164 || current.isPaused == 1 || current.levelComplete == 1; } gameTime { if (current.room <= vars.worldMapId) { timer.SetGameTime(new TimeSpan(0)); return TimeSpan.FromMilliseconds(vars.savedTime); } // gameTime is frames rendered. Game runs at 60 fps double ms = current.gameTimer * (1000d / 60d); double oldMs = old.gameTimer * (1000d / 60d); if (ms > oldMs) { vars.savedTime += ms - oldMs; } // Game display rounds up to the nearest hundredth, although a frame is much longer // so such precision doesn't really matter. But it looks more consistent. return TimeSpan.FromMilliseconds(Math.Round(vars.savedTime / 10d) * 10d); }