diff --git a/src/core/commands/ListCommand.cpp b/src/core/commands/ListCommand.cpp index d65c5e5..cdc680b 100644 --- a/src/core/commands/ListCommand.cpp +++ b/src/core/commands/ListCommand.cpp @@ -38,6 +38,12 @@ namespace YimMenu MarkDirty(); } + void ListCommand::SetList(std::vector> list) + { + m_List = std::move(list); + MarkDirty(); + } + std::vector>& ListCommand::GetList() { return m_List; diff --git a/src/core/commands/ListCommand.hpp b/src/core/commands/ListCommand.hpp index 8dca5f7..4738c84 100644 --- a/src/core/commands/ListCommand.hpp +++ b/src/core/commands/ListCommand.hpp @@ -18,6 +18,7 @@ namespace YimMenu ListCommand(std::string name, std::string label, std::string description, std::vector> list, int def_val = 0); int GetState(); void SetState(int state); + void SetList(std::vector> list); std::vector>& GetList(); }; } \ No newline at end of file diff --git a/src/core/hooking/CallHook.cpp b/src/core/hooking/CallHook.cpp new file mode 100644 index 0000000..5eb1539 --- /dev/null +++ b/src/core/hooking/CallHook.cpp @@ -0,0 +1,78 @@ +#include "CallHook.hpp" + +namespace YimMenu +{ + CallHookMemory::CallHookMemory() + { + m_Memory = VirtualAlloc((void*)((uintptr_t)GetModuleHandle(0) + 0x20000000), 1024, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + m_Offset = 0; + } + + void* CallHookMemory::AllocateJumpSequenceImpl(void* func) + { + m_Offset = m_Offset + ((16 - (m_Offset % 16)) % 16); + + *m_Memory.Add(m_Offset).As() = 0xB848; + *m_Memory.Add(m_Offset).Add(2).As() = func; + *m_Memory.Add(m_Offset).Add(10).As() = 0xE0FF; + + m_Offset += 12; + + return m_Memory.Add(m_Offset).Sub(12).As(); + } + + void CallHookMemory::DestroyImpl() + { + VirtualFree(m_Memory.As(), 0, MEM_RELEASE); + } + + CallSiteHook::Hook::Hook(void* location, void* hook) : + m_Location(location), + m_Hook(hook), + m_Enabled(false) + { + auto seq = CallHookMemory::AllocateJumpSequence(hook); + m_PatchedBytes[0] = 0xE8; + *(int32_t*)&m_PatchedBytes[1] = (int32_t)((uint64_t)seq - (uint64_t)location - 5); + memcpy(m_OriginalBytes, location, 5); + m_OriginalFunction = PointerCalculator(location).Add(1).Rip().As(); + } + + void CallSiteHook::Hook::Enable() + { + if (!m_Enabled) + { + memcpy(m_Location, m_PatchedBytes, 5); + m_Enabled = true; + } + } + + void CallSiteHook::Hook::Disable() + { + if (m_Enabled) + { + memcpy(m_Location, m_OriginalBytes, 5); + m_Enabled = false; + } + } + + std::shared_ptr CallSiteHook::AddHookImpl(void* location, void* hook) + { + auto hookPtr = std::make_shared(location, hook); + + m_Hooks.push_back(hookPtr); + return hookPtr; + } + + void CallSiteHook::DestroyImpl() + { + for (auto& hook : m_Hooks) + { + if (hook) + hook->Disable(); + } + + m_Hooks.clear(); + CallHookMemory::Destroy(); + } +} \ No newline at end of file diff --git a/src/core/hooking/CallHook.hpp b/src/core/hooking/CallHook.hpp new file mode 100644 index 0000000..e7c6a32 --- /dev/null +++ b/src/core/hooking/CallHook.hpp @@ -0,0 +1,88 @@ +#pragma once +#include "core/memory/PointerCalculator.hpp" + +namespace YimMenu +{ + class CallHookMemory + { + static CallHookMemory& GetInstance() + { + static CallHookMemory instance; + return instance; + } + + void* AllocateJumpSequenceImpl(void* func); + void DestroyImpl(); + + PointerCalculator m_Memory; + int m_Offset; + + public: + CallHookMemory(); + + static void* AllocateJumpSequence(void* func) + { + return GetInstance().AllocateJumpSequenceImpl(func); + } + + static void Destroy() + { + GetInstance().DestroyImpl(); + } + }; + + class CallSiteHook + { + public: + class Hook + { + Hook(Hook&& that) = delete; + Hook& operator=(Hook&& that) = delete; + Hook(Hook const&) = delete; + Hook& operator=(Hook const&) = delete; + + void* m_Location; + void* m_Hook; + uint8_t m_PatchedBytes[5]; + uint8_t m_OriginalBytes[5]; + void* m_OriginalFunction; + bool m_Enabled; + + public: + Hook(void* location, void* hook); + + void Enable(); + void Disable(); + + template + T GetOriginal() + { + return static_cast(m_OriginalFunction); + } + }; + + static std::shared_ptr AddHook(void* location, void* hook) + { + return GetInstance().AddHookImpl(location, hook); + } + + static void Destroy() + { + GetInstance().DestroyImpl(); + } + + private: + static CallSiteHook& GetInstance() + { + static CallSiteHook Instance; + return Instance; + } + + std::shared_ptr AddHookImpl(void* location, void* hook); + void DestroyImpl(); + + static inline std::vector> m_Hooks; + }; + + using CallHook = std::shared_ptr; +} \ No newline at end of file diff --git a/src/game/backend/ScriptPatches.cpp b/src/game/backend/ScriptPatches.cpp index 2bfc7ac..e58e63f 100644 --- a/src/game/backend/ScriptPatches.cpp +++ b/src/game/backend/ScriptPatches.cpp @@ -1,67 +1,13 @@ #include "ScriptPatches.hpp" +#include "game/backend/ScriptPointers.hpp" #include "game/pointers/Pointers.hpp" #include "types/script/scrProgram.hpp" namespace YimMenu { - static uint8_t* GetCodeLocation(ScriptPatches::Data* data, int idx) - { - return &data->GetData()[idx >> 14][idx & 0x3FFF]; - } - - // TODO: refactor this and cache results - static std::optional GetCodeLocationByPattern(ScriptPatches::Data* data, const SimplePattern& pattern) - { - uint32_t code_size = data->GetSize(); - for (uint32_t i = 0; i < (code_size - pattern.m_Bytes.size()); i++) - { - for (uint32_t j = 0; j < pattern.m_Bytes.size(); j++) - { - if (pattern.m_Bytes[j].has_value()) - { - int loc = i + j; - if (pattern.m_Bytes[j] != *GetCodeLocation(data, loc)) - goto incorrect; - } - } - - return i; - incorrect: - continue; - } - - return std::nullopt; - } - - ScriptPatches::Data::Data(rage::scrProgram* program) - { - auto pages = new uint8_t*[program->GetNumCodePages()]; - - for (auto i = 0u; i < program->GetNumCodePages(); i++) - { - pages[i] = new uint8_t[program->GetCodePageSize(i)]; - std::memcpy(pages[i], program->GetCodePage(i), program->GetCodePageSize(i)); - } - - m_Data = pages; - m_Pages = program->GetNumCodePages(); - m_Size = program->GetFullCodeSize(); - } - - ScriptPatches::Data::~Data() - { - for (auto i = 0u; i < m_Pages; i++) - { - delete[] m_Data[i]; - } - - delete[] m_Data; - m_Data = nullptr; - } - - std::optional ScriptPatches::Patch::GetPC() + std::optional ScriptPatches::Patch::GetPC() { if (m_Pc.has_value()) return m_Pc; @@ -71,15 +17,14 @@ namespace YimMenu if (!data) return std::nullopt; - auto location = GetCodeLocationByPattern(data, m_Pattern); - - if (!location.has_value()) + if (auto address = ScriptPointers::GetPointer(Joaat(m_Pointer.GetName()))) { - LOG(FATAL) << "Failed to find pattern for script " << HEX(m_Hash); + m_Pc = address; } else { - m_Pc = location.value() + m_Offset; + m_Pc = m_Pointer.Scan(data).As(); + ScriptPointers::CachePointer(Joaat(m_Pointer.GetName()), m_Pc.value()); } return m_Pc; @@ -96,10 +41,10 @@ namespace YimMenu if (m_OriginalBytes.size() == 0) { m_OriginalBytes.resize(m_PatchedBytes.size()); - memcpy(m_OriginalBytes.data(), GetCodeLocation(data, pc.value()), m_PatchedBytes.size()); + memcpy(m_OriginalBytes.data(), ScriptData::GetCodeLocation(data, pc.value()), m_PatchedBytes.size()); } - memcpy(GetCodeLocation(data, pc.value()), m_PatchedBytes.data(), m_PatchedBytes.size()); + memcpy(ScriptData::GetCodeLocation(data, pc.value()), m_PatchedBytes.data(), m_PatchedBytes.size()); } void ScriptPatches::Patch::Restore() @@ -113,13 +58,12 @@ namespace YimMenu if (!pc || !data) return; - memcpy(GetCodeLocation(data, pc.value()), m_OriginalBytes.data(), m_OriginalBytes.size()); + memcpy(ScriptData::GetCodeLocation(data, pc.value()), m_OriginalBytes.data(), m_OriginalBytes.size()); } - ScriptPatches::Patch::Patch(joaat_t script, SimplePattern pattern, int32_t offset, std::vector patch) : - m_Pattern(pattern), - m_Offset(offset), + ScriptPatches::Patch::Patch(joaat_t script, ScriptPointer pointer, std::vector patch) : m_PatchedBytes(patch), + m_Pointer(pointer), m_Enabled(false), m_Pc(std::nullopt), m_OriginalBytes({}), @@ -162,9 +106,9 @@ namespace YimMenu return m_Hash == hash; } - std::shared_ptr ScriptPatches::AddPatchImpl(joaat_t script, const std::string& pattern, int32_t offset, std::vector patch) + std::shared_ptr ScriptPatches::AddPatchImpl(joaat_t script, ScriptPointer pointer, std::vector patch) { - auto scr_patch = std::make_shared(script, SimplePattern(pattern), offset, patch); + auto scr_patch = std::make_shared(script, pointer, patch); // add patch to map m_Patches.push_back(scr_patch); @@ -202,7 +146,7 @@ namespace YimMenu if (!in_scope) return; // avoid polluting memory with unused program bytecode - m_Datas.emplace(program->m_NameHash, std::move(std::make_unique(program))); + m_Datas.emplace(program->m_NameHash, std::move(std::make_unique(program))); // refresh patches so they can enable themselves for (auto& patch : m_Patches) @@ -248,7 +192,7 @@ namespace YimMenu } } - ScriptPatches::Data* ScriptPatches::GetDataImpl(joaat_t script) + ScriptData* ScriptPatches::GetDataImpl(joaat_t script) { if (auto it = m_Datas.find(script); it != m_Datas.end()) return it->second.get(); diff --git a/src/game/backend/ScriptPatches.hpp b/src/game/backend/ScriptPatches.hpp index 9a79245..3f1b73d 100644 --- a/src/game/backend/ScriptPatches.hpp +++ b/src/game/backend/ScriptPatches.hpp @@ -1,6 +1,7 @@ #pragma once -#include "core/memory/Pattern.hpp" #include "core/util/Joaat.hpp" +#include "game/gta/ScriptData.hpp" +#include "game/gta/ScriptPointer.hpp" namespace rage { @@ -12,52 +13,29 @@ namespace YimMenu class ScriptPatches { public: - class Data - { - std::uint8_t** m_Data; - std::uint32_t m_Pages; - std::uint32_t m_Size; - - public: - inline std::uint8_t** GetData() const - { - return m_Data; - } - - inline std::uint32_t GetSize() const - { - return m_Size; - } - - Data(rage::scrProgram* program); - ~Data(); - }; - class Patch { std::vector m_OriginalBytes; std::vector m_PatchedBytes; - std::optional m_Pc; - SimplePattern m_Pattern; - int32_t m_Offset; + std::optional m_Pc; + ScriptPointer m_Pointer; bool m_Enabled; std::uint32_t m_Hash; - - std::optional GetPC(); + std::optional GetPC(); void Apply(); void Restore(); public: - Patch(joaat_t script, SimplePattern pattern, int32_t offset, std::vector patch); + Patch(joaat_t script, ScriptPointer pointer, std::vector patch); void Enable(); void Disable(); void Update(); bool InScope(std::uint32_t hash); }; - static std::shared_ptr AddPatch(joaat_t script, const std::string& pattern, int32_t offset, std::vector patch) + static std::shared_ptr AddPatch(joaat_t script, ScriptPointer pointer, std::vector patch) { - return GetInstance().AddPatchImpl(script, pattern, offset, patch); + return GetInstance().AddPatchImpl(script, pointer, patch); } static void RegisterProgram(rage::scrProgram* program) @@ -90,15 +68,15 @@ namespace YimMenu return Instance; } - std::shared_ptr AddPatchImpl(joaat_t script, const std::string& pattern, int32_t offset, std::vector patch); + std::shared_ptr AddPatchImpl(joaat_t script, ScriptPointer pointer, std::vector patch); void RegisterProgramImpl(rage::scrProgram* program); void UnregisterProgramImpl(rage::scrProgram* program); void OnScriptVMEnterImpl(rage::scrProgram* program); void OnScriptVMLeaveImpl(rage::scrProgram* program); - Data* GetDataImpl(joaat_t script); + ScriptData* GetDataImpl(joaat_t script); std::vector> m_Patches; - std::unordered_map> m_Datas; + std::unordered_map> m_Datas; std::uint8_t** m_CurrentlyReplacedBytecode; }; diff --git a/src/game/backend/ScriptPointers.cpp b/src/game/backend/ScriptPointers.cpp new file mode 100644 index 0000000..19a7b42 --- /dev/null +++ b/src/game/backend/ScriptPointers.cpp @@ -0,0 +1,75 @@ +#include "ScriptPointers.hpp" +#include "core/filemgr/FileMgr.hpp" +#include "core/memory/ModuleMgr.hpp" + +namespace YimMenu +{ + ScriptPointers::ScriptPointers() : + m_CacheFile(FileMgr::GetProjectFile("./scr_pointers.bin")) + { + } + + void ScriptPointers::InitImpl() + { + m_CacheFile.Load(); + if (m_CacheFile.UpToDate(ModuleMgr.Get("GTA5_Enhanced.exe"_J)->GetNtHeader()->FileHeader.TimeDateStamp)) + { + Load(); + } + } + + void ScriptPointers::CachePointerImpl(joaat_t nameHash, std::uint32_t address) + { + m_ScriptPointers.emplace(nameHash, address); + Save(); + } + + std::uint32_t ScriptPointers::GetPointerImpl(joaat_t nameHash) + { + if (auto it = m_ScriptPointers.find(nameHash); it != m_ScriptPointers.end()) + { + return it->second; + } + + return 0; + } + + void ScriptPointers::Save() + { + auto dataSize = sizeof(std::uint32_t) + sizeof(scrPointerSaveStruct) * m_ScriptPointers.size(); + auto data = std::make_unique(dataSize); + auto dataPtr = data.get(); + + *(std::uint32_t*)dataPtr = m_ScriptPointers.size(); + dataPtr += sizeof(std::uint32_t); + + for (auto& [hash, val] : m_ScriptPointers) + { + auto saveStruct = (scrPointerSaveStruct*)dataPtr; + saveStruct->m_NameHash = hash; + saveStruct->m_Address = val; + dataPtr += sizeof(scrPointerSaveStruct); + } + + m_CacheFile.SetHeaderVersion(ModuleMgr.Get("GTA5_Enhanced.exe"_J)->GetNtHeader()->FileHeader.TimeDateStamp); + m_CacheFile.SetData(std::move(data), dataSize); + m_CacheFile.Write(); + } + + void ScriptPointers::Load() + { + auto data = m_CacheFile.Data(); + + auto numPointers = *(std::uint32_t*)data; + data += sizeof(std::uint32_t); + + for (int i = 0; i < numPointers; i++) + { + auto saveStruct = (scrPointerSaveStruct*)data; + m_ScriptPointers.emplace(saveStruct->m_NameHash, saveStruct->m_Address); + data += sizeof(scrPointerSaveStruct); + } + + LOG(INFO) << "Loaded " << m_ScriptPointers.size() << " script pointers from cache."; + } +} \ No newline at end of file diff --git a/src/game/backend/ScriptPointers.hpp b/src/game/backend/ScriptPointers.hpp new file mode 100644 index 0000000..5907a6a --- /dev/null +++ b/src/game/backend/ScriptPointers.hpp @@ -0,0 +1,50 @@ +#pragma once +#include "core/util/Joaat.hpp" +#include "core/filemgr/CacheFile.hpp" + +namespace YimMenu +{ +#pragma pack(push, 1) + struct scrPointerSaveStruct + { + joaat_t m_NameHash; + std::uint32_t m_Address; + }; +#pragma pack(pop) + + class ScriptPointers + { + static ScriptPointers& GetInstance() + { + static ScriptPointers instance; + return instance; + } + + void InitImpl(); + void CachePointerImpl(joaat_t nameHash, std::uint32_t address); + std::uint32_t GetPointerImpl(joaat_t nameHash); + void Save(); + void Load(); + + CacheFile m_CacheFile; + std::unordered_map m_ScriptPointers{}; + + public: + ScriptPointers(); + + static void Init() + { + GetInstance().InitImpl(); + } + + static void CachePointer(joaat_t nameHash, std::uint32_t address) + { + GetInstance().CachePointerImpl(nameHash, address); + } + + static std::uint32_t GetPointer(joaat_t nameHash) + { + return GetInstance().GetPointerImpl(nameHash); + } + }; +} \ No newline at end of file diff --git a/src/game/features/network/DisableDeathBarriers.cpp b/src/game/features/network/DisableDeathBarriers.cpp index 573ba54..19df9f4 100644 --- a/src/game/features/network/DisableDeathBarriers.cpp +++ b/src/game/features/network/DisableDeathBarriers.cpp @@ -13,7 +13,7 @@ namespace YimMenu::Features { if (!m_DeathBarriersPatch) { - m_DeathBarriersPatch = ScriptPatches::AddPatch("freemode"_J, "2D 01 09 00 00 5D ? ? ? 56 ? ? 3A", 5, {0x2E, 0x01, 0x00}); + m_DeathBarriersPatch = ScriptPatches::AddPatch("freemode"_J, ScriptPointer("DeathBarriersPatch", "2D 01 09 00 00 5D ? ? ? 56 ? ? 3A").Add(5), {0x2E, 0x01, 0x00}); } m_DeathBarriersPatch->Enable(); } diff --git a/src/game/features/network/ForceScriptHost.cpp b/src/game/features/network/ForceScriptHost.cpp new file mode 100644 index 0000000..889b358 --- /dev/null +++ b/src/game/features/network/ForceScriptHost.cpp @@ -0,0 +1,25 @@ +#include "core/commands/LoopedCommand.hpp" +#include "game/gta/Natives.hpp" +#include "game/gta/Scripts.hpp" +#include "game/pointers/Pointers.hpp" + +namespace YimMenu::Features +{ + class ForceScriptHost : public LoopedCommand + { + using LoopedCommand::LoopedCommand; + + virtual void OnTick() override + { + if (!*Pointers.IsSessionStarted || SCRIPT::GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH("maintransition"_J) > 0) + return; + + if (auto freemode = Scripts::FindScriptThread("freemode"_J)) + { + Scripts::ForceScriptHost(freemode); + } + } + }; + + static ForceScriptHost _ForceScriptHost{"forcescripthost", "Force Script Host", "Forces you to become freemode script host. Note that this might break some missions."}; +} \ No newline at end of file diff --git a/src/game/features/recovery/CayoPericoCooldown.cpp b/src/game/features/recovery/CayoPericoCooldown.cpp deleted file mode 100644 index 13ea767..0000000 --- a/src/game/features/recovery/CayoPericoCooldown.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "core/commands/Command.hpp" -#include "game/gta/Stats.hpp" - -namespace YimMenu::Features -{ - class CayoPericoCooldown : public Command - { - using Command::Command; - - virtual void OnCall() override - { - Stats::SetInt("MPX_H4_TARGET_POSIX", 1); - Stats::SetInt("MPX_H4_COOLDOWN", 1); - Stats::SetInt("MPX_H4_COOLDOWN_HARD", 1); - } - }; - - static CayoPericoCooldown _CayoPericoCooldown{"skipcayocooldown", "Cooldown skip for Cayo Perico Heist", "Skips the cooldown between each Cayo Perico heist"}; -} \ No newline at end of file diff --git a/src/game/features/recovery/CayoPericoSetup.cpp b/src/game/features/recovery/CayoPericoSetup.cpp deleted file mode 100644 index c615bd1..0000000 --- a/src/game/features/recovery/CayoPericoSetup.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "core/commands/Command.hpp" -#include "game/gta/Stats.hpp" - -namespace YimMenu::Features -{ - class CayoPericoSetup : public Command - { - using Command::Command; - - virtual void OnCall() override - { - Stats::SetInt("MPX_H4CNF_BS_GEN", 131071); - Stats::SetInt("MPX_H4CNF_BS_ENTR", 63); - Stats::SetInt("MPX_H4CNF_BS_ABIL", 63); - Stats::SetInt("MPX_H4CNF_WEAPONS", 2); - Stats::SetInt("MPX_H4CNF_WEP_DISRP", 3); - Stats::SetInt("MPX_H4CNF_ARM_DISRP", 3); - Stats::SetInt("MPX_H4CNF_HEL_DISRP", 3); - Stats::SetInt("MPX_H4CNF_TARGET", 5); - Stats::SetInt("MPX_H4CNF_TROJAN", 4); - Stats::SetInt("MPX_H4CNF_APPROACH", -1); - Stats::SetInt("MPX_H4LOOT_CASH_I", 0); - Stats::SetInt("MPX_H4LOOT_CASH_C", 0); - Stats::SetInt("MPX_H4LOOT_WEED_I", 0); - Stats::SetInt("MPX_H4LOOT_WEED_C", 0); - Stats::SetInt("MPX_H4LOOT_COKE_I", 0); - Stats::SetInt("MPX_H4LOOT_COKE_C", 0); - Stats::SetInt("MPX_H4LOOT_GOLD_I", -1); - Stats::SetInt("MPX_H4LOOT_GOLD_C", -1); - Stats::SetInt("MPX_H4LOOT_PAINT", -1); - Stats::SetInt("MPX_H4_PROGRESS", 131055); - Stats::SetInt("MPX_H4LOOT_CASH_I_SCOPED", 0); - Stats::SetInt("MPX_H4LOOT_CASH_C_SCOPED", 0); - Stats::SetInt("MPX_H4LOOT_WEED_I_SCOPED", 0); - Stats::SetInt("MPX_H4LOOT_WEED_C_SCOPED", 0); - Stats::SetInt("MPX_H4LOOT_COKE_I_SCOPED", 0); - Stats::SetInt("MPX_H4LOOT_COKE_C_SCOPED", 0); - Stats::SetInt("MPX_H4LOOT_GOLD_I_SCOPED", -1); - Stats::SetInt("MPX_H4LOOT_GOLD_C_SCOPED", -1); - Stats::SetInt("MPX_H4LOOT_PAINT_SCOPED", -1); - Stats::SetInt("MPX_H4_MISSIONS", 65535); - Stats::SetInt("MPX_H4_PLAYTHROUGH_STATUS", 40000); - } - }; - - static CayoPericoSetup _CayoPericoSetup{"skipcayosetup", "Setup Cayo Perico Heist", "Gives the best cayo perico loadout."}; -} \ No newline at end of file diff --git a/src/game/features/recovery/DLCVehicles.cpp b/src/game/features/recovery/DLCVehicles.cpp index 98706c9..12605f0 100644 --- a/src/game/features/recovery/DLCVehicles.cpp +++ b/src/game/features/recovery/DLCVehicles.cpp @@ -15,19 +15,19 @@ namespace YimMenu::Features { if (!m_VehiclePOSIXPatch) { - m_VehiclePOSIXPatch = ScriptPatches::AddPatch("appinternet"_J, "59 ? ? 72 2E 02 01", 0, {0x2B, 0x00, 0x00}); + m_VehiclePOSIXPatch = ScriptPatches::AddPatch("appinternet"_J, ScriptPointer("VehiclePOSIXPatch", "59 ? ? 72 2E 02 01"), {0x2B, 0x00, 0x00}); } m_VehiclePOSIXPatch->Enable(); if (!m_GetVehiclePricePatch) { - m_GetVehiclePricePatch = ScriptPatches::AddPatch("appinternet"_J, "56 ? ? 70 2E 04 01 38 01", 0, {0x55}); + m_GetVehiclePricePatch = ScriptPatches::AddPatch("appinternet"_J, ScriptPointer("GetVehiclePricePatch", "56 ? ? 70 2E 04 01 38 01"), {0x55}); } m_GetVehiclePricePatch->Enable(); if (!m_BuyVehiclePatch) { - m_BuyVehiclePatch = ScriptPatches::AddPatch("appinternet"_J, "5D ? ? ? 06 56 ? ? 38 00 25 ? 50", 5, {0x55}); + m_BuyVehiclePatch = ScriptPatches::AddPatch("appinternet"_J, ScriptPointer("BuyVehiclePatch", "5D ? ? ? 06 56 ? ? 38 00 25 ? 50").Add(5), {0x55}); } m_BuyVehiclePatch->Enable(); } diff --git a/src/game/features/recovery/GiveVehicleReward.cpp b/src/game/features/recovery/GiveVehicleReward.cpp index 39634a7..e4a7269 100644 --- a/src/game/features/recovery/GiveVehicleReward.cpp +++ b/src/game/features/recovery/GiveVehicleReward.cpp @@ -2,14 +2,13 @@ #include "core/backend/ScriptMgr.hpp" #include "game/backend/Self.hpp" #include "game/gta/Natives.hpp" +#include "game/gta/Scripts.hpp" #include "game/gta/ScriptFunction.hpp" #include "game/gta/ScriptLocal.hpp" #include "types/script/locals/VehicleRewardData.hpp" namespace YimMenu { - static ScriptFunction giveVehicleReward("GVR", "AM_MP_VEHICLE_REWARD"_J, "2D 0C 1E 00 00"); - bool GiveVehicleReward::IsSafeToRunScript() { return Self::GetVehicle().IsValid() && *Pointers.IsSessionStarted; @@ -67,6 +66,7 @@ namespace YimMenu if (auto VehicleRewardData = VEHICLE_REWARD_DATA::Get(m_Thread)) { auto VehicleMenuData = ScriptLocal(m_Thread, 176).As(); // TO-DO: add struct for this? + static ScriptFunction giveVehicleReward("AM_MP_VEHICLE_REWARD"_J, ScriptPointer("GiveVehicleReward", "2D 0C 1E 00 00")); if (giveVehicleReward.Call(Self::GetVehicle().GetHandle(), VehicleMenuData, &VehicleRewardData->TransactionStatus, &VehicleRewardData->Garage, &VehicleRewardData->GarageOffset, &VehicleRewardData->ControlStatus, false, true, true, false, 0, -1)) { if (VehicleRewardData->ControlStatus != 3) diff --git a/src/game/features/recovery/Heist/ApartmentHeist.cpp b/src/game/features/recovery/Heist/ApartmentHeist.cpp new file mode 100644 index 0000000..d23ef76 --- /dev/null +++ b/src/game/features/recovery/Heist/ApartmentHeist.cpp @@ -0,0 +1,132 @@ +#include "core/commands/IntCommand.hpp" +#include "core/commands/Command.hpp" +#include "game/gta/Stats.hpp" +#include "game/backend/Players.hpp" +#include "game/gta/ScriptGlobal.hpp" +#include "game/gta/ScriptLocal.hpp" +#include "core/backend/ScriptMgr.hpp" +#include "types/script/globals/GlobalPlayerBD.hpp" + +namespace YimMenu::Features +{ + namespace ApartmentHeist + { + static IntCommand _ApartmentHeistCut1{"apartmentheistcut1", "Player 1", "Player 1 cut", std::nullopt, std::nullopt, 0}; + static IntCommand _ApartmentHeistCut2{"apartmentheistcut2", "Player 2", "Player 2 cut", std::nullopt, std::nullopt, 0}; + static IntCommand _ApartmentHeistCut3{"apartmentheistcut3", "Player 3", "Player 3 cut", std::nullopt, std::nullopt, 0}; + static IntCommand _ApartmentHeistCut4{"apartmentheistcut4", "Player 4", "Player 4 cut", std::nullopt, std::nullopt, 0}; + + class SetCuts : public Command + { + using Command::Command; + + virtual void OnCall() override + { + auto base1 = ScriptGlobal(1929794).At(1); + auto base2 = ScriptGlobal(1931762).At(3008); + + *base1.At(1).As() = 100 - (_ApartmentHeistCut1.GetState() + _ApartmentHeistCut2.GetState() + _ApartmentHeistCut3.GetState() + _ApartmentHeistCut4.GetState()); + *base1.At(2).As() = _ApartmentHeistCut2.GetState(); + *base1.At(3).As() = _ApartmentHeistCut3.GetState(); + *base1.At(4).As() = _ApartmentHeistCut4.GetState(); + + ScriptMgr::Yield(500ms); + + *base2.At(1).As() = -1 * (*base1.At(1).As() + *base1.At(2).As() + *base1.At(3).As() + *base1.At(4).As() - 100); + *base2.At(2).As() = *base1.At(2).As(); + *base2.At(3).As() = *base1.At(3).As(); + *base2.At(4).As() = *base1.At(4).As(); + } + }; + + class ForceReady : public Command + { + using Command::Command; + + virtual void OnCall() override + { + if (auto gpbd = GlobalPlayerBD::Get(); gpbd && Scripts::SafeToModifyFreemodeBroadcastGlobals()) + { + for (auto& player : Players::GetPlayers()) + { + gpbd->Entries[player.second.GetId()].HeistCutSelectionStage = 6; + } + } + } + }; + + class Setup : public Command + { + using Command::Command; + + virtual void OnCall() override + { + Stats::SetInt("MPX_HEIST_PLANNING_STAGE", -1); + } + }; + + class SkipHacking : public Command + { + using Command::Command; + + virtual void OnCall() override + { + *ScriptLocal("fm_mission_controller"_J, 12216).At(24).As() = 7; + *ScriptLocal("fm_mission_controller"_J, 10213).As() = *ScriptLocal("fm_mission_controller"_J, 10213).As() | (1 << 9); + } + }; + + class SkipDrilling : public Command + { + using Command::Command; + + virtual void OnCall() override + { + *ScriptLocal("fm_mission_controller"_J, 10507).At(11).As() = 100.0f; + } + }; + + class InstantFinish : public Command + { + using Command::Command; + + virtual void OnCall() override + { + Scripts::ForceScriptHost(Scripts::FindScriptThread("fm_mission_controller"_J)); + ScriptMgr::Yield(500ms); + + *ScriptLocal("fm_mission_controller"_J, 20387).At(1725).At(1).As() = 80; + *ScriptLocal("fm_mission_controller"_J, 20387).As() = 12; + *ScriptLocal("fm_mission_controller"_J, 29006).At(1).As() = 99999; + *ScriptLocal("fm_mission_controller"_J, 32462).At(1).At(68).As() = 99999; + + // TODO: find a way of getting current heist info so that InstantFinishPacific can be implemented here conditionally. + } + }; + + class InstantFinishPacific : public Command + { + using Command::Command; + + virtual void OnCall() override + { + Scripts::ForceScriptHost(Scripts::FindScriptThread("fm_mission_controller"_J)); + ScriptMgr::Yield(500ms); + + *ScriptLocal("fm_mission_controller"_J, 20387).At(2686).As() = 1875000; + *ScriptLocal("fm_mission_controller"_J, 20387).At(1062).As() = 5; + *ScriptLocal("fm_mission_controller"_J, 20387).As() = 12; + *ScriptLocal("fm_mission_controller"_J, 29006).At(1).As() = 99999; + *ScriptLocal("fm_mission_controller"_J, 32462).At(1).At(68).As() = 99999; + } + }; + + static SetCuts _ApartmentHeistSetCuts{"apartmentheistsetcuts", "Set Cuts", "Sets heist cut"}; + static ForceReady _ApartmentHeistForceReady{"apartmentheistforceready", "Force Ready", "Forces all players to be ready"}; + static Setup _ApartmentHeistSetup{"apartmentheistsetup", "Setup", "Sets up current apartment heist"}; + static SkipHacking _ApartmentHeistSkipHacking{"apartmentheistskiphacking", "Skip Hacking", "Skips hacking process"}; + static SkipDrilling _ApartmentHeistSkipDrilling{"apartmentheistskipdrilling", "Skip Drilling", "Skips drilling process"}; + static InstantFinish _ApartmentHeistInstantFinish{"apartmentheistinstantfinish", "Instant Finish", "Instantly passes the heist"}; + static InstantFinishPacific _ApartmentHeistInstantFinishPacific{"apartmentheistinstantfinishpacific", "Instant Finish (Pacific)", "Instantly passes Pacific Standard Job"}; + } +} \ No newline at end of file diff --git a/src/game/features/recovery/Heist/CayoPericoHeist.cpp b/src/game/features/recovery/Heist/CayoPericoHeist.cpp new file mode 100644 index 0000000..401c5df --- /dev/null +++ b/src/game/features/recovery/Heist/CayoPericoHeist.cpp @@ -0,0 +1,257 @@ +#include "core/commands/Command.hpp" +#include "core/commands/IntCommand.hpp" +#include "core/commands/ListCommand.hpp" +#include "game/backend/Self.hpp" +#include "game/gta/Natives.hpp" +#include "game/gta/Stats.hpp" +#include "game/gta/ScriptGlobal.hpp" +#include "game/gta/ScriptLocal.hpp" +#include "core/backend/ScriptMgr.hpp" +#include "game/backend/Tunables.hpp" + +namespace YimMenu::Features +{ + namespace CayoPericoHeist + { + static IntCommand _CayoPericoHeistCut1{"cayopericoheistcut1", "Player 1", "Player 1 cut", std::nullopt, std::nullopt, 0}; + static IntCommand _CayoPericoHeistCut2{"cayopericoheistcut2", "Player 2", "Player 2 cut", std::nullopt, std::nullopt, 0}; + static IntCommand _CayoPericoHeistCut3{"cayopericoheistcut3", "Player 3", "Player 3 cut", std::nullopt, std::nullopt, 0}; + static IntCommand _CayoPericoHeistCut4{"cayopericoheistcut4", "Player 4", "Player 4 cut", std::nullopt, std::nullopt, 0}; + + class SetCuts : public Command + { + using Command::Command; + + virtual void OnCall() override + { + auto base = ScriptGlobal(1973698).At(831).At(56); + + *base.At(1).As() = _CayoPericoHeistCut1.GetState(); + *base.At(2).As() = _CayoPericoHeistCut2.GetState(); + *base.At(3).As() = _CayoPericoHeistCut3.GetState(); + *base.At(4).As() = _CayoPericoHeistCut4.GetState(); + } + }; + + class ForceReady : public Command + { + using Command::Command; + + virtual void OnCall() override + { + auto base = ScriptGlobal(1974810); + + *base.At(0, 27).At(8).At(0).As() = 1; + *base.At(1, 27).At(8).At(1).As() = 1; + *base.At(2, 27).At(8).At(2).As() = 1; + *base.At(3, 27).At(8).At(3).As() = 1; + } + }; + + static std::vector> cayoPericoHeistDifficulty = { + {126823, "Normal"}, + {131055, "Hard"} + }; + static ListCommand _CayoPericoHeistDifficulty{"cayopericoheistdifficulty", "Difficulty", "Heist difficulty", cayoPericoHeistDifficulty, 126823}; + + static std::vector> cayoPericoHeistPrimaryTarget = { + {5, "Panther Statue"}, + {3, "Pink Diamond"}, + {4, "Madrazo Files"}, + {2, "Bearer Bonds"}, + {1, "Ruby Necklace"}, + {0, "Sinsimito Tequila"} + }; + static ListCommand _CayoPericoHeistPrimaryTarget{"cayopericoheistprimarytarget", "Primary Target", "Primary target", cayoPericoHeistPrimaryTarget, 5}; + + static std::vector> cayoPericoHeistWeapon = { + {1, "Aggressor"}, + {2, "Conspirator"}, + {3, "Crack Shot"}, + {4, "Saboteur"}, + {5, "Marksman"} + }; + static ListCommand _CayoPericoHeistWeapon{"cayopericoheistweapon", "Weapon", "Weapon category", cayoPericoHeistWeapon, 1}; + + class Setup : public Command + { + using Command::Command; + + virtual void OnCall() override + { + Stats::SetInt("MPX_H4CNF_TARGET", _CayoPericoHeistPrimaryTarget.GetState()); // Primary target + Stats::SetInt("MPX_H4LOOT_CASH_I", 2359448); + Stats::SetInt("MPX_H4LOOT_CASH_I_SCOPED", 2359448); + Stats::SetInt("MPX_H4LOOT_CASH_C", 0); + Stats::SetInt("MPX_H4LOOT_CASH_C_SCOPED", 0); + Stats::SetInt("MPX_H4LOOT_COKE_I", 4901222); + Stats::SetInt("MPX_H4LOOT_COKE_I_SCOPED", 4901222); + Stats::SetInt("MPX_H4LOOT_COKE_C", 0); + Stats::SetInt("MPX_H4LOOT_COKE_C_SCOPED", 0); + Stats::SetInt("MPX_H4LOOT_GOLD_I", 0); + Stats::SetInt("MPX_H4LOOT_GOLD_I_SCOPED", 0); + Stats::SetInt("MPX_H4LOOT_GOLD_C", 255); + Stats::SetInt("MPX_H4LOOT_GOLD_C_SCOPED", 255); + Stats::SetInt("MPX_H4LOOT_WEED_I", 0); + Stats::SetInt("MPX_H4LOOT_WEED_I_SCOPED", 0); + Stats::SetInt("MPX_H4LOOT_WEED_C", 0); + Stats::SetInt("MPX_H4LOOT_WEED_C_SCOPED", 0); + Stats::SetInt("MPX_H4LOOT_PAINT", 127); + Stats::SetInt("MPX_H4LOOT_PAINT_SCOPED", 127); + Stats::SetInt("MPX_H4LOOT_CASH_V", 599431); + Stats::SetInt("MPX_H4LOOT_COKE_V", 1198863); + Stats::SetInt("MPX_H4LOOT_GOLD_V", 1598484); + Stats::SetInt("MPX_H4LOOT_PAINT_V", 1198863); + Stats::SetInt("MPX_H4LOOT_WEED_V", 0); + Stats::SetInt("MPX_H4_PROGRESS", _CayoPericoHeistDifficulty.GetState()); // Difficulty + Stats::SetInt("MPX_H4CNF_BS_GEN", 262143); + Stats::SetInt("MPX_H4CNF_BS_ENTR", 63); + Stats::SetInt("MPX_H4CNF_BS_ABIL", 63); + Stats::SetInt("MPX_H4CNF_WEP_DISRP", 3); + Stats::SetInt("MPX_H4CNF_ARM_DISRP", 3); + Stats::SetInt("MPX_H4CNF_HEL_DISRP", 3); + Stats::SetInt("MPX_H4CNF_APPROACH", -1); + Stats::SetInt("MPX_H4CNF_BOLTCUT", 4424); + Stats::SetInt("MPX_H4CNF_UNIFORM", 5256); + Stats::SetInt("MPX_H4CNF_GRAPPEL", 5156); + Stats::SetInt("MPX_H4_MISSIONS", -1); + Stats::SetInt("MPX_H4CNF_WEAPONS", _CayoPericoHeistWeapon.GetState()); // Weapon + Stats::SetInt("MPX_H4CNF_TROJAN", 5); + Stats::SetInt("MPX_H4_PLAYTHROUGH_STATUS", 100); + + ScriptMgr::Yield(500ms); + + *ScriptLocal("heist_island_planning"_J, 1566).As() = 2; + } + }; + + static IntCommand _CayoPericoHeistPrimaryTargetValue{"cayopericoheistprimarytargetvalue", "Primary Target Value", "Updates primary target value", std::nullopt, std::nullopt, 0}; + + class SetPrimaryTargetValue : public Command + { + using Command::Command; + + virtual void OnCall() override + { + auto primary_target = Stats::GetInt("MPX_H4CNF_TARGET"); + + switch (primary_target) + { + case 0: *Tunables::GetTunable("IH_PRIMARY_TARGET_VALUE_TEQUILA"_J).As() = _CayoPericoHeistPrimaryTargetValue.GetState(); break; + case 1: *Tunables::GetTunable("IH_PRIMARY_TARGET_VALUE_PEARL_NECKLACE"_J).As() = _CayoPericoHeistPrimaryTargetValue.GetState(); break; + case 2: *Tunables::GetTunable("IH_PRIMARY_TARGET_VALUE_BEARER_BONDS"_J).As() = _CayoPericoHeistPrimaryTargetValue.GetState(); break; + case 3: *Tunables::GetTunable("IH_PRIMARY_TARGET_VALUE_PINK_DIAMOND"_J).As() = _CayoPericoHeistPrimaryTargetValue.GetState(); break; + case 4: *Tunables::GetTunable("IH_PRIMARY_TARGET_VALUE_MADRAZO_FILES"_J).As() = _CayoPericoHeistPrimaryTargetValue.GetState(); break; + case 5: *Tunables::GetTunable("IH_PRIMARY_TARGET_VALUE_SAPPHIRE_PANTHER_STATUE"_J).As() = _CayoPericoHeistPrimaryTargetValue.GetState(); break; + default: break; + } + } + }; + + static IntCommand _CayoPericoHeistSecondaryTakeValue{"cayopericoheistsecondarytakevalue", "Secondary Take Value", "Updates secondary take value", std::nullopt, std::nullopt, 0}; + + class SetSecondaryTakeValue : public Command + { + using Command::Command; + + virtual void OnCall() override + { + *ScriptLocal("fm_mission_controller_2020"_J, 56033).At(1518).At(53).As() = _CayoPericoHeistSecondaryTakeValue.GetState(); + } + }; + + class SkipHacking : public Command + { + using Command::Command; + + virtual void OnCall() override + { + *ScriptLocal("fm_mission_controller_2020"_J, 25388).As() = 5; + } + }; + + class CutSewer : public Command + { + using Command::Command; + + virtual void OnCall() override + { + *ScriptLocal("fm_mission_controller_2020"_J, 30212).As() = 6; + } + }; + + class CutGlass : public Command + { + using Command::Command; + + virtual void OnCall() override + { + *ScriptLocal("fm_mission_controller_2020"_J, 31451).At(3).As() = 100.0f; + } + }; + + class TakePrimaryTarget : public Command + { + using Command::Command; + + virtual void OnCall() override + { + auto ped = Self::GetPed(); + + if (!ped) + return; + + auto pos = ped.GetPosition(); + auto heading = ped.GetHeading(); + auto primary_target = Stats::GetInt("MPX_H4CNF_TARGET"); + + TASK::TASK_GO_STRAIGHT_TO_COORD(ped.GetHandle(), 5006.917, -5755.931, 15.484, 1.0, 3, 15, 5); + + switch (primary_target) + { + case 0: + case 1: + case 3: + case 5: + *ScriptLocal("fm_mission_controller_2020"_J, 31450).As() = 5; + *ScriptLocal("fm_mission_controller_2020"_J, 31451).As() = 3; + break; + case 2: + case 4: + *ScriptLocal("fm_mission_controller_2020"_J, 31426).As() = 7; + break; + default: break; + } + + ScriptMgr::Yield(4000ms); + + TASK::TASK_GO_STRAIGHT_TO_COORD(ped.GetHandle(), pos.x, pos.y, pos.z, 1.0, 3, heading, 5); + } + }; + + class InstantFinish : public Command + { + using Command::Command; + + virtual void OnCall() override + { + Scripts::ForceScriptHost(Scripts::FindScriptThread("fm_mission_controller_2020"_J)); + ScriptMgr::Yield(500ms); + + *ScriptLocal("fm_mission_controller_2020"_J, 52581).As() = 9; + *ScriptLocal("fm_mission_controller_2020"_J, 52581).At(1776).At(1).As() = 50; + } + }; + + static SetCuts _CayoPericoHeistSetCuts{"cayopericoheistsetcuts", "Set Cuts", "Sets heist cut"}; + static ForceReady _CayoPericoHeistForceReady{"cayopericoheistforceready", "Force Ready", "Forces all players to be ready"}; + static Setup _CayoPericoHeistSetup{"cayopericoheistsetup", "Setup", "Sets up cayo perico heist"}; + static SetPrimaryTargetValue _CayoPericoHeistSetPrimaryTargetValue{"cayopericoheistsetprimarytargetvalue", "Set Primary Target Value", "Updates primary target value"}; + static SetSecondaryTakeValue _CayoPericoHeistSetSecondaryTakeValue{"cayopericoheistsetsecondarytakevalue", "Set Secondary Take Value", "Updates secondary take value"}; + static SkipHacking _CayoPericoHeistSkipHacking{"cayopericoheistskiphacking", "Skip Hacking", "Skips hacking process"}; + static CutSewer _CayoPericoHeistCutSewer{"cayopericoheistcutsewer", "Cut Sewer", "Cuts the sewer"}; + static CutGlass _CayoPericoHeistCutGlass{"cayopericoheistcutglass", "Cut Glass", "Cuts the glass"}; + static TakePrimaryTarget _CayoPericoHeistTakePrimaryTarget{"cayopericoheisttakeprimarytarget", "Take Primary Target", "Takes primary target"}; + static InstantFinish _CayoPericoHeistInstantFinish{"cayopericoheistinstantfinish", "Instant Finish", "Instantly passes the heist"}; + } +} \ No newline at end of file diff --git a/src/game/features/recovery/Heist/DiamondCasinoHeist.cpp b/src/game/features/recovery/Heist/DiamondCasinoHeist.cpp new file mode 100644 index 0000000..0fa8ac0 --- /dev/null +++ b/src/game/features/recovery/Heist/DiamondCasinoHeist.cpp @@ -0,0 +1,333 @@ +#include "core/commands/Command.hpp" +#include "core/commands/IntCommand.hpp" +#include "core/commands/ListCommand.hpp" +#include "game/gta/Stats.hpp" +#include "core/backend/FiberPool.hpp" +#include "game/gta/ScriptGlobal.hpp" +#include "game/gta/ScriptLocal.hpp" +#include "core/backend/ScriptMgr.hpp" +#include "game/backend/Tunables.hpp" + +namespace YimMenu::Features +{ + namespace DiamondCasinoHeist + { + static IntCommand _DiamondCasinoHeistCut1{"diamondcasinoheistcut1", "Player 1", "Player 1 cut", std::nullopt, std::nullopt, 0}; + static IntCommand _DiamondCasinoHeistCut2{"diamondcasinoheistcut2", "Player 2", "Player 2 cut", std::nullopt, std::nullopt, 0}; + static IntCommand _DiamondCasinoHeistCut3{"diamondcasinoheistcut3", "Player 3", "Player 3 cut", std::nullopt, std::nullopt, 0}; + static IntCommand _DiamondCasinoHeistCut4{"diamondcasinoheistcut4", "Player 4", "Player 4 cut", std::nullopt, std::nullopt, 0}; + + class SetCuts : public Command + { + using Command::Command; + + virtual void OnCall() override + { + auto base = ScriptGlobal(1966898).At(1497).At(736).At(92); + + *base.At(1).As() = _DiamondCasinoHeistCut1.GetState(); + *base.At(2).As() = _DiamondCasinoHeistCut2.GetState(); + *base.At(3).As() = _DiamondCasinoHeistCut3.GetState(); + *base.At(4).As() = _DiamondCasinoHeistCut4.GetState(); + } + }; + + class ForceReady : public Command + { + using Command::Command; + + virtual void OnCall() override + { + auto base = ScriptGlobal(1971261); + + *base.At(0, 68).At(8).At(0).As() = 1; + *base.At(1, 68).At(8).At(1).As() = 1; + *base.At(2, 68).At(8).At(2).As() = 1; + *base.At(3, 68).At(8).At(3).As() = 1; + } + }; + + static std::vector> diamondCasinoHeistDifficulty = { + {0, "Normal"}, + {1, "Hard"} + }; + static ListCommand _DiamondCasinoHeistDifficulty{"diamondcasinoheistdifficulty", "Difficulty", "Heist difficulty", diamondCasinoHeistDifficulty, 0}; + + static std::vector> diamondCasinoHeistPrimaryTarget = { + {3, "Diamonds"}, + {1, "Gold"}, + {2, "Artwork"}, + {0, "Cash"} + }; + static ListCommand _DiamondCasinoHeistPrimaryTarget{"diamondcasinoheistprimarytarget", "Primary Target", "Primary target", diamondCasinoHeistPrimaryTarget, 3}; + + static ListCommand* _DiamondCasinoHeistGunmanPtr = nullptr; + static ListCommand* _DiamondCasinoHeistApproachPtr = nullptr; + + static std::vector>>> diamondCasinoHeistWeapon = { + { + {{0, "MK II Shotgun Loadout"}, {1, "MK II Rifle Loadout"}}, + {{0, "MK II SMG Loadout"}, {1, "MK II Rifle Loadout"}}, + {{0, "MK II Shotgun Loadout"}, {1, "MK II Rifle Loadout"}} + }, + { + {{0, "Rifle Loadout"}, {1, "Shotgun Loadout"}}, + {{0, "Rifle Loadout"}, {1, "Shotgun Loadout"}}, + {{0, "Rifle Loadout"}, {1, "Shotgun Loadout"}} + }, + { + {{0, "Combat PDW Loadout"}, {1, "Rifle Loadout"}}, + {{0, "Shotgun Loadout"}, {1, "Rifle Loadout"}}, + {{0, "Shotgun Loadout"}, {1, "Combat MG Loadout"}} + }, + { + {{0, "SMG Loadout"}, {1, "Shotgun Loadout"}}, + {{0, "Machine Pistol Loadout"}, {1, "Shotgun Loadout"}}, + {{0, "SMG Loadout"}, {1, "Shotgun Loadout"}} + }, + { + {{0, "Micro SMG Loadout"}, {1, "Machine Pistol Loadout"}}, + {{0, "Micro SMG Loadout"}, {1, "Shotgun Loadout"}}, + {{0, "Shotgun Loadout"}, {1, "Revolver Loadout"}} + }, + { + {{0, " "}, {1, ""}}, + {{0, ""}, {1, ""}}, + {{0, ""}, {1, ""}} + } + }; + static ListCommand _DiamondCasinoHeistWeapon{"diamondcasinoheistweapon", "Weapon", "Weapon", diamondCasinoHeistWeapon[5][0], 0}; + + static std::vector> diamondCasinoHeistGunman = { + {0, "Chester McCoy"}, + {1, "Gustavo Mota"}, + {2, "Patrick McReary"}, + {3, "Charlie Reed"}, + {4, "Karl Abolaji"}, + {5, "Remove Gunman"} + }; + + class Gunman : public ListCommand + { + using ListCommand::ListCommand; + + virtual void OnChange() override + { + _DiamondCasinoHeistWeapon.SetList(diamondCasinoHeistWeapon[this->GetState()][_DiamondCasinoHeistApproachPtr->GetState()]); + _DiamondCasinoHeistWeapon.SetState(0); + } + }; + static Gunman _DiamondCasinoHeistGunman{"diamondcasinoheistgunman", "Gunman", "Gunman", diamondCasinoHeistGunman, 5}; + + static std::vector> diamondCasinoHeistApproach = { + {0, "Silent & Sneaky"}, + {1, "The Big Con"}, + {2, "Aggressive"} + }; + + class Approach : public ListCommand + { + using ListCommand::ListCommand; + + virtual void OnChange() override + { + _DiamondCasinoHeistWeapon.SetList(diamondCasinoHeistWeapon[_DiamondCasinoHeistGunmanPtr->GetState()][this->GetState()]); + _DiamondCasinoHeistWeapon.SetState(0); + } + }; + static Approach _DiamondCasinoHeistApproach{"diamondcasinoheistapproach", "Approach", "Heist approach", diamondCasinoHeistApproach, 0}; + + static struct InitPtrs + { + InitPtrs() + { + _DiamondCasinoHeistGunmanPtr = &_DiamondCasinoHeistGunman; + _DiamondCasinoHeistApproachPtr = &_DiamondCasinoHeistApproach; + } + } _initPtrs; + + static std::vector>> diamondCasinoHeistVehicle = { + {{0, "Zhaba"}, {1, "Vagrant"}, {2, "Outlaw"}, {3, "Everon"}}, + {{0, "Sultan Classic"}, {1, "Gauntlet Classic"}, {2, "Ellie"}, {3, "Komoda"}}, + {{0, "Retinue MK II"}, {1, "Drifty Yosemite"}, {2, "Sugoi"}, {3, "Jugular"}}, + {{0, "Manchez"}, {1, "Stryder"}, {2, "Defiler"}, {3, "Lectro"}}, + {{0, "Issi Classic"}, {1, "Asbo"}, {2, "Kanjo"}, {3, "Sentinel Classic"}}, + {{0, " "}, {1, ""}, {2, ""}, {3, ""}} + }; + static ListCommand _DiamondCasinoHeistVehicle{"diamondcasinoheistvehicle", "Vehicle", "Vehicle", diamondCasinoHeistVehicle[5], 0}; + + static std::vector> diamondCasinoHeistDriver = { + {0, "Chester McCoy"}, + {1, "Eddie Toh"}, + {2, "Taliana Martinez"}, + {3, "Zach Nelson"}, + {4, "Karim Denz"}, + {5, "Remove Driver"} + }; + + class Driver : public ListCommand + { + using ListCommand::ListCommand; + + virtual void OnChange() override + { + _DiamondCasinoHeistVehicle.SetList(diamondCasinoHeistVehicle[this->GetState()]); + _DiamondCasinoHeistVehicle.SetState(0); + } + }; + static Driver _DiamondCasinoHeistDriver{"diamondcasinoheistdriver", "Driver", "Driver", diamondCasinoHeistDriver, 5}; + + static std::vector> diamondCasinoHeistHacker = { + {4, "Avi Schwartzman"}, + {5, "Paige Harris"}, + {2, "Christian Feltz"}, + {3, "Yohan Blair"}, + {1, "Rickie Lukens"}, + {6, "Remove Hacker"} + }; + static ListCommand _DiamondCasinoHeistHacker{"diamondcasinoheisthacker", "Hacker", "Hacker", diamondCasinoHeistHacker, 6}; + + class Setup : public Command + { + using Command::Command; + + virtual void OnCall() override + { + Stats::SetInt("MPX_H3_COMPLETEDPOSIX", -1); + Stats::SetInt("MPX_H3OPT_MASKS", 4); + Stats::SetInt("MPX_H3OPT_WEAPS", _DiamondCasinoHeistWeapon.GetState()); + Stats::SetInt("MPX_H3OPT_VEHS", _DiamondCasinoHeistVehicle.GetState()); + Stats::SetInt("MPX_CAS_HEIST_FLOW", -1); + Stats::SetInt("MPX_H3_LAST_APPROACH", 0); + Stats::SetInt("MPX_H3OPT_APPROACH", _DiamondCasinoHeistApproach.GetState() + 1); // 1 = Silent & Sneaky, 2 = The Big Con, 3 = Aggressive + + switch (_DiamondCasinoHeistDifficulty.GetState()) + { + case 0: Stats::SetInt("MPX_H3_HARD_APPROACH", 0); break; + case 1: Stats::SetInt("MPX_H3_HARD_APPROACH", _DiamondCasinoHeistApproach.GetState() + 1); break; + default: break; + } + + Stats::SetInt("MPX_H3OPT_TARGET", _DiamondCasinoHeistPrimaryTarget.GetState()); + Stats::SetInt("MPX_H3OPT_POI", 1023); + Stats::SetInt("MPX_H3OPT_ACCESSPOINTS", 2047); + + switch (_DiamondCasinoHeistGunman.GetState()) + { + case 0: Stats::SetInt("MPX_H3OPT_CREWWEAP", 4); break; + case 1: Stats::SetInt("MPX_H3OPT_CREWWEAP", 2); break; + case 2: Stats::SetInt("MPX_H3OPT_CREWWEAP", 5); break; + case 3: Stats::SetInt("MPX_H3OPT_CREWWEAP", 3); break; + case 4: Stats::SetInt("MPX_H3OPT_CREWWEAP", 1); break; + case 5: Stats::SetInt("MPX_H3OPT_CREWWEAP", 6); break; + default: break; + } + + switch (_DiamondCasinoHeistDriver.GetState()) + { + case 0: Stats::SetInt("MPX_H3OPT_CREWDRIVER", 5); break; + case 1: Stats::SetInt("MPX_H3OPT_CREWDRIVER", 3); break; + case 2: Stats::SetInt("MPX_H3OPT_CREWDRIVER", 2); break; + case 3: Stats::SetInt("MPX_H3OPT_CREWDRIVER", 4); break; + case 4: Stats::SetInt("MPX_H3OPT_CREWDRIVER", 1); break; + case 5: Stats::SetInt("MPX_H3OPT_CREWDRIVER", 6); break; + default: break; + } + + Stats::SetInt("MPX_H3OPT_CREWHACKER", _DiamondCasinoHeistHacker.GetState()); + Stats::SetInt("MPX_H3OPT_DISRUPTSHIP", 3); // Security Strength: 1 = Strong, 2 = Medium, 3 = Weak + Stats::SetInt("MPX_H3OPT_BODYARMORLVL", -1); + Stats::SetInt("MPX_H3OPT_KEYLEVELS", 2); // 1 = Level1 Security Pass, 2 = Level2 Security Pass + + Stats::SetInt("MPX_H3OPT_BITSET0", rand() % (INT_MAX)); // Refresh board + Stats::SetInt("MPX_H3OPT_BITSET1", rand() % (INT_MAX)); // Refresh board + + ScriptMgr::Yield(500ms); + + Stats::SetInt("MPX_H3OPT_BITSET0", -1); // Refresh board + Stats::SetInt("MPX_H3OPT_BITSET1", -1); // Refresh board + } + }; + + static IntCommand _DiamondCasinoHeistPotentialTake{"diamondcasinoheistpotentialtake", "Potential Take", "Updates potential take", std::nullopt, std::nullopt, 0}; + + class SetPotentialTake : public Command + { + using Command::Command; + + virtual void OnCall() override + { + int primary_target = Stats::GetInt("MPX_H3OPT_TARGET"); + + switch (primary_target) + { + case 0: *Tunables::GetTunable(static_cast(-1638885821)).As() = _DiamondCasinoHeistPotentialTake.GetState(); break; // "Cash" + case 1: *Tunables::GetTunable(static_cast(-582734553)).As() = _DiamondCasinoHeistPotentialTake.GetState(); break; // "Gold" + case 2: *Tunables::GetTunable(static_cast(1934398910)).As() = _DiamondCasinoHeistPotentialTake.GetState(); break; // "Artwork" + case 3: *Tunables::GetTunable(static_cast(1277889925)).As() = _DiamondCasinoHeistPotentialTake.GetState(); break; // "Diamond" + default: break; + } + } + }; + + static IntCommand _DiamondCasinoHeistActualTake{"diamondcasinoheistactualtake", "Actual Take", "Updates actual take", std::nullopt, std::nullopt, 0}; + + class SetActualTake : public Command + { + using Command::Command; + + virtual void OnCall() override + { + *ScriptLocal("fm_mission_controller"_J, 20387).At(2686).As() = _DiamondCasinoHeistActualTake.GetState(); + } + }; + + class SkipHacking : public Command + { + using Command::Command; + + virtual void OnCall() override + { + *ScriptLocal("fm_mission_controller"_J, 53999).As() = 5; + *ScriptLocal("fm_mission_controller"_J, 55065).As() = 5; + } + }; + + class SkipDrilling : public Command + { + using Command::Command; + + virtual void OnCall() override + { + *ScriptLocal("fm_mission_controller"_J, 10547).At(7).As() = *ScriptLocal("fm_mission_controller"_J, 10547).At(37).As(); + } + }; + + class InstantFinish : public Command + { + using Command::Command; + + virtual void OnCall() override + { + Scripts::ForceScriptHost(Scripts::FindScriptThread("fm_mission_controller"_J)); + ScriptMgr::Yield(500ms); + + *ScriptLocal("fm_mission_controller"_J, 20387).At(1740).At(1).As() = 80; + *ScriptLocal("fm_mission_controller"_J, 20387).At(2686).As() = 4443220; + *ScriptLocal("fm_mission_controller"_J, 20387).At(1062).As() = 5; + *ScriptLocal("fm_mission_controller"_J, 20387).As() = 12; + *ScriptLocal("fm_mission_controller"_J, 29006).At(1).As() = 99999; + *ScriptLocal("fm_mission_controller"_J, 32462).At(1).At(68).As() = 99999; + } + }; + + static SetCuts _DiamondCasinoHeistSetCuts{"diamondcasinoheistsetcuts", "Set Cuts", "Sets heist cut"}; + static ForceReady _DiamondCasinoHeistForceReady{"diamondcasinoheistforceready", "Force Ready", "Forces all players to be ready"}; + static Setup _DiamondCasinoHeistSetup{"diamondcasinoheistsetup", "Setup", "Sets up diamond casino heist"}; + static SetPotentialTake _DiamondCasinoHeistSetPotentialTake{"diamondcasinoheistsetpotentialtake", "Set Potential Take", "Updates potential take"}; + static SetActualTake _DiamondCasinoHeistSetActualTake{"diamondcasinoheistsetactualtake", "Set Actual Take", "Updates actual take"}; + static SkipHacking _DiamondCasinoHeistSkipHacking{"diamondcasinoheistskiphacking", "Skip Hacking", "Skips hacking process"}; + static SkipDrilling _DiamondCasinoHeistSkipDrilling{"diamondcasinoheistskipdrilling", "Skip Drilling", "Skips drilling process"}; + static InstantFinish _DiamondCasinoHeistInstantFinish{"diamondcasinoheistinstantfinish", "Instant Finish", "Instantly passes the heist"}; + } +} \ No newline at end of file diff --git a/src/game/features/recovery/Heist/DoomsdayHeist.cpp b/src/game/features/recovery/Heist/DoomsdayHeist.cpp new file mode 100644 index 0000000..2431dd3 --- /dev/null +++ b/src/game/features/recovery/Heist/DoomsdayHeist.cpp @@ -0,0 +1,141 @@ +#include "core/commands/Command.hpp" +#include "core/commands/IntCommand.hpp" +#include "core/commands/ListCommand.hpp" +#include "game/gta/Stats.hpp" +#include "game/gta/ScriptGlobal.hpp" +#include "game/gta/ScriptLocal.hpp" +#include "core/backend/ScriptMgr.hpp" + +namespace YimMenu::Features +{ + namespace DoomsdayHeist + { + static IntCommand _DoomsdayHeistCut1{"doomsdayheistcut1", "Player 1", "Player 1 cut", std::nullopt, std::nullopt, 0}; + static IntCommand _DoomsdayHeistCut2{"doomsdayheistcut2", "Player 2", "Player 2 cut", std::nullopt, std::nullopt, 0}; + static IntCommand _DoomsdayHeistCut3{"doomsdayheistcut3", "Player 3", "Player 3 cut", std::nullopt, std::nullopt, 0}; + static IntCommand _DoomsdayHeistCut4{"doomsdayheistcut4", "Player 4", "Player 4 cut", std::nullopt, std::nullopt, 0}; + + class SetCuts : public Command + { + using Command::Command; + + virtual void OnCall() override + { + auto base = ScriptGlobal(1962078).At(812).At(50); + + *base.At(1).As() = _DoomsdayHeistCut1.GetState(); + *base.At(2).As() = _DoomsdayHeistCut2.GetState(); + *base.At(3).As() = _DoomsdayHeistCut3.GetState(); + *base.At(4).As() = _DoomsdayHeistCut4.GetState(); + } + }; + + class ForceReady : public Command + { + using Command::Command; + + virtual void OnCall() override + { + auto base = ScriptGlobal(1882925); + + *base.At(0, 149).At(43).At(0).At(4).At(8).As() = 1; + *base.At(1, 149).At(43).At(1).At(4).At(8).As() = 1; + *base.At(2, 149).At(43).At(2).At(4).At(8).As() = 1; + *base.At(3, 149).At(43).At(3).At(4).At(8).As() = 1; + } + }; + + static std::vector> doomsdayHeistCategory = { + {0, "The Data Breaches"}, + {1, "The Bogdan Problem"}, + {2, "The Doomsday Senario"} + }; + static ListCommand _DoomsdayHeistCategory{"doomsdayheistcategory", "Select Heist", "Heist categories", doomsdayHeistCategory, 0}; + + class Setup : public Command + { + using Command::Command; + + virtual void OnCall() override + { + switch (_DoomsdayHeistCategory.GetState()) + { + case 0: + Stats::SetInt("MPX_GANGOPS_FLOW_MISSION_PROG", 503); + Stats::SetInt("MPX_GANGOPS_HEIST_STATUS", -229383); + Stats::SetInt("MPX_GANGOPS_FLOW_NOTIFICATIONS", 1557); + break; + case 1: + Stats::SetInt("MPX_GANGOPS_FLOW_MISSION_PROG", 240); + Stats::SetInt("MPX_GANGOPS_HEIST_STATUS", -229378); + Stats::SetInt("MPX_GANGOPS_FLOW_NOTIFICATIONS", 1557); + break; + case 2: + Stats::SetInt("MPX_GANGOPS_FLOW_MISSION_PROG", 16368); + Stats::SetInt("MPX_GANGOPS_HEIST_STATUS", -229380); + Stats::SetInt("MPX_GANGOPS_FLOW_NOTIFICATIONS", 1557); + break; + default: break; + } + + ScriptMgr::Yield(500ms); + + *ScriptLocal("gb_gang_ops_planning"_J, 207).As() = 6; // Reload planning screen + } + }; + + class SkipHacking : public Command + { + using Command::Command; + + virtual void OnCall() override + { + *ScriptLocal("fm_mission_controller"_J, 1538).As() = 3; + *ScriptLocal("fm_mission_controller"_J, 1569).As() = 2; + *ScriptLocal("fm_mission_controller"_J, 1294).At(135).As() = 3; + } + }; + + class InstantFinish : public Command + { + using Command::Command; + + virtual void OnCall() override + { + Scripts::ForceScriptHost(Scripts::FindScriptThread("fm_mission_controller"_J)); + ScriptMgr::Yield(500ms); + + *ScriptLocal("fm_mission_controller"_J, 20387).At(1725).At(1).As() = 80; + *ScriptLocal("fm_mission_controller"_J, 20387).As() = 12; + *ScriptLocal("fm_mission_controller"_J, 29006).At(1).As() = 99999; + *ScriptLocal("fm_mission_controller"_J, 32462).At(1).At(68).As() = 99999; + + // TODO: find a way of getting current heist info so that InstantFinishAct3 can be implemented here conditionally. + } + }; + + class InstantFinishAct3 : public Command + { + using Command::Command; + + virtual void OnCall() override + { + Scripts::ForceScriptHost(Scripts::FindScriptThread("fm_mission_controller"_J)); + ScriptMgr::Yield(500ms); + + *ScriptLocal("fm_mission_controller"_J, 20387).As() = 12; + *ScriptLocal("fm_mission_controller"_J, 20387).At(1740).At(1).As() = 150; + *ScriptLocal("fm_mission_controller"_J, 20387).At(1062).As() = 5; + *ScriptLocal("fm_mission_controller"_J, 29006).At(1).As() = 99999; + *ScriptLocal("fm_mission_controller"_J, 32462).At(1).At(68).As() = 99999; + } + }; + + static SetCuts _DoomsdayHeistSetCuts{"doomsdayheistsetcuts", "Set Cuts", "Sets heist cut"}; + static ForceReady _DoomsdayHeistForceReady{"doomsdayheistforceready", "Force Ready", "Forces all players to be ready"}; + static Setup _DoomsdayHeistSetup{"doomsdayheistsetup", "Setup", "Sets up doomsday heist"}; + static SkipHacking _DoomsdayHeistSkipHacking{"doomsdayheistskiphacking", "Skip Hacking", "Skips hacking process"}; + static InstantFinish _DoomsdayHeistInstantFinish{"doomsdayheistinstantfinish", "Instant Finish", "Instantly passes the heist"}; + static InstantFinishAct3 _DoomsdayHeistInstantFinishAct3{"doomsdayheistinstantfinishact3", "Instant Finish (Act: III)", "Instantly passes Doomsday Scenario Act: III"}; + } +} \ No newline at end of file diff --git a/src/game/features/recovery/PlayAllMissionsSolo.cpp b/src/game/features/recovery/PlayAllMissionsSolo.cpp index 68c7e1c..c5c8001 100644 --- a/src/game/features/recovery/PlayAllMissionsSolo.cpp +++ b/src/game/features/recovery/PlayAllMissionsSolo.cpp @@ -32,62 +32,62 @@ namespace YimMenu::Features { if (!m_CanLaunchJobPatch) { - m_CanLaunchJobPatch = ScriptPatches::AddPatch("fmmc_launcher"_J, "2D 05 0C 00 00 71", 5, {0x72, 0x2E, 0x05, 0x01}); // the main patch that bypasses the not enough players alert + m_CanLaunchJobPatch = ScriptPatches::AddPatch("fmmc_launcher"_J, ScriptPointer("CanLaunchJobPatch", "2D 05 0C 00 00 71").Add(5), {0x72, 0x2E, 0x05, 0x01}); // the main patch that bypasses the not enough players alert } m_CanLaunchJobPatch->Enable(); // these patches allow the mission request system to launch fm_mission_controller with insufficent players if (!m_MissionRequestPatch1) { - m_MissionRequestPatch1 = ScriptPatches::AddPatch("freemode"_J, "38 00 72 5D ? ? ? 38 00 5D ? ? ? 2E 02 00 38 00 38 01", 0, std::vector(14, 0x0)); + m_MissionRequestPatch1 = ScriptPatches::AddPatch("freemode"_J, ScriptPointer("MissionRequestPatch1", "38 00 72 5D ? ? ? 38 00 5D ? ? ? 2E 02 00 38 00 38 01"), std::vector(14, 0x0)); } m_MissionRequestPatch1->Enable(); if (!m_MissionRequestPatch2) { - m_MissionRequestPatch2 = ScriptPatches::AddPatch("freemode"_J, "25 ? 37 07 33 37 07 5D ? ? ? 39 11", 20, {0x72}); + m_MissionRequestPatch2 = ScriptPatches::AddPatch("freemode"_J, ScriptPointer("MissionRequestPatch2", "25 ? 37 07 33 37 07 5D ? ? ? 39 11").Add(20), {0x72}); } m_MissionRequestPatch2->Enable(); if (!m_MissionRequestPatch3) { - m_MissionRequestPatch3 = ScriptPatches::AddPatch("freemode"_J, "2D 03 1A 00 00 38 00 5D ? ? ? 39 05", 5, {0x72, 0x2E, 0x03, 0x01}); + m_MissionRequestPatch3 = ScriptPatches::AddPatch("freemode"_J, ScriptPointer("MissionRequestPatch3", "2D 03 1A 00 00 38 00 5D ? ? ? 39 05").Add(5), {0x72, 0x2E, 0x03, 0x01}); } m_MissionRequestPatch3->Enable(); if (!m_MissionRequestPatch4) { - m_MissionRequestPatch4 = ScriptPatches::AddPatch("freemode"_J, "5D ? ? ? 39 06 38 01 5D ? ? ? 25", 0, {0x72, 0x2E, 0x04, 0x01}); + m_MissionRequestPatch4 = ScriptPatches::AddPatch("freemode"_J, ScriptPointer("MissionRequestPatch4", "5D ? ? ? 39 06 38 01 5D ? ? ? 25"), {0x72, 0x2E, 0x04, 0x01}); } m_MissionRequestPatch4->Enable(); if (!m_PlayerCountWatchdogPatch1) { - m_PlayerCountWatchdogPatch1 = ScriptPatches::AddPatch("fmmc_launcher"_J, "2D 02 09 00 00 25 A4", 5, {0x71, 0x2E, 0x02, 0x01}); // instant kick if this check fails + m_PlayerCountWatchdogPatch1 = ScriptPatches::AddPatch("fmmc_launcher"_J, ScriptPointer("PlayerCountWatchdogPatch1", "2D 02 09 00 00 25 A4").Add(5), {0x71, 0x2E, 0x02, 0x01}); // instant kick if this check fails } m_PlayerCountWatchdogPatch1->Enable(); if (!m_PlayerCountWatchdogPatch2) { - m_PlayerCountWatchdogPatch2 = ScriptPatches::AddPatch("fmmc_launcher"_J, "2D 04 0B 00 00 2C ? ? ? 71 5D", 5, {0x71, 0x2E, 0x04, 0x01}); + m_PlayerCountWatchdogPatch2 = ScriptPatches::AddPatch("fmmc_launcher"_J, ScriptPointer("PlayerCountWatchdogPatch2", "2D 04 0B 00 00 2C ? ? ? 71 5D").Add(5), {0x71, 0x2E, 0x04, 0x01}); } m_PlayerCountWatchdogPatch2->Enable(); if (!m_HeistTeamsPatch1) { - m_HeistTeamsPatch1 = ScriptPatches::AddPatch("fmmc_launcher"_J, "47 ? ? 5B 7B 00 38 04", 0, {0x2B, 0x2B, 0x00, 0x55}); // TODO: doesn't work for doomsday heist + m_HeistTeamsPatch1 = ScriptPatches::AddPatch("fmmc_launcher"_J, ScriptPointer("HeistTeamsPatch1", "47 ? ? 5B 7B 00 38 04"), {0x2B, 0x2B, 0x00, 0x55}); // TODO: doesn't work for doomsday heist } m_HeistTeamsPatch1->Enable(); if (!m_HeistTeamsPatch2) { - m_HeistTeamsPatch2 = ScriptPatches::AddPatch("fmmc_launcher"_J, "2D 01 05 00 00 25 5D", 5, {0x72, 0x2E, 0x01, 0x01}); + m_HeistTeamsPatch2 = ScriptPatches::AddPatch("fmmc_launcher"_J, ScriptPointer("HeistTeamsPatch2", "2D 01 05 00 00 25 5D").Add(5), {0x72, 0x2E, 0x01, 0x01}); } m_HeistTeamsPatch2->Enable(); if (!m_ShouldFailMissionPatch1) { - m_ShouldFailMissionPatch1 = ScriptPatches::AddPatch("fm_mission_controller"_J, "50 ? ? 78 82 06 1F 56 ? ? 38 02", 0, {0x00, 0x00, 0x00, 0x72, 0x00}); + m_ShouldFailMissionPatch1 = ScriptPatches::AddPatch("fm_mission_controller"_J, ScriptPointer("ShouldFailMissionPatch1", "50 ? ? 78 82 06 1F 56 ? ? 38 02"), {0x00, 0x00, 0x00, 0x72, 0x00}); } m_ShouldFailMissionPatch1->Enable(); @@ -96,7 +96,10 @@ namespace YimMenu::Features // TODO: this is a very bad idea that can break anytime // for some reason, Rockstar thought it's a good idea to copy-paste the same failure check in four different branches, "just in case" for (int i = 0; i < 4; i++) - m_ShouldFailMissionPatches.push_back(ScriptPatches::AddPatch("fm_mission_controller"_J, "38 02 5D ? ? ? 71 09 2A 56 07 00 71 5D ? ? ? 06 1F 56", 0, {0x71, 0x00, 0x00, 0x00, 0x00, 0x00})); + { + std::string ptrName = "ShouldFailMissionPatches" + std::to_string(i); + m_ShouldFailMissionPatches.push_back(ScriptPatches::AddPatch("fm_mission_controller"_J, ScriptPointer(ptrName, "38 02 5D ? ? ? 71 09 2A 56 07 00 71 5D ? ? ? 06 1F 56"), {0x71, 0x00, 0x00, 0x00, 0x00, 0x00})); + } } for (auto& patch : m_ShouldFailMissionPatches) @@ -104,37 +107,37 @@ namespace YimMenu::Features if (!m_ShouldFailMissionPatch2) { - m_ShouldFailMissionPatch2 = ScriptPatches::AddPatch("fm_mission_controller"_J, "56 07 00 38 02 5D ? ? ? 20 56 BC 01", 0, {0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x71}); // some heist check, more or less same as above + m_ShouldFailMissionPatch2 = ScriptPatches::AddPatch("fm_mission_controller"_J, ScriptPointer("ShouldFailMissionPatch2", "56 07 00 38 02 5D ? ? ? 20 56 BC 01"), {0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x71}); // some heist check, more or less same as above } m_ShouldFailMissionPatch2->Enable(); if (!m_ShouldFailMissionPatch3) { - m_ShouldFailMissionPatch3 = ScriptPatches::AddPatch("fm_mission_controller"_J, "50 ? ? 78 82 06 1F 56 2E 01", 0, {0x00, 0x00, 0x00, 0x00, 0x72}); + m_ShouldFailMissionPatch3 = ScriptPatches::AddPatch("fm_mission_controller"_J, ScriptPointer("ShouldFailMissionPatch3", "50 ? ? 78 82 06 1F 56 2E 01"), {0x00, 0x00, 0x00, 0x00, 0x72}); } m_ShouldFailMissionPatch3->Enable(); if (!m_NotEnoughTeamsPatch) { - m_NotEnoughTeamsPatch = ScriptPatches::AddPatch("fm_mission_controller"_J, "2D 00 02 00 00 61 ? ? ? 47 ? ? 72 57 04 00 72 2E 00 01 4F", 5, {0x71, 0x2E, 0x00, 0x01}); + m_NotEnoughTeamsPatch = ScriptPatches::AddPatch("fm_mission_controller"_J, ScriptPointer("NotEnoughTeamsPatch", "2D 00 02 00 00 61 ? ? ? 47 ? ? 72 57 04 00 72 2E 00 01 4F").Add(5), {0x71, 0x2E, 0x00, 0x01}); } m_NotEnoughTeamsPatch->Enable(); if (!m_IsTeamValidPatch) { - m_IsTeamValidPatch = ScriptPatches::AddPatch("fm_mission_controller"_J, "2D 01 03 00 00 38 00 71 57 1D 00", 5, {0x72, 0x2E, 0x01, 0x01}); + m_IsTeamValidPatch = ScriptPatches::AddPatch("fm_mission_controller"_J, ScriptPointer("IsTeamValidPatch", "2D 01 03 00 00 38 00 71 57 1D 00").Add(5), {0x72, 0x2E, 0x01, 0x01}); } m_IsTeamValidPatch->Enable(); if (!m_EnsureMissionPassedPatch) { - m_EnsureMissionPassedPatch = ScriptPatches::AddPatch("fm_mission_controller"_J, "55 C8 FF 61", 3, {0x2E, 0x00, 0x00}); // the game does one final check to ensure all players exist before the mission is passed + m_EnsureMissionPassedPatch = ScriptPatches::AddPatch("fm_mission_controller"_J, ScriptPointer("EnsureMissionPassedPatch", "55 C8 FF 61").Add(3), {0x2E, 0x00, 0x00}); // the game does one final check to ensure all players exist before the mission is passed } m_EnsureMissionPassedPatch->Enable(); if (!m_ProcessPhoneHackingPatch) { - m_ProcessPhoneHackingPatch = ScriptPatches::AddPatch("fm_mission_controller"_J, "58 13 00 38 00 4F ? ? 48", 0, {0x2B, 0x00, 0x00}); + m_ProcessPhoneHackingPatch = ScriptPatches::AddPatch("fm_mission_controller"_J, ScriptPointer("ProcessPhoneHackingPatch", "58 13 00 38 00 4F ? ? 48"), {0x2B, 0x00, 0x00}); } if (!m_QuickRestartPatch1) diff --git a/src/game/features/self/FormatMoney.cpp b/src/game/features/self/FormatMoney.cpp new file mode 100644 index 0000000..f816be4 --- /dev/null +++ b/src/game/features/self/FormatMoney.cpp @@ -0,0 +1,75 @@ +#include "core/commands/BoolCommand.hpp" +#include "core/hooking/CallHook.hpp" +#include "game/backend/NativeHooks.hpp" +#include "game/gta/Natives.hpp" +#include "game/gta/Stats.hpp" + +namespace YimMenu::Features +{ + static CallHook formatIntCaller1Hook{}; + static CallHook formatIntCaller2Hook{}; + static void FormatIntHook(int64_t integer, char* str, size_t strSize, bool insertCommas); + static void ScaleformAddIntHook(rage::scrNativeCallContext* ctx); + + class FormatMoney : public BoolCommand + { + using BoolCommand::BoolCommand; + + virtual void OnEnable() override + { + if (!formatIntCaller1Hook) + { + formatIntCaller1Hook = CallSiteHook::AddHook(Pointers.FormatIntCaller1, FormatIntHook); + } + formatIntCaller1Hook->Enable(); + if (!formatIntCaller2Hook) + { + formatIntCaller2Hook = CallSiteHook::AddHook(Pointers.FormatIntCaller2, FormatIntHook); + } + formatIntCaller2Hook->Enable(); + NativeHooks::AddHook("shop_controller"_J, NativeIndex::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT, &ScaleformAddIntHook); + } + + virtual void OnDisable() override + { + if (formatIntCaller1Hook) + { + formatIntCaller1Hook->Disable(); + } + if (formatIntCaller2Hook) + { + formatIntCaller2Hook->Disable(); + } + } + }; + + static FormatMoney _FormatMoney{"formatmoney", "Format Money", "Formats HUD money with commas."}; + + static void FormatIntHook(int64_t integer, char* str, size_t strSize, bool insertCommas) + { + insertCommas = true; + + auto original = formatIntCaller1Hook->GetOriginal(); + original(integer, str, strSize, insertCommas); + } + + static void ScaleformAddIntHook(rage::scrNativeCallContext* ctx) + { + int value = ctx->GetArg(0); + if (_FormatMoney.GetState()) + { + int chips = Stats::GetInt("MPX_CASINO_CHIPS"); + if (value == chips && chips >= 1000) + { + auto formatted = std::to_string(value); + for (int i = formatted.size() - 3; i > 0; i -= 3) + { + formatted.insert(i, ","); + } + return GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_PLAYER_NAME_STRING(formatted.c_str()); + } + } + + GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(value); + } +} \ No newline at end of file diff --git a/src/game/features/self/mobile_radio.cpp b/src/game/features/self/mobile_radio.cpp new file mode 100644 index 0000000..1a2103d --- /dev/null +++ b/src/game/features/self/mobile_radio.cpp @@ -0,0 +1,26 @@ +#include "core/commands/LoopedCommand.hpp" +#include "game/backend/Self.hpp" +#include "game/gta/Natives.hpp" + +namespace YimMenu::Features +{ + class MobileRadio : public LoopedCommand + { + using LoopedCommand::LoopedCommand; + + + virtual void OnTick() override + { + AUDIO::SET_MOBILE_PHONE_RADIO_STATE(true); + AUDIO::SET_MOBILE_RADIO_ENABLED_DURING_GAMEPLAY(true); + } + + virtual void OnDisable() override + { + AUDIO::SET_MOBILE_PHONE_RADIO_STATE(false); + AUDIO::SET_MOBILE_RADIO_ENABLED_DURING_GAMEPLAY(false); + } + }; + + static MobileRadio _MobileRadio{"mobileradio", "listen any radio station on foot", "Allows you to listen to any radio station on foot with Hotkey Q"}; +} diff --git a/src/game/features/system/DespawnBypass.cpp b/src/game/features/system/DespawnBypass.cpp index 9edc831..9035ac3 100644 --- a/src/game/features/system/DespawnBypass.cpp +++ b/src/game/features/system/DespawnBypass.cpp @@ -13,7 +13,7 @@ namespace YimMenu::Features { if (!m_ShopPatch) { - m_ShopPatch = ScriptPatches::AddPatch("shop_controller"_J, "2D 01 04 00 00 2C ? ? ? 56 ? ? 71", 5, {0x71, 0x2E, 0x01, 0x01}); // disable the kick out subroutine + m_ShopPatch = ScriptPatches::AddPatch("shop_controller"_J, ScriptPointer("ShopPatch", "2D 01 04 00 00 2C ? ? ? 56 ? ? 71").Add(5), {0x71, 0x2E, 0x01, 0x01}); // disable the kick out subroutine } m_ShopPatch->Enable(); } diff --git a/src/game/features/system/LSCCustomsBypass.cpp b/src/game/features/system/LSCCustomsBypass.cpp index b3615ad..ef9103e 100644 --- a/src/game/features/system/LSCCustomsBypass.cpp +++ b/src/game/features/system/LSCCustomsBypass.cpp @@ -15,13 +15,13 @@ namespace YimMenu::Features { if (!m_CanUseVehiclePatch) { - m_CanUseVehiclePatch = ScriptPatches::AddPatch("carmod_shop"_J, "2D ? ? ? ? 38 ? 5D ? ? ? 56 ? ? 71 2E ? ? 5D", 5, {0x72, 0x2E, 0x03, 0x01}); + m_CanUseVehiclePatch = ScriptPatches::AddPatch("carmod_shop"_J, ScriptPointer("CanUseVehiclePatch", "2D ? ? ? ? 38 ? 5D ? ? ? 56 ? ? 71 2E ? ? 5D").Add(5), {0x72, 0x2E, 0x03, 0x01}); } m_CanUseVehiclePatch->Enable(); if (!m_BlockMenuOptionPatch) { - m_BlockMenuOptionPatch = ScriptPatches::AddPatch("carmod_shop"_J, "2D ? ? ? ? 38 ? 5D ? ? ? 5D ? ? ? 56", 5, {0x71, 0x2E, 0x01, 0x01}); + m_BlockMenuOptionPatch = ScriptPatches::AddPatch("carmod_shop"_J, ScriptPointer("BlockMenuOptionPatch", "2D ? ? ? ? 38 ? 5D ? ? ? 5D ? ? ? 56").Add(5), {0x71, 0x2E, 0x01, 0x01}); } m_BlockMenuOptionPatch->Enable(); } diff --git a/src/game/features/teleport/TpToWaypoint.cpp b/src/game/features/teleport/TpToWaypoint.cpp index 153b604..9271dc3 100644 --- a/src/game/features/teleport/TpToWaypoint.cpp +++ b/src/game/features/teleport/TpToWaypoint.cpp @@ -75,7 +75,6 @@ namespace YimMenu::Features auto new_coords{coords}; ResolveZCoordinate(new_coords); Self::GetPed().TeleportTo(new_coords); - ScriptMgr::Yield(); }); HUD::SET_WAYPOINT_OFF(); } diff --git a/src/game/features/world/Weather.cpp b/src/game/features/world/Weather.cpp index 54e61ef..15a7ef7 100644 --- a/src/game/features/world/Weather.cpp +++ b/src/game/features/world/Weather.cpp @@ -105,7 +105,7 @@ namespace YimMenu::Features // ensure shop controller can't control the weather if (!m_ShopControllerWeatherPatch) { - m_ShopControllerWeatherPatch = ScriptPatches::AddPatch("shop_controller"_J, "2D 00 07 00 00 71 39 02 71", 5, {0x2E, 0x00, 0x00}); + m_ShopControllerWeatherPatch = ScriptPatches::AddPatch("shop_controller"_J, ScriptPointer("ShopControllerWeatherPatch", "2D 00 07 00 00 71 39 02 71").Add(5), {0x2E, 0x00, 0x00}); } m_ShopControllerWeatherPatch->Enable(); } diff --git a/src/game/frontend/ESP.cpp b/src/game/frontend/ESP.cpp index edcef66..321697b 100644 --- a/src/game/frontend/ESP.cpp +++ b/src/game/frontend/ESP.cpp @@ -10,8 +10,12 @@ #include "game/gta/Pools.hpp" #include "game/gta/Scripts.hpp" //#include "game/gta/data/PedModels.hpp" +#include "game/gta/data/RandomEvents.hpp" #include "game/gta/invoker/Invoker.hpp" #include "game/gta/Natives.hpp" +#include "types/script/globals/GPBD_FM_2.hpp" +#include "types/script/globals/GSBD_RandomEvents.hpp" +#include "types/script/locals/FMRandomEvents.hpp" namespace { @@ -58,6 +62,9 @@ namespace YimMenu::Features ColorCommand _HashColorPeds("hashcolorpeds", "Ped Hash Color", "Changes the color of the hash ESP for peds", ImVec4{1.0f, 1.0f, 1.0f, 1.0f}); ColorCommand _DistanceColorPeds("distancecolorpeds", "Ped Distance Color", "Changes the color of the distance ESP for peds", ImVec4{1.0f, 1.0f, 1.0f, 1.0f}); ColorCommand _SkeletonColorPeds("skeletoncolorpeds", "Ped Skeleton Color", "Changes the color of the skeleton ESP for peds", ImVec4{1.0f, 1.0f, 1.0f, 1.0f}); + + // Random Events + BoolCommand _ESPDrawRandomEvents("esprandomevents", "Random Events ESP", "Should the ESP draw Random Events?"); } namespace YimMenu @@ -75,42 +82,43 @@ namespace YimMenu static ImVec4 Orange = ImVec4(0.69f, 0.49f, 0.29f, 1.f); static ImVec4 Red = ImVec4(0.69f, 0.29f, 0.29f, 1.f); static ImVec4 Blue = ImVec4(0.36f, 0.71f, 0.89f, 1.f); + static ImVec4 White = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); - static auto boneToScreen = [](rage::fvector3 bone) + static auto worldToScreen = [](rage::fvector3 coords) { float screen_x{}, screen_y{}; - GRAPHICS::GET_SCREEN_COORD_FROM_WORLD_COORD(bone.x, bone.y, bone.z, &screen_x, &screen_y); + GRAPHICS::GET_SCREEN_COORD_FROM_WORLD_COORD(coords.x, coords.y, coords.z, &screen_x, &screen_y); return ImVec2{screen_x * (*Pointers.ScreenResX), screen_y * (*Pointers.ScreenResY)}; }; void DrawSkeleton(Ped ped, ImDrawList* drawList, ImColor color) { - drawList->AddLine(boneToScreen(ped.GetBonePosition(headBone)), boneToScreen(ped.GetBonePosition(neckBone)), color, 1.5f); + drawList->AddLine(worldToScreen(ped.GetBonePosition(headBone)), worldToScreen(ped.GetBonePosition(neckBone)), color, 1.5f); - drawList->AddLine(boneToScreen(ped.GetBonePosition(neckBone)), boneToScreen(ped.GetBonePosition(leftShoulderBone)), color, 1.5f); - drawList->AddLine(boneToScreen(ped.GetBonePosition(leftShoulderBone)), boneToScreen(ped.GetBonePosition(leftElbowBone)), color, 1.5f); - drawList->AddLine(boneToScreen(ped.GetBonePosition(leftElbowBone)), boneToScreen(ped.GetBonePosition(leftHandBone)), color, 1.5f); + drawList->AddLine(worldToScreen(ped.GetBonePosition(neckBone)), worldToScreen(ped.GetBonePosition(leftShoulderBone)), color, 1.5f); + drawList->AddLine(worldToScreen(ped.GetBonePosition(leftShoulderBone)), worldToScreen(ped.GetBonePosition(leftElbowBone)), color, 1.5f); + drawList->AddLine(worldToScreen(ped.GetBonePosition(leftElbowBone)), worldToScreen(ped.GetBonePosition(leftHandBone)), color, 1.5f); - drawList->AddLine(boneToScreen(ped.GetBonePosition(neckBone)), boneToScreen(ped.GetBonePosition(rightShoulderBone)), color, 1.5f); - drawList->AddLine(boneToScreen(ped.GetBonePosition(rightShoulderBone)), boneToScreen(ped.GetBonePosition(rightElbowBone)), color, 1.5f); - drawList->AddLine(boneToScreen(ped.GetBonePosition(rightElbowBone)), boneToScreen(ped.GetBonePosition(rightHandBone)), color, 1.5f); + drawList->AddLine(worldToScreen(ped.GetBonePosition(neckBone)), worldToScreen(ped.GetBonePosition(rightShoulderBone)), color, 1.5f); + drawList->AddLine(worldToScreen(ped.GetBonePosition(rightShoulderBone)), worldToScreen(ped.GetBonePosition(rightElbowBone)), color, 1.5f); + drawList->AddLine(worldToScreen(ped.GetBonePosition(rightElbowBone)), worldToScreen(ped.GetBonePosition(rightHandBone)), color, 1.5f); - drawList->AddLine(boneToScreen(ped.GetBonePosition(neckBone)), boneToScreen(ped.GetBonePosition(torsoBone)), color, 1.5f); + drawList->AddLine(worldToScreen(ped.GetBonePosition(neckBone)), worldToScreen(ped.GetBonePosition(torsoBone)), color, 1.5f); - drawList->AddLine(boneToScreen(ped.GetBonePosition(torsoBone)), boneToScreen(ped.GetBonePosition(leftKneeBone)), color, 1.5f); - drawList->AddLine(boneToScreen(ped.GetBonePosition(leftKneeBone)), boneToScreen(ped.GetBonePosition(leftFootBone)), color, 1.5f); + drawList->AddLine(worldToScreen(ped.GetBonePosition(torsoBone)), worldToScreen(ped.GetBonePosition(leftKneeBone)), color, 1.5f); + drawList->AddLine(worldToScreen(ped.GetBonePosition(leftKneeBone)), worldToScreen(ped.GetBonePosition(leftFootBone)), color, 1.5f); - drawList->AddLine(boneToScreen(ped.GetBonePosition(torsoBone)), boneToScreen(ped.GetBonePosition(rightKneeBone)), color, 1.5f); - drawList->AddLine(boneToScreen(ped.GetBonePosition(rightKneeBone)), boneToScreen(ped.GetBonePosition(rightFootBone)), color, 1.5f); + drawList->AddLine(worldToScreen(ped.GetBonePosition(torsoBone)), worldToScreen(ped.GetBonePosition(rightKneeBone)), color, 1.5f); + drawList->AddLine(worldToScreen(ped.GetBonePosition(rightKneeBone)), worldToScreen(ped.GetBonePosition(rightFootBone)), color, 1.5f); } //TODO : Very bare bones currently, expand and possibly refactor void ESP::DrawPlayer(Player plyr, ImDrawList* drawList) { if (!plyr.IsValid() || !plyr.GetPed().IsValid() || plyr == Self::GetPlayer() - || boneToScreen(plyr.GetPed().GetBonePosition(torsoBone)).x == 0 + || worldToScreen(plyr.GetPed().GetBonePosition(torsoBone)).x == 0 || (plyr.GetPed().IsDead() && !Features::_ESPDrawDeadPlayers.GetState())) return; @@ -132,7 +140,7 @@ namespace YimMenu if (Features::_ESPName.GetState()) { - drawList->AddText(boneToScreen(plyr.GetPed().GetBonePosition(headBone)), + drawList->AddText(worldToScreen(plyr.GetPed().GetBonePosition(headBone)), plyr == Players::GetSelected() ? ImGui::ColorConvertFloat4ToU32(Blue) : ImGui::ColorConvertFloat4ToU32(Features::_NameColorPlayers.GetState()), plyr.GetName()); @@ -141,7 +149,7 @@ namespace YimMenu if (Features::_ESPDistance.GetState()) { std::string distanceStr = std::to_string((int)Self::GetPed().GetPosition().GetDistance(plyr.GetPed().GetBonePosition(torsoBone))) + "m"; - drawList->AddText({boneToScreen(plyr.GetPed().GetBonePosition(headBone)).x, boneToScreen(plyr.GetPed().GetBonePosition(headBone)).y + 20}, colorBasedOnDistance, distanceStr.c_str()); + drawList->AddText({worldToScreen(plyr.GetPed().GetBonePosition(headBone)).x, worldToScreen(plyr.GetPed().GetBonePosition(headBone)).y + 20}, colorBasedOnDistance, distanceStr.c_str()); } currentFont->Scale = originalFontSize; @@ -160,7 +168,7 @@ namespace YimMenu void ESP::DrawPed(Ped ped, ImDrawList* drawList) { - if (!ped.IsValid() || ped.IsPlayer() || ped == Self::GetPlayer().GetPed() || boneToScreen(ped.GetBonePosition(torsoBone)).x == 0 || (ped.IsDead() && !Features::_ESPDrawDeadPeds.GetState())) + if (!ped.IsValid() || ped.IsPlayer() || ped == Self::GetPlayer().GetPed() || worldToScreen(ped.GetBonePosition(torsoBone)).x == 0 || (ped.IsDead() && !Features::_ESPDrawDeadPeds.GetState())) return; float distanceToPed = 0.0f; @@ -210,13 +218,13 @@ namespace YimMenu } if (!info.empty()) - drawList->AddText(boneToScreen(ped.GetBonePosition(headBone)), ImGui::ColorConvertFloat4ToU32(Features::_HashColorPeds.GetState()), info.c_str()); + drawList->AddText(worldToScreen(ped.GetBonePosition(headBone)), ImGui::ColorConvertFloat4ToU32(Features::_HashColorPeds.GetState()), info.c_str()); if (Features::_ESPDistancePeds.GetState()) { std::string distanceStr = std::to_string((int)distanceToPed) + "m"; drawList->AddText( - {boneToScreen(ped.GetBonePosition(headBone)).x, boneToScreen(ped.GetBonePosition(headBone)).y + 20}, + {worldToScreen(ped.GetBonePosition(headBone)).x, worldToScreen(ped.GetBonePosition(headBone)).y + 20}, colorBasedOnDistance, distanceStr.c_str()); } @@ -235,6 +243,32 @@ namespace YimMenu } } + void ESP::DrawRandomEvent(int event, ImDrawList* drawList) + { + if (SCRIPT::GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH("freemode"_J) == 0 || HUD::IS_PAUSE_MENU_ACTIVE() || NETWORK::NETWORK_IS_IN_MP_CUTSCENE()) + return; + + if (GPBD_FM_2::Get()->Entries[Self::GetPlayer().GetId()].RandomEventsClientData.InitState != eRandomEventClientInitState::INITIALIZED) + return; + + auto state = GSBD_RandomEvents::Get()->EventData[event].State; + auto coords = GSBD_RandomEvents::Get()->EventData[event].TriggerPosition; + auto range = GSBD_RandomEvents::Get()->EventData[event].TriggerRange; + auto timer = GSBD_RandomEvents::Get()->EventData[event].TimerState; + auto availability = RANDOM_EVENTS_FREEMODE_DATA::Get(Scripts::FindScriptThread("freemode"_J))->EventData[event].AvailableTime; + auto timeLeft = GSBD_RandomEvents::Get()->EventData[event].TimerState.GetRemainingTimeStr(availability); + if (state != eRandomEventState::INACTIVE && coords != Vector3(0.0f, 0.0f, 0.0f)) + { + float distance = Self::GetPed().GetPosition().GetDistance(coords); + float formattedDistance = (distance < 1000.0f) ? distance : (distance / 1000.0f); + std::string unit = (distance < 1000.0f) ? "m" : "km"; + std::string text = std::format("{}\n{:.2f}{} {}", randomEventNames[event], formattedDistance, unit, (state == eRandomEventState::AVAILABLE ? timeLeft : "")); + ImColor color = (state == eRandomEventState::ACTIVE) ? Green : White; + + drawList->AddText({worldToScreen(coords).x, worldToScreen(coords).y}, color, text.c_str()); + } + } + void ESP::Draw() { if (!NativeInvoker::AreHandlersCached() || CAM::IS_SCREEN_FADED_OUT() || HUD::IS_WARNING_MESSAGE_ACTIVE()) @@ -257,6 +291,13 @@ namespace YimMenu DrawPed(ped, drawList); } } + if (Features::_ESPDrawRandomEvents.GetState()) + { + for (int event = 0; event < 21; event++) + { + DrawRandomEvent(event, drawList); + } + } } } diff --git a/src/game/frontend/ESP.hpp b/src/game/frontend/ESP.hpp index 14cfbba..823cadb 100644 --- a/src/game/frontend/ESP.hpp +++ b/src/game/frontend/ESP.hpp @@ -9,5 +9,6 @@ namespace YimMenu static void Draw(); static void DrawPlayer(Player plyr, ImDrawList* draw_list); static void DrawPed(Ped plyr, ImDrawList* drawList); + static void DrawRandomEvent(int event, ImDrawList* drawList); }; } \ No newline at end of file diff --git a/src/game/frontend/Menu.cpp b/src/game/frontend/Menu.cpp index dd92b60..f392acc 100644 --- a/src/game/frontend/Menu.cpp +++ b/src/game/frontend/Menu.cpp @@ -1,5 +1,6 @@ #include "Menu.hpp" - +#include "imgui.h" +#include "imgui_internal.h" #include "core/commands/Commands.hpp" #include "core/frontend/manager/UIManager.hpp" #include "core/renderer/Renderer.hpp" @@ -15,7 +16,7 @@ #include "submenus/Settings.hpp" #include "submenus/Debug.hpp" #include "submenus/World.hpp" - +#include "core/filemgr/FileMgr.hpp" namespace YimMenu { void Menu::Init() @@ -110,7 +111,11 @@ namespace YimMenu void Menu::SetupFonts() { - auto& IO = ImGui::GetIO(); + auto& IO = ImGui::GetIO(); + auto file_path = std::filesystem::path(std::getenv("appdata")) / "YimMenuV2" / "imgui.ini"; + static auto path = file_path.string(); + IO.IniFilename = path.c_str(); + IO.LogFilename = NULL; ImFontConfig FontCfg{}; FontCfg.FontDataOwnedByAtlas = false; diff --git a/src/game/frontend/items/Items.hpp b/src/game/frontend/items/Items.hpp index 34f1b67..bd15723 100644 --- a/src/game/frontend/items/Items.hpp +++ b/src/game/frontend/items/Items.hpp @@ -197,4 +197,20 @@ namespace YimMenu std::optional m_LabelOverride; std::optional m_CurrentInput = std::nullopt; }; + + class TabItem : public UIItem + { + public: + explicit TabItem(const std::string& name); + void Draw() override; + + void AddItem(std::shared_ptr&& item) + { + m_Items.push_back(std::move(item)); + } + + private: + std::string m_Name; + std::vector> m_Items; + }; } \ No newline at end of file diff --git a/src/game/frontend/items/TabItem.cpp b/src/game/frontend/items/TabItem.cpp new file mode 100644 index 0000000..427f47b --- /dev/null +++ b/src/game/frontend/items/TabItem.cpp @@ -0,0 +1,22 @@ +#include "Items.hpp" + +namespace YimMenu +{ + TabItem::TabItem(const std::string& name) : + m_Name(name) + { + } + + void TabItem::Draw() + { + if (ImGui::BeginTabItem(m_Name.c_str())) + { + for (auto& item : m_Items) + { + if (item->CanDraw()) + item->Draw(); + } + ImGui::EndTabItem(); + } + } +} diff --git a/src/game/frontend/submenus/Debug/Misc.cpp b/src/game/frontend/submenus/Debug/Misc.cpp index 9a1c118..dd098ca 100644 --- a/src/game/frontend/submenus/Debug/Misc.cpp +++ b/src/game/frontend/submenus/Debug/Misc.cpp @@ -18,7 +18,7 @@ namespace YimMenu::Submenus if (ImGui::Button("fm_mission_controller DoTeamSwap")) { FiberPool::Push([] { - static ScriptFunction DoTeamSwap("DoTeamSwap", "fm_mission_controller"_J, "2D 02 04 00 00 38 00 50"); + static ScriptFunction DoTeamSwap("fm_mission_controller"_J, ScriptPointer("DoTeamSwap", "2D 02 04 00 00 38 00 50")); DoTeamSwap.Call(team, true); }); } diff --git a/src/game/frontend/submenus/Network.cpp b/src/game/frontend/submenus/Network.cpp index 9bb0f96..b521f35 100644 --- a/src/game/frontend/submenus/Network.cpp +++ b/src/game/frontend/submenus/Network.cpp @@ -4,7 +4,7 @@ #include "game/frontend/items/Items.hpp" #include "game/frontend/submenus/Network/SavedPlayers.hpp" #include "game/gta/Network.hpp" -#include "game/frontend/submenus/network/RandomEvents.hpp" +#include "game/frontend/submenus/Network/RandomEvents.hpp" namespace YimMenu::Submenus { @@ -78,6 +78,7 @@ namespace YimMenu::Submenus enhancements->AddItem(std::make_shared("disabledeathbarriers"_J)); enhancements->AddItem(std::make_shared("despawnbypass"_J)); // move this somewhere else? enhancements->AddItem(std::make_shared("bypasscasinogeoblock"_J)); + enhancements->AddItem(std::make_shared("forcescripthost"_J)); session->AddItem(joinGroup); session->AddItem(bountyGroup); diff --git a/src/game/frontend/submenus/Recovery.cpp b/src/game/frontend/submenus/Recovery.cpp index 481b90e..60e76fc 100644 --- a/src/game/frontend/submenus/Recovery.cpp +++ b/src/game/frontend/submenus/Recovery.cpp @@ -3,6 +3,7 @@ #include "game/features/recovery/GiveVehicleReward.hpp" #include "Recovery/StatEditor.hpp" #include "Recovery/Transactions.hpp" +#include "Recovery/HeistModifier.hpp" namespace YimMenu::Submenus { @@ -13,7 +14,6 @@ namespace YimMenu::Submenus auto missions = std::make_shared("Missions"); auto vehiclesGroup = std::make_shared("Vehicles"); auto generalGroup = std::make_shared("General"); - auto cayoPericoGroup = std::make_shared("Cayo Perico"); vehiclesGroup->AddItem(std::make_shared("dlcvehicles"_J)); @@ -30,15 +30,12 @@ namespace YimMenu::Submenus generalGroup->AddItem(std::make_shared("playallmissionssolo"_J)); generalGroup->AddItem(std::make_shared("forcelaunchheist"_J)); - cayoPericoGroup->AddItem(std::make_shared("skipcayosetup"_J)); - cayoPericoGroup->AddItem(std::make_shared("skipcayocooldown"_J)); - shopping->AddItem(vehiclesGroup); missions->AddItem(generalGroup); - missions->AddItem(cayoPericoGroup); AddCategory(std::move(shopping)); AddCategory(std::move(missions)); AddCategory(BuildStatEditorMenu()); AddCategory(BuildTransactionsMenu()); + AddCategory(BuildHeistModifierMenu()); } } \ No newline at end of file diff --git a/src/game/frontend/submenus/Recovery/Heist/ApartmentHeist.cpp b/src/game/frontend/submenus/Recovery/Heist/ApartmentHeist.cpp new file mode 100644 index 0000000..309cddd --- /dev/null +++ b/src/game/frontend/submenus/Recovery/Heist/ApartmentHeist.cpp @@ -0,0 +1,33 @@ +#include "ApartmentHeist.hpp" + +namespace YimMenu::Submenus +{ + std::shared_ptr RenderApartmentHeistMenu() + { + auto tab = std::make_shared("Apartment Heist"); + + auto cuts = std::make_shared("Heist Cuts", 2); + auto setups = std::make_shared("Heist Setups"); + auto misc = std::make_shared("Misc", 1); + + cuts->AddItem(std::make_shared("apartmentheistcut1"_J)); + cuts->AddItem(std::make_shared("apartmentheistcut3"_J)); + cuts->AddItem(std::make_shared("apartmentheistcut2"_J)); + cuts->AddItem(std::make_shared("apartmentheistcut4"_J)); + cuts->AddItem(std::make_shared("apartmentheistforceready"_J)); + cuts->AddItem(std::make_shared("apartmentheistsetcuts"_J)); + + setups->AddItem(std::make_shared("apartmentheistsetup"_J)); + + misc->AddItem(std::make_shared("apartmentheistskiphacking"_J)); + misc->AddItem(std::make_shared("apartmentheistskipdrilling"_J)); + misc->AddItem(std::make_shared("apartmentheistinstantfinish"_J)); + misc->AddItem(std::make_shared("apartmentheistinstantfinishpacific"_J)); + + tab->AddItem(cuts); + tab->AddItem(setups); + tab->AddItem(misc); + + return tab; + } +} \ No newline at end of file diff --git a/src/game/frontend/submenus/Recovery/Heist/ApartmentHeist.hpp b/src/game/frontend/submenus/Recovery/Heist/ApartmentHeist.hpp new file mode 100644 index 0000000..7de2104 --- /dev/null +++ b/src/game/frontend/submenus/Recovery/Heist/ApartmentHeist.hpp @@ -0,0 +1,6 @@ +#include "game/frontend/submenus/Recovery/HeistModifier.hpp" + +namespace YimMenu::Submenus +{ + std::shared_ptr RenderApartmentHeistMenu(); +} diff --git a/src/game/frontend/submenus/Recovery/Heist/CayoPericoHeist.cpp b/src/game/frontend/submenus/Recovery/Heist/CayoPericoHeist.cpp new file mode 100644 index 0000000..471059f --- /dev/null +++ b/src/game/frontend/submenus/Recovery/Heist/CayoPericoHeist.cpp @@ -0,0 +1,50 @@ +#include "CayoPericoHeist.hpp" + +namespace YimMenu::Submenus +{ + std::shared_ptr RenderCayoPericoHeistMenu() + { + auto tab = std::make_shared("Cayo Perico Heist"); + + auto cuts = std::make_shared("Heist Cuts", 2); + auto setups = std::make_shared("Heist Setups"); + auto loots = std::make_shared("Loots"); + auto misc = std::make_shared("Misc", 1); + + cuts->AddItem(std::make_shared("cayopericoheistcut1"_J)); + cuts->AddItem(std::make_shared("cayopericoheistcut3"_J)); + cuts->AddItem(std::make_shared("cayopericoheistcut2"_J)); + cuts->AddItem(std::make_shared("cayopericoheistcut4"_J)); + cuts->AddItem(std::make_shared("cayopericoheistforceready"_J)); + cuts->AddItem(std::make_shared("cayopericoheistsetcuts"_J)); + + setups->AddItem(std::make_shared("cayopericoheistdifficulty"_J)); + setups->AddItem(std::make_shared("cayopericoheistprimarytarget"_J)); + setups->AddItem(std::make_shared("cayopericoheistweapon"_J)); + setups->AddItem(std::make_shared("cayopericoheistsetup"_J)); + + loots->AddItem(std::make_shared("cayopericoheistprimarytargetvalue"_J)); + loots->AddItem(std::make_shared([] { + ImGui::SameLine(); + })); + loots->AddItem(std::make_shared("cayopericoheistsetprimarytargetvalue"_J)); + loots->AddItem(std::make_shared("cayopericoheistsecondarytakevalue"_J)); + loots->AddItem(std::make_shared([] { + ImGui::SameLine(); + })); + loots->AddItem(std::make_shared("cayopericoheistsetsecondarytakevalue"_J)); + + misc->AddItem(std::make_shared("cayopericoheistskiphacking"_J)); + misc->AddItem(std::make_shared("cayopericoheistcutsewer"_J)); + misc->AddItem(std::make_shared("cayopericoheistcutglass"_J)); + misc->AddItem(std::make_shared("cayopericoheisttakeprimarytarget"_J)); + misc->AddItem(std::make_shared("cayopericoheistinstantfinish"_J)); + + tab->AddItem(cuts); + tab->AddItem(setups); + tab->AddItem(loots); + tab->AddItem(misc); + + return tab; + } +} \ No newline at end of file diff --git a/src/game/frontend/submenus/Recovery/Heist/CayoPericoHeist.hpp b/src/game/frontend/submenus/Recovery/Heist/CayoPericoHeist.hpp new file mode 100644 index 0000000..59dddfa --- /dev/null +++ b/src/game/frontend/submenus/Recovery/Heist/CayoPericoHeist.hpp @@ -0,0 +1,6 @@ +#include "game/frontend/submenus/Recovery/HeistModifier.hpp" + +namespace YimMenu::Submenus +{ + std::shared_ptr RenderCayoPericoHeistMenu(); +} diff --git a/src/game/frontend/submenus/Recovery/Heist/DiamondCasinoHeist.cpp b/src/game/frontend/submenus/Recovery/Heist/DiamondCasinoHeist.cpp new file mode 100644 index 0000000..3ef6b85 --- /dev/null +++ b/src/game/frontend/submenus/Recovery/Heist/DiamondCasinoHeist.cpp @@ -0,0 +1,61 @@ +#include "DiamondCasinoHeist.hpp" + +namespace YimMenu::Submenus +{ + std::shared_ptr RenderDiamondCasinoHeistMenu() + { + auto tab = std::make_shared("Diamond Casino Heist"); + + auto cuts = std::make_shared("Heist Cuts", 2); + auto setups1 = std::make_shared("Heist Setups"); + auto setups2 = std::make_shared(""); + auto loots = std::make_shared("Loots"); + auto misc = std::make_shared("Misc", 1); + + cuts->AddItem(std::make_shared("diamondcasinoheistcut1"_J)); + cuts->AddItem(std::make_shared("diamondcasinoheistcut3"_J)); + cuts->AddItem(std::make_shared("diamondcasinoheistcut2"_J)); + cuts->AddItem(std::make_shared("diamondcasinoheistcut4"_J)); + cuts->AddItem(std::make_shared("diamondcasinoheistforceready"_J)); + cuts->AddItem(std::make_shared("diamondcasinoheistsetcuts"_J)); + + setups1->AddItem(std::make_shared("diamondcasinoheistdifficulty"_J)); + setups1->AddItem(std::make_shared("diamondcasinoheistprimarytarget"_J)); + setups1->AddItem(std::make_shared("diamondcasinoheistapproach"_J)); + setups1->AddItem(std::make_shared("diamondcasinoheistgunman"_J)); + setups1->AddItem(std::make_shared([] { + ImGui::SameLine(); + })); + setups1->AddItem(std::make_shared("diamondcasinoheistweapon"_J)); + setups2->AddItem(std::make_shared("diamondcasinoheistdriver"_J)); + setups2->AddItem(std::make_shared([] { + ImGui::SameLine(); + })); + setups2->AddItem(std::make_shared("diamondcasinoheistvehicle"_J)); + setups2->AddItem(std::make_shared("diamondcasinoheisthacker"_J)); + setups2->AddItem(std::make_shared("diamondcasinoheistsetup"_J)); + + loots->AddItem(std::make_shared("diamondcasinoheistpotentialtake"_J)); + loots->AddItem(std::make_shared([] { + ImGui::SameLine(); + })); + loots->AddItem(std::make_shared("diamondcasinoheistsetpotentialtake"_J)); + loots->AddItem(std::make_shared("diamondcasinoheistactualtake"_J)); + loots->AddItem(std::make_shared([] { + ImGui::SameLine(); + })); + loots->AddItem(std::make_shared("diamondcasinoheistsetactualtake"_J)); + + misc->AddItem(std::make_shared("diamondcasinoheistskiphacking"_J)); + misc->AddItem(std::make_shared("diamondcasinoheistskipdrilling"_J)); + misc->AddItem(std::make_shared("diamondcasinoheistinstantfinish"_J)); + + tab->AddItem(cuts); + tab->AddItem(setups1); + tab->AddItem(setups2); + tab->AddItem(loots); + tab->AddItem(misc); + + return tab; + } +} \ No newline at end of file diff --git a/src/game/frontend/submenus/Recovery/Heist/DiamondCasinoHeist.hpp b/src/game/frontend/submenus/Recovery/Heist/DiamondCasinoHeist.hpp new file mode 100644 index 0000000..7394e33 --- /dev/null +++ b/src/game/frontend/submenus/Recovery/Heist/DiamondCasinoHeist.hpp @@ -0,0 +1,6 @@ +#include "game/frontend/submenus/Recovery/HeistModifier.hpp" + +namespace YimMenu::Submenus +{ + std::shared_ptr RenderDiamondCasinoHeistMenu(); +} diff --git a/src/game/frontend/submenus/Recovery/Heist/DoomsdayHeist.cpp b/src/game/frontend/submenus/Recovery/Heist/DoomsdayHeist.cpp new file mode 100644 index 0000000..39d40a4 --- /dev/null +++ b/src/game/frontend/submenus/Recovery/Heist/DoomsdayHeist.cpp @@ -0,0 +1,33 @@ +#include "DoomsdayHeist.hpp" + +namespace YimMenu::Submenus +{ + std::shared_ptr RenderDoomsdayHeistMenu() + { + auto tab = std::make_shared("Doomsday Heist"); + + auto cuts = std::make_shared("Heist Cuts", 2); + auto setups = std::make_shared("Heist Setups"); + auto misc = std::make_shared("Misc", 1); + + cuts->AddItem(std::make_shared("doomsdayheistcut1"_J)); + cuts->AddItem(std::make_shared("doomsdayheistcut3"_J)); + cuts->AddItem(std::make_shared("doomsdayheistcut2"_J)); + cuts->AddItem(std::make_shared("doomsdayheistcut4"_J)); + cuts->AddItem(std::make_shared("doomsdayheistforceready"_J)); + cuts->AddItem(std::make_shared("doomsdayheistsetcuts"_J)); + + setups->AddItem(std::make_shared("doomsdayheistcategory"_J)); + setups->AddItem(std::make_shared("doomsdayheistsetup"_J)); + + misc->AddItem(std::make_shared("doomsdayheistskiphacking"_J)); + misc->AddItem(std::make_shared("doomsdayheistinstantfinish"_J)); + misc->AddItem(std::make_shared("doomsdayheistinstantfinishact3"_J)); + + tab->AddItem(cuts); + tab->AddItem(setups); + tab->AddItem(misc); + + return tab; + } +} \ No newline at end of file diff --git a/src/game/frontend/submenus/Recovery/Heist/DoomsdayHeist.hpp b/src/game/frontend/submenus/Recovery/Heist/DoomsdayHeist.hpp new file mode 100644 index 0000000..078d71e --- /dev/null +++ b/src/game/frontend/submenus/Recovery/Heist/DoomsdayHeist.hpp @@ -0,0 +1,6 @@ +#include "game/frontend/submenus/Recovery/HeistModifier.hpp" + +namespace YimMenu::Submenus +{ + std::shared_ptr RenderDoomsdayHeistMenu(); +} diff --git a/src/game/frontend/submenus/Recovery/HeistModifier.cpp b/src/game/frontend/submenus/Recovery/HeistModifier.cpp new file mode 100644 index 0000000..5b7c01a --- /dev/null +++ b/src/game/frontend/submenus/Recovery/HeistModifier.cpp @@ -0,0 +1,26 @@ +#include "HeistModifier.hpp" +#include "Heist/ApartmentHeist.hpp" +#include "Heist/DiamondCasinoHeist.hpp" +#include "Heist/CayoPericoHeist.hpp" +#include "Heist/DoomsdayHeist.hpp" + +namespace YimMenu::Submenus +{ + std::shared_ptr BuildHeistModifierMenu() + { + auto menu = std::make_shared("Heists"); + + menu->AddItem(std::make_shared([] { + ImGui::BeginTabBar("Heist Modifier"); + })); + menu->AddItem(RenderApartmentHeistMenu()); + menu->AddItem(RenderDiamondCasinoHeistMenu()); + menu->AddItem(RenderCayoPericoHeistMenu()); + menu->AddItem(RenderDoomsdayHeistMenu()); + menu->AddItem(std::make_shared([] { + ImGui::EndTabBar(); + })); + + return menu; + } +} \ No newline at end of file diff --git a/src/game/frontend/submenus/Recovery/HeistModifier.hpp b/src/game/frontend/submenus/Recovery/HeistModifier.hpp new file mode 100644 index 0000000..843f994 --- /dev/null +++ b/src/game/frontend/submenus/Recovery/HeistModifier.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "core/frontend/manager/Category.hpp" +#include "game/frontend/items/Items.hpp" + +namespace YimMenu::Submenus +{ + std::shared_ptr BuildHeistModifierMenu(); +} \ No newline at end of file diff --git a/src/game/frontend/submenus/Recovery/StatEditor.cpp b/src/game/frontend/submenus/Recovery/StatEditor.cpp index 0435f55..3f3089f 100644 --- a/src/game/frontend/submenus/Recovery/StatEditor.cpp +++ b/src/game/frontend/submenus/Recovery/StatEditor.cpp @@ -203,7 +203,7 @@ namespace YimMenu::Submenus if (info.m_IsBoolStat) value.m_AsBool = STATS::GET_PACKED_STAT_BOOL_CODE(info.m_Index, -1); else - value.m_AsInt = STATS::GET_PACKED_STAT_BOOL_CODE(info.m_Index, -1); + value.m_AsInt = STATS::GET_PACKED_STAT_INT_CODE(info.m_Index, -1); } static void WritePackedStat(const StatValue& value, const PackedStatInfo& info) @@ -224,8 +224,6 @@ namespace YimMenu::Submenus if (info.m_IsBoolStat) STATS::SET_PACKED_STAT_BOOL_CODE(info.m_Index, static_cast(value), -1); - else - STATS::SET_PACKED_STAT_INT_CODE(info.m_Index, value, -1); } } diff --git a/src/game/frontend/submenus/Self.cpp b/src/game/frontend/submenus/Self.cpp index 835ade3..6b90c6a 100644 --- a/src/game/frontend/submenus/Self.cpp +++ b/src/game/frontend/submenus/Self.cpp @@ -12,7 +12,7 @@ namespace YimMenu::Submenus auto globalsGroup = std::make_shared("Globals"); auto movementGroup = std::make_shared("Movement"); auto wantedGroup = std::make_shared("Wanted"); - auto toolsGroup = std::make_shared("Tools"); + auto toolsGroup = std::make_shared("Tools", 1); globalsGroup->AddItem(std::make_shared("godmode"_J)); globalsGroup->AddItem(std::make_shared("invis"_J)); @@ -21,6 +21,7 @@ namespace YimMenu::Submenus globalsGroup->AddItem(std::make_shared("noragdoll"_J)); globalsGroup->AddItem(std::make_shared("noidlekick"_J)); globalsGroup->AddItem(std::make_shared("unlimitedoxygen"_J)); + globalsGroup->AddItem(std::make_shared("formatmoney"_J)); auto clearWanted = std::make_shared("", 1); clearWanted->AddItem(std::make_shared("neverwanted"_J, std::make_shared("clearwanted"_J), true)); @@ -37,6 +38,7 @@ namespace YimMenu::Submenus toolsGroup->AddItem(std::make_shared("heal"_J)); toolsGroup->AddItem(std::make_shared("cleardamage"_J)); toolsGroup->AddItem(std::make_shared("fillinventory"_J)); + toolsGroup->AddItem(std::make_shared("MobileRadio"_J)); movementGroup->AddItem(std::make_shared("standonvehicles"_J)); movementGroup->AddItem(std::make_shared("disableactionmode"_J)); diff --git a/src/game/frontend/submenus/network/RandomEvents.cpp b/src/game/frontend/submenus/network/RandomEvents.cpp index aafa7f1..e6ad5d7 100644 --- a/src/game/frontend/submenus/network/RandomEvents.cpp +++ b/src/game/frontend/submenus/network/RandomEvents.cpp @@ -3,9 +3,12 @@ #include "core/backend/ScriptMgr.hpp" #include "core/frontend/Notifications.hpp" #include "game/backend/Self.hpp" +#include "game/backend/Tunables.hpp" +#include "game/backend/ScriptPatches.hpp" +#include "game/gta/data/RandomEvents.hpp" #include "game/gta/Scripts.hpp" #include "game/gta/ScriptFunction.hpp" -#include "game/pointers/Pointers.hpp" +#include "types/script/globals/GPBD_FM_2.hpp" #include "types/script/globals/GSBD_RandomEvents.hpp" #include "types/script/locals/FMRandomEvents.hpp" #include "types/script/ScriptEvent.hpp" @@ -42,84 +45,25 @@ namespace YimMenu::Submenus MAX_EVENTS }; - static constexpr std::array randomEventNames = { - "Drug Vehicle", - "Movie Props", - "Sleeping Guard", - "Exotic Exports", - "The Slashers", - "Phantom Car", - "Sightseeing", - "Smuggler Trail", - "Cerberus Surprise", - "Smuggler Plane", - "Crime Scene", - "Metal Detector", - "Finders Keepers", - "Shop Robbery", - "The Gooch", - "Weazel Plaza Shootout", - "Armored Truck", - "Possessed Animals", - "Ghosts Exposed", - "Happy Holidays Hauler", - "Community Outreach" - }; - - static constexpr std::array RandomEventScripts = { - "fm_content_drug_vehicle"_J, - "fm_content_movie_props"_J, - "fm_content_golden_gun"_J, - "fm_content_vehicle_list"_J, - "fm_content_slasher"_J, - "fm_content_phantom_car"_J, - "fm_content_sightseeing"_J, - "fm_content_smuggler_trail"_J, - "fm_content_cerberus"_J, - "fm_content_smuggler_plane"_J, - "fm_content_crime_scene"_J, - "fm_content_metal_detector"_J, - "fm_content_convoy"_J, - "fm_content_robbery"_J, - "fm_content_xmas_mugger"_J, - "fm_content_bank_shootout"_J, - "fm_content_armoured_truck"_J, - "fm_content_possessed_animals"_J, - "fm_content_ghosthunt"_J, - "fm_content_xmas_truck"_J, - "fm_content_community_outreach"_J - }; - - static YimMenu::ScriptFunction getNumFMMCVariations("GMFV", "freemode"_J, "5D ? ? ? 01 72 02 39 04", 1, true); + static std::vector sendUpdateRECoordsTSECooldownPatches{}; + static GPBD_FM_2* GPBDFM2 = nullptr; static GSBD_RandomEvents* GSBDRandomEvents = nullptr; static RANDOM_EVENTS_FREEMODE_DATA* FMRandomEvents = nullptr; static eRandomEvent selectedEvent = DRUG_VEHICLE; static int selectedSubvariation = 0; - static int numSubVariations = 30; - - std::string GetEventTimeLeftStr(int eventTime, int timer) - { - int timePassed = (*Pointers.NetworkTime - timer) < 0 ? -(*Pointers.NetworkTime - timer) : (*Pointers.NetworkTime - timer); - int diff = (eventTime - timePassed); - int totalSeconds = diff / 1000; - int hours = totalSeconds / 3600; - int minutes = (totalSeconds % 3600) / 60; - int seconds = totalSeconds % 60; - - if (hours < 1) - return std::format("{:02}:{:02}", minutes, seconds); - else - return std::format("{:02}:{:02}:{:02}", hours, minutes, seconds); - } + static int numSubvariations = 29; + static int setCooldown = 1800000; + static int setAvailability = 900000; + static bool applyInMinutes = false; static std::string GetEventStateString() { switch (GSBDRandomEvents->EventData[selectedEvent].State) { case eRandomEventState::INACTIVE: - return "Inactive - launching in " + GetEventTimeLeftStr(FMRandomEvents->EventData[selectedEvent].InactiveTime, GSBDRandomEvents->EventData[selectedEvent].TimerState.Time); + return "Inactive - launching in " + GSBDRandomEvents->EventData[selectedEvent].TimerState.GetRemainingTimeStr(FMRandomEvents->EventData[selectedEvent].InactiveTime); case eRandomEventState::AVAILABLE: - return "Available - deactivating in " + GetEventTimeLeftStr(FMRandomEvents->EventData[selectedEvent].AvailableTime, GSBDRandomEvents->EventData[selectedEvent].TimerState.Time); + return "Available - deactivating in " + GSBDRandomEvents->EventData[selectedEvent].TimerState.GetRemainingTimeStr(FMRandomEvents->EventData[selectedEvent].AvailableTime); case eRandomEventState::ACTIVE: return "Active"; case eRandomEventState::CLEANUP: @@ -129,20 +73,97 @@ namespace YimMenu::Submenus return "N/A"; } + static int GetNumLocallyActiveEvents() + { + int numEvents{}; + + for (int event = DRUG_VEHICLE; event < MAX_EVENTS; event++) + { + if (GPBDFM2->Entries[Self::GetPlayer().GetId()].RandomEventsClientData.EventData[event].State != eRandomEventClientState::INACTIVE) + numEvents++; + } + + return numEvents; + } + + static void ResetEventTunables(eRandomEvent event) + { + if (event == ARMOURED_TRUCK) // It doesn't have tunables + { + setCooldown = *ScriptGlobal(262145).At(33719).As(); + setAvailability = *ScriptGlobal(262145).At(33720).As(); + } + else + { + // Phantom Car's cooldown is actually 2147483647ms if STANDARDTARGETTINGTIME is not enabled + setCooldown = *Tunables::GetTunable(randomEventCooldowns[event]).As(); + setAvailability = *Tunables::GetTunable(randomEventAvailabilities[event]).As(); + } + } + + static void OnComboChange() + { + static ScriptFunction getNumFMMCVariations("freemode"_J, ScriptPointer("GetNumFMMCVariations", "5D ? ? ? 01 72 02 39 04").Add(1).Rip()); + numSubvariations = getNumFMMCVariations.Call(FMRandomEvents->MissionData.FMMCData[selectedEvent].FMMCType, 0) - 1; + selectedSubvariation = 0; + ResetEventTunables(selectedEvent); + } + + static void KillActiveEvent() + { + if (auto eventThread = Scripts::FindScriptThread(randomEventScripts[(int)selectedEvent])) + { + if (auto NetComponent = reinterpret_cast(eventThread)->m_NetComponent) + { + if (auto eventProgram = Scripts::FindScriptProgram(randomEventScripts[(int)selectedEvent])) + { + // TO-DO: Cache these too? + if (NetComponent->IsLocalPlayerHost()) + { + std::string ptrName = "SetFMContentScriptServerState" + std::to_string(selectedEvent); + ScriptFunction setFMContentScriptServerState(randomEventScripts[(int)selectedEvent], ScriptPointer(ptrName, "5D ? ? ? 55 2E 00 5D").Add(1).Rip()); + setFMContentScriptServerState.Call(3); + } + else + { + std::string ptrName = "SetFMContentScriptClientState" + std::to_string(selectedEvent); + ScriptFunction setFMContentScriptClientState(randomEventScripts[(int)selectedEvent], ScriptPointer(ptrName, "5D ? ? ? 55 08 00 74").Add(1).Rip()); + setFMContentScriptClientState.Call(3); + } + } + } + } + else + { + Notifications::Show("Random Events", "Event script is not active. Are you a participant?", NotificationType::Error); + } + } + std::shared_ptr BuildRandomEventsMenu() { - auto menu = std::make_shared("Random Events"); + if (sendUpdateRECoordsTSECooldownPatches.empty()) + { + for (int event = DRUG_VEHICLE; event < MAX_EVENTS; event++) + { + std::string ptrName = "SendUpdateRECoordsTSECooldownPatch" + std::to_string(event); + sendUpdateRECoordsTSECooldownPatches.push_back(ScriptPatches::AddPatch(randomEventScripts[event], ScriptPointer(ptrName, "43 88 13 2E 00 01"), {0x71, 0x00, 0x00})); + } + } + + for (auto& patch : sendUpdateRECoordsTSECooldownPatches) + patch->Enable(); + + auto menu = std::make_shared("Random Events"); + auto settings = std::make_shared("Settings"); menu->AddItem(std::make_unique([] { - GSBDRandomEvents = nullptr; - FMRandomEvents = nullptr; - + GPBDFM2 = GPBD_FM_2::Get(); GSBDRandomEvents = GSBD_RandomEvents::Get(); - if (!GSBDRandomEvents) - return ImGui::Text("Freemode global block is not loaded yet."); + if (!GPBDFM2 || !GSBDRandomEvents) + return ImGui::Text("Freemode global block is not loaded."); - if (GSBDRandomEvents->InitState != eRandomEventInitState::INITIALIZED) - return ImGui::Text("Random Events are not initialized yet."); + if (GPBDFM2->Entries[Self::GetPlayer().GetId()].RandomEventsClientData.InitState != eRandomEventClientInitState::INITIALIZED) + return ImGui::Text("Random Events are not initialized."); if (auto freemode = Scripts::FindScriptThread("freemode"_J)) { @@ -176,8 +197,8 @@ namespace YimMenu::Submenus if (ImGui::Selectable(randomEventNames[event], event == selectedEvent)) { FiberPool::Push([event] { - selectedEvent = (eRandomEvent)event; - numSubVariations = getNumFMMCVariations.Call(FMRandomEvents->MissionData.FMMCData[selectedEvent].FMMCType, 0) - 1; + selectedEvent = (eRandomEvent)event; + OnComboChange(); }); } @@ -187,11 +208,18 @@ namespace YimMenu::Submenus ImGui::EndCombo(); } - if (ImGui::InputInt(std::format("Select Location (0-{})", numSubVariations).c_str(), &selectedSubvariation)) + if (ImGui::InputInt(std::format("Select Location (0-{})", numSubvariations).c_str(), &selectedSubvariation)) { - selectedSubvariation = std::clamp(selectedSubvariation, 0, numSubVariations); + selectedSubvariation = std::clamp(selectedSubvariation, 0, numSubvariations); } + int numActiveEvents = GetNumLocallyActiveEvents(); + int maxActiveEvents = *Tunables::GetTunable("FMREMAXACTIVATEDEVENTS"_J).As(); + if (numActiveEvents >= maxActiveEvents) + ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Active Events: %d/%d", numActiveEvents, maxActiveEvents); + else + ImGui::Text("Active Events: %d/%d", numActiveEvents, maxActiveEvents); + if (ImGui::Button("Launch Event")) { FiberPool::Push([] { @@ -215,9 +243,7 @@ namespace YimMenu::Submenus }); } if (ImGui::IsItemHovered()) - { - ImGui::SetTooltip("Force launches the event."); - } + ImGui::SetTooltip("Requires freemode script host."); ImGui::SameLine(); @@ -230,8 +256,7 @@ namespace YimMenu::Submenus } else if (GSBDRandomEvents->EventData[selectedEvent].State == eRandomEventState::ACTIVE) { - ScriptFunction terminateFMContentScript("TFMCS", RandomEventScripts[(int)selectedEvent], "2D 01 05 00 00 5D ? ? ?"); - terminateFMContentScript.Call(false); // Is it ok to do this when we're not host? + KillActiveEvent(); } else { @@ -239,10 +264,6 @@ namespace YimMenu::Submenus } }); } - if (ImGui::IsItemHovered()) - { - ImGui::SetTooltip("Terminates the event."); - } ImGui::SameLine(); @@ -267,14 +288,10 @@ namespace YimMenu::Submenus } }); } - if (ImGui::IsItemHovered()) - { - ImGui::SetTooltip("Teleports to the event."); - } if (GSBDRandomEvents->EventData[selectedEvent].State == eRandomEventState::ACTIVE) { - if (auto eventThread = Scripts::FindScriptThread(RandomEventScripts[(int)selectedEvent])) + if (auto eventThread = Scripts::FindScriptThread(randomEventScripts[(int)selectedEvent])) { if (auto netComponent = reinterpret_cast(eventThread)->m_NetComponent) { @@ -296,10 +313,46 @@ namespace YimMenu::Submenus } ImGui::Text("State: %s", GetEventStateString().c_str()); - ImGui::Text("Location: %d", GSBDRandomEvents->EventData[selectedEvent].Subvariation); - ImGui::Text("Trigger Range: %.2f", GSBDRandomEvents->EventData[selectedEvent].TriggerRange); // Default value is 400, it will be updated once the event switches to the available state + if (GSBDRandomEvents->EventData[selectedEvent].State == eRandomEventState::INACTIVE) + { + ImGui::Text("Location: N/A"); + ImGui::Text("Trigger Range: N/A"); + } + else + { + ImGui::Text("Location: %d", GSBDRandomEvents->EventData[selectedEvent].Subvariation); + ImGui::Text("Trigger Range: %.2f", GSBDRandomEvents->EventData[selectedEvent].TriggerRange); // Default value is 400, it will be updated once the event switches to the available state + } + + // We should probably put this into a separate group, but I just don't want to do the same safety checks before rendering it + ImGui::SeparatorText("Cooldown & Availability"); + + ImGui::InputInt("##cooldown", &setCooldown); + ImGui::SameLine(); + if (ImGui::Button("Set Cooldown")) + { + int value = applyInMinutes ? (setCooldown * 60000) : setCooldown; + FMRandomEvents->EventData[selectedEvent].InactiveTime = value; + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Requires freemode script host."); + + ImGui::InputInt("##availability", &setAvailability); + ImGui::SameLine(); + if (ImGui::Button("Set Availability")) + { + int value = applyInMinutes ? (setAvailability * 60000) : setAvailability; + FMRandomEvents->EventData[selectedEvent].AvailableTime = value; + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Requires freemode script host."); + + ImGui::Checkbox("Apply in Minutes", &applyInMinutes); })); + settings->AddItem(std::make_shared("esprandomevents"_J)); + + menu->AddItem(std::move(settings)); return menu; } } \ No newline at end of file diff --git a/src/game/gta/Network.cpp b/src/game/gta/Network.cpp index e7116f0..a42cf34 100644 --- a/src/game/gta/Network.cpp +++ b/src/game/gta/Network.cpp @@ -18,10 +18,10 @@ namespace YimMenu::Network { void LaunchJoinType(JoinType to_launch) { - static ScriptFunction send_to_clouds("SendToClouds", "shop_controller"_J, "2D 00 02 00 00 72 5D ? ? ? 72"); + static ScriptFunction sendToClouds("shop_controller"_J, ScriptPointer("SendToClouds", "2D 00 02 00 00 72 5D ? ? ? 72")); static ScriptGlobal join_type_global(1575038); - send_to_clouds.Call(); + sendToClouds.Call(); *join_type_global.As() = to_launch; } diff --git a/src/game/gta/ScriptData.cpp b/src/game/gta/ScriptData.cpp new file mode 100644 index 0000000..def94ec --- /dev/null +++ b/src/game/gta/ScriptData.cpp @@ -0,0 +1,61 @@ +#include "ScriptData.hpp" +#include "types/script/scrProgram.hpp" + +namespace YimMenu +{ + ScriptData::ScriptData(rage::scrProgram* program) + { + auto pages = new uint8_t*[program->GetNumCodePages()]; + + for (auto i = 0u; i < program->GetNumCodePages(); i++) + { + pages[i] = new uint8_t[program->GetCodePageSize(i)]; + std::memcpy(pages[i], program->GetCodePage(i), program->GetCodePageSize(i)); + } + + m_Data = pages; + m_Pages = program->GetNumCodePages(); + m_Size = program->GetFullCodeSize(); + m_Name = program->m_Name; + } + + ScriptData::~ScriptData() + { + for (auto i = 0u; i < m_Pages; i++) + { + delete[] m_Data[i]; + } + + delete[] m_Data; + m_Data = nullptr; + m_Name = nullptr; + } + + std::uint8_t* ScriptData::GetCodeLocation(ScriptData* data, int idx) + { + return &data->GetData()[idx >> 14][idx & 0x3FFF]; + } + + std::optional ScriptData::GetCodeLocationByPattern(ScriptData* data, const SimplePattern& pattern) + { + std::uint32_t codeSize = data->GetSize(); + for (uint32_t i = 0; i < (codeSize - pattern.m_Bytes.size()); i++) + { + for (uint32_t j = 0; j < pattern.m_Bytes.size(); j++) + { + if (pattern.m_Bytes[j].has_value()) + { + int loc = i + j; + if (pattern.m_Bytes[j] != *GetCodeLocation(data, loc)) + goto incorrect; + } + } + + return i; + incorrect: + continue; + } + + return std::nullopt; + } +} \ No newline at end of file diff --git a/src/game/gta/ScriptData.hpp b/src/game/gta/ScriptData.hpp new file mode 100644 index 0000000..f6e744a --- /dev/null +++ b/src/game/gta/ScriptData.hpp @@ -0,0 +1,40 @@ +#pragma once +#include "core/memory/Pattern.hpp" + +namespace rage +{ + class scrProgram; +} + +namespace YimMenu +{ + class ScriptData + { + std::uint8_t** m_Data; + std::uint32_t m_Pages; + std::uint32_t m_Size; + const char* m_Name; + + public: + inline std::uint8_t** GetData() const + { + return m_Data; + } + + inline std::uint32_t GetSize() const + { + return m_Size; + } + + inline const char* GetName() const + { + return m_Name; + } + + ScriptData(rage::scrProgram* program); + ~ScriptData(); + + static uint8_t* GetCodeLocation(ScriptData* data, int idx); + static std::optional GetCodeLocationByPattern(ScriptData* data, const SimplePattern& pattern); + }; +} \ No newline at end of file diff --git a/src/game/gta/ScriptFunction.cpp b/src/game/gta/ScriptFunction.cpp index 9875e4a..66050ee 100644 --- a/src/game/gta/ScriptFunction.cpp +++ b/src/game/gta/ScriptFunction.cpp @@ -1,34 +1,66 @@ #include "ScriptFunction.hpp" +#include "game/backend/ScriptPointers.hpp" +#include "game/pointers/Pointers.hpp" +#include "game/gta/Scripts.hpp" +#include "game/gta/ScriptPointer.hpp" +#include "types/script/scrThread.hpp" +#include "types/script/scrProgram.hpp" +#include "types/rage/tlsContext.hpp" namespace YimMenu { - ScriptFunction::ScriptFunction(const std::string& name, const joaat_t script, const SimplePattern& pattern, std::int32_t offset, bool rip) : - m_Name(name), - m_Script(script), - m_Pattern(pattern), - m_Offset(offset), - m_Rip(rip), - m_PC(0) + ScriptFunction::ScriptFunction(const joaat_t script, ScriptPointer ptr) : + m_Script(script), + m_Pointer(ptr), + m_Pc(0) { } - std::uint32_t ScriptFunction::GetPC(rage::scrProgram* program) + void ScriptFunction::CallImpl(const std::vector& args, void* returnValue, std::uint32_t returnSize) { - if (m_PC != 0) - return m_PC; + auto thread = Scripts::FindScriptThread(m_Script); + auto program = Scripts::FindScriptProgram(m_Script); - if (auto location = Scripts::GetCodeLocationByPattern(program, m_Pattern)) + if (!thread || !program) + return; + + if (!m_Pc) { - m_PC = location.value() + m_Offset; - if (m_Rip) - m_PC = ReadThreeByte(program->GetCodeAddress(m_PC)); - LOG(INFO) << "Found pattern for " << m_Name << " at " << HEX(m_PC) << " in script " << program->m_Name; - } - else - { - LOG(FATAL) << "Failed to find pattern for " << m_Name << " in script " << program->m_Name; + if (auto address = ScriptPointers::GetPointer(Joaat(m_Pointer.GetName()))) + { + m_Pc = address; + } + else + { + m_Pc = m_Pointer.Scan(program).As(); + ScriptPointers::CachePointer(Joaat(m_Pointer.GetName()), m_Pc); + } + + if (!m_Pc) // if still not valid + return; } - return m_PC; + auto tlsCtx = rage::tlsContext::Get(); + auto stack = (std::uint64_t*)thread->m_Stack; + auto ogThread = tlsCtx->m_CurrentScriptThread; + tlsCtx->m_CurrentScriptThread = thread; + tlsCtx->m_ScriptThreadActive = true; + rage::scrThread::Context ctx = thread->m_Context; + auto topStack = ctx.m_StackPointer; + + for (auto& arg : args) + stack[ctx.m_StackPointer++] = arg; + + stack[ctx.m_StackPointer++] = 0; + ctx.m_ProgramCounter = m_Pc; + ctx.m_State = rage::scrThread::State::IDLE; + + Pointers.ScriptVM(stack, Pointers.ScriptGlobals, program, &ctx); + + tlsCtx->m_CurrentScriptThread = ogThread; + tlsCtx->m_ScriptThreadActive = ogThread != nullptr; + + if (returnValue) + std::memcpy(returnValue, stack + topStack, returnSize); } } \ No newline at end of file diff --git a/src/game/gta/ScriptFunction.hpp b/src/game/gta/ScriptFunction.hpp index 96c3b37..fec445d 100644 --- a/src/game/gta/ScriptFunction.hpp +++ b/src/game/gta/ScriptFunction.hpp @@ -1,74 +1,41 @@ #pragma once #include "core/util/Joaat.hpp" -#include "core/memory/Pattern.hpp" -#include "game/pointers/Pointers.hpp" -#include "game/gta/Scripts.hpp" -#include "types/script/scrThread.hpp" -#include "types/script/scrProgram.hpp" -#include "types/rage/tlsContext.hpp" +#include "game/gta/ScriptPointer.hpp" namespace YimMenu { class ScriptFunction { joaat_t m_Script; - const SimplePattern m_Pattern; - std::int32_t m_Offset; - bool m_Rip; - std::uint32_t m_PC; - std::string m_Name; - - std::uint32_t GetPC(rage::scrProgram* program); + ScriptPointer m_Pointer; + std::uint32_t m_Pc; template - void PushArg(std::uint64_t* stack, std::uint32_t& stackPtr, Arg&& value) + static void PushArg(std::uint64_t* stack, std::uint32_t& stackPtr, Arg&& value) { *reinterpret_cast>*>(reinterpret_cast(stack) + (stackPtr++)) = std::forward(value); } - std::uint32_t ReadThreeByte(std::uint8_t* arr) - { - return arr[0] + (arr[1] << 8) + (arr[2] << 16); - } + void CallImpl(const std::vector& args, void* returnValue = 0, std::uint32_t returnSize = 0); public: - ScriptFunction(const std::string& name, const joaat_t script, const SimplePattern& pattern, std::int32_t offset = 0, bool rip = false); + ScriptFunction(const joaat_t script, ScriptPointer ptr); template Ret Call(Args... args) { - auto thread = Scripts::FindScriptThread(m_Script); - auto program = Scripts::FindScriptProgram(m_Script); - - if (!thread || !program) - return Ret(); - - auto pc = GetPC(program); - if (!pc) - return Ret(); - - auto tlsCtx = rage::tlsContext::Get(); - auto stack = (std::uint64_t*)thread->m_Stack; - auto ogThread = tlsCtx->m_CurrentScriptThread; - tlsCtx->m_CurrentScriptThread = thread; - tlsCtx->m_ScriptThreadActive = true; - rage::scrThread::Context ctx = thread->m_Context; - auto topStack = ctx.m_StackPointer; - - (PushArg(stack, ctx.m_StackPointer, std::forward(args)), ...); - - stack[ctx.m_StackPointer++] = 0; - ctx.m_ProgramCounter = pc; - ctx.m_State = rage::scrThread::State::IDLE; - - Pointers.ScriptVM(stack, Pointers.ScriptGlobals, program, &ctx); - - tlsCtx->m_CurrentScriptThread = ogThread; - tlsCtx->m_ScriptThreadActive = ogThread != nullptr; - + std::uint32_t index{}; + std::vector params(sizeof...(Args)); + (PushArg(params.data(), index, std::forward(args)), ...); if constexpr (!std::is_same_v) { - return *reinterpret_cast(stack + topStack); + Ret returnValue; + CallImpl(params, &returnValue, sizeof(returnValue)); + return returnValue; + } + else + { + CallImpl(params); } } }; diff --git a/src/game/gta/ScriptGlobal.hpp b/src/game/gta/ScriptGlobal.hpp index 1d3b166..b9ec82f 100644 --- a/src/game/gta/ScriptGlobal.hpp +++ b/src/game/gta/ScriptGlobal.hpp @@ -42,5 +42,4 @@ namespace YimMenu void* Get() const; }; - } \ No newline at end of file diff --git a/src/game/gta/ScriptLocal.hpp b/src/game/gta/ScriptLocal.hpp index f9d88d9..470b864 100644 --- a/src/game/gta/ScriptLocal.hpp +++ b/src/game/gta/ScriptLocal.hpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include namespace YimMenu { @@ -32,6 +34,12 @@ namespace YimMenu } + constexpr ScriptLocal(joaat_t script, std::size_t index) : + ScriptLocal(Scripts::FindScriptThread(script)->m_Stack, index) + { + + } + constexpr ScriptLocal At(std::ptrdiff_t offset) const { return { m_StackPtr, m_Index + offset }; diff --git a/src/game/gta/ScriptPointer.cpp b/src/game/gta/ScriptPointer.cpp new file mode 100644 index 0000000..6de4320 --- /dev/null +++ b/src/game/gta/ScriptPointer.cpp @@ -0,0 +1,67 @@ +#include "ScriptPointer.hpp" +#include "game/gta/Scripts.hpp" +#include "types/script/scrProgram.hpp" + +namespace YimMenu +{ + static std::uint32_t ReadThreeByte(std::uint8_t* arr) + { + return arr[0] + (arr[1] << 8) + (arr[2] << 16); + } + + ScriptPointer::ScriptPointer(const std::string& name, const SimplePattern& pattern, std::int32_t offset, bool rip, std::uint32_t address) : + m_Name(name), + m_Pattern(pattern), + m_Offset(offset), + m_Rip(rip), + m_Address(address) + { + } + + ScriptPointer ScriptPointer::Add(std::uint32_t offset) + { + return ScriptPointer(m_Name, m_Pattern, m_Offset + offset, m_Rip, m_Address); + } + + ScriptPointer ScriptPointer::Sub(std::uint32_t offset) + { + return ScriptPointer(m_Name, m_Pattern, m_Offset - offset, m_Rip, m_Address); + } + + ScriptPointer ScriptPointer::Rip() + { + return ScriptPointer(m_Name, m_Pattern, m_Offset, true, m_Address); + } + + ScriptPointer ScriptPointer::Scan(rage::scrProgram* program) + { + if (auto location = Scripts::GetCodeLocationByPattern(program, m_Pattern)) + { + m_Address = location.value() + m_Offset; + if (m_Rip) + m_Address = ReadThreeByte(program->GetCodeAddress(m_Address)); + + LOG(INFO) << "Found pattern for " << m_Name << " at " << HEX(m_Address) << " in script " << program->m_Name; + return ScriptPointer(m_Name, m_Pattern, m_Offset, m_Rip, m_Address); + } + + LOG(FATAL) << "Failed to find pattern for " << m_Name; + return ScriptPointer(m_Name, m_Pattern, m_Offset, m_Rip, 0); + } + + ScriptPointer ScriptPointer::Scan(ScriptData* data) + { + if (auto location = ScriptData::GetCodeLocationByPattern(data, m_Pattern)) + { + m_Address = location.value() + m_Offset; + if (m_Rip) + m_Address = ReadThreeByte(ScriptData::GetCodeLocation(data, m_Address)); + + LOG(INFO) << "Found pattern for " << m_Name << " at " << HEX(m_Address) << " in script " << data->GetName(); + return ScriptPointer(m_Name, m_Pattern, m_Offset, m_Rip, m_Address); + } + + LOG(FATAL) << "Failed to find pattern for " << m_Name; + return ScriptPointer(m_Name, m_Pattern, m_Offset, m_Rip, 0); + } +} \ No newline at end of file diff --git a/src/game/gta/ScriptPointer.hpp b/src/game/gta/ScriptPointer.hpp new file mode 100644 index 0000000..b3edf6c --- /dev/null +++ b/src/game/gta/ScriptPointer.hpp @@ -0,0 +1,34 @@ +#pragma once +#include "game/gta/ScriptData.hpp" + +namespace YimMenu +{ + class ScriptPointer + { + const std::string m_Name; + SimplePattern m_Pattern; + std::int32_t m_Offset; + bool m_Rip; + std::uint32_t m_Address; + + public: + ScriptPointer(const std::string& name, const SimplePattern& pattern, std::int32_t offset = 0, bool rip = false, std::uint32_t address = 0); + + ScriptPointer Add(std::uint32_t offset); + ScriptPointer Sub(std::uint32_t offset); + ScriptPointer Rip(); + ScriptPointer Scan(rage::scrProgram* program); + ScriptPointer Scan(ScriptData* data); // For script patches + + template + T As() + { + return (T)m_Address; + } + + std::string GetName() const + { + return m_Name; + } + }; +} \ No newline at end of file diff --git a/src/game/gta/data/RandomEvents.hpp b/src/game/gta/data/RandomEvents.hpp new file mode 100644 index 0000000..865a853 --- /dev/null +++ b/src/game/gta/data/RandomEvents.hpp @@ -0,0 +1,101 @@ +#pragma once +#include "core/util/Joaat.hpp" + +namespace YimMenu +{ + static constexpr std::array randomEventNames = { + "Drug Vehicle", + "Movie Props", + "Sleeping Guard", + "Exotic Exports", + "The Slashers", + "Phantom Car", + "Sightseeing", + "Smuggler Trail", + "Cerberus Surprise", + "Smuggler Plane", + "Crime Scene", + "Metal Detector", + "Finders Keepers", + "Shop Robbery", + "The Gooch", + "Weazel Plaza Shootout", + "Armored Truck", + "Possessed Animals", + "Ghosts Exposed", + "Happy Holidays Hauler", + "Community Outreach" + }; + + static constexpr std::array randomEventScripts = { + "fm_content_drug_vehicle"_J, + "fm_content_movie_props"_J, + "fm_content_golden_gun"_J, + "fm_content_vehicle_list"_J, + "fm_content_slasher"_J, + "fm_content_phantom_car"_J, + "fm_content_sightseeing"_J, + "fm_content_smuggler_trail"_J, + "fm_content_cerberus"_J, + "fm_content_smuggler_plane"_J, + "fm_content_crime_scene"_J, + "fm_content_metal_detector"_J, + "fm_content_convoy"_J, + "fm_content_robbery"_J, + "fm_content_xmas_mugger"_J, + "fm_content_bank_shootout"_J, + "fm_content_armoured_truck"_J, + "fm_content_possessed_animals"_J, + "fm_content_ghosthunt"_J, + "fm_content_xmas_truck"_J, + "fm_content_community_outreach"_J + }; + + static constexpr std::array randomEventCooldowns = { + "SUM22_RE_DRUG_VEHICLE_INACTIVE_TIME"_J, + "SUM22_RE_MOVIE_PROPS_INACTIVE_TIME"_J, + "SUM22_RE_GOLDEN_GUN_INACTIVE_TIME"_J, + "SUM22_RE_VEHICLE_LIST_INACTIVE_TIME"_J, + "STANDARDCONTROLLERVOLUME_COOLDOWN"_J, + "STANDARDTARGETTINGTIME_COOLDOWN"_J, + "SSP2_COOLDOWN"_J + "SUM22_RE_SMUGGLER_TRAIL_INACTIVE_TIME"_J, + "NC_SOURCE_TRUCK_COOLDOWN"_J, + "SUM22_RE_SMUGGLER_PLANE_INACTIVE_TIME"_J, + "SUM22_RE_CRIME_SCENE_INACTIVE_TIME"_J, + "SUM22_RE_METAL_DETECTOR_INACTIVE_TIME"_J, + "XM22_RE_GANG_CONVOY_INACTIVE_TIME"_J, + "XM22_RE_ROBBERY_INACTIVE_TIME"_J, + "STANDARD_KEYBIND_COOLDOWN"_J, + "XM22_RE_BANK_SHOOTOUT_INACTIVE_TIME"_J, + (joaat_t)NULL, + "STANDARDCONTROLLERVOLUME_COOLDOWN"_J, + "SUM23_RE_GHOSTHUNT_INACTIVE_TIME"_J, + "XMAS_TRUCK_INACTIVE_TIME"_J, + "RE_COMMUNITY_OUTREACH_INACTIVE_TIME"_J + }; + + static constexpr std::array randomEventAvailabilities = { + "SUM22_RE_DRUG_VEHICLE_AVAILABLE_TIME"_J, + "SUM22_RE_MOVIE_PROPS_AVAILABLE_TIME"_J, + "SUM22_RE_GOLDEN_GUN_AVAILABLE_TIME"_J, + "SUM22_RE_VEHICLE_LIST_AVAILABLE_TIME"_J, + "STANDARDCONTROLLERVOLUME_AVAILABILITY"_J, + "STANDARDTARGETTINGTIME_AVAILABILITY"_J, + "SSP2_AVAILABILITY"_J, + "SUM22_RE_SMUGGLER_TRAIL_AVAILABLE_TIME"_J, + "NC_SOURCE_TRUCK_AVAILABILITY"_J, + "SUM22_RE_SMUGGLER_PLANE_AVAILABLE_TIME"_J, + "SUM22_RE_CRIME_SCENE_AVAILABLE_TIME"_J, + "SUM22_RE_METAL_DETECTOR_AVAILABLE_TIME"_J, + "XM22_RE_GANG_CONVOY_AVAILABLE_TIME"_J, + "XM22_RE_ROBBERY_AVAILABLE_TIME"_J, + "STANDARD_KEYBIND_AVAILABILITY"_J, + "XM22_RE_BANK_SHOOTOUT_AVAILABLE_TIME"_J, + (joaat_t)NULL, + "STANDARDCONTROLLERVOLUME_AVAILABILITY"_J, + "SUM23_RE_GHOSTHUNT_AVAILABLE_TIME"_J, + "XMAS_TRUCK_AVAILABLE_TIME"_J, + "RE_COMMUNITY_OUTREACH_AVAILABLE_TIME"_J + }; +} \ No newline at end of file diff --git a/src/game/pointers/Pointers.cpp b/src/game/pointers/Pointers.cpp index cc3591d..1bf98ce 100644 --- a/src/game/pointers/Pointers.cpp +++ b/src/game/pointers/Pointers.cpp @@ -308,6 +308,21 @@ namespace YimMenu NetworkTime = ptr.Add(2).Rip().As(); }); + constexpr auto gameTimerPtrn = Pattern<"3B 2D ? ? ? ? 76">("GameTimer"); + scanner.Add(gameTimerPtrn, [this](PointerCalculator ptr) { + GameTimer = ptr.Add(2).Rip().As(); + }); + + constexpr auto formatIntCaller1Ptrn = Pattern<"48 89 35 ? ? ? ? 48 8B 74 24">("FormatIntCaller1"); + scanner.Add(formatIntCaller1Ptrn, [this](PointerCalculator ptr) { + FormatIntCaller1 = ptr.Add(0x5D).As(); + }); + + constexpr auto formatIntCaller2Ptrn = Pattern<"48 B8 20 73 69 7A 65 3D 27 32 48 89 84 24">("FormatIntCaller2"); + scanner.Add(formatIntCaller2Ptrn, [this](PointerCalculator ptr) { + FormatIntCaller2 = ptr.Sub(0x11).As(); + }); + if (!scanner.Scan()) { LOG(FATAL) << "Some patterns could not be found, unloading."; diff --git a/src/game/pointers/Pointers.hpp b/src/game/pointers/Pointers.hpp index 23fef46..51e98c1 100644 --- a/src/game/pointers/Pointers.hpp +++ b/src/game/pointers/Pointers.hpp @@ -135,6 +135,9 @@ namespace YimMenu Functions::GetPresenceAttributes GetPresenceAttributes; Functions::GetAvatars GetAvatars; std::uint32_t* NetworkTime; + std::uint32_t* GameTimer; + PVOID FormatIntCaller1; + PVOID FormatIntCaller2; }; struct Pointers : PointerData diff --git a/src/main.cpp b/src/main.cpp index 828802f..4bb402e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ #include "core/filemgr/FileMgr.hpp" #include "core/frontend/Notifications.hpp" #include "core/hooking/Hooking.hpp" +#include "core/hooking/CallHook.hpp" #include "core/memory/ModuleMgr.hpp" #include "core/renderer/Renderer.hpp" #include "game/backend/AnticheatBypass.hpp" @@ -16,6 +17,7 @@ #include "game/backend/Self.hpp" #include "game/backend/NativeHooks.hpp" #include "game/backend/Tunables.hpp" +#include "game/backend/ScriptPointers.hpp" #include "game/frontend/GUI.hpp" #include "game/pointers/Pointers.hpp" #include "game/features/recovery/GiveVehicleReward.hpp" @@ -49,6 +51,8 @@ namespace YimMenu ScriptMgr::Init(); LOG(INFO) << "ScriptMgr initialized"; + ScriptPointers::Init(); + GUI::Init(); ScriptMgr::AddScript(std::make_unique