diff --git a/src/core/commands/HotkeySystem.cpp b/src/core/commands/HotkeySystem.cpp index 08fb700..9e860e2 100644 --- a/src/core/commands/HotkeySystem.cpp +++ b/src/core/commands/HotkeySystem.cpp @@ -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) diff --git a/src/core/commands/HotkeySystem.hpp b/src/core/commands/HotkeySystem.hpp index a7d24f4..ec5b5f3 100644 --- a/src/core/commands/HotkeySystem.hpp +++ b/src/core/commands/HotkeySystem.hpp @@ -26,7 +26,8 @@ namespace YimMenu std::string GetHotkeyLabel(int hotkey_modifiers); void CreateHotkey(std::vector& Hotkey); - void Update(); + void RunScriptImpl(); + static void RunScript(); virtual void SaveStateImpl(nlohmann::json& state) override; virtual void LoadStateImpl(nlohmann::json& state) override; diff --git a/src/core/logger/LogSink.cpp b/src/core/logger/LogSink.cpp index 39fdce6..17f592e 100644 --- a/src/core/logger/LogSink.cpp +++ b/src/core/logger/LogSink.cpp @@ -18,7 +18,7 @@ namespace YimMenu const char* LogSink::GetLevelStr(const eLogLevel level) { - constexpr std::array levelStrings = {{{"DEBUG"}, {"INFO"}, {"WARN"}, {"ERROR"}}}; + constexpr std::array levelStrings = {"DEBUG", "INFO", "WARN", "ERROR"}; return levelStrings[level]; } diff --git a/src/core/renderer/Renderer.cpp b/src/core/renderer/Renderer.cpp index 47faa9b..57bb593 100644 --- a/src/core/renderer/Renderer.cpp +++ b/src/core/renderer/Renderer.cpp @@ -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() diff --git a/src/game/backend/SavedLocations.cpp b/src/game/backend/SavedLocations.cpp new file mode 100644 index 0000000..3a3341c --- /dev/null +++ b/src/game/backend/SavedLocations.cpp @@ -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 SavedLocations::SavedLocationsFilteredListImpl(std::string filter) + { + std::vector 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>>(); + + 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; + } +} \ No newline at end of file diff --git a/src/game/backend/SavedLocations.hpp b/src/game/backend/SavedLocations.hpp new file mode 100644 index 0000000..abefda3 --- /dev/null +++ b/src/game/backend/SavedLocations.hpp @@ -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> 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 SavedLocationsFilteredListImpl(std::string filter = ""); + + static SavedLocations& GetInstance() + { + static SavedLocations instance{}; + return instance; + } + + public: + static std::map>& 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 SavedLocationsFilteredList(std::string filter = "") + { + return GetInstance().SavedLocationsFilteredListImpl(filter); + }; + }; + +} \ No newline at end of file diff --git a/src/game/features/self/OffTheRadar.cpp b/src/game/features/self/OffTheRadar.cpp new file mode 100644 index 0000000..8aa670d --- /dev/null +++ b/src/game/features/self/OffTheRadar.cpp @@ -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"}; +} \ No newline at end of file diff --git a/src/game/features/teleport/TpToWaypoint.cpp b/src/game/features/teleport/TpToWaypoint.cpp new file mode 100644 index 0000000..22692cc --- /dev/null +++ b/src/game/features/teleport/TpToWaypoint.cpp @@ -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"}; +} \ No newline at end of file diff --git a/src/game/features/vehicle/KeepFixed.cpp b/src/game/features/vehicle/KeepFixed.cpp new file mode 100644 index 0000000..82ffbc7 --- /dev/null +++ b/src/game/features/vehicle/KeepFixed.cpp @@ -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"}; +} \ No newline at end of file diff --git a/src/game/features/vehicle/Speedometer.cpp b/src/game/features/vehicle/Speedometer.cpp new file mode 100644 index 0000000..08bae11 --- /dev/null +++ b/src/game/features/vehicle/Speedometer.cpp @@ -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"}; +} \ No newline at end of file diff --git a/src/game/frontend/Menu.cpp b/src/game/frontend/Menu.cpp index 0d2fcea..8c5cff3 100644 --- a/src/game/frontend/Menu.cpp +++ b/src/game/frontend/Menu.cpp @@ -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()); + UIManager::AddSubmenu(std::make_shared()); UIManager::AddSubmenu(std::make_shared()); UIManager::AddSubmenu(std::make_shared()); + UIManager::AddSubmenu(std::make_shared()); Renderer::AddRendererCallBack( [&] { diff --git a/src/game/frontend/Overlay.cpp b/src/game/frontend/Overlay.cpp index b99c24b..752ede3 100644 --- a/src/game/frontend/Overlay.cpp +++ b/src/game/frontend/Overlay.cpp @@ -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 } } \ No newline at end of file diff --git a/src/game/frontend/items/Vector3CommandItem.cpp b/src/game/frontend/items/Vector3CommandItem.cpp index 872f3c0..1b2b336 100644 --- a/src/game/frontend/items/Vector3CommandItem.cpp +++ b/src/game/frontend/items/Vector3CommandItem.cpp @@ -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()) diff --git a/src/game/frontend/submenus/Players.cpp b/src/game/frontend/submenus/Players.cpp index 4c7062d..00216d5 100644 --- a/src/game/frontend/submenus/Players.cpp +++ b/src/game/frontend/submenus/Players.cpp @@ -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; diff --git a/src/game/frontend/submenus/Self.cpp b/src/game/frontend/submenus/Self.cpp index fe8d8a9..5a42e7b 100644 --- a/src/game/frontend/submenus/Self.cpp +++ b/src/game/frontend/submenus/Self.cpp @@ -26,6 +26,7 @@ namespace YimMenu::Submenus globalsGroup->AddItem(std::make_shared("godmode"_J)); globalsGroup->AddItem(std::make_shared("invis"_J)); + globalsGroup->AddItem(std::make_shared("otr"_J)); toolsGroup->AddItem(std::make_shared("suicide"_J)); @@ -50,12 +51,16 @@ namespace YimMenu::Submenus auto vehicle = std::make_shared("Vehicle"); auto vehicleGlobalsGroup = std::make_shared("Globals"); + auto vehicleMiscGroup = std::make_shared("Misc"); - vehicleGlobalsGroup->AddItem(std::make_shared("vehiclegodmode"_J)); + vehicleGlobalsGroup->AddItem(std::make_shared("vehiclegodmode"_J, "Godmode")); + vehicleGlobalsGroup->AddItem(std::make_shared("keepfixed"_J, "Keep Fixed")); vehicleGlobalsGroup->AddItem(std::make_shared("repairvehicle"_J)); - + vehicleMiscGroup->AddItem(std::make_shared("speedometer"_J)); + vehicle->AddItem(vehicleGlobalsGroup); + vehicle->AddItem(vehicleMiscGroup); AddCategory(std::move(vehicle)); } } diff --git a/src/game/frontend/submenus/Settings.cpp b/src/game/frontend/submenus/Settings.cpp new file mode 100644 index 0000000..eaf0bb7 --- /dev/null +++ b/src/game/frontend/submenus/Settings.cpp @@ -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("Hotkeys"); + auto gui = std::make_shared("GUI"); + auto overlay = std::make_shared("Overlay"); + + hotkeys->AddItem(std::make_shared(Hotkeys)); + + overlay->AddItem(std::make_shared("overlay"_J)); + overlay->AddItem(std::make_shared("overlay"_J, std::make_shared("overlayfps"_J))); + + gui->AddItem(overlay); + + AddCategory(std::move(hotkeys)); + AddCategory(std::move(gui)); + } +} \ No newline at end of file diff --git a/src/game/frontend/submenus/Settings.hpp b/src/game/frontend/submenus/Settings.hpp new file mode 100644 index 0000000..b691c31 --- /dev/null +++ b/src/game/frontend/submenus/Settings.hpp @@ -0,0 +1,11 @@ +#pragma once +#include "core/frontend/manager/UIManager.hpp" + +namespace YimMenu::Submenus +{ + class Settings : public Submenu + { + public: + Settings(); + }; +} \ No newline at end of file diff --git a/src/game/frontend/submenus/Teleport.cpp b/src/game/frontend/submenus/Teleport.cpp new file mode 100644 index 0000000..30bb305 --- /dev/null +++ b/src/game/frontend/submenus/Teleport.cpp @@ -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 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("Main"); + auto miscGroup = std::make_shared("Misc"); + + // miscGroup->AddItem(std::make_shared("autotp"_J)); + miscGroup->AddItem(std::make_shared("tptowaypoint"_J)); + // miscGroup->AddItem(std::make_shared("tptomount"_J)); + // miscGroup->AddItem(std::make_shared("tptotraintrack"_J)); + // miscGroup->AddItem(std::make_shared("tptomoonshineshack"_J)); + // miscGroup->AddItem(std::make_shared("tptonazar"_J)); + + main->AddItem(miscGroup); + + auto customteleport = std::make_shared("Saved"); + customteleport->AddItem(std::make_shared([] { + RenderCustomTeleport(); + })); + + + AddCategory(std::move(main)); + AddCategory(std::move(customteleport)); + } +} \ No newline at end of file diff --git a/src/game/frontend/submenus/Teleport.hpp b/src/game/frontend/submenus/Teleport.hpp new file mode 100644 index 0000000..bf5d472 --- /dev/null +++ b/src/game/frontend/submenus/Teleport.hpp @@ -0,0 +1,11 @@ +#pragma once +#include "core/frontend/manager/UIManager.hpp" + +namespace YimMenu::Submenus +{ + class Teleport : public Submenu + { + public: + Teleport(); + }; +} \ No newline at end of file diff --git a/src/game/gta/Entity.cpp b/src/game/gta/Entity.cpp index 5ba523c..19cf39b 100644 --- a/src/game/gta/Entity.cpp +++ b/src/game/gta/Entity.cpp @@ -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(); diff --git a/src/game/gta/Entity.hpp b/src/game/gta/Entity.hpp index 36b562b..4a14656 100644 --- a/src/game/gta/Entity.hpp +++ b/src/game/gta/Entity.hpp @@ -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); diff --git a/src/game/gta/Ped.cpp b/src/game/gta/Ped.cpp index ec8911c..a2e9954 100644 --- a/src/game/gta/Ped.cpp +++ b/src/game/gta/Ped.cpp @@ -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); + } } \ No newline at end of file diff --git a/src/game/gta/Ped.hpp b/src/game/gta/Ped.hpp index 12563a0..21bc837 100644 --- a/src/game/gta/Ped.hpp +++ b/src/game/gta/Ped.hpp @@ -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 }; } \ No newline at end of file diff --git a/src/game/gta/Scripts.cpp b/src/game/gta/Scripts.cpp index 7e9b46d..e647b1f 100644 --- a/src/game/gta/Scripts.cpp +++ b/src/game/gta/Scripts.cpp @@ -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; + } } \ No newline at end of file diff --git a/src/game/gta/Scripts.hpp b/src/game/gta/Scripts.hpp index 278ecea..dee9604 100644 --- a/src/game/gta/Scripts.hpp +++ b/src/game/gta/Scripts.hpp @@ -12,4 +12,5 @@ namespace YimMenu::Scripts extern rage::scrThread* FindScriptThread(joaat_t hash); extern void RunAsScript(rage::scrThread* thread, std::function callback); extern void RunWithSpoofedThreadName(std::uint32_t name, std::function callback); // consider using RunAsScript if possible + extern bool SafeToModifyFreemodeBroadcastGlobals(); // TODO: find a better method for this that works for all scripts } \ No newline at end of file diff --git a/src/game/gta/Vehicle.cpp b/src/game/gta/Vehicle.cpp index ab23b84..bb163fe 100644 --- a/src/game/gta/Vehicle.cpp +++ b/src/game/gta/Vehicle.cpp @@ -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()); + } } \ No newline at end of file diff --git a/src/game/gta/Vehicle.hpp b/src/game/gta/Vehicle.hpp index 995fffe..433ab5c 100644 --- a/src/game/gta/Vehicle.hpp +++ b/src/game/gta/Vehicle.hpp @@ -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(); }; } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 85fbd1b..0f84a12 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,6 +43,7 @@ namespace YimMenu ScriptMgr::AddScript(std::make_unique