feat(hotkeys): implement hotkeys and more

This commit is contained in:
maybegreat48
2025-03-16 17:40:29 -04:00
parent 9530fd24c0
commit 03a13f100d
28 changed files with 772 additions and 39 deletions
+35 -26
View File
@@ -88,43 +88,52 @@ namespace YimMenu
MarkStateDirty();
}
void HotkeySystem::Update()
void HotkeySystem::RunScriptImpl()
{
for (auto& [hash, link] : m_CommandHotkeys)
while (g_Running)
{
if (link.m_Chain.empty() || link.m_BeingModified)
continue;
bool all_keys_pressed = true;
for (auto modifier : link.m_Chain)
for (auto& [hash, link] : m_CommandHotkeys)
{
if (!(GetAsyncKeyState(modifier) & 0x8000))
if (link.m_Chain.empty() || link.m_BeingModified)
continue;
bool all_keys_pressed = true;
for (auto modifier : link.m_Chain)
{
all_keys_pressed = false;
}
}
if (all_keys_pressed && std::chrono::system_clock::now() - m_LastHotkeyTriggerTime > 100ms)
{
auto command = Commands::GetCommand(hash);
if (command)
{
// TODO: this is the only way I can prevent chat from blocking the main loop while keeping everything else fast
if (hash != "chathelper"_J)
command->Call();
else
if (!(GetAsyncKeyState(modifier) & 0x8000))
{
FiberPool::Push([command] {
command->Call();
});
all_keys_pressed = false;
}
}
m_LastHotkeyTriggerTime = std::chrono::system_clock::now();
if (all_keys_pressed && std::chrono::system_clock::now() - m_LastHotkeyTriggerTime > 100ms)
{
auto command = Commands::GetCommand(hash);
if (command)
{
// TODO: this is the only way I can prevent chat from blocking the main loop while keeping everything else fast
if (hash != "chathelper"_J)
command->Call();
else
{
FiberPool::Push([command] {
command->Call();
});
}
}
m_LastHotkeyTriggerTime = std::chrono::system_clock::now();
}
}
ScriptMgr::Yield();
}
}
void HotkeySystem::RunScript()
{
g_HotkeySystem.RunScriptImpl();
}
void HotkeySystem::SaveStateImpl(nlohmann::json& state)
{
for (auto& hotkey : m_CommandHotkeys)
+2 -1
View File
@@ -26,7 +26,8 @@ namespace YimMenu
std::string GetHotkeyLabel(int hotkey_modifiers);
void CreateHotkey(std::vector<int>& Hotkey);
void Update();
void RunScriptImpl();
static void RunScript();
virtual void SaveStateImpl(nlohmann::json& state) override;
virtual void LoadStateImpl(nlohmann::json& state) override;
+1 -1
View File
@@ -18,7 +18,7 @@ namespace YimMenu
const char* LogSink::GetLevelStr(const eLogLevel level)
{
constexpr std::array<const char*, 4> levelStrings = {{{"DEBUG"}, {"INFO"}, {"WARN"}, {"ERROR"}}};
constexpr std::array<const char*, 4> levelStrings = {"DEBUG", "INFO", "WARN", "ERROR"};
return levelStrings[level];
}
+2
View File
@@ -31,10 +31,12 @@ namespace YimMenu
ImGui::DestroyContext();
#if 0
// manually destroy the allocators we created for the rest of the frame contexts
for (size_t i = 1; i < m_SwapChainDesc.BufferCount; ++i)
if (m_FrameContext[i].CommandAllocator)
m_FrameContext[i].CommandAllocator->Release();
#endif
}
bool Renderer::InitDX12()
+113
View File
@@ -0,0 +1,113 @@
#include "SavedLocations.hpp"
#include "core/filemgr/FileMgr.hpp"
#include "core/frontend/Notifications.hpp"
namespace YimMenu
{
std::filesystem::path SavedLocations::GetSavedLocationsFilePath()
{
return FileMgr::GetProjectFile("./saved_locations.json").Path();
}
std::vector<SavedLocation> SavedLocations::SavedLocationsFilteredListImpl(std::string filter)
{
std::vector<SavedLocation> filterList{};
static auto toLower = [=](std::string text) -> std::string {
std::transform(text.begin(), text.end(), text.begin(), ::tolower);
return text;
};
for (auto& location : m_AllSavedLocations | std::views::values | std::views::join)
if (toLower(location.name).find(toLower(filter)) != std::string::npos)
filterList.push_back(location);
return filterList;
}
bool SavedLocations::FetchSavedLocationsImpl()
{
m_AllSavedLocations.clear();
auto path = GetSavedLocationsFilePath();
std::ifstream file(path, std::ios::binary);
try
{
if (!file.is_open())
return false;
nlohmann::json j;
file >> j;
m_AllSavedLocations = j.get<std::map<std::string, std::vector<SavedLocation>>>();
return true;
}
catch (const std::exception& e)
{
LOG(WARNING) << "Failed fetching saved locations: " << e.what() << '\n';
return false;
}
return false;
}
bool SavedLocations::SaveNewLocationImpl(const std::string& category, SavedLocation t)
{
const auto& pair = m_AllSavedLocations.insert({category, {t}});
if (!pair.second)
{
pair.first->second.push_back(t);
}
auto path = GetSavedLocationsFilePath();
std::ofstream fileOut(path, std::ofstream::trunc | std::ofstream::binary);
if (!fileOut.is_open())
return false;
nlohmann::json j = m_AllSavedLocations;
fileOut << j.dump(4);
fileOut.close();
return true;
}
bool SavedLocations::DeleteSavedLocationImpl(const std::string& category, const std::string& locationName)
{
auto path = GetSavedLocationsFilePath();
const auto& it = m_AllSavedLocations.find(category);
if (it == m_AllSavedLocations.end())
return false;
std::erase_if(it->second, [locationName](SavedLocation t) {
return t.name == locationName;
});
if (!it->second.size())
{
m_AllSavedLocations.erase(category);
}
std::ofstream fileOut(path, std::ofstream::trunc | std::ofstream::binary);
if (!fileOut.is_open())
return false;
nlohmann::json j = m_AllSavedLocations;
fileOut << j.dump(4);
fileOut.close();
return true;
}
SavedLocation* SavedLocations::GetSavedLocationByNameImpl(std::string name)
{
for (auto& loc : SavedLocationsFilteredList())
if (loc.name == name)
return &loc;
return nullptr;
}
}
+65
View File
@@ -0,0 +1,65 @@
#pragma once
namespace YimMenu
{
struct SavedLocation
{
std::string name;
float x, y, z;
float yaw = 0.0f, pitch = 0.0f, roll = 0.0f;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(SavedLocation, name, x, y, z, yaw, pitch, roll);
class SavedLocations
{
private:
std::filesystem::path GetSavedLocationsFilePath();
std::map<std::string, std::vector<SavedLocation>> m_AllSavedLocations;
private:
bool FetchSavedLocationsImpl();
bool SaveNewLocationImpl(const std::string& category, SavedLocation t);
bool DeleteSavedLocationImpl(const std::string& category, const std::string& location_name);
SavedLocation* GetSavedLocationByNameImpl(std::string);
std::vector<SavedLocation> SavedLocationsFilteredListImpl(std::string filter = "");
static SavedLocations& GetInstance()
{
static SavedLocations instance{};
return instance;
}
public:
static std::map<std::string, std::vector<SavedLocation>>& GetAllSavedLocations()
{
return GetInstance().m_AllSavedLocations;
}
static bool FetchSavedLocations()
{
return GetInstance().FetchSavedLocationsImpl();
};
static bool SaveNewLocation(const std::string& category, SavedLocation t)
{
return GetInstance().SaveNewLocationImpl(category, t);
};
static bool DeleteSavedLocation(const std::string& category, const std::string& location_name)
{
return GetInstance().DeleteSavedLocationImpl(category, location_name);
};
static SavedLocation* GetSavedLocationByName(std::string location_name)
{
return GetInstance().GetSavedLocationByNameImpl(location_name);
};
static std::vector<SavedLocation> SavedLocationsFilteredList(std::string filter = "")
{
return GetInstance().SavedLocationsFilteredListImpl(filter);
};
};
}
+26
View File
@@ -0,0 +1,26 @@
#include "core/commands/LoopedCommand.hpp"
#include "game/backend/Self.hpp"
#include "game/gta/Scripts.hpp"
#include "types/script/globals/GlobalPlayerBD.hpp"
namespace YimMenu::Features
{
class OffTheRadar : public LoopedCommand
{
using LoopedCommand::LoopedCommand;
virtual void OnTick() override
{
if (auto gpbd = GlobalPlayerBD::Get(); gpbd && Scripts::SafeToModifyFreemodeBroadcastGlobals())
gpbd->Entries[Self::GetPlayer().GetId()].OffRadarActive = true;
}
virtual void OnDisable() override
{
if (auto gpbd = GlobalPlayerBD::Get(); gpbd && Scripts::SafeToModifyFreemodeBroadcastGlobals())
gpbd->Entries[Self::GetPlayer().GetId()].OffRadarActive = false;
}
};
static OffTheRadar _OffTheRadar{"otr", "Off The Radar", "Hides you from other players on the radar"};
}
@@ -0,0 +1,61 @@
#include "core/commands/Command.hpp"
#include "core/backend/ScriptMgr.hpp"
#include "game/backend/Self.hpp"
#include "game/gta/Natives.hpp"
#include "core/frontend/Notifications.hpp"
namespace YimMenu::Features
{
void ResolveZCoordinate(Vector3& vec)
{
constexpr float max_ground_check = 1000.f;
constexpr int max_attempts = 20;
float ground_z = vec.z;
int current_attempts = 0;
do
{
STREAMING::REQUEST_COLLISION_AT_COORD(vec.x, vec.y, vec.z);
float water_height;
if (WATER::GET_WATER_HEIGHT(vec.x, vec.y, vec.z, &water_height))
{
vec.z = water_height;
return;
}
if (MISC::GET_GROUND_Z_FOR_3D_COORD(vec.x, vec.y, max_ground_check, &ground_z, false, false))
{
vec.z = ground_z + 1.0f;
return;
}
if (current_attempts % 3 == 0)
{
ground_z += 25.f;
}
++current_attempts;
ScriptMgr::Yield();
} while (current_attempts < max_attempts);
vec.z = PATHFIND::GET_APPROX_HEIGHT_FOR_POINT(vec.x, vec.y); // fallback value
}
class TpToWaypoint : public Command
{
using Command::Command;
virtual void OnCall() override
{
if (!HUD::IS_WAYPOINT_ACTIVE())
Notifications::Show("Teleport", "No waypoint set", NotificationType::Warning);
auto coords = HUD::GET_BLIP_COORDS(HUD::GET_CLOSEST_BLIP_INFO_ID(HUD::GET_WAYPOINT_BLIP_ENUM_ID()));
ResolveZCoordinate(coords);
Self::GetPed().TeleportTo(coords);
}
};
static TpToWaypoint _TpToWaypoint{"tptowaypoint", "Teleport to Waypoint", "Teleports you to the waypoint"};
}
+19
View File
@@ -0,0 +1,19 @@
#include "core/commands/LoopedCommand.hpp"
#include "game/backend/Self.hpp"
namespace YimMenu::Features
{
class KeepFixed : public LoopedCommand
{
using LoopedCommand::LoopedCommand;
virtual void OnTick() override
{
auto veh = Self::GetVehicle();
if (veh)
veh.Fix();
}
};
static KeepFixed _KeepFixed{"keepfixed", "Keep Vehicle Fixed", "Keeps your vehicle clean and fixed"};
}
+78
View File
@@ -0,0 +1,78 @@
#include "core/commands/LoopedCommand.hpp"
#include "game/backend/Self.hpp"
#include "game/gta/Natives.hpp"
namespace YimMenu::Features
{
class Speedometer : public LoopedCommand
{
using LoopedCommand::LoopedCommand;
int m_ScaleformHandle{};
bool EnsureScaleformLoaded()
{
if (m_ScaleformHandle && GRAPHICS::HAS_SCALEFORM_MOVIE_LOADED(m_ScaleformHandle))
return true;
if (!m_ScaleformHandle)
m_ScaleformHandle = GRAPHICS::REQUEST_SCALEFORM_MOVIE("DRAG_RACE");
return false;
}
int GetVehicleSpeed(Vehicle veh)
{
auto speed = veh.GetSpeed();
if (MISC::SHOULD_USE_METRIC_MEASUREMENTS())
return speed * 3.6f;
else
return speed * 2.23694f;
}
virtual void OnTick() override
{
auto veh = Self::GetVehicle();
if (!veh || !EnsureScaleformLoaded() || STREAMING::IS_PLAYER_SWITCH_IN_PROGRESS())
return;
GRAPHICS::BEGIN_SCALEFORM_MOVIE_METHOD(m_ScaleformHandle, "SET_GEAR");
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(veh.GetGear());
GRAPHICS::END_SCALEFORM_MOVIE_METHOD();
GRAPHICS::BEGIN_SCALEFORM_MOVIE_METHOD(m_ScaleformHandle, "SET_SPEED");
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(GetVehicleSpeed(veh));
GRAPHICS::END_SCALEFORM_MOVIE_METHOD();
GRAPHICS::BEGIN_SCALEFORM_MOVIE_METHOD(m_ScaleformHandle, "SET_SPEED_UNITS");
GRAPHICS::BEGIN_TEXT_COMMAND_SCALEFORM_STRING("STRING");
HUD::ADD_TEXT_COMPONENT_SUBSTRING_PLAYER_NAME(MISC::SHOULD_USE_METRIC_MEASUREMENTS() ? "KPH" : "MPH");
GRAPHICS::END_TEXT_COMMAND_SCALEFORM_STRING();
GRAPHICS::BEGIN_SCALEFORM_MOVIE_METHOD(m_ScaleformHandle, "SET_METER_VALUE");
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(veh.GetSpeed() * 2000);
GRAPHICS::END_SCALEFORM_MOVIE_METHOD();
GRAPHICS::BEGIN_SCALEFORM_MOVIE_METHOD(m_ScaleformHandle, "SET_OUTER_GOAL");
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(-1.0f);
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(-1.0f);
GRAPHICS::END_SCALEFORM_MOVIE_METHOD();
GRAPHICS::BEGIN_SCALEFORM_MOVIE_METHOD(m_ScaleformHandle, "SET_INNER_GOAL");
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(-1.0f);
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(-1.0f);
GRAPHICS::END_SCALEFORM_MOVIE_METHOD();
GRAPHICS::BEGIN_SCALEFORM_MOVIE_METHOD(m_ScaleformHandle, "SET_SCREEN_POSITION");
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(1.0f);
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_FLOAT(0.85f);
GRAPHICS::END_SCALEFORM_MOVIE_METHOD();
GRAPHICS::BEGIN_SCALEFORM_MOVIE_METHOD(m_ScaleformHandle, "SET_IS_DRIFT_RACE");
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_BOOL(false);
GRAPHICS::END_SCALEFORM_MOVIE_METHOD();
GRAPHICS::DRAW_SCALEFORM_MOVIE_FULLSCREEN(m_ScaleformHandle, 255, 255, 255, 255, 0);
}
virtual void OnDisable() override
{
GRAPHICS::SET_SCALEFORM_MOVIE_AS_NO_LONGER_NEEDED(&m_ScaleformHandle);
}
};
static Speedometer _Speedometer{"speedometer", "Speedometer", "Shows a speedometer whenever you are in a vehicle"};
}
+4
View File
@@ -8,8 +8,10 @@
#include "game/frontend/fonts/Fonts.hpp"
#include "game/pointers/Pointers.hpp"
#include "submenus/Self.hpp"
#include "submenus/Teleport.hpp"
#include "submenus/Network.hpp"
#include "submenus/Players.hpp"
#include "submenus/Settings.hpp"
namespace YimMenu
{
@@ -17,8 +19,10 @@ namespace YimMenu
{
// Arguably the only place this file should be edited at for more menus
UIManager::AddSubmenu(std::make_shared<Submenus::Self>());
UIManager::AddSubmenu(std::make_shared<Submenus::Teleport>());
UIManager::AddSubmenu(std::make_shared<Submenus::Network>());
UIManager::AddSubmenu(std::make_shared<Submenus::Players>());
UIManager::AddSubmenu(std::make_shared<Submenus::Settings>());
Renderer::AddRendererCallBack(
[&] {
+1 -2
View File
@@ -3,6 +3,7 @@
#include "core/commands/BoolCommand.hpp"
#include "game/pointers/Pointers.hpp"
#include "game/gta/invoker/Invoker.hpp"
#include "game/gta/Natives.hpp"
namespace YimMenu::Features
{
@@ -14,7 +15,6 @@ namespace YimMenu
{
void Overlay::Draw()
{
#if 0
if (!Features::_OverlayEnabled.GetState() || !NativeInvoker::AreHandlersCached())
return;
@@ -31,6 +31,5 @@ namespace YimMenu
ImGui::PopFont();
ImGui::PopStyleColor();
ImGui::End();
#endif
}
}
@@ -2,8 +2,9 @@
#include "core/commands/Command.hpp"
#include "core/commands/Commands.hpp"
#include "core/commands/Vector3Command.hpp"
//#include "game/backend/SavedLocations.hpp"
//#include "game/backend/Self.hpp"
#include "game/backend/SavedLocations.hpp"
#include "game/backend/Self.hpp"
#include "game/pointers/Pointers.hpp"
namespace YimMenu
{
@@ -26,7 +27,7 @@ namespace YimMenu
ImGui::SetNextItemWidth(180);
if (ImGui::InputFloat3("##coord_inp", &value.x, "%.1f"))
m_Command->SetState(value);
#if 0
if (Self::GetPed())
{
ImGui::SameLine();
@@ -90,7 +91,7 @@ namespace YimMenu
if (ImGui::IsItemHovered() && l.name.length() > 27)
{
ImGui::BeginTooltip();
ImGui::Text(l.name.data());
ImGui::Text("%s", l.name.data());
ImGui::EndTooltip();
}
}
@@ -108,7 +109,6 @@ namespace YimMenu
ImGui::EndPopup();
}
#endif
auto& label = m_LabelOverride.has_value() ? m_LabelOverride.value() : m_Command->GetLabel();
if (!label.empty())
+1 -1
View File
@@ -28,7 +28,7 @@ namespace YimMenu::Submenus
if (player.GetPed() && player.GetPed().IsInvincible())
tags.push_back({"GOD", ImGui::Colors::Crimson});
if (player.GetPed() && !player.GetPed().IsVisible())
if (player.GetPed() && !player.GetPed().IsVisible() && false) // doesn't really seem to work on GTA
tags.push_back({"INVIS", ImGui::Colors::MediumPurple});
return tags;
+7 -2
View File
@@ -26,6 +26,7 @@ namespace YimMenu::Submenus
globalsGroup->AddItem(std::make_shared<BoolCommandItem>("godmode"_J));
globalsGroup->AddItem(std::make_shared<BoolCommandItem>("invis"_J));
globalsGroup->AddItem(std::make_shared<BoolCommandItem>("otr"_J));
toolsGroup->AddItem(std::make_shared<CommandItem>("suicide"_J));
@@ -50,12 +51,16 @@ namespace YimMenu::Submenus
auto vehicle = std::make_shared<Category>("Vehicle");
auto vehicleGlobalsGroup = std::make_shared<Group>("Globals");
auto vehicleMiscGroup = std::make_shared<Group>("Misc");
vehicleGlobalsGroup->AddItem(std::make_shared<BoolCommandItem>("vehiclegodmode"_J));
vehicleGlobalsGroup->AddItem(std::make_shared<BoolCommandItem>("vehiclegodmode"_J, "Godmode"));
vehicleGlobalsGroup->AddItem(std::make_shared<BoolCommandItem>("keepfixed"_J, "Keep Fixed"));
vehicleGlobalsGroup->AddItem(std::make_shared<CommandItem>("repairvehicle"_J));
vehicleMiscGroup->AddItem(std::make_shared<BoolCommandItem>("speedometer"_J));
vehicle->AddItem(vehicleGlobalsGroup);
vehicle->AddItem(vehicleMiscGroup);
AddCategory(std::move(vehicle));
}
}
+51
View File
@@ -0,0 +1,51 @@
#include "Settings.hpp"
#include "core/commands/Commands.hpp"
#include "core/commands/HotkeySystem.hpp"
#include "core/commands/LoopedCommand.hpp"
#include "game/backend/Self.hpp"
#include "game/frontend/items/Items.hpp"
namespace YimMenu::Submenus
{
// TODO: refactor this
static void Hotkeys()
{
ImGui::BulletText("Hold the command name clicked to change its hotkey");
ImGui::BulletText("Press any registered key to remove");
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
for (auto& [Hash, Command] : Commands::GetCommands())
{
ImGui::PushID(Hash);
if (g_HotkeySystem.m_CommandHotkeys.find(Hash) != g_HotkeySystem.m_CommandHotkeys.end())
HotkeySetter(Hash).Draw();
ImGui::Spacing();
ImGui::PopID();
}
};
Settings::Settings() :
Submenu::Submenu("Settings")
{
auto hotkeys = std::make_shared<Category>("Hotkeys");
auto gui = std::make_shared<Category>("GUI");
auto overlay = std::make_shared<Group>("Overlay");
hotkeys->AddItem(std::make_shared<ImGuiItem>(Hotkeys));
overlay->AddItem(std::make_shared<BoolCommandItem>("overlay"_J));
overlay->AddItem(std::make_shared<ConditionalItem>("overlay"_J, std::make_shared<BoolCommandItem>("overlayfps"_J)));
gui->AddItem(overlay);
AddCategory(std::move(hotkeys));
AddCategory(std::move(gui));
}
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include "core/frontend/manager/UIManager.hpp"
namespace YimMenu::Submenus
{
class Settings : public Submenu
{
public:
Settings();
};
}
+192
View File
@@ -0,0 +1,192 @@
#include "Teleport.hpp"
#include "core/frontend/Notifications.hpp"
#include "core/backend/FiberPool.hpp"
#include "game/backend/SavedLocations.hpp"
#include "game/backend/Self.hpp"
#include "game/frontend/items/Items.hpp"
namespace YimMenu::Submenus
{
static float GetDistanceFromLocation(const SavedLocation& t)
{
return rage::fvector3(t.x, t.y, t.z).GetDistance(Self::GetPed().GetPosition());
}
void RenderCustomTeleport()
{
ImGui::BeginGroup();
static std::string newLocationName{};
static std::string category = "Default";
static SavedLocation locationToDelete;
if (!std::string(locationToDelete.name).empty())
ImGui::OpenPopup("##deletelocation");
if (ImGui::BeginPopupModal("##deletelocation", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove))
{
ImGui::Text("Are you sure you want to delete %s?", locationToDelete.name.data());
ImGui::Spacing();
if (ImGui::Button("Yes"))
{
SavedLocations::DeleteSavedLocation(category, locationToDelete.name);
locationToDelete.name = "";
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("No"))
{
locationToDelete.name = "";
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::PushItemWidth(300);
InputTextWithHint("Category", "Category", &category).Draw();
ImGui::PushItemWidth(200);
InputTextWithHint("Location name", "New location", &newLocationName).Draw();
ImGui::PopItemWidth();
if (ImGui::Button("Save current location")) // Button widget still crashes
{
FiberPool::Push([=] {
if (newLocationName.empty())
{
Notifications::Show("Custom Teleport", "Please enter a valid name", NotificationType::Warning);
}
else if (SavedLocations::GetSavedLocationByName(newLocationName))
{
Notifications::Show("Custom Teleport", std::format("Location with name {} already exists", newLocationName));
}
else
{
SavedLocation teleportLocation;
Entity teleportEntity = Self::GetPed();
if (auto vehicle = Self::GetVehicle())
teleportEntity = vehicle;
auto coords = teleportEntity.GetPosition();
teleportLocation.name = newLocationName;
teleportLocation.x = coords.x;
teleportLocation.y = coords.y;
teleportLocation.z = coords.z;
teleportLocation.yaw = teleportEntity.GetHeading();
teleportLocation.pitch = 0.0f; // why do we need pitch and roll anyway?
teleportLocation.roll = 0.0f;
SavedLocations::SaveNewLocation(category, teleportLocation);
}
});
};
ImGui::Separator();
ImGui::Text("Double click to teleport\nShift click to delete");
ImGui::Spacing();
static std::string filter{};
InputTextWithHint("##filter", "Search", &filter).Draw();
ImGui::BeginGroup();
ImGui::Text("Categories");
if (ImGui::BeginListBox("##categories", {200, -1}))
{
for (auto& l : SavedLocations::GetAllSavedLocations() | std::ranges::views::keys)
{
if (ImGui::Selectable(l.data(), l == category))
{
category = l;
}
if (category.empty())
{
category = l;
}
}
ImGui::EndListBox();
}
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::Text("Locations");
if (ImGui::BeginListBox("##saved_locs", {200, -1})) // Need automatic dimensions instead of hard coded
{
if (SavedLocations::GetAllSavedLocations().find(category) != SavedLocations::GetAllSavedLocations().end())
{
std::vector<SavedLocation> current_list{};
if (!filter.empty())
current_list = SavedLocations::SavedLocationsFilteredList(filter);
else
current_list = SavedLocations::GetAllSavedLocations().at(category);
for (const auto& l : current_list)
{
if (ImGui::Selectable(l.name.data(), false, ImGuiSelectableFlags_AllowDoubleClick))
{
if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
{
locationToDelete = l;
}
else
{
if (ImGui::IsMouseDoubleClicked(0))
{
FiberPool::Push([l] {
rage::fvector3 l_ = {l.x, l.y, l.z};
Self::GetPed().TeleportTo(l_);
});
}
}
}
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
if (l.name.length() > 27)
ImGui::Text("%s", l.name.data());
ImGui::Text("Distance: %f", GetDistanceFromLocation(l));
ImGui::EndTooltip();
}
}
}
ImGui::EndListBox();
}
ImGui::EndGroup();
ImGui::EndGroup();
}
Teleport::Teleport() :
Submenu::Submenu("Teleport")
{
auto main = std::make_shared<Category>("Main");
auto miscGroup = std::make_shared<Group>("Misc");
// miscGroup->AddItem(std::make_shared<BoolCommandItem>("autotp"_J));
miscGroup->AddItem(std::make_shared<CommandItem>("tptowaypoint"_J));
// miscGroup->AddItem(std::make_shared<CommandItem>("tptomount"_J));
// miscGroup->AddItem(std::make_shared<CommandItem>("tptotraintrack"_J));
// miscGroup->AddItem(std::make_shared<CommandItem>("tptomoonshineshack"_J));
// miscGroup->AddItem(std::make_shared<CommandItem>("tptonazar"_J));
main->AddItem(miscGroup);
auto customteleport = std::make_shared<Category>("Saved");
customteleport->AddItem(std::make_shared<ImGuiItem>([] {
RenderCustomTeleport();
}));
AddCategory(std::move(main));
AddCategory(std::move(customteleport));
}
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include "core/frontend/manager/UIManager.hpp"
namespace YimMenu::Submenus
{
class Teleport : public Submenu
{
public:
Teleport();
};
}
+19
View File
@@ -155,6 +155,25 @@ namespace YimMenu
ENTITY::SET_ENTITY_VELOCITY(GetHandle(), vel.x, vel.y, vel.z);
}
float Entity::GetHeading()
{
ENTITY_ASSERT_VALID();
return ENTITY::GET_ENTITY_HEADING(GetHandle());
}
void Entity::SetHeading(float heading)
{
ENTITY_ASSERT_VALID();
ENTITY_ASSERT_CONTROL();
ENTITY::SET_ENTITY_HEADING(GetHandle(), heading);
}
float Entity::GetSpeed()
{
ENTITY_ASSERT_VALID();
return ENTITY::GET_ENTITY_SPEED(GetHandle());
}
void Entity::SetCollision(bool enabled)
{
ENTITY_ASSERT_VALID();
+3
View File
@@ -101,6 +101,9 @@ namespace YimMenu
void SetRotation(rage::fvector3 rotation, int order = 2);
rage::fvector3 GetVelocity();
void SetVelocity(rage::fvector3 velocity);
float GetHeading();
void SetHeading(float heading);
float GetSpeed();
// physical
void SetCollision(bool enabled);
+14 -1
View File
@@ -51,7 +51,9 @@ namespace YimMenu
Vehicle Ped::GetVehicle()
{
ENTITY_ASSERT_VALID();
return Vehicle(PED::GET_VEHICLE_PED_IS_IN(GetHandle(), true));
if (!PED::IS_PED_IN_ANY_VEHICLE(GetHandle(), false))
return nullptr;
return Vehicle(PED::GET_VEHICLE_PED_IS_USING(GetHandle()));
}
bool Ped::GetRagdoll()
@@ -139,4 +141,15 @@ namespace YimMenu
WEAPON::SET_PED_INFINITE_AMMO_CLIP(GetHandle(), infinite); // is this networked?
}
void Ped::TeleportTo(const rage::fvector3& pos)
{
ENTITY_ASSERT_VALID();
ENTITY_ASSERT_CONTROL();
if (auto veh = GetVehicle())
veh.SetPosition(pos);
else
SetPosition(pos);
}
}
+3
View File
@@ -40,5 +40,8 @@ namespace YimMenu
// weapons
void SetInfiniteAmmo(bool infinite);
void SetInfiniteClip(bool infinite);
// teleport
void TeleportTo(const rage::fvector3& pos); // similar to SetPosition, but handles vehicles correctly
};
}
+18
View File
@@ -5,6 +5,8 @@
#include "types/script/GtaThread.hpp"
#include "types/script/CGameScriptHandler.hpp"
#include "types/script/CGameScriptId.hpp"
#include "types/script/globals/GlobalPlayerBD.hpp"
#include "game/backend/Self.hpp"
namespace YimMenu::Scripts
{
@@ -59,4 +61,20 @@ namespace YimMenu::Scripts
thread->m_ScriptHandler->GetId()->m_Hash = *hash_4;
}
}
bool SafeToModifyFreemodeBroadcastGlobals()
{
if (!*Pointers.IsSessionStarted)
return false;
if (!FindScriptThread("freemode"_J))
return false;
if (auto gpbd = GlobalPlayerBD::Get())
{
return gpbd->Entries[Self::GetPlayer().GetId()].FreemodeState == eFreemodeState::RUNNING;
}
return false;
}
}
+1
View File
@@ -12,4 +12,5 @@ namespace YimMenu::Scripts
extern rage::scrThread* FindScriptThread(joaat_t hash);
extern void RunAsScript(rage::scrThread* thread, std::function<void()> callback);
extern void RunWithSpoofedThreadName(std::uint32_t name, std::function<void()> callback); // consider using RunAsScript if possible
extern bool SafeToModifyFreemodeBroadcastGlobals(); // TODO: find a better method for this that works for all scripts
}
+21
View File
@@ -57,4 +57,25 @@ namespace YimMenu
VEHICLE::SET_VEHICLE_DIRT_LEVEL(GetHandle(), 0);
}
}
int Vehicle::GetGear()
{
ENTITY_ASSERT_VALID();
return VEHICLE::_GET_VEHICLE_CURRENT_DRIVE_GEAR(GetHandle());
}
float Vehicle::GetRevRatio()
{
ENTITY_ASSERT_VALID();
return VEHICLE::_GET_VEHICLE_CURRENT_REV_RATIO(GetHandle());
}
float Vehicle::GetMaxSpeed()
{
ENTITY_ASSERT_VALID();
return VEHICLE::GET_VEHICLE_ESTIMATED_MAX_SPEED(GetHandle());
}
}
+7
View File
@@ -11,5 +11,12 @@ namespace YimMenu
static Vehicle Create(std::uint32_t model, rage::fvector3 coords, float heading = 0.0f);
void Fix();
// gears
int GetGear();
float GetRevRatio(); // doesn't work
// speed
float GetMaxSpeed();
};
}
+1
View File
@@ -43,6 +43,7 @@ namespace YimMenu
ScriptMgr::AddScript(std::make_unique<Script>(&Self::RunScript));
FiberPool::Init(5);
ScriptMgr::AddScript(std::make_unique<Script>(&HotkeySystem::RunScript));
ScriptMgr::AddScript(std::make_unique<Script>(&Commands::RunScript));
Notifications::Show("YimMenuV2", "Loaded succesfully", NotificationType::Success);