From 5791c1962e682120064d2f362870ce31be905e82 Mon Sep 17 00:00:00 2001 From: daobashun <49193927+fangshun2004@users.noreply.github.com> Date: Sat, 12 Apr 2025 08:31:15 +0800 Subject: [PATCH 1/8] Fix read packed int not working (#128) * Fix read packed int not working Remove the scope setting packed int (because accidentally setting packed int in bulk will cause account failures and there is no need to set it in bulk) Do not generate some configuration files at the root of the game * In VS, the views are sorted by folder * quash * Change the path * Reset the window style --- src/game/frontend/Menu.cpp | 11 ++++++++--- src/game/frontend/submenus/Recovery/StatEditor.cpp | 4 +--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/game/frontend/Menu.cpp b/src/game/frontend/Menu.cpp index dd92b60..8d71c83 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"; + std::string path = file_path.string(); + IO.IniFilename = path.c_str(); + IO.LogFilename = NULL; ImFontConfig FontCfg{}; FontCfg.FontDataOwnedByAtlas = false; 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); } } From cdf1cfef1894e082ba29a5a55851f98988e368d4 Mon Sep 17 00:00:00 2001 From: Arthur <121949966+ShinyWasabi@users.noreply.github.com> Date: Sat, 12 Apr 2025 20:07:32 +0300 Subject: [PATCH 2/8] Random Event Fixes & Improvements (#170) --- .../submenus/network/RandomEvents.cpp | 173 ++++++++++++++---- src/types/script/globals/GPBD_FM_2.hpp | 120 ++++++++++++ .../script/globals/GSBD_RandomEvents.hpp | 4 +- src/types/script/globals/Globals.cpp | 2 + src/types/script/locals/FMRandomEvents.hpp | 4 +- 5 files changed, 268 insertions(+), 35 deletions(-) create mode 100644 src/types/script/globals/GPBD_FM_2.hpp diff --git a/src/game/frontend/submenus/network/RandomEvents.cpp b/src/game/frontend/submenus/network/RandomEvents.cpp index aafa7f1..f91d1d1 100644 --- a/src/game/frontend/submenus/network/RandomEvents.cpp +++ b/src/game/frontend/submenus/network/RandomEvents.cpp @@ -3,9 +3,11 @@ #include "core/backend/ScriptMgr.hpp" #include "core/frontend/Notifications.hpp" #include "game/backend/Self.hpp" +#include "game/backend/Tunables.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" @@ -66,7 +68,7 @@ namespace YimMenu::Submenus "Community Outreach" }; - static constexpr std::array RandomEventScripts = { + static constexpr std::array randomEventScripts = { "fm_content_drug_vehicle"_J, "fm_content_movie_props"_J, "fm_content_golden_gun"_J, @@ -90,14 +92,66 @@ namespace YimMenu::Submenus "fm_content_community_outreach"_J }; - static YimMenu::ScriptFunction getNumFMMCVariations("GMFV", "freemode"_J, "5D ? ? ? 01 72 02 39 04", 1, true); + 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 + }; + + static ScriptFunction getNumFMMCVariations("GNFMMCV", "freemode"_J, "5D ? ? ? 01 72 02 39 04", 1, true); + 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; + static int numSubvariations = 29; + static int setCooldown = 1800000; + static int setAvailability = 900000; + static bool applyInMinutes = false; - std::string GetEventTimeLeftStr(int eventTime, int timer) + static std::string GetEventTimeLeftStr(int eventTime, int timer) { int timePassed = (*Pointers.NetworkTime - timer) < 0 ? -(*Pointers.NetworkTime - timer) : (*Pointers.NetworkTime - timer); int diff = (eventTime - timePassed); @@ -129,20 +183,53 @@ 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() + { + selectedSubvariation = 0; + numSubvariations = getNumFMMCVariations.Call(FMRandomEvents->MissionData.FMMCData[selectedEvent].FMMCType, 0) - 1; + ResetEventTunables(selectedEvent); + } + std::shared_ptr BuildRandomEventsMenu() { auto menu = std::make_shared("Random Events"); 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 +263,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 +274,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([] { @@ -214,10 +308,6 @@ namespace YimMenu::Submenus } }); } - if (ImGui::IsItemHovered()) - { - ImGui::SetTooltip("Force launches the event."); - } ImGui::SameLine(); @@ -230,7 +320,7 @@ namespace YimMenu::Submenus } else if (GSBDRandomEvents->EventData[selectedEvent].State == eRandomEventState::ACTIVE) { - ScriptFunction terminateFMContentScript("TFMCS", RandomEventScripts[(int)selectedEvent], "2D 01 05 00 00 5D ? ? ?"); + 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? } else @@ -239,10 +329,6 @@ namespace YimMenu::Submenus } }); } - if (ImGui::IsItemHovered()) - { - ImGui::SetTooltip("Terminates the event."); - } ImGui::SameLine(); @@ -267,14 +353,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,8 +378,37 @@ 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; + } + + ImGui::InputInt("##availability", &setAvailability); + ImGui::SameLine(); + if (ImGui::Button("Set Availability")) + { + int value = applyInMinutes ? (setAvailability * 60000) : setAvailability; + FMRandomEvents->EventData[selectedEvent].AvailableTime = value; + } + + ImGui::Checkbox("Apply in Minutes", &applyInMinutes); })); return menu; diff --git a/src/types/script/globals/GPBD_FM_2.hpp b/src/types/script/globals/GPBD_FM_2.hpp new file mode 100644 index 0000000..9aa0bcb --- /dev/null +++ b/src/types/script/globals/GPBD_FM_2.hpp @@ -0,0 +1,120 @@ +#pragma once +#include "../Timer.hpp" +#include "../types.hpp" + +enum class eRaceRoundType +{ + NOT_SELECTED = -1, + FINISH_FIRST, + MOST_POINTS, + FASTEST_TIME +}; + +enum class eRaceFinalRoundType +{ + NOT_SELECTED, + HEAD_TO_HEAD +}; + +enum class eRandomEventClientState +{ + INACTIVE, + TRIGGERING, // This is different than the server available state + ACTIVE, + CLEANUP +}; + +enum class eRandomEventClientFlags +{ + FORCE_LAUNCH +}; + +enum class eRandomEventClientInitState +{ + INITIALIZING, + INITIALIZED +}; + +struct GANGOPS_DATA +{ + SCR_INT UNK_0000; // 0 + SCR_ARRAY PlayerIndexes; // 1 + SCR_ARRAY PlayerRoles; // 6 + SCR_ARRAY ArePlayersReady; // 11 + SCR_ARRAY PlayerCuts; // 16 + SCR_INT HighlightedPlayer; // 22 + SCR_INT GangopsFlowMissionProgFlags; // 23 + SCR_INT GangopsFMMissionProgFlags; // 24 + SCR_INT GangopsFMMissionSkipFlags; // 25 + SCR_INT GangopsHeistStatusFlags; // 26 + SCR_INT Flags; // 27 + SCR_BOOL IsPlanningScreenReady; // 28 + SCR_BOOL QuittedPlanningScreen; // 29 + SCR_BOOL IsTutorialActive; // 30 + SCR_ARRAY IsTutorialActiveForPlayer; // 31 + TIMER FinaleForceLaunchTimer; // 36 (H2_FINALE_FORCE_LAUNCH_TIME) + SCR_BOOL IsFinaleForceLaunched; // 38 (if players don't click ready in 3 minutes) +}; +static_assert(sizeof(GANGOPS_DATA) == 39 * 8); + +struct RANDOM_EVENTS_CLIENT_EVENT_DATA +{ + alignas(8) eRandomEventClientState State; // 0 + SCR_ARRAY, 1> Flags; // 1 +}; +static_assert(sizeof(RANDOM_EVENTS_CLIENT_EVENT_DATA) == 3 * 8); + +struct RANDOM_EVENTS_CLIENT_DATA +{ + alignas(8) eRandomEventClientInitState InitState; // 0 + SCR_ARRAY EventData; // 1 + SCR_BITSET IsParticipantInEventFlags; // 65 + SCR_INT UNK_0066; // 66 (unused) +}; +static_assert(sizeof(RANDOM_EVENTS_CLIENT_DATA) == 67 * 8); + +struct GPBD_FM_2_Entry +{ + SCR_INT NumTeams; // 0 + SCR_INT NumRounds; // 1 + SCR_INT RoundsPlayed; // 2 + SCR_INT RoundScore; // 3 + SCR_INT CurrentTeam; // 4 + SCR_INT Flags; // 5 + SCR_INT TotalScore; // 6 + SCR_INT TotalKills; // 7 + SCR_INT TotalDeaths; // 8 + SCR_INT RPEarned; // 9 + SCR_INT CashEarned; // 10 + uint64_t PAD_0011[2]; // 11 + SCR_INT APEarned; // 13 + SCR_INT Duration; // 14 + alignas(8) eRaceRoundType RaceRoundType; // 15 + alignas(8) eRaceFinalRoundType RaceFinalRoundType; // 16 + SCR_INT Flags2; // 17 + SCR_INT Vote; // 18 + SCR_INT NumVotes; // 19 + SCR_INT MaxVotes; // 20 + SCR_INT PlayerTeam; // 21 + uint64_t PAD_0022[8]; // 22 (mission stuff) + SCR_INT TunableCloudCRC; // 30 + SCR_INT CatalogCloudCRC; // 31 + SCR_INT UNK_0032; // 32 + SCR_INT Flags3; // 33 + SCR_INT UNK_0034; // 34 + SCR_BOOL HasVoted; // 35 + TIMER UNK_0036; // 36 + TIMER UNK_0038; // 38 + SCR_VEC3 CoronaPosition; // 40 + GANGOPS_DATA GangopsData; // 43 + RANDOM_EVENTS_CLIENT_DATA RandomEventsClientData; // 82 +}; +static_assert(sizeof(GPBD_FM_2_Entry) == 149 * 8); + +struct GPBD_FM_2 +{ + SCR_ARRAY Entries; + + static GPBD_FM_2* Get(); +}; +static_assert(sizeof(GPBD_FM_2) == 4769 * 8); \ No newline at end of file diff --git a/src/types/script/globals/GSBD_RandomEvents.hpp b/src/types/script/globals/GSBD_RandomEvents.hpp index b9c595e..f10939b 100644 --- a/src/types/script/globals/GSBD_RandomEvents.hpp +++ b/src/types/script/globals/GSBD_RandomEvents.hpp @@ -16,7 +16,7 @@ enum class eRandomEventState CLEANUP }; -enum class eRandomEventBitset +enum class eRandomEventFlags { TRIGGERED_BY_CLIENT, FORCE_LAUNCH, @@ -36,7 +36,7 @@ struct RANDOM_EVENTS_DATA { alignas(8) eRandomEventState State; // 0 TIMER TimerState; // 1 - SCR_ARRAY, 1> Bitset; // 3 + SCR_ARRAY, 1> Flags; // 3 SCR_INT Variation; // 5 SCR_INT Subvariation; // 6 RANDOM_EVENTS_RESERVATION_DATA ReservationData; // 7 diff --git a/src/types/script/globals/Globals.cpp b/src/types/script/globals/Globals.cpp index f89d8fe..11e5dd2 100644 --- a/src/types/script/globals/Globals.cpp +++ b/src/types/script/globals/Globals.cpp @@ -1,5 +1,6 @@ #include "GlobalPlayerBD.hpp" #include "GPBD_FM.hpp" +#include "GPBD_FM_2.hpp" #include "GPBD_FM_3.hpp" #include "GPBD_Kicking.hpp" #include "GPBD_MissionName.hpp" @@ -23,6 +24,7 @@ DEFINE_GLOBAL_ACCESSOR(GlobalPlayerBD, 2657994); DEFINE_GLOBAL_ACCESSOR(GPBD_FM, 1845270); +DEFINE_GLOBAL_ACCESSOR(GPBD_FM_2, 1882925); DEFINE_GLOBAL_ACCESSOR(GPBD_FM_3, 1887694); DEFINE_GLOBAL_ACCESSOR(GPBD_Kicking, 1874328); DEFINE_GLOBAL_ACCESSOR(GPBD_MissionName, 1873815); diff --git a/src/types/script/locals/FMRandomEvents.hpp b/src/types/script/locals/FMRandomEvents.hpp index 1d4adfb..d5a0ae4 100644 --- a/src/types/script/locals/FMRandomEvents.hpp +++ b/src/types/script/locals/FMRandomEvents.hpp @@ -7,7 +7,7 @@ namespace rage class scrThread; } -enum class eRandomEventFlags +enum class eRandomEventFMFlags { RESTART = 2, DISABLE_DISTANCE_CHECK = 4 @@ -22,7 +22,7 @@ struct RANDOM_EVENTS_LOCAL_DATA SCR_INT AvailablePtr; // 5 SCR_INT InactiveTime; // 6 SCR_INT AvailableTime; // 7 - SCR_BITSET Flags; // 8 + SCR_BITSET Flags; // 8 TIMER LastTriggerAttempt; // 9 SCR_INT UNK_0011; // 11 (unused, ranges from 0 to 4) }; From 3fa9052f0fed6f054796a0124f1c4e586af07d59 Mon Sep 17 00:00:00 2001 From: daobashun <49193927+fangshun2004@users.noreply.github.com> Date: Mon, 14 Apr 2025 01:03:10 +0800 Subject: [PATCH 3/8] imgui.ini configuration file could not be generated (#182) --- src/game/frontend/Menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/frontend/Menu.cpp b/src/game/frontend/Menu.cpp index 8d71c83..f392acc 100644 --- a/src/game/frontend/Menu.cpp +++ b/src/game/frontend/Menu.cpp @@ -113,7 +113,7 @@ namespace YimMenu { auto& IO = ImGui::GetIO(); auto file_path = std::filesystem::path(std::getenv("appdata")) / "YimMenuV2" / "imgui.ini"; - std::string path = file_path.string(); + static auto path = file_path.string(); IO.IniFilename = path.c_str(); IO.LogFilename = NULL; ImFontConfig FontCfg{}; From 963bfe468a304d33c999485e0ef16fc400d335d2 Mon Sep 17 00:00:00 2001 From: Arthur <121949966+ShinyWasabi@users.noreply.github.com> Date: Sun, 13 Apr 2025 20:44:21 +0300 Subject: [PATCH 4/8] feat: Format Money (#180) * feat: Format Money * Code review changes --- src/core/hooking/CallHook.cpp | 78 +++++++++++++++++++++++ src/core/hooking/CallHook.hpp | 88 ++++++++++++++++++++++++++ src/game/features/self/FormatMoney.cpp | 75 ++++++++++++++++++++++ src/game/frontend/submenus/Self.cpp | 3 +- src/game/pointers/Pointers.cpp | 10 +++ src/game/pointers/Pointers.hpp | 2 + src/main.cpp | 2 + 7 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 src/core/hooking/CallHook.cpp create mode 100644 src/core/hooking/CallHook.hpp create mode 100644 src/game/features/self/FormatMoney.cpp 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/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/frontend/submenus/Self.cpp b/src/game/frontend/submenus/Self.cpp index 835ade3..55d5362 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)); diff --git a/src/game/pointers/Pointers.cpp b/src/game/pointers/Pointers.cpp index cc3591d..b5c6215 100644 --- a/src/game/pointers/Pointers.cpp +++ b/src/game/pointers/Pointers.cpp @@ -308,6 +308,16 @@ namespace YimMenu NetworkTime = 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..507f1ff 100644 --- a/src/game/pointers/Pointers.hpp +++ b/src/game/pointers/Pointers.hpp @@ -135,6 +135,8 @@ namespace YimMenu Functions::GetPresenceAttributes GetPresenceAttributes; Functions::GetAvatars GetAvatars; std::uint32_t* NetworkTime; + PVOID FormatIntCaller1; + PVOID FormatIntCaller2; }; struct Pointers : PointerData diff --git a/src/main.cpp b/src/main.cpp index 828802f..958eaa9 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" @@ -78,6 +79,7 @@ namespace YimMenu FiberPool::Destroy(); ScriptMgr::Destroy(); Hooking::Destroy(); + CallSiteHook::Destroy(); EARLY_UNLOAD: g_Running = false; From c8e51437474431e4753af3bab89f670d0f651af9 Mon Sep 17 00:00:00 2001 From: Arthur <121949966+ShinyWasabi@users.noreply.github.com> Date: Wed, 16 Apr 2025 23:44:58 +0300 Subject: [PATCH 5/8] Add Random Event ESP & Stuff (#193) * Add Random Event ESP & Stuff * changes * Use vector3::GetDistance --- src/game/features/network/ForceScriptHost.cpp | 25 +++ .../features/recovery/GiveVehicleReward.cpp | 2 +- src/game/frontend/ESP.cpp | 81 ++++++--- src/game/frontend/ESP.hpp | 1 + src/game/frontend/submenus/Network.cpp | 1 + .../submenus/network/RandomEvents.cpp | 163 +++++------------- src/game/gta/data/RandomEvents.hpp | 101 +++++++++++ src/game/pointers/Pointers.cpp | 5 + src/game/pointers/Pointers.hpp | 1 + src/types/script/Timer.cpp | 62 +++++++ src/types/script/Timer.hpp | 8 + 11 files changed, 312 insertions(+), 138 deletions(-) create mode 100644 src/game/features/network/ForceScriptHost.cpp create mode 100644 src/game/gta/data/RandomEvents.hpp create mode 100644 src/types/script/Timer.cpp 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/GiveVehicleReward.cpp b/src/game/features/recovery/GiveVehicleReward.cpp index 39634a7..1bac5a3 100644 --- a/src/game/features/recovery/GiveVehicleReward.cpp +++ b/src/game/features/recovery/GiveVehicleReward.cpp @@ -8,7 +8,7 @@ namespace YimMenu { - static ScriptFunction giveVehicleReward("GVR", "AM_MP_VEHICLE_REWARD"_J, "2D 0C 1E 00 00"); + static ScriptFunction giveVehicleReward("GiveVehicleReward", "AM_MP_VEHICLE_REWARD"_J, "2D 0C 1E 00 00"); bool GiveVehicleReward::IsSafeToRunScript() { 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/submenus/Network.cpp b/src/game/frontend/submenus/Network.cpp index 9bb0f96..0f4f47f 100644 --- a/src/game/frontend/submenus/Network.cpp +++ b/src/game/frontend/submenus/Network.cpp @@ -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/network/RandomEvents.cpp b/src/game/frontend/submenus/network/RandomEvents.cpp index f91d1d1..0cf3d83 100644 --- a/src/game/frontend/submenus/network/RandomEvents.cpp +++ b/src/game/frontend/submenus/network/RandomEvents.cpp @@ -4,6 +4,8 @@ #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" @@ -44,103 +46,8 @@ 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 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 - }; - - static ScriptFunction getNumFMMCVariations("GNFMMCV", "freemode"_J, "5D ? ? ? 01 72 02 39 04", 1, true); + static std::vector sendUpdateRECoordsTSECooldownPatches{}; + static ScriptFunction getNumFMMCVariations("GetNumFMMCVariations", "freemode"_J, "5D ? ? ? 01 72 02 39 04", 1, true); static GPBD_FM_2* GPBDFM2 = nullptr; static GSBD_RandomEvents* GSBDRandomEvents = nullptr; static RANDOM_EVENTS_FREEMODE_DATA* FMRandomEvents = nullptr; @@ -151,29 +58,14 @@ namespace YimMenu::Submenus static int setAvailability = 900000; static bool applyInMinutes = false; - static 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 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: @@ -220,7 +112,17 @@ namespace YimMenu::Submenus std::shared_ptr BuildRandomEventsMenu() { - auto menu = std::make_shared("Random Events"); + if (sendUpdateRECoordsTSECooldownPatches.empty()) + { + for (int event = DRUG_VEHICLE; event < MAX_EVENTS; event++) + sendUpdateRECoordsTSECooldownPatches.push_back(ScriptPatches::AddPatch(randomEventScripts[event], "43 88 13 2E 00 01", 0, {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([] { GPBDFM2 = GPBD_FM_2::Get(); @@ -308,6 +210,8 @@ namespace YimMenu::Submenus } }); } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Requires freemode script host."); ImGui::SameLine(); @@ -320,8 +224,26 @@ 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? + if (auto eventThread = Scripts::FindScriptThread(randomEventScripts[(int)selectedEvent])) + { + if (auto NetComponent = reinterpret_cast(eventThread)->m_NetComponent) + { + if (NetComponent->IsLocalPlayerHost()) + { + ScriptFunction setFMContentScriptServerState("SetFMContentScriptServerState", randomEventScripts[(int)selectedEvent], "5D ? ? ? 55 2E 00 5D", 1, true); + setFMContentScriptServerState.Call(3); + } + else + { + ScriptFunction setFMContentScriptClientState("SetFMContentScriptClientState", randomEventScripts[(int)selectedEvent], "5D ? ? ? 55 08 00 74", 1, true); + setFMContentScriptClientState.Call(3); + } + } + } + else + { + Notifications::Show("Random Events", "Event script is not active. Are you a participant?", NotificationType::Error); + } } else { @@ -399,6 +321,8 @@ namespace YimMenu::Submenus 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(); @@ -407,10 +331,15 @@ namespace YimMenu::Submenus 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/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 b5c6215..1bf98ce 100644 --- a/src/game/pointers/Pointers.cpp +++ b/src/game/pointers/Pointers.cpp @@ -308,6 +308,11 @@ 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(); diff --git a/src/game/pointers/Pointers.hpp b/src/game/pointers/Pointers.hpp index 507f1ff..51e98c1 100644 --- a/src/game/pointers/Pointers.hpp +++ b/src/game/pointers/Pointers.hpp @@ -135,6 +135,7 @@ namespace YimMenu Functions::GetPresenceAttributes GetPresenceAttributes; Functions::GetAvatars GetAvatars; std::uint32_t* NetworkTime; + std::uint32_t* GameTimer; PVOID FormatIntCaller1; PVOID FormatIntCaller2; }; diff --git a/src/types/script/Timer.cpp b/src/types/script/Timer.cpp new file mode 100644 index 0000000..91457d9 --- /dev/null +++ b/src/types/script/Timer.cpp @@ -0,0 +1,62 @@ +#include "Timer.hpp" +#include "game/pointers/Pointers.hpp" + +void TIMER::Initialize(bool localTimer) +{ + if (!IsInitialized) + { + Time = (*YimMenu::Pointers.IsSessionStarted && !localTimer) ? *YimMenu::Pointers.NetworkTime : *YimMenu::Pointers.GameTimer; + IsInitialized = true; + } +} + +void TIMER::Reset(bool localTimer) +{ + Time = (*YimMenu::Pointers.IsSessionStarted && !localTimer) ? *YimMenu::Pointers.NetworkTime : *YimMenu::Pointers.GameTimer; + IsInitialized = true; +} + +void TIMER::Destroy() +{ + IsInitialized = false; +} + +bool TIMER::_IsInitialized() +{ + return IsInitialized; +} + +bool TIMER::HasTimePassed(int ms, bool localTimer) +{ + if (ms < 0) + return true; + + Initialize(localTimer); + + int timePassed = (*YimMenu::Pointers.IsSessionStarted && !localTimer) ? (*YimMenu::Pointers.NetworkTime - Time) : (*YimMenu::Pointers.GameTimer - Time); + if (timePassed >= ms) + return true; + + return false; +} + +int TIMER::GetRemainingTime(int ms, bool localTimer) +{ + int timePassed = (*YimMenu::Pointers.IsSessionStarted && !localTimer) ? (*YimMenu::Pointers.NetworkTime - Time) : (*YimMenu::Pointers.GameTimer - Time); + return (ms - timePassed); +} + +std::string TIMER::GetRemainingTimeStr(int ms, bool localTimer) +{ + int timePassed = (*YimMenu::Pointers.IsSessionStarted && !localTimer) ? (*YimMenu::Pointers.NetworkTime - Time) : (*YimMenu::Pointers.GameTimer - Time); + int difference = (ms - timePassed); + int totalSeconds = difference / 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); +} \ No newline at end of file diff --git a/src/types/script/Timer.hpp b/src/types/script/Timer.hpp index 4e2b2b3..1b765de 100644 --- a/src/types/script/Timer.hpp +++ b/src/types/script/Timer.hpp @@ -5,5 +5,13 @@ struct TIMER { SCR_INT Time; SCR_BOOL IsInitialized; + + void Initialize(bool localTimer = false); + void Reset(bool localTimer = false); + void Destroy(); + bool _IsInitialized(); + bool HasTimePassed(int ms, bool localTimer = false); + int GetRemainingTime(int ms, bool localTimer = false); + std::string GetRemainingTimeStr(int ms, bool localTimer = false); }; static_assert(sizeof(TIMER) == 2 * 8); \ No newline at end of file From 6e6e151b92075d25827bb6950475065fad0df0ca Mon Sep 17 00:00:00 2001 From: kurumimeow Date: Sun, 20 Apr 2025 02:37:46 +0530 Subject: [PATCH 6/8] add heist modifier (#181) * add heist modifier * move cayo perico cooldown into heist tab * fixes - used tunables instead of globals for primary target - removed cayo perico "remove cooldown" as setup gets the job done --- src/core/commands/ListCommand.cpp | 6 + src/core/commands/ListCommand.hpp | 1 + .../features/recovery/CayoPericoCooldown.cpp | 19 - .../features/recovery/CayoPericoSetup.cpp | 47 --- .../recovery/Heist/ApartmentHeist.cpp | 132 +++++++ .../recovery/Heist/CayoPericoHeist.cpp | 257 ++++++++++++++ .../recovery/Heist/DiamondCasinoHeist.cpp | 333 ++++++++++++++++++ .../features/recovery/Heist/DoomsdayHeist.cpp | 141 ++++++++ src/game/features/teleport/TpToWaypoint.cpp | 1 - src/game/frontend/items/Items.hpp | 16 + src/game/frontend/items/TabItem.cpp | 22 ++ src/game/frontend/submenus/Network.cpp | 2 +- src/game/frontend/submenus/Recovery.cpp | 7 +- .../Recovery/Heist/ApartmentHeist.cpp | 33 ++ .../Recovery/Heist/ApartmentHeist.hpp | 6 + .../Recovery/Heist/CayoPericoHeist.cpp | 50 +++ .../Recovery/Heist/CayoPericoHeist.hpp | 6 + .../Recovery/Heist/DiamondCasinoHeist.cpp | 61 ++++ .../Recovery/Heist/DiamondCasinoHeist.hpp | 6 + .../submenus/Recovery/Heist/DoomsdayHeist.cpp | 33 ++ .../submenus/Recovery/Heist/DoomsdayHeist.hpp | 6 + .../submenus/Recovery/HeistModifier.cpp | 26 ++ .../submenus/Recovery/HeistModifier.hpp | 8 + src/game/gta/ScriptGlobal.hpp | 1 - src/game/gta/ScriptLocal.hpp | 8 + 25 files changed, 1154 insertions(+), 74 deletions(-) delete mode 100644 src/game/features/recovery/CayoPericoCooldown.cpp delete mode 100644 src/game/features/recovery/CayoPericoSetup.cpp create mode 100644 src/game/features/recovery/Heist/ApartmentHeist.cpp create mode 100644 src/game/features/recovery/Heist/CayoPericoHeist.cpp create mode 100644 src/game/features/recovery/Heist/DiamondCasinoHeist.cpp create mode 100644 src/game/features/recovery/Heist/DoomsdayHeist.cpp create mode 100644 src/game/frontend/items/TabItem.cpp create mode 100644 src/game/frontend/submenus/Recovery/Heist/ApartmentHeist.cpp create mode 100644 src/game/frontend/submenus/Recovery/Heist/ApartmentHeist.hpp create mode 100644 src/game/frontend/submenus/Recovery/Heist/CayoPericoHeist.cpp create mode 100644 src/game/frontend/submenus/Recovery/Heist/CayoPericoHeist.hpp create mode 100644 src/game/frontend/submenus/Recovery/Heist/DiamondCasinoHeist.cpp create mode 100644 src/game/frontend/submenus/Recovery/Heist/DiamondCasinoHeist.hpp create mode 100644 src/game/frontend/submenus/Recovery/Heist/DoomsdayHeist.cpp create mode 100644 src/game/frontend/submenus/Recovery/Heist/DoomsdayHeist.hpp create mode 100644 src/game/frontend/submenus/Recovery/HeistModifier.cpp create mode 100644 src/game/frontend/submenus/Recovery/HeistModifier.hpp 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/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/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/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/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/Network.cpp b/src/game/frontend/submenus/Network.cpp index 0f4f47f..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 { 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/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 }; From 2f1727530f86e0efa05e5942be761d88807a131a Mon Sep 17 00:00:00 2001 From: CSY0N <134310111+CSY0N@users.noreply.github.com> Date: Mon, 21 Apr 2025 23:55:21 +0200 Subject: [PATCH 7/8] Radio Station on Foot (#218) * mobile_radio.cpp * Update Self.cpp --- src/game/features/self/mobile_radio.cpp | 26 +++++++++++++++++++++++++ src/game/frontend/submenus/Self.cpp | 1 + 2 files changed, 27 insertions(+) create mode 100644 src/game/features/self/mobile_radio.cpp 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/frontend/submenus/Self.cpp b/src/game/frontend/submenus/Self.cpp index 55d5362..6b90c6a 100644 --- a/src/game/frontend/submenus/Self.cpp +++ b/src/game/frontend/submenus/Self.cpp @@ -38,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)); From 16a6bc9081857f11ff15d14b80d07b949946462d Mon Sep 17 00:00:00 2001 From: Arthur <121949966+ShinyWasabi@users.noreply.github.com> Date: Tue, 22 Apr 2025 00:56:57 +0300 Subject: [PATCH 8/8] Cache Script Pointers (#211) * Cache Script Pointers * Rewrite script pointers --- src/game/backend/ScriptPatches.cpp | 86 ++++--------------- src/game/backend/ScriptPatches.hpp | 44 +++------- src/game/backend/ScriptPointers.cpp | 75 ++++++++++++++++ src/game/backend/ScriptPointers.hpp | 50 +++++++++++ .../features/network/DisableDeathBarriers.cpp | 2 +- src/game/features/recovery/DLCVehicles.cpp | 6 +- .../features/recovery/GiveVehicleReward.cpp | 4 +- .../features/recovery/PlayAllMissionsSolo.cpp | 37 ++++---- src/game/features/system/DespawnBypass.cpp | 2 +- src/game/features/system/LSCCustomsBypass.cpp | 4 +- src/game/features/world/Weather.cpp | 2 +- src/game/frontend/submenus/Debug/Misc.cpp | 2 +- .../submenus/network/RandomEvents.cpp | 61 +++++++------ src/game/gta/Network.cpp | 4 +- src/game/gta/ScriptData.cpp | 61 +++++++++++++ src/game/gta/ScriptData.hpp | 40 +++++++++ src/game/gta/ScriptFunction.cpp | 72 +++++++++++----- src/game/gta/ScriptFunction.hpp | 65 ++++---------- src/game/gta/ScriptPointer.cpp | 67 +++++++++++++++ src/game/gta/ScriptPointer.hpp | 34 ++++++++ src/main.cpp | 3 + src/types/script/globals/Globals.cpp | 19 ++-- src/types/script/locals/Locals.cpp | 2 +- 23 files changed, 505 insertions(+), 237 deletions(-) create mode 100644 src/game/backend/ScriptPointers.cpp create mode 100644 src/game/backend/ScriptPointers.hpp create mode 100644 src/game/gta/ScriptData.cpp create mode 100644 src/game/gta/ScriptData.hpp create mode 100644 src/game/gta/ScriptPointer.cpp create mode 100644 src/game/gta/ScriptPointer.hpp 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/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 1bac5a3..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("GiveVehicleReward", "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/PlayAllMissionsSolo.cpp b/src/game/features/recovery/PlayAllMissionsSolo.cpp index f092f7e..c5b2e63 100644 --- a/src/game/features/recovery/PlayAllMissionsSolo.cpp +++ b/src/game/features/recovery/PlayAllMissionsSolo.cpp @@ -30,62 +30,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(); @@ -94,7 +94,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) @@ -102,37 +105,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}); } } 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/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/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/RandomEvents.cpp b/src/game/frontend/submenus/network/RandomEvents.cpp index 0cf3d83..e6ad5d7 100644 --- a/src/game/frontend/submenus/network/RandomEvents.cpp +++ b/src/game/frontend/submenus/network/RandomEvents.cpp @@ -8,7 +8,6 @@ #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" @@ -47,7 +46,6 @@ namespace YimMenu::Submenus }; static std::vector sendUpdateRECoordsTSECooldownPatches{}; - static ScriptFunction getNumFMMCVariations("GetNumFMMCVariations", "freemode"_J, "5D ? ? ? 01 72 02 39 04", 1, true); static GPBD_FM_2* GPBDFM2 = nullptr; static GSBD_RandomEvents* GSBDRandomEvents = nullptr; static RANDOM_EVENTS_FREEMODE_DATA* FMRandomEvents = nullptr; @@ -105,17 +103,51 @@ namespace YimMenu::Submenus static void OnComboChange() { - selectedSubvariation = 0; + 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() { if (sendUpdateRECoordsTSECooldownPatches.empty()) { for (int event = DRUG_VEHICLE; event < MAX_EVENTS; event++) - sendUpdateRECoordsTSECooldownPatches.push_back(ScriptPatches::AddPatch(randomEventScripts[event], "43 88 13 2E 00 01", 0, {0x71, 0x00, 0x00})); + { + 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) @@ -224,26 +256,7 @@ namespace YimMenu::Submenus } else if (GSBDRandomEvents->EventData[selectedEvent].State == eRandomEventState::ACTIVE) { - if (auto eventThread = Scripts::FindScriptThread(randomEventScripts[(int)selectedEvent])) - { - if (auto NetComponent = reinterpret_cast(eventThread)->m_NetComponent) - { - if (NetComponent->IsLocalPlayerHost()) - { - ScriptFunction setFMContentScriptServerState("SetFMContentScriptServerState", randomEventScripts[(int)selectedEvent], "5D ? ? ? 55 2E 00 5D", 1, true); - setFMContentScriptServerState.Call(3); - } - else - { - ScriptFunction setFMContentScriptClientState("SetFMContentScriptClientState", randomEventScripts[(int)selectedEvent], "5D ? ? ? 55 08 00 74", 1, true); - setFMContentScriptClientState.Call(3); - } - } - } - else - { - Notifications::Show("Random Events", "Event script is not active. Are you a participant?", NotificationType::Error); - } + KillActiveEvent(); } else { 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/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/main.cpp b/src/main.cpp index 958eaa9..4bb402e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,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" @@ -50,6 +51,8 @@ namespace YimMenu ScriptMgr::Init(); LOG(INFO) << "ScriptMgr initialized"; + ScriptPointers::Init(); + GUI::Init(); ScriptMgr::AddScript(std::make_unique