-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of https://github.com/jakzo/SlzSpeedrunTools
- Loading branch information
Showing
3 changed files
with
161 additions
and
0 deletions.
There are no files selected for viewing
50 changes: 50 additions & 0 deletions
50
projects/Boneworks/SpeedrunTools/src/Features/ArenaSplitState.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
using MelonLoader; | ||
using HarmonyLib; | ||
using UnityEngine; | ||
using StressLevelZero.Arena; | ||
|
||
namespace Sst.Features { | ||
class ArenaSplitState : Feature { | ||
// State that the ASL finds using signature scanning | ||
private static byte[] State = { | ||
// 0 = magic string start | ||
// Signature is set dynamically to avoid finding this hardcoded array | ||
0x00, // 0xD5 | ||
0xE2, | ||
0x03, | ||
0x34, | ||
0xC2, | ||
0xDF, | ||
0x63, | ||
// 7 = arena index | ||
0x00, | ||
}; | ||
|
||
private static bool _isCurrentChallengeCompleted = true; | ||
|
||
public ArenaSplitState() { State[0] = 0xD5; } | ||
|
||
[HarmonyPatch(typeof(Arena_GameManager), | ||
nameof(Arena_GameManager.RingTheBell))] | ||
class ArenaGameManager_RingTheBell_Patch { | ||
[HarmonyPostfix()] | ||
internal static void Postfix(Arena_GameManager __instance) { | ||
_isCurrentChallengeCompleted = | ||
__instance.activeChallenge?.profile?.completedHard ?? true; | ||
} | ||
} | ||
|
||
[HarmonyPatch(typeof(Arena_GameManager), | ||
nameof(Arena_GameManager.SaveChallengeCompletion))] | ||
class ArenaGameManager_SaveChallengeCompletion_Patch { | ||
[HarmonyPostfix()] | ||
internal static void Postfix(Arena_GameManager __instance) { | ||
if (!_isCurrentChallengeCompleted && | ||
__instance.activeChallenge.profile.completedHard) { | ||
_isCurrentChallengeCompleted = true; | ||
State[7]++; | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
state("BONEWORKS") { | ||
int levelNumber : "GameAssembly.dll", 0x01E7E4E0, 0xB8, 0x590; | ||
} | ||
|
||
init { | ||
var module = modules.First(x => x.ModuleName == "vrclient_x64.dll"); | ||
|
||
var scanner = | ||
new SignatureScanner(game, module.BaseAddress, module.ModuleMemorySize); | ||
|
||
vars.loadingPointer = scanner.Scan(new SigScanTarget( | ||
3, | ||
"4889??????????488B??????????48FF????488B??????????488B??4889??????????488D??????????488B??????????48FF??????????????????????????????488B??????????418B") { | ||
OnFound = (process, scanners, addr) => | ||
addr + 0x4 + process.ReadValue<int>(addr) | ||
}); | ||
if (vars.loadingPointer == IntPtr.Zero) { | ||
throw new Exception("Game engine not initialized - retrying"); | ||
} | ||
|
||
vars.isLoading = | ||
new MemoryWatcher<bool>(new DeepPointer(vars.loadingPointer, 0xC54)); | ||
|
||
var arenaTarget = new SigScanTarget(7, "D5 E2 03 34 C2 DF 63 ??"); | ||
IntPtr ptr = IntPtr.Zero; | ||
foreach (var page in game.MemoryPages(true)) { | ||
var pageScanner = | ||
new SignatureScanner(game, page.BaseAddress, (int)page.RegionSize); | ||
ptr = pageScanner.Scan(arenaTarget); | ||
if (ptr != IntPtr.Zero) | ||
break; | ||
} | ||
if (ptr == IntPtr.Zero) { | ||
throw new Exception("Arena state not found - retrying"); | ||
} | ||
vars.arenaWatcher = new MemoryWatcher<byte>(ptr); | ||
|
||
// Index in levelOrder to start the run at (for practice) | ||
vars.startingSplit = 0; | ||
|
||
// Will split when entering each level in this list in this order | ||
// If entering a level later in the list it will split until it reaches it | ||
vars.levelOrder = new int[] { | ||
2, // scene_theatrigon_movie01 -> scene_breakroom | ||
4, // scene_museum | ||
5, // scene_streets | ||
5, // scene_streets | ||
6, // scene_runoff | ||
6, // scene_runoff | ||
7, // scene_sewerStation | ||
8, // scene_warehouse | ||
9, // scene_subwayStation | ||
10, // scene_tower | ||
11, // scene_towerBoss | ||
12, // scene_theatrigon_movie02 -> scene_dungeon | ||
14, // scene_arena | ||
15, // scene_throneRoom | ||
1, // scene_mainMenu | ||
18, // scene_redactedChamber | ||
19, // sandbox_handgunBox | ||
22, // scene_hoverJunkers | ||
16, // arena_fantasy | ||
23, // zombie_warehouse | ||
// 23, // zombie_warehouse | ||
// 23, // zombie_warehouse | ||
1, // scene_mainMenu | ||
}; | ||
vars.levelOrderIdx = 0; | ||
vars.targetLevelOrderIdx = vars.startingSplit; | ||
} | ||
|
||
update { | ||
vars.isLoading.Update(game); | ||
vars.arenaWatcher.Update(game); | ||
} | ||
|
||
isLoading { return vars.isLoading.Current; } | ||
|
||
start { | ||
if (vars.isLoading.Current && | ||
current.levelNumber == vars.levelOrder[vars.startingSplit]) { | ||
vars.levelOrderIdx = 0; | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
split { | ||
if (vars.arenaWatcher.Current != vars.arenaWatcher.Old) { | ||
return true; | ||
} | ||
|
||
if (vars.isLoading.Current && | ||
(!vars.isLoading.Old || current.levelNumber != old.levelNumber)) { | ||
var nextLevelOrderIdx = vars.levelOrderIdx + 1; | ||
if (nextLevelOrderIdx < vars.levelOrder.Length && | ||
vars.levelOrder[nextLevelOrderIdx] == current.levelNumber) { | ||
vars.targetLevelOrderIdx = nextLevelOrderIdx; | ||
} | ||
} | ||
|
||
if (vars.levelOrderIdx < vars.targetLevelOrderIdx) { | ||
vars.levelOrderIdx++; | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
exit { timer.IsGameTimePaused = true; } |