Stealth mode cheat improvement. Add god mode cheat

This commit is contained in:
2026-01-19 18:52:33 +01:00
parent 143a04ff4e
commit 8b91fc31d6

View File

@@ -1,5 +1,7 @@
#include "CommonHeaders.h"
#include "UEngine.hpp";
#include "UETools.hpp"
#include "UEngine.hpp"
#include "UEVars.hpp"
#include "SDK/Basic.hpp"
#include "SDK/Engine_classes.hpp"
#include "SDK/WBP_Cutscene_classes.hpp"
@@ -16,6 +18,7 @@ static AExponentialHeightFog* previousFogActor = nullptr;
// Constants
const std::string PLUGIN_NAME = "SilentHillf";
const std::string PLUGIN_LOG = PLUGIN_NAME + ".log";
constexpr ULONGLONG DEFAULT_ACTORS_SCAN_BETWEEN_TICKS = 300; // Used for enemies time dilation
// Logger
std::shared_ptr<spdlog::logger> logger;
@@ -37,6 +40,7 @@ static bool g_Cutscenes_FPS_fix_enabled = false;
static bool g_SkipIntro_fix_enabled = false;
static bool g_TimeDilation_fix_enabled = false;
static bool g_StealthMode_fix_enabled = false;
static bool g_GodMode_fix_enabled = false;
static int g_AdditionalFOVValue = 0;
static float g_CameraDistance = 1.f;
static float g_FogDensity = 0.4f;
@@ -55,11 +59,6 @@ static float g_DefaultFogDensity = -1.f;
static float g_DefaultFogMaxOpacity = -1.f;
static bool g_Console_Enabled = false;
// AOB Unreal Engine offsets addresses
static uint8_t* GObjectsaddress = nullptr;
static uint8_t* AppendStringaddress = nullptr;
static uint8_t* ProcessEventaddress = nullptr;
// AOB Scan pointers
static uint8_t* FOVaddress = nullptr;
static uint8_t* DOFaddress = nullptr;
@@ -138,7 +137,7 @@ extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled, bool init) {
Make(&FogOpacityEngineaddress, FogOpacityStringObfuscated, "Fog opacity"),
Make(&VolumetricFogaddress, VolumetricFogStringObfuscated, "Volumetric fog"),
Make(&CutscenesFPSaddress, CutscenesFPSStringObfuscated, "Cutscenes framerate"),
Make(&WorldTimedilationaddress, WorldTimeDilationStringObfuscated, "World time dilation"),
Make(&WorldTimedilationaddress, WorldTimeDilationStringObfuscated, "World time dilation")
};
// Scan all signature in a batch
Memory::AOBScanBatch(signatures, logger);
@@ -152,12 +151,14 @@ extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled, bool init) {
if (!GObjectsaddress || !AppendStringaddress || !ProcessEventaddress) {
logger->info("------------ UEngine offsets search ------------");
constexpr auto GObjetcsStringObfuscated = make_obfuscated<0x4A>("48 8B ?? ?? ?? ?? ?? 48 8B ?? ?? 48 8D ?? ?? EB ?? 33");
constexpr auto AppendStringStringObfuscated = make_obfuscated<0x4A>("48 89 ?? ?? ?? 48 89 ?? ?? ?? 57 48 83 ?? ?? 80 3D ?? ?? ?? ?? ?? 48 8B F2 8B ?? 48");
constexpr auto ProcessEventStringObfuscated = make_obfuscated<0x4A>("40 ?? 56 57 41 ?? 41 ?? 41 ?? 41 ?? 48 81 ?? ?? ?? ?? ?? 48 8D ?? ?? ?? 48 89 ?? ?? ?? ?? ?? 48 8B ?? ?? ?? ?? ?? 48 33 ?? 48 89 ?? ?? ?? ?? ?? 4D ?? ?? 48");
constexpr auto GObjetcsStringObfuscated = make_obfuscated<0x48>("48 8B ?? ?? ?? ?? ?? 48 8B ?? ?? 48 8D ?? ?? EB ?? 33");
constexpr auto GWorldStringObfuscated = make_obfuscated<0x5B>("48 8B 05 ?? ?? ?? ?? 48 ?? ?? 75 ?? 48 ?? ?? ?? 5B C3");
constexpr auto AppendStringStringObfuscated = make_obfuscated<0x89>("48 89 ?? ?? ?? 48 89 ?? ?? ?? 57 48 83 ?? ?? 80 3D ?? ?? ?? ?? ?? 48 8B F2 8B ?? 48");
constexpr auto ProcessEventStringObfuscated = make_obfuscated<0x81>("40 ?? 56 57 41 ?? 41 ?? 41 ?? 41 ?? 48 81 ?? ?? ?? ?? ?? 48 8D ?? ?? ?? 48 89 ?? ?? ?? ?? ?? 48 8B ?? ?? ?? ?? ?? 48 33 ?? 48 89 ?? ?? ?? ?? ?? 4D ?? ?? 48");
// Prepare all data for scanning
std::vector<OffsetScanEntry> UEoffsetsScans = {
Make(&GObjectsaddress, GObjetcsStringObfuscated, "GObjects", OffsetCalcType::GetOffsetFromOpcode, &Offsets::GObjects, 0x3),
Make(&GWorldaddress, GWorldStringObfuscated, "GWorld", OffsetCalcType::GetOffsetFromOpcode, &Offsets::GWorld, 0x3),
Make(&AppendStringaddress, AppendStringStringObfuscated, "AppendString", OffsetCalcType::UE_CalculateOffset, &Offsets::AppendString),
Make(&ProcessEventaddress, ProcessEventStringObfuscated, "ProcessEvent", OffsetCalcType::UE_CalculateOffset, &Offsets::ProcessEvent)
};
@@ -177,6 +178,7 @@ extern "C" __declspec(dllexport) void SetFixEnabled(bool enabled, bool init) {
if (!init && WorldTimedilationaddress) {
EnableCheats(Cheat::TimeDilation);
EnableCheats(Cheat::Stealth);
EnableCheats(Cheat::GodMode);
}
if (!init && Fogaddress && FogDensityEngineaddress && FogOpacityEngineaddress) FogFixEnabled();
if (!init && VolumetricFogaddress) VolumetricFogFixEnabled();
@@ -200,6 +202,7 @@ extern "C" __declspec(dllexport) void SetFixesEnabled(GameFixes fix, bool enable
if (fix == GameFixes::Fog) { g_Fog_fix_enabled = enabled; FogFixEnabled(); }
if (fix == GameFixes::TimeDilation) { g_TimeDilation_fix_enabled = enabled; EnableCheats(Cheat::TimeDilation); }
if (fix == GameFixes::Stealth) { g_StealthMode_fix_enabled = enabled; EnableCheats(Cheat::Stealth); }
if (fix == GameFixes::GodMode) { g_GodMode_fix_enabled = enabled; EnableCheats(Cheat::GodMode); }
}
// Setters for Reshade addon call
@@ -356,7 +359,6 @@ static void VolumetricFogFixEnabled() {
}
}
static APawn* g_PlayerPawn = nullptr;
static void ProcessEvent() {
if (!PEHook && ProcessEventaddress) {
PEHook = safetyhook::create_mid(ProcessEventaddress + 0xc,
@@ -378,8 +380,9 @@ static void ProcessEvent() {
}
bool bEnable = g_fix_enabled && g_Cutscenes_fix_enabled;
if (object->GetName().contains("WBP_Cutscene_C") && (funcName == "Destruct" || funcName == "Construct")) bLastState = false;
if (object->GetName().contains("WBP_Cutscene_C")) { // Black bars widgets removal
bLastState = !(funcName == "Destruct" || funcName == "Construct");
auto cutscene = static_cast<UWBP_Cutscene_C*>(object);
if (bEnable != bLastState) {
float opacity = bEnable ? 0.f : 1.f;
@@ -429,24 +432,57 @@ static void CutscenesFPSFixEnabled() {
logger->info("Cutscenes FPS unlocker fix disabled");
}
constexpr int ScanEveryNFrames = 120;
static std::unordered_map<ANoceEnemyCharacter*, float> CachedAlertness;
// Cheats
static std::unordered_map<ANoceEnemyCharacter*, float> CachedAlertness;
static float hinakoHealth = -1.f;
static float hinakoSanity = -1.f;
static float hinakoStamina = -1.f;
static UWorld* LastWorld = nullptr;
static ULONGLONG lastScanTick = 0; // Last time Actors were scanned for enemies time dilation
static void EnableCheats(Cheat cheat) {
if (WorldTimedilationaddress && !WorldTimeDilationHook) {
WorldTimeDilationHook = safetyhook::create_mid(WorldTimedilationaddress + 0x10,
[](SafetyHookContext& ctx) {
// From AWorldSettings retrieved from world->K2_GetWorldSettings()
ctx.xmm0.f32[0] *= g_TimeDilation_fix_enabled ? g_WorldTimeDilationValue : 1.f;
UWorld* world = UWorld::GetWorld();
if (world && world->OwningGameInstance->LocalPlayers[0]->PlayerController->AcknowledgedPawn)
g_PlayerPawn = world->OwningGameInstance->LocalPlayers[0]->PlayerController->AcknowledgedPawn;
if (!world || !world->OwningGameInstance) return;
if (world != LastWorld) {
CachedAlertness.clear();
hinakoHealth = -1.f;
hinakoSanity = -1.f;
hinakoStamina = -1.f;
LastWorld = world;
}
APawn* playerPawn = GetPawnFromWorld(world);
if (playerPawn && playerPawn->IsA(ANocePlayerCharacter::StaticClass()) && playerPawn->IsPlayerControlled()) {
auto* Hinako = static_cast<ANocePlayerCharacter*>(playerPawn);
if (Hinako && Hinako->IsInBattling() && Hinako->InRealBattleStatus()) {
if (g_GodMode_fix_enabled) {
if (hinakoHealth < 0.f) hinakoHealth = Hinako->GetHealth();
if (hinakoStamina < 0.f) hinakoStamina = Hinako->GetStamina();
if (hinakoSanity < 0.f) hinakoSanity = Hinako->GetSanity();
// Scan levels and actors every 120 frames
static uint64_t FrameCounter = 0;
if (++FrameCounter < ScanEveryNFrames) return;
FrameCounter = 0;
Hinako->SetHealth(hinakoHealth);
Hinako->SetSanity(hinakoSanity);
Hinako->SetStamina(hinakoStamina);
Hinako->ForceRunningNoCostStamina = true;
}
else {
hinakoHealth = -1.f;
hinakoSanity = -1.f;
hinakoStamina = -1.f;
Hinako->ForceRunningNoCostStamina = false;
}
}
}
ULONGLONG now = GetTickCount64();
if (now - lastScanTick < DEFAULT_ACTORS_SCAN_BETWEEN_TICKS) return;
lastScanTick = now;
if (world->Levels.Num() == 0) return;
// Enemies time dilation & stealth
for (int i = 0; i < world->Levels.Num(); i++) { // Loop through level to find actors
ULevel* Level = world->Levels[i];
@@ -454,25 +490,29 @@ static void EnableCheats(Cheat cheat) {
for (int j = 0; j < Level->Actors.Num(); j++) { // Loop through actors
AActor* actor = Level->Actors[j];
if (!actor || !g_PlayerPawn || actor == g_PlayerPawn) continue; // We don't want to affect Hinako
if (actor->IsA(ANoceEnemyCharacter::StaticClass())) { // actor is enemy
actor->CustomTimeDilation = g_TimeDilation_fix_enabled ? g_AITimeDilationValue : 1.f; // Enemy time dilation
// Set enemy alert
ANoceEnemyCharacter* enemy = static_cast<ANoceEnemyCharacter*>(actor);
if (enemy && enemy->EnemyAIController) {
if (g_StealthMode_fix_enabled) {
if (CachedAlertness.find(enemy) == CachedAlertness.end())
CachedAlertness[enemy] = enemy->EnemyAIController->GetAlertness();
if (!actor || !playerPawn || actor == playerPawn) continue; // We don't want to affect Hinako
if (!actor->IsA(ANoceEnemyCharacter::StaticClass())) continue; // actor is enemy
// Set enemy alert
ANoceEnemyCharacter* enemy = static_cast<ANoceEnemyCharacter*>(actor);
if (!enemy || !enemy->EnemyAIController) continue;
enemy->EnemyAIController->SetAlertness(0.f, true); // Set enemy alert to 0
}
else {
auto it = CachedAlertness.find(enemy);
if (it != CachedAlertness.end()) {
enemy->EnemyAIController->SetAlertness(it->second, true); // Restore original enemy alert
CachedAlertness.erase(it);
}
}
enemy->CustomTimeDilation = g_TimeDilation_fix_enabled ? g_AITimeDilationValue : 1.f; // Enemy time dilation
UAIPerceptionComponent* AIPerception = enemy->EnemyAIController->GetAIPerceptionComponent();
if (!AIPerception) continue;
if (g_StealthMode_fix_enabled) {
AIPerception->SetSenseEnabled(UAISense_Sight::StaticClass(), false);
if (enemy->EnemyAIController->GetAlertness() > 0.f && CachedAlertness.find(enemy) == CachedAlertness.end()) {
CachedAlertness[enemy] = enemy->EnemyAIController->GetAlertness();
}
enemy->EnemyAIController->SetAlertness(0.f, true); // Set enemy alert to 0
}
else {
AIPerception->SetSenseEnabled(UAISense_Sight::StaticClass(), true);
auto it = CachedAlertness.find(enemy);
if (it != CachedAlertness.end()) {
enemy->EnemyAIController->SetAlertness(it->second, true); // Restore original enemy alert
CachedAlertness.erase(it);
}
}
}
@@ -481,6 +521,7 @@ static void EnableCheats(Cheat cheat) {
}
if (cheat == Cheat::TimeDilation) logger->info("Time dilation cheat {}",g_TimeDilation_fix_enabled ? "enabled" : "disabled");
if (cheat == Cheat::Stealth) logger->info("Stealth mode cheat {}", g_StealthMode_fix_enabled ? "enabled" : "disabled");
if (cheat == Cheat::GodMode) logger->info("God mode cheat {}", g_GodMode_fix_enabled ? "enabled" : "disabled");
}
// Memory patch fixes
@@ -518,8 +559,7 @@ static void VignettingFixEnabled() {
}
// UE Console creation
static void EnableConsole()
{
static void EnableConsole() {
if (g_Console_Enabled || !g_Console || !GObjectsaddress || !AppendStringaddress || !ProcessEventaddress) {
if (!g_Console && !user_inputs_logged) {
logger->info("------------------ User inputs ------------------");
@@ -590,8 +630,7 @@ static void InitializeLogger() {
}
// Standard dll entry
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
{
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID) {
if (reason == DLL_PROCESS_ATTACH) {
InitializeLogger();
logger->info("Plugin {} loaded.", PLUGIN_NAME);