diff --git a/SilentHillF/dllmain.cpp b/SilentHillF/dllmain.cpp index defd6ee..0cf7b35 100644 --- a/SilentHillF/dllmain.cpp +++ b/SilentHillF/dllmain.cpp @@ -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 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 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(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 CachedAlertness; // Cheats +static std::unordered_map 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(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(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(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);