OutlastTrials.asl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. Created by KunoDemetries
  3. With help debugging and testing from Symstery, Meta, and N3rd_Squared
  4. This ASL is a big mess that may have a few minor that'll be patched out through testing. I shouldn't have to update the ASL in the future as it finds direct pointers to each address.
  5. If any issues arise please contact myself or join the outlast community speedrun server here: https://discord.com/invite/ekHJZth8Cn
  6. If you would like to donate, as this ASL took around 30 hours to code and debugm my links are here: https://linktr.ee/KunoDemetries
  7. */
  8. state("TOTClient-Win64-Shipping")
  9. {
  10. }
  11. init
  12. {
  13. vars.doneMaps = new List<string>();
  14. vars.MapTimeResetter = new List<string>();
  15. vars.FinalSplitPerLevel = new List<string>();
  16. vars.totalGameTime = 0;
  17. current.FinalTime = "/";
  18. vars.watchertouseforIGT = "/";
  19. vars.runTimer = 0;
  20. vars.Spacer = 0;
  21. vars.LeveltimeUpdater = 0; // We need to only run the add. once per level (level name curLevel)
  22. current.Minutes = 0;
  23. current.Seconds = 0;
  24. current.Time = 0;
  25. vars.Trials = new List<string>
  26. {
  27. "KILL THE SNITCH",
  28. "CANCEL THE AUTOPSY",
  29. "SABOTAGE THE LOCKDOWN",
  30. "EXAM: KILL THE SNITCH",
  31. "GRIND THE BAD APPLES",
  32. "PUNISH THE MISCREANTS",
  33. "OPEN THE GATES",
  34. "EXAM: GRIND THE BAD APPLES",
  35. "CLEANSE THE ORPHANS",
  36. "FEED THE CHILDREN",
  37. "FOSTER THE ORPHANS",
  38. "EXAM: CLEANSE THE ORPHANS"
  39. };
  40. //TY Ero for helping me fix this code here, it basically is setting up the offset and pattern scanner for UE4 games. Same concept from Micro
  41. Func<int, string, IntPtr> scan = (offset, pattern) => {
  42. var scn = new SignatureScanner(game, game.MainModule.BaseAddress, game.MainModule.ModuleMemorySize);
  43. var pttern = new SigScanTarget(offset, pattern);
  44. var ptr = scn.Scan(pttern);
  45. return ptr + 0x4 + game.ReadValue<int>(ptr);
  46. };
  47. vars.CurMap = scan(0x3, "48 89 0D ???????? 48 89 0D ???????? 48 89 0D ???????? F3 ?? ?? 05") + 0xF0;
  48. vars.LevelName = scan(0x3, "48 8d 0d ?? ?? ?? ?? e8 ?? ?? ?? ?? 40 8a d7");
  49. vars.FinalTime = scan(0x3, "48 8D 15 ???????? 48 8B CB FF 90 ???????? 48 8B 8B");
  50. vars.GWorld = scan(0x3, "48 8B 1D ???????? 48 85 DB 74 35 33 D2");
  51. vars.IGT = scan(0x3, "48 8D 0D ???????? E8 ???????? 48 8B 0D ???????? 48 89 3C F1");
  52. print(vars.CurMap.ToString("X"));
  53. print(vars.LevelName.ToString("X"));
  54. print(vars.FinalTime.ToString("X"));
  55. print(vars.GWorld.ToString("X"));
  56. print(vars.IGT.ToString("X"));
  57. vars.watchers = new MemoryWatcherList
  58. {
  59. new StringWatcher(new DeepPointer(vars.LevelName,0xC0, 0x18, 0x278, 0x288, 0x0, 0x108, 0x1A), 100) { Name = "CurLevelName"},
  60. new StringWatcher(new DeepPointer(vars.CurMap, 0xE30, 0x0, 0x50, 0x290, 0xD0, 0x30), 100) { Name = "CurMap"},
  61. new MemoryWatcher<byte>(new DeepPointer(vars.GWorld, 0x188, 0x230, 0x1D0, 0x2A8)) { Name = "Loading"},
  62. new MemoryWatcher<float>(new DeepPointer(vars.IGT, 0x18, 0x8, 0x4BC)) { Name = "IGT"},
  63. };
  64. vars.IGTWatchers = new MemoryWatcherList
  65. {
  66. new StringWatcher(new DeepPointer(vars.FinalTime, 0x118, 0x20, 0x888, 0x128, 0x38, 0x0), 10) { Name = "PoliceStationLoad"},
  67. new StringWatcher(new DeepPointer(vars.FinalTime, 0x70, 0x20, 0x888, 0x128, 0x38, 0x0), 10) { Name = "SabatogeLoad"},
  68. new StringWatcher(new DeepPointer(vars.FinalTime, 0x78, 0x20, 0x888, 0x128, 0x38, 0x0), 10) { Name = "PoliceStationBasementLoad"},
  69. new StringWatcher(new DeepPointer(vars.FinalTime, 0xE8, 0x20, 0x888, 0x128, 0x38, 0x0), 10) { Name = "FunParkLoad"},
  70. new StringWatcher(new DeepPointer(vars.FinalTime, 0xB0, 0x20, 0x888, 0x128, 0x38, 0x0), 10) { Name = "FunParkBarnLoad"},
  71. new StringWatcher(new DeepPointer(vars.FinalTime, 0x128, 0x20, 0x888, 0x128, 0x38, 0x0), 10) { Name = "OrphanageLoad"},
  72. new StringWatcher(new DeepPointer(vars.FinalTime, 0xA8, 0x20, 0x888, 0x128, 0x38, 0x0), 10) { Name = "OrphanageServicesLoad"},
  73. };
  74. }
  75. startup
  76. {
  77. settings.Add("FSPL", false, "Split at the end of each trial? (dont have below settings enabled or you'll have it split twice between trials)");
  78. settings.Add("OT", true, "Outlast Trials");
  79. settings.Add("P1", true, "Program 1 - Police Station", "OT");
  80. settings.Add("P2", true, "Program 2 - Fun Park", "OT");
  81. settings.Add("P3", true, "Program 3 - Orphanage", "OT");
  82. settings.Add("PX", true, "Program X - Police Station", "OT");
  83. var tB = (Func<string, string, string, Tuple<string, string, string>>) ((elmt1, elmt2, elmt3) => { return Tuple.Create(elmt1, elmt2, elmt3); });
  84. var sB = new List<Tuple<string, string, string>>
  85. {
  86. tB("P1","KILL THE SNITCH","KILL THE SNITCH"),
  87. tB("P1","CANCEL THE AUTOPSY","CANCEL THE AUTOPSY"),
  88. tB("P1","SABOTAGE THE LOCKDOWN","SABOTAGE THE LOCKDOWN"),
  89. tB("P1","EXAM: KILL THE SNITCH","EXAM: KILL THE SNITCH"),
  90. tB("P2","GRIND THE BAD APPLES","GRIND THE BAD APPLES"),
  91. tB("P2","PUNISH THE MISCREANTS","PUNISH THE MISCREANTS"),
  92. tB("P2","OPEN THE GATES","OPEN THE GATES"),
  93. tB("P2","EXAM: GRIND THE BAD APPLES","EXAM: GRIND THE BAD APPLES"),
  94. tB("P3","CLEANSE THE ORPHANS","CLEANSE THE ORPHANS"),
  95. tB("P3","FEED THE CHILDREN","FEED THE CHILDREN"),
  96. tB("P3","FOSTER THE ORPHANS","FOSTER THE ORPHANS"),
  97. tB("P3","EXAM: CLEANSE THE ORPHANS","EXAM: CLEANSE THE ORPHANS"),
  98. };
  99. foreach (var s in sB) settings.Add(s.Item2, true, s.Item3, s.Item1);
  100. if (timer.CurrentTimingMethod == TimingMethod.RealTime) // stolen from dude simulator 3, basically asks the runner to set their livesplit to game time
  101. {
  102. var timingMessage = MessageBox.Show (
  103. "This game uses Time without Loads (Game Time) as the main timing method.\n"+
  104. "LiveSplit is currently set to show Real Time (RTA).\n"+
  105. "Would you like to set the timing method to Game Time?\n\n"+
  106. "Outlast Trials does not contain an anti-cheat, this ASL will not get you banned in any regard. If one is enabled the ASL will not intialize.",
  107. "LiveSplit | Outlast Trials",
  108. MessageBoxButtons.YesNo,MessageBoxIcon.Question
  109. );
  110. if (timingMessage == DialogResult.Yes)
  111. {
  112. timer.CurrentTimingMethod = TimingMethod.GameTime;
  113. }
  114. }
  115. }
  116. update
  117. {
  118. vars.watchers.UpdateAll(game);
  119. current.CurMap = vars.watchers["CurMap"].Current;
  120. current.CurLevelName = vars.watchers["CurLevelName"].Current;
  121. current.IGT = vars.watchers["IGT"].Current;
  122. current.Loading = vars.watchers["Loading"].Current;
  123. switch((string)vars.watchers["CurLevelName"].Current)
  124. {
  125. case "KILL THE SNITCH" : vars.watchertouseforIGT = "PoliceStationLoad";
  126. break;
  127. case "CANCEL THE AUTOPSY" : vars.watchertouseforIGT = "PoliceStationBasementLoad";
  128. break;
  129. case "SABOTAGE THE LOCKDOWN" : vars.watchertouseforIGT = "SabatogeLoad";
  130. break;
  131. case "EXAM: KILL THE SNITCH" : vars.watchertouseforIGT = "PoliceStationLoad";
  132. break;
  133. case "GRIND THE BAD APPLES" : vars.watchertouseforIGT = "FunParkLoad";
  134. break;
  135. case "PUNISH THE MISCREANTS" : vars.watchertouseforIGT = "FunParkBarnLoad";
  136. break;
  137. case "OPEN THE GATES" : vars.watchertouseforIGT = "FunParkBarnLoad";
  138. break;
  139. case "EXAM: GRIND THE BAD APPLES" : vars.watchertouseforIGT = "FunParkLoad";
  140. break;
  141. case "CLEANSE THE ORPHANS" : vars.watchertouseforIGT = "OrphanageLoad";
  142. break;
  143. case "FEED THE CHILDREN" : vars.watchertouseforIGT = "OrphanageServicesLoad";
  144. break;
  145. case "FOSTER THE ORPHANS" : vars.watchertouseforIGT = "OrphanageServicesLoad";
  146. break;
  147. case "EXAM: CLEANSE THE ORPHANS" : vars.watchertouseforIGT = "OrphanageLoad";
  148. break;
  149. default: vars.watchertouseforIGT = "OrphanageLoad";
  150. break;
  151. }
  152. current.FinalTime = vars.IGTWatchers[vars.watchertouseforIGT.ToString()].Current;
  153. print (vars.watchertouseforIGT.ToString());
  154. if (current.FinalTime == null)
  155. {
  156. current.FinalTime = "1";
  157. }
  158. if (current.CurMap == null)
  159. {
  160. current.CurMap = "1";
  161. }
  162. if (current.CurMap.Contains("Lobby") || current.CurMap.Contains("MainMenu"))
  163. {
  164. current.CurLevelName = "";
  165. }
  166. if (vars.Trials.Contains((string)vars.watchers["CurLevelName"].Current) || !current.CurMap.Contains("Lobby") && !current.CurMap.Contains("MainMenu") && !(current.CurLevelName == "Content/Text/Ingame_General.uasset"))
  167. {
  168. vars.runTimer = 1;
  169. }
  170. if (current.Loading != 0 || current.CurMap.Contains("Lobby") || current.CurMap.Contains("MainMenu") || (current.CurLevelName == "Content/Text/Ingame_General.uasset") || current.FinalTime.Contains(":"))
  171. {
  172. vars.runTimer = 0;
  173. current.Time = 0;
  174. }
  175. if (current.FinalTime.Contains(":") && (vars.LeveltimeUpdater == 0) && (!vars.MapTimeResetter.Contains(current.CurLevelName)) && (current.CurLevelName != "Content/Text/Ingame_General.uasset") && (current.CurLevelName != " ") && (current.CurLevelName != null) && (current.CurLevelName.Length > 4))
  176. {
  177. vars.MapTimeResetter.Add(current.CurLevelName);
  178. current.Minutes = int.Parse(current.FinalTime.Split(':')[0]);
  179. current.Seconds = int.Parse(current.FinalTime.Split(':')[1]);
  180. vars.LeveltimeUpdater = 1;
  181. current.Time = 0;
  182. vars.runTimer = 0;
  183. }
  184. if (vars.runTimer == 1)
  185. {
  186. current.Time = (vars.watchers["IGT"].Current - vars.watchers["IGT"].Old);
  187. vars.IGTWatchers.UpdateAll(game);
  188. }
  189. else
  190. {
  191. current.Time = 0;
  192. }
  193. }
  194. start
  195. {
  196. return (settings[current.CurLevelName] && !vars.doneMaps.Contains(current.CurLevelName) && current.Loading == 0 && (!current.FinalTime.Contains(":")));
  197. }
  198. onStart
  199. {
  200. vars.doneMaps.Add(current.CurLevelName);
  201. }
  202. split
  203. {
  204. if ((settings[current.CurLevelName]) && (!vars.doneMaps.Contains(current.CurLevelName)) && (current.CurLevelName != "Content/Text/Ingame_General.uasset"))
  205. {
  206. vars.doneMaps.Add(current.CurLevelName);
  207. return true;
  208. }
  209. //The trial doesn't end until the end screen appears, so the LRT will remove the time from when the FinalTime appears till the next level select screen
  210. if ((settings["FSPL"]) && (current.FinalTime.Contains(":")) && (!vars.FinalSplitPerLevel.Contains(current.CurLevelName)) && (current.Loading != 0))
  211. {
  212. vars.FinalSplitPerLevel.Add(current.CurLevelName);
  213. return true;
  214. }
  215. }
  216. isLoading
  217. {
  218. return true;
  219. }
  220. gameTime
  221. {
  222. if ((vars.LeveltimeUpdater == 1))
  223. {
  224. vars.totalGameTime = vars.totalGameTime + (current.Minutes * 60) + (current.Seconds);
  225. vars.Spacer = 0;
  226. current.Time = 0;
  227. current.Seconds = 0;
  228. current.Minutes = 0;
  229. vars.IGTWatchers[vars.watchertouseforIGT.ToString()].Current = "0";
  230. vars.MapTimeResetter.Add(current.CurLevelName);
  231. vars.LeveltimeUpdater = 0;
  232. return TimeSpan.FromSeconds(vars.totalGameTime);
  233. }
  234. else
  235. {
  236. current.Seconds = 0;
  237. current.Minutes = 0;
  238. vars.Spacer = vars.Spacer + (current.Time);
  239. return TimeSpan.FromSeconds(vars.totalGameTime + vars.Spacer);
  240. }
  241. }
  242. onReset
  243. {
  244. vars.Spacer = 0;
  245. vars.doneMaps.Clear();
  246. vars.FinalSplitPerLevel.Clear();
  247. vars.totalGameTime = 0;
  248. vars.MapTimeResetter.Clear();
  249. current.Seconds = 0;
  250. current.Minutes = 0;
  251. vars.watchers.ResetAll();
  252. current.FinalTime = 0;
  253. vars.IGTWatchers[vars.watchertouseforIGT.ToString()].Current = "1";
  254. }