about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--Lingo.asl102
1 files changed, 102 insertions, 0 deletions
diff --git a/Lingo.asl b/Lingo.asl new file mode 100644 index 0000000..f53eee9 --- /dev/null +++ b/Lingo.asl
@@ -0,0 +1,102 @@
1// Autosplitter script for Lingo, by hatkirby.
2//
3// Requires a version released January 10th, 2023 or later.
4//
5// Massive thanks to the game developer, Brenton, for working with me to
6// make this possible.
7
8state("Lingo") {}
9
10startup
11{
12 vars.log = (Action<string>)((string logLine) => {
13 print("[Lingo ASL] " + logLine);
14 });
15
16 settings.Add("end", false, "Split on The End");
17 settings.Add("unchallenged", false, "Split on The Unchallenged");
18 settings.Add("master", false, "Split on The Master");
19 settings.Add("pilgrimage", false, "Split on Pilgrimage");
20 settings.Add("showLastPanel",false, "Override first text component with the name of the most recently solved panel");
21
22 vars.log("Autosplitter loaded");
23}
24
25init
26{
27 // magic byte array format:
28 // [0-7]: 5b a6 7d fe b8 69 f1 80 (random bytes used for sigscanning)
29 // [8]: First input
30 // [9-40]: Name of last solved panel
31 IntPtr ptr = IntPtr.Zero;
32 foreach (var page in game.MemoryPages(true).Reverse()) {
33 var scanner = new SignatureScanner(game, page.BaseAddress, (int)page.RegionSize);
34 ptr = scanner.Scan(new SigScanTarget(0, "5b a6 7d fe b8 69 f1 80"));
35 if (ptr != IntPtr.Zero) {
36 break;
37 }
38 }
39 if (ptr == IntPtr.Zero) {
40 throw new Exception("Could not find magic autosplitter array!");
41 }
42 vars.firstInput = new MemoryWatcher<byte>(ptr + 8);
43 vars.lastPanel = new StringWatcher(ptr + 9, 32);
44
45 vars.log(String.Format("Magic autosplitter array: {0}", ptr.ToString("X")));
46
47 vars.updateText = false;
48 if (settings["showLastPanel"]) {
49 foreach (LiveSplit.UI.Components.IComponent component in timer.Layout.Components) {
50 if (component.GetType().Name == "TextComponent") {
51 vars.tc = component;
52 vars.tcs = vars.tc.Settings;
53 vars.updateText = true;
54 vars.log("Found text component at " + component);
55 break;
56 }
57 }
58 }
59}
60
61update
62{
63 vars.firstInput.Update(game);
64 vars.lastPanel.Update(game);
65
66 if (settings["showLastPanel"] && vars.updateText && vars.lastPanel.Old != vars.lastPanel.Current) {
67 vars.tcs.Text2 = vars.lastPanel.Current;
68 }
69}
70
71start
72{
73 return vars.firstInput.Old == 0 && vars.firstInput.Current == 255;
74}
75
76onStart
77{
78 if (settings["showLastPanel"] && vars.updateText) {
79 vars.tcs.Text1 = "Last Panel:";
80 vars.tcs.Text2 = "";
81 }
82}
83
84split
85{
86 if (settings["end"] && vars.lastPanel.Old != "Panel_end_end" && vars.lastPanel.Current == "Panel_end_end") {
87 vars.log("Split on The End");
88 return true;
89 }
90 if (settings["unchallenged"] && vars.lastPanel.Old == "Panel_challenged_unchallenged" && vars.lastPanel.Current == "Panel_challenged_unchallenged") {
91 vars.log("Split on The Unchallenged");
92 return true;
93 }
94 if (settings["master"] && vars.lastPanel.Old == "Panel_master_master" && vars.lastPanel.Current == "Panel_master_master") {
95 vars.log("Split on The Master");
96 return true;
97 }
98 if (settings["pilgrimage"] && vars.lastPanel.Old == "Panel_pilgrim" && vars.lastPanel.Current == "Panel_pilgrim") {
99 vars.log("Split on Pilgrimage");
100 return true;
101 }
102}