From 9e66ee6b3c375252562ff4df6c91e89781a586da Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Thu, 18 Aug 2022 14:51:46 -0400 Subject: [Manifold Garden] Rewrote using ASLHelper This version no longer relies on pointer paths, and should thus be pretty version independent. I tested it on both 14704 and 15769. The priority is still 14704, but version independence is nice. --- Manifold Garden.asl | 206 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 155 insertions(+), 51 deletions(-) (limited to 'Manifold Garden.asl') diff --git a/Manifold Garden.asl b/Manifold Garden.asl index a5009ad..71cb203 100644 --- a/Manifold Garden.asl +++ b/Manifold Garden.asl @@ -1,79 +1,164 @@ -// AutoSplit script for Manifold Garden 1.0.30.14704 +// AutoSplit script for Manifold Garden // // Written by hatkirby, with help from preshing and Gelly. // -// Automatically starts the timer when a new game is started. You must still reset the timer -// manually between runs. +// Automatically starts the timer when a new game is started. You must still +// reset the timer manually between runs. // -// A split is also triggered after being in one of the ending cutscenes for 1.1 seconds, -// since this is when the kaleidoscope appears. +// A split is also triggered after being in one of the ending cutscenes for 1.1 +// seconds, since this is when the kaleidoscope appears. // -// If you check "All God Cubes waypoints" in the script's Advanced settings (below), the script -// will only split at mandala scenes. This is useful when running "All God Cubes" categories. +// If you check "All God Cubes waypoints" in the script's Advanced settings +// (below), the script will only split at mandala scenes. This is useful when +// running "All God Cubes" categories. // -// The pointer path to the current level often changes when a new version of Manifold Garden is -// released. When that happens, a new pointer path must be found using CheatEngine. If the -// current pointer path stops working (even for a frame or two), a message is logged to the -// debug output. +// This should be mostly version-independent, but it works best on 1.0.0.14704 +// (Steam "Speedrunning Branch"). // // To view debug output (print statements from this script), use DebugView: // https://technet.microsoft.com/en-us/Library/bb896647.aspx -state("ManifoldGarden") { - // These pointer paths seem to work with Manifold Garden 1.1.0.14704 (2020-11-09): - int level: "mono-2.0-bdwgc.dll", 0x00494DC8, 0x48, 0x120, 0x120, 0x120, 0x120, 0x120, 0xC60, 0x1A0; - bool transitionFadeHeld: "UnityPlayer.dll", 0x017945A8, 0x80, 0x10, 0x48, 0xA0, 0x10, 0xE40; - bool isLoadingGameFromUI: "UnityPlayer.dll", 0x017945A8, 0x90, 0x100, 0xC0, 0xC0, 0xC0, 0xC0, 0xDC1; - bool startScreenActive: "UnityPlayer.dll", 0x0178BBC0, 0x3B8, 0x38, 0x18, 0x8, 0x198, 0x0, 0x8ab; - - // Older pointer paths: - //int level: "UnityPlayer.dll", 0x014BE300, 0x60, 0xA8, 0x38, 0x30, 0xB0, 0x118, 0x5C; // 1.0.30.13294 (2020-02-25) - //int level: "UnityPlayer.dll", 0x01552858, 0x8, 0x0, 0xB8, 0x80, 0x80, 0x28, 0x5C; // 13294 - //int level: "UnityPlayer.dll", 0x01552858, 0x28, 0x8, 0xB8, 0x80, 0x80, 0x28, 0x5C; // 13294 - //int level: "UnityPlayer.dll", 0x01507BE0, 0x0, 0x928, 0x38, 0x30, 0xB0, 0x118, 0x5C; // 1.0.29.12904 (2020-02-??), 1.0.29.12830 (2019-12-18), 1.0.29.12781 (2019-12-11) - //int level: "UnityPlayer.dll", 0x01507C68, 0x8, 0x38, 0xA8, 0x58, 0x118, 0x5C; -} +state("ManifoldGarden") {} startup { + var bytes = File.ReadAllBytes(@"Components\LiveSplit.ASLHelper.bin"); + var type = Assembly.Load(bytes).GetType("ASLHelper.Unity"); + vars.Helper = Activator.CreateInstance(type, timer, this); + vars.Helper.LoadSceneManager = true; + settings.Add("every",true,"Split on every level change"); settings.Add("fall",false,"Including all ending falling scenes","every"); settings.Add("allGodCubes", false, "All God Cubes waypoints"); settings.Add("zero", false, "Zero% waypoints"); settings.Add("raymarchitecture", true, "Split on Raymarchitecture (ending cutscene)"); settings.Add("norepeats",false,"Split only on the first encounter of each level"); + vars.waypoints = null; vars.prevLevel = 0; vars.stopwatch = null; // Used for the final split vars.prev = new List(); vars.firstRoom = false; - vars.fall = new List{97, 98, 99, 101, 102, 103, 104}; + vars.inEnding = false; + vars.noSplitScenes = new List{ + "StudioLogoScreen", + "RequiredComponents", + "StartScreen_01", + "StartScreen_02", + "StartScreen_03", + "StartScreen_15", + "StartScreen_51", + "StartScreen_53", + "StartScreen_63" + }; + vars.startScreens = new List{ + "StartScreen_01", + "StartScreen_02", + "StartScreen_03", + "StartScreen_15", + "StartScreen_51", + "StartScreen_53", + "StartScreen_63" + }; + vars.endings = new List{ + "World_905_EndingCollapseCutscene_Optimized", + "World_907_EndingZeroCollapseCutscene_Optimized", + "World_906_EndingDarkCollapseCutscene_Optimized" + }; + vars.fall = new List{ + "World_903_EndingFallTwo_Optimized", + "World_904_EndingFallThree_Optimized", + "World_905_EndingCollapseCutscene_Optimized", + "World_923_AlternateEndingFallTwo_Optimized", + "World_924_AlternateEndingFallThree_Optimized", + "World_907_EndingZeroCollapseCutscene_Optimized", + "World_906_EndingDarkCollapseCutscene_Optimized" + }; + vars.mandalaScenes = new List{ + "AudioVisual_001_Optimized", + "AudioVisual_002_Optimized", + "AudioVisual_053_Optimized", + "AudioVisual_051_Optimized", + "AudioVisual_003_Optimized", + "AudioVisual_063_Optimized", + "AudioVisual_071_Optimized" + }; + vars.zeroPercentPoints = new List{ + "Hallway_W000_W052_Optimized", + "World_002_Optimized", + "Hallway_W026_W015_Optimized", + "Hallway_W612_W057_Optimized", + "Hallway_W073_W026_Optimized", + "World_804_Optimized", + "World_071_AkshardhamTemple_Optimized" + }; } init { - print(String.Format("**** AUTOSPLIT: Game found, pointer path {0} ****", - current.level == 0 ? "DOESN'T work (this is normal at startup)" : "works")); + vars.studioScreenDone = true; + vars.doneFirstLook = false; + vars.Helper.TryOnLoad = (Func)(mono => + { + var gameMan = mono.GetClass("GameManager"); + vars.Helper["isLoadingGameFromUI"] = gameMan.Make("isLoadingGameFromUI"); + + var versionNum = mono.GetClass("VersionNumber"); + vars.Helper["version"] = versionNum.MakeString("instance", "_text"); + + current.onStartScreen = false; + + return true; + }); + + vars.Helper.Load(); } update { - // Log a message when the pointer path starts/stops working: - if (current.level == 0 && old.level != 0) { - print("**** AUTOSPLIT: Pointer path STOPPED working ****"); - } else if (current.level != 0 && old.level == 0) { - print("**** AUTOSPLIT: Pointer path STARTED working ****"); + if (!vars.Helper.Update()) + return false; + + current.level = vars.Helper.Scenes.Active.Index; + current.isLoadingGameFromUI = vars.Helper["isLoadingGameFromUI"].Current; + + if (!vars.doneFirstLook) { + vars.doneFirstLook = true; + print(String.Format("Connected to Manifold Garden version {0}", vars.Helper["version"].Current)); + + current.onStartScreen = vars.startScreens.Contains(vars.Helper.Scenes.Active.Name); + + // The "isLoadingGameFromUI" boolean is set while the studio screen is + // showing during game startup, which means that if the autosplitter is + // running before the game opens, it'll erroneously start a run. To + // avoid this, when the autosplitter initialises, we check if we are on + // a noSplitScene (the game usually reports itself as being on a start + // screen rather than the studio screen) and if the game is loading. If + // so, we disable starting a run until the load is complete. If the + // splitter is opened after the game starts up, this shouldn't activate, + // which means run starting should work as expected. + if (vars.noSplitScenes.Contains(vars.Helper.Scenes.Active.Name) + && current.isLoadingGameFromUI) { + vars.studioScreenDone = false; + } + } else { + if (current.level != old.level) { + current.onStartScreen = vars.startScreens.Contains(vars.Helper.Scenes.Active.Name); + } + if (!vars.studioScreenDone) { + vars.studioScreenDone = !current.isLoadingGameFromUI; + } } } start { - // Start the timer as soon as a game is being loaded (specifically the moment you click - // a save slot to start a new game in, although it will also start if you just load a file). - // This boolean is set to true during the studio logo when the game starts up, so we check - // for that as well. - if (current.transitionFadeHeld && current.isLoadingGameFromUI) { + // Start the timer as soon as a game is being loaded (specifically the + // moment you click a save slot to start a new game in, although it will + // also start if you just load a file). This boolean is set to true during + // the studio logo when the game starts up, so we check for that as well. + if (vars.studioScreenDone && current.isLoadingGameFromUI) { print(String.Format("Level changed from {0} to {1}: START", old.level, current.level)); if (settings["zero"]) { - vars.waypoints = new List{106, 17, 110, 115, 111, 36, 44}; + vars.waypoints = vars.zeroPercentPoints; } else if (settings["allGodCubes"]) { - vars.waypoints = new List{82, 83, 84, 85, 86, 87, 88}; + vars.waypoints = vars.mandalaScenes; } else { vars.waypoints = null; } @@ -81,26 +166,31 @@ start { vars.stopwatch = Stopwatch.StartNew(); vars.prev.Clear(); vars.firstRoom = false; + vars.inEnding = false; return true; } } split { - // Split when level index changes. We don't split for the first room change in a run, - // because that is always going to be changing from -1 to 9, and it happens a couple of - // seconds after the timer starts. - if (vars.firstRoom && current.level != vars.prevLevel && current.level > 0) { + // Split when level index changes. We don't split for the first room change + // in a run, because that is always going to be changing from 3 to 73, and + // it happens a couple of seconds after the timer starts. + if (vars.firstRoom + && current.level != vars.prevLevel + && current.level > 0 + && !vars.noSplitScenes.Contains(vars.Helper.Scenes.Active.Name)) { + print(String.Format("{0}: '{1}'", current.level, vars.Helper.Scenes.Active.Name)); + string action = "NO SPLIT"; - // Ignore the split rules when script is reloaded mid-game: if (vars.prevLevel != 0) { // Split rules: if (settings["every"]) { - if (settings["fall"] || !vars.fall.Contains(current.level)) { + if (settings["fall"] || !vars.fall.Contains(vars.Helper.Scenes.Active.Name)) { action = "SPLIT"; } } else if (vars.waypoints != null) { - if (vars.waypoints.Contains(current.level)) { + if (vars.waypoints.Contains(vars.Helper.Scenes.Active.Name)) { action = "SPLIT"; } } @@ -115,19 +205,23 @@ split { print(String.Format("Level changed from {0} to {1}: {2}", vars.prevLevel, current.level, action)); } + if (vars.endings.Contains(vars.Helper.Scenes.Active.Name)) { + vars.inEnding = true; + } + vars.prevLevel = current.level; vars.stopwatch = Stopwatch.StartNew(); return action.StartsWith("SPLIT"); - } else if (!vars.firstRoom && current.level == 9) { + } else if (!vars.firstRoom && vars.Helper.Scenes.Active.Name == "World_000_Optimized") { vars.firstRoom = true; vars.prevLevel = current.level; - vars.prev.Add(9); + vars.prev.Add(current.level); } // Final split of the game: // Split after being in one of the ending cutscenes for 1.1 seconds. if (settings["raymarchitecture"] - && (current.level == 99 || current.level == 103 || current.level == 104) + && vars.inEnding && vars.stopwatch != null && vars.stopwatch.ElapsedMilliseconds >= 1100) { print("SPLIT on Raymarchitecture"); @@ -137,5 +231,15 @@ split { } reset { - return current.startScreenActive; + return current.onStartScreen && !old.onStartScreen; +} + +exit +{ + vars.Helper.Dispose(); +} + +shutdown +{ + vars.Helper.Dispose(); } -- cgit 1.4.1