From 2ace51ebdb6a729a6a374e1dca53f564374401d7 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Thu, 13 Feb 2025 11:29:12 -0500 Subject: [Lingo 2] Started autosplitter --- Lingo 2.asl | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 Lingo 2.asl (limited to 'Lingo 2.asl') diff --git a/Lingo 2.asl b/Lingo 2.asl new file mode 100644 index 0000000..9c59387 --- /dev/null +++ b/Lingo 2.asl @@ -0,0 +1,145 @@ +// Autosplitter script for Lingo 2, by hatkirby. +// +// Massive thanks to the game developer, Brenton, for working with me to +// make this possible. + +state("Lingo2") {} + +startup +{ + // Relative to Livesplit.exe + vars.logFilePath = Directory.GetCurrentDirectory() + "\\autosplitter_lingo2.log"; + vars.log = (Action)((string logLine) => { + print("[Lingo 2 ASL] " + logLine); + string time = System.DateTime.Now.ToString("dd/MM/yy hh:mm:ss.fff"); + // AppendAllText will create the file if it doesn't exist. + System.IO.File.AppendAllText(vars.logFilePath, time + ": " + logLine + "\r\n"); + }); + + settings.Add("panels", false, "Split on solving a panel"); + settings.Add("panels_first", false, "Only split the first time each panel is solved", "panels"); + settings.Add("maps", false, "Split on changing maps"); + settings.Add("maps_first", false, "Only split the first time a map is entered", "maps"); + settings.Add("ends", false, "Split on any ending"); + settings.Add("keys", false, "Split on unlocking a key"); + settings.Add("paintings", false, "Split on unlocking a gallery painting"); + settings.Add("graves", false, "Split on completing a gravestone"); + settings.Add("masteries", false, "Split on collecting a mastery"); + + vars.prevPanelMap = ""; + vars.prevPanelPath = ""; + vars.prevMap = ""; + vars.collectedKeys = new HashSet(); + vars.latestKeyKey = null; + + vars.log("Autosplitter loaded"); +} + +init +{ + // magic byte array format: + // [0-7]: 9c 46 9f b0 4b 6a e0 8d (random bytes used for sigscanning) + // [8]: Loading flag + // [9]: First movement flag + // [10]: Latest unlocked key (0 if run just started) + // [11]: 1 or 2, indicating the level of the latest unlocked key + // [12-52]: Latest collectable unlocked (null terminated, truncated at 40 chars excluding null) + // [53-93]: Current map name (null terminated, truncated at 40 chars excluding null) + // [94-134]: Map name of latest solved panel (null terminated, truncated at 40 chars excluding null) + // [135-235]: Full nodepath of latest solved panel (null terminated, truncated at 100 chars excluding null) + IntPtr ptr = IntPtr.Zero; + foreach (var page in game.MemoryPages(true).Reverse()) { + var scanner = new SignatureScanner(game, page.BaseAddress, (int)page.RegionSize); + ptr = scanner.Scan(new SigScanTarget(0, "9c 46 9f b0 4b 6a e0 8d")); + if (ptr != IntPtr.Zero) { + break; + } + } + if (ptr == IntPtr.Zero) { + throw new Exception("Could not find magic autosplitter array!"); + } + vars.loading = new MemoryWatcher(ptr + 8); + vars.firstInput = new MemoryWatcher(ptr + 9); + vars.latestKey = new MemoryWatcher(ptr + 10); + vars.latestKeyLevel = new MemoryWatcher(ptr + 11); + vars.latestCollectible = new StringWatcher(ptr + 12, 41); + vars.currentMap = new StringWatcher(ptr + 53, 41); + vars.lastPanelMap = new StringWatcher(ptr + 94, 41); + vars.lastPanelPath = new StringWatcher(ptr + 135, 101); + + vars.log(String.Format("Magic autosplitter array: {0}", ptr.ToString("X"))); +} + +update +{ + vars.loading.Update(game); + vars.firstInput.Update(game); + vars.latestKey.Update(game); + vars.latestKeyLevel.Update(game); + vars.latestCollectible.Update(game); + vars.currentMap.Update(game); + vars.lastPanelMap.Update(game); + vars.lastPanelPath.Update(game); + + if (vars.latestKeyLevel.Current > 0) + { + vars.latestKeyKey = string.Format("{0}{1}", (char)vars.latestKey.Current, vars.latestKeyLevel.Current); + } +} + +start +{ + return vars.firstInput.Old == 0 && vars.firstInput.Current == 1; +} + +onStart +{ + vars.prevPanelMap = vars.lastPanelMap.Current; + vars.prevPanelPath = vars.lastPanelPath.Current; + vars.prevMap = vars.currentMap.Current; +} + +reset +{ + return vars.firstInput.Old == 1 && vars.firstInput.Current == 0; +} + +isLoading +{ + return vars.loading.Current == 1; +} + +split +{ + bool should_split = false; + + if (vars.lastPanelMap.Current != vars.prevPanelMap || vars.lastPanelPath.Current != vars.prevPanelPath) { + if (settings["panels"]) { + should_split = true; + vars.log("Split on any panel: " + vars.lastPanelMap.Current + " / " + vars.lastPanelPath.Current); + } + + vars.prevPanelMap = vars.lastPanelMap.Current; + vars.prevPanelPath = vars.lastPanelPath.Current; + } + else if (vars.currentMap.Current != vars.prevMap) + { + if (settings["maps"]) { + should_split = true; + vars.log("Split on map change: " + vars.currentMap.Current); + } + + vars.prevMap = vars.currentMap.Current; + } + else if (vars.latestKeyKey != null && !vars.collectedKeys.Contains(vars.latestKeyKey)) + { + if (settings["keys"]) { + should_split = true; + vars.log("Split on collected key: " + vars.latestKeyKey); + } + + vars.collectedKeys.Add(vars.latestKeyKey); + } + + return should_split; +} -- cgit 1.4.1