Merge branch 'enhanced' of https://github.com/YimMenu/YimMenuV2 into enhanced

This commit is contained in:
maybegreat48
2025-04-09 15:18:17 -04:00
12 changed files with 477 additions and 6 deletions
+2
View File
@@ -4,6 +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"
namespace YimMenu::Submenus
{
@@ -96,5 +97,6 @@ namespace YimMenu::Submenus
AddCategory(std::move(session));
AddCategory(std::move(spoofing));
AddCategory(std::move(BuildSavedPlayersMenu()));
AddCategory(BuildRandomEventsMenu());
}
}
@@ -0,0 +1,304 @@
#include "RandomEvents.hpp"
#include "core/backend/FiberPool.hpp"
#include "core/backend/ScriptMgr.hpp"
#include "core/frontend/Notifications.hpp"
#include "game/backend/Self.hpp"
#include "game/gta/Scripts.hpp"
#include "game/gta/ScriptFunction.hpp"
#include "game/pointers/Pointers.hpp"
#include "types/script/globals/GSBD_RandomEvents.hpp"
#include "types/script/locals/FMRandomEvents.hpp"
#include "types/script/ScriptEvent.hpp"
#include "types/script/GtaThread.hpp"
#include "types/script/CGameScriptHandlerNetComponent.hpp"
#include "types/network/CNetGamePlayer.hpp"
namespace YimMenu::Submenus
{
enum eRandomEvent
{
DRUG_VEHICLE,
MOVIE_PROPS,
GOLDEN_GUN,
VEHICLE_LIST,
SLASHER,
PHANTOM_CAR,
SIGHTSEEING,
SMUGGLER_TRAIL,
CERBERUS,
SMUGGLER_PLANE,
CRIME_SCENE,
METAL_DETECTOR,
CONVOY,
ROBBERY,
XMAS_MUGGER,
BANK_SHOOTOUT,
ARMOURED_TRUCK,
POSSESSED_ANIMALS,
GHOSTHUNT,
XMAS_TRUCK,
COMMUNITY_OUTREACH,
MAX_EVENTS
};
static constexpr std::array randomEventNames = {
"Drug Vehicle",
"Movie Props",
"Sleeping Guard",
"Exotic Exports",
"The Slashers",
"Phantom Car",
"Sightseeing",
"Smuggler Trail",
"Cerberus Surprise",
"Smuggler Plane",
"Crime Scene",
"Metal Detector",
"Finders Keepers",
"Shop Robbery",
"The Gooch",
"Weazel Plaza Shootout",
"Armored Truck",
"Possessed Animals",
"Ghosts Exposed",
"Happy Holidays Hauler",
"Community Outreach"
};
static constexpr std::array RandomEventScripts = {
"fm_content_drug_vehicle"_J,
"fm_content_movie_props"_J,
"fm_content_golden_gun"_J,
"fm_content_vehicle_list"_J,
"fm_content_slasher"_J,
"fm_content_phantom_car"_J,
"fm_content_sightseeing"_J,
"fm_content_smuggler_trail"_J,
"fm_content_cerberus"_J,
"fm_content_smuggler_plane"_J,
"fm_content_crime_scene"_J,
"fm_content_metal_detector"_J,
"fm_content_convoy"_J,
"fm_content_robbery"_J,
"fm_content_xmas_mugger"_J,
"fm_content_bank_shootout"_J,
"fm_content_armoured_truck"_J,
"fm_content_possessed_animals"_J,
"fm_content_ghosthunt"_J,
"fm_content_xmas_truck"_J,
"fm_content_community_outreach"_J
};
static YimMenu::ScriptFunction getNumFMMCVariations("GMFV", "freemode"_J, "5D ? ? ? 01 72 02 39 04", 1, true);
static GSBD_RandomEvents* GSBDRandomEvents = nullptr;
static RANDOM_EVENTS_FREEMODE_DATA* FMRandomEvents = nullptr;
static eRandomEvent selectedEvent = DRUG_VEHICLE;
static int selectedSubvariation = 0;
static int numSubVariations = 30;
std::string GetEventTimeLeftStr(int eventTime, int timer)
{
int timePassed = (*Pointers.NetworkTime - timer) < 0 ? -(*Pointers.NetworkTime - timer) : (*Pointers.NetworkTime - timer);
int diff = (eventTime - timePassed);
int totalSeconds = diff / 1000;
int hours = totalSeconds / 3600;
int minutes = (totalSeconds % 3600) / 60;
int seconds = totalSeconds % 60;
if (hours < 1)
return std::format("{:02}:{:02}", minutes, seconds);
else
return std::format("{:02}:{:02}:{:02}", hours, minutes, seconds);
}
static 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);
case eRandomEventState::AVAILABLE:
return "Available - deactivating in " + GetEventTimeLeftStr(FMRandomEvents->EventData[selectedEvent].AvailableTime, GSBDRandomEvents->EventData[selectedEvent].TimerState.Time);
case eRandomEventState::ACTIVE:
return "Active";
case eRandomEventState::CLEANUP:
return "Cleanup";
}
return "N/A";
}
std::shared_ptr<Category> BuildRandomEventsMenu()
{
auto menu = std::make_shared<Category>("Random Events");
menu->AddItem(std::make_unique<ImGuiItem>([] {
GSBDRandomEvents = nullptr;
FMRandomEvents = nullptr;
GSBDRandomEvents = GSBD_RandomEvents::Get();
if (!GSBDRandomEvents)
return ImGui::Text("Freemode global block is not loaded yet.");
if (GSBDRandomEvents->InitState != eRandomEventInitState::INITIALIZED)
return ImGui::Text("Random Events are not initialized yet.");
if (auto freemode = Scripts::FindScriptThread("freemode"_J))
{
FMRandomEvents = RANDOM_EVENTS_FREEMODE_DATA::Get(freemode);
if (!FMRandomEvents)
return ImGui::Text("Freemode stack is not valid.");
}
else
{
return ImGui::Text("Freemode is not running.");
}
if (ImGui::BeginCombo("Select Event", randomEventNames[selectedEvent]))
{
for (int event = DRUG_VEHICLE; event < MAX_EVENTS; event++)
{
switch (GSBDRandomEvents->EventData[event].State)
{
case eRandomEventState::INACTIVE:
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f));
break;
case eRandomEventState::AVAILABLE:
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
break;
case eRandomEventState::ACTIVE:
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 1.0f, 0.0f, 1.0f));
break;
}
if (ImGui::Selectable(randomEventNames[event], event == selectedEvent))
{
FiberPool::Push([event] {
selectedEvent = (eRandomEvent)event;
numSubVariations = getNumFMMCVariations.Call<int>(FMRandomEvents->MissionData.FMMCData[selectedEvent].FMMCType, 0) - 1;
});
}
ImGui::PopStyleColor();
}
ImGui::EndCombo();
}
if (ImGui::InputInt(std::format("Select Location (0-{})", numSubVariations).c_str(), &selectedSubvariation))
{
selectedSubvariation = std::clamp(selectedSubvariation, 0, numSubVariations);
}
if (ImGui::Button("Launch Event"))
{
FiberPool::Push([] {
if (GSBDRandomEvents->EventData[selectedEvent].State != eRandomEventState::ACTIVE)
{
SCRIPT_EVENT_REQUEST_RANDOM_EVENT eventData;
eventData.FMMCType = FMRandomEvents->MissionData.FMMCData[selectedEvent].FMMCType;
eventData.Subvariation = selectedSubvariation;
eventData.PlayersToSend = 1; // Set FORCE_LAUNCH bit of all players
eventData.Send();
ScriptMgr::Yield(100ms);
if (GSBDRandomEvents->EventData[selectedEvent].State == eRandomEventState::INACTIVE)
{
Notifications::Show("Random Events", "Failed to launch event. Are you freemode host?", NotificationType::Error);
}
}
else
{
Notifications::Show("Random Events", "Event is already active.", NotificationType::Error);
}
});
}
if (ImGui::IsItemHovered())
{
ImGui::SetTooltip("Force launches the event.");
}
ImGui::SameLine();
if (ImGui::Button("Kill Event"))
{
FiberPool::Push([] {
if (GSBDRandomEvents->EventData[selectedEvent].State == eRandomEventState::AVAILABLE)
{
GSBDRandomEvents->EventData[selectedEvent].State = eRandomEventState::CLEANUP;
}
else if (GSBDRandomEvents->EventData[selectedEvent].State == eRandomEventState::ACTIVE)
{
ScriptFunction terminateFMContentScript("TFMCS", RandomEventScripts[(int)selectedEvent], "2D 01 05 00 00 5D ? ? ?");
terminateFMContentScript.Call<void>(false); // Is it ok to do this when we're not host?
}
else
{
Notifications::Show("Random Events", "Event is not active.", NotificationType::Error);
}
});
}
if (ImGui::IsItemHovered())
{
ImGui::SetTooltip("Terminates the event.");
}
ImGui::SameLine();
if (ImGui::Button("Teleport to Event"))
{
FiberPool::Push([] {
if (GSBDRandomEvents->EventData[selectedEvent].State >= eRandomEventState::AVAILABLE)
{
auto coords = GSBDRandomEvents->EventData[selectedEvent].TriggerPosition;
if (coords != Vector3(0.f, 0.f, 0.f))
{
Self::GetPed().TeleportTo(coords);
}
else // Either update event coords TSE not sent yet or event doesn't register a trigger point
{
Notifications::Show("Random Events", "Failed to teleport to event. Coordinates are not valid.", NotificationType::Error);
}
}
else
{
Notifications::Show("Random Events", "Event is not active.", NotificationType::Error);
}
});
}
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 netComponent = reinterpret_cast<GtaThread*>(eventThread)->m_NetComponent)
{
if (auto host = netComponent->GetHost())
{
ImGui::Text("Host: %s", host->GetName());
}
ImGui::SameLine();
ImGui::BeginDisabled(netComponent->IsLocalPlayerHost());
if (ImGui::SmallButton("Take Control"))
{
FiberPool::Push([eventThread] {
Scripts::ForceScriptHost(eventThread);
});
}
ImGui::EndDisabled();
}
}
}
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
}));
return menu;
}
}
@@ -0,0 +1,8 @@
#pragma once
#include "core/frontend/manager/Category.hpp"
#include "game/frontend/items/Items.hpp"
namespace YimMenu::Submenus
{
std::shared_ptr<Category> BuildRandomEventsMenu();
}
+6 -2
View File
@@ -2,10 +2,12 @@
namespace YimMenu
{
ScriptFunction::ScriptFunction(const std::string& name, const joaat_t script, const SimplePattern& pattern) :
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)
{
}
@@ -17,7 +19,9 @@ namespace YimMenu
if (auto location = Scripts::GetCodeLocationByPattern(program, m_Pattern))
{
m_PC = location.value();
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
+8 -1
View File
@@ -13,6 +13,8 @@ namespace YimMenu
{
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;
@@ -24,8 +26,13 @@ namespace YimMenu
*reinterpret_cast<std::remove_cv_t<std::remove_reference_t<Arg>>*>(reinterpret_cast<std::uint64_t*>(stack) + (stackPtr++)) = std::forward<Arg>(value);
}
std::uint32_t ReadThreeByte(std::uint8_t* arr)
{
return arr[0] + (arr[1] << 8) + (arr[2] << 16);
}
public:
ScriptFunction(const std::string& name, const joaat_t script, const SimplePattern& pattern);
ScriptFunction(const std::string& name, const joaat_t script, const SimplePattern& pattern, std::int32_t offset = 0, bool rip = false);
template<typename Ret, typename... Args>
Ret Call(Args... args)
+5
View File
@@ -303,6 +303,11 @@ namespace YimMenu
GetSessionByGamerHandle = ptr.Sub(0x4A).Add(1).Rip().As<Functions::GetSessionByGamerHandle>();
});
constexpr auto networkTimePtrn = Pattern<"89 05 ? ? ? ? 80 3D ? ? ? ? ? 0F 84 ? ? ? ? E9">("NetworkTime");
scanner.Add(networkTimePtrn, [this](PointerCalculator ptr) {
NetworkTime = ptr.Add(2).Rip().As<std::uint32_t*>();
});
if (!scanner.Scan())
{
LOG(FATAL) << "Some patterns could not be found, unloading.";
+1
View File
@@ -134,6 +134,7 @@ namespace YimMenu
Functions::GetSessionByGamerHandle GetSessionByGamerHandle;
Functions::GetPresenceAttributes GetPresenceAttributes;
Functions::GetAvatars GetAvatars;
std::uint32_t* NetworkTime;
};
struct Pointers : PointerData
+14 -1
View File
@@ -76,7 +76,9 @@ enum class ScriptEventIndex
TriggerCEORaid = -1906536929,
StartScriptBegin = -366707054,
StartScriptProceed = 1757622014
StartScriptProceed = 1757622014,
RequestRandomEvent = -126218586
};
struct SCRIPT_EVENT
@@ -169,4 +171,15 @@ struct SCRIPT_EVENT_SEND_TO_INTERIOR : public SCRIPT_EVENT
};
static_assert(sizeof(SCRIPT_EVENT_SEND_TO_INTERIOR) == 16 * 8);
struct SCRIPT_EVENT_REQUEST_RANDOM_EVENT : public SCRIPT_EVENT
{
REGISTER_SCRIPT_EVENT(SCRIPT_EVENT_REQUEST_RANDOM_EVENT, RequestRandomEvent);
SCR_INT FMMCType;
SCR_INT Variation;
SCR_INT Subvariation;
SCR_INT PlayersToSend;
};
static_assert(sizeof(SCRIPT_EVENT_REQUEST_RANDOM_EVENT) == 7 * 8);
#undef REGISTER_SCRIPT_EVENT
@@ -0,0 +1,72 @@
#pragma once
#include "../Timer.hpp"
#include "../types.hpp"
enum class eRandomEventInitState
{
INITIALIZING,
INITIALIZED
};
enum class eRandomEventState
{
INACTIVE,
AVAILABLE,
ACTIVE,
CLEANUP
};
enum class eRandomEventBitset
{
TRIGGERED_BY_CLIENT,
FORCE_LAUNCH,
UNK_2, // unused
FORCE_CLEANUP
};
struct RANDOM_EVENTS_RESERVATION_DATA
{
SCR_INT ReservedPeds; // 0
SCR_INT ReservedVehicles; // 1
SCR_INT ReservedObjects; // 2
};
static_assert(sizeof(RANDOM_EVENTS_RESERVATION_DATA) == 3 * 8);
struct RANDOM_EVENTS_DATA
{
alignas(8) eRandomEventState State; // 0
TIMER TimerState; // 1
SCR_ARRAY<SCR_BITSET<eRandomEventBitset>, 1> Bitset; // 3
SCR_INT Variation; // 5
SCR_INT Subvariation; // 6
RANDOM_EVENTS_RESERVATION_DATA ReservationData; // 7
SCR_VEC3 TriggerPosition; // 10
SCR_FLOAT TriggerRange; // 13
SCR_INT Retries; // 14
};
static_assert(sizeof(RANDOM_EVENTS_DATA) == 15 * 8);
struct RANDOM_EVENTS_VEHICLE_LIST_DATA
{
SCR_INT UNK_0000; // 0
SCR_INT Variation; // 1
};
static_assert(sizeof(RANDOM_EVENTS_VEHICLE_LIST_DATA) == 2 * 8);
struct RANDOM_EVENTS_TARGET_DATA
{
PLAYER_INDEX PhantomCarTarget; // 0
PLAYER_INDEX XMASMuggerTarget; // 1
};
static_assert(sizeof(RANDOM_EVENTS_TARGET_DATA) == 2 * 8);
struct GSBD_RandomEvents
{
alignas(8) eRandomEventInitState InitState; // 0
SCR_ARRAY<RANDOM_EVENTS_DATA, 21> EventData; // 1
RANDOM_EVENTS_VEHICLE_LIST_DATA VehicleListData; // 317
RANDOM_EVENTS_TARGET_DATA TargetData; // 319
static GSBD_RandomEvents* Get();
};
static_assert(sizeof(GSBD_RandomEvents) == 321 * 8);
+3 -1
View File
@@ -10,6 +10,7 @@
#include "GSBD_Kicking.hpp"
#include "GSBD_PropertyInstances.hpp"
#include "g_AMC_playerBD.hpp"
#include "GSBD_RandomEvents.hpp"
#include "game/gta/ScriptGlobal.hpp"
#define DEFINE_GLOBAL_ACCESSOR(str, global) str* str::Get()\
@@ -31,4 +32,5 @@ DEFINE_GLOBAL_ACCESSOR(GSBD_FM, 1835557);
DEFINE_GLOBAL_ACCESSOR(GSBD_FM_Events, 1917230);
DEFINE_GLOBAL_ACCESSOR(GSBD_Kicking, 1877529);
DEFINE_GLOBAL_ACCESSOR(GSBD_PropertyInstances, 1937699);
DEFINE_GLOBAL_ACCESSOR(g_AMC_playerBD, 2699624);
DEFINE_GLOBAL_ACCESSOR(g_AMC_playerBD, 2699624);
DEFINE_GLOBAL_ACCESSOR(GSBD_RandomEvents, 1882524);
@@ -0,0 +1,51 @@
#pragma once
#include "../types.hpp"
#include "../Timer.hpp"
namespace rage
{
class scrThread;
}
enum class eRandomEventFlags
{
RESTART = 2,
DISABLE_DISTANCE_CHECK = 4
};
struct RANDOM_EVENTS_LOCAL_DATA
{
SCR_INT RegisteredEvents; // 0
SCR_ARRAY<uint64_t, 1> TriggerPtr; // 1
SCR_INT TriggerPositionPtr; // 3
SCR_INT TriggerRangePtr; // 4
SCR_INT AvailablePtr; // 5
SCR_INT InactiveTime; // 6
SCR_INT AvailableTime; // 7
SCR_BITSET<eRandomEventFlags> Flags; // 8
TIMER LastTriggerAttempt; // 9
SCR_INT UNK_0011; // 11 (unused, ranges from 0 to 4)
};
static_assert(sizeof(RANDOM_EVENTS_LOCAL_DATA) == 12 * 8);
struct RANDOM_EVENTS_FMMC_DATA
{
SCR_INT FMMCType; // 0
};
static_assert(sizeof(RANDOM_EVENTS_FMMC_DATA) == 1 * 8);
struct RANDOM_EVENTS_MISSION_DATA
{
SCR_INT EventsCount; // 0
SCR_ARRAY<RANDOM_EVENTS_FMMC_DATA, 21> FMMCData; // 1
};
static_assert(sizeof(RANDOM_EVENTS_MISSION_DATA) == 23 * 8);
struct RANDOM_EVENTS_FREEMODE_DATA
{
SCR_ARRAY<RANDOM_EVENTS_LOCAL_DATA, 21> EventData; // 0
RANDOM_EVENTS_MISSION_DATA MissionData; // 253
static RANDOM_EVENTS_FREEMODE_DATA* Get(rage::scrThread* thread);
};
static_assert(sizeof(RANDOM_EVENTS_FREEMODE_DATA) == 276 * 8);
+3 -1
View File
@@ -1,4 +1,5 @@
#include "VehicleRewardData.hpp"
#include "FMRandomEvents.hpp"
#include "game/gta/ScriptLocal.hpp"
#define DEFINE_LOCAL_ACCESSOR(str, local) \
@@ -10,4 +11,5 @@
return lcl.As<str*>(); \
}
DEFINE_LOCAL_ACCESSOR(VEHICLE_REWARD_DATA, 129);
DEFINE_LOCAL_ACCESSOR(VEHICLE_REWARD_DATA, 129);
DEFINE_LOCAL_ACCESSOR(RANDOM_EVENTS_FREEMODE_DATA, 15919);